]> sigrok.org Git - libsigrok.git/commitdiff
Backport recent changes from mainline.
authorUwe Hermann <redacted>
Thu, 15 Jun 2017 16:46:14 +0000 (18:46 +0200)
committerUwe Hermann <redacted>
Sun, 14 Oct 2018 20:39:48 +0000 (22:39 +0200)
This includes all changes from

  59cae77e28538120a472aca1aeee6b2c4465e77a
  serial_stream_detect(): Make a code comment more generic.

up to

  a7600dc5c7712c588f8a940293e50853c88706ca
  Makefile.am: Install MIME info file in $(datadir)/mime/packages.

This is possible since (almost) none of the changes above change or
remove public API calls of the library.

289 files changed:
.gitignore
Makefile.am
README
README.devices
bindings/cxx/ConfigKey_methods.cpp
bindings/cxx/ConfigKey_methods.hpp
bindings/cxx/classes.cpp
bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp
bindings/python/sigrok/core/classes.i
bindings/ruby/classes.i
configure.ac
contrib/60-libsigrok.rules [new file with mode: 0644]
contrib/61-libsigrok-plugdev.rules [new file with mode: 0644]
contrib/61-libsigrok-uaccess.rules [new file with mode: 0644]
contrib/z60_libsigrok.rules [deleted file]
include/libsigrok/libsigrok.h
include/libsigrok/proto.h
src/analog.c
src/backend.c
src/conversion.c [new file with mode: 0644]
src/device.c
src/dmm/asycii.c
src/dmm/bm25x.c
src/dmm/dtm0660.c
src/dmm/eev121gw.c [new file with mode: 0644]
src/dmm/es519xx.c
src/dmm/fs9721.c
src/dmm/fs9922.c
src/dmm/m2110.c
src/dmm/metex14.c
src/dmm/ms8250d.c [new file with mode: 0644]
src/dmm/ut372.c
src/dmm/ut71x.c
src/dmm/vc870.c
src/dmm/vc96.c [new file with mode: 0644]
src/driver_list_start.c [new file with mode: 0644]
src/driver_list_stop.c [new file with mode: 0644]
src/drivers.c
src/hardware/agilent-dmm/api.c
src/hardware/agilent-dmm/protocol.c
src/hardware/agilent-dmm/protocol.h
src/hardware/appa-55ii/api.c
src/hardware/appa-55ii/protocol.c
src/hardware/appa-55ii/protocol.h
src/hardware/arachnid-labs-re-load-pro/api.c
src/hardware/arachnid-labs-re-load-pro/protocol.c
src/hardware/arachnid-labs-re-load-pro/protocol.h
src/hardware/asix-sigma/api.c
src/hardware/asix-sigma/protocol.c
src/hardware/asix-sigma/protocol.h
src/hardware/atten-pps3xxx/api.c
src/hardware/atten-pps3xxx/protocol.h
src/hardware/baylibre-acme/api.c
src/hardware/baylibre-acme/protocol.c
src/hardware/baylibre-acme/protocol.h
src/hardware/beaglelogic/api.c
src/hardware/beaglelogic/beaglelogic.h
src/hardware/beaglelogic/beaglelogic_native.c [new file with mode: 0644]
src/hardware/beaglelogic/beaglelogic_tcp.c [new file with mode: 0644]
src/hardware/beaglelogic/protocol.c
src/hardware/beaglelogic/protocol.h
src/hardware/brymen-bm86x/api.c
src/hardware/brymen-bm86x/protocol.c
src/hardware/brymen-bm86x/protocol.h
src/hardware/brymen-dmm/api.c
src/hardware/brymen-dmm/parser.c
src/hardware/brymen-dmm/protocol.c
src/hardware/brymen-dmm/protocol.h
src/hardware/cem-dt-885x/api.c
src/hardware/cem-dt-885x/protocol.c
src/hardware/cem-dt-885x/protocol.h
src/hardware/center-3xx/api.c
src/hardware/center-3xx/protocol.c
src/hardware/center-3xx/protocol.h
src/hardware/chronovu-la/api.c
src/hardware/chronovu-la/protocol.h
src/hardware/colead-slm/api.c
src/hardware/colead-slm/protocol.c
src/hardware/colead-slm/protocol.h
src/hardware/conrad-digi-35-cpu/api.c
src/hardware/conrad-digi-35-cpu/protocol.c
src/hardware/conrad-digi-35-cpu/protocol.h
src/hardware/demo/api.c
src/hardware/demo/protocol.c
src/hardware/demo/protocol.h
src/hardware/dreamsourcelab-dslogic/api.c [new file with mode: 0644]
src/hardware/dreamsourcelab-dslogic/protocol.c [new file with mode: 0644]
src/hardware/dreamsourcelab-dslogic/protocol.h [new file with mode: 0644]
src/hardware/fluke-45/api.c [new file with mode: 0644]
src/hardware/fluke-45/protocol.c [new file with mode: 0644]
src/hardware/fluke-45/protocol.h [new file with mode: 0644]
src/hardware/fluke-dmm/api.c
src/hardware/fluke-dmm/protocol.c
src/hardware/fluke-dmm/protocol.h
src/hardware/ftdi-la/api.c
src/hardware/ftdi-la/protocol.c
src/hardware/ftdi-la/protocol.h
src/hardware/fx2lafw/api.c
src/hardware/fx2lafw/dslogic.c [deleted file]
src/hardware/fx2lafw/dslogic.h [deleted file]
src/hardware/fx2lafw/protocol.c
src/hardware/fx2lafw/protocol.h
src/hardware/gmc-mh-1x-2x/api.c
src/hardware/gmc-mh-1x-2x/protocol.c
src/hardware/gmc-mh-1x-2x/protocol.h
src/hardware/gwinstek-gds-800/api.c
src/hardware/gwinstek-gds-800/protocol.c
src/hardware/gwinstek-gds-800/protocol.h
src/hardware/gwinstek-gpd/api.c [new file with mode: 0644]
src/hardware/gwinstek-gpd/protocol.c [new file with mode: 0644]
src/hardware/gwinstek-gpd/protocol.h [new file with mode: 0644]
src/hardware/hameg-hmo/api.c
src/hardware/hameg-hmo/protocol.c
src/hardware/hameg-hmo/protocol.h
src/hardware/hantek-4032l/api.c [new file with mode: 0644]
src/hardware/hantek-4032l/protocol.c [new file with mode: 0644]
src/hardware/hantek-4032l/protocol.h [new file with mode: 0644]
src/hardware/hantek-6xxx/api.c
src/hardware/hantek-6xxx/protocol.c
src/hardware/hantek-6xxx/protocol.h
src/hardware/hantek-dso/api.c
src/hardware/hantek-dso/protocol.c
src/hardware/hantek-dso/protocol.h
src/hardware/hp-3457a/api.c
src/hardware/hp-3457a/protocol.c
src/hardware/hp-3457a/protocol.h
src/hardware/hp-3478a/api.c [new file with mode: 0644]
src/hardware/hp-3478a/protocol.c [new file with mode: 0644]
src/hardware/hp-3478a/protocol.h [new file with mode: 0644]
src/hardware/hung-chang-dso-2100/api.c
src/hardware/hung-chang-dso-2100/protocol.c
src/hardware/hung-chang-dso-2100/protocol.h
src/hardware/ikalogic-scanalogic2/api.c
src/hardware/ikalogic-scanalogic2/protocol.c
src/hardware/ikalogic-scanalogic2/protocol.h
src/hardware/ikalogic-scanaplus/api.c
src/hardware/ikalogic-scanaplus/protocol.c
src/hardware/ikalogic-scanaplus/protocol.h
src/hardware/ipdbg-la/api.c [new file with mode: 0644]
src/hardware/ipdbg-la/protocol.c [new file with mode: 0644]
src/hardware/ipdbg-la/protocol.h [new file with mode: 0644]
src/hardware/kecheng-kc-330b/api.c
src/hardware/kecheng-kc-330b/protocol.c
src/hardware/kecheng-kc-330b/protocol.h
src/hardware/kern-scale/api.c
src/hardware/kern-scale/protocol.c
src/hardware/kern-scale/protocol.h
src/hardware/korad-kaxxxxp/api.c
src/hardware/korad-kaxxxxp/protocol.c
src/hardware/korad-kaxxxxp/protocol.h
src/hardware/lascar-el-usb/api.c
src/hardware/lascar-el-usb/protocol.c
src/hardware/lascar-el-usb/protocol.h
src/hardware/lecroy-logicstudio/api.c
src/hardware/lecroy-logicstudio/protocol.c
src/hardware/lecroy-logicstudio/protocol.h
src/hardware/lecroy-xstream/api.c
src/hardware/lecroy-xstream/protocol.c
src/hardware/lecroy-xstream/protocol.h
src/hardware/link-mso19/api.c
src/hardware/link-mso19/protocol.c
src/hardware/link-mso19/protocol.h
src/hardware/manson-hcs-3xxx/api.c
src/hardware/manson-hcs-3xxx/protocol.c
src/hardware/manson-hcs-3xxx/protocol.h
src/hardware/maynuo-m97/api.c
src/hardware/maynuo-m97/protocol.c
src/hardware/maynuo-m97/protocol.h
src/hardware/mic-985xx/api.c
src/hardware/mic-985xx/protocol.c
src/hardware/mic-985xx/protocol.h
src/hardware/motech-lps-30x/api.c
src/hardware/motech-lps-30x/protocol.c
src/hardware/motech-lps-30x/protocol.h
src/hardware/norma-dmm/api.c
src/hardware/norma-dmm/protocol.c
src/hardware/norma-dmm/protocol.h
src/hardware/openbench-logic-sniffer/api.c
src/hardware/openbench-logic-sniffer/protocol.h
src/hardware/pce-322a/api.c
src/hardware/pce-322a/protocol.c
src/hardware/pce-322a/protocol.h
src/hardware/pipistrello-ols/api.c
src/hardware/pipistrello-ols/protocol.c
src/hardware/pipistrello-ols/protocol.h
src/hardware/rdtech-dps/api.c [new file with mode: 0644]
src/hardware/rdtech-dps/protocol.c [new file with mode: 0644]
src/hardware/rdtech-dps/protocol.h [new file with mode: 0644]
src/hardware/rigol-ds/api.c
src/hardware/rigol-ds/protocol.c
src/hardware/rigol-ds/protocol.h
src/hardware/rohde-schwarz-sme-0x/api.c
src/hardware/rohde-schwarz-sme-0x/protocol.h
src/hardware/saleae-logic-pro/api.c [new file with mode: 0644]
src/hardware/saleae-logic-pro/protocol.c [new file with mode: 0644]
src/hardware/saleae-logic-pro/protocol.h [new file with mode: 0644]
src/hardware/saleae-logic16/api.c
src/hardware/saleae-logic16/protocol.c
src/hardware/saleae-logic16/protocol.h
src/hardware/scpi-pps/api.c
src/hardware/scpi-pps/profiles.c
src/hardware/scpi-pps/protocol.c
src/hardware/scpi-pps/protocol.h
src/hardware/serial-dmm/api.c
src/hardware/serial-dmm/protocol.c
src/hardware/serial-dmm/protocol.h
src/hardware/serial-lcr/api.c
src/hardware/siglent-sds/api.c [new file with mode: 0644]
src/hardware/siglent-sds/protocol.c [new file with mode: 0644]
src/hardware/siglent-sds/protocol.h [new file with mode: 0644]
src/hardware/sysclk-lwla/api.c
src/hardware/sysclk-lwla/lwla.c
src/hardware/sysclk-lwla/lwla.h
src/hardware/sysclk-lwla/lwla1016.c
src/hardware/sysclk-lwla/lwla1034.c
src/hardware/sysclk-lwla/protocol.c
src/hardware/sysclk-lwla/protocol.h
src/hardware/teleinfo/api.c
src/hardware/teleinfo/protocol.c
src/hardware/teleinfo/protocol.h
src/hardware/testo/api.c
src/hardware/testo/protocol.c
src/hardware/testo/protocol.h
src/hardware/tondaj-sl-814/api.c
src/hardware/tondaj-sl-814/protocol.c
src/hardware/tondaj-sl-814/protocol.h
src/hardware/uni-t-dmm/api.c
src/hardware/uni-t-dmm/protocol.c
src/hardware/uni-t-dmm/protocol.h
src/hardware/uni-t-ut32x/api.c
src/hardware/uni-t-ut32x/protocol.c
src/hardware/uni-t-ut32x/protocol.h
src/hardware/victor-dmm/api.c
src/hardware/victor-dmm/protocol.c
src/hardware/victor-dmm/protocol.h
src/hardware/yokogawa-dlm/api.c
src/hardware/yokogawa-dlm/protocol.c
src/hardware/yokogawa-dlm/protocol.h
src/hardware/zeroplus-logic-cube/api.c
src/hardware/zeroplus-logic-cube/protocol.c
src/hardware/zeroplus-logic-cube/protocol.h
src/hardware/zketech-ebd-usb/api.c [new file with mode: 0644]
src/hardware/zketech-ebd-usb/protocol.c [new file with mode: 0644]
src/hardware/zketech-ebd-usb/protocol.h [new file with mode: 0644]
src/hwdriver.c
src/input/binary.c
src/input/chronovu_la8.c
src/input/csv.c
src/input/input.c
src/input/logicport.c [new file with mode: 0644]
src/input/null.c [new file with mode: 0644]
src/input/raw_analog.c
src/input/trace32_ad.c
src/input/vcd.c
src/input/wav.c
src/lcr/es51919.c
src/libsigrok-internal.h
src/log.c
src/modbus/modbus_serial_rtu.c
src/output/analog.c
src/output/ascii.c
src/output/binary.c
src/output/bits.c
src/output/chronovu_la8.c
src/output/csv.c
src/output/hex.c
src/output/null.c [new file with mode: 0644]
src/output/ols.c
src/output/output.c
src/output/srzip.c
src/output/vcd.c
src/output/wav.c
src/resource.c
src/scpi.h
src/scpi/helpers.c [deleted file]
src/scpi/scpi.c
src/scpi/scpi_libgpib.c
src/scpi/scpi_serial.c
src/scpi/scpi_usbtmc_libusb.c
src/serial.c
src/session.c
src/session_driver.c
src/session_file.c
src/soft-trigger.c
src/std.c
src/strutil.c
src/usb.c
tests/analog.c
tests/strutil.c

index c2d6b42c43d50ef5e55489e377590c1ba2e9643c..e943562f81b4ab9fb310c18913c9b78684f90457 100644 (file)
@@ -12,6 +12,7 @@
 # Editor/IDE cruft
 *.kate-swp
 *~
+.*.sw*
 /*.kdev4
 /Makefile.am.user
 
index c98422956561d641d36c014d4f84dc267de04ea1..148de0ff70108628d0230697c754e30ea26bfc5a 100644 (file)
@@ -50,6 +50,7 @@ lib_LTLIBRARIES = libsigrok.la
 # Backend files
 libsigrok_la_SOURCES = \
        src/backend.c \
+       src/conversion.c \
        src/device.c \
        src/session.c \
        src/session_file.c \
@@ -73,10 +74,12 @@ libsigrok_la_SOURCES += \
        src/input/binary.c \
        src/input/chronovu_la8.c \
        src/input/csv.c \
+       src/input/logicport.c \
        src/input/raw_analog.c \
        src/input/trace32_ad.c \
        src/input/vcd.c \
-       src/input/wav.c
+       src/input/wav.c \
+       src/input/null.c
 
 # Output modules
 libsigrok_la_SOURCES += \
@@ -91,7 +94,8 @@ libsigrok_la_SOURCES += \
        src/output/hex.c \
        src/output/ols.c \
        src/output/srzip.c \
-       src/output/vcd.c
+       src/output/vcd.c \
+       src/output/null.c
 
 # Transform modules
 libsigrok_la_SOURCES += \
@@ -104,7 +108,6 @@ libsigrok_la_SOURCES += \
 libsigrok_la_SOURCES += \
        src/scpi.h \
        src/scpi/scpi.c \
-       src/scpi/helpers.c \
        src/scpi/scpi_tcp.c
 if NEED_RPC
 libsigrok_la_SOURCES += \
@@ -143,18 +146,21 @@ endif
 
 # Hardware (DMM chip parsers)
 libsigrok_la_SOURCES += \
+       src/dmm/asycii.c \
+       src/dmm/bm25x.c \
+       src/dmm/dtm0660.c \
+       src/dmm/eev121gw.c \
        src/dmm/es519xx.c \
        src/dmm/fs9721.c \
        src/dmm/fs9922.c \
        src/dmm/m2110.c \
        src/dmm/metex14.c \
-       src/dmm/asycii.c \
+       src/dmm/ms8250d.c \
        src/dmm/rs9lcd.c \
-       src/dmm/bm25x.c \
-       src/dmm/ut71x.c \
        src/dmm/ut372.c \
+       src/dmm/ut71x.c \
        src/dmm/vc870.c \
-       src/dmm/dtm0660.c
+       src/dmm/vc96.c
 
 # Hardware (LCR chip parsers)
 if NEED_SERIAL
@@ -167,15 +173,22 @@ libsigrok_la_SOURCES += \
        src/scale/kern.c
 
 # Hardware drivers
-noinst_LTLIBRARIES = src/libdrivers.la
+noinst_LTLIBRARIES = src/libdrivers.la \
+       src/libdrivers_head.la src/libdrivers_tail.la
 
-src/libdrivers.o: src/libdrivers.la
-       $(AM_V_CCLD)$(LINK) src/libdrivers.la
+src/libdrivers.o: src/libdrivers.la \
+               src/libdrivers_head.la src/libdrivers_tail.la
+       $(AM_V_CCLD)$(LINK) src/libdrivers_head.la src/libdrivers.la \
+               src/libdrivers_tail.la
 src/libdrivers.lo: src/libdrivers.o
        $(AM_V_GEN)echo "# Generated by libtool" > $@
        $(AM_V_at)echo "pic_object='libdrivers.o'" >> $@
        $(AM_V_at)echo "non_pic_object='libdrivers.o'" >> $@
 
+src_libdrivers_head_la_SOURCES = src/driver_list_start.c
+
+src_libdrivers_tail_la_SOURCES = src/driver_list_stop.c
+
 src_libdrivers_la_SOURCES = src/drivers.c
 
 if HW_AGILENT_DMM
@@ -221,7 +234,9 @@ src_libdrivers_la_SOURCES += \
        src/hardware/beaglelogic/beaglelogic.h \
        src/hardware/beaglelogic/protocol.h \
        src/hardware/beaglelogic/protocol.c \
-       src/hardware/beaglelogic/api.c
+       src/hardware/beaglelogic/api.c \
+       src/hardware/beaglelogic/beaglelogic_native.c \
+       src/hardware/beaglelogic/beaglelogic_tcp.c
 endif
 if HW_BRYMEN_BM86X
 src_libdrivers_la_SOURCES += \
@@ -272,6 +287,18 @@ src_libdrivers_la_SOURCES += \
        src/hardware/demo/protocol.c \
        src/hardware/demo/api.c
 endif
+if HW_DREAMSOURCELAB_DSLOGIC
+src_libdrivers_la_SOURCES += \
+       src/hardware/dreamsourcelab-dslogic/protocol.h \
+       src/hardware/dreamsourcelab-dslogic/protocol.c \
+       src/hardware/dreamsourcelab-dslogic/api.c
+endif
+if HW_FLUKE_45
+src_libdrivers_la_SOURCES += \
+       src/hardware/fluke-45/protocol.h \
+       src/hardware/fluke-45/protocol.c \
+       src/hardware/fluke-45/api.c
+endif
 if HW_FLUKE_DMM
 src_libdrivers_la_SOURCES += \
        src/hardware/fluke-dmm/protocol.h \
@@ -288,9 +315,7 @@ if HW_FX2LAFW
 src_libdrivers_la_SOURCES += \
        src/hardware/fx2lafw/protocol.h \
        src/hardware/fx2lafw/protocol.c \
-       src/hardware/fx2lafw/api.c \
-       src/hardware/fx2lafw/dslogic.c \
-       src/hardware/fx2lafw/dslogic.h
+       src/hardware/fx2lafw/api.c
 endif
 if HW_GMC_MH_1X_2X
 src_libdrivers_la_SOURCES += \
@@ -304,12 +329,24 @@ src_libdrivers_la_SOURCES += \
        src/hardware/gwinstek-gds-800/protocol.c \
        src/hardware/gwinstek-gds-800/api.c
 endif
+if HW_GWINSTEK_GPD
+src_libdrivers_la_SOURCES += \
+       src/hardware/gwinstek-gpd/protocol.h \
+       src/hardware/gwinstek-gpd/protocol.c \
+       src/hardware/gwinstek-gpd/api.c
+endif
 if HW_HAMEG_HMO
 src_libdrivers_la_SOURCES += \
        src/hardware/hameg-hmo/protocol.h \
        src/hardware/hameg-hmo/protocol.c \
        src/hardware/hameg-hmo/api.c
 endif
+if HW_HANTEK_4032L
+src_libdrivers_la_SOURCES += \
+       src/hardware/hantek-4032l/protocol.h \
+       src/hardware/hantek-4032l/protocol.c \
+       src/hardware/hantek-4032l/api.c
+endif
 if HW_HANTEK_6XXX
 src_libdrivers_la_SOURCES += \
        src/hardware/hantek-6xxx/protocol.h \
@@ -328,6 +365,12 @@ src_libdrivers_la_SOURCES += \
        src/hardware/hp-3457a/protocol.c \
        src/hardware/hp-3457a/api.c
 endif
+if HW_HP_3478A
+src_libdrivers_la_SOURCES += \
+       src/hardware/hp-3478a/protocol.h \
+       src/hardware/hp-3478a/protocol.c \
+       src/hardware/hp-3478a/api.c
+endif
 if HW_HUNG_CHANG_DSO_2100
 src_libdrivers_la_SOURCES += \
        src/hardware/hung-chang-dso-2100/protocol.h \
@@ -346,6 +389,12 @@ src_libdrivers_la_SOURCES += \
        src/hardware/ikalogic-scanaplus/protocol.c \
        src/hardware/ikalogic-scanaplus/api.c
 endif
+if HW_IPDBG_LA
+src_libdrivers_la_SOURCES += \
+       src/hardware/ipdbg-la/protocol.h \
+       src/hardware/ipdbg-la/protocol.c \
+       src/hardware/ipdbg-la/api.c
+endif
 if HW_KECHENG_KC_330B
 src_libdrivers_la_SOURCES += \
        src/hardware/kecheng-kc-330b/protocol.h \
@@ -430,6 +479,12 @@ src_libdrivers_la_SOURCES += \
        src/hardware/pipistrello-ols/protocol.c \
        src/hardware/pipistrello-ols/api.c
 endif
+if HW_RDTECH_DPS
+src_libdrivers_la_SOURCES += \
+       src/hardware/rdtech-dps/protocol.h \
+       src/hardware/rdtech-dps/protocol.c \
+       src/hardware/rdtech-dps/api.c
+endif
 if HW_RIGOL_DS
 src_libdrivers_la_SOURCES += \
        src/hardware/rigol-ds/protocol.h \
@@ -448,6 +503,12 @@ src_libdrivers_la_SOURCES += \
        src/hardware/saleae-logic16/protocol.c \
        src/hardware/saleae-logic16/api.c
 endif
+if HW_SALEAE_LOGIC_PRO
+src_libdrivers_la_SOURCES += \
+       src/hardware/saleae-logic-pro/protocol.h \
+       src/hardware/saleae-logic-pro/protocol.c \
+       src/hardware/saleae-logic-pro/api.c
+endif
 if HW_SCPI_PPS
 src_libdrivers_la_SOURCES += \
        src/hardware/scpi-pps/protocol.h \
@@ -465,6 +526,12 @@ if HW_SERIAL_LCR
 src_libdrivers_la_SOURCES += \
        src/hardware/serial-lcr/api.c
 endif
+if HW_SIGLENT_SDS
+src_libdrivers_la_SOURCES += \
+       src/hardware/siglent-sds/protocol.h \
+       src/hardware/siglent-sds/protocol.c \
+       src/hardware/siglent-sds/api.c
+endif
 if HW_SYSCLK_LWLA
 src_libdrivers_la_SOURCES += \
        src/hardware/sysclk-lwla/lwla.h \
@@ -529,6 +596,12 @@ src_libdrivers_la_SOURCES += \
        src/hardware/zeroplus-logic-cube/protocol.c \
        src/hardware/zeroplus-logic-cube/api.c
 endif
+if HW_ZKETECH_EBD_USB
+src_libdrivers_la_SOURCES += \
+       src/hardware/zketech-ebd-usb/protocol.h \
+       src/hardware/zketech-ebd-usb/protocol.c \
+       src/hardware/zketech-ebd-usb/api.c
+endif
 
 libsigrok_la_LIBADD = src/libdrivers.lo $(SR_EXTRA_LIBS) $(LIBSIGROK_LIBS)
 libsigrok_la_LDFLAGS = -version-info $(SR_LIB_VERSION) -no-undefined
@@ -544,7 +617,7 @@ noinst_HEADERS = src/libsigrok-internal.h
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libsigrok.pc
 
-mimeappdir = $(datadir)/mime/application
+mimeappdir = $(datadir)/mime/packages
 mimeapp_DATA = contrib/vnd.sigrok.session.xml
 
 mimeicondir = $(datadir)/icons/hicolor/48x48/mimetypes
@@ -581,7 +654,9 @@ EXTRA_DIST = \
        contrib/libsigrok.png \
        contrib/libsigrok.svg \
        contrib/vnd.sigrok.session.xml \
-       contrib/z60_libsigrok.rules
+       contrib/60-libsigrok.rules \
+       contrib/61-libsigrok-plugdev.rules \
+       contrib/61-libsigrok-uaccess.rules
 
 if HAVE_CHECK
 TESTS = tests/main
diff --git a/README b/README
index f6d9842e3aae1740c1b4aea2e832bc057cf5b43a..154c4928929c5eb6763d8c04523cf4c01d1e5893 100644 (file)
--- a/README
+++ b/README
@@ -41,7 +41,7 @@ Requirements for the C library:
  - libserialport >= 0.1.1 (optional, used by some drivers)
  - librevisa >= 0.0.20130412 (optional, used by some drivers)
  - libusb-1.0 >= 1.0.16 (optional, used by some drivers)
- - libftdi >= 0.16 or libftdi1 >= 1.0 (optional, used by some drivers)
+ - libftdi1 >= 1.0 (optional, used by some drivers)
  - libgpib (optional, used by some drivers)
  - libieee1284 (optional, used by some drivers)
  - check >= 0.9.4 (optional, only needed to run unit tests)
index 584899e2b0648ed65aae7c39cbbdf083a46db24f..a64c587f941cbc12e6b00d21cc0355c33f2beea5 100644 (file)
@@ -14,6 +14,7 @@ the device is connected to the PC (usually via USB), before it can be used.
 
 The default locations where libsigrok expects the firmware files are:
 
+  $SIGROK_FIRMWARE_DIR (environment variable)
   $HOME/.local/share/sigrok-firmware
   $prefix/share/sigrok-firmware
   /usr/local/share/sigrok-firmware
@@ -114,6 +115,7 @@ The following drivers/devices do not need any firmware upload:
  - scpi-pps
  - serial-dmm (including all subdrivers)
  - serial-lcr (including all subdrivers)
+ - siglent-sds
  - teleinfo
  - testo
  - tondaj-sl-814
@@ -138,7 +140,8 @@ Example:
 
  $ sigrok-cli --driver <somedriver>:conn=/dev/ttyUSB0 ...
 
-The following drivers/devices require a serial port specification:
+The following drivers/devices require a serial port specification. Some of
+the drivers implement a default for the connection.
 
  - agilent-dmm
  - appa-55ii
@@ -183,6 +186,21 @@ The following drivers/devices do not require a serial port specification:
  - yokogawa-dlm (USBTMC or TCP)
  - zeroplus-logic-cube
 
+Beyond strict serial communication over COM ports (e.g. /dev/ttyUSB0), the
+conn= property can also address specific USB devices, as well as specify TCP
+or VXI communication parameters. See these examples:
+
+ $ sigrok-cli --driver <somedriver>:conn=<vid>.<pid> ...
+ $ sigrok-cli --driver <somedriver>:conn=tcp-raw/<ipaddr>/<port> ...
+ $ sigrok-cli --driver <somedriver>:conn=vxi/<ipaddr> ...
+
+The following drivers/devices accept network communication parameters:
+
+ - hameg-hmo
+ - rigol-ds
+ - siglent-sds
+ - yokogawa-dlm
+
 
 Specifying serial port parameters
 ---------------------------------
@@ -212,33 +230,39 @@ For USB-to-serial based devices, we recommended using our udev rules file
 (see below for details).
 
 
-Permissions for USB devices (udev rules file)
----------------------------------------------
+Permissions for USB devices (udev rules files)
+----------------------------------------------
 
 When using USB-based devices supported by libsigrok, the user running the
 libsigrok frontend (e.g. sigrok-cli) has to have (read/write) permissions
 for the respective USB device.
 
-On Linux, this is accomplished using either 'chmod' (not recommended) or
-using the udev rules file shipped with libsigrok (recommended).
+On Linux, this is accomplished using udev rules. libsigrok ships a rules
+file containing all supported devices which can be detected reliably
+(generic USB-to-serial converters are omitted, as these are used for a wide
+range of devices, e.g. GPS receivers, which are not handled by libsigrok).
 
-The file is available in contrib/z60_libsigrok.rules. It contains entries
-for all libsigrok-supported (USB-based) devices and changes their group
-to 'plugdev' and the permissions to '664'.
+The file is available in contrib/60-libsigrok.rules. This file just contains
+the list of devices and flags these devices with ID_SIGROK="1". Access is
+granted by the 61-libsigrok-plugdev.rules or 61-libsigrok-uaccess.rules files,
+allowing access to members of the plugdev group or to currently logged in
+users, respectively.
 
 When using a libsigrok package from your favorite Linux distribution, the
-packager will have already taken care of properly installing the udev file
-in the correct (distro-specific) place, and you don't have to do anything.
-The packager might also have adapted 'plugdev' and '664' as needed.
+files should already be installed in /usr/lib/udev/rules.d/, i.e.
+60-libsigrok.rules and one of the access granting rules files. Use of
+61-libsigrok-uaccess.rules is encouraged on systemd distributions.
+
+The access policy can be locally overridden by placing appropriate rules in
+/etc/udev/rules.d/, disabling or ammending the default policy. See the
+udev documentation, e.g. man 7 udev, for details.
 
 If you're building from source, you need to copy the file to the place
-where your distro expects such files. This is beyond the scope of this README,
-but generally the location could be e.g. /etc/udev/rules.d, or maybe
-/lib/udev/rules.d, or something else. Afterwards you might have to restart
-udev, e.g. via '/etc/init.d/udev restart' or similar, and you'll have to
-re-attach your device via USB.
+where udev will read these rules. Local rules should go to /etc/udev/rules.d.
+Keep the file naming, otherwise interaction between the libsigrok rules and
+rules shipped by the system will be broken.
 
-Please consult the udev docs of your distro for details.
+Please consult the udev docs for details.
 
 
 Cypress FX2 based devices
@@ -349,6 +373,9 @@ a short list for convenience:
       'SI232 online' (28-29S) or 'SI232 store' (22-26x). The interface must
       be configured to the same baud rate as the host (default 9600).
       Multimeter and interface must be configured to the same address.
+ - Metrix MX56C: Press the PRINT button to have the meter send acquisition
+   data via IR. Hold the PRINT button to adjust the meter's transmission
+   interval.
  - Norma DM950: If the interface doesn't work (e.g. USB-RS232 converter), power
    on the device with "FUNC" pressed (to power the interface from the DMM).
  - PCE PCE-DM32: Briefly press the "RS232" button.
index 54af62e804e76f2d8b3c7c0d628b9e17cc3a747c..9c3668ffbb35e9ee4337b7951c0dc7cac08215e2 100644 (file)
@@ -1,3 +1,5 @@
+#include <config.h>
+
 const DataType *ConfigKey::data_type() const
 {
        const struct sr_key_info *info = sr_key_info_get(SR_KEY_CONFIG, id());
@@ -30,8 +32,6 @@ const ConfigKey *ConfigKey::get_by_identifier(string identifier)
        return get(info->key);
 }
 
-#include <config.h>
-
 #ifndef HAVE_STOI_STOD
 
 /* Fallback implementation of stoi and stod */
@@ -70,12 +70,12 @@ static inline double stod( const std::string& str )
 }
 #endif
 
-Glib::VariantBase ConfigKey::parse_string(string value) const
+Glib::VariantBase ConfigKey::parse_string(string value, enum sr_datatype dt)
 {
        GVariant *variant;
        uint64_t p, q;
 
-       switch (data_type()->id())
+       switch (dt)
        {
                case SR_T_UINT64:
                        check(sr_parse_sizestring(value.c_str(), &p));
@@ -90,7 +90,7 @@ Glib::VariantBase ConfigKey::parse_string(string value) const
                case SR_T_FLOAT:
                        try {
                                variant = g_variant_new_double(stod(value));
-                       } catch (invalid_argument) {
+                       } catch (invalid_argument&) {
                                throw Error(SR_ERR_ARG);
                        }
                        break;
@@ -105,7 +105,7 @@ Glib::VariantBase ConfigKey::parse_string(string value) const
                case SR_T_INT32:
                        try {
                                variant = g_variant_new_int32(stoi(value));
-                       } catch (invalid_argument) {
+                       } catch (invalid_argument&) {
                                throw Error(SR_ERR_ARG);
                        }
                        break;
@@ -116,3 +116,8 @@ Glib::VariantBase ConfigKey::parse_string(string value) const
        return Glib::VariantBase(variant, false);
 }
 
+Glib::VariantBase ConfigKey::parse_string(string value) const
+{
+       enum sr_datatype dt = (enum sr_datatype)(data_type()->id());
+       return parse_string(value, dt);
+}
index f759cc4e3d6be717279a5873cc0d85345452f34b..bbc7ce8132ef5967f576e1f1e4126f91fe5d9401 100644 (file)
@@ -7,4 +7,5 @@
     /** Get configuration key by string identifier. */
     static const ConfigKey *get_by_identifier(string identifier);
     /** Parse a string argument into the appropriate type for this key. */
+    static Glib::VariantBase parse_string(string value, enum sr_datatype dt);
     Glib::VariantBase parse_string(string value) const;
index e4340fdf8f164d9db5d80bc98f9c4beb683912ed..5bc5dc2fed78f594d0605f4a58bdd644ccfb0239 100644 (file)
@@ -18,8 +18,9 @@
  */
 
 /* Needed for isascii(), as used in the GNU libstdc++ headers */
+/* Needed in strutil.c for POSIX.1-2008 locale functions */
 #ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 600
+#define _XOPEN_SOURCE 700
 #endif
 
 #include <config.h>
@@ -128,19 +129,19 @@ Context::Context() :
        if (struct sr_dev_driver **driver_list = sr_driver_list(_structure))
                for (int i = 0; driver_list[i]; i++) {
                        unique_ptr<Driver> driver {new Driver{driver_list[i]}};
-                       _drivers.insert(make_pair(driver->name(), move(driver)));
+                       _drivers.emplace(driver->name(), move(driver));
                }
 
        if (const struct sr_input_module **input_list = sr_input_list())
                for (int i = 0; input_list[i]; i++) {
                        unique_ptr<InputFormat> input {new InputFormat{input_list[i]}};
-                       _input_formats.insert(make_pair(input->name(), move(input)));
+                       _input_formats.emplace(input->name(), move(input));
                }
 
        if (const struct sr_output_module **output_list = sr_output_list())
                for (int i = 0; output_list[i]; i++) {
                        unique_ptr<OutputFormat> output {new OutputFormat{output_list[i]}};
-                       _output_formats.insert(make_pair(output->name(), move(output)));
+                       _output_formats.emplace(output->name(), move(output));
                }
 }
 
@@ -157,11 +158,10 @@ string Context::lib_version()
 map<string, shared_ptr<Driver>> Context::drivers()
 {
        map<string, shared_ptr<Driver>> result;
-       for (const auto &entry: _drivers)
-       {
+       for (const auto &entry: _drivers) {
                const auto &name = entry.first;
                const auto &driver = entry.second;
-               result.insert({name, driver->share_owned_by(shared_from_this())});
+               result.emplace(name, driver->share_owned_by(shared_from_this()));
        }
        return result;
 }
@@ -169,23 +169,47 @@ map<string, shared_ptr<Driver>> Context::drivers()
 map<string, shared_ptr<InputFormat>> Context::input_formats()
 {
        map<string, shared_ptr<InputFormat>> result;
-       for (const auto &entry: _input_formats)
-       {
+       for (const auto &entry: _input_formats) {
                const auto &name = entry.first;
                const auto &input_format = entry.second;
-               result.insert({name, input_format->share_owned_by(shared_from_this())});
+               result.emplace(name, input_format->share_owned_by(shared_from_this()));
        }
        return result;
 }
 
+shared_ptr<InputFormat> Context::input_format_match(string filename)
+{
+       const struct sr_input *input;
+       const struct sr_input_module *imod;
+       int rc;
+
+       /*
+        * Have the input module looked up for the specified file.
+        * Failed lookup (or "successful lookup" with an empty result)
+        * are non-fatal. Free the sr_input that was created by the
+        * lookup routine, but grab the input module kind and return an
+        * InputFormat instance to the application. This works because
+        * the application passes a filename, no input data got buffered
+        * in the sr_input that we release.
+        */
+       input = NULL;
+       rc = sr_input_scan_file(filename.c_str(), &input);
+       if (rc != SR_OK)
+               return nullptr;
+       if (!input)
+               return nullptr;
+       imod = sr_input_module_get(input);
+       sr_input_free(input);
+       return shared_ptr<InputFormat>{new InputFormat{imod}, default_delete<InputFormat>{}};
+}
+
 map<string, shared_ptr<OutputFormat>> Context::output_formats()
 {
        map<string, shared_ptr<OutputFormat>> result;
-       for (const auto &entry: _output_formats)
-       {
+       for (const auto &entry: _output_formats) {
                const auto &name = entry.first;
                const auto &output_format = entry.second;
-               result.insert({name, output_format->share_owned_by(shared_from_this())});
+               result.emplace(name, output_format->share_owned_by(shared_from_this()));
        }
        return result;
 }
@@ -213,12 +237,9 @@ static int call_log_callback(void *cb_data, int loglevel,
 
        auto *const callback = static_cast<LogCallbackFunction *>(cb_data);
 
-       try
-       {
+       try {
                (*callback)(LogLevel::get(loglevel), message.get());
-       }
-       catch (Error e)
-       {
+       } catch (Error &e) {
                return e.result;
        }
 
@@ -281,8 +302,7 @@ shared_ptr<Packet> Context::create_meta_packet(
        map<const ConfigKey *, Glib::VariantBase> config)
 {
        auto meta = g_new0(struct sr_datafeed_meta, 1);
-       for (const auto &input : config)
-       {
+       for (const auto &input : config) {
                const auto &key = input.first;
                const auto &value = input.second;
                auto *const output = g_new(struct sr_config, 1);
@@ -312,7 +332,7 @@ shared_ptr<Packet> Context::create_logic_packet(
 
 shared_ptr<Packet> Context::create_analog_packet(
        vector<shared_ptr<Channel> > channels,
-       float *data_pointer, unsigned int num_samples, const Quantity *mq,
+       const float *data_pointer, unsigned int num_samples, const Quantity *mq,
        const Unit *unit, vector<const QuantityFlag *> mqflags)
 {
        auto analog = g_new0(struct sr_datafeed_analog, 1);
@@ -350,7 +370,7 @@ shared_ptr<Packet> Context::create_analog_packet(
        spec->spec_digits = 0;
 
        analog->num_samples = num_samples;
-       analog->data = data_pointer;
+       analog->data = (float*)data_pointer;
        auto packet = g_new(struct sr_datafeed_packet, 1);
        packet->type = SR_DF_ANALOG;
        packet->payload = analog;
@@ -446,16 +466,14 @@ vector<shared_ptr<HardwareDevice>> Driver::scan(
        map<const ConfigKey *, Glib::VariantBase> options)
 {
        /* Initialise the driver if not yet done. */
-       if (!_initialized)
-       {
+       if (!_initialized) {
                check(sr_driver_init(_parent->_structure, _structure));
                _initialized = true;
        }
 
        /* Translate scan options to GSList of struct sr_config pointers. */
        GSList *option_list = nullptr;
-       for (const auto &entry : options)
-       {
+       for (const auto &entry : options) {
                const auto &key = entry.first;
                const auto &value = entry.second;
                auto *const config = g_new(struct sr_config, 1);
@@ -473,8 +491,7 @@ vector<shared_ptr<HardwareDevice>> Driver::scan(
 
        /* Create device objects. */
        vector<shared_ptr<HardwareDevice>> result;
-       for (GSList *device = device_list; device; device = device->next)
-       {
+       for (GSList *device = device_list; device; device = device->next) {
                auto *const sdi = static_cast<struct sr_dev_inst *>(device->data);
                shared_ptr<HardwareDevice> hwdev {
                        new HardwareDevice{shared_from_this(), sdi},
@@ -570,23 +587,22 @@ Device::Device(struct sr_dev_inst *structure) :
        Configurable(sr_dev_inst_driver_get(structure), structure, nullptr),
        _structure(structure)
 {
-       for (GSList *entry = sr_dev_inst_channels_get(structure); entry; entry = entry->next)
-       {
+       for (GSList *entry = sr_dev_inst_channels_get(structure); entry; entry = entry->next) {
                auto *const ch = static_cast<struct sr_channel *>(entry->data);
                unique_ptr<Channel> channel {new Channel{ch}};
-               _channels.insert(make_pair(ch, move(channel)));
+               _channels.emplace(ch, move(channel));
        }
 
-       for (GSList *entry = sr_dev_inst_channel_groups_get(structure); entry; entry = entry->next)
-       {
+       for (GSList *entry = sr_dev_inst_channel_groups_get(structure); entry; entry = entry->next) {
                auto *const cg = static_cast<struct sr_channel_group *>(entry->data);
                unique_ptr<ChannelGroup> group {new ChannelGroup{this, cg}};
-               _channel_groups.insert(make_pair(group->name(), move(group)));
+               _channel_groups.emplace(group->name(), move(group));
        }
 }
 
 Device::~Device()
-{}
+{
+}
 
 string Device::vendor() const
 {
@@ -632,11 +648,10 @@ map<string, shared_ptr<ChannelGroup>>
 Device::channel_groups()
 {
        map<string, shared_ptr<ChannelGroup>> result;
-       for (const auto &entry: _channel_groups)
-       {
+       for (const auto &entry: _channel_groups) {
                const auto &name = entry.first;
                const auto &channel_group = entry.second;
-               result.insert({name, channel_group->share_owned_by(get_shared_from_this())});
+               result.emplace(name, channel_group->share_owned_by(get_shared_from_this()));
        }
        return result;
 }
@@ -695,7 +710,7 @@ shared_ptr<Channel> UserDevice::add_channel(unsigned int index,
        GSList *const last = g_slist_last(sr_dev_inst_channels_get(Device::_structure));
        auto *const ch = static_cast<struct sr_channel *>(last->data);
        unique_ptr<Channel> channel {new Channel{ch}};
-       _channels.insert(make_pair(ch, move(channel)));
+       _channels.emplace(ch, move(channel));
        return get_channel(ch);
 }
 
@@ -918,7 +933,7 @@ Session::Session(shared_ptr<Context> context, string filename) :
        for (GSList *dev = dev_list; dev; dev = dev->next) {
                auto *const sdi = static_cast<struct sr_dev_inst *>(dev->data);
                unique_ptr<SessionDevice> device {new SessionDevice{sdi}};
-               _owned_devices.insert(make_pair(sdi, move(device)));
+               _owned_devices.emplace(sdi, move(device));
        }
        _context->_session = this;
 }
@@ -1299,6 +1314,48 @@ vector<const QuantityFlag *> Analog::mq_flags() const
        return QuantityFlag::flags_from_mask(_structure->meaning->mqflags);
 }
 
+shared_ptr<Logic> Analog::get_logic_via_threshold(float threshold,
+       uint8_t *data_ptr) const
+{
+       auto datafeed = g_new(struct sr_datafeed_logic, 1);
+       datafeed->length = num_samples();
+       datafeed->unitsize = 1;
+
+       if (data_ptr)
+               datafeed->data = data_ptr;
+       else
+               datafeed->data = g_malloc(datafeed->length);
+
+       shared_ptr<Logic> logic =
+               shared_ptr<Logic>{new Logic{datafeed}, default_delete<Logic>{}};
+
+       check(sr_a2l_threshold(_structure, threshold,
+               (uint8_t*)datafeed->data, datafeed->length));
+
+       return logic;
+}
+
+shared_ptr<Logic> Analog::get_logic_via_schmitt_trigger(float lo_thr,
+       float hi_thr, uint8_t *state, uint8_t *data_ptr) const
+{
+       auto datafeed = g_new(struct sr_datafeed_logic, 1);
+       datafeed->length = num_samples();
+       datafeed->unitsize = 1;
+
+       if (data_ptr)
+               datafeed->data = data_ptr;
+       else
+               datafeed->data = g_malloc(datafeed->length);
+
+       shared_ptr<Logic> logic =
+               shared_ptr<Logic>{new Logic{datafeed}, default_delete<Logic>{}};
+
+       check(sr_a2l_schmitt_trigger(_structure, lo_thr, hi_thr, state,
+               (uint8_t*)datafeed->data, datafeed->length));
+
+       return logic;
+}
+
 Rational::Rational(const struct sr_rational *structure) :
        _structure(structure)
 {
@@ -1361,15 +1418,14 @@ map<string, shared_ptr<Option>> InputFormat::options()
 {
        map<string, shared_ptr<Option>> result;
 
-       if (const struct sr_option **options = sr_input_options_get(_structure))
-       {
+       if (const struct sr_option **options = sr_input_options_get(_structure)) {
                shared_ptr<const struct sr_option *> option_array
                        {options, &sr_input_options_free};
                for (int i = 0; options[i]; i++) {
                        shared_ptr<Option> opt {
                                new Option{options[i], option_array},
                                default_delete<Option>{}};
-                       result.insert({opt->id(), move(opt)});
+                       result.emplace(opt->id(), move(opt));
                }
        }
        return result;
@@ -1392,8 +1448,7 @@ Input::Input(shared_ptr<Context> context, const struct sr_input *structure) :
 
 shared_ptr<InputDevice> Input::device()
 {
-       if (!_device)
-       {
+       if (!_device) {
                auto sdi = sr_input_dev_inst_get(_structure);
                if (!sdi)
                        throw Error(SR_ERR_NA);
@@ -1483,6 +1538,28 @@ vector<Glib::VariantBase> Option::values() const
        return result;
 }
 
+Glib::VariantBase Option::parse_string(string value)
+{
+       enum sr_datatype dt;
+       Glib::VariantBase dflt = default_value();
+       GVariant *tmpl = dflt.gobj();
+
+       if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_UINT64)) {
+               dt = SR_T_UINT64;
+       } else if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_STRING)) {
+               dt = SR_T_STRING;
+       } else if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_BOOLEAN)) {
+               dt = SR_T_BOOL;
+       } else if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_DOUBLE)) {
+               dt = SR_T_FLOAT;
+       } else if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_INT32)) {
+               dt = SR_T_INT32;
+       } else {
+               throw Error(SR_ERR_BUG);
+       }
+       return ConfigKey::parse_string(value, dt);
+}
+
 OutputFormat::OutputFormat(const struct sr_output_module *structure) :
        _structure(structure)
 {
@@ -1515,15 +1592,14 @@ map<string, shared_ptr<Option>> OutputFormat::options()
 {
        map<string, shared_ptr<Option>> result;
 
-       if (const struct sr_option **options = sr_output_options_get(_structure))
-       {
+       if (const struct sr_option **options = sr_output_options_get(_structure)) {
                shared_ptr<const struct sr_option *> option_array
                        {options, &sr_output_options_free};
                for (int i = 0; options[i]; i++) {
                        shared_ptr<Option> opt {
                                new Option{options[i], option_array},
                                default_delete<Option>{}};
-                       result.insert({opt->id(), move(opt)});
+                       result.emplace(opt->id(), move(opt));
                }
        }
        return result;
@@ -1579,14 +1655,11 @@ string Output::receive(shared_ptr<Packet> packet)
 {
        GString *out;
        check(sr_output_send(_structure, packet->_structure, &out));
-       if (out)
-       {
+       if (out) {
                auto result = string(out->str, out->str + out->len);
                g_string_free(out, true);
                return result;
-       }
-       else
-       {
+       } else {
                return string();
        }
 }
index b1ab7a5bd6f6e04a06607c80017e8fc0d9473f73..80888af61c5064a81c97110af4e6568740126371 100644 (file)
@@ -252,6 +252,8 @@ public:
        map<string, shared_ptr<Driver> > drivers();
        /** Available input formats, indexed by name. */
        map<string, shared_ptr<InputFormat> > input_formats();
+       /** Lookup the responsible input module for an input file. */
+       shared_ptr<InputFormat> input_format_match(string filename);
        /** Available output formats, indexed by name. */
        map<string, shared_ptr<OutputFormat> > output_formats();
        /** Current log level. */
@@ -283,7 +285,7 @@ public:
        /** Create an analog packet. */
        shared_ptr<Packet> create_analog_packet(
                vector<shared_ptr<Channel> > channels,
-               float *data_pointer, unsigned int num_samples, const Quantity *mq,
+               const float *data_pointer, unsigned int num_samples, const Quantity *mq,
                const Unit *unit, vector<const QuantityFlag *> mqflags);
        /** Load a saved session.
         * @param filename File name string. */
@@ -333,7 +335,7 @@ public:
        set<const Capability *> config_capabilities(const ConfigKey *key) const;
        /** Check whether a configuration capability is supported for a given key.
         * @param key ConfigKey to check.
-     * @param capability Capability to check for. */
+        * @param capability Capability to check for. */
        bool config_check(const ConfigKey *key, const Capability *capability) const;
 protected:
        Configurable(
@@ -758,6 +760,8 @@ private:
        const struct sr_datafeed_logic *_structure;
 
        friend class Packet;
+       friend class Analog;
+       friend struct std::default_delete<Logic>;
 };
 
 /** Payload of a datafeed packet with analog data */
@@ -803,6 +807,34 @@ public:
        const Unit *unit() const;
        /** Measurement flags associated with the samples in this packet. */
        vector<const QuantityFlag *> mq_flags() const;
+       /**
+        * Provides a Logic packet that contains a conversion of the analog
+        * data using a simple threshold.
+        *
+        * @param threshold Threshold to use.
+        * @param data_ptr Pointer to num_samples() bytes where the logic
+        *                 samples are stored. When nullptr, memory for
+        *                 logic->data_pointer() will be allocated and must
+        *                 be freed by the caller.
+        */
+       shared_ptr<Logic> get_logic_via_threshold(float threshold,
+               uint8_t *data_ptr=nullptr) const;
+       /**
+        * Provides a Logic packet that contains a conversion of the analog
+        * data using a Schmitt-Trigger.
+        *
+        * @param lo_thr Low threshold to use (anything below this is low).
+        * @param hi_thr High threshold to use (anything above this is high).
+        * @param state Points to a byte that contains the current state of the
+        *              converter. For best results, set to value of logic
+        *              sample n-1.
+        * @param data_ptr Pointer to num_samples() bytes where the logic
+        *                 samples are stored. When nullptr, memory for
+        *                 logic->data_pointer() will be allocated and must be
+        *                 freed by the caller.
+        */
+       shared_ptr<Logic> get_logic_via_schmitt_trigger(float lo_thr,
+               float hi_thr, uint8_t *state, uint8_t *data_ptr=nullptr) const;
 private:
        explicit Analog(const struct sr_datafeed_analog *structure);
        ~Analog();
@@ -845,7 +877,7 @@ public:
        /** Description of this input format. */
        string description() const;
        /** A list of preferred file name extensions for this file format.
-         * @note This list is a recommendation only. */
+        * @note This list is a recommendation only. */
        vector<string> extensions() const;
        /** Options supported by this input format. */
        map<string, shared_ptr<Option> > options();
@@ -917,6 +949,8 @@ public:
        Glib::VariantBase default_value() const;
        /** Possible values for this option, if a limited set. */
        vector<Glib::VariantBase> values() const;
+       /** Parse a string argument into the appropriate type for this option. */
+       Glib::VariantBase parse_string(string value);
 private:
        Option(const struct sr_option *structure,
                shared_ptr<const struct sr_option *> structure_array);
@@ -939,7 +973,7 @@ public:
        /** Description of this output format. */
        string description() const;
        /** A list of preferred file name extensions for this file format.
-         * @note This list is a recommendation only. */
+        * @note This list is a recommendation only. */
        vector<string> extensions() const;
        /** Options supported by this output format. */
        map<string, shared_ptr<Option> > options();
index 2afd9cf131ed6ae7f7977dce357ae881fa4b9a66..69c05a30b808317aa0f11b849c0fbb94bbff9f81 100644 (file)
@@ -45,6 +45,8 @@ which provides access to the error code and description."
 %module(docstring=DOCSTRING) classes
 
 %{
+#include "config.h"
+
 #include <stdio.h>
 #include <pygobject.h>
 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
@@ -53,8 +55,6 @@ which provides access to the error code and description."
 PyObject *PyGObject_lib;
 PyObject *GLib;
 
-#include "config.h"
-
 #if PYGOBJECT_FLAGS_SIGNED
 typedef gint pyg_flags_type;
 #else
index fab2a4df286499bc46560d0908dff7551edba7c4..13496a86bb98fc64fc181300aaf2031b19e2ef76 100644 (file)
@@ -41,10 +41,10 @@ which provides access to the error code and description."
 %module(docstring=DOCSTRING) sigrok
 
 %{
+#include "config.h"
+
 #include <stdio.h>
 #include <glibmm.h>
-
-#include "config.h"
 %}
 
 %include "../swig/templates.i"
index 8282434bfd1c4f8e2cdf9abe0807b05c252ac6b1..95f9f7f4d72c3d297e9a4a365ea6c8e1310f7ecc 100644 (file)
@@ -99,8 +99,7 @@ SR_EXTRA_CXX_LIBS=
 SR_ARG_OPT_PKG([libserialport], [LIBSERIALPORT], [NEED_SERIAL],
        [libserialport >= 0.1.1])
 
-SR_ARG_OPT_PKG([libftdi], [LIBFTDI],,
-       [libftdi1 >= 1.0], [libftdi >= 0.16])
+SR_ARG_OPT_PKG([libftdi], [LIBFTDI], , [libftdi1 >= 1.0])
 
 # FreeBSD comes with an "integrated" libusb-1.0-style USB API.
 # This means libusb-1.0 is always available; no need to check for it.
@@ -234,18 +233,24 @@ SR_DRIVER([ChronoVu LA], [chronovu-la], [libusb libftdi])
 SR_DRIVER([Colead SLM], [colead-slm], [libserialport])
 SR_DRIVER([Conrad DIGI 35 CPU], [conrad-digi-35-cpu], [libserialport])
 SR_DRIVER([demo], [demo])
+SR_DRIVER([DreamSourceLab DSLogic], [dreamsourcelab-dslogic], [libusb])
+SR_DRIVER([Fluke 45], [fluke-45])
 SR_DRIVER([Fluke DMM], [fluke-dmm], [libserialport])
 SR_DRIVER([FTDI LA], [ftdi-la], [libusb libftdi])
 SR_DRIVER([fx2lafw], [fx2lafw], [libusb])
 SR_DRIVER([GMC MH 1x/2x], [gmc-mh-1x-2x], [libserialport])
 SR_DRIVER([GW Instek GDS-800], [gwinstek-gds-800], [libserialport])
+SR_DRIVER([GW Instek GPD], [gwinstek-gpd], [libserialport])
 SR_DRIVER([Hameg HMO], [hameg-hmo], [libserialport])
+SR_DRIVER([Hantek 4032L], [hantek-4032l], [libusb])
 SR_DRIVER([Hantek 6xxx], [hantek-6xxx], [libusb])
 SR_DRIVER([Hantek DSO], [hantek-dso], [libusb])
 SR_DRIVER([HP 3457A], [hp-3457a])
+SR_DRIVER([HP 3478A], [hp-3478a], [libgpib])
 SR_DRIVER([Hung-Chang DSO-2100], [hung-chang-dso-2100], [libieee1284])
 SR_DRIVER([Ikalogic Scanalogic-2], [ikalogic-scanalogic2], [libusb])
 SR_DRIVER([Ikalogic Scanaplus], [ikalogic-scanaplus], [libftdi])
+SR_DRIVER([IPDBG LA], [ipdbg-la])
 SR_DRIVER([Kecheng KC-330B], [kecheng-kc-330b], [libusb])
 SR_DRIVER([KERN scale], [kern-scale], [libserialport])
 SR_DRIVER([Korad KAxxxxP], [korad-kaxxxxp], [libserialport])
@@ -260,12 +265,15 @@ SR_DRIVER([Norma DMM], [norma-dmm], [libserialport])
 SR_DRIVER([OpenBench Logic Sniffer], [openbench-logic-sniffer], [libserialport])
 SR_DRIVER([PCE PCE-322A], [pce-322a], [libserialport])
 SR_DRIVER([Pipistrello-OLS], [pipistrello-ols], [libftdi])
+SR_DRIVER([RDTech DPSxxxx/DPHxxxx], [rdtech-dps], [libserialport])
 SR_DRIVER([Rigol DS], [rigol-ds])
 SR_DRIVER([Rohde&Schwarz SME-0x], [rohde-schwarz-sme-0x], [libserialport])
 SR_DRIVER([Saleae Logic16], [saleae-logic16], [libusb])
+SR_DRIVER([Saleae Logic Pro], [saleae-logic-pro], [libusb])
 SR_DRIVER([SCPI PPS], [scpi-pps])
 SR_DRIVER([serial DMM], [serial-dmm], [libserialport])
 SR_DRIVER([serial LCR], [serial-lcr], [libserialport])
+SR_DRIVER([Siglent SDS], [siglent-sds])
 SR_DRIVER([Sysclk LWLA], [sysclk-lwla], [libusb])
 SR_DRIVER([Teleinfo], [teleinfo], [libserialport])
 SR_DRIVER([Testo], [testo], [libusb])
@@ -275,6 +283,7 @@ SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [libusb])
 SR_DRIVER([Victor DMM], [victor-dmm], [libusb])
 SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm])
 SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb])
+SR_DRIVER([ZKETECH EBD-USB], [zketech-ebd-usb], [libserialport])
 
 ###############################
 ##  Language bindings setup  ##
@@ -604,3 +613,24 @@ Enabled language bindings:
  - Java............................ $BINDINGS_JAVA$sr_report_java
 
 _EOF
+
+# Emit a warning if the C++ bindings are not being built.
+AM_COND_IF([BINDINGS_CXX], [], [
+cat >&AS_MESSAGE_FD <<_EOF
+===============================================================================
+WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+===============================================================================
+===                                                                         ===
+===         The libsigrok C++ bindings are not being built since you        ===
+===             are missing one or more dependencies (see above)!           ===
+===                                                                         ===
+===      This means you won't be able to compile frontends that require     ===
+===  the C++ bindings (such as PulseView)! You also won't be able to build  ===
+===     other bindings and frontends using those (such as sigrok-meter)!    ===
+===                                                                         ===
+===============================================================================
+WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+===============================================================================
+
+_EOF
+])
diff --git a/contrib/60-libsigrok.rules b/contrib/60-libsigrok.rules
new file mode 100644 (file)
index 0000000..5446834
--- /dev/null
@@ -0,0 +1,298 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2010-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, see <http://www.gnu.org/licenses/>.
+##
+
+#
+# These rules do not grant any permission by itself, but flag devices
+# supported by libsigrok.
+# The access policy is stored in the 61-libsigrok-plugdev.rules and
+# 61-libsigrok-uaccess.rules.
+#
+# Note: Any syntax changes here will need to be tested against the
+# 'update-device-filter' Makefile target in the sigrok-androidutils
+# repo, since that parses this file.
+#
+
+#
+# Please keep this list sorted alphabetically by vendor/device name.
+#
+
+ACTION!="add|change", GOTO="libsigrok_rules_end"
+SUBSYSTEM!="usb|usbmisc|usb_device", GOTO="libsigrok_rules_end"
+
+# Agilent USBTMC-connected devices
+# 34405A
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0618", ENV{ID_SIGROK}="1"
+# 34410A
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0607", ENV{ID_SIGROK}="1"
+# DSO1000 series
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", ENV{ID_SIGROK}="1"
+# MSO7000A series
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="1735", ENV{ID_SIGROK}="1"
+
+# ASIX SIGMA
+# ASIX SIGMA2
+ATTRS{idVendor}=="a600", ATTRS{idProduct}=="a000", ENV{ID_SIGROK}="1"
+
+# Braintechnology USB-LPS
+ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0498", ENV{ID_SIGROK}="1"
+
+# Brymen BU-86X adapter (e.g. for Brymen BM867/BM869 and possibly others).
+ATTRS{idVendor}=="0820", ATTRS{idProduct}=="0001", ENV{ID_SIGROK}="1"
+
+# CEM DT-8852
+ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ENV{ID_SIGROK}="1"
+
+# ChronoVu LA8 (new VID/PID)
+# ChronoVu LA16 (new VID/PID)
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8867", ENV{ID_SIGROK}="1"
+
+# CWAV USBee AX
+# ARMFLY AX-Pro (clone of the CWAV USBee AX)
+# ARMFLY Mini-Logic (clone of the CWAV USBee AX)
+# EE Electronics ESLA201A (clone of the CWAV USBee AX)
+# HT USBee-AxPro (clone of the CWAV USBee AX)
+# MCU123 USBee AX Pro clone (clone of the CWAV USBee AX)
+# Noname LHT00SU1 (clone of the CWAV USBee AX)
+# XZL_Studio AX (clone of the CWAV USBee AX)
+ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0014", ENV{ID_SIGROK}="1"
+
+# CWAV USBee DX
+# HT USBee-DxPro (clone of the CWAV USBee DX), not yet supported!
+# XZL_Studio DX (clone of the CWAV USBee DX)
+ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0015", ENV{ID_SIGROK}="1"
+
+# CWAV USBee SX
+ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0009", ENV{ID_SIGROK}="1"
+
+# CWAV USBee ZX
+ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0005", ENV{ID_SIGROK}="1"
+
+# Cypress FX2 eval boards without EEPROM:
+# Lcsoft Mini Board
+# Braintechnology USB Interface V2.x
+# fx2grok-tiny
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", ENV{ID_SIGROK}="1"
+
+# Dangerous Prototypes Buspirate (v3)
+# ChronoVu LA8 (old VID/PID)
+# ChronoVu LA16 (old VID/PID)
+# ftdi-la
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ENV{ID_SIGROK}="1"
+
+# Dangerous Prototypes Buspirate (v4)
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00", ENV{ID_SIGROK}="1"
+
+# DreamSourceLab DSLogic
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0001", ENV{ID_SIGROK}="1"
+# DreamSourceLab DSLogic Pro
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0003", ENV{ID_SIGROK}="1"
+# DreamSourceLab DScope
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0002", ENV{ID_SIGROK}="1"
+# DreamSourceLab DSLogic Plus
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0020", ENV{ID_SIGROK}="1"
+# DreamSourceLab DSLogic Basic
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0021", ENV{ID_SIGROK}="1"
+
+# HAMEG HO720
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed72", ENV{ID_SIGROK}="1"
+
+# HAMEG HO730
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed73", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-2090
+# lsusb: "04b4:2090 Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:2090 ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2090", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2090", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-2150
+# lsusb: "04b4:2150 Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:2150 ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2150", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2150", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-2250
+# lsusb: "04b4:2250 Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:2250 ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2250", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2250", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-5200
+# lsusb: "04b4:5200 Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:5200 ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="5200", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="5200", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-5200A
+# lsusb: "04b4:520a Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:520a ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="520a", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="520a", ENV{ID_SIGROK}="1"
+
+# Hantek 6022BE, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Hantek 6022BE
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6022", ENV{ID_SIGROK}="1"
+
+# Hantek 6022BL, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Hantek 6022BL
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="602a", ENV{ID_SIGROK}="1"
+
+# Hantek 4032L
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="4032", ENV{ID_SIGROK}="1"
+
+# IKALOGIC Scanalogic-2
+ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4123", ENV{ID_SIGROK}="1"
+
+# IKALOGIC ScanaPLUS
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", ENV{ID_SIGROK}="1"
+
+# Kecheng KC-330B
+ATTRS{idVendor}=="1041", ATTRS{idProduct}=="8101", ENV{ID_SIGROK}="1"
+
+# Lascar Electronics EL-USB-2
+# Lascar Electronics EL-USB-CO
+# This is actually the generic SiLabs (Cygnal) F32x USBXpress VID:PID.
+ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="0002", ENV{ID_SIGROK}="1"
+
+# LeCroy LogicStudio16
+ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a001", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a002", ENV{ID_SIGROK}="1"
+
+# Link Instruments MSO-19
+ATTRS{idVendor}=="3195", ATTRS{idProduct}=="f190", ENV{ID_SIGROK}="1"
+
+# Logic Shrimp
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa95", ENV{ID_SIGROK}="1"
+
+# MiniLA Mockup
+# ftdi-la
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ENV{ID_SIGROK}="1"
+
+# MIC 98581
+# MIC 98583
+# Tondaj SL-814
+ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", ENV{ID_SIGROK}="1"
+
+# Openbench Logic Sniffer
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", ENV{ID_SIGROK}="1"
+
+# Rigol DS1000 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0588", ENV{ID_SIGROK}="1"
+
+# Rigol 1000Z series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04ce", ENV{ID_SIGROK}="1"
+
+# Rigol DS2000 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b0", ENV{ID_SIGROK}="1"
+
+# Rigol DS4000 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b1", ENV{ID_SIGROK}="1"
+
+# Rigol DG4000 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0641", ENV{ID_SIGROK}="1"
+
+# Rigol DP800 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0e11", ENV{ID_SIGROK}="1"
+
+# Rohde&Schwarz HMO1002 Series VCP/USBTMC mode
+ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0118", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0119", ENV{ID_SIGROK}="1"
+
+# Sainsmart DDS120 / Rocktech BM102, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Sainsmart DDS120
+ATTRS{idVendor}=="8102", ATTRS{idProduct}=="8102", ENV{ID_SIGROK}="1"
+
+# Saleae Logic
+# EE Electronics ESLA100 (clone of the Saleae Logic)
+# Hantek 6022BL in LA mode (clone of the Saleae Logic)
+# Instrustar ISDS205X in LA mode (clone of the Saleae Logic)
+# Robomotic MiniLogic (clone of the Saleae Logic)
+# Robomotic BugLogic 3 (clone of the Saleae Logic)
+# MCU123 Saleae Logic clone (clone of the Saleae Logic)
+ATTRS{idVendor}=="0925", ATTRS{idProduct}=="3881", ENV{ID_SIGROK}="1"
+
+# Saleae Logic16
+ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1001", ENV{ID_SIGROK}="1"
+
+# Saleae Logic Pro 16
+ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1006", ENV{ID_SIGROK}="1"
+
+# Siglent USBTMC devices.
+# f4ec:ee3a: E.g. SDS1052DL+ scope
+# f4ed:ee3a: E.g. SDS1202X-E scope or SDG1010 waveform generator
+ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="f4ed", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1"
+
+# sigrok FX2 LA (8ch)
+# fx2grok-flat (before and after renumeration)
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608c", ENV{ID_SIGROK}="1"
+
+# sigrok FX2 LA (16ch)
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608d", ENV{ID_SIGROK}="1"
+
+# sigrok FX2 DSO (2ch)
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", ENV{ID_SIGROK}="1"
+
+# sigrok usb-c-grok
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608f", ENV{ID_SIGROK}="1"
+
+# SysClk LWLA1016
+ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6688", ENV{ID_SIGROK}="1"
+
+# SysClk LWLA1034
+ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6689", ENV{ID_SIGROK}="1"
+
+# Testo 435
+ATTRS{idVendor}=="128d", ATTRS{idProduct}=="0003", ENV{ID_SIGROK}="1"
+
+# UNI-T UT-D04 multimeter cable (for various UNI-T and rebranded DMMs)
+# http://sigrok.org/wiki/Device_cables#UNI-T_UT-D04
+# UNI-T UT325
+ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="e008", ENV{ID_SIGROK}="1"
+
+# V&A VA4000 multimeter cable (for various V&A DMMs)
+# http://sigrok.org/wiki/Device_cables#V.26A_VA4000
+ATTRS{idVendor}=="04fc", ATTRS{idProduct}=="0201", ENV{ID_SIGROK}="1"
+
+# Victor 70C
+# Victor 86C
+ATTRS{idVendor}=="1244", ATTRS{idProduct}=="d237", ENV{ID_SIGROK}="1"
+
+# ZEROPLUS Logic Cube LAP-C series
+# There are various devices in the ZEROPLUS Logic Cube series:
+# 0c12:7002: LAP-16128U
+# 0c12:7009: LAP-C(16064)
+# 0c12:700a: LAP-C(16128)
+# 0c12:700b: LAP-C(32128)
+# 0c12:700c: LAP-C(321000)
+# 0c12:700d: LAP-C(322000)
+# 0c12:700e: LAP-C(16032)
+# 0c12:7016: LAP-C(162000)
+# 0c12:7025: LAP-C(16128+)
+# 0c12:7100: AKIP-9101
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7002", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7007", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7009", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700a", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700b", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700c", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700d", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700e", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7016", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7025", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7100", ENV{ID_SIGROK}="1"
+
+LABEL="libsigrok_rules_end"
diff --git a/contrib/61-libsigrok-plugdev.rules b/contrib/61-libsigrok-plugdev.rules
new file mode 100644 (file)
index 0000000..2fddba4
--- /dev/null
@@ -0,0 +1,31 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2017 Stefan Bruens <stefan.bruens@rwth-aachen.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, see <http://www.gnu.org/licenses/>.
+##
+
+# Grant access permissions to users who are in the "plugdev" group.
+# "plugdev" is typically used on Debian based distributions and may not
+# exist elsewhere.
+#
+# This file, when installed, must be installed with a name lexicographically
+# sorted later than the accompanied 60-libsigrok.rules
+
+ACTION!="add|change", GOTO="libsigrok_rules_plugdev_end"
+
+ENV{ID_SIGROK}=="1", MODE="660", GROUP="plugdev"
+
+LABEL="libsigrok_rules_plugdev_end"
diff --git a/contrib/61-libsigrok-uaccess.rules b/contrib/61-libsigrok-uaccess.rules
new file mode 100644 (file)
index 0000000..c6bc51a
--- /dev/null
@@ -0,0 +1,32 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2017 Stefan Bruens <stefan.bruens@rwth-aachen.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, see <http://www.gnu.org/licenses/>.
+##
+
+# Grant access permissions to users who are currently logged in locally.
+# This is the default policy for systems using systemd-logind (or a
+# compatible replacement).
+#
+# This file, when installed, must be installed with a name lexicographically
+# sorted later than the accompanied 60-libsigrok.rules, and earlier than
+# the systemd upstream 71-seat.rules.
+
+ACTION!="add|change", GOTO="libsigrok_rules_uaccess_end"
+
+ENV{ID_SIGROK}=="1", TAG+="uaccess"
+
+LABEL="libsigrok_rules_uaccess_end"
diff --git a/contrib/z60_libsigrok.rules b/contrib/z60_libsigrok.rules
deleted file mode 100644 (file)
index 0dff435..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-##
-## This file is part of the libsigrok project.
-##
-## Copyright (C) 2010-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, see <http://www.gnu.org/licenses/>.
-##
-
-# NOTE: On a systemd based system, using tag=uaccess, this file _must_ be
-# named/numbered to come before the seat rules are applied in 70-xxx.
-
-##
-## Please keep this list sorted alphabetically by vendor/device name.
-##
-
-ACTION!="add|change", GOTO="libsigrok_rules_end"
-SUBSYSTEM!="usb|usbmisc|usb_device", GOTO="libsigrok_rules_end"
-
-# Agilent USBTMC-connected devices
-# 34405A
-ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0618", MODE="660", GROUP="plugdev", TAG+="uaccess"
-# 34410A
-ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0607", MODE="660", GROUP="plugdev", TAG+="uaccess"
-# DSO1000 series
-ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# ASIX SIGMA
-# ASIX SIGMA2
-ATTRS{idVendor}=="a600", ATTRS{idProduct}=="a000", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Braintechnology USB-LPS
-ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0498", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# CEM DT-8852
-ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# ChronoVu LA8 (new VID/PID)
-# ChronoVu LA16 (new VID/PID)
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8867", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# CWAV USBee AX
-# ARMFLY AX-Pro (clone of the CWAV USBee AX)
-# ARMFLY Mini-Logic (clone of the CWAV USBee AX)
-# EE Electronics ESLA201A (clone of the CWAV USBee AX)
-# MCU123 USBee AX Pro clone (clone of the CWAV USBee AX)
-# XZL_Studio AX (clone of the CWAV USBee AX)
-ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0014", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# CWAV USBee DX
-# XZL_Studio DX (clone of the CWAV USBee DX)
-ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0015", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# CWAV USBee SX
-ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0009", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Cypress FX2 eval boards without EEPROM:
-# Lcsoft Mini Board
-# Braintechnology USB Interface V2.x
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Dangerous Prototypes Buspirate (v3)
-# ChronoVu LA8 (old VID/PID)
-# ChronoVu LA16 (old VID/PID)
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Dangerous Prototypes Buspirate (v4)
-ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# DreamSourceLab DSLogic
-ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0001", MODE="660", GROUP="plugdev", TAG+="uaccess"
-# DreamSourceLab DSLogic Pro
-ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0003", MODE="660", GROUP="plugdev", TAG+="uaccess"
-# DreamSourceLab DScope
-ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0002", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# HAMEG HO720
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed72", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# HAMEG HO730
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed73", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-2090
-# lsusb: "04b4:2090 Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:2090 ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2090", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2090", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-2150
-# lsusb: "04b4:2150 Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:2150 ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2150", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2150", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-2250
-# lsusb: "04b4:2250 Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:2250 ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2250", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2250", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-5200
-# lsusb: "04b4:5200 Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:5200 ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="5200", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="5200", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-5200A
-# lsusb: "04b4:520a Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:520a ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="520a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="520a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek 6022BE
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6022", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek 6022BL
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="602a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# IKALOGIC Scanalogic-2
-ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4123", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# IKALOGIC ScanaPLUS
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Kecheng KC-330B
-ATTRS{idVendor}=="1041", ATTRS{idProduct}=="8101", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Lascar Electronics EL-USB-2
-# Lascar Electronics EL-USB-CO
-# This is actually the generic SiLabs (Cygnal) F32x USBXpress VID:PID.
-ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="0002", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# LeCroy LogicStudio16
-ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a001", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a002", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Link Instruments MSO-19
-ATTRS{idVendor}=="3195", ATTRS{idProduct}=="f190", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Logic Shrimp
-ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa95", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# MiniLA Mockup
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# MIC 98581
-# MIC 98583
-# Tondaj SL-814
-ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Openbench Logic Sniffer
-ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rigol DS1000 series
-ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0588", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rigol 1000Z series
-ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04ce", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rigol DS2000 series
-ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b0", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rigol DG4000 series
-ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0641", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rohde&Schwarz HMO1002 Series VCP/USBTMC mode
-ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0118", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0119", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Sainsmart DDS120 / Rocktech BM102
-ATTRS{idVendor}=="8102", ATTRS{idProduct}=="8102", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Saleae Logic
-# EE Electronics ESLA100 (clone of the Saleae Logic)
-# Robomotic MiniLogic (clone of the Saleae Logic)
-# Robomotic BugLogic 3 (clone of the Saleae Logic)
-# MCU123 Saleae Logic clone (clone of the Saleae Logic)
-ATTRS{idVendor}=="0925", ATTRS{idProduct}=="3881", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Saleae Logic16
-ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1001", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# sigrok FX2 LA (8ch)
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608c", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# sigrok FX2 LA (16ch)
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608d", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# SysClk LWLA1016
-ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6688", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# SysClk LWLA1034
-ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6689", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Testo 435
-ATTRS{idVendor}=="128d", ATTRS{idProduct}=="0003", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# UNI-T UT-D04 multimeter cable (for various UNI-T and rebranded DMMs)
-# http://sigrok.org/wiki/Device_cables#UNI-T_UT-D04
-# UNI-T UT325
-ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="e008", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# V&A VA4000 multimeter cable (for various V&A DMMs)
-# http://sigrok.org/wiki/Device_cables#V.26A_VA4000
-ATTRS{idVendor}=="04fc", ATTRS{idProduct}=="0201", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Victor 70C
-# Victor 86C
-ATTRS{idVendor}=="1244", ATTRS{idProduct}=="d237", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# ZEROPLUS Logic Cube LAP-C series
-# There are various devices in the ZEROPLUS Logic Cube series:
-# 0c12:7002: LAP-16128U
-# 0c12:7009: LAP-C(16064)
-# 0c12:700a: LAP-C(16128)
-# 0c12:700b: LAP-C(32128)
-# 0c12:700c: LAP-C(321000)
-# 0c12:700d: LAP-C(322000)
-# 0c12:700e: LAP-C(16032)
-# 0c12:7016: LAP-C(162000)
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7002", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7009", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700b", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700c", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700d", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700e", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7016", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-LABEL="libsigrok_rules_end"
index f0ad4d04c855714e8396f1893b3610600224ae8d..a8b118c3809aaf25854981ca830bb02848b57bca 100644 (file)
@@ -84,11 +84,11 @@ enum sr_error_code {
 
 /* Handy little macros */
 #define SR_HZ(n)  (n)
-#define SR_KHZ(n) ((n) * (uint64_t)(1000ULL))
-#define SR_MHZ(n) ((n) * (uint64_t)(1000000ULL))
-#define SR_GHZ(n) ((n) * (uint64_t)(1000000000ULL))
+#define SR_KHZ(n) ((n) * UINT64_C(1000))
+#define SR_MHZ(n) ((n) * UINT64_C(1000000))
+#define SR_GHZ(n) ((n) * UINT64_C(1000000000))
 
-#define SR_HZ_TO_NS(n) ((uint64_t)(1000000000ULL) / (n))
+#define SR_HZ_TO_NS(n) (UINT64_C(1000000000) / (n))
 
 /** libsigrok loglevels. */
 enum sr_loglevel {
@@ -650,11 +650,11 @@ struct sr_key_info {
 /** Configuration capabilities. */
 enum sr_configcap {
        /** Value can be read. */
-       SR_CONF_GET = (1 << 31),
+       SR_CONF_GET = (1UL << 31),
        /** Value can be written. */
-       SR_CONF_SET = (1 << 30),
+       SR_CONF_SET = (1UL << 30),
        /** Possible values can be enumerated. */
-       SR_CONF_LIST = (1 << 29),
+       SR_CONF_LIST = (1UL << 29),
 };
 
 /** Configuration keys */
@@ -703,6 +703,9 @@ enum sr_configkey {
        /** The device can act as a function generator. */
        SR_CONF_SIGNAL_GENERATOR,
 
+       /** The device can measure power. */
+       SR_CONF_POWERMETER,
+
        /* Update sr_key_info_config[] (hwdriver.c) upon changes! */
 
        /*--- Driver scan options -------------------------------------------*/
@@ -983,6 +986,15 @@ enum sr_configkey {
        /** Trigger level. */
        SR_CONF_TRIGGER_LEVEL,
 
+       /** Under-voltage condition threshold. */
+       SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD,
+
+       /**
+        * Which external clock source to use if the device supports
+        * multiple external clock channels.
+        */
+       SR_CONF_EXTERNAL_CLOCK_SOURCE,
+
        /* Update sr_key_info_config[] (hwdriver.c) upon changes! */
 
        /*--- Special stuff -------------------------------------------------*/
index 48c12c63bcf52f421f42429952e4bab8fe1a5a84..41d9cfcc672931f62bac06a648d24a171bcceb76 100644 (file)
@@ -50,6 +50,14 @@ SR_API GSList *sr_buildinfo_libs_get(void);
 SR_API char *sr_buildinfo_host_get(void);
 SR_API char *sr_buildinfo_scpi_backends_get(void);
 
+/*--- conversion.c ----------------------------------------------------------*/
+
+SR_API int sr_a2l_threshold(const struct sr_datafeed_analog *analog,
+               float threshold, uint8_t *output, uint64_t count);
+SR_API int sr_a2l_schmitt_trigger(const struct sr_datafeed_analog *analog,
+               float lo_thr, float hi_thr, uint8_t *state, uint8_t *output,
+               uint64_t count);
+
 /*--- log.c -----------------------------------------------------------------*/
 
 typedef int (*sr_log_callback)(void *cb_data, int loglevel,
@@ -59,6 +67,7 @@ SR_API int sr_log_loglevel_set(int loglevel);
 SR_API int sr_log_loglevel_get(void);
 SR_API int sr_log_callback_set(sr_log_callback cb, void *cb_data);
 SR_API int sr_log_callback_set_default(void);
+SR_API int sr_log_callback_get(sr_log_callback *cb, void **cb_data);
 
 /*--- device.c --------------------------------------------------------------*/
 
@@ -145,6 +154,10 @@ SR_API int sr_session_is_running(struct sr_session *session);
 SR_API int sr_session_stopped_callback_set(struct sr_session *session,
                sr_session_stopped_callback cb, void *cb_data);
 
+SR_API int sr_packet_copy(const struct sr_datafeed_packet *packet,
+               struct sr_datafeed_packet **copy);
+SR_API void sr_packet_free(struct sr_datafeed_packet *packet);
+
 /*--- input/input.c ---------------------------------------------------------*/
 
 SR_API const struct sr_input_module **sr_input_list(void);
@@ -155,13 +168,12 @@ SR_API const char *const *sr_input_extensions_get(
                const struct sr_input_module *imod);
 SR_API const struct sr_input_module *sr_input_find(char *id);
 SR_API const struct sr_option **sr_input_options_get(const struct sr_input_module *imod);
-SR_API gboolean sr_output_test_flag(const struct sr_output_module *omod,
-               uint64_t flag);
 SR_API void sr_input_options_free(const struct sr_option **options);
 SR_API struct sr_input *sr_input_new(const struct sr_input_module *imod,
                GHashTable *options);
 SR_API int sr_input_scan_buffer(GString *buf, const struct sr_input **in);
 SR_API int sr_input_scan_file(const char *filename, const struct sr_input **in);
+SR_API const struct sr_input_module *sr_input_module_get(const struct sr_input *in);
 SR_API struct sr_dev_inst *sr_input_dev_inst_get(const struct sr_input *in);
 SR_API int sr_input_send(const struct sr_input *in, GString *buf);
 SR_API int sr_input_end(const struct sr_input *in);
@@ -182,6 +194,8 @@ SR_API void sr_output_options_free(const struct sr_option **opts);
 SR_API const struct sr_output *sr_output_new(const struct sr_output_module *omod,
                GHashTable *params, const struct sr_dev_inst *sdi,
                const char *filename);
+SR_API gboolean sr_output_test_flag(const struct sr_output_module *omod,
+               uint64_t flag);
 SR_API int sr_output_send(const struct sr_output *o,
                const struct sr_datafeed_packet *packet, GString **out);
 SR_API int sr_output_free(const struct sr_output *o);
@@ -221,6 +235,8 @@ typedef int (*sr_resource_close_callback)(struct sr_resource *res,
 typedef gssize (*sr_resource_read_callback)(const struct sr_resource *res,
                void *buf, size_t count, void *cb_data);
 
+SR_API GSList *sr_resourcepaths_get(int res_type);
+
 SR_API int sr_resource_set_hooks(struct sr_context *ctx,
                sr_resource_open_callback open_cb,
                sr_resource_close_callback close_cb,
@@ -237,6 +253,12 @@ SR_API uint64_t sr_parse_timestring(const char *timestring);
 SR_API gboolean sr_parse_boolstring(const char *boolstring);
 SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q);
 SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q);
+SR_API int sr_sprintf_ascii(char *buf, const char *format, ...);
+SR_API int sr_vsprintf_ascii(char *buf, const char *format, va_list args);
+SR_API int sr_snprintf_ascii(char *buf, size_t buf_size,
+               const char *format, ...);
+SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size,
+               const char *format, va_list args);
 SR_API int sr_parse_rational(const char *str, struct sr_rational *ret);
 
 /*--- version.c -------------------------------------------------------------*/
index 9fdece5df4a82a31333eb0577155b898e4bbea5d..4eda290f1197f162f03e3e54b64330f1f1f3a5d0 100644 (file)
@@ -174,8 +174,7 @@ SR_PRIV int sr_analog_init(struct sr_datafeed_analog *analog,
 SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
                float *outbuf)
 {
-       float offset;
-       unsigned int b, i, count;
+       unsigned int b, count;
        gboolean bigendian;
 
        if (!analog || !(analog->data) || !(analog->meaning)
@@ -275,7 +274,7 @@ SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
                /* The data is already in the right format. */
                memcpy(outbuf, analog->data, count * sizeof(float));
        } else {
-               for (i = 0; i < count; i += analog->encoding->unitsize) {
+               for (unsigned int i = 0; i < count; i += analog->encoding->unitsize) {
                        for (b = 0; b < analog->encoding->unitsize; b++) {
                                if (analog->encoding->is_bigendian == bigendian)
                                        ((uint8_t *)outbuf)[i + b] =
@@ -287,7 +286,7 @@ SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
                        if (analog->encoding->scale.p != 1
                                        || analog->encoding->scale.q != 1)
                                outbuf[i] = (outbuf[i] * analog->encoding->scale.p) / analog->encoding->scale.q;
-                       offset = ((float)analog->encoding->offset.p / (float)analog->encoding->offset.q);
+                       float offset = ((float)analog->encoding->offset.p / (float)analog->encoding->offset.q);
                        outbuf[i] += offset;
                }
        }
@@ -308,7 +307,7 @@ SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
 SR_API const char *sr_analog_si_prefix(float *value, int *digits)
 {
 /** @cond PRIVATE */
-#define NEG_PREFIX_COUNT 5  /* number of prefixes below unity */
+#define NEG_PREFIX_COUNT 5 /* number of prefixes below unity */
 #define POS_PREFIX_COUNT (int)(ARRAY_SIZE(prefixes) - NEG_PREFIX_COUNT - 1)
 /** @endcond */
        static const char *prefixes[] = { "f", "p", "n", "µ", "m", "", "k", "M", "G", "T" };
@@ -366,12 +365,9 @@ SR_API gboolean sr_analog_si_prefix_friendly(enum sr_unit unit)
 
        for (i = 0; i < ARRAY_SIZE(prefix_friendly_units); i++)
                if (unit == prefix_friendly_units[i])
-                       break;
-
-       if (unit != prefix_friendly_units[i])
-               return FALSE;
+                       return TRUE;
 
-       return TRUE;
+       return FALSE;
 }
 
 /**
@@ -555,8 +551,8 @@ SR_API int sr_rational_mult(struct sr_rational *res, const struct sr_rational *a
                return SR_ERR_ARG;
        }
 
-       res->p = (int64_t)(p);
-       res->q = (uint64_t)(q);
+       res->p = (int64_t)p;
+       res->q = (uint64_t)q;
 
        return SR_OK;
 
index beb5a8525b4bff4145d8030a2775a27bbb00ca86..ef5cd260e280d426a20580cc64dbbfc46f4edaa6 100644 (file)
@@ -153,8 +153,14 @@ SR_API GSList *sr_buildinfo_libs_get(void)
        m = g_slist_append(m, g_strdup_printf("%s", CONF_LIBUSB_1_0_VERSION));
 #else
        lv = libusb_get_version();
-       m = g_slist_append(m, g_strdup_printf("%d.%d.%d.%d%s",
-               lv->major, lv->minor, lv->micro, lv->nano, lv->rc));
+       m = g_slist_append(m, g_strdup_printf("%d.%d.%d.%d%s API 0x%08x",
+               lv->major, lv->minor, lv->micro, lv->nano, lv->rc,
+#if defined(LIBUSB_API_VERSION)
+               LIBUSB_API_VERSION
+#elif defined(LIBUSBX_API_VERSION)
+               LIBUSBX_API_VERSION
+#endif
+               ));
 #endif
        l = g_slist_append(l, m);
 #endif
@@ -255,6 +261,17 @@ static void print_versions(void)
        g_free(str);
 }
 
+static void print_resourcepaths(void)
+{
+       GSList *l, *l_orig;
+
+       sr_dbg("Firmware search paths:");
+       l_orig = sr_resourcepaths_get(SR_RESOURCE_FIRMWARE);
+       for (l = l_orig; l; l = l->next)
+               sr_dbg(" - %s", (const char *)l->data);
+       g_slist_free_full(l_orig, g_free);
+}
+
 /**
  * Sanity-check all libsigrok drivers.
  *
@@ -309,7 +326,10 @@ static int sanity_check_all_drivers(const struct sr_context *ctx)
                        sr_err("No dev_list in driver %d ('%s').", i, d);
                        errors++;
                }
-               /* Note: dev_clear() is optional. */
+               if (!drivers[i]->dev_clear) {
+                       sr_err("No dev_clear 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);
@@ -526,6 +546,8 @@ SR_API int sr_init(struct sr_context **ctx)
 
        print_versions();
 
+       print_resourcepaths();
+
        if (!ctx) {
                sr_err("%s(): libsigrok context was NULL.", __func__);
                return SR_ERR;
@@ -537,22 +559,22 @@ SR_API int sr_init(struct sr_context **ctx)
 
        if (sanity_check_all_drivers(context) < 0) {
                sr_err("Internal driver error(s), aborting.");
-               return ret;
+               goto done;
        }
 
        if (sanity_check_all_input_modules() < 0) {
                sr_err("Internal input module error(s), aborting.");
-               return ret;
+               goto done;
        }
 
        if (sanity_check_all_output_modules() < 0) {
                sr_err("Internal output module error(s), aborting.");
-               return ret;
+               goto done;
        }
 
        if (sanity_check_all_transform_modules() < 0) {
                sr_err("Internal transform module error(s), aborting.");
-               return ret;
+               goto done;
        }
 
 #ifdef _WIN32
@@ -577,9 +599,7 @@ SR_API int sr_init(struct sr_context **ctx)
        context = NULL;
        ret = SR_OK;
 
-#if defined(HAVE_LIBUSB_1_0) || defined(_WIN32)
 done:
-#endif
        g_free(context);
        return ret;
 }
diff --git a/src/conversion.c b/src/conversion.c
new file mode 100644 (file)
index 0000000..3d96930
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Soeren Apel <soeren@apelpie.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 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/>.
+ */
+
+/**
+ * @file
+ *
+ * Conversion helper functions.
+ */
+
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "conv"
+
+/**
+ * Convert analog values to logic values by using a fixed threshold.
+ *
+ * @param[in] analog The analog input values.
+ * @param[in] threshold The threshold to use.
+ * @param[out] output The converted output values; either 0 or 1. Must provide
+ *                    space for count bytes.
+ * @param[in] count The number of samples to process.
+ *
+ * @return SR_OK on success or SR_ERR on failure.
+ */
+SR_API int sr_a2l_threshold(const struct sr_datafeed_analog *analog,
+               float threshold, uint8_t *output, uint64_t count)
+{
+       float *input;
+
+       if (!analog->encoding->is_float) {
+               input = g_try_malloc(sizeof(float) * count);
+               if (!input)
+                       return SR_ERR;
+
+               sr_analog_to_float(analog, input);
+       } else
+               input = analog->data;
+
+       for (uint64_t i = 0; i < count; i++)
+               output[i] = (input[i] >= threshold) ? 1 : 0;
+
+       if (!analog->encoding->is_float)
+               g_free(input);
+
+       return SR_OK;
+}
+
+/**
+ * Convert analog values to logic values by using a Schmitt-trigger algorithm.
+ *
+ * @param analog The analog input values.
+ * @param lo_thr The low threshold - result becomes 0 below it.
+ * @param lo_thr The high threshold - result becomes 1 above it.
+ * @param state The internal converter state. Must contain the state of logic
+ *        sample n-1, will contain the state of logic sample n+count upon exit.
+ * @param output The converted output values; either 0 or 1. Must provide
+ *        space for count bytes.
+ * @param count The number of samples to process.
+ *
+ * @return SR_OK on success or SR_ERR on failure.
+ */
+SR_API int sr_a2l_schmitt_trigger(const struct sr_datafeed_analog *analog,
+               float lo_thr, float hi_thr, uint8_t *state, uint8_t *output,
+               uint64_t count)
+{
+       float *input;
+
+       if (!analog->encoding->is_float) {
+               input = g_try_malloc(sizeof(float) * count);
+               if (!input)
+                       return SR_ERR;
+
+               sr_analog_to_float(analog, input);
+       } else
+               input = analog->data;
+
+       for (uint64_t i = 0; i < count; i++) {
+               if (input[i] < lo_thr)
+                       *state = 0;
+               else if (input[i] > hi_thr)
+                       *state = 1;
+
+               output[i] = *state;
+       }
+
+       if (!analog->encoding->is_float)
+               g_free(input);
+
+       return SR_OK;
+}
index aa499c8d74252ad07a37cf54afbec3b1233196ce..34d80a9dadf45771b3751c1ba1a5917aba034900 100644 (file)
@@ -18,8 +18,9 @@
  */
 
 #include <config.h>
-#include <stdio.h>
 #include <glib.h>
+#include <stdio.h>
+#include <string.h>
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
@@ -73,6 +74,30 @@ SR_PRIV struct sr_channel *sr_channel_new(struct sr_dev_inst *sdi,
        return ch;
 }
 
+/**
+ * Release a previously allocated struct sr_channel.
+ *
+ * @param[in] ch Pointer to struct sr_channel.
+ *
+ * @private
+ */
+SR_PRIV void sr_channel_free(struct sr_channel *ch)
+{
+       if (!ch)
+               return;
+       g_free(ch->name);
+       g_free(ch->priv);
+       g_free(ch);
+}
+
+/**
+ * Wrapper around @ref sr_channel_free(), suitable for glib iterators.
+ */
+SR_PRIV void sr_channel_free_cb(void *p)
+{
+       sr_channel_free(p);
+}
+
 /**
  * Set the name of the specified channel.
  *
@@ -135,10 +160,18 @@ SR_API int sr_dev_channel_enable(struct sr_channel *channel, gboolean state)
        return SR_OK;
 }
 
-/* Returns the next enabled channel, wrapping around if necessary. */
-/** @private */
+/**
+ * Returns the next enabled channel, wrapping around if necessary.
+ *
+ * @param[in] sdi The device instance the channel is connected to.
+ *                Must not be NULL.
+ * @param[in] cur_channel The current channel.
+ *
+ * @return A pointer to the next enabled channel of this device.
+ *
+ * @private
+ */
 SR_PRIV struct sr_channel *sr_next_enabled_channel(const struct sr_dev_inst *sdi,
-
                struct sr_channel *cur_channel)
 {
        struct sr_channel *next_channel;
@@ -156,6 +189,69 @@ SR_PRIV struct sr_channel *sr_next_enabled_channel(const struct sr_dev_inst *sdi
        return next_channel;
 }
 
+/**
+ * Compare two channels, return whether they differ.
+ *
+ * The channels' names and types are checked. The enabled state is not
+ * considered a condition for difference. The test is motivated by the
+ * desire to detect changes in the configuration of acquisition setups
+ * between re-reads of an input file.
+ *
+ * @param[in] ch1 First channel.
+ * @param[in] ch2 Second channel.
+ *
+ * @return #TRUE upon differences or unexpected input, #FALSE otherwise.
+ *
+ * @internal
+ */
+SR_PRIV gboolean sr_channels_differ(struct sr_channel *ch1, struct sr_channel *ch2)
+{
+       if (!ch1 || !ch2)
+               return TRUE;
+
+       if (ch1->type != ch2->type)
+               return TRUE;
+       if (strcmp(ch1->name, ch2->name))
+               return TRUE;
+
+       return FALSE;
+}
+
+/**
+ * Compare two channel lists, return whether they differ.
+ *
+ * Listing the same set of channels but in a different order is considered
+ * a difference in the lists.
+ *
+ * @param[in] l1 First channel list.
+ * @param[in] l2 Second channel list.
+ *
+ * @return #TRUE upon differences or unexpected input, #FALSE otherwise.
+ *
+ * @internal
+ */
+SR_PRIV gboolean sr_channel_lists_differ(GSList *l1, GSList *l2)
+{
+       struct sr_channel *ch1, *ch2;
+
+       while (l1 && l2) {
+               ch1 = l1->data;
+               ch2 = l2->data;
+               l1 = l1->next;
+               l2 = l2->next;
+               if (!ch1 || !ch2)
+                       return TRUE;
+               if (sr_channels_differ(ch1, ch2))
+                       return TRUE;
+               if (ch1->index != ch2->index)
+                       return TRUE;
+       }
+       if (l1 || l2)
+               return TRUE;
+
+       return FALSE;
+}
+
 /**
  * Determine whether the specified device instance has the specified
  * capability.
@@ -356,9 +452,7 @@ SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi)
 
        for (l = sdi->channels; l; l = l->next) {
                ch = l->data;
-               g_free(ch->name);
-               g_free(ch->priv);
-               g_free(ch);
+               sr_channel_free(ch);
        }
        g_slist_free(sdi->channels);
 
@@ -529,27 +623,40 @@ SR_API GSList *sr_dev_list(const struct sr_dev_driver *driver)
  */
 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(driver);
-       else
-               ret = std_dev_clear(driver, NULL);
+       if (!driver->context) {
+               /*
+                * Driver was never initialized, nothing to do.
+                *
+                * No log message since this usually gets called for all
+                * drivers, whether they were initialized or not.
+                */
+               return SR_OK;
+       }
 
-       return ret;
+       /* No log message here, too verbose and not very useful. */
+
+       return driver->dev_clear(driver);
 }
 
 /**
- * Open the specified device.
+ * Open the specified device instance.
+ *
+ * If the device instance is already open (sdi->status == SR_ST_ACTIVE),
+ * SR_ERR will be returned and no re-opening of the device will be attempted.
+ *
+ * If opening was successful, sdi->status is set to SR_ST_ACTIVE, otherwise
+ * it will be left unchanged.
  *
  * @param sdi Device instance to use. Must not be NULL.
  *
- * @return SR_OK upon success, a negative error code upon errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid arguments.
+ * @retval SR_ERR Device instance was already active, or other error.
  *
  * @since 0.2.0
  */
@@ -558,32 +665,59 @@ SR_API int sr_dev_open(struct sr_dev_inst *sdi)
        int ret;
 
        if (!sdi || !sdi->driver || !sdi->driver->dev_open)
+               return SR_ERR_ARG;
+
+       if (sdi->status == SR_ST_ACTIVE) {
+               sr_err("%s: Device instance already active, can't re-open.",
+                       sdi->driver->name);
                return SR_ERR;
+       }
+
+       sr_dbg("%s: Opening device instance.", sdi->driver->name);
 
        ret = sdi->driver->dev_open(sdi);
 
+       if (ret == SR_OK)
+               sdi->status = SR_ST_ACTIVE;
+
        return ret;
 }
 
 /**
- * Close the specified device.
+ * Close the specified device instance.
+ *
+ * If the device instance is not open (sdi->status != SR_ST_ACTIVE),
+ * SR_ERR_DEV_CLOSED will be returned and no closing will be attempted.
+ *
+ * Note: sdi->status will be set to SR_ST_INACTIVE, regardless of whether
+ * there are any errors during closing of the device instance (any errors
+ * will be reported via error code and log message, though).
  *
  * @param sdi Device instance to use. Must not be NULL.
  *
- * @return SR_OK upon success, a negative error code upon errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid arguments.
+ * @retval SR_ERR_DEV_CLOSED Device instance was not active.
+ * @retval SR_ERR Other error.
  *
  * @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;
+               return SR_ERR_ARG;
 
-       ret = sdi->driver->dev_close(sdi);
+       if (sdi->status != SR_ST_ACTIVE) {
+               sr_err("%s: Device instance not active, can't close.",
+                       sdi->driver->name);
+               return SR_ERR_DEV_CLOSED;
+       }
 
-       return ret;
+       sdi->status = SR_ST_INACTIVE;
+
+       sr_dbg("%s: Closing device instance.", sdi->driver->name);
+
+       return sdi->driver->dev_close(sdi);
 }
 
 /**
@@ -713,7 +847,9 @@ SR_API const char *sr_dev_inst_connid_get(const struct sr_dev_inst *sdi)
                        if (b != usb->bus || a != usb->address)
                                continue;
 
-                       usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+                       if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                               continue;
+
                        ((struct sr_dev_inst *)sdi)->connection_id = g_strdup(connection_id);
                        break;
                }
index 54858ffa60b91737191eeb3c90834b0393eb9d34..f32cbfef36250a7dd9fc315503588e8497ec265f 100644 (file)
@@ -379,7 +379,7 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
        if (info->is_dc)
                analog->meaning->mqflags |= SR_MQFLAG_DC;
        if (info->is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        if (info->is_peak_max)
                analog->meaning->mqflags |= SR_MQFLAG_MAX;
        if (info->is_peak_min)
@@ -519,7 +519,7 @@ SR_PRIV int sr_asycii_parse(const uint8_t *buf, float *floatval,
        int ret, exponent;
        struct asycii_info *info_local;
 
-       info_local = (struct asycii_info *)info;
+       info_local = info;
 
        /* Don't print byte 15. That one contains the carriage return. */
        sr_dbg("DMM packet: \"%.15s\"", buf);
index 385d246e6e53e85bdcf410256b4ffb540c170a89..b838d1ce73b5dda3744118ef69858e974e7229f8 100644 (file)
@@ -173,7 +173,7 @@ SR_PRIV int sr_brymen_bm25x_parse(const uint8_t *buf, float *floatval,
                analog->meaning->mq = SR_MQ_VOLTAGE;
                analog->meaning->unit = SR_UNIT_VOLT;
                if ((analog->meaning->mqflags & (SR_MQFLAG_DC | SR_MQFLAG_AC)) == 0)
-                       analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+                       analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        }
        if (buf[14] & 2) {
                analog->meaning->mq = SR_MQ_CURRENT;
index e10e773eadd37918e3ee8692739cb304a3c2469d..42012399e22195a2b3ee10e1f5dcfe4f7cfc642f 100644 (file)
@@ -254,6 +254,8 @@ static void parse_flags(const uint8_t *buf, struct dtm0660_info *info)
 static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
                         int *exponent, const struct dtm0660_info *info)
 {
+       int initial_exponent = *exponent;
+
        /* Factors */
        if (info->is_nano)
                *exponent -= 9;
@@ -265,7 +267,7 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
                *exponent += 3;
        if (info->is_mega)
                *exponent += 6;
-       *floatval *= powf(10, *exponent);
+       *floatval *= powf(10, (*exponent - initial_exponent));
 
        /* Measurement modes */
        if (info->is_volt) {
@@ -318,7 +320,7 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
        if (info->is_auto)
                analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
        if (info->is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        if (info->is_hold)
                analog->meaning->mqflags |= SR_MQFLAG_HOLD;
        if (info->is_rel)
@@ -373,7 +375,7 @@ SR_PRIV int sr_dtm0660_parse(const uint8_t *buf, float *floatval,
        int ret, exponent = 0;
        struct dtm0660_info *info_local;
 
-       info_local = (struct dtm0660_info *)info;
+       info_local = info;
 
        if ((ret = parse_value(buf, floatval, &exponent)) != SR_OK) {
                sr_dbg("Error parsing value: %d.", ret);
diff --git a/src/dmm/eev121gw.c b/src/dmm/eev121gw.c
new file mode 100644 (file)
index 0000000..4f13ba8
--- /dev/null
@@ -0,0 +1,1373 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/>.
+ */
+
+/**
+ * @file
+ *
+ * EEVblog 121GW 19-bytes binary protocol parser.
+ *
+ * @internal
+ *
+ * Note that this protocol is different from other meters. We need not
+ * decode the LCD presentation (segments a-g and dot of seven segment
+ * displays). Neither need we decode a textual presentation consisting
+ * of number strings with decimals, and scale/quantity suffixes. Instead
+ * a binary packet is received which contains an unsigned mantissa for
+ * the value, and a number of boolean flags as well as bitfields for modes
+ * and ranges.
+ *
+ * But the protocol is also similar to the four-display variant of the
+ * metex14 protocol. A single DMM packet contains information for two
+ * displays and a bargraph, as well as several flags corresponding to
+ * display indicators and global device state. The vendor's documentation
+ * refers to these sections as "main", "sub", "bar", and "icon".
+ *
+ * It's essential to understand that the serial-dmm API is only able to
+ * communicate a single float value (including its precision and quantity
+ * details) in a single parse call. Which is why we keep a channel index
+ * in the 'info' structure, and run the parse routine several times upon
+ * reception of a single packet. This approach is shared with the metex14
+ * parser.
+ *
+ * The parse routine here differs from other DMM parsers which typically
+ * are split into routines which parse a value (get a number and exponent),
+ * parse flags, and handle flags which were parsed before. The 121GW
+ * meter's packets don't fit this separation naturally, getting the value
+ * and related flags heavily depends on which display shall get inspected,
+ * thus should be done at the same time. Filling in an 'info' structure
+ * from packet content first, and mapping this 'info' to the 'analog'
+ * details then still is very useful for maintainability.
+ *
+ * TODO:
+ * - The meter is feature packed. This implementation does support basic
+ *   operation (voltage, current, power, resistance, continuity, diode,
+ *   capacitance, temperature). Support for remaining modes, previously
+ *   untested ranges, and advanced features (DC+AC, VA power, dB gain,
+ *   burden voltage) may be missing or incomplete. Ranges support and
+ *   value scaling should be considered "under development" in general
+ *   until test coverage was increased. Some flags are not evaluated
+ *   correctly yet, or not at all (min/max/avg, memory).
+ * - Test previously untested modes: current, power, gain, sub display
+ *   modes. Test untested ranges (voltage above 30V, temperature above
+ *   30deg (into the hundreds), negative temperatures, large resistors,
+ *   large capacitors). Test untested features (min/max/avg, 1ms peak,
+ *   log memory).
+ * - It's assumed that a continuous data stream was arranged for. This
+ *   implementation does not support the "packet request" API. Also I
+ *   was to understand that once the request was sent (write 0300 to
+ *   handle 9, after connecting) no further request is needed. Only
+ *   the loss of communication may need recovery, which we leave as an
+ *   option for later improvement, or as a feature of an external helper
+ *   which feeds the COM port from Bluetooth communication data, or
+ *   abstracts away the BLE communication.
+ *
+ * Implementation notes:
+ * - Yes some ranges seem duplicate but that's fine. The meter's packets
+ *   do provide multiple range indices for some of the modes which do
+ *   communicate values in the same range of values.
+ * - Some of the packet's bits don't match the available documentation.
+ *   Some of the meter's features are not available to the PC side by
+ *   means of inspecting packets.
+ *   - Bit 5 of "bar value" was seen with value 1 in FREQ and OHM:
+ *     f2  17 84 21 21  08 00 00 00  64 01 01 17  12 37  02 40 00  7d
+ *     So we keep the test around but accept when it fails.
+ *   - The "gotta beep" activity of continuity/break test mode is not
+ *     available in the packets.
+ * - The interpretation of range indices depends on the specific mode
+ *   (meter's function, and range when selectable by the user like mV).
+ *   As does the precision of results.
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include <glib.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include "libsigrok/libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "eev121gw"
+
+/*
+ * TODO:
+ * When these bit field extraction helpers move to some common location,
+ * their names may need adjustment to reduce the potential for conflicts.
+ */
+// #define BIT(n) (1UL << (n))
+#define MASK(len) ((1UL << (len)) - 1)
+#define FIELD_PL(v, pos, len) (((v) >> (pos)) & MASK(len))
+#define FIELD_NL(v, name) FIELD_PL(v, POS_ ## name, LEN_ ## name)
+#define FIELD_NB(v, name) FIELD_PL(v, POS_ ## name, 1)
+
+/*
+ * Support compile time checks for expected sizeof() results etc, like
+ *   STATIC_ASSERT(sizeof(struct packet) == 19, "packet size");
+ * Probably should go to some common location.
+ * See http://www.pixelbeat.org/programming/gcc/static_assert.html for details.
+ */
+#define ASSERT_CONCAT_(a, b) a ## b
+#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
+/* These can't be used after statements in c89. */
+#ifdef __COUNTER__
+  #define STATIC_ASSERT(e, m) \
+    ; enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1 / (int)(!!(e)) }
+#else
+  /*
+   * This can't be used twice on the same line so ensure if using in headers
+   * that the headers are not included twice (by wrapping in #ifndef...#endif).
+   * Note it doesn't cause an issue when used on same line of separate modules
+   * compiled with gcc -combine -fwhole-program.
+   */
+  #define STATIC_ASSERT(e, m) \
+    ; enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1 / (int)(!!(e)) }
+#endif
+
+/*
+ * Symbolic identifiers for access to the packet's payload. "Offsets"
+ * address bytes within the packet. "Positions" specify the (lowest)
+ * bit number of a field, "lengths" specify the fields' number of bits.
+ * "Values" specify magic values or fixed content (SBZ, RSV, etc).
+ */
+enum eev121gw_packet_offs {
+       OFF_START_CMD,
+#define  VAL_START_CMD         0xf2
+       OFF_SERIAL_3,
+       OFF_SERIAL_2,
+       OFF_SERIAL_1,
+       OFF_SERIAL_0,
+#define  POS_SERIAL_YEAR       24
+#define  LEN_SERIAL_YEAR       8
+#define  POS_SERIAL_MONTH      20
+#define  LEN_SERIAL_MONTH      4
+#define  POS_SERIAL_NUMBER     0
+#define  LEN_SERIAL_NUMBER     20
+       OFF_MAIN_MODE,
+#define  POS_MAIN_MODE_VAL_U   6
+#define  LEN_MAIN_MODE_VAL_U   2
+#define  POS_MAIN_MODE_RSV_5   5
+#define  POS_MAIN_MODE_MODE    0
+#define  LEN_MAIN_MODE_MODE    5
+       OFF_MAIN_RANGE,
+#define  POS_MAIN_RANGE_OFL    7
+#define  POS_MAIN_RANGE_SIGN   6
+#define  POS_MAIN_RANGE_DEGC   5
+#define  POS_MAIN_RANGE_DEGF   4
+#define  POS_MAIN_RANGE_RANGE  0
+#define  LEN_MAIN_RANGE_RANGE  4
+       OFF_MAIN_VAL_H,
+       OFF_MAIN_VAL_L,
+       OFF_SUB_MODE,
+#define  POS_SUB_MODE_MODE     0
+#define  LEN_SUB_MODE_MODE     8
+       OFF_SUB_RANGE,
+#define  POS_SUB_RANGE_OFL     7
+#define  POS_SUB_RANGE_SIGN    6
+#define  POS_SUB_RANGE_K       5
+#define  POS_SUB_RANGE_HZ      4
+#define  POS_SUB_RANGE_RSV_3   3
+#define  POS_SUB_RANGE_POINT   0
+#define  LEN_SUB_RANGE_POINT   3
+       OFF_SUB_VAL_H,
+       OFF_SUB_VAL_L,
+       OFF_BAR_STATUS,
+#define  POS_BAR_STATUS_RSV_5  5
+#define  LEN_BAR_STATUS_RSV_5  3
+#define  POS_BAR_STATUS_USE    4
+#define  POS_BAR_STATUS_150    3
+#define  POS_BAR_STATUS_SIGN   2
+#define  POS_BAR_STATUS_1K_500 0
+#define  LEN_BAR_STATUS_1K_500 2
+       OFF_BAR_VALUE,
+#define  POS_BAR_VALUE_RSV_6   6
+#define  LEN_BAR_VALUE_RSV_6   2
+#define  POS_BAR_VALUE_RSV_5   5
+#define  POS_BAR_VALUE_VALUE   0
+#define  LEN_BAR_VALUE_VALUE   5
+       OFF_ICON_STS_1,
+#define  POS_ICON_STS1_DEGC    7
+#define  POS_ICON_STS1_1KHZ    6
+#define  POS_ICON_STS1_1MSPK   5
+#define  POS_ICON_STS1_DCAC    3
+#define  LEN_ICON_STS1_DCAC    2
+#define  POS_ICON_STS1_AUTO    2
+#define  POS_ICON_STS1_APO     1
+#define  POS_ICON_STS1_BAT     0
+       OFF_ICON_STS_2,
+#define  POS_ICON_STS2_DEGF    7
+#define  POS_ICON_STS2_BT      6
+#define  POS_ICON_STS2_UNK     5 /* TODO: What is this flag? 20mA loop current? */
+#define  POS_ICON_STS2_REL     4
+#define  POS_ICON_STS2_DBM     3
+#define  POS_ICON_STS2_MINMAX  0 /* TODO: How to interpret the 3-bit field? */
+#define  LEN_ICON_STS2_MINMAX  3
+       OFF_ICON_STS_3,
+#define  POS_ICON_STS3_RSV_7   7
+#define  POS_ICON_STS3_TEST    6
+#define  POS_ICON_STS3_MEM     4 /* TODO: How to interpret the 2-bit field? */
+#define  LEN_ICON_STS3_MEM     2
+#define  POS_ICON_STS3_AHOLD   2
+#define  LEN_ICON_STS3_AHOLD   2
+#define  POS_ICON_STS3_AC      1
+#define  POS_ICON_STS3_DC      0
+       OFF_CHECKSUM,
+       /* This is not an offset, but the packet's "byte count". */
+       PACKET_LAST_OFF,
+};
+
+STATIC_ASSERT(PACKET_LAST_OFF == EEV121GW_PACKET_SIZE,
+       "byte offsets vs packet length mismatch");
+
+enum mode_codes {
+       /* Modes for 'main' and 'sub' displays. */
+       MODE_LOW_Z = 0,
+       MODE_DC_V = 1,
+       MODE_AC_V = 2,
+       MODE_DC_MV = 3,
+       MODE_AC_MV = 4,
+       MODE_TEMP = 5,
+       MODE_FREQ = 6,
+       MODE_PERIOD = 7,
+       MODE_DUTY = 8,
+       MODE_RES = 9,
+       MODE_CONT = 10,
+       MODE_DIODE = 11,
+       MODE_CAP = 12,
+       MODE_AC_UVA = 13,
+       MODE_AC_MVA = 14,
+       MODE_AC_VA = 15,
+       MODE_AC_UA = 16,
+       MODE_DC_UA = 17,
+       MODE_AC_MA = 18,
+       MODE_DC_MA = 19,
+       MODE_AC_A = 20,
+       MODE_DC_A = 21,
+       MODE_DC_UVA = 22,
+       MODE_DC_MVA = 23,
+       MODE_DC_VA = 24,
+       /* More modes for 'sub' display. */
+       MODE_SUB_TEMPC = 100,
+       MODE_SUB_TEMPF = 105,
+       MODE_SUB_BATT = 110,
+       MODE_SUB_APO_ON = 120,
+       MODE_SUB_APO_OFF = 125,
+       MODE_SUB_YEAR = 130,
+       MODE_SUB_DATE = 135,
+       MODE_SUB_TIME = 140,
+       MODE_SUB_B_VOLT = 150,
+       MODE_SUB_LCD = 160,
+       MODE_SUB_CONT_PARM_0 = 170,
+       MODE_SUB_CONT_PARM_1 = 171,
+       MODE_SUB_CONT_PARM_2 = 172,
+       MODE_SUB_CONT_PARM_3 = 173,
+       MODE_SUB_DBM = 180,
+       MODE_SUB_IVAL = 190,
+};
+
+enum range_codes {
+       RANGE_0,
+       RANGE_1,
+       RANGE_2,
+       RANGE_3,
+       RANGE_4,
+       RANGE_5,
+       RANGE_6,
+       RANGE_MAX,
+};
+
+enum bar_range_codes {
+       BAR_RANGE_5,
+       BAR_RANGE_50,
+       BAR_RANGE_500,
+       BAR_RANGE_1000,
+};
+#define BAR_VALUE_MAX 25
+
+enum acdc_codes {
+       ACDC_NONE,
+       ACDC_DC,
+       ACDC_AC,
+       ACDC_ACDC,
+};
+
+SR_PRIV const char *eev121gw_channel_formats[EEV121GW_DISPLAY_COUNT] = {
+       /*
+        * TODO:
+        * The "main", "sub", "bar" names were taken from the packet
+        * description. Will users prefer "primary", "secondary", and
+        * "bargraph" names? Or even-length "pri", "sec", "bar" instead?
+        */
+       "main", "sub", "bar",
+};
+
+/*
+ * See page 69 in the 2018-09-24 manual for a table of modes and their
+ * respective ranges ("Calibration Reference Table"). This is the input
+ * to get the number of significant digits, and the decimal's position.
+ */
+struct mode_range_item {
+       const char *desc; /* Description, for diagnostics. */
+       int digits; /* Number of significant digits, see @ref sr_analog_encoding. */
+       int factor; /* Factor to convert the uint to a float. */
+};
+
+struct mode_range_items {
+       size_t range_count;
+       const struct mode_range_item ranges[RANGE_MAX];
+};
+
+static const struct mode_range_items mode_ranges_lowz = {
+       .range_count = 1,
+       .ranges = {
+               { .desc =  "600.0V", .digits = 1, .factor = 1, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_volts = {
+       .range_count = 4,
+       .ranges = {
+               { .desc = "5.0000V", .digits = 4, .factor = 4, },
+               { .desc = "50.000V", .digits = 3, .factor = 3, },
+               { .desc = "500.00V", .digits = 2, .factor = 2, },
+               { .desc =  "600.0V", .digits = 1, .factor = 1, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_millivolts = {
+       .range_count = 2,
+       .ranges = {
+               { .desc = "50.000mV", .digits = 6, .factor = 6, },
+               { .desc = "500.00mV", .digits = 5, .factor = 5, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_temp = {
+       .range_count = 1,
+       .ranges = {
+               { .desc = "-200.0C ~ 1350.0C", .digits = 1, .factor = 1, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_freq = {
+       .range_count = 5,
+       .ranges = {
+               { .desc = "99.999Hz", .digits = 3, .factor = 3, },
+               { .desc = "999.99Hz", .digits = 2, .factor = 2, },
+               { .desc = "9.9999kHz", .digits = 1, .factor = 1, },
+               { .desc = "99.999kHz", .digits = 0, .factor = 0, },
+               { .desc = "999.99kHz", .digits = -1, .factor = -1, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_period = {
+       .range_count = 3,
+       .ranges = {
+               { .desc = "9.9999ms", .digits = 7, .factor = 7, },
+               { .desc = "99.999ms", .digits = 6, .factor = 6, },
+               { .desc = "999.99ms", .digits = 5, .factor = 5, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_duty = {
+       .range_count = 1,
+       .ranges = {
+               { .desc = "99.9%", .digits = 1, .factor = 1, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_res = {
+       .range_count = 7,
+       .ranges = {
+               { .desc = "50.000R", .digits = 3, .factor = 3, },
+               { .desc = "500.00R", .digits = 2, .factor = 2, },
+               { .desc = "5.0000k", .digits = 1, .factor = 1, },
+               { .desc = "50.000k", .digits = 0, .factor = 0, },
+               { .desc = "500.00k", .digits = -1, .factor = -1, },
+               { .desc = "5.0000M", .digits = -2, .factor = -2, },
+               { .desc = "50.000M", .digits = -3, .factor = -3, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_cont = {
+       .range_count = 1,
+       .ranges = {
+               { .desc = "500.00R", .digits = 2, .factor = 2, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_diode = {
+       .range_count = 2,
+       .ranges = {
+               { .desc = "3.0000V", .digits = 4, .factor = 4, },
+               { .desc = "15.000V", .digits = 3, .factor = 3, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_cap = {
+       .range_count = 6,
+       .ranges = {
+               { .desc =  "10.00n", .digits = 11, .factor = 11, },
+               { .desc =  "100.0n", .digits = 10, .factor = 10, },
+               { .desc =  "1.000u", .digits = 9, .factor = 9, },
+               { .desc =  "10.00u", .digits = 8, .factor = 8, },
+               { .desc =  "100.0u", .digits = 7, .factor = 7, },
+               { .desc =  "10.00m", .digits = 5, .factor = 5, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_pow_va = {
+       .range_count = 4,
+       .ranges = {
+               { .desc = "2500.0mVA", .digits = 4, .factor = 4, },
+               { .desc = "25000.mVA", .digits = 3, .factor = 3, },
+               { .desc = "25.000VA", .digits = 3, .factor = 3, },
+               { .desc = "500.00VA", .digits = 2, .factor = 2, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_pow_mva = {
+       .range_count = 4,
+       .ranges = {
+               { .desc = "25.000mVA", .digits = 6, .factor = 6, },
+               { .desc = "250.00mVA", .digits = 5, .factor = 5, },
+               { .desc = "250.00mVA", .digits = 5, .factor = 5, },
+               { .desc = "2500.0mVA", .digits = 4, .factor = 4, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_pow_uva = {
+       .range_count = 4,
+       .ranges = {
+               { .desc = "250.00uVA", .digits = 8, .factor = 8, },
+               { .desc = "2500.0uVA", .digits = 7, .factor = 7, },
+               { .desc = "2500.0uVA", .digits = 7, .factor = 7, },
+               { .desc = "25000.uVA", .digits = 6, .factor = 6, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_curr_a = {
+       .range_count = 3,
+       .ranges = {
+               { .desc = "500.00mA", .digits = 5, .factor = 5, },
+               { .desc = "5.0000A", .digits = 4, .factor = 4, },
+               { .desc = "10.000A", .digits = 3, .factor = 3, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_curr_ma = {
+       .range_count = 2,
+       .ranges = {
+               { .desc = "5.0000mA", .digits = 7, .factor = 7, },
+               { .desc = "50.000mA", .digits = 6, .factor = 6, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_curr_ua = {
+       .range_count = 2,
+       .ranges = {
+               { .desc = "50.000uA", .digits = 9, .factor = 9, },
+               { .desc = "500.00uA", .digits = 8, .factor = 8, },
+       },
+};
+
+static const struct mode_range_items *mode_ranges_main[] = {
+       [MODE_LOW_Z] = &mode_ranges_lowz,
+       [MODE_DC_V] = &mode_ranges_volts,
+       [MODE_AC_V] = &mode_ranges_volts,
+       [MODE_DC_MV] = &mode_ranges_millivolts,
+       [MODE_AC_MV] = &mode_ranges_millivolts,
+       [MODE_TEMP] = &mode_ranges_temp,
+       [MODE_FREQ] = &mode_ranges_freq,
+       [MODE_PERIOD] = &mode_ranges_period,
+       [MODE_DUTY] = &mode_ranges_duty,
+       [MODE_RES] = &mode_ranges_res,
+       [MODE_CONT] = &mode_ranges_cont,
+       [MODE_DIODE] = &mode_ranges_diode,
+       [MODE_CAP] = &mode_ranges_cap,
+       [MODE_DC_VA] = &mode_ranges_pow_va,
+       [MODE_AC_VA] = &mode_ranges_pow_va,
+       [MODE_DC_MVA] = &mode_ranges_pow_mva,
+       [MODE_AC_MVA] = &mode_ranges_pow_mva,
+       [MODE_DC_UVA] = &mode_ranges_pow_uva,
+       [MODE_AC_UVA] = &mode_ranges_pow_uva,
+       [MODE_DC_A] = &mode_ranges_curr_a,
+       [MODE_AC_A] = &mode_ranges_curr_a,
+       [MODE_DC_MA] = &mode_ranges_curr_ma,
+       [MODE_AC_MA] = &mode_ranges_curr_ma,
+       [MODE_DC_UA] = &mode_ranges_curr_ua,
+       [MODE_AC_UA] = &mode_ranges_curr_ua,
+};
+
+/*
+ * The secondary display encodes SI units / scaling differently from the
+ * main display, and fewer ranges are available. So we share logic between
+ * displays for scaling, but have to keep separate tables for the displays.
+ */
+
+static const struct mode_range_items mode_ranges_temp_sub = {
+       .range_count = 2,
+       .ranges = {
+               [1] = { .desc = "sub 100.0C", .digits = 1, .factor = 1, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_freq_sub = {
+       .range_count = 4,
+       .ranges = {
+               [1] = { .desc = "999.9Hz", .digits = 1, .factor = 1, },
+               [2] = { .desc = "99.99Hz", .digits = 2, .factor = 2, },
+               [3] = { .desc = "9.999kHz", .digits = 3, .factor = 3, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_batt_sub = {
+       .range_count = 2,
+       .ranges = {
+               [1] = { .desc = "sub 10.0V", .digits = 1, .factor = 1, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_gain_sub = {
+       .range_count = 4,
+       .ranges = {
+               [1] = { .desc = "dbm 5000.0dBm", .digits = 1, .factor = 1, },
+               [2] = { .desc = "dbm 500.00dBm", .digits = 2, .factor = 2, },
+               [3] = { .desc = "dbm 50.000dBm", .digits = 3, .factor = 3, },
+       },
+};
+
+static const struct mode_range_items mode_ranges_diode_sub = {
+       .range_count = 1,
+       .ranges = {
+               [0] = { .desc = "diode 15.0V", .digits = 0, .factor = 0, },
+       },
+};
+
+/* TODO: Complete the list of ranges. Only tested with low voltages so far. */
+static const struct mode_range_items mode_ranges_volts_sub = {
+       .range_count = 5,
+       .ranges = {
+               [4] = { .desc = "5.0000V", .digits = 4, .factor = 4, },
+       },
+};
+
+/* TODO: Complete the list of ranges. Only tested with low voltages so far. */
+static const struct mode_range_items mode_ranges_mamps_sub = {
+       .range_count = 3,
+       .ranges = {
+               [2] = { .desc = "500.00mA", .digits = 5, .factor = 5, },
+       },
+};
+
+static const struct mode_range_items *mode_ranges_sub[] = {
+       [MODE_DC_V] = &mode_ranges_volts_sub,
+       [MODE_AC_V] = &mode_ranges_volts_sub,
+       [MODE_DC_A] = &mode_ranges_mamps_sub,
+       [MODE_AC_A] = &mode_ranges_mamps_sub,
+       [MODE_FREQ] = &mode_ranges_freq_sub,
+       [MODE_DIODE] = &mode_ranges_diode_sub,
+       [MODE_SUB_TEMPC] = &mode_ranges_temp_sub,
+       [MODE_SUB_TEMPF] = &mode_ranges_temp_sub,
+       [MODE_SUB_BATT] = &mode_ranges_batt_sub,
+       [MODE_SUB_DBM] = &mode_ranges_gain_sub,
+};
+
+static const struct mode_range_item *mode_range_get_scale(
+       enum eev121gw_display display,
+       enum mode_codes mode, enum range_codes range)
+{
+       const struct mode_range_items *items;
+       const struct mode_range_item *item;
+
+       if (display == EEV121GW_DISPLAY_MAIN) {
+               if (mode >= ARRAY_SIZE(mode_ranges_main))
+                       return NULL;
+               items = mode_ranges_main[mode];
+               if (!items || !items->range_count)
+                       return NULL;
+               if (range >= items->range_count)
+                       return NULL;
+               item = &items->ranges[range];
+               return item;
+       }
+       if (display == EEV121GW_DISPLAY_SUB) {
+               if (mode >= ARRAY_SIZE(mode_ranges_sub))
+                       return NULL;
+               items = mode_ranges_sub[mode];
+               if (!items || !items->range_count)
+                       return NULL;
+               if (range >= items->range_count)
+                       return NULL;
+               item = &items->ranges[range];
+               if (!item->desc || !*item->desc)
+                       return NULL;
+               return item;
+       }
+
+       return NULL;
+}
+
+SR_PRIV gboolean sr_eev121gw_packet_valid(const uint8_t *buf)
+{
+       uint8_t csum;
+       size_t idx;
+
+       /* Leading byte, literal / fixed value. */
+       if (buf[OFF_START_CMD] != VAL_START_CMD)
+               return FALSE;
+
+       /* Check some always-zero bits in reserved locations. */
+       if (FIELD_NB(buf[OFF_MAIN_MODE], MAIN_MODE_RSV_5))
+               return FALSE;
+       if (FIELD_NB(buf[OFF_SUB_RANGE], SUB_RANGE_RSV_3))
+               return FALSE;
+       if (FIELD_NL(buf[OFF_BAR_STATUS], BAR_STATUS_RSV_5))
+               return FALSE;
+       if (FIELD_NL(buf[OFF_BAR_VALUE], BAR_VALUE_RSV_6))
+               return FALSE;
+       /* See TODO for bit 5 of "bar value" not always being 0. */
+       if (0 && FIELD_NB(buf[OFF_BAR_VALUE], BAR_VALUE_RSV_5))
+               return FALSE;
+       if (FIELD_NB(buf[OFF_ICON_STS_3], ICON_STS3_RSV_7))
+               return FALSE;
+
+       /* Checksum, XOR over all previous bytes. */
+       csum = 0x00;
+       for (idx = OFF_START_CMD; idx < OFF_CHECKSUM; idx++)
+               csum ^= buf[idx];
+       if (csum != buf[OFF_CHECKSUM]) {
+               /* Non-critical condition, almost expected to see invalid data. */
+               sr_spew("Packet csum: want %02x, got %02x.", csum, buf[OFF_CHECKSUM]);
+               return FALSE;
+       }
+
+       sr_spew("Packet valid.");
+
+       return TRUE;
+}
+
+/**
+ * Parse a protocol packet.
+ *
+ * @param[in] buf Buffer containing the protocol packet. Must not be NULL.
+ * @param[out] floatval Pointer to a float variable. That variable will be
+ *             modified in-place depending on the protocol packet.
+ *             Must not be NULL.
+ * @param[out] 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[out] info Pointer to a struct eevblog_121gw_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_eev121gw_parse(const uint8_t *buf, float *floatval,
+               struct sr_datafeed_analog *analog, void *info)
+{
+       struct eev121gw_info *info_local;
+       enum eev121gw_display display;
+       const char *channel_name;
+       uint32_t raw_serial;
+       uint8_t raw_main_mode, raw_main_range;
+       uint16_t raw_main_value;
+       uint8_t raw_sub_mode, raw_sub_range;
+       uint16_t raw_sub_value;
+       uint8_t raw_bar_status, raw_bar_value;
+       uint8_t raw_icon_stat_1, raw_icon_stat_2, raw_icon_stat_3;
+       uint32_t uint_value;
+       enum mode_codes main_mode;
+       enum range_codes main_range;
+       enum mode_codes sub_mode;
+       enum range_codes sub_range;
+       const struct mode_range_item *scale;
+       gboolean is_dc, is_sign, use_sign;
+       gboolean is_k;
+       unsigned int cont_code;
+
+       info_local = info;
+       display = info_local->ch_idx;
+       channel_name = eev121gw_channel_formats[display];
+       memset(info_local, 0, sizeof(*info_local));
+       *floatval = 0.0f;
+
+       /*
+        * Get the packet's bytes into native C language typed variables.
+        * This keeps byte position references out of logic/calculations.
+        * The start command and CRC were verified before we get here.
+        */
+       raw_serial = RB32(&buf[OFF_SERIAL_3]);
+       raw_main_mode = R8(&buf[OFF_MAIN_MODE]);
+       raw_main_range = R8(&buf[OFF_MAIN_RANGE]);
+       raw_main_value = RB16(&buf[OFF_MAIN_VAL_H]);
+       raw_sub_mode = R8(&buf[OFF_SUB_MODE]);
+       raw_sub_range = R8(&buf[OFF_SUB_RANGE]);
+       raw_sub_value = RB16(&buf[OFF_SUB_VAL_H]);
+       raw_bar_status = R8(&buf[OFF_BAR_STATUS]);
+       raw_bar_value = R8(&buf[OFF_BAR_VALUE]);
+       raw_icon_stat_1 = R8(&buf[OFF_ICON_STS_1]);
+       raw_icon_stat_2 = R8(&buf[OFF_ICON_STS_2]);
+       raw_icon_stat_3 = R8(&buf[OFF_ICON_STS_3]);
+
+       /*
+        * Packets contain a YEAR-MONTH date spec. It's uncertain how
+        * this data relates to the device's production or the firmware
+        * version. It certainly is not the current date either. Only
+        * optionally log this information, it's consistent across all
+        * packets (won't change within a session), and will be noisy if
+        * always enabled.
+        *
+        * Packets also contain a user adjustable device identification
+        * number (see the SETUP options). This is motivated by support
+        * for multiple devices, but won't change here within a session.
+        * The user chose to communicate to one specific device when the
+        * session started, by means of the conn= spec.
+        *
+        * It was suggested that this 'serial' field might be used as an
+        * additional means to check for a packet's validity (or absence
+        * of communication errors). This remains as an option for future
+        * improvement.
+        */
+       if (0) {
+               unsigned int ser_year, ser_mon, ser_nr;
+
+               ser_year = FIELD_NL(raw_serial, SERIAL_YEAR);
+               ser_mon = FIELD_NL(raw_serial, SERIAL_MONTH);
+               ser_nr = FIELD_NL(raw_serial, SERIAL_NUMBER);
+               sr_spew("Packet: Y-M %x-%x, nr %x.", ser_year, ser_mon, ser_nr);
+       }
+
+       switch (display) {
+
+       case EEV121GW_DISPLAY_MAIN:
+               /*
+                * Get those fields which correspond to the main display.
+                * The value's mantissa has 18 bits. The sign is separate
+                * (and is not universally applicable, mode needs to get
+                * inspected). The range's scaling and precision also
+                * depend on the mode.
+                */
+               main_mode = FIELD_NL(raw_main_mode, MAIN_MODE_MODE);
+               main_range = FIELD_NL(raw_main_range, MAIN_RANGE_RANGE);
+               scale = mode_range_get_scale(EEV121GW_DISPLAY_MAIN,
+                       main_mode, main_range);
+               if (!scale)
+                       return SR_ERR_NA;
+               info_local->factor = scale->factor;
+               info_local->digits = scale->digits;
+
+               uint_value = raw_main_value;
+               uint_value |= FIELD_NL(raw_main_mode, MAIN_MODE_VAL_U) << 16;
+               info_local->uint_value = uint_value;
+               info_local->is_ofl = FIELD_NB(raw_main_range, MAIN_RANGE_OFL);
+
+               switch (main_mode) {
+               case MODE_LOW_Z:
+                       is_dc = FALSE;
+                       if (FIELD_NB(raw_icon_stat_3, ICON_STS3_DC))
+                               is_dc = TRUE;
+                       if (FIELD_NB(raw_icon_stat_3, ICON_STS3_AC))
+                               is_dc = FALSE;
+                       use_sign = is_dc;
+                       break;
+               case MODE_DC_V:
+               case MODE_DC_MV:
+               case MODE_TEMP:
+               case MODE_DC_UVA:
+               case MODE_DC_MVA:
+               case MODE_DC_VA:
+               case MODE_DC_UA:
+               case MODE_DC_MA:
+               case MODE_DC_A:
+                       use_sign = TRUE;
+                       break;
+               default:
+                       use_sign = FALSE;
+                       break;
+               }
+               if (use_sign) {
+                       is_sign = FIELD_NB(raw_main_range, MAIN_RANGE_SIGN);
+                       info_local->is_neg = is_sign;
+               }
+
+               switch (main_mode) {
+               case MODE_LOW_Z:
+                       info_local->is_voltage = TRUE;
+                       /* TODO: Need to determine AC/DC here? */
+                       info_local->is_volt = TRUE;
+                       info_local->is_low_pass = TRUE;
+                       break;
+               case MODE_DC_V:
+                       info_local->is_voltage = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_volt = TRUE;
+                       break;
+               case MODE_AC_V:
+                       info_local->is_voltage = TRUE;
+                       info_local->is_volt = TRUE;
+                       info_local->is_ac = TRUE;
+                       break;
+               case MODE_DC_MV:
+                       info_local->is_voltage = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_volt = TRUE;
+                       break;
+               case MODE_AC_MV:
+                       info_local->is_voltage = TRUE;
+                       info_local->is_volt = TRUE;
+                       info_local->is_ac = TRUE;
+                       break;
+               case MODE_TEMP:
+                       info_local->is_temperature = TRUE;
+                       if (FIELD_NB(raw_main_range, MAIN_RANGE_DEGC))
+                               info_local->is_celsius = TRUE;
+                       if (FIELD_NB(raw_main_range, MAIN_RANGE_DEGF))
+                               info_local->is_fahrenheit = TRUE;
+                       break;
+               case MODE_FREQ:
+                       info_local->is_frequency = TRUE;
+                       info_local->is_hertz = TRUE;
+                       break;
+               case MODE_PERIOD:
+                       info_local->is_period = TRUE;
+                       info_local->is_seconds = TRUE;
+                       break;
+               case MODE_DUTY:
+                       info_local->is_duty_cycle = TRUE;
+                       info_local->is_percent = TRUE;
+                       break;
+               case MODE_RES:
+                       info_local->is_resistance = TRUE;
+                       info_local->is_ohm = TRUE;
+                       break;
+               case MODE_CONT:
+                       info_local->is_continuity = TRUE;
+                       info_local->is_ohm = TRUE;
+                       /*
+                        * In continuity mode the packet provides the
+                        * resistance in ohms (500R range), but seems to
+                        * _not_ carry the "boolean" open/closed state
+                        * which controls the beeper. Users can select
+                        * whether to trigger at 30R or 300R, and whether
+                        * to trigger on values below (continuity) or
+                        * above (cable break) the limit, but we cannot
+                        * tell what the currently used setting is. So
+                        * we neither get the beeper's state, nor can we
+                        * derive it from other information.
+                        */
+                       break;
+               case MODE_DIODE:
+                       info_local->is_diode = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_volt = TRUE;
+                       break;
+               case MODE_CAP:
+                       info_local->is_capacitance = TRUE;
+                       info_local->is_farad = TRUE;
+                       break;
+               case MODE_AC_UVA:
+                       info_local->is_power = TRUE;
+                       info_local->is_ac = TRUE;
+                       info_local->is_volt_ampere = TRUE;
+                       break;
+               case MODE_AC_MVA:
+                       info_local->is_power = TRUE;
+                       info_local->is_ac = TRUE;
+                       info_local->is_volt_ampere = TRUE;
+                       break;
+               case MODE_AC_VA:
+                       info_local->is_power = TRUE;
+                       info_local->is_ac = TRUE;
+                       info_local->is_volt_ampere = TRUE;
+                       break;
+               case MODE_AC_UA:
+                       info_local->is_current = TRUE;
+                       info_local->is_ac = TRUE;
+                       info_local->is_ampere = TRUE;
+                       break;
+               case MODE_DC_UA:
+                       info_local->is_current = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_ampere = TRUE;
+                       break;
+               case MODE_AC_MA:
+                       info_local->is_current = TRUE;
+                       info_local->is_ac = TRUE;
+                       info_local->is_ampere = TRUE;
+                       break;
+               case MODE_DC_MA:
+                       info_local->is_current = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_ampere = TRUE;
+                       break;
+               case MODE_AC_A:
+                       info_local->is_current = TRUE;
+                       info_local->is_ac = TRUE;
+                       info_local->is_ampere = TRUE;
+                       break;
+               case MODE_DC_A:
+                       info_local->is_current = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_ampere = TRUE;
+                       break;
+               case MODE_DC_UVA:
+                       info_local->is_power = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_volt_ampere = TRUE;
+                       break;
+               case MODE_DC_MVA:
+                       info_local->is_power = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_volt_ampere = TRUE;
+                       break;
+               case MODE_DC_VA:
+                       info_local->is_power = TRUE;
+                       info_local->is_dc = TRUE;
+                       info_local->is_volt_ampere = TRUE;
+                       break;
+               /* Modes 100-199 only apply to the secondary display. */
+               default:
+                       return SR_ERR_NA;
+               }
+
+               /*
+                * Inspect the "icons" section, since it is associated
+                * with the primary display and global device state.
+                */
+               if (FIELD_NB(raw_icon_stat_1, ICON_STS1_1KHZ))
+                       info_local->is_low_pass = TRUE;
+               if (FIELD_NB(raw_icon_stat_1, ICON_STS1_1MSPK))
+                       info_local->is_1ms_peak = TRUE;
+               switch (FIELD_NL(raw_icon_stat_1, ICON_STS1_DCAC)) {
+               case ACDC_ACDC:
+                       info_local->is_ac = TRUE;
+                       info_local->is_dc = TRUE;
+                       break;
+               case ACDC_AC:
+                       info_local->is_ac = TRUE;
+                       break;
+               case ACDC_DC:
+                       info_local->is_dc = TRUE;
+                       break;
+               case ACDC_NONE:
+                       /* EMPTY */
+                       break;
+               }
+               if (FIELD_NB(raw_icon_stat_1, ICON_STS1_AUTO))
+                       info_local->is_auto_range = TRUE;
+               if (FIELD_NB(raw_icon_stat_1, ICON_STS1_APO))
+                       info_local->is_auto_poweroff = TRUE;
+               if (FIELD_NB(raw_icon_stat_1, ICON_STS1_BAT))
+                       info_local->is_low_batt = TRUE;
+               if (FIELD_NB(raw_icon_stat_2, ICON_STS2_BT))
+                       info_local->is_bt = TRUE;
+               /* TODO: Is this the "20mA loop current" flag? */
+               if (FIELD_NB(raw_icon_stat_2, ICON_STS2_UNK))
+                       info_local->is_loop_current = TRUE;
+               if (FIELD_NB(raw_icon_stat_2, ICON_STS2_REL))
+                       info_local->is_rel = TRUE;
+               /* dBm only applies to secondary display, not main. */
+               switch (FIELD_NL(raw_icon_stat_2, ICON_STS2_MINMAX)) {
+               /* TODO: Do inspect the min/max/avg flags. */
+               default:
+                       /* EMPTY */
+                       break;
+               }
+               if (FIELD_NB(raw_icon_stat_3, ICON_STS3_TEST))
+                       info_local->is_test = TRUE;
+               /* TODO: How to interpret the 2-bit MEM field? */
+               if (FIELD_NL(raw_icon_stat_3, ICON_STS3_MEM))
+                       info_local->is_mem = TRUE;
+               if (FIELD_NL(raw_icon_stat_3, ICON_STS3_AHOLD))
+                       info_local->is_hold = TRUE;
+               /* TODO: Are these for the secondary display? See status-2 ACDC. */
+               if (FIELD_NB(raw_icon_stat_3, ICON_STS3_AC))
+                       info_local->is_ac = TRUE;
+               if (FIELD_NB(raw_icon_stat_3, ICON_STS3_DC))
+                       info_local->is_dc = TRUE;
+
+               sr_spew("Disp '%s', value: %lu (ov %d, neg %d), mode %d, range %d.",
+                       channel_name,
+                       (unsigned long)info_local->uint_value,
+                       info_local->is_ofl, info_local->is_neg,
+                       (int)main_mode, (int)main_range);
+               /* Advance to the number and units conversion below. */
+               break;
+
+       case EEV121GW_DISPLAY_SUB:
+               /*
+                * Get those fields which correspond to the secondary
+                * display. The value's mantissa has 16 bits. The sign
+                * is separate is only applies to some of the modes.
+                * Scaling and precision also depend on the mode. The
+                * interpretation of the secondary display is different
+                * from the main display: The 'range' is not an index
+                * into ranges, instead it's the decimal's position.
+                * Yet more scaling depends on the mode, to complicate
+                * matters. The secondary display uses modes 100-199,
+                * and some of the 0-24 modes as well.
+                */
+               sub_mode = FIELD_NL(raw_sub_mode, SUB_MODE_MODE);
+               sub_range = FIELD_NL(raw_sub_range, SUB_RANGE_POINT);
+               scale = mode_range_get_scale(EEV121GW_DISPLAY_SUB,
+                       sub_mode, sub_range);
+               if (!scale)
+                       return SR_ERR_NA;
+               info_local->factor = scale->factor;
+               info_local->digits = scale->digits;
+
+               info_local->uint_value = raw_sub_value;
+               info_local->is_ofl = FIELD_NB(raw_sub_range, SUB_RANGE_OFL);
+
+               switch (sub_mode) {
+               case MODE_DC_V:
+               case MODE_AC_V:
+               case MODE_DC_A:
+               case MODE_AC_A:
+               case MODE_SUB_TEMPC:
+               case MODE_SUB_TEMPF:
+               case MODE_SUB_B_VOLT:
+               case MODE_SUB_DBM:
+                       use_sign = TRUE;
+                       break;
+               default:
+                       use_sign = FALSE;
+                       break;
+               }
+               if (use_sign) {
+                       is_sign = FIELD_NB(raw_sub_range, SUB_RANGE_SIGN);
+                       info_local->is_neg = is_sign;
+               }
+               is_k = FIELD_NB(raw_sub_range, SUB_RANGE_K);
+
+               /*
+                * TODO: Re-check the power mode display as more data becomes
+                * available.
+                *
+                * The interpretation of the secondary display in power (VA)
+                * modes is uncertain. The mode suggests A or uA units but the
+                * value is supposed to be mA without a reliable condition
+                * for us to check...
+                *
+                * f2  17 84 21 21  18 02 00 00  01 04 00 0b  00 00  0a 40 00  3f
+                * f2  17 84 21 21  18 02 00 00  15 03 00 00  00 00  0a 40 00  27
+                *                  DC VA        DC V / DC A
+                *                  25.000VA     dot 4 / dot 3
+                *
+                * f2  17 84 21 21  18 00 00 26  01 04 4c 57  00 00  0e 40 00  0f
+                * f2  17 84 21 21  18 00 00 26  15 02 00 c7  00 00  0e 40 00  c1
+                *                  3.8mVA DC    1.9543V
+                *                                 1.98mA (!) DC A + dot 2 -> milli(!) amps?
+                *
+                * f2  17 84 21 21  17 00 07 85  01 04 4c 5a  00 00  0e 40 00  a9
+                * f2  17 84 21 21  17 00 07 85  13 04 26 7b  00 00  0e 40 00  f0
+                *                  1.925mVA DC  1.9546V
+                *                               0.9852mA
+                *
+                * f2  17 84 21 21  16 02 11 e0  01 04 26 39  00 02  0e 40 00  d2
+                * f2  17 84 21 21  16 02 11 e0  11 04 12 44  00 02  0e 40 00  8b
+                *                  457.6uVA DC  0.9785V
+                *                               0.4676mA (!) DC uA + dot 4 -> milli(!) amps?
+                */
+
+               switch (sub_mode) {
+               case MODE_DC_V:
+                       info_local->is_voltage = TRUE;
+                       info_local->is_volt = TRUE;
+                       break;
+               case MODE_DC_A:
+                       info_local->is_current = TRUE;
+                       info_local->is_ampere = TRUE;
+                       break;
+               case MODE_FREQ:
+                       info_local->is_frequency = TRUE;
+                       info_local->is_hertz = TRUE;
+                       if (is_k) {
+                               info_local->factor -= 3;
+                               info_local->digits -= 3;
+                       }
+                       info_local->is_ofl = FALSE;
+                       break;
+               case MODE_SUB_TEMPC:
+                       info_local->is_temperature = TRUE;
+                       info_local->is_celsius = TRUE;
+                       break;
+               case MODE_SUB_TEMPF:
+                       info_local->is_temperature = TRUE;
+                       info_local->is_fahrenheit = TRUE;
+                       break;
+               case MODE_SUB_BATT:
+                       /* TODO: How to communicate it's the *battery* voltage? */
+                       info_local->is_voltage = TRUE;
+                       info_local->is_volt = TRUE;
+                       break;
+               case MODE_SUB_DBM:
+                       info_local->is_gain = TRUE;
+                       info_local->is_dbm = TRUE;
+                       break;
+               case MODE_SUB_CONT_PARM_0:
+               case MODE_SUB_CONT_PARM_1:
+               case MODE_SUB_CONT_PARM_2:
+               case MODE_SUB_CONT_PARM_3:
+                       /*
+                        * These "continuity parameters" are special. The
+                        * least significant bits represent the options:
+                        *
+                        * 0xaa = 170 => down 30
+                        * 0xab = 171 => up 30
+                        * 0xac = 172 => down 300
+                        * 0xad = 173 => up 300
+                        *
+                        * bit 0 value 0 -> close (cont)
+                        * bit 0 value 1 -> open (break)
+                        * bit 1 value 0 -> 30R limit
+                        * bit 1 value 1 -> 300R limit
+                        *
+                        * This "display value" is only seen during setup
+                        * but not during regular operation of continuity
+                        * mode. :( In theory we could somehow pass the
+                        * 30/300 ohm limit to sigrok, but that'd be of
+                        * somewhat limited use.
+                        */
+                       cont_code = sub_mode - MODE_SUB_CONT_PARM_0;
+                       info_local->is_resistance = TRUE;
+                       info_local->is_ohm = TRUE;
+                       info_local->uint_value = (cont_code & 0x02) ? 300 : 30;
+                       info_local->is_neg = FALSE;
+                       info_local->is_ofl = FALSE;
+                       info_local->factor = 0;
+                       info_local->digits = 0;
+                       break;
+               case MODE_DIODE:
+                       /* Displays configured diode test voltage. */
+                       info_local->is_voltage = TRUE;
+                       info_local->is_volt = TRUE;
+                       break;
+
+               /* Reflecting these to users seems pointless, ignore them. */
+               case MODE_SUB_APO_ON:
+               case MODE_SUB_APO_OFF:
+               case MODE_SUB_LCD:
+               case MODE_SUB_YEAR:
+               case MODE_SUB_DATE:
+               case MODE_SUB_TIME:
+                       return SR_ERR_NA;
+
+               /* Unknown / unsupported sub display mode. */
+               default:
+                       return SR_ERR_NA;
+               }
+
+               sr_spew("disp '%s', value: %lu (ov %d, neg %d), mode %d, range %d",
+                       channel_name,
+                       (unsigned long)info_local->uint_value,
+                       info_local->is_ofl, info_local->is_neg,
+                       (int)sub_mode, (int)sub_range);
+               /* Advance to the number and units conversion below. */
+               break;
+
+       case EEV121GW_DISPLAY_BAR:
+               /*
+                * Get those fields which correspond to the bargraph.
+                * There are 26 segments (ticks 0-25), several ranges
+                * apply (up to 5, or up to 10, several decades). The
+                * bargraph does not apply to all modes and ranges,
+                * hence there is a "use" flag (negative logic, blank
+                * signal). Bit 5 was also found to have undocumented
+                * values, we refuse to use the bargraph value then.
+                */
+               if (FIELD_NB(raw_bar_status, BAR_STATUS_USE))
+                       return SR_ERR_NA;
+               if (FIELD_NB(raw_bar_value, BAR_VALUE_RSV_5))
+                       return SR_ERR_NA;
+               uint_value = FIELD_NL(raw_bar_value, BAR_VALUE_VALUE);
+               if (uint_value > BAR_VALUE_MAX)
+                       uint_value = BAR_VALUE_MAX;
+               info_local->is_neg = FIELD_NB(raw_bar_status, BAR_STATUS_SIGN);
+               switch (FIELD_NL(raw_bar_status, BAR_STATUS_1K_500)) {
+               case BAR_RANGE_5:
+                       /* Full range 5.0, in steps of 0.2 each. */
+                       uint_value *= 5000 / BAR_VALUE_MAX;
+                       info_local->factor = 3;
+                       info_local->digits = 1;
+                       break;
+               case BAR_RANGE_50:
+                       /* Full range 50, in steps of 2 each. */
+                       uint_value *= 50 / BAR_VALUE_MAX;
+                       info_local->factor = 0;
+                       info_local->digits = 0;
+                       break;
+               case BAR_RANGE_500:
+                       /* Full range 500, in steps of 20 each. */
+                       uint_value *= 500 / BAR_VALUE_MAX;
+                       info_local->factor = 0;
+                       info_local->digits = -1;
+                       break;
+               case BAR_RANGE_1000:
+                       /* Full range 1000, in steps of 40 each. */
+                       uint_value *= 1000 / BAR_VALUE_MAX;
+                       info_local->factor = 0;
+                       info_local->digits = -1;
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
+               info_local->uint_value = uint_value;
+               info_local->is_unitless = TRUE;
+               sr_spew("Disp '%s', value: %u.", channel_name,
+                       (unsigned int)info_local->uint_value);
+               /* Advance to the number and units conversion below. */
+               break;
+
+       default:
+               /* Unknown display, programmer's error, ShouldNotHappen(TM). */
+               sr_err("Disp '-?-'.");
+               return SR_ERR_ARG;
+       }
+
+       /*
+        * Convert the unsigned mantissa and its modifiers to a float
+        * analog value, including scale and quantity. Do the conversion
+        * first, and optionally override the result with 'inf' later.
+        * Apply the sign last so that +inf and -inf are supported.
+        */
+       *floatval = info_local->uint_value;
+       if (info_local->factor)
+               *floatval *= powf(10, -info_local->factor);
+       if (info_local->is_ofl)
+               *floatval = INFINITY;
+       if (info_local->is_neg)
+               *floatval = -*floatval;
+
+       analog->encoding->digits = info_local->digits;
+       analog->spec->spec_digits = info_local->digits;
+
+       /*
+        * Communicate the measured quantity.
+        */
+       /* Determine the quantity itself. */
+       if (info_local->is_voltage)
+               analog->meaning->mq = SR_MQ_VOLTAGE;
+       if (info_local->is_current)
+               analog->meaning->mq = SR_MQ_CURRENT;
+       if (info_local->is_power)
+               analog->meaning->mq = SR_MQ_POWER;
+       if (info_local->is_gain)
+               analog->meaning->mq = SR_MQ_GAIN;
+       if (info_local->is_resistance)
+               analog->meaning->mq = SR_MQ_RESISTANCE;
+       if (info_local->is_capacitance)
+               analog->meaning->mq = SR_MQ_CAPACITANCE;
+       if (info_local->is_diode)
+               analog->meaning->mq = SR_MQ_VOLTAGE;
+       if (info_local->is_temperature)
+               analog->meaning->mq = SR_MQ_TEMPERATURE;
+       if (info_local->is_continuity)
+               analog->meaning->mq = SR_MQ_CONTINUITY;
+       if (info_local->is_frequency)
+               analog->meaning->mq = SR_MQ_FREQUENCY;
+       if (info_local->is_period)
+               analog->meaning->mq = SR_MQ_TIME;
+       if (info_local->is_duty_cycle)
+               analog->meaning->mq = SR_MQ_DUTY_CYCLE;
+       if (info_local->is_unitless)
+               analog->meaning->mq = SR_MQ_COUNT;
+       /* Add AC / DC / DC+AC flags. */
+       if (info_local->is_ac)
+               analog->meaning->mqflags |= SR_MQFLAG_AC;
+       if (info_local->is_dc)
+               analog->meaning->mqflags |= SR_MQFLAG_DC;
+       /* Specify units. */
+       if (info_local->is_ampere)
+               analog->meaning->unit = SR_UNIT_AMPERE;
+       if (info_local->is_volt)
+               analog->meaning->unit = SR_UNIT_VOLT;
+       if (info_local->is_volt_ampere)
+               analog->meaning->unit = SR_UNIT_VOLT_AMPERE;
+       if (info_local->is_dbm)
+               analog->meaning->unit = SR_UNIT_DECIBEL_VOLT;
+       if (info_local->is_ohm)
+               analog->meaning->unit = SR_UNIT_OHM;
+       if (info_local->is_farad)
+               analog->meaning->unit = SR_UNIT_FARAD;
+       if (info_local->is_celsius)
+               analog->meaning->unit = SR_UNIT_CELSIUS;
+       if (info_local->is_fahrenheit)
+               analog->meaning->unit = SR_UNIT_FAHRENHEIT;
+       if (info_local->is_hertz)
+               analog->meaning->unit = SR_UNIT_HERTZ;
+       if (info_local->is_seconds)
+               analog->meaning->unit = SR_UNIT_SECOND;
+       if (info_local->is_percent)
+               analog->meaning->unit = SR_UNIT_PERCENTAGE;
+       if (info_local->is_loop_current)
+               analog->meaning->unit = SR_UNIT_PERCENTAGE;
+       if (info_local->is_unitless)
+               analog->meaning->unit = SR_UNIT_UNITLESS;
+       if (info_local->is_logic)
+               analog->meaning->unit = SR_UNIT_UNITLESS;
+       /* Add other indicator flags. */
+       if (info_local->is_diode) {
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DC;
+       }
+       if (info_local->is_min)
+               analog->meaning->mqflags |= SR_MQFLAG_MIN;
+       if (info_local->is_max)
+               analog->meaning->mqflags |= SR_MQFLAG_MAX;
+       if (info_local->is_avg)
+               analog->meaning->mqflags |= SR_MQFLAG_AVG;
+       /* TODO: How to communicate info_local->is_1ms_peak? */
+       if (info_local->is_rel)
+               analog->meaning->mqflags |= SR_MQFLAG_RELATIVE;
+       if (info_local->is_hold)
+               analog->meaning->mqflags |= SR_MQFLAG_HOLD;
+       /* TODO: How to communicate info_local->is_low_pass? */
+       if (info_local->is_mem) /* XXX Is REF appropriate here? */
+               analog->meaning->mqflags |= SR_MQFLAG_REFERENCE;
+       if (info_local->is_auto_range)
+               analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
+       /* TODO: How to communicate info->is_test? What's its meaning at all? */
+       /* TODO: How to communicate info->is_auto_poweroff? */
+       /* TODO: How to communicate info->is_low_batt? */
+
+       return SR_OK;
+}
+
+/*
+ * Parse the same packet multiple times, to extract individual analog
+ * values which correspond to several displays of the device. Make sure
+ * to keep the channel index in place, even if the parse routine will
+ * clear the info structure.
+ */
+SR_PRIV int sr_eev121gw_3displays_parse(const uint8_t *buf, float *floatval,
+               struct sr_datafeed_analog *analog, void *info)
+{
+       struct eev121gw_info *info_local;
+       size_t ch_idx;
+       int rc;
+
+       info_local = info;
+       ch_idx = info_local->ch_idx;
+       rc = sr_eev121gw_parse(buf, floatval, analog, info);
+       info_local->ch_idx = ch_idx + 1;
+
+       return rc;
+}
index 4ebc293d8191939c4bf242e83a1354e94ac9dc49..202b64859a9986105e2674e0367e9f273ac02592 100644 (file)
@@ -502,7 +502,7 @@ static void handle_flags(struct sr_datafeed_analog *analog,
        if (info->is_auto)
                analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
        if (info->is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        if (info->is_hold)
                /*
                * Note: HOLD only affects the number displayed on the LCD,
index 17c75178e2184de876708ba3f5ec0a27022ffe37..392e37e75f2ff0d842ace56c59686a86e9f80997 100644 (file)
@@ -302,7 +302,7 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
        if (info->is_auto)
                analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
        if (info->is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        if (info->is_hold)
                analog->meaning->mqflags |= SR_MQFLAG_HOLD;
        if (info->is_rel)
@@ -353,7 +353,7 @@ SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
        int ret, exponent = 0;
        struct fs9721_info *info_local;
 
-       info_local = (struct fs9721_info *)info;
+       info_local = info;
 
        if ((ret = parse_value(buf, floatval, &exponent)) != SR_OK) {
                sr_dbg("Error parsing value: %d.", ret);
@@ -373,7 +373,7 @@ 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;
+       info_local = info;
 
        /* User-defined FS9721_LP3 flag 'c2c1_00' means temperature (C). */
        if (info_local->is_c2c1_00) {
@@ -386,7 +386,7 @@ 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;
+       info_local = info;
 
        /* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
        if (info_local->is_c2c1_01) {
@@ -399,7 +399,7 @@ 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;
+       info_local = info;
 
        /* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
        if (info_local->is_c2c1_10) {
@@ -412,7 +412,7 @@ SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *i
 {
        struct fs9721_info *info_local;
 
-       info_local = (struct fs9721_info *)info;
+       info_local = info;
 
        /* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (F). */
        if (info_local->is_c2c1_01) {
@@ -431,7 +431,7 @@ 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;
+       info_local = info;
 
        /* User-defined FS9721_LP3 flag 'c2c1_00' means MAX. */
        if (info_local->is_c2c1_00)
index b98d8ee29e6d14bb6624d218d3497f35dbd2859c..06c8b3c430b29e67011ec6065f7f6fb347f56154 100644 (file)
@@ -290,7 +290,7 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
        if (info->is_auto)
                analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
        if (info->is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        if (info->is_hold)
                analog->meaning->mqflags |= SR_MQFLAG_HOLD;
        if (info->is_max)
@@ -359,7 +359,7 @@ SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
        int ret, exponent = 0;
        struct fs9922_info *info_local;
 
-       info_local = (struct fs9922_info *)info;
+       info_local = info;
 
        if ((ret = parse_value(buf, floatval, &exponent)) != SR_OK) {
                sr_dbg("Error parsing value: %d.", ret);
@@ -379,12 +379,12 @@ 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;
+       info_local = info;
 
        /* User-defined z1 flag means "diode mode". */
        if (info_local->is_z1) {
                analog->meaning->mq = SR_MQ_VOLTAGE;
                analog->meaning->unit = SR_UNIT_VOLT;
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        }
 }
index fc09276561b6e60b134e2438b7c3a79f4605a40d..4f3af6940c062e2a9a28aa12e09a14f2fab9f613 100644 (file)
@@ -44,7 +44,7 @@ SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf)
        if (!strncmp((const char *)buf, "OVERRNG", 7))
                return TRUE;
 
-       if (sscanf((const char *)buf, "%f", &val) == 1)
+       if (sr_atof_ascii((const char *)buf, &val) == SR_OK)
                return TRUE;
        else
                return FALSE;
@@ -65,7 +65,7 @@ SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
 
        if (!strncmp((const char *)buf, "OVERRNG", 7))
                *floatval = INFINITY;
-       else if (sscanf((const char *)buf, "%f", &val) == 1) {
+       else if (sr_atof_ascii((const char *)buf, &val) == SR_OK) {
                *floatval = val;
                dot_pos = strcspn((const char *)buf, ".");
                if (dot_pos < 7)
index 0d7234f36748da95e378a977b165aae93087c199..b610aba9fab962f43b09312414df38acf44680ce 100644 (file)
@@ -86,7 +86,7 @@ static int parse_value(const uint8_t *buf, struct metex14_info *info,
                return SR_OK;
 
        /* Bytes 2-8: Sign, value (up to 5 digits) and decimal point */
-       sscanf((const char *)&valstr, "%f", result);
+       sr_atof_ascii((const char *)&valstr, result);
 
        dot_pos = strcspn(valstr, ".");
        if (dot_pos < cnt)
@@ -146,8 +146,14 @@ static void parse_flags(const char *buf, struct metex14_info *info)
                info->is_kilo = info->is_hertz = TRUE;
        else if (!g_ascii_strcasecmp(u, "C"))
                info->is_celsius = TRUE;
+       else if (!g_ascii_strcasecmp(u, "F"))
+               info->is_fahrenheit = TRUE;
        else if (!g_ascii_strcasecmp(u, "DB"))
                info->is_decibel = TRUE;
+       else if (!g_ascii_strcasecmp(u, "dBm"))
+               info->is_decibel_mw = TRUE;
+       else if (!g_ascii_strcasecmp(u, "W"))
+               info->is_watt = TRUE;
        else if (!g_ascii_strcasecmp(u, ""))
                info->is_unitless = TRUE;
 
@@ -156,15 +162,25 @@ static void parse_flags(const char *buf, struct metex14_info *info)
                (!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_temperature = !strncmp(buf, "TE", 2) ||
+               info->is_celsius || info->is_fahrenheit;
        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_gain = !strncmp(buf, "DB", 2) && info->is_decibel;
+       info->is_power = (!strncmp(buf, "dB", 2) && info->is_decibel_mw) ||
+               ((!strncmp(buf, "WT", 2) && info->is_watt));
+       info->is_power_factor = !strncmp(buf, "CO", 2) && info->is_unitless;
        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);
+               (!strncmp(buf, "  ", 2) && !info->is_ampere &&!info->is_volt &&
+               !info->is_resistance && !info->is_capacity && !info->is_frequency &&
+               !info->is_temperature && !info->is_power && !info->is_power_factor &&
+               !info->is_gain && !info->is_logic && !info->is_diode);
+       info->is_min = !strncmp(buf, "MN", 2);
+       info->is_max = !strncmp(buf, "MX", 2);
+       info->is_avg = !strncmp(buf, "AG", 2);
+
        /*
         * Note:
         * - Protocol doesn't distinguish "resistance" from "beep" mode.
@@ -178,8 +194,12 @@ static void parse_flags(const char *buf, struct metex14_info *info)
 static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
                         int *exponent, const struct metex14_info *info)
 {
-       int factor = 0;
+       int factor;
+
+       (void)exponent;
+
        /* Factors */
+       factor = 0;
        if (info->is_pico)
                factor -= 12;
        if (info->is_nano)
@@ -193,7 +213,6 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
        if (info->is_mega)
                factor += 6;
        *floatval *= powf(10, factor);
-       *exponent += factor;
 
        /* Measurement modes */
        if (info->is_volt) {
@@ -216,14 +235,32 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
                analog->meaning->mq = SR_MQ_CAPACITANCE;
                analog->meaning->unit = SR_UNIT_FARAD;
        }
-       if (info->is_celsius) {
+       if (info->is_temperature) {
                analog->meaning->mq = SR_MQ_TEMPERATURE;
-               analog->meaning->unit = SR_UNIT_CELSIUS;
+               if (info->is_celsius)
+                       analog->meaning->unit = SR_UNIT_CELSIUS;
+               else if (info->is_fahrenheit)
+                       analog->meaning->unit = SR_UNIT_FAHRENHEIT;
+               else
+                       analog->meaning->unit = SR_UNIT_UNITLESS;
        }
        if (info->is_diode) {
                analog->meaning->mq = SR_MQ_VOLTAGE;
                analog->meaning->unit = SR_UNIT_VOLT;
        }
+       if (info->is_power) {
+               analog->meaning->mq = SR_MQ_POWER;
+               if (info->is_decibel_mw)
+                       analog->meaning->unit = SR_UNIT_DECIBEL_MW;
+               else if (info->is_watt)
+                       analog->meaning->unit = SR_UNIT_WATT;
+               else
+                       analog->meaning->unit = SR_UNIT_UNITLESS;
+       }
+       if (info->is_power_factor) {
+               analog->meaning->mq = SR_MQ_POWER_FACTOR;
+               analog->meaning->unit = SR_UNIT_UNITLESS;
+       }
        if (info->is_gain) {
                analog->meaning->mq = SR_MQ_GAIN;
                analog->meaning->unit = SR_UNIT_DECIBEL_VOLT;
@@ -243,7 +280,13 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
        if (info->is_dc)
                analog->meaning->mqflags |= SR_MQFLAG_DC;
        if (info->is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
+       if (info->is_min)
+               analog->meaning->mqflags |= SR_MQFLAG_MIN;
+       if (info->is_max)
+               analog->meaning->mqflags |= SR_MQFLAG_MAX;
+       if (info->is_avg)
+               analog->meaning->mqflags |= SR_MQFLAG_AVG;
 }
 
 static gboolean flags_valid(const struct metex14_info *info)
@@ -293,7 +336,7 @@ SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial)
 
        sr_spew("Requesting DMM packet.");
 
-       return (serial_write_nonblocking(serial, &wbuf, 1) == 1) ? SR_OK : SR_ERR;
+       return serial_write_blocking(serial, &wbuf, 1, 0);
 }
 #endif
 
@@ -313,6 +356,25 @@ SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf)
        return TRUE;
 }
 
+SR_PRIV gboolean sr_metex14_4packets_valid(const uint8_t *buf)
+{
+       struct metex14_info info;
+       size_t ch_idx;
+       const uint8_t *ch_buf;
+
+       ch_buf = buf;
+       for (ch_idx = 0; ch_idx < 4; ch_idx++) {
+               if (ch_buf[13] != '\r')
+                       return FALSE;
+               memset(&info, 0x00, sizeof(info));
+               parse_flags((const char *)ch_buf, &info);
+               if (!flags_valid(&info))
+                       return FALSE;
+               ch_buf += METEX14_PACKET_SIZE;
+       }
+       return TRUE;
+}
+
 /**
  * Parse a protocol packet.
  *
@@ -334,7 +396,7 @@ SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
        int ret, exponent = 0;
        struct metex14_info *info_local;
 
-       info_local = (struct metex14_info *)info;
+       info_local = info;
 
        /* Don't print byte 13. That one contains the carriage return. */
        sr_dbg("DMM packet: \"%.13s\"", buf);
@@ -354,3 +416,34 @@ SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
 
        return SR_OK;
 }
+
+/**
+ * Parse one out of four values of a four-display Metex14 variant.
+ *
+ * The caller's 'info' parameter can be used to track the channel index,
+ * as long as the information is kept across calls to the 14-byte packet
+ * parse routine (which clears the 'info' container).
+ *
+ * Since analog values have further details in the 'analog' parameter,
+ * passing multiple values per parse routine call is problematic. So we
+ * prefer the approach of passing one value per call, which is most
+ * reliable and shall fit every similar device with multiple displays.
+ *
+ * The meters which use this parse routine send one 14-byte packet per
+ * display. Each packet has the regular Metex14 layout.
+ */
+SR_PRIV int sr_metex14_4packets_parse(const uint8_t *buf, float *floatval,
+       struct sr_datafeed_analog *analog, void *info)
+{
+       struct metex14_info *info_local;
+       size_t ch_idx;
+       const uint8_t *ch_buf;
+       int rc;
+
+       info_local = info;
+       ch_idx = info_local->ch_idx;
+       ch_buf = buf + ch_idx * METEX14_PACKET_SIZE;
+       rc = sr_metex14_parse(ch_buf, floatval, analog, info);
+       info_local->ch_idx = ch_idx + 1;
+       return rc;
+}
diff --git a/src/dmm/ms8250d.c b/src/dmm/ms8250d.c
new file mode 100644 (file)
index 0000000..9bdf1d3
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * 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>
+ * Copyright (C) 2018 Stefan Mandl
+ *
+ * This program is free software; you can 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/>.
+ */
+
+/*
+ * MASTECH MS8250D protocol parser.
+ *
+ * Sends 18 bytes.
+ * 40 02 32 75 53 33 35 5303 10 00 00 00 00 00 00 10 00
+ *
+ * - Communication parameters: Unidirectional, 2400/8n1
+ * - CP2102 USB to UART bridge controller
+ */
+
+#include <config.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ms8250d"
+
+/*
+ * Main display (7-segment LCD value): xxDGA xxEF xxxx xxCB
+ * https://en.wikipedia.org/wiki/Seven-segment_display
+ */
+static int parse_digit(uint16_t b)
+{
+       switch (b) {
+       case 0x0: /* 7-segment not active */
+               return 0;
+       case 0x430: /* Overflow */
+               return 0xF;
+       case 0x533:
+               return 0;
+       case 0x003:
+               return 1;
+       case 0x721:
+               return 2;
+       case 0x703:
+               return 3;
+       case 0x213:
+               return 4;
+       case 0x712:
+               return 5;
+       case 0x732:
+               return 6;
+       case 0x103:
+               return 7;
+       case 0x733:
+               return 8;
+       case 0x713:
+               return 9;
+       default:
+               sr_dbg("Invalid digit word: 0x%04x.", b);
+               return -1;
+       }
+}
+
+/* Parse second display. */
+static int parse_digit2(uint16_t b)
+{
+       switch (b) {
+       case 0x00:
+               return 0;
+       case 0x7D:
+               return 0;
+       case 0x05:
+               return 1;
+       case 0x1B:
+               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 second display digit word: 0x%04x.", b);
+               return -1;
+       }
+}
+
+static void parse_flags(const uint8_t *buf, struct ms8250d_info *info)
+{
+       info->is_volt   = (buf[9]  & (1 << 4)) ? 1 : 0;
+       info->is_ohm    = (buf[9]  & (1 << 6)) ? 1 : 0;
+       info->is_ampere = (buf[10] & (1 << 0)) ? 1 : 0;
+       info->is_hz     = (buf[10] & (1 << 2)) ? 1 : 0;
+       info->is_farad  = (buf[10] & (1 << 1)) ? 1 : 0;
+
+       /* Micro */
+       if (!info->is_farad)
+               info->is_micro = (buf[8] & (1 << 4)) ? 1 : 0;
+       else
+               info->is_micro = (buf[9] & (1 << 1)) ? 1 : 0; /* uF */
+
+       info->is_nano  = (buf[8] & (1 << 5)) ? 1 : 0;
+       info->is_milli = (buf[9] & (1 << 0)) ? 1 : 0;
+       info->is_kilo  = (buf[9] & (1 << 2)) ? 1 : 0;
+       info->is_mega  = (buf[8] & (1 << 6)) ? 1 : 0;
+
+       info->is_autotimer = (buf[1]  & (1 << 0)) ? 1 : 0; /* Auto off timer */
+       info->is_rs232     = (buf[1]  & (1 << 1)) ? 1 : 0; /* RS232 via USB */
+       info->is_ac        = (buf[1]  & (1 << 4)) ? 1 : 0;
+       info->is_dc        = (buf[2]  & (1 << 1)) ? 1 : 0;
+       info->is_auto      = (buf[16] & (1 << 4)) ? 1 : 0;
+       info->is_bat       = (buf[1]  & (1 << 5)) ? 1 : 0; /* Low battery */
+       info->is_min       = (buf[16] & (1 << 2)) ? 1 : 0;
+       info->is_max       = (buf[16] & (1 << 1)) ? 1 : 0;
+       info->is_rel       = (buf[15] & (1 << 7)) ? 1 : 0;
+       info->is_hold      = (buf[16] & (1 << 3)) ? 1 : 0;
+       info->is_diode     = (buf[11] & (1 << 0)) ? 1 : 0;
+       info->is_beep      = (buf[11] & (1 << 1)) ? 1 : 0;
+       info->is_ncv       = (buf[0]  & (1 << 0)) ? 1 : 0;
+}
+
+static gboolean flags_valid(const struct ms8250d_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;
+       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 set? */
+       if (!info->is_rs232) {
+               sr_dbg("No RS232 flag detected in packet.");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+               int *exponent, const struct ms8250d_info *info)
+{
+       /* Factors */
+       if (info->is_nano)
+               *exponent -= 9;
+       if (info->is_micro)
+               *exponent -= 6;
+       if (info->is_milli)
+               *exponent -= 3;
+       if (info->is_kilo)
+               *exponent += 3;
+       if (info->is_mega)
+               *exponent += 6;
+       *floatval *= powf(10, *exponent);
+
+       /* Measurement modes */
+       if (info->is_volt) {
+               analog->meaning->mq = SR_MQ_VOLTAGE;
+               analog->meaning->unit = SR_UNIT_VOLT;
+       }
+       if (info->is_ampere) {
+               analog->meaning->mq = SR_MQ_CURRENT;
+               analog->meaning->unit = SR_UNIT_AMPERE;
+       }
+       if (info->is_ohm) {
+               analog->meaning->mq = SR_MQ_RESISTANCE;
+               analog->meaning->unit = SR_UNIT_OHM;
+       }
+       if (info->is_hz) {
+               analog->meaning->mq = SR_MQ_FREQUENCY;
+               analog->meaning->unit = SR_UNIT_HERTZ;
+       }
+       if (info->is_farad) {
+               analog->meaning->mq = SR_MQ_CAPACITANCE;
+               analog->meaning->unit = SR_UNIT_FARAD;
+       }
+       if (info->is_beep) {
+               analog->meaning->mq = SR_MQ_CONTINUITY;
+               analog->meaning->unit = SR_UNIT_BOOLEAN;
+               *floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
+       }
+       if (info->is_diode) {
+               analog->meaning->mq = SR_MQ_VOLTAGE;
+               analog->meaning->unit = SR_UNIT_VOLT;
+       }
+       if (info->is_percent) {
+               analog->meaning->mq = SR_MQ_DUTY_CYCLE;
+               analog->meaning->unit = SR_UNIT_PERCENTAGE;
+       }
+
+       /* Measurement related flags */
+       if (info->is_ac)
+               analog->meaning->mqflags |= SR_MQFLAG_AC;
+       if (info->is_dc)
+               analog->meaning->mqflags |= SR_MQFLAG_DC;
+       if (info->is_auto)
+               analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
+       if (info->is_diode)
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
+       if (info->is_hold)
+               analog->meaning->mqflags |= SR_MQFLAG_HOLD;
+       if (info->is_rel)
+               analog->meaning->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_beep)
+               sr_spew("Beep is active");
+}
+
+SR_PRIV gboolean sr_ms8250d_packet_valid(const uint8_t *buf)
+{
+       struct ms8250d_info info;
+
+       sr_dbg("DMM packet: %02x %02x %02x %02x %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],
+               buf[14], buf[15], buf[16], buf[17]);
+
+       parse_flags(buf, &info);
+
+       if ((buf[17] == 0x00) && flags_valid(&info))
+               return TRUE;
+
+       return FALSE;
+}
+
+/**
+ * Parse a protocol packet.
+ *
+ * @param buf Buffer containing the 18-byte 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 ms8250d_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_ms8250d_parse(const uint8_t *buf, float *floatval,
+               struct sr_datafeed_analog *analog, void *info)
+{
+       int exponent = 0, sec_exponent = 0, sign;
+       float sec_floatval;
+
+       /* buf[0] bar display. */
+       /* buf[1] bar display. */
+
+       /* Parse seven segment digit. */
+       int16_t digit4 = parse_digit(((buf[7] & 0x73) << 4) | (buf[8] & 0x3));
+
+       int16_t digit3 = parse_digit(((buf[6] & 0x07) << 8) | (buf[5] & 0x30) \
+                                       | ((buf[6] & 0x30) >> 4));
+
+       int16_t digit2 = parse_digit(((buf[4] & 0x73) << 4) | (buf[5] & 0x03));
+
+       int16_t digit1 = parse_digit(((buf[3] & 0x07) << 8) | (buf[2] & 0x30) \
+                                       | ((buf[3] & 0x30) >> 4));
+
+       sr_dbg("Digits: %d %d %d %d.", digit1, digit2, digit3, digit4);
+
+       /* Decimal point position. */
+       if ((buf[3] & (1 << 6)) != 0) {
+               exponent = -3;
+               sr_spew("Decimal point after first digit.");
+       } else if ((buf[5] & (1 << 6)) != 0) {
+               exponent = -2;
+               sr_spew("Decimal point after second digit.");
+       } else if ((buf[7] & (1 << 2)) != 0) {
+               exponent = -1;
+               sr_spew("Decimal point after third digit.");
+       } else {
+               exponent = 0;
+               sr_spew("No decimal point in the number.");
+       }
+
+       struct ms8250d_info *info_local;
+
+       info_local = info;
+
+       parse_flags(buf, info_local);
+
+       /* Sign */
+       sign = (buf[0] & (1 << 2)) ? -1 : 1;
+
+       /* Parse second display. */
+       int16_t sec_digit4 = parse_digit2(buf[12] & 0x7F);
+       int16_t sec_digit3 = parse_digit2(buf[13] & 0x7F);
+       int16_t sec_digit2 = parse_digit2(buf[14] & 0x7F);
+       int16_t sec_digit1 = parse_digit2(buf[15] & 0x7F);
+
+       sr_dbg("Digits (2nd display): %d %d %d %d.",
+               sec_digit1, sec_digit2, sec_digit3, sec_digit4);
+
+       /* Second display decimal point position. */
+       if ((buf[14] & (1 << 7)) != 0) {
+               sec_exponent = -3;
+               sr_spew("Sec decimal point after first digit.");
+       } else if ((buf[13] & (1 << 7)) != 0) {
+               sec_exponent = -2;
+               sr_spew("Sec decimal point after second digit.");
+       } else if ((buf[12] & (1 << 7)) != 0) {
+               sec_exponent = -1;
+               sr_spew("Sec decimal point after third digit.");
+       } else {
+               sec_exponent = 0;
+               sr_spew("Sec no decimal point in the number.");
+       }
+
+       *floatval = (double)((digit1 * 1000) + (digit2 * 100) + (digit3 * 10) + digit4);
+
+       sec_floatval = (double)(sec_digit1 * 1000) + (sec_digit2 * 100) + (sec_digit3 * 10) + sec_digit4;
+       sec_floatval *= powf(10, sec_exponent);
+
+       /* Apply sign. */
+       *floatval *= sign;
+
+       handle_flags(analog, floatval, &exponent, info_local);
+
+       /* Check for "OL". */
+       if (digit3 == 0x0F) {
+               sr_spew("Over limit.");
+               *floatval = INFINITY;
+               return SR_OK;
+       }
+
+       sr_spew("The display value is %f.", (double)*floatval);
+       sr_spew("The 2nd display value is %f.", sec_floatval);
+
+       analog->encoding->digits = -exponent;
+       analog->spec->spec_digits = -exponent;
+
+       return SR_OK;
+}
index 271d034c3bbc3c3de899d23dace1b0fa5131f707..ce21273b688a9f63a9f11a942bb5a545dd3e68da 100644 (file)
@@ -132,7 +132,7 @@ SR_PRIV int sr_ut372_parse(const uint8_t *buf, float *floatval,
 
        *floatval = (float) value * powf(10, exponent);
 
-       analog->encoding->digits  = -exponent;
+       analog->encoding->digits = -exponent;
        analog->spec->spec_digits = -exponent;
 
        return SR_OK;
index 9a7d2eab93b0776bd56e1a75fcaf9fea86887cb1..ab7ac3a8741aa0d85f74cc5f5e45e1975c2a054a 100644 (file)
@@ -284,7 +284,7 @@ static void handle_flags(struct sr_datafeed_analog *analog,
        if (info->is_auto)
                analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
        if (info->is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
 }
 
 static gboolean flags_valid(const struct ut71x_info *info)
@@ -336,7 +336,7 @@ SR_PRIV int sr_ut71x_parse(const uint8_t *buf, float *floatval,
        int ret, exponent = 0;
        struct ut71x_info *info_local;
 
-       info_local = (struct ut71x_info *)info;
+       info_local = info;
        memset(info_local, 0, sizeof(struct ut71x_info));
 
        if (!sr_ut71x_packet_valid(buf))
index b0095b418dbcd162014074dfcbcf991c8bae76ef..d9c3d5ac77199b8897958933053a403e090e0f8b 100644 (file)
@@ -360,7 +360,7 @@ static void handle_flags(struct sr_datafeed_analog *analog,
        if (info->is_auto)
                analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
        if (info->is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        if (info->is_hold)
                /*
                 * Note: HOLD only affects the number displayed on the LCD,
@@ -410,7 +410,7 @@ SR_PRIV int sr_vc870_parse(const uint8_t *buf, float *floatval,
        int ret, exponent = 0;
        struct vc870_info *info_local;
 
-       info_local = (struct vc870_info *)info;
+       info_local = info;
        memset(info_local, 0, sizeof(struct vc870_info));
 
        if (!sr_vc870_packet_valid(buf))
diff --git a/src/dmm/vc96.c b/src/dmm/vc96.c
new file mode 100644 (file)
index 0000000..8e51097
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2018 Matthias Schulz <matthschulz@arcor.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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Voltcraft 13-bytes ASCII protocol parser.
+ *
+ * Bytes 1-3 measuring mode, byte 4 '-' for negative,
+ * bytes 5-9 value, bytes 10-11 unit, bytes 12-13 CRLF 0d 0a.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "vc96"
+
+/** Parse value from buf, byte 3-8. */
+static int parse_value(const uint8_t *buf, struct vc96_info *info,
+                       float *result, int *exponent)
+{
+       int i, is_ol, cnt, dot_pos;
+       char valstr[8 + 1];
+
+       (void)info;
+
+       /* Strip all spaces from bytes 3-8. */
+       memset(&valstr, 0, 6 + 1);
+       for (i = 0, cnt = 0; i < 6; i++) {
+               if (buf[3 + i] != ' ')
+                       valstr[cnt++] = buf[3 + i];
+       }
+
+       /* Bytes 5-7: Over limit (various forms) */
+       is_ol = 0;
+       is_ol += (!g_ascii_strcasecmp((const char *)&valstr, ".OL")) ? 1 : 0;
+       is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "O.L")) ? 1 : 0;
+       is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "OL.")) ? 1 : 0;
+       is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "OL")) ? 1 : 0;
+       is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-.OL")) ? 1 : 0;
+       is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-O.L")) ? 1 : 0;
+       is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-OL.")) ? 1 : 0;
+       is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-OL")) ? 1 : 0;
+       if (is_ol != 0) {
+               sr_spew("Over limit.");
+               *result = INFINITY;
+               return SR_OK;
+       }
+
+       /* Bytes 3-10: Sign, value (up to 5 digits) and decimal point */
+       sr_atof_ascii((const char *)&valstr, result);
+
+       dot_pos = strcspn(valstr, ".");
+       if (dot_pos < cnt)
+               *exponent = -(cnt - dot_pos - 1);
+       else
+               *exponent = 0;
+
+       sr_spew("The display value is %f.", *result);
+
+       return SR_OK;
+}
+
+static void parse_flags(const char *buf, struct vc96_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 0-2: Measurement mode DIO, OHM */
+       info->is_ohm = !strncmp(buf, "OHM", 3);
+       info->is_diode = !strncmp(buf, "DIO", 3);
+       info->is_hfe = !strncmp(buf, "hfe", 3);
+
+       /* Bytes 3-8: See parse_value(). */
+
+       /* Strip all spaces from bytes 9-10. */
+       memset(&unit, 0, 2 + 1);
+       for (i = 0, cnt = 0; i < 2; i++) {
+               if (buf[9 + i] != ' ')
+                       unit[cnt++] = buf[9 + i];
+       }
+       sr_spew("Bytes 9..10 without spaces \"%.4s\".", unit);
+
+       /* Bytes 9-10: Unit */
+       u = (const char *)&unit;
+       if (!g_ascii_strcasecmp(u, "A"))
+               info->is_ampere = TRUE;
+       else if (!g_ascii_strcasecmp(u, "mA"))
+               info->is_milli = info->is_ampere = TRUE;
+       else if (!g_ascii_strcasecmp(u, "uA"))
+               info->is_micro = info->is_ampere = TRUE;
+       else if (!g_ascii_strcasecmp(u, "V"))
+               info->is_volt = TRUE;
+       else if (!g_ascii_strcasecmp(u, "mV"))
+               info->is_milli = info->is_volt = TRUE;
+       else if (!g_ascii_strcasecmp(u, "K"))
+               info->is_kilo = TRUE;
+       else if (!g_ascii_strcasecmp(u, "M"))
+               info->is_mega = TRUE;
+       else if (!g_ascii_strcasecmp(u, ""))
+               info->is_unitless = TRUE;
+
+       /* Bytes 0-2: Measurement mode, except AC/DC */
+       info->is_resistance = !strncmp(buf, "OHM", 3) ||
+               (!strncmp(buf, "  ", 3) && info->is_ohm);
+       info->is_diode = !strncmp(buf, "DIO", 3) ||
+               (!strncmp(buf, "  ", 3) && info->is_volt && info->is_milli);
+       info->is_hfe = !strncmp(buf, "hfe", 3) ||
+               (!strncmp(buf, "  ", 3) && !info->is_ampere && !info->is_volt &&
+               !info->is_resistance && !info->is_diode);
+
+       /*
+        * Note:
+        * - Protocol doesn't distinguish "resistance" from "beep" mode.
+        */
+
+       /* Byte 12: Always '\r' (carriage return, 0x0d, 12) */
+       /* Byte 13: Always '\n' (carriage return, 0x0a, 13) */
+}
+
+static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+                        int *exponent, const struct vc96_info *info)
+{
+       int factor;
+
+       (void)exponent;
+
+       /* Factors */
+       factor = 0;
+       if (info->is_micro)
+               factor -= 6;
+       if (info->is_milli)
+               factor -= 3;
+       if (info->is_kilo)
+               factor += 3;
+       if (info->is_mega)
+               factor += 6;
+       *floatval *= powf(10, factor);
+
+       /* Measurement modes */
+       if (info->is_volt) {
+               analog->meaning->mq = SR_MQ_VOLTAGE;
+               analog->meaning->unit = SR_UNIT_VOLT;
+       }
+       if (info->is_ampere) {
+               analog->meaning->mq = SR_MQ_CURRENT;
+               analog->meaning->unit = SR_UNIT_AMPERE;
+       }
+       if (info->is_ohm) {
+               analog->meaning->mq = SR_MQ_RESISTANCE;
+               analog->meaning->unit = SR_UNIT_OHM;
+       }
+       if (info->is_diode) {
+               analog->meaning->mq = SR_MQ_VOLTAGE;
+               analog->meaning->unit = SR_UNIT_VOLT;
+       }
+       if (info->is_hfe) {
+               analog->meaning->mq = SR_MQ_GAIN;
+               analog->meaning->unit = SR_UNIT_UNITLESS;
+       }
+
+       /* Measurement related flags */
+       if (info->is_ac)
+               analog->meaning->mqflags |= SR_MQFLAG_AC;
+       if (info->is_dc)
+               analog->meaning->mqflags |= SR_MQFLAG_DC;
+       if (info->is_diode)
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
+}
+
+static gboolean flags_valid(const struct vc96_info *info)
+{
+       int count;
+
+       /* Does the packet have more than one multiplier? */
+       count = 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_diode) ? 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;
+}
+
+SR_PRIV gboolean sr_vc96_packet_valid(const uint8_t *buf)
+{
+       struct vc96_info info;
+
+       memset(&info, 0x00, sizeof(struct vc96_info));
+       parse_flags((const char *)buf, &info);
+
+       if (!flags_valid(&info))
+               return FALSE;
+
+       if ((buf[11] != '\r') || (buf[12] != '\n'))
+               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 vc96_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_vc96_parse(const uint8_t *buf, float *floatval,
+                       struct sr_datafeed_analog *analog, void *info)
+{
+       int ret, exponent = 0;
+       struct vc96_info *info_local;
+
+       info_local = info;
+
+       /* Don't print byte 12 + 13. Those contain the CR LF. */
+       sr_dbg("DMM packet: \"%.11s\".", buf);
+
+       memset(info_local, 0x00, sizeof(struct vc96_info));
+
+       if ((ret = parse_value(buf, info_local, floatval, &exponent)) < 0) {
+               sr_dbg("Error parsing value: %d.", ret);
+               return ret;
+       }
+
+       parse_flags((const char *)buf, info_local);
+       handle_flags(analog, floatval, &exponent, info_local);
+
+       analog->encoding->digits = -exponent;
+       analog->spec->spec_digits = -exponent;
+
+       return SR_OK;
+}
diff --git a/src/driver_list_start.c b/src/driver_list_start.c
new file mode 100644 (file)
index 0000000..7b6c775
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.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 <config.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/*
+ * This marks the start of the driver list. This file must be linked
+ * before any actual drivers.
+ */
+
+SR_PRIV const struct sr_dev_driver *sr_driver_list__start[]
+       __attribute__((section (SR_DRIVER_LIST_SECTION),
+                      used, aligned(sizeof(struct sr_dev_driver *))))
+  = { NULL /* Dummy item, as zero length arrays are not allowed by C99 */ };
diff --git a/src/driver_list_stop.c b/src/driver_list_stop.c
new file mode 100644 (file)
index 0000000..ff2c51d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.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 <config.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/*
+ * This marks the end of the driver list. This file must be linked
+ * after any actual drivers.
+ */
+
+SR_PRIV const struct sr_dev_driver *sr_driver_list__stop[]
+       __attribute__((section (SR_DRIVER_LIST_SECTION),
+                      used, aligned(sizeof(struct sr_dev_driver *))))
+  = { NULL /* Dummy item, as zero length arrays are not allowed by C99 */ };
index c6165b6360c71778b63b5ffcf7b656a6b06a72a2..7641e24d0d31f4cca76a094fbafffebfef2ebc7f 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2016 Lars-Peter Clausen <lars@metafoo.de>
  * Copyright (C) 2016 Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.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
 /*
  * sr_driver_list is a special section contains pointers to all the hardware
  * drivers built into the library. The __start and __stop symbols are
- * auto-generated by the linker (OSX needs a little help) and point to the start
- * and end of the section. They are used to iterate over the list of all
- * drivers.
+ * created from driver_list_start.c and driver_list_stop.c, and point to the
+ * start and end of the section. They are used to iterate over the list of
+ * all drivers.
  */
-#ifdef __APPLE__
-extern struct sr_dev_driver *__start_sr_driver_list __asm("section$start$__DATA$__sr_driver_list");
-extern struct sr_dev_driver *__stop_sr_driver_list __asm("section$end$__DATA$__sr_driver_list");
-#else
-extern struct sr_dev_driver *__start_sr_driver_list;
-extern struct sr_dev_driver *__stop_sr_driver_list;
-#endif
+SR_PRIV extern const struct sr_dev_driver *sr_driver_list__start[];
+SR_PRIV extern const struct sr_dev_driver *sr_driver_list__stop[];
 
 /** @private
  * Initialize the driver list in a fresh libsigrok context.
@@ -49,8 +45,8 @@ SR_API void sr_drivers_init(struct sr_context *ctx)
 
        array = g_array_new(TRUE, FALSE, sizeof(struct sr_dev_driver *));
 #ifdef HAVE_DRIVERS
-       for (struct sr_dev_driver **drivers = &__start_sr_driver_list;
-            drivers < &__stop_sr_driver_list; drivers++)
+       for (const struct sr_dev_driver **drivers = sr_driver_list__start + 1;
+            drivers < sr_driver_list__stop; drivers++)
                g_array_append_val(array, *drivers);
 #endif
        ctx->driver_list = (struct sr_dev_driver **)array->data;
index aaba328192789dcc575ac7a595cea14c4e6ef998..c410b37c02a02276810f9f40e9e2c1655057f007 100644 (file)
@@ -51,11 +51,7 @@ static const uint64_t samplerates[] = {
 };
 
 static const char *data_sources[] = {
-       "Live",
-       "Log-Hand",
-       "Log-Trig",
-       "Log-Auto",
-       "Log-Export",
+       "Live", "Log-Hand", "Log-Trig", "Log-Auto", "Log-Export",
 };
 
 extern const struct agdmm_job agdmm_jobs_live[];
@@ -187,25 +183,22 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, devices);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       int ret;
 
        (void)cg;
 
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_SAMPLERATE:
                *data = g_variant_new_uint64(devc->cur_samplerate);
                break;
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
-               ret = sr_sw_limits_config_get(&devc->limits, key, data);
-               break;
+               return sr_sw_limits_config_get(&devc->limits, key, data);
        case SR_CONF_DATA_SOURCE:
                *data = g_variant_new_string(data_sources[devc->data_source]);
                break;
@@ -213,91 +206,54 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        uint64_t samplerate;
-       const char *tmp_str;
-       unsigned int i;
-       int ret;
+       int idx;
 
        (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:
                samplerate = g_variant_get_uint64(data);
                if (samplerate < samplerates[0] || samplerate > samplerates[1])
-                       ret = SR_ERR_ARG;
-               else
-                       devc->cur_samplerate = g_variant_get_uint64(data);
+                       return SR_ERR_ARG;
+               devc->cur_samplerate = g_variant_get_uint64(data);
                break;
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
-               ret = sr_sw_limits_config_set(&devc->limits, key, data);
-               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;
+               return sr_sw_limits_config_set(&devc->limits, key, data);
+       case SR_CONF_DATA_SOURCE:
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+                       return SR_ERR_ARG;
+               devc->data_source = idx;
                break;
-       }
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       GVariant *gvar;
-       GVariantBuilder gvb;
-
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (!sdi || cg)
-               return SR_ERR_ARG;
-
        switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
                break;
        case SR_CONF_DATA_SOURCE:
-               *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
                break;
        default:
                return SR_ERR_NA;
@@ -311,9 +267,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc = sdi->priv;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc->cur_channel = sr_next_enabled_channel(sdi, NULL);
        devc->cur_conf = sr_next_enabled_channel(sdi, NULL);
        devc->cur_sample = 1;
@@ -338,7 +291,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* Poll every 10ms, or whenever some data comes in. */
        serial = sdi->conn;
        serial_source_add(sdi->session, serial, G_IO_IN, 10,
                        agdmm_receive_data, (void *)sdi);
@@ -354,7 +306,7 @@ static struct sr_dev_driver agdmm_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 439cbd00ec621f5a70d8cb1860ce832a80272adc..764136e8f437734b9537b5001265060ef6cd6b9e 100644 (file)
@@ -77,7 +77,7 @@ static const struct agdmm_job *job_next(struct dev_context *devc)
                devc->current_job++;
                if (!job_current(devc)->send)
                        devc->current_job = 0;
-       } while(job_in_interval(devc) && devc->current_job != current_job);
+       } while (job_in_interval(devc) && devc->current_job != current_job);
        return job_current(devc);
 }
 
@@ -200,7 +200,7 @@ SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits) || stop)
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        else
                dispatch(sdi);
 
@@ -600,7 +600,7 @@ static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
        } else if (!strcmp(mstr, "DIOD")) {
                devc->cur_mq[i] = SR_MQ_VOLTAGE;
                devc->cur_unit[i] = SR_UNIT_VOLT;
-               devc->cur_mqflags[i] = SR_MQFLAG_DIODE;
+               devc->cur_mqflags[i] = SR_MQFLAG_DIODE | SR_MQFLAG_DC;
                devc->cur_exponent[i] = 0;
                devc->cur_digits[i] = 3;
        } else if (!strcmp(mstr, "CAP")) {
@@ -626,7 +626,7 @@ static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
                } else if (!strcmp(mstr, "DC")) {
                        devc->cur_mqflags[i] |= SR_MQFLAG_DC;
                } else {
-               sr_dbg("Unknown first argument '%s'.", mstr);
+                       sr_dbg("Unknown first argument '%s'.", mstr);
                }
                g_free(mstr);
        } else
@@ -738,7 +738,7 @@ static int recv_conf_u124x_5x(const struct sr_dev_inst *sdi, GMatchInfo *match)
        } else if (!strcmp(mstr, "DIOD")) {
                devc->cur_mq[i] = SR_MQ_VOLTAGE;
                devc->cur_unit[i] = SR_UNIT_VOLT;
-               devc->cur_mqflags[i] = SR_MQFLAG_DIODE;
+               devc->cur_mqflags[i] = SR_MQFLAG_DIODE | SR_MQFLAG_DC;
                devc->cur_exponent[i] = 0;
                if (devc->profile->model == KEYSIGHT_U1281 ||
                    devc->profile->model == KEYSIGHT_U1282) {
@@ -871,7 +871,7 @@ static int recv_log(const struct sr_dev_inst *sdi, GMatchInfo *match,
        if (mstr[12] & 1)  mqflags |= SR_MQFLAG_AVG;
        if (mstr[12] & 2)  mqflags |= SR_MQFLAG_MIN;
        if (mstr[12] & 4)  mqflags |= SR_MQFLAG_MAX;
-       if (function == 5) mqflags |= SR_MQFLAG_DIODE;
+       if (function == 5) mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        g_free(mstr);
 
        mq = mqs[function];
index 2510d7360fd5cd940bfe246184d20221b6c5dcc1..85b5235743eecd703671f97fd38eba6c6125eb8c 100644 (file)
@@ -68,13 +68,11 @@ struct agdmm_profile {
        const struct agdmm_recv *recvs;
 };
 
-/* Private, per-device-instance driver context. */
 struct dev_context {
        const struct agdmm_profile *profile;
        struct sr_sw_limits limits;
        int data_source;
 
-       /* Runtime. */
        const struct agdmm_job *jobs;
        int current_job;
        gboolean job_running;
index 27fb992a67307c658038540bba4d069dea63e50e..ddf3fe4a11885468a041838e1ab8d01a0300c2ea 100644 (file)
@@ -109,8 +109,8 @@ scan_cleanup:
        return std_scan_complete(di, devices);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
 
@@ -130,35 +130,25 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       const char *tmp_str;
-       unsigned int i;
+       int idx;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
                return sr_sw_limits_config_set(&devc->limits, key, data);
-       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;
+       case SR_CONF_DATA_SOURCE:
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+                       return SR_ERR_ARG;
+               devc->data_source = idx;
                break;
-       }
        default:
                return SR_ERR_NA;
        }
@@ -166,26 +156,15 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       (void)cg;
-
        switch (key) {
        case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi)
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               else
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        case SR_CONF_DATA_SOURCE:
-               *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
                break;
        default:
                return SR_ERR_NA;
@@ -200,16 +179,12 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
 
        serial = sdi->conn;
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->limits);
 
        std_session_send_df_header(sdi);
 
-       /* 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);
 
@@ -224,7 +199,7 @@ static struct sr_dev_driver appa_55ii_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index c22ede50fcef1179fb6c499f07386d4591521e96..f31b08b806b9199669467d5bec567c9b16c260db 100644 (file)
@@ -224,7 +224,7 @@ static void appa_55ii_log_end(struct sr_dev_inst *sdi)
        if (devc->data_source != DATA_SOURCE_MEMORY)
                return;
 
-       sdi->driver->dev_acquisition_stop(sdi);
+       sr_dev_acquisition_stop(sdi);
 }
 
 static const uint8_t *appa_55ii_parse_data(struct sr_dev_inst *sdi,
@@ -309,7 +309,7 @@ SR_PRIV int appa_55ii_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits)) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        }
 
index 248d672e1970b15e38352ddf61b1de02e3ca7396..171ac63670cb959391e8cfc640144042f8846c37 100644 (file)
@@ -36,13 +36,10 @@ enum {
        DATA_SOURCE_MEMORY,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Acquisition settings */
        struct sr_sw_limits limits;
        gboolean data_source; /**< Whether to read live samples or memory */
 
-       /* Temporary state across callbacks */
        uint8_t buf[APPA_55II_BUF_SIZE];
        unsigned int buf_len;
        uint8_t log_buf[64];
index 1a5281f80185e0cabf678f7a6978cafdc3761297..d6f95723d83ec9b7603cf4234f34ff1d144c0e8d 100644 (file)
@@ -25,6 +25,7 @@
 
 #define CMD_VERSION "version\r\n"
 #define CMD_MONITOR "monitor 200\r\n"
+#define CMD_MONITOR_STOP "monitor 0\r\n"
 
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
@@ -43,7 +44,7 @@ static const uint32_t devopts[] = {
 
 static const uint32_t devopts_cg[] = {
        SR_CONF_ENABLED | SR_CONF_SET,
-       SR_CONF_REGULATION | SR_CONF_GET,
+       SR_CONF_REGULATION | SR_CONF_GET | SR_CONF_LIST,
        SR_CONF_VOLTAGE | SR_CONF_GET,
        SR_CONF_CURRENT | SR_CONF_GET,
        SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
@@ -53,6 +54,12 @@ static const uint32_t devopts_cg[] = {
        SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET,
        SR_CONF_UNDER_VOLTAGE_CONDITION | SR_CONF_GET,
        SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE | SR_CONF_GET,
+       SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const char *regulation[] = {
+       /* CC mode only. */
+       "CC",
 };
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
@@ -94,6 +101,19 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
        serial_flush(serial);
 
+       /*
+        * First stop potentially running monitoring and wait for 50ms before
+        * next command can be sent.
+        */
+       if (serial_write_blocking(serial, CMD_MONITOR_STOP,
+                       strlen(CMD_MONITOR_STOP), serial_timeout(serial,
+                       strlen(CMD_MONITOR_STOP))) < (int)strlen(CMD_MONITOR_STOP)) {
+               sr_dbg("Unable to write while probing for hardware.");
+               serial_close(serial);
+               return NULL;
+       }
+       g_usleep(50 * 1000);
+
        if (serial_write_blocking(serial, CMD_VERSION,
                        strlen(CMD_VERSION), serial_timeout(serial,
                        strlen(CMD_VERSION))) < (int)strlen(CMD_VERSION)) {
@@ -135,7 +155,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
        cg->channels = g_slist_append(cg->channels, ch);
 
-       ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "I");
+       ch = sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I");
        cg->channels = g_slist_append(cg->channels, ch);
 
        devc = g_malloc0(sizeof(struct dev_context));
@@ -150,64 +170,34 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       GVariantBuilder gvb;
-       int ret;
-
-       /* Always available. */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (!sdi)
-               return SR_ERR_ARG;
-
-       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_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       break;
-               default:
-                       return SR_ERR_NA;
-               }
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        } else {
                switch (key) {
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
+                       break;
+               case SR_CONF_REGULATION:
+                       *data = std_gvar_array_str(ARRAY_AND_SIZE(regulation));
                        break;
                case SR_CONF_CURRENT_LIMIT:
-                       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-                       /* Min, max, step. */
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.0));
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(6.0));
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.001)); /* 1mA steps */
-                       *data = g_variant_builder_end(&gvb);
+                       *data = std_gvar_min_max_step(0.0, 6.0, 0.001);
+                       break;
+               case SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD:
+                       *data = std_gvar_min_max_step(0.0, 60.0, 0.001);
                        break;
                default:
                        return SR_ERR_NA;
                }
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_get(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       int ret;
        float fvalue;
 
        (void)cg;
@@ -223,7 +213,6 @@ static int config_get(uint32_t key, GVariant **data,
         *  - SR_CONF_ENABLED (state cannot be queried, only set)
         */
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
@@ -258,48 +247,59 @@ static int config_get(uint32_t key, GVariant **data,
                *data = g_variant_new_boolean(devc->otp_active);
                break;
        case SR_CONF_UNDER_VOLTAGE_CONDITION:
-               *data = g_variant_new_boolean(TRUE); /* Always on. */
+               if (reloadpro_get_under_voltage_threshold(sdi, &fvalue) == SR_OK)
+                       *data = g_variant_new_boolean(fvalue != 0.0);
                break;
        case SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE:
                *data = g_variant_new_boolean(devc->uvc_active);
                break;
+       case SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD:
+               if (reloadpro_get_under_voltage_threshold(sdi, &fvalue) == SR_OK)
+                       *data = g_variant_new_double(fvalue);
+               break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_set(uint32_t key, GVariant *data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       int ret;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
                return sr_sw_limits_config_set(&devc->limits, key, data);
        case SR_CONF_ENABLED:
-               ret = reloadpro_set_on_off(sdi, g_variant_get_boolean(data));
-               break;
+               return reloadpro_set_on_off(sdi, g_variant_get_boolean(data));
        case SR_CONF_CURRENT_LIMIT:
-               ret = reloadpro_set_current_limit(sdi,
+               return reloadpro_set_current_limit(sdi, g_variant_get_double(data));
+       case SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD:
+               return reloadpro_set_under_voltage_threshold(sdi,
                        g_variant_get_double(data));
-               break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       if (serial_write_blocking(sdi->conn, CMD_MONITOR_STOP,
+                       strlen(CMD_MONITOR_STOP), serial_timeout(sdi->conn,
+                       strlen(CMD_MONITOR_STOP))) < (int)strlen(CMD_MONITOR_STOP)) {
+               sr_dbg("Unable to stop monitoring.");
+       }
+
+       return std_serial_dev_close(sdi);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -308,10 +308,9 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        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->acquisition_running = TRUE;
+
        serial = sdi->conn;
 
        /* Send the 'monitor <ms>' command (doesn't have a reply). */
@@ -322,19 +321,34 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                return SR_ERR;
        }
 
-       /* Poll every 100ms, or whenever some data comes in. */
-       serial_source_add(sdi->session, serial, G_IO_IN, 100,
-                         reloadpro_receive_data, (void *)sdi);
-
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
        memset(devc->buf, 0, RELOADPRO_BUFSIZE);
        devc->buflen = 0;
 
+       g_mutex_init(&devc->acquisition_mutex);
+
+       serial_source_add(sdi->session, serial, G_IO_IN, 100,
+                         reloadpro_receive_data, (void *)sdi);
+
        return SR_OK;
 }
 
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       int ret;
+
+       devc = sdi->priv;
+       devc->acquisition_running = FALSE;
+
+       ret = std_serial_dev_acquisition_stop(sdi);
+       g_mutex_clear(&devc->acquisition_mutex);
+
+       return ret;
+}
+
 static struct sr_dev_driver arachnid_labs_re_load_pro_driver_info = {
        .name = "arachnid-labs-re-load-pro",
        .longname = "Arachnid Labs Re:load Pro",
@@ -343,13 +357,14 @@ static struct sr_dev_driver arachnid_labs_re_load_pro_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
        .dev_open = std_serial_dev_open,
-       .dev_close = std_serial_dev_close,
+       .dev_close = dev_close,
        .dev_acquisition_start = dev_acquisition_start,
-       .dev_acquisition_stop = std_serial_dev_acquisition_stop,
+       .dev_acquisition_stop = dev_acquisition_stop,
        .context = NULL,
 };
 SR_REGISTER_DEV_DRIVER(arachnid_labs_re_load_pro_driver_info);
index dd0ad409508ffc37a4376e8d3d890f49d77c028e..998ba17119113956bd1f3f17a57f9cb19223f0ad 100644 (file)
  */
 
 #include <config.h>
+#include <math.h>
 #include <string.h>
 #include "protocol.h"
 
-#define READ_TIMEOUT_MS 1000
+#define READ_TIMEOUT_MS 500
 
 static int send_cmd(const struct sr_dev_inst *sdi, const char *cmd,
                char *replybuf, int replybufsize)
 {
        char *bufptr;
        int len, ret;
+       struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
 
+       devc = sdi->priv;
        serial = sdi->conn;
 
        /* Send the command (blocking, with timeout). */
@@ -40,82 +43,182 @@ static int send_cmd(const struct sr_dev_inst *sdi, const char *cmd,
                return SR_ERR;
        }
 
-       /* Read the reply (blocking, with timeout). */
-       memset(replybuf, 0, replybufsize);
-       bufptr = replybuf;
-       len = replybufsize;
-       ret = serial_readline(serial, &bufptr, &len, READ_TIMEOUT_MS);
-
-       /* If we got 0 characters (possibly one \r or \n), retry once. */
-       if (len == 0) {
+       if (!devc->acquisition_running) {
+               /* Read the reply (blocking, with timeout). */
+               memset(replybuf, 0, replybufsize);
+               bufptr = replybuf;
                len = replybufsize;
                ret = serial_readline(serial, &bufptr, &len, READ_TIMEOUT_MS);
-       }
 
-       if (g_str_has_prefix((const char *)&bufptr, "err ")) {
-               sr_err("Device replied with an error: '%s'.", bufptr);
-               return SR_ERR;
+               /* If we got 0 characters (possibly one \r or \n), retry once. */
+               if (len == 0) {
+                       len = replybufsize;
+                       ret = serial_readline(serial, &bufptr, &len, READ_TIMEOUT_MS);
+               }
+
+               if (g_str_has_prefix((const char *)&bufptr, "err ")) {
+                       sr_err("Device replied with an error: '%s'.", bufptr);
+                       return SR_ERR;
+               }
        }
 
        return ret;
 }
 
 SR_PRIV int reloadpro_set_current_limit(const struct sr_dev_inst *sdi,
-                                       float current)
+                                       float current_limit)
 {
+       struct dev_context *devc;
        int ret, ma;
        char buf[100];
        char *cmd;
 
-       if (current < 0 || current > 6) {
-               sr_err("The current limit must be 0-6 A (was %f A).", current);
+       devc = sdi->priv;
+
+       if (current_limit < 0 || current_limit > 6) {
+               sr_err("The current limit must be 0-6 A (was %f A).", current_limit);
                return SR_ERR_ARG;
        }
 
-       /* Hardware expects current in mA, integer (0..6000). */
-       ma = (int)(current * 1000);
-
-       sr_err("Setting current limit to %f A (%d mA).", current, ma);
+       /* Hardware expects current limit in mA, integer (0..6000). */
+       ma = (int)round(current_limit * 1000);
 
        cmd = g_strdup_printf("set %d\n", ma);
-       if ((ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf))) < 0) {
+       g_mutex_lock(&devc->acquisition_mutex);
+       ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf));
+       g_mutex_unlock(&devc->acquisition_mutex);
+       g_free(cmd);
+
+       if (ret < 0) {
                sr_err("Error sending current limit command: %d.", ret);
-               g_free(cmd);
                return SR_ERR;
        }
-       g_free(cmd);
-
        return SR_OK;
 }
 
 SR_PRIV int reloadpro_set_on_off(const struct sr_dev_inst *sdi, gboolean on)
 {
+       struct dev_context *devc;
        int ret;
        char buf[100];
        const char *cmd;
 
+       devc = sdi->priv;
+
        cmd = (on) ? "on\n" : "off\n";
-       if ((ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf))) < 0) {
+       g_mutex_lock(&devc->acquisition_mutex);
+       ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf));
+       g_mutex_unlock(&devc->acquisition_mutex);
+
+       if (ret < 0) {
                sr_err("Error sending on/off command: %d.", ret);
                return SR_ERR;
        }
+       return SR_OK;
+}
+
+SR_PRIV int reloadpro_set_under_voltage_threshold(const struct sr_dev_inst *sdi,
+                                       float voltage)
+{
+       struct dev_context *devc;
+       int ret, mv;
+       char buf[100];
+       char *cmd;
+
+       devc = sdi->priv;
+
+       if (voltage < 0 || voltage > 60) {
+               sr_err("The under voltage threshold must be 0-60 V (was %f V).",
+                       voltage);
+               return SR_ERR_ARG;
+       }
+
+       /* Hardware expects voltage in mV, integer (0..60000). */
+       mv = (int)round(voltage * 1000);
+
+       sr_spew("Setting under voltage threshold to %f V (%d mV).", voltage, mv);
+
+       cmd = g_strdup_printf("uvlo %d\n", mv);
+       g_mutex_lock(&devc->acquisition_mutex);
+       ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf));
+       g_mutex_unlock(&devc->acquisition_mutex);
+       g_free(cmd);
 
+       if (ret < 0) {
+               sr_err("Error sending under voltage threshold command: %d.", ret);
+               return SR_ERR;
+       }
        return SR_OK;
 }
 
 SR_PRIV int reloadpro_get_current_limit(const struct sr_dev_inst *sdi,
-                                       float *current)
+                                       float *current_limit)
 {
+       struct dev_context *devc;
        int ret;
        char buf[100];
+       gint64 end_time;
 
+       devc = sdi->priv;
+
+       g_mutex_lock(&devc->acquisition_mutex);
        if ((ret = send_cmd(sdi, "set\n", (char *)&buf, sizeof(buf))) < 0) {
                sr_err("Error sending current limit query: %d.", ret);
                return SR_ERR;
        }
 
-       /* Hardware sends current in mA, integer (0..6000). */
-       *current = g_ascii_strtod(buf + 4, NULL) / 1000;
+       if (devc->acquisition_running) {
+               end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
+               if (!g_cond_wait_until(&devc->current_limit_cond,
+                               &devc->acquisition_mutex, end_time)) {
+                       /* Timeout has passed. */
+                       g_mutex_unlock(&devc->acquisition_mutex);
+                       return SR_ERR;
+               }
+       } else {
+               /* Hardware sends current limit in mA, integer (0..6000). */
+               devc->current_limit = g_ascii_strtod(buf + 4, NULL) / 1000;
+       }
+       g_mutex_unlock(&devc->acquisition_mutex);
+
+       if (current_limit)
+               *current_limit = devc->current_limit;
+
+       return SR_OK;
+}
+
+SR_PRIV int reloadpro_get_under_voltage_threshold(const struct sr_dev_inst *sdi,
+                                       float *uvc_threshold)
+{
+       struct dev_context *devc;
+       int ret;
+       char buf[100];
+       gint64 end_time;
+
+       devc = sdi->priv;
+
+       g_mutex_lock(&devc->acquisition_mutex);
+       if ((ret = send_cmd(sdi, "uvlo\n", (char *)&buf, sizeof(buf))) < 0) {
+               sr_err("Error sending under voltage threshold query: %d.", ret);
+               return SR_ERR;
+       }
+
+       if (devc->acquisition_running) {
+               end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
+               if (!g_cond_wait_until(&devc->uvc_threshold_cond,
+                               &devc->acquisition_mutex, end_time)) {
+                       /* Timeout has passed. */
+                       g_mutex_unlock(&devc->acquisition_mutex);
+                       return SR_ERR;
+               }
+       } else {
+               /* Hardware sends voltage in mV, integer (0..60000). */
+               devc->uvc_threshold = g_ascii_strtod(buf + 5, NULL) / 1000;
+       }
+       g_mutex_unlock(&devc->acquisition_mutex);
+
+       if (uvc_threshold)
+               *uvc_threshold = devc->uvc_threshold;
 
        return SR_OK;
 }
@@ -123,29 +226,72 @@ SR_PRIV int reloadpro_get_current_limit(const struct sr_dev_inst *sdi,
 SR_PRIV int reloadpro_get_voltage_current(const struct sr_dev_inst *sdi,
                float *voltage, float *current)
 {
+       struct dev_context *devc;
        int ret;
        char buf[100];
        char **tokens;
+       gint64 end_time;
+
+       devc = sdi->priv;
 
+       g_mutex_lock(&devc->acquisition_mutex);
        if ((ret = send_cmd(sdi, "read\n", (char *)&buf, sizeof(buf))) < 0) {
                sr_err("Error sending voltage/current query: %d.", ret);
                return SR_ERR;
        }
 
-       /* Reply: "read <current> <voltage>". */
-       tokens = g_strsplit((const char *)&buf, " ", 3);
+       if (devc->acquisition_running) {
+               end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
+               if (!g_cond_wait_until(&devc->voltage_cond,
+                               &devc->acquisition_mutex, end_time)) {
+                       /* Timeout has passed. */
+                       g_mutex_unlock(&devc->acquisition_mutex);
+                       return SR_ERR;
+               }
+       } else {
+               /* Reply: "read <current> <voltage>". */
+               tokens = g_strsplit((const char *)&buf, " ", 3);
+               devc->voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
+               devc->current = g_ascii_strtod(tokens[1], NULL) / 1000;
+               g_strfreev(tokens);
+       }
+       g_mutex_unlock(&devc->acquisition_mutex);
+
        if (voltage)
-               *voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
+               *voltage = devc->voltage;
        if (current)
-               *current = g_ascii_strtod(tokens[1], NULL) / 1000;
-       g_strfreev(tokens);
+               *current = devc->current;
 
        return SR_OK;
 }
 
+static int send_config_update_key(const struct sr_dev_inst *sdi,
+               uint32_t key, GVariant *var)
+{
+       struct sr_config *cfg;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_meta meta;
+       int ret;
+
+       cfg = sr_config_new(key, var);
+       if (!cfg)
+               return SR_ERR;
+
+       memset(&meta, 0, sizeof(meta));
+
+       packet.type = SR_DF_META;
+       packet.payload = &meta;
+
+       meta.config = g_slist_append(meta.config, cfg);
+
+       ret = sr_session_send(sdi, &packet);
+       sr_config_free(cfg);
+
+       return ret;
+}
+
 static void handle_packet(const struct sr_dev_inst *sdi)
 {
-       float voltage, current;
        struct sr_datafeed_packet packet;
        struct sr_datafeed_analog analog;
        struct sr_analog_encoding encoding;
@@ -160,12 +306,49 @@ static void handle_packet(const struct sr_dev_inst *sdi)
        if (g_str_has_prefix((const char *)devc->buf, "overtemp")) {
                sr_warn("Overtemperature condition!");
                devc->otp_active = TRUE;
+               send_config_update_key(sdi, SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE,
+                       g_variant_new_boolean(TRUE));
                return;
        }
 
        if (g_str_has_prefix((const char *)devc->buf, "undervolt")) {
                sr_warn("Undervoltage condition!");
                devc->uvc_active = TRUE;
+               send_config_update_key(sdi, SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE,
+                       g_variant_new_boolean(TRUE));
+               return;
+       }
+
+       if (g_str_has_prefix((const char *)devc->buf, "err ")) {
+               sr_err("Device replied with an error: '%s'.", devc->buf);
+               return;
+       }
+
+       if (g_str_has_prefix((const char *)devc->buf, "set ")) {
+               tokens = g_strsplit((const char *)devc->buf, " ", 2);
+               devc->current_limit = g_ascii_strtod(tokens[1], NULL) / 1000;
+               g_strfreev(tokens);
+               g_cond_signal(&devc->current_limit_cond);
+               send_config_update_key(sdi, SR_CONF_CURRENT_LIMIT,
+                       g_variant_new_double(devc->current_limit));
+               return;
+       }
+
+       if (g_str_has_prefix((const char *)devc->buf, "uvlo ")) {
+               tokens = g_strsplit((const char *)devc->buf, " ", 2);
+               devc->uvc_threshold = g_ascii_strtod(tokens[1], NULL) / 1000;
+               g_strfreev(tokens);
+               g_cond_signal(&devc->uvc_threshold_cond);
+               if (devc->uvc_threshold == .0) {
+                       send_config_update_key(sdi, SR_CONF_UNDER_VOLTAGE_CONDITION,
+                               g_variant_new_boolean(FALSE));
+               } else {
+                       send_config_update_key(sdi, SR_CONF_UNDER_VOLTAGE_CONDITION,
+                               g_variant_new_boolean(TRUE));
+                       send_config_update_key(sdi,
+                               SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD,
+                               g_variant_new_double(devc->uvc_threshold));
+               }
                return;
        }
 
@@ -175,8 +358,8 @@ static void handle_packet(const struct sr_dev_inst *sdi)
        }
 
        tokens = g_strsplit((const char *)devc->buf, " ", 3);
-       voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
-       current = g_ascii_strtod(tokens[1], NULL) / 1000;
+       devc->voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
+       devc->current = g_ascii_strtod(tokens[1], NULL) / 1000;
        g_strfreev(tokens);
 
        /* Begin frame. */
@@ -197,7 +380,8 @@ static void handle_packet(const struct sr_dev_inst *sdi)
        meaning.mq = SR_MQ_VOLTAGE;
        meaning.mqflags = SR_MQFLAG_DC;
        meaning.unit = SR_UNIT_VOLT;
-       analog.data = &voltage;
+       encoding.digits = 3;
+       analog.data = &devc->voltage;
        sr_session_send(sdi, &packet);
        g_slist_free(l);
 
@@ -208,7 +392,8 @@ static void handle_packet(const struct sr_dev_inst *sdi)
        meaning.mq = SR_MQ_CURRENT;
        meaning.mqflags = SR_MQFLAG_DC;
        meaning.unit = SR_UNIT_AMPERE;
-       analog.data = &current;
+       encoding.digits = 3;
+       analog.data = &devc->current;
        sr_session_send(sdi, &packet);
        g_slist_free(l);
 
@@ -225,26 +410,34 @@ static void handle_new_data(const struct sr_dev_inst *sdi)
        int len;
        struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
+       char *buf;
 
        devc = sdi->priv;
        serial = sdi->conn;
 
-       /* Try to get as much data as the buffer can hold. */
        len = RELOADPRO_BUFSIZE - devc->buflen;
-       len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
-       if (len == 0)
+       buf = devc->buf;
+       g_mutex_lock(&devc->acquisition_mutex);
+       if (serial_readline(serial, &buf, &len, 250) != SR_OK) {
+               g_mutex_unlock(&devc->acquisition_mutex);
+               return;
+       }
+
+       if (len == 0) {
+               g_mutex_unlock(&devc->acquisition_mutex);
                return; /* No new bytes, nothing to do. */
+       }
        if (len < 0) {
                sr_err("Serial port read error: %d.", len);
+               g_mutex_unlock(&devc->acquisition_mutex);
                return;
        }
        devc->buflen += len;
 
-       if (g_str_has_suffix((const char *)devc->buf, "\n")) {
-               handle_packet(sdi);
-               memset(devc->buf, 0, RELOADPRO_BUFSIZE);
-               devc->buflen = 0;
-       }
+       handle_packet(sdi);
+       g_mutex_unlock(&devc->acquisition_mutex);
+       memset(devc->buf, 0, RELOADPRO_BUFSIZE);
+       devc->buflen = 0;
 }
 
 SR_PRIV int reloadpro_receive_data(int fd, int revents, void *cb_data)
@@ -263,7 +456,7 @@ SR_PRIV int reloadpro_receive_data(int fd, int revents, void *cb_data)
        handle_new_data(sdi);
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index 8307053b27b2198d3f6409440b630d01540b4751..fca1b9782cb98c354b61cb7b378257a87531237b 100644 (file)
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-#define LOG_PREFIX "re-load-pro"
+#define LOG_PREFIX "arachnid-labs-re-load-pro"
 
 #define RELOADPRO_BUFSIZE 100
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits limits;
-       uint8_t buf[RELOADPRO_BUFSIZE];
+
+       char buf[RELOADPRO_BUFSIZE];
        int buflen;
+
+       float current_limit;
+       float voltage;
+       float current;
        gboolean otp_active;
        gboolean uvc_active;
+       float uvc_threshold;
+
+       gboolean acquisition_running;
+       GMutex acquisition_mutex;
+
+       GCond current_limit_cond;
+       GCond voltage_cond;
+       GCond uvc_threshold_cond;
 };
 
 SR_PRIV int reloadpro_set_current_limit(const struct sr_dev_inst *sdi,
                float current);
 SR_PRIV int reloadpro_set_on_off(const struct sr_dev_inst *sdi, gboolean on);
+SR_PRIV int reloadpro_set_under_voltage_threshold(const struct sr_dev_inst *sdi,
+               float uvc_threshold);
 SR_PRIV int reloadpro_get_current_limit(const struct sr_dev_inst *sdi,
-               float *current);
+               float *current_limit);
+SR_PRIV int reloadpro_get_under_voltage_threshold(const struct sr_dev_inst *sdi,
+               float *uvc_threshold);
 SR_PRIV int reloadpro_get_voltage_current(const struct sr_dev_inst *sdi,
                float *voltage, float *current);
 SR_PRIV int reloadpro_receive_data(int fd, int revents, void *cb_data);
index 4e3d32beb99f1f934e37a36af306883628d94373..89783456c5bbe416ed5fcc8c3945eb9093400181 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/*
- * ASIX SIGMA/SIGMA2 logic analyzer driver
- */
-
 #include <config.h>
 #include "protocol.h"
 
@@ -59,9 +55,14 @@ static const int32_t trigger_matches[] = {
 };
 #endif
 
+static void clear_helper(struct dev_context *devc)
+{
+       ftdi_deinit(&devc->ftdic);
+}
+
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, sigma_clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
@@ -80,8 +81,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
        ftdi_init(&devc->ftdic);
 
-       /* Look for SIGMAs. */
-
        if ((ret = ftdi_usb_find_all(&devc->ftdic, &devlist,
            USB_VENDOR, USB_PRODUCT)) <= 0) {
                if (ret < 0)
@@ -111,18 +110,16 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        devc->capture_ratio = 50;
        devc->use_triggers = 0;
 
-       /* Register SIGMA device. */
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INITIALIZING;
-       sdi->vendor = g_strdup(USB_VENDOR_NAME);
-       sdi->model = g_strdup(USB_MODEL_NAME);
+       sdi->vendor = g_strdup("ASIX");
+       sdi->model = g_strdup("SIGMA");
 
        for (i = 0; i < ARRAY_SIZE(channel_names); i++)
                sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_names[i]);
 
        sdi->priv = devc;
 
-       /* We will open the device again when we need it. */
        ftdi_list_free(&devlist);
 
        return std_scan_complete(di, g_slist_append(NULL, sdi));
@@ -140,18 +137,13 @@ static int dev_open(struct sr_dev_inst *sdi)
 
        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;
+                       USB_VENDOR, USB_PRODUCT, USB_DESCRIPTION, NULL)) < 0) {
+               sr_err("Failed to open device (%d): %s.",
+                      ret, ftdi_get_error_string(&devc->ftdic));
+               return SR_ERR;
        }
 
-       sdi->status = SR_ST_ACTIVE;
-
        return SR_OK;
 }
 
@@ -161,17 +153,11 @@ static int dev_close(struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       /* TODO */
-       if (sdi->status == SR_ST_ACTIVE)
-               ftdi_usb_close(&devc->ftdic);
-
-       sdi->status = SR_ST_INACTIVE;
-
-       return SR_OK;
+       return (ftdi_usb_close(&devc->ftdic) == 0) ? SR_OK : SR_ERR;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -203,81 +189,50 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       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 (key) {
        case SR_CONF_SAMPLERATE:
-               ret = sigma_set_samplerate(sdi, g_variant_get_uint64(data));
-               break;
+               return sigma_set_samplerate(sdi, g_variant_get_uint64(data));
        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;
+               devc->limit_msec = g_variant_get_uint64(data);
                break;
        case SR_CONF_LIMIT_SAMPLES:
-               tmp = g_variant_get_uint64(data);
-               devc->limit_samples = tmp;
-               devc->limit_msec = sigma_limit_samples_to_msec(devc, tmp);
+               devc->limit_samples = g_variant_get_uint64(data);
+               devc->limit_msec = sigma_limit_samples_to_msec(devc,
+                                               devc->limit_samples);
                break;
 #if ASIX_SIGMA_WITH_TRIGGER
        case SR_CONF_CAPTURE_RATIO:
-               tmp = g_variant_get_uint64(data);
-               if (tmp > 100)
-                       return SR_ERR;
-               devc->capture_ratio = tmp;
+               devc->capture_ratio = g_variant_get_uint64(data);
                break;
 #endif
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       GVariant *gvar;
-       GVariantBuilder gvb;
-
-       (void)cg;
-
        switch (key) {
        case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi)
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               else
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
        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,
-                               samplerates_count, sizeof(samplerates[0]));
-               g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
-               *data = g_variant_builder_end(&gvb);
+               *data = std_gvar_samplerates(samplerates, samplerates_count);
                break;
 #if ASIX_SIGMA_WITH_TRIGGER
        case SR_CONF_TRIGGER_MATCH:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-                               trigger_matches, ARRAY_SIZE(trigger_matches),
-                               sizeof(int32_t));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                break;
 #endif
        default:
@@ -299,9 +254,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        uint8_t clock_bytes[sizeof(clockselect)];
        size_t clock_idx;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        if (sigma_convert_trigger(sdi) != SR_OK) {
@@ -411,9 +363,20 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
        struct dev_context *devc;
 
        devc = sdi->priv;
-       devc->state.state = SIGMA_IDLE;
 
-       sr_session_source_remove(sdi->session, -1);
+       /*
+        * When acquisition is currently running, keep the receive
+        * routine registered and have it stop the acquisition upon the
+        * next invocation. Else unregister the receive routine here
+        * already. The detour is required to have sample data retrieved
+        * for forced acquisition stops.
+        */
+       if (devc->state.state == SIGMA_CAPTURE) {
+               devc->state.state = SIGMA_STOPPING;
+       } else {
+               devc->state.state = SIGMA_IDLE;
+               sr_session_source_remove(sdi->session, -1);
+       }
 
        return SR_OK;
 }
index 48de1b954ba4559d1de20f6355335212cffbb2f8..94fd8de044356a9a999b9a32cea11cd8c998f3ae 100644 (file)
@@ -47,7 +47,7 @@ SR_PRIV const uint64_t samplerates[] = {
 
 SR_PRIV const size_t samplerates_count = ARRAY_SIZE(samplerates);
 
-static const char sigma_firmware_files[][24] = {
+static const char firmware_files[][24] = {
        /* 50 MHz, supports 8 bit fractions */
        "asix-sigma-50.fw",
        /* 100 MHz */
@@ -78,12 +78,11 @@ 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) {
+       if (ret < 0)
                sr_err("ftdi_write_data failed: %s",
                       ftdi_get_error_string(&devc->ftdic));
-       } else if ((size_t) ret != size) {
+       else if ((size_t) ret != size)
                sr_err("ftdi_write_data did not complete write.");
-       }
 
        return ret;
 }
@@ -272,15 +271,6 @@ SR_PRIV int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *
        return SR_OK;
 }
 
-SR_PRIV void sigma_clear_helper(void *priv)
-{
-       struct dev_context *devc;
-
-       devc = priv;
-
-       ftdi_deinit(&devc->ftdic);
-}
-
 /*
  * Configure the FPGA for bitbang mode.
  * This sequence is documented in section 2. of the ASIX Sigma programming
@@ -446,38 +436,27 @@ static int upload_firmware(struct sr_context *ctx,
        unsigned char pins;
        size_t buf_size;
        const char *firmware;
-       struct ftdi_context *ftdic;
 
        /* Avoid downloading the same firmware multiple times. */
-       firmware = sigma_firmware_files[firmware_idx];
+       firmware = firmware_files[firmware_idx];
        if (devc->cur_firmware == firmware_idx) {
                sr_info("Not uploading firmware file '%s' again.", firmware);
                return SR_OK;
        }
 
-       /* Make sure it's an ASIX SIGMA. */
-       ftdic = &devc->ftdic;
-       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);
+       ret = ftdi_set_bitmode(&devc->ftdic, 0xdf, BITMODE_BITBANG);
        if (ret < 0) {
                sr_err("ftdi_set_bitmode failed: %s",
-                      ftdi_get_error_string(ftdic));
-               return 0;
+                      ftdi_get_error_string(&devc->ftdic));
+               return SR_ERR;
        }
 
        /* Four times the speed of sigmalogan - Works well. */
-       ret = ftdi_set_baudrate(ftdic, 750 * 1000);
+       ret = ftdi_set_baudrate(&devc->ftdic, 750 * 1000);
        if (ret < 0) {
                sr_err("ftdi_set_baudrate failed: %s",
-                      ftdi_get_error_string(ftdic));
-               return 0;
+                      ftdi_get_error_string(&devc->ftdic));
+               return SR_ERR;
        }
 
        /* Initialize the FPGA for firmware upload. */
@@ -499,14 +478,14 @@ static int upload_firmware(struct sr_context *ctx,
 
        g_free(buf);
 
-       ret = ftdi_set_bitmode(ftdic, 0x00, BITMODE_RESET);
+       ret = ftdi_set_bitmode(&devc->ftdic, 0x00, BITMODE_RESET);
        if (ret < 0) {
                sr_err("ftdi_set_bitmode failed: %s",
-                      ftdi_get_error_string(ftdic));
+                      ftdi_get_error_string(&devc->ftdic));
                return SR_ERR;
        }
 
-       ftdi_usb_purge_buffers(ftdic);
+       ftdi_usb_purge_buffers(&devc->ftdic);
 
        /* Discard garbage. */
        while (sigma_read(&pins, 1, devc) == 1)
@@ -554,6 +533,7 @@ SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi, uint64_t sampler
        struct drv_context *drvc;
        size_t i;
        int ret;
+       int num_channels;
 
        devc = sdi->priv;
        drvc = sdi->driver->context;
@@ -572,15 +552,16 @@ SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi, uint64_t sampler
         * firmware is required and higher rates might limit the set
         * of available channels.
         */
+       num_channels = devc->num_channels;
        if (samplerate <= SR_MHZ(50)) {
                ret = upload_firmware(drvc->sr_ctx, 0, devc);
-               devc->num_channels = 16;
+               num_channels = 16;
        } else if (samplerate == SR_MHZ(100)) {
                ret = upload_firmware(drvc->sr_ctx, 1, devc);
-               devc->num_channels = 8;
+               num_channels = 8;
        } else if (samplerate == SR_MHZ(200)) {
                ret = upload_firmware(drvc->sr_ctx, 2, devc);
-               devc->num_channels = 4;
+               num_channels = 4;
        }
 
        /*
@@ -589,6 +570,7 @@ SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi, uint64_t sampler
         * an "event" (memory organization internal to the device).
         */
        if (ret == SR_OK) {
+               devc->num_channels = num_channels;
                devc->cur_samplerate = samplerate;
                devc->samples_per_event = 16 / devc->num_channels;
                devc->state.state = SIGMA_IDLE;
@@ -663,16 +645,13 @@ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi)
                                if (match->match == SR_TRIGGER_ONE) {
                                        devc->trigger.simplevalue |= channelbit;
                                        devc->trigger.simplemask |= channelbit;
-                               }
-                               else if (match->match == SR_TRIGGER_ZERO) {
+                               } else if (match->match == SR_TRIGGER_ZERO) {
                                        devc->trigger.simplevalue &= ~channelbit;
                                        devc->trigger.simplemask |= channelbit;
-                               }
-                               else if (match->match == SR_TRIGGER_FALLING) {
+                               } else if (match->match == SR_TRIGGER_FALLING) {
                                        devc->trigger.fallingmask |= channelbit;
                                        trigger_set++;
-                               }
-                               else if (match->match == SR_TRIGGER_RISING) {
+                               } else if (match->match == SR_TRIGGER_RISING) {
                                        devc->trigger.risingmask |= channelbit;
                                        trigger_set++;
                                }
@@ -694,7 +673,6 @@ SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi)
        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)
@@ -1039,6 +1017,7 @@ static int download_capture(struct sr_dev_inst *sdi)
                return FALSE;
 
        sr_info("Downloading sample data.");
+       devc->state.state = SIGMA_DOWNLOAD;
 
        /*
         * Ask the hardware to stop data acquisition. Reception of the
@@ -1119,12 +1098,12 @@ static int download_capture(struct sr_dev_inst *sdi)
 
                dl_lines_done += dl_lines_curr;
        }
+       g_free(dram_line);
 
        std_session_send_df_end(sdi);
 
-       sdi->driver->dev_acquisition_stop(sdi);
-
-       g_free(dram_line);
+       devc->state.state = SIGMA_IDLE;
+       sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
@@ -1168,6 +1147,14 @@ SR_PRIV int sigma_receive_data(int fd, int revents, void *cb_data)
        if (devc->state.state == SIGMA_IDLE)
                return TRUE;
 
+       /*
+        * When the application has requested to stop the acquisition,
+        * then immediately start downloading sample data. Otherwise
+        * keep checking configured limits which will terminate the
+        * acquisition and initiate download.
+        */
+       if (devc->state.state == SIGMA_STOPPING)
+               return download_capture(sdi);
        if (devc->state.state == SIGMA_CAPTURE)
                return sigma_capture_mode(sdi);
 
index e84badd9c8db77f79ffa53124183670d45425038..6b934c252cc489a1ac108f7e8d3550b564c7136c 100644 (file)
@@ -30,6 +30,8 @@
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
+#define LOG_PREFIX "asix-sigma"
+
 /*
  * Triggers are not working in this implementation. Stop claiming
  * support for the feature which effectively is not available, until
  */
 #define ASIX_SIGMA_WITH_TRIGGER        0
 
-#define LOG_PREFIX "asix-sigma"
-
 #define USB_VENDOR                     0xa600
 #define USB_PRODUCT                    0xa000
 #define USB_DESCRIPTION                        "ASIX SIGMA"
-#define USB_VENDOR_NAME                        "ASIX"
-#define USB_MODEL_NAME                 "SIGMA"
 
 enum sigma_write_register {
        WRITE_CLOCK_SELECT      = 0,
@@ -248,14 +246,13 @@ struct sigma_state {
                SIGMA_UNINITIALIZED = 0,
                SIGMA_IDLE,
                SIGMA_CAPTURE,
+               SIGMA_STOPPING,
                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;
@@ -267,7 +264,7 @@ struct dev_context {
        int num_channels;
        int cur_channels;
        int samples_per_event;
-       int capture_ratio;
+       uint64_t capture_ratio;
        struct sigma_trigger trigger;
        int use_triggers;
        struct sigma_state state;
@@ -280,7 +277,6 @@ SR_PRIV int sigma_write_register(uint8_t reg, uint8_t *data, size_t len,
                                 struct dev_context *devc);
 SR_PRIV int sigma_set_register(uint8_t reg, uint8_t value, struct dev_context *devc);
 SR_PRIV int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *devc);
-SR_PRIV void sigma_clear_helper(void *priv);
 SR_PRIV uint64_t sigma_limit_samples_to_msec(const struct dev_context *devc,
                                             uint64_t limit_samples);
 SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
index 01c0d369a490adb6a8983d3f3f5f5a69913a050e..b82b9fb333cf9fd460764d638ce325f3e607caaf 100644 (file)
@@ -182,21 +182,19 @@ static GSList *scan_3203(struct sr_dev_driver *di, GSList *options)
        return scan(di, options, PPS_3203T_3S);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_channel *ch;
-       int channel, ret;
+       int channel;
 
        if (!sdi)
                return SR_ERR_ARG;
 
        devc = sdi->priv;
 
-       ret = SR_OK;
        if (!cg) {
-               /* No channel group: global options. */
                switch (key) {
                case SR_CONF_CHANNEL_CONFIG:
                        *data = g_variant_new_string(channel_modes[devc->channel_mode]);
@@ -233,64 +231,36 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                }
        }
 
-       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;
+       return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_channel *ch;
        gdouble dval;
-       int channel, ret, ival;
-       const char *sval;
+       int channel, ival;
        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_CHANNEL_CONFIG:
-                       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 = std_str_idx(data, ARRAY_AND_SIZE(channel_modes))) < 0)
+                               return SR_ERR_ARG;
+                       if (devc->model->channel_modes && (1 << ival) == 0)
+                               return SR_ERR_ARG; /* Not supported on this model. */
                        if (ival == devc->channel_mode_set)
-                               /* Nothing to do. */
-                               break;
+                               break; /* Nothing to do. */
                        devc->channel_mode_set = ival;
                        devc->config_dirty = TRUE;
                        break;
                case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
                        bval = g_variant_get_boolean(data);
                        if (bval == devc->over_current_protection_set)
-                               /* Nothing to do. */
-                               break;
+                               break; /* Nothing to do. */
                        devc->over_current_protection_set = bval;
                        devc->config_dirty = TRUE;
                        break;
@@ -298,7 +268,6 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        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;
@@ -307,116 +276,85 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                case SR_CONF_VOLTAGE_TARGET:
                        dval = g_variant_get_double(data);
                        if (dval < 0 || dval > devc->model->channels[channel].voltage[1])
-                               ret = SR_ERR_ARG;
+                               return SR_ERR_ARG;
                        devc->config[channel].output_voltage_max = dval;
                        devc->config_dirty = TRUE;
                        break;
                case SR_CONF_CURRENT_LIMIT:
                        dval = g_variant_get_double(data);
                        if (dval < 0 || dval > devc->model->channels[channel].current[1])
-                               ret = SR_ERR_ARG;
+                               return SR_ERR_ARG;
                        devc->config[channel].output_current_max = dval;
                        devc->config_dirty = TRUE;
                        break;
                case SR_CONF_ENABLED:
                        bval = g_variant_get_boolean(data);
                        if (bval == devc->config[channel].output_enabled_set)
-                               /* Nothing to do. */
-                               break;
+                               break; /* Nothing to do. */
                        devc->config[channel].output_enabled_set = bval;
                        devc->config_dirty = TRUE;
                        break;
                default:
-                       ret = SR_ERR_NA;
+                       return SR_ERR_NA;
                }
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_channel *ch;
-       GVariant *gvar;
-       GVariantBuilder gvb;
-       int channel, ret, i;
-
-       /* Always available. */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
+       int channel;
 
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
+       devc = (sdi) ? sdi->priv : NULL;
 
-       if (!sdi)
-               return SR_ERR_ARG;
-
-       devc = sdi->priv;
-       ret = SR_OK;
        if (!cg) {
-               /* No channel group: global options. */
                switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       break;
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
                case SR_CONF_CHANNEL_CONFIG:
+                       if (!devc || !devc->model)
+                               return SR_ERR_ARG;
                        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));
+                               *data = g_variant_new_strv(ARRAY_AND_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_UINT32,
-                                       devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
                        break;
                case SR_CONF_VOLTAGE_TARGET:
-                       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);
+                       if (!devc || !devc->model)
+                               return SR_ERR_ARG;
+                       *data = std_gvar_min_max_step_array(devc->model->channels[channel].voltage);
                        break;
                case SR_CONF_CURRENT_LIMIT:
-                       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);
+                       if (!devc || !devc->model)
+                               return SR_ERR_ARG;
+                       *data = std_gvar_min_max_step_array(devc->model->channels[channel].current);
                        break;
                default:
                        return SR_ERR_NA;
                }
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
@@ -424,6 +362,7 @@ 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
@@ -439,9 +378,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct sr_serial_dev_inst *serial;
        uint8_t packet[PACKET_SIZE];
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
        memset(devc->packet, 0x44, PACKET_SIZE);
        devc->packet_size = 0;
@@ -466,9 +402,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
        devc->acquisition_running = FALSE;
 
@@ -483,7 +416,7 @@ static struct sr_dev_driver atten_pps3203_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan_3203,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index b9a256d17ab820daf73e631890a70bc6ad05278b..f9409c2d1f7c01dcf61533fe3454a74c2b09809d 100644 (file)
@@ -71,15 +71,11 @@ struct per_channel_config {
        gboolean output_enabled_set;
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        const struct pps_model *model;
 
-       /* Acquisition state */
        gboolean acquisition_running;
 
-       /* Operational state */
        gboolean config_dirty;
        struct per_channel_config *config;
        /* Blocking write timeout for packet. */
@@ -91,7 +87,6 @@ struct dev_context {
        int channel_mode_set;
        gboolean over_current_protection_set;
 
-       /* Temporary state across callbacks */
        uint8_t packet[PACKET_SIZE];
        int packet_size;
 
index a1b860e3621fb91a1655d97fda085a66fb339e08..ebaf3e5a674a07db20b3b0dac2ece3ee5bf15237 100644 (file)
 #include <time.h>
 #include <sys/timerfd.h>
 
+static const uint32_t drvopts[] = {
+       SR_CONF_THERMOMETER,
+       SR_CONF_POWERMETER,
+};
+
 static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
@@ -126,27 +131,8 @@ err_out:
        return NULL;
 }
 
-static int dev_open(struct sr_dev_inst *sdi)
-{
-       (void)sdi;
-
-       sdi->status = SR_ST_ACTIVE;
-
-       return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
-       (void)sdi;
-
-       sdi->status = SR_ST_INACTIVE;
-
-       return SR_OK;
-}
-
 static int config_get(uint32_t key, GVariant **data,
-                     const struct sr_dev_inst *sdi,
-                     const struct sr_channel_group *cg)
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        int ret;
@@ -156,11 +142,11 @@ static int config_get(uint32_t key, GVariant **data,
        devc = sdi->priv;
 
        ret = SR_OK;
+
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
-               ret = sr_sw_limits_config_get(&devc->limits, key, data);
-               break;
+               return sr_sw_limits_config_get(&devc->limits, key, data);
        case SR_CONF_SAMPLERATE:
                *data = g_variant_new_uint64(devc->samplerate);
                break;
@@ -186,30 +172,22 @@ static int config_get(uint32_t key, GVariant **data,
 }
 
 static int config_set(uint32_t key, GVariant *data,
-                     const struct sr_dev_inst *sdi,
-                     const struct sr_channel_group *cg)
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        uint64_t samplerate;
-       int ret;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
 
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
-               ret = sr_sw_limits_config_set(&devc->limits, key, data);
-               break;
+               return sr_sw_limits_config_set(&devc->limits, key, data);
        case SR_CONF_SAMPLERATE:
                samplerate = g_variant_get_uint64(data);
                if (samplerate > MAX_SAMPLE_RATE) {
                        sr_err("Maximum sample rate is %d", MAX_SAMPLE_RATE);
-                       ret = SR_ERR_SAMPLERATE;
-                       break;
+                       return SR_ERR_SAMPLERATE;
                }
                devc->samplerate = samplerate;
                bl_acme_maybe_set_update_interval(sdi, samplerate);
@@ -217,46 +195,30 @@ static int config_set(uint32_t key, GVariant *data,
        case SR_CONF_PROBE_FACTOR:
                if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               ret = bl_acme_set_shunt(cg, g_variant_get_uint64(data));
-               break;
+               return bl_acme_set_shunt(cg, g_variant_get_uint64(data));
        case SR_CONF_POWER_OFF:
                if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               ret = bl_acme_set_power_off(cg, g_variant_get_boolean(data));
-               break;
+               return bl_acme_set_power_off(cg, g_variant_get_boolean(data));
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_list(uint32_t key, GVariant **data,
-                      const struct sr_dev_inst *sdi,
-                      const struct sr_channel_group *cg)
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        uint32_t devopts_cg[MAX_DEVOPTS_CG];
-       GVariant *gvar;
-       GVariantBuilder gvb;
-       int ret, num_devopts_cg = 0;
-
-       (void)sdi;
-       (void)cg;
+       int num_devopts_cg = 0;
 
-       ret = SR_OK;
        if (!cg) {
                switch (key) {
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       break;
+                       return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
                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);
+                       *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
                        break;
                default:
                        return SR_ERR_NA;
@@ -269,15 +231,14 @@ static int config_list(uint32_t key, GVariant **data,
                        if (bl_acme_probe_has_pws(cg))
                                devopts_cg[num_devopts_cg++] = HAS_POWER_OFF;
 
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts_cg, num_devopts_cg, sizeof(uint32_t));
+                       *data = std_gvar_array_u32(devopts_cg, num_devopts_cg);
                        break;
                default:
                        return SR_ERR_NA;
                }
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static void dev_acquisition_close(const struct sr_dev_inst *sdi)
@@ -316,9 +277,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                .it_value = { 0, 0 }
        };
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        if (dev_acquisition_open(sdi))
                return SR_ERR;
 
@@ -360,9 +318,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        dev_acquisition_close(sdi);
        sr_session_source_remove_channel(sdi->session, devc->channel);
        g_io_channel_shutdown(devc->channel, FALSE, NULL);
@@ -385,11 +340,12 @@ static struct sr_dev_driver baylibre_acme_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
-       .dev_open = dev_open,
-       .dev_close = dev_close,
+       .dev_open = std_dummy_dev_open,
+       .dev_close = std_dummy_dev_close,
        .dev_acquisition_start = dev_acquisition_start,
        .dev_acquisition_stop = dev_acquisition_stop,
        .context = NULL,
index 4f62a9e9ecc2ac7650cc206ebd9b6dac21cf69ed..17ae5b90859e764eb852439dadeab9314823cd64 100644 (file)
@@ -166,8 +166,10 @@ SR_PRIV gboolean bl_acme_detect_probe(unsigned int addr,
        probe_name_path(addr, path);
        status = g_file_get_contents(path->str, &buf, &size, &err);
        if (!status) {
-               sr_dbg("Name for probe %d can't be read: %s",
-                      prb_num, err->message);
+               /* Don't log "No such file or directory" messages. */
+               if (err->code != G_FILE_ERROR_NOENT)
+                       sr_dbg("Name for probe %d can't be read (%d): %s",
+                              prb_num, err->code, err->message);
                g_string_free(path, TRUE);
                g_error_free(err);
                return ret;
@@ -308,11 +310,11 @@ static int read_probe_eeprom(unsigned int addr, struct probe_eeprom *eeprom)
 static int revB_addr_to_num(unsigned int addr)
 {
        switch (addr) {
-       case 0x44:      return 5;
-       case 0x45:      return 6;
-       case 0x42:      return 3;
-       case 0x43:      return 4;
-       default:        return addr - 0x3f;
+       case 0x44: return 5;
+       case 0x45: return 6;
+       case 0x42: return 3;
+       case 0x43: return 4;
+       default:   return addr - 0x3f;
        }
 }
 
@@ -793,7 +795,7 @@ SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
        sr_sw_limits_update_samples_read(&devc->limits, 1);
 
        if (sr_sw_limits_check(&devc->limits)) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        }
 
index 8717debd42cda396eb514983ab237a291355d48e..2425c992648f72ebdce4e0525bb8f79ac36d2147 100644 (file)
@@ -49,7 +49,6 @@ enum probe_type {
        PROBE_TEMP,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        uint64_t samplerate;
        struct sr_sw_limits limits;
index 34b09f211fb26f2b54e464a7f973c2ce0a51ee3e..95f53159a85fa073209ad6da1ca5f4eef83ca874 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Copyright (C) 2014-2017 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
 #include "protocol.h"
 #include "beaglelogic.h"
 
-/* Scan options */
 static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
        SR_CONF_NUM_LOGIC_CHANNELS,
 };
 
-/* Hardware capabilities */
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
-       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
        SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_NUM_LOGIC_CHANNELS | SR_CONF_GET,
 };
 
-/* Trigger matching capabilities */
-static const int32_t soft_trigger_matches[] = {
+static const int32_t trigger_matches[] = {
        SR_TRIGGER_ZERO,
        SR_TRIGGER_ONE,
        SR_TRIGGER_RISING,
@@ -58,67 +59,78 @@ static const uint64_t samplerates[] = {
        SR_HZ(1),
 };
 
-static struct dev_context *beaglelogic_devc_alloc(void)
-{
-       struct dev_context *devc;
-
-       devc = g_malloc0(sizeof(struct dev_context));
-
-       /* Default non-zero values (if any) */
-       devc->fd = -1;
-       devc->limit_samples = (uint64_t)-1;
-
-       return devc;
-}
-
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
        GSList *l;
        struct sr_config *src;
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
+       const char *conn;
+       gchar **params;
        int i, maxch;
 
-       /* Probe for /dev/beaglelogic */
-       if (!g_file_test(BEAGLELOGIC_DEV_NODE, G_FILE_TEST_EXISTS))
-               return NULL;
-
-       sdi = g_malloc0(sizeof(struct sr_dev_inst));
-       sdi->status = SR_ST_INACTIVE;
-       sdi->model = g_strdup("BeagleLogic");
-       sdi->version = g_strdup("1.0");
-
-       /* Unless explicitly specified, keep max channels to 8 only */
-       maxch = 8;
+       maxch = NUM_CHANNELS;
+       conn = NULL;
        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);
+               if (src->key == SR_CONF_CONN)
+                       conn = g_variant_get_string(src->data, NULL);
        }
 
-       /* We need to test for number of channels by opening the node */
-       devc = beaglelogic_devc_alloc();
+       /* Probe for /dev/beaglelogic if not connecting via TCP */
+       if (!conn) {
+               params = NULL;
+               if (!g_file_test(BEAGLELOGIC_DEV_NODE, G_FILE_TEST_EXISTS))
+                       return NULL;
+       } else {
+               params = g_strsplit(conn, "/", 0);
+               if (!params || !params[1] || !params[2]) {
+                       sr_err("Invalid Parameters.");
+                       g_strfreev(params);
+                       return NULL;
+               }
+               if (g_ascii_strncasecmp(params[0], "tcp", 3)) {
+                       sr_err("Only TCP (tcp-raw) protocol is currently supported.");
+                       g_strfreev(params);
+                       return NULL;
+               }
+       }
 
-       if (beaglelogic_open_nonblock(devc) != SR_OK) {
-               g_free(devc);
-               sr_dev_inst_free(sdi);
+       maxch = (maxch > 8) ? NUM_CHANNELS : 8;
 
-               return NULL;
-       }
+       sdi = g_new0(struct sr_dev_inst, 1);
+       sdi->status = SR_ST_INACTIVE;
+       sdi->model = g_strdup("BeagleLogic");
+       sdi->version = g_strdup("1.0");
 
-       if (maxch > 8) {
-               maxch = NUM_CHANNELS;
-               devc->sampleunit = BL_SAMPLEUNIT_16_BITS;
-       } else {
-               maxch = 8;
-               devc->sampleunit = BL_SAMPLEUNIT_8_BITS;
-       }
+       devc = g_malloc0(sizeof(struct dev_context));
 
-       beaglelogic_set_sampleunit(devc);
-       beaglelogic_close(devc);
+       /* Default non-zero values (if any) */
+       devc->fd = -1;
+       devc->limit_samples = 10000000;
+       devc->tcp_buffer = 0;
 
-       /* Signal */
-       sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE);
+       if (!conn) {
+               devc->beaglelogic = &beaglelogic_native_ops;
+               sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE);
+       } else {
+               devc->read_timeout = 1000 * 1000;
+               devc->beaglelogic = &beaglelogic_tcp_ops;
+               devc->address = g_strdup(params[1]);
+               devc->port = g_strdup(params[2]);
+               g_strfreev(params);
+
+               if (devc->beaglelogic->open(devc) != SR_OK)
+                       goto err_free;
+               if (beaglelogic_tcp_detect(devc) != SR_OK)
+                       goto err_free;
+               if (devc->beaglelogic->close(devc) != SR_OK)
+                       goto err_free;
+               sr_info("BeagleLogic device found at %s : %s",
+                       devc->address, devc->port);
+       }
 
        /* Fill the channels */
        for (i = 0; i < maxch; i++)
@@ -128,6 +140,16 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        sdi->priv = devc;
 
        return std_scan_complete(di, g_slist_append(NULL, sdi));
+
+err_free:
+       g_free(sdi->model);
+       g_free(sdi->version);
+       g_free(devc->address);
+       g_free(devc->port);
+       g_free(devc);
+       g_free(sdi);
+
+       return NULL;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -135,30 +157,39 @@ static int dev_open(struct sr_dev_inst *sdi)
        struct dev_context *devc = sdi->priv;
 
        /* Open BeagleLogic */
-       if (beaglelogic_open_nonblock(devc))
+       if (devc->beaglelogic->open(devc))
                return SR_ERR;
 
        /* Set fd and local attributes */
-       devc->pollfd.fd = devc->fd;
+       if (devc->beaglelogic == &beaglelogic_tcp_ops)
+               devc->pollfd.fd = devc->socket;
+       else
+               devc->pollfd.fd = devc->fd;
        devc->pollfd.events = G_IO_IN;
        devc->pollfd.revents = 0;
 
        /* Get the default attributes */
-       beaglelogic_get_samplerate(devc);
-       beaglelogic_get_sampleunit(devc);
-       beaglelogic_get_triggerflags(devc);
-       beaglelogic_get_buffersize(devc);
-       beaglelogic_get_bufunitsize(devc);
+       devc->beaglelogic->get_samplerate(devc);
+       devc->beaglelogic->get_sampleunit(devc);
+       devc->beaglelogic->get_buffersize(devc);
+       devc->beaglelogic->get_bufunitsize(devc);
+
+       /* Set the triggerflags to default for continuous capture unless we
+        * explicitly limit samples using SR_CONF_LIMIT_SAMPLES */
+       devc->triggerflags = BL_TRIGGERFLAGS_CONTINUOUS;
+       devc->beaglelogic->set_triggerflags(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;
+       if (devc->beaglelogic == &beaglelogic_native_ops) {
+               if (devc->beaglelogic->mmap(devc) != SR_OK) {
+                       sr_err("Unable to map capture buffer");
+                       devc->beaglelogic->close(devc);
+                       return SR_ERR;
+               }
+       } else {
+               devc->tcp_buffer = g_malloc(TCP_BUFFER_SIZE);
        }
 
-       /* We're good to go now */
-       sdi->status = SR_ST_ACTIVE;
        return SR_OK;
 }
 
@@ -166,17 +197,28 @@ 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;
+       /* Close the memory mapping and the file */
+       if (devc->beaglelogic == &beaglelogic_native_ops)
+               devc->beaglelogic->munmap(devc);
+       devc->beaglelogic->close(devc);
+
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static void clear_helper(struct dev_context *devc)
+{
+       g_free(devc->tcp_buffer);
+       g_free(devc->address);
+       g_free(devc->port);
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
 
@@ -202,21 +244,18 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
        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);
+               return devc->beaglelogic->set_samplerate(devc);
        case SR_CONF_LIMIT_SAMPLES:
                tmp_u64 = g_variant_get_uint64(data);
                devc->limit_samples = tmp_u64;
@@ -228,17 +267,15 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        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"\
+                               "\' 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);
+               return devc->beaglelogic->set_triggerflags(devc);
        case SR_CONF_CAPTURE_RATIO:
                devc->capture_ratio = g_variant_get_uint64(data);
-               if (devc->capture_ratio > 100)
-                       return SR_ERR;
-               return SR_OK;
+               break;
        default:
                return SR_ERR_NA;
        }
@@ -246,43 +283,24 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
-       GVariant *gvar;
-       GVariantBuilder gvb;
-
-       (void)sdi;
-       (void)cg;
-
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
                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));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 /* get a sane timeout for poll() */
@@ -292,25 +310,33 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc = sdi->priv;
+       GSList *l;
        struct sr_trigger *trigger;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
+       struct sr_channel *channel;
 
        /* 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);
+       devc->sampleunit = BL_SAMPLEUNIT_8_BITS;
+
+       for (l = sdi->channels; l; l = l->next) {
+               channel = l->data;
+               if (channel->index >= 8 && channel->enabled)
+                       devc->sampleunit = BL_SAMPLEUNIT_16_BITS;
+       }
+       devc->beaglelogic->set_sampleunit(devc);
+
+       /* If continuous sampling, set the limit_samples to max possible value */
+       if (devc->triggerflags == BL_TRIGGERFLAGS_CONTINUOUS)
+               devc->limit_samples = UINT64_MAX;
 
        /* Configure triggers & send header packet */
        if ((trigger = sr_session_trigger_get(sdi->session))) {
                int pre_trigger_samples = 0;
                if (devc->limit_samples > 0)
-                       pre_trigger_samples = devc->capture_ratio * devc->limit_samples/100;
+                       pre_trigger_samples = (devc->capture_ratio * devc->limit_samples) / 100;
                devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
                if (!devc->stl)
                        return SR_ERR_MALLOC;
@@ -320,9 +346,14 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        std_session_send_df_header(sdi);
 
        /* 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,
+       devc->beaglelogic->start(devc);
+       if (devc->beaglelogic == &beaglelogic_native_ops)
+               sr_session_source_add_pollfd(sdi->session, &devc->pollfd,
+                       BUFUNIT_TIMEOUT_MS(devc), beaglelogic_native_receive_data,
+                       (void *)sdi);
+       else
+               sr_session_source_add_pollfd(sdi->session, &devc->pollfd,
+                       BUFUNIT_TIMEOUT_MS(devc), beaglelogic_tcp_receive_data,
                        (void *)sdi);
 
        return SR_OK;
@@ -332,14 +363,14 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        /* Execute a stop on BeagleLogic */
-       beaglelogic_stop(devc);
+       devc->beaglelogic->stop(devc);
 
-       /* lseek to offset 0, flushes the cache */
-       lseek(devc->fd, 0, SEEK_SET);
+       /* Flush the cache */
+       if (devc->beaglelogic == &beaglelogic_native_ops)
+               lseek(devc->fd, 0, SEEK_SET);
+       else
+               beaglelogic_tcp_drain(devc);
 
        /* Remove session source and send EOT packet */
        sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
@@ -356,6 +387,7 @@ static struct sr_dev_driver beaglelogic_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 96713ba707d9b0a647d2a0cd2a7826c2208969e3..2150032cfa455b1ea5517798962b400e4fa5c703 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Copyright (C) 2014-2017 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
 #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_GET_CUR_INDEX      _IOR('k', 0x24, 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_SET_BUFUNIT_SIZE   _IOW('k', 0x27, uint32_t)
 
 #define IOCTL_BL_FILL_TEST_PATTERN   _IO('k', 0x28)
 
@@ -90,120 +92,41 @@ enum beaglelogic_sampleunit {
  *     SR_OK or SR_ERR
  */
 
-SR_PRIV int beaglelogic_open_nonblock(struct dev_context *devc);
-SR_PRIV int beaglelogic_close(struct dev_context *devc);
+struct beaglelogic_ops {
+       int (*open)(struct dev_context *devc);
+       int (*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);
+       int (*get_buffersize)(struct dev_context *devc);
+       int (*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);
+       int (*get_samplerate)(struct dev_context *devc);
+       int (*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);
+       int (*get_sampleunit)(struct dev_context *devc);
+       int (*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);
+       int (*get_triggerflags)(struct dev_context *devc);
+       int (*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);
+       /* Start and stop the capture operation */
+       int (*start)(struct dev_context *devc);
+       int (*stop)(struct dev_context *devc);
 
-/* Get the last error size */
-SR_PRIV int beaglelogic_getlasterror(struct dev_context *devc);
+       /* Get the last error size */
+       int (*get_lasterror)(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);
+       /* Gets the unit size of the capture buffer (usually 4 or 8 MB) */
+       int (*get_bufunitsize)(struct dev_context *devc);
+       int (*set_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);
-}
+       int (*mmap)(struct dev_context *devc);
+       int (*munmap)(struct dev_context *devc);
+};
 
-SR_PRIV inline int beaglelogic_stop(struct dev_context *devc) {
-       return ioctl(devc->fd, IOCTL_BL_STOP);
-}
+SR_PRIV extern const struct beaglelogic_ops beaglelogic_native_ops;
+SR_PRIV extern const struct beaglelogic_ops beaglelogic_tcp_ops;
 
-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);
-}
+SR_PRIV int beaglelogic_tcp_detect(struct dev_context *devc);
+SR_PRIV int beaglelogic_tcp_drain(struct dev_context *devc);
 
 #endif
diff --git a/src/hardware/beaglelogic/beaglelogic_native.c b/src/hardware/beaglelogic/beaglelogic_native.c
new file mode 100644 (file)
index 0000000..42d51ed
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014-2017 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"
+
+static 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);
+}
+
+static int beaglelogic_close(struct dev_context *devc)
+{
+       return close(devc->fd);
+}
+
+static int beaglelogic_get_buffersize(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_GET_BUFFER_SIZE, &devc->buffersize);
+}
+
+static 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 */
+static 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;
+}
+
+static int beaglelogic_set_samplerate(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_RATE,
+                       (uint32_t)devc->cur_samplerate);
+}
+
+static int beaglelogic_get_sampleunit(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_GET_SAMPLE_UNIT, &devc->sampleunit);
+}
+
+static int beaglelogic_set_sampleunit(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_UNIT, devc->sampleunit);
+}
+
+static int beaglelogic_get_triggerflags(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_GET_TRIGGER_FLAGS, &devc->triggerflags);
+}
+
+static int beaglelogic_set_triggerflags(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_SET_TRIGGER_FLAGS, devc->triggerflags);
+}
+
+static int beaglelogic_get_lasterror(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;
+}
+
+static int beaglelogic_start(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_START);
+}
+
+static int beaglelogic_stop(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_STOP);
+}
+
+static int beaglelogic_get_bufunitsize(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_GET_BUFUNIT_SIZE, &devc->bufunitsize);
+}
+
+static int beaglelogic_set_bufunitsize(struct dev_context *devc)
+{
+       return ioctl(devc->fd, IOCTL_BL_SET_BUFUNIT_SIZE, devc->bufunitsize);
+}
+
+static 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);
+}
+
+static int beaglelogic_munmap(struct dev_context *devc)
+{
+       return munmap(devc->sample_buf, devc->buffersize);
+}
+
+SR_PRIV const struct beaglelogic_ops beaglelogic_native_ops = {
+       .open = beaglelogic_open_nonblock,
+       .close = beaglelogic_close,
+       .get_buffersize = beaglelogic_get_buffersize,
+       .set_buffersize = beaglelogic_set_buffersize,
+       .get_samplerate = beaglelogic_get_samplerate,
+       .set_samplerate = beaglelogic_set_samplerate,
+       .get_sampleunit = beaglelogic_get_sampleunit,
+       .set_sampleunit = beaglelogic_set_sampleunit,
+       .get_triggerflags = beaglelogic_get_triggerflags,
+       .set_triggerflags = beaglelogic_set_triggerflags,
+       .start = beaglelogic_start,
+       .stop = beaglelogic_stop,
+       .get_lasterror = beaglelogic_get_lasterror,
+       .get_bufunitsize = beaglelogic_get_bufunitsize,
+       .set_bufunitsize = beaglelogic_set_bufunitsize,
+       .mmap = beaglelogic_mmap,
+       .munmap = beaglelogic_munmap,
+};
diff --git a/src/hardware/beaglelogic/beaglelogic_tcp.c b/src/hardware/beaglelogic/beaglelogic_tcp.c
new file mode 100644 (file)
index 0000000..9cf78bb
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Portions of the code are adapted from scpi_tcp.c and scpi.c, their
+ * copyright notices are listed below:
+ *
+ * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
+ * 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 <config.h>
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+#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>
+
+#include "protocol.h"
+#include "beaglelogic.h"
+
+static int beaglelogic_tcp_open(struct dev_context *devc)
+{
+       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(devc->address, devc->port, &hints, &results);
+
+       if (err) {
+               sr_err("Address lookup failed: %s:%s: %s", devc->address,
+                       devc->port, gai_strerror(err));
+               return SR_ERR;
+       }
+
+       for (res = results; res; res = res->ai_next) {
+               if ((devc->socket = socket(res->ai_family, res->ai_socktype,
+                                               res->ai_protocol)) < 0)
+                       continue;
+               if (connect(devc->socket, res->ai_addr, res->ai_addrlen) != 0) {
+                       close(devc->socket);
+                       devc->socket = -1;
+                       continue;
+               }
+               break;
+       }
+
+       freeaddrinfo(results);
+
+       if (devc->socket < 0) {
+               sr_err("Failed to connect to %s:%s: %s", devc->address,
+                       devc->port, g_strerror(errno));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int beaglelogic_tcp_send_cmd(struct dev_context *devc,
+                                   const char *format, ...)
+{
+       int len, out;
+       va_list args, args_copy;
+       char *buf;
+
+       va_start(args, format);
+       va_copy(args_copy, args);
+       len = vsnprintf(NULL, 0, format, args_copy);
+       va_end(args_copy);
+
+       buf = g_malloc0(len + 2);
+       vsprintf(buf, format, args);
+       va_end(args);
+
+       if (buf[len - 1] != '\n')
+               buf[len] = '\n';
+
+       out = send(devc->socket, buf, strlen(buf), 0);
+
+       if (out < 0) {
+               sr_err("Send error: %s", g_strerror(errno));
+               g_free(buf);
+               return SR_ERR;
+       }
+
+       if (out < (int)strlen(buf)) {
+               sr_dbg("Only sent %d/%zu bytes of command: '%s'.", out,
+                      strlen(buf), buf);
+       }
+
+       sr_spew("Sent command: '%s'.", buf);
+
+       g_free(buf);
+
+       return SR_OK;
+}
+
+static int beaglelogic_tcp_read_data(struct dev_context *devc, char *buf,
+                                    int maxlen)
+{
+       int len;
+
+       len = recv(devc->socket, buf, maxlen, 0);
+
+       if (len < 0) {
+               sr_err("Receive error: %s", g_strerror(errno));
+               return SR_ERR;
+       }
+
+       return len;
+}
+
+SR_PRIV int beaglelogic_tcp_drain(struct dev_context *devc)
+{
+       char *buf = g_malloc(1024);
+       fd_set rset;
+       int ret, len = 0;
+       struct timeval tv;
+
+       FD_ZERO(&rset);
+       FD_SET(devc->socket, &rset);
+
+       /* 25ms timeout */
+       tv.tv_sec = 0;
+       tv.tv_usec = 25 * 1000;
+
+       do {
+               ret = select(devc->socket + 1, &rset, NULL, NULL, &tv);
+               if (ret > 0)
+                       len += beaglelogic_tcp_read_data(devc, buf, 1024);
+       } while (ret > 0);
+
+       sr_spew("Drained %d bytes of data.", len);
+
+       g_free(buf);
+
+       return SR_OK;
+}
+
+static int beaglelogic_tcp_get_string(struct dev_context *devc, const char *cmd,
+                                     char **tcp_resp)
+{
+       GString *response = g_string_sized_new(1024);
+       int len;
+       gint64 timeout;
+
+       *tcp_resp = NULL;
+       if (cmd) {
+               if (beaglelogic_tcp_send_cmd(devc, cmd) != SR_OK)
+                       return SR_ERR;
+       }
+
+       timeout = g_get_monotonic_time() + devc->read_timeout;
+       len = beaglelogic_tcp_read_data(devc, response->str,
+                                       response->allocated_len);
+
+       if (len < 0) {
+               g_string_free(response, TRUE);
+               return SR_ERR;
+       }
+
+       if (len > 0)
+               g_string_set_size(response, len);
+
+       if (g_get_monotonic_time() > timeout) {
+               sr_err("Timed out waiting for response.");
+               g_string_free(response, TRUE);
+               return SR_ERR_TIMEOUT;
+       }
+
+       /* Remove trailing newline if present */
+       if (response->len >= 1 && response->str[response->len - 1] == '\n')
+               g_string_truncate(response, response->len - 1);
+
+       /* Remove trailing carriage return if present */
+       if (response->len >= 1 && response->str[response->len - 1] == '\r')
+               g_string_truncate(response, response->len - 1);
+
+       sr_spew("Got response: '%.70s', length %" G_GSIZE_FORMAT ".",
+               response->str, response->len);
+
+       *tcp_resp = g_string_free(response, FALSE);
+
+       return SR_OK;
+}
+
+static int beaglelogic_tcp_get_int(struct dev_context *devc,
+                                  const char *cmd, int *response)
+{
+       int ret;
+       char *resp = NULL;
+
+       ret = beaglelogic_tcp_get_string(devc, cmd, &resp);
+       if (!resp && ret != SR_OK)
+               return ret;
+
+       if (sr_atoi(resp, response) == SR_OK)
+               ret = SR_OK;
+       else
+               ret = SR_ERR_DATA;
+
+       g_free(resp);
+
+       return ret;
+}
+
+SR_PRIV int beaglelogic_tcp_detect(struct dev_context *devc)
+{
+       char *resp = NULL;
+       int ret;
+
+       ret = beaglelogic_tcp_get_string(devc, "version", &resp);
+       if (ret == SR_OK && !g_ascii_strncasecmp(resp, "BeagleLogic", 11))
+               ret = SR_OK;
+       else
+               ret = SR_ERR;
+
+       g_free(resp);
+
+       return ret;
+}
+
+static int beaglelogic_open(struct dev_context *devc)
+{
+       return beaglelogic_tcp_open(devc);
+}
+
+static int beaglelogic_close(struct dev_context *devc)
+{
+       if (close(devc->socket) < 0)
+               return SR_ERR;
+
+       return SR_OK;
+}
+
+static int beaglelogic_get_buffersize(struct dev_context *devc)
+{
+       return beaglelogic_tcp_get_int(devc, "memalloc",
+               (int *)&devc->buffersize);
+}
+
+static int beaglelogic_set_buffersize(struct dev_context *devc)
+{
+       int ret;
+       char *resp;
+
+       beaglelogic_tcp_send_cmd(devc, "memalloc %lu", devc->buffersize);
+       ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+       if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+               ret = SR_OK;
+       else
+               ret = SR_ERR;
+
+       g_free(resp);
+
+       return ret;
+}
+
+static int beaglelogic_get_samplerate(struct dev_context *devc)
+{
+       int arg, err;
+
+       err = beaglelogic_tcp_get_int(devc, "samplerate", &arg);
+       if (err)
+               return err;
+
+       devc->cur_samplerate = arg;
+       return SR_OK;
+}
+
+static int beaglelogic_set_samplerate(struct dev_context *devc)
+{
+       int ret;
+       char *resp;
+
+       beaglelogic_tcp_send_cmd(devc, "samplerate %lu",
+               (uint32_t)devc->cur_samplerate);
+       ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+       if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+               ret = SR_OK;
+       else
+               ret = SR_ERR;
+
+       g_free(resp);
+
+       return ret;
+}
+
+static int beaglelogic_get_sampleunit(struct dev_context *devc)
+{
+       return beaglelogic_tcp_get_int(devc, "sampleunit",
+               (int *)&devc->sampleunit);
+}
+
+static int beaglelogic_set_sampleunit(struct dev_context *devc)
+{
+       int ret;
+       char *resp;
+
+       beaglelogic_tcp_send_cmd(devc, "sampleunit %lu", devc->sampleunit);
+       ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+       if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+               ret = SR_OK;
+       else
+               ret = SR_ERR;
+
+       g_free(resp);
+
+       return ret;
+}
+
+static int beaglelogic_get_triggerflags(struct dev_context *devc)
+{
+       return beaglelogic_tcp_get_int(devc, "triggerflags",
+               (int *)&devc->triggerflags);
+}
+
+static int beaglelogic_set_triggerflags(struct dev_context *devc)
+{
+       int ret;
+       char *resp;
+
+       beaglelogic_tcp_send_cmd(devc, "triggerflags %lu", devc->triggerflags);
+       ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+       if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+               ret = SR_OK;
+       else
+               ret = SR_ERR;
+
+       g_free(resp);
+
+       return ret;
+}
+
+static int beaglelogic_get_lasterror(struct dev_context *devc)
+{
+       devc->last_error = 0;
+
+       return SR_OK;
+}
+
+static int beaglelogic_start(struct dev_context *devc)
+{
+       beaglelogic_tcp_drain(devc);
+
+       return beaglelogic_tcp_send_cmd(devc, "get");
+}
+
+static int beaglelogic_stop(struct dev_context *devc)
+{
+       return beaglelogic_tcp_send_cmd(devc, "close");
+}
+
+static int beaglelogic_get_bufunitsize(struct dev_context *devc)
+{
+       return beaglelogic_tcp_get_int(devc, "bufunitsize",
+               (int *)&devc->bufunitsize);
+}
+
+static int beaglelogic_set_bufunitsize(struct dev_context *devc)
+{
+       int ret;
+       char *resp;
+
+       beaglelogic_tcp_send_cmd(devc, "bufunitsize %ld", devc->bufunitsize);
+       ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+       if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+               ret = SR_OK;
+       else
+               ret = SR_ERR;
+
+       g_free(resp);
+
+       return ret;
+}
+
+static int dummy(struct dev_context *devc)
+{
+       (void)devc;
+
+       return SR_ERR_NA;
+}
+
+SR_PRIV const struct beaglelogic_ops beaglelogic_tcp_ops = {
+       .open = beaglelogic_open,
+       .close = beaglelogic_close,
+       .get_buffersize = beaglelogic_get_buffersize,
+       .set_buffersize = beaglelogic_set_buffersize,
+       .get_samplerate = beaglelogic_get_samplerate,
+       .set_samplerate = beaglelogic_set_samplerate,
+       .get_sampleunit = beaglelogic_get_sampleunit,
+       .set_sampleunit = beaglelogic_set_sampleunit,
+       .get_triggerflags = beaglelogic_get_triggerflags,
+       .set_triggerflags = beaglelogic_set_triggerflags,
+       .start = beaglelogic_start,
+       .stop = beaglelogic_stop,
+       .get_lasterror = beaglelogic_get_lasterror,
+       .get_bufunitsize = beaglelogic_get_bufunitsize,
+       .set_bufunitsize = beaglelogic_set_bufunitsize,
+       .mmap = dummy,
+       .munmap = dummy,
+};
index 8f3ce26593175b50aff04570dc481a633684071b..053daad9176e7b4390faf522a285bec4c5f949aa 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Copyright (C) 2014-2017 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
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#endif
+#include <errno.h>
 #include "protocol.h"
+#include "beaglelogic.h"
 
 /* Define data packet size independent of packet (bufunitsize bytes) size
  * from the BeagleLogic kernel module */
@@ -32,7 +41,7 @@
  * 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)
+SR_PRIV int beaglelogic_native_receive_data(int fd, int revents, void *cb_data)
 {
        const struct sr_dev_inst *sdi;
        struct dev_context *devc;
@@ -90,7 +99,7 @@ SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data)
                if ((devc->offset += packetsize) >= devc->buffersize) {
                        /* One shot capture, we abort and settle with less than
                         * the required number of samples */
-                       if (devc->triggerflags)
+                       if (devc->triggerflags == BL_TRIGGERFLAGS_CONTINUOUS)
                                devc->offset = 0;
                        else
                                packetsize = 0;
@@ -107,3 +116,90 @@ SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data)
 
        return TRUE;
 }
+
+SR_PRIV int beaglelogic_tcp_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 len;
+       int pre_trigger_samples;
+       int trigger_offset;
+       uint32_t packetsize;
+       uint64_t bytes_remaining;
+
+       if (!(sdi = cb_data) || !(devc = sdi->priv))
+               return TRUE;
+
+       packetsize = TCP_BUFFER_SIZE;
+       logic.unitsize = SAMPLEUNIT_TO_BYTES(devc->sampleunit);
+
+       if (revents == G_IO_IN) {
+               sr_info("In callback G_IO_IN");
+
+               len = recv(fd, devc->tcp_buffer, TCP_BUFFER_SIZE, 0);
+               if (len < 0) {
+                       sr_err("Receive error: %s", g_strerror(errno));
+                       return SR_ERR;
+               }
+
+               packetsize = len;
+
+               bytes_remaining = (devc->limit_samples * logic.unitsize) -
+                               devc->bytes_read;
+
+               /* Configure data packet */
+               packet.type = SR_DF_LOGIC;
+               packet.payload = &logic;
+               logic.data = devc->tcp_buffer;
+               logic.length = MIN(packetsize, bytes_remaining);
+
+               if (devc->trigger_fired) {
+                       /* Send the incoming transfer to the session bus. */
+                       sr_session_send(sdi, &packet);
+               } else {
+                       /* Check for trigger */
+                       trigger_offset = soft_trigger_logic_check(devc->stl,
+                                       logic.data, packetsize, &pre_trigger_samples);
+                       if (trigger_offset > -1) {
+                               devc->bytes_read += pre_trigger_samples * logic.unitsize;
+                               trigger_offset *= logic.unitsize;
+                               logic.length = MIN(packetsize - trigger_offset,
+                                               bytes_remaining);
+                               logic.data += trigger_offset;
+
+                               sr_session_send(sdi, &packet);
+
+                               devc->trigger_fired = TRUE;
+                       }
+               }
+
+               /* 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 == BL_TRIGGERFLAGS_CONTINUOUS)
+                               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 */
+               std_session_send_df_end(sdi);
+               devc->beaglelogic->stop(devc);
+
+               /* Drain the receive buffer */
+               beaglelogic_tcp_drain(devc);
+
+               sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
+       }
+
+       return TRUE;
+}
index e058a60ab6560af593b1a8e83716f0ab5fa70dbc..a2af44f2543be96e22c54f7b2810f6ff6410ed9f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Copyright (C) 2014-2017 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
 
 #define SAMPLEUNIT_TO_BYTES(x) ((x) == 1 ? 1 : 2)
 
+#define TCP_BUFFER_SIZE         (128 * 1024)
+
 /** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        int max_channels;
        uint32_t fw_ver;
 
+       /* Operations */
+       const struct beaglelogic_ops *beaglelogic;
+
+       /* TCP Settings */
+       char *address;
+       char *port;
+       int socket;
+       unsigned int read_timeout;
+       unsigned char *tcp_buffer;
+
        /* Acquisition settings: see beaglelogic.h */
        uint64_t cur_samplerate;
        uint64_t limit_samples;
@@ -49,7 +60,6 @@ struct dev_context {
        uint32_t bufunitsize;
        uint32_t buffersize;
 
-       /* Operational state */
        int fd;
        GPollFD pollfd;
        int last_error;
@@ -64,6 +74,7 @@ struct dev_context {
        gboolean trigger_fired;
 };
 
-SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int beaglelogic_native_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int beaglelogic_tcp_receive_data(int fd, int revents, void *cb_data);
 
 #endif
index fb9f2807efd87496a18e346a8597d7a319c61cbe..f0713010ea820ac60706f987ace12ec3545b81a4 100644 (file)
@@ -26,8 +26,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
@@ -95,12 +98,9 @@ static int dev_open(struct sr_dev_inst *sdi)
        usb = sdi->conn;
        devc = sdi->priv;
 
-       if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
-               sdi->status = SR_ST_ACTIVE;
-       else
+       if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) < 0)
                return SR_ERR;
 
-       /* 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) {
@@ -109,20 +109,15 @@ static int dev_open(struct sr_dev_inst *sdi)
                        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;
+       return SR_OK;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
@@ -131,36 +126,30 @@ static int dev_close(struct sr_dev_inst *sdi)
        struct dev_context *devc;
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        usb = sdi->conn;
        devc = sdi->priv;
 
+       if (!usb->devhdl)
+               return SR_OK;
+
        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))) {
+               if ((ret = libusb_attach_kernel_driver(usb->devhdl, 0)))
                        sr_err("Failed to attach kernel driver: %s.\n",
                               libusb_error_name(ret));
-               } else {
+               else
                        devc->detached_kernel_driver = 0;
-                       sr_dbg("Successfully attached kernel driver.\n");
-               }
        }
 
        libusb_close(usb->devhdl);
 
-       sdi->status = SR_ST_INACTIVE;
-
-       return ret;
+       return (ret == 0) ? SR_OK : SR_ERR;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
 
@@ -169,50 +158,28 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return sr_sw_limits_config_get(&devc->sw_limits, key, data);
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->sw_limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->sw_limits);
@@ -227,9 +194,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        std_session_send_df_end(sdi);
 
        sr_session_source_remove(sdi->session, -1);
@@ -245,7 +209,7 @@ static struct sr_dev_driver brymen_bm86x_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 3181fef081c85cbf4380af54c774099b1d9e43af..097b8874ef61c20b9a0c22b781eb3d515bd3c682 100644 (file)
@@ -99,8 +99,10 @@ static void brymen_bm86x_parse(unsigned char *buf, float *floatval,
                if (buf[8] & 0x01) {
                        analog[0].meaning->mq = SR_MQ_VOLTAGE;
                        analog[0].meaning->unit = SR_UNIT_VOLT;
-                       if (!strcmp(str, "diod"))
+                       if (!strcmp(str, "diod")) {
                                analog[0].meaning->mqflags |= SR_MQFLAG_DIODE;
+                               analog[0].meaning->mqflags |= SR_MQFLAG_DC;
+                       }
                } else if (buf[14] & 0x80) {
                        analog[0].meaning->mq = SR_MQ_CURRENT;
                        analog[0].meaning->unit = SR_UNIT_AMPERE;
@@ -214,6 +216,8 @@ static void brymen_bm86x_handle_packet(const struct sr_dev_inst *sdi,
        struct sr_analog_encoding encoding[2];
        struct sr_analog_meaning meaning[2];
        struct sr_analog_spec spec[2];
+       struct sr_channel *channel;
+       int sent_ch1, sent_ch2;
        float floatval[2];
 
        devc = sdi->priv;
@@ -223,30 +227,35 @@ static void brymen_bm86x_handle_packet(const struct sr_dev_inst *sdi,
        sr_analog_init(&analog[1], &encoding[1], &meaning[1], &spec[1], 0);
 
        brymen_bm86x_parse(buf, floatval, analog);
+       sent_ch1 = sent_ch2 = 0;
 
-       if (analog[0].meaning->mq != 0) {
+       channel = sdi->channels->data;
+       if (analog[0].meaning->mq != 0 && channel->enabled) {
                /* Got a measurement. */
+               sent_ch1 = 1;
                analog[0].num_samples = 1;
                analog[0].data = &floatval[0];
-               analog[0].meaning->channels = g_slist_append(NULL, sdi->channels->data);
+               analog[0].meaning->channels = g_slist_append(NULL, channel);
                packet.type = SR_DF_ANALOG;
                packet.payload = &analog[0];
                sr_session_send(sdi, &packet);
                g_slist_free(analog[0].meaning->channels);
        }
 
-       if (analog[1].meaning->mq != 0) {
+       channel = sdi->channels->next->data;
+       if (analog[1].meaning->mq != 0 && channel->enabled) {
                /* Got a measurement. */
+               sent_ch2 = 1;
                analog[1].num_samples = 1;
                analog[1].data = &floatval[1];
-               analog[1].meaning->channels = g_slist_append(NULL, sdi->channels->next->data);
+               analog[1].meaning->channels = g_slist_append(NULL, channel);
                packet.type = SR_DF_ANALOG;
                packet.payload = &analog[1];
                sr_session_send(sdi, &packet);
                g_slist_free(analog[1].meaning->channels);
        }
 
-       if (analog[0].meaning->mq != 0 || analog[1].meaning->mq != 0)
+       if (sent_ch1 || sent_ch2)
                sr_sw_limits_update_samples_read(&devc->sw_limits, 1);
 }
 
@@ -344,7 +353,7 @@ SR_PRIV int brymen_bm86x_receive_data(int fd, int revents, void *cb_data)
                return FALSE;
 
        if (sr_sw_limits_check(&devc->sw_limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index 4cba7674b7de5ac1a3fa2d4cbc556713d7b85bca..c5c4d596807330ee3ce994abddf93d34df86136c 100644 (file)
 
 #define LOG_PREFIX "brymen-bm86x"
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Acquisition settings */
        struct sr_sw_limits sw_limits;
-
-       /* Operational state */
-       int detached_kernel_driver;/**< Whether kernel driver was detached or not */
-
-       /* Temporary state across callbacks */
+       int detached_kernel_driver; /**< Whether kernel driver was detached or not */
        int interrupt_pending;
 };
 
index 00ff6cf110783be92707c3e42c8d53183b069a91..ce6f1f33e4f4c8e832fa34232a74f04698d89f13 100644 (file)
@@ -25,8 +25,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -58,7 +61,7 @@ static GSList *brymen_scan(struct sr_dev_driver *di, const char *conn,
                goto scan_cleanup;
        }
 
-       len = 128;
+       len = sizeof(buf);
        ret = brymen_stream_detect(serial, buf, &len, brymen_packet_length,
                             brymen_packet_is_valid, 1000, 9600);
        if (ret != SR_OK)
@@ -107,52 +110,30 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        if (!conn)
                return NULL;
 
-       if (serialcomm) {
-               /* Use the provided comm specs. */
+       if (serialcomm)
                devices = brymen_scan(di, conn, serialcomm);
-       } else {
-               /* But 9600/8n1 should work all of the time. */
+       else
                devices = brymen_scan(di, conn, "9600/8n1/dtr=1/rts=1");
-       }
 
        return devices;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->sw_limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -160,15 +141,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->sw_limits);
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -184,7 +161,7 @@ static struct sr_dev_driver brymen_bm857_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = NULL,
        .config_set = config_set,
        .config_list = config_list,
index 2f55cdf70f2ea9d148e9dfe1f0ce526b8a86af01..d42ff45ac80549d8b8540168495283d201573b63 100644 (file)
@@ -276,7 +276,7 @@ SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
        }
 
        if (flags.is_diode)
-               analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+               analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
        /* We can have both AC+DC in a single measurement. */
        if (flags.is_ac)
                analog->meaning->mqflags |= SR_MQFLAG_AC;
index 5a7f2252a7e701639278207d2ad880de30942a49..3dd8dd3137a3074ea2bbb397963f09cb1e991874 100644 (file)
@@ -143,7 +143,7 @@ SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->sw_limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index 7e02180ec1de3e31005150da0da185a70602f14e..d5090ee63b3783524d378ce8b523a68fd0c708a7 100644 (file)
@@ -38,7 +38,6 @@ enum packet_len_status {
        PACKET_INVALID_HEADER,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits sw_limits;
 
index 5b8bb94cab9132e29cea329b25a8677cd97ccf1e..6db50a90d1b9cf38b9d879f03c03213ba9dca929 100644 (file)
@@ -125,11 +125,10 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, devices);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *range[2];
        uint64_t low, high;
        int tmp, ret;
 
@@ -139,7 +138,9 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                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);
@@ -175,11 +176,8 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                        *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);
-               }
+               if ((ret = cem_dt_885x_meas_range_get(sdi, &low, &high)) == SR_OK)
+                       *data = std_gvar_tuple_u64(low, high);
                break;
        case SR_CONF_POWER_OFF:
                *data = g_variant_new_boolean(FALSE);
@@ -197,148 +195,83 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return ret;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       uint64_t tmp_u64, low, high;
-       unsigned int i;
-       int tmp, ret;
-       const char *tmp_str;
+       int tmp, idx;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
-       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;
+               devc->limit_samples = g_variant_get_uint64(data);
                break;
        case SR_CONF_DATALOG:
-               ret = cem_dt_885x_recording_set(sdi, g_variant_get_boolean(data));
-               break;
+               return cem_dt_885x_recording_set(sdi, g_variant_get_boolean(data));
        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
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_freq))) < 0)
                        return SR_ERR_ARG;
-               break;
+               return cem_dt_885x_weight_freq_set(sdi, (weight_freq[idx][0] == 'A') ?
+                       SR_MQFLAG_SPL_FREQ_WEIGHT_A : SR_MQFLAG_SPL_FREQ_WEIGHT_C);
        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
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_time))) < 0)
                        return SR_ERR_ARG;
-               break;
+               return cem_dt_885x_weight_time_set(sdi, (weight_time[idx][0] == 'F') ?
+                       SR_MQFLAG_SPL_TIME_WEIGHT_F : SR_MQFLAG_SPL_TIME_WEIGHT_S);
        case SR_CONF_HOLD_MAX:
                tmp = g_variant_get_boolean(data) ? SR_MQFLAG_MAX : 0;
-               ret = cem_dt_885x_holdmode_set(sdi, tmp);
-               break;
+               return cem_dt_885x_holdmode_set(sdi, tmp);
        case SR_CONF_HOLD_MIN:
                tmp = g_variant_get_boolean(data) ? SR_MQFLAG_MIN : 0;
-               ret = cem_dt_885x_holdmode_set(sdi, tmp);
-               break;
+               return cem_dt_885x_holdmode_set(sdi, tmp);
        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;
+               if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(meas_ranges))) < 0)
+                       return SR_ERR_ARG;
+               return cem_dt_885x_meas_range_set(sdi, meas_ranges[idx][0], meas_ranges[idx][1]);
        case SR_CONF_POWER_OFF:
                if (g_variant_get_boolean(data))
-                       ret = cem_dt_885x_power_off(sdi);
+                       return 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;
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+                       return SR_ERR_ARG;
+               devc->cur_data_source = idx;
+               devc->enable_data_source_memory = (idx == DATA_SOURCE_MEMORY);
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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)cg;
-
-       ret = SR_OK;
-       if (!sdi) {
-               switch (key) {
-               case SR_CONF_SCAN_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-                       break;
-               case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-                       break;
-               default:
-                       return SR_ERR_NA;
-               }
-       } else {
-               switch (key) {
-               case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_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;
-               }
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_SPL_WEIGHT_FREQ:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_freq));
+               break;
+       case SR_CONF_SPL_WEIGHT_TIME:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_time));
+               break;
+       case SR_CONF_SPL_MEASUREMENT_RANGE:
+               *data = std_gvar_tuple_array(ARRAY_AND_SIZE(meas_ranges));
+               break;
+       case SR_CONF_DATA_SOURCE:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
+               break;
+       default:
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -346,9 +279,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        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->state = ST_INIT;
        devc->num_samples = 0;
@@ -356,7 +286,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -372,7 +301,7 @@ static struct sr_dev_driver cem_dt_885x_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 88fa8b7fbc68dfa26b8e1bbf6d67919143ac31eb..f6e05de9693845aee20dfb30e3508fd3f474b254 100644 (file)
@@ -147,7 +147,7 @@ static void process_mset(const struct sr_dev_inst *sdi)
 
                devc->num_samples++;
                if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
-                       sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
+                       sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
                break;
        case TOKEN_RECORDING_ON:
                devc->recording = TRUE;
@@ -208,7 +208,7 @@ static void send_data(const struct sr_dev_inst *sdi, unsigned char *data,
 
        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);
+               sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
 
        return;
 }
@@ -321,7 +321,7 @@ static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c,
                         * 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);
+                       sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
                } else if (c == RECORD_DATA) {
                        devc->buf_len = 0;
                        devc->state = ST_GET_LOG_RECORD_DATA;
@@ -400,7 +400,7 @@ SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data)
                        } else {
                                /* Tell device to start transferring from memory. */
                                cmd = CMD_TRANSFER_MEMORY;
-                               serial_write_nonblocking(serial, &cmd, 1);
+                               serial_write_blocking(serial, &cmd, 1, 0);
                        }
                }
        }
@@ -456,7 +456,7 @@ static int cem_dt_885x_toggle(const struct sr_dev_inst *sdi, uint8_t cmd,
         * 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_nonblocking(serial, (const void *)&cmd, 1) != 1)
+               if (serial_write_blocking(serial, (const void *)&cmd, 1, 0) < 0)
                        return SR_ERR;
                if (wait_for_token(sdi, tokens, timeout) == SR_ERR)
                        return SR_ERR;
@@ -817,7 +817,7 @@ SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi)
        cmd = CMD_TOGGLE_POWER_OFF;
        while (TRUE) {
                serial_flush(serial);
-               if (serial_write_nonblocking(serial, (const void *)&cmd, 1) != 1)
+               if (serial_write_blocking(serial, (const void *)&cmd, 1, 0) < 0)
                        return SR_ERR;
                /* It never takes more than 23ms for the next token to arrive. */
                g_usleep(25 * 1000);
index bb0984225e0dc5de2dfa5a3a5951aa2778f0c627..f183ed11be26773eeee9b0f495b0e94da1105a3e 100644 (file)
@@ -87,23 +87,18 @@ enum {
        DATA_SOURCE_MEMORY,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Device state */
        enum sr_mqflag 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 */
        unsigned char cmd;
        unsigned char token;
        int buf_len;
index 44f07f4d542b79f8aeb2eb40646f93f2d2f82a72..4591ae967dc7d0e5e85fede5b37c6dae5a343dc6 100644 (file)
@@ -109,55 +109,30 @@ static GSList *scan(GSList *options, int idx)
        if (!conn)
                return NULL;
 
-       if (serialcomm) {
-               /* Use the provided comm specs. */
+       if (serialcomm)
                devices = center_scan(conn, serialcomm, idx);
-       } else {
-               /* Try the default. */
+       else
                devices = center_scan(conn, center_devs[idx].conn, idx);
-       }
 
        return std_scan_complete(center_devs[idx].di, devices);
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->sw_limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       (void)cg;
-
-       switch (key) {
-       case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi)
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               else
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, int idx)
@@ -165,16 +140,12 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, 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;
 
        sr_sw_limits_acquisition_start(&devc->sw_limits);
 
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -202,6 +173,7 @@ static struct sr_dev_driver ID##_driver_info = { \
        .cleanup = std_cleanup, \
        .scan = scan_##ID_UPPER, \
        .dev_list = std_dev_list, \
+       .dev_clear = std_dev_clear, \
        .config_get = NULL, \
        .config_set = config_set, \
        .config_list = config_list, \
index ca1ae3b59821f14d8622ada038acaca0fbae846f..81868b0618aa141f8702bdea993eac3a8fe96110 100644 (file)
@@ -177,7 +177,7 @@ 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;
+       int len, offset, ret = FALSE;
 
        devc = sdi->priv;
        serial = sdi->conn;
@@ -193,6 +193,7 @@ static gboolean handle_new_data(struct sr_dev_inst *sdi, int idx)
        devc->buflen += len;
 
        /* Now look for packets in that data. */
+       offset = 0;
        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);
@@ -204,8 +205,8 @@ static gboolean handle_new_data(struct sr_dev_inst *sdi, int idx)
        }
 
        /* 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];
+       if (offset < devc->buflen)
+               memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
        devc->buflen -= offset;
 
        return ret;
@@ -243,7 +244,7 @@ static int receive_data(int fd, int revents, int idx, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->sw_limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index fbbdaf3010c59377a3a2d3b6c3089af1a0480ce9..6bd2c27c20c1c4f4dc26ca5a0388ba14c0c173ae 100644 (file)
@@ -51,7 +51,6 @@ extern SR_PRIV const struct center_dev_info center_devs[];
 
 #define SERIAL_BUFSIZE 256
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits sw_limits;
 
index 11721ebe6bbc72d0a6933ff8c880cd7eefa5f865..694bd54ac84fb91f0f864cefa67d9d31eb2a641c 100644 (file)
 #include <config.h>
 #include "protocol.h"
 
-static const uint32_t drvopts[] = {
-       SR_CONF_LOGIC_ANALYZER,
-};
-
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
+static const uint32_t drvopts[] = {
+       SR_CONF_LOGIC_ANALYZER,
+};
+
 static const uint32_t devopts[] = {
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST,
@@ -43,21 +43,15 @@ static const int32_t trigger_matches[] = {
        SR_TRIGGER_FALLING,
 };
 
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
-
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
-
        ftdi_free(devc->ftdic);
        g_free(devc->final_buf);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static int add_device(int model, struct libusb_device_descriptor *des,
@@ -71,7 +65,6 @@ static int add_device(int model, struct libusb_device_descriptor *des,
 
        ret = SR_OK;
 
-       /* Allocate memory for our private device context. */
        devc = g_malloc0(sizeof(struct dev_context));
 
        /* Set some sane defaults. */
@@ -103,7 +96,6 @@ static int add_device(int model, struct libusb_device_descriptor *des,
        /* We now know the device, set its max. samplerate as default. */
        devc->cur_samplerate = devc->prof->max_samplerate;
 
-       /* Register the device with libsigrok. */
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
        sdi->vendor = g_strdup("ChronoVu");
@@ -200,18 +192,17 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                        continue;
                }
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
-
                libusb_close(hdl);
 
-               if (!strcmp(product, "ChronoVu LA8")) {
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
+
+               if (!strcmp(product, "ChronoVu LA8"))
                        model = 0;
-               } else if (!strcmp(product, "ChronoVu LA16")) {
+               else if (!strcmp(product, "ChronoVu LA16"))
                        model = 1;
-               } else {
-                       sr_spew("Unknown iProduct string '%s'.", product);
-                       continue;
-               }
+               else
+                       continue; /* Unknown iProduct string, ignore. */
 
                sr_dbg("Found %s (%04x:%04x, %d.%d, %s).",
                       product, des.idVendor, des.idProduct,
@@ -237,7 +228,6 @@ static int dev_open(struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       /* Allocate memory for the FTDI context and initialize it. */
        if (!(devc->ftdic = ftdi_new())) {
                sr_err("Failed to initialize libftdi.");
                return SR_ERR;
@@ -246,43 +236,33 @@ static int dev_open(struct sr_dev_inst *sdi)
        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;
-
-       if (ret == SR_OK)
-               return SR_OK;
+       return SR_OK;
 
 err_ftdi_free:
        ftdi_free(devc->ftdic); /* Close device (if open), free FTDI context. */
        devc->ftdic = NULL;
-       return ret;
+       return SR_ERR;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
@@ -290,25 +270,23 @@ 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)
+       if (!devc->ftdic)
+               return SR_ERR_BUG;
+
+       if ((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;
+       return (ret == 0) ? SR_OK : SR_ERR;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
-       char str[128];
 
        (void)cg;
 
@@ -316,8 +294,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        case SR_CONF_CONN:
                if (!sdi || !(usb = sdi->conn))
                        return SR_ERR_ARG;
-               snprintf(str, 128, "%d.%d", usb->bus, usb->address);
-               *data = g_variant_new_string(str);
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                break;
        case SR_CONF_SAMPLERATE:
                if (!sdi)
@@ -332,16 +309,13 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
@@ -350,13 +324,9 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        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:
@@ -366,57 +336,30 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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;
+       devc = (sdi) ? sdi->priv : NULL;
 
        switch (key) {
        case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi)
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               else
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        case SR_CONF_SAMPLERATE:
-               if (!sdi)
-                       return SR_ERR_BUG;
-               devc = sdi->priv;
                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);
+               *data = std_gvar_samplerates(ARRAY_AND_SIZE(devc->samplerates));
                break;
        case SR_CONF_LIMIT_SAMPLES:
-               if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
+               if (!devc || !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);
+               *data = std_gvar_tuple_u64(0, (devc->prof->model == CHRONOVU_LA8) ? MAX_NUM_SAMPLES : MAX_NUM_SAMPLES / 2);
                break;
        case SR_CONF_TRIGGER_MATCH:
-               if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
+               if (!devc || !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));
+               *data = std_gvar_array_i32(trigger_matches, devc->prof->num_trigger_matches);
                break;
        default:
                return SR_ERR_NA;
@@ -452,7 +395,7 @@ static int receive_data(int fd, int revents, void *cb_data)
        /* 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);
+               sr_dev_acquisition_stop(sdi);
                return FALSE;
        }
 
@@ -475,7 +418,7 @@ static int receive_data(int fd, int revents, void *cb_data)
        for (i = 0; i < NUM_BLOCKS; i++)
                cv_send_block_to_session_bus(sdi, i);
 
-       dev_acquisition_stop(sdi);
+       sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
@@ -486,9 +429,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        uint8_t buf[8];
        int bytes_to_write, bytes_written;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        if (!devc->ftdic) {
@@ -534,8 +474,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                return SR_ERR;
        }
 
-       sr_dbg("Hardware acquisition started successfully.");
-
        std_session_send_df_header(sdi);
 
        /* Time when we should be done (for detecting trigger timeouts). */
@@ -552,7 +490,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       sr_dbg("Stopping acquisition.");
        sr_session_source_remove(sdi->session, -1);
        std_session_send_df_end(sdi);
 
index 609f37d9c5888b8ad1f6636037ddb826d91b018d..5a051da3882238e49f1ab673ff8f0ceb2e4b3244 100644 (file)
@@ -28,7 +28,7 @@
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-#define LOG_PREFIX "la8/la16"
+#define LOG_PREFIX "chronovu-la"
 
 #define SDRAM_SIZE                     (8 * 1024 * 1024)
 #define MAX_NUM_SAMPLES                        SDRAM_SIZE
@@ -44,28 +44,18 @@ enum {
 struct cv_profile {
        int model;
        const char *modelname;
-       const char *iproduct; /* USB iProduct string */
+       const char *iproduct;
        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;
 
        /**
@@ -124,7 +114,6 @@ struct dev_context {
        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);
index 762cda19df7809a859c4aadc5ce82693debf8b29..8423c71972696a80acb3927897f8a404254a4350 100644 (file)
@@ -35,8 +35,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_SOUNDLEVELMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -81,41 +84,22 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, g_slist_append(NULL, sdi));
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -123,13 +107,9 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc = sdi->priv;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -145,7 +125,7 @@ static struct sr_dev_driver colead_slm_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = NULL,
        .config_set = config_set,
        .config_list = config_list,
index 2f6957abe2d0170770eb86694812abcc566090f2..570e7db33a6a918eecff07f11bf2a9817dcd09be 100644 (file)
@@ -177,7 +177,7 @@ static void process_packet(const struct sr_dev_inst *sdi)
        sr_sw_limits_update_samples_read(&devc->limits, 1);
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
+               sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
 }
 
 SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
@@ -202,7 +202,7 @@ SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
 
        serial = sdi->conn;
        if (devc->state == IDLE) {
-               if (serial_read_nonblocking(serial, buf, 128) != 1 || buf[0] != 0x10)
+               if (serial_read_nonblocking(serial, buf, sizeof(buf)) != 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. */
index 8b298bb7648b132bd901502a0932c7d8d2713525..1849a865193300736e32bb571a0388b10eaef08f 100644 (file)
@@ -31,7 +31,6 @@ enum {
        COMMAND_SENT,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits limits;
 
index cfa503d52a125d86a010d476f2b5398ab04cab98..1524df0a5bc9437fabd2d8ea7a2c85eb3aed3cb2 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * <em>Conrad DIGI 35 CPU</em> power supply driver
- *
- * @internal
- */
-
 #include <config.h>
 #include "protocol.h"
 
@@ -35,15 +27,19 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_POWER_SUPPLY,
-       SR_CONF_VOLTAGE | SR_CONF_SET,
-       SR_CONF_CURRENT | SR_CONF_SET,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_VOLTAGE_TARGET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CURRENT_LIMIT | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_SET,
 };
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
+       struct dev_context *devc;
        struct sr_dev_inst *sdi;
        struct sr_config *src;
        struct sr_serial_dev_inst *serial;
@@ -88,92 +84,67 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        sdi->status = SR_ST_INACTIVE;
        sdi->vendor = g_strdup("Conrad");
        sdi->model = g_strdup("DIGI 35 CPU");
+       devc = g_malloc0(sizeof(struct dev_context));
+       sr_sw_limits_init(&devc->limits);
+       sdi->inst_type = SR_INST_SERIAL;
        sdi->conn = serial;
-       sdi->priv = NULL;
+       sdi->priv = devc;
        sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
 
        return std_scan_complete(di, g_slist_append(NULL, sdi));
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t 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;
-
        switch (key) {
-       case SR_CONF_VOLTAGE:
+       case SR_CONF_VOLTAGE_TARGET:
                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_CURRENT:
+               return send_msg1(sdi, 'V', (int) (dblval * 10 + 0.5));
+       case SR_CONF_CURRENT_LIMIT:
                dblval = g_variant_get_double(data);
-               if ((dblval < 0.01) || (dblval > 2.55)) {
+               if ((dblval < 0.00) || (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;
+               return send_msg1(sdi, 'C', (int) (dblval * 100 + 0.5));
        case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
                if (g_variant_get_boolean(data))
-                       ret = send_msg1(sdi, 'V', 900);
+                       return send_msg1(sdi, 'V', 900);
                else /* Constant current mode */
-                       ret = send_msg1(sdi, 'V', 901);
-               break;
+                       return send_msg1(sdi, 'V', 901);
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_VOLTAGE_TARGET:
+               *data = std_gvar_min_max_step(0.0, 35.0, 0.1);
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               *data = std_gvar_min_max_step(0.0, 2.55, 0.01);
                break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi)
-{
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi)
-{
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        return SR_OK;
 }
 
@@ -185,14 +156,14 @@ static struct sr_dev_driver conrad_digi_35_cpu_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .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,
+       .dev_acquisition_start = std_dummy_dev_acquisition_start,
+       .dev_acquisition_stop = std_dummy_dev_acquisition_stop,
        .context = NULL,
 };
 SR_REGISTER_DEV_DRIVER(conrad_digi_35_cpu_driver_info);
index 732bec62c7805169daaa74a840950c2dab01b626..78f7d1c34c0f7136db9396f374b590fd45c440de 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- * <em>Conrad DIGI 35 CPU</em> power supply driver
- * @internal
- */
-
 #include <config.h>
 #include "protocol.h"
 
index 99d2cb7041e2da670edb847469fcaa1318208ed3..09a1428f3bc28e936db589f1743100936eb95aef 100644 (file)
  * 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
 
 
 #define LOG_PREFIX "conrad-digi-35-cpu"
 
+struct dev_context {
+       struct sr_sw_limits limits;
+};
+
 SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param);
 
 #endif
index 9c1b7c4466590a7975565b3652e46f7ab6b1dfe8..7d3e08872d07c21b38832845a138d5cf2bd3fe80 100644 (file)
 #include "protocol.h"
 
 #define DEFAULT_NUM_LOGIC_CHANNELS     8
-#define DEFAULT_ENABLED_LOGIC_CHANNELS 8
 #define DEFAULT_LOGIC_PATTERN          PATTERN_SIGROK
 
 #define DEFAULT_NUM_ANALOG_CHANNELS    4
-#define DEFAULT_ENABLED_ANALOG_CHANNELS        4
 #define DEFAULT_ANALOG_AMPLITUDE       10
 
 /* Note: No spaces allowed because of sigrok-cli. */
@@ -46,6 +44,13 @@ static const char *logic_pattern_str[] = {
        "all-low",
        "all-high",
        "squid",
+       "graycode",
+};
+
+static const uint32_t scanopts[] = {
+       SR_CONF_NUM_LOGIC_CHANNELS,
+       SR_CONF_NUM_ANALOG_CHANNELS,
+       SR_CONF_LIMIT_FRAMES,
 };
 
 static const uint32_t drvopts[] = {
@@ -54,18 +59,16 @@ static const uint32_t drvopts[] = {
        SR_CONF_OSCILLOSCOPE,
 };
 
-static const uint32_t scanopts[] = {
-       SR_CONF_NUM_LOGIC_CHANNELS,
-       SR_CONF_NUM_ANALOG_CHANNELS,
-};
-
 static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_AVERAGING | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+       SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint32_t devopts_cg_logic[] = {
@@ -81,6 +84,14 @@ static const uint32_t devopts_cg_analog_channel[] = {
        SR_CONF_AMPLITUDE | SR_CONF_GET | SR_CONF_SET,
 };
 
+static const int32_t trigger_matches[] = {
+       SR_TRIGGER_ZERO,
+       SR_TRIGGER_ONE,
+       SR_TRIGGER_RISING,
+       SR_TRIGGER_FALLING,
+       SR_TRIGGER_EDGE,
+};
+
 static const uint64_t samplerates[] = {
        SR_HZ(1),
        SR_GHZ(1),
@@ -97,11 +108,12 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        struct analog_gen *ag;
        GSList *l;
        int num_logic_channels, num_analog_channels, pattern, i;
+       uint64_t limit_frames;
        char channel_name[16];
-       gboolean enabled;
 
        num_logic_channels = DEFAULT_NUM_LOGIC_CHANNELS;
        num_analog_channels = DEFAULT_NUM_ANALOG_CHANNELS;
+       limit_frames = DEFAULT_LIMIT_FRAMES;
        for (l = options; l; l = l->next) {
                src = l->data;
                switch (src->key) {
@@ -111,6 +123,9 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                case SR_CONF_NUM_ANALOG_CHANNELS:
                        num_analog_channels = g_variant_get_int32(src->data);
                        break;
+               case SR_CONF_LIMIT_FRAMES:
+                       limit_frames = g_variant_get_uint64(src->data);
+                       break;
                }
        }
 
@@ -122,8 +137,14 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        devc->cur_samplerate = SR_KHZ(200);
        devc->num_logic_channels = num_logic_channels;
        devc->logic_unitsize = (devc->num_logic_channels + 7) / 8;
+       devc->all_logic_channels_mask = 1UL << 0;
+       devc->all_logic_channels_mask <<= devc->num_logic_channels;
+       devc->all_logic_channels_mask--;
        devc->logic_pattern = DEFAULT_LOGIC_PATTERN;
        devc->num_analog_channels = num_analog_channels;
+       devc->limit_frames = limit_frames;
+       devc->capture_ratio = 20;
+       devc->stl = NULL;
 
        if (num_logic_channels > 0) {
                /* Logic channels, all in one channel group. */
@@ -131,14 +152,14 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                cg->name = g_strdup("Logic");
                for (i = 0; i < num_logic_channels; i++) {
                        sprintf(channel_name, "D%d", i);
-                       enabled = (i < DEFAULT_ENABLED_LOGIC_CHANNELS) ? TRUE : FALSE;
-                       ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, enabled, channel_name);
+                       ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
                        cg->channels = g_slist_append(cg->channels, ch);
                }
                sdi->channel_groups = g_slist_append(NULL, cg);
        }
 
        /* Analog channels, channel groups and pattern generators. */
+       devc->ch_ag = g_hash_table_new(g_direct_hash, g_direct_equal);
        if (num_analog_channels > 0) {
                pattern = 0;
                /* An "Analog" channel group with all analog channels in it. */
@@ -146,12 +167,10 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                acg->name = g_strdup("Analog");
                sdi->channel_groups = g_slist_append(sdi->channel_groups, acg);
 
-               devc->ch_ag = g_hash_table_new(g_direct_hash, g_direct_equal);
                for (i = 0; i < num_analog_channels; i++) {
                        snprintf(channel_name, 16, "A%d", i);
-                       enabled = (i < DEFAULT_ENABLED_ANALOG_CHANNELS) ? TRUE : FALSE;
                        ch = sr_channel_new(sdi, i + num_logic_channels, SR_CHANNEL_ANALOG,
-                                       enabled, channel_name);
+                                       TRUE, channel_name);
                        acg->channels = g_slist_append(acg->channels, ch);
 
                        /* Every analog channel gets its own channel group as well. */
@@ -162,6 +181,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
                        /* Every channel gets a generator struct. */
                        ag = g_malloc(sizeof(struct analog_gen));
+                       ag->ch = ch;
                        ag->amplitude = DEFAULT_ANALOG_AMPLITUDE;
                        sr_analog_init(&ag->packet, &ag->encoding, &ag->meaning, &ag->spec, 2);
                        ag->packet.meaning->channels = cg->channels;
@@ -184,43 +204,25 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, g_slist_append(NULL, sdi));
 }
 
-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 void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
        GHashTableIter iter;
        void *value;
 
-       devc = priv;
-
        /* Analog generators. */
        g_hash_table_iter_init(&iter, devc->ch_ag);
        while (g_hash_table_iter_next(&iter, NULL, &value))
                g_free(value);
        g_hash_table_unref(devc->ch_ag);
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_channel *ch;
@@ -241,6 +243,9 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        case SR_CONF_LIMIT_MSEC:
                *data = g_variant_new_uint64(devc->limit_msec);
                break;
+       case SR_CONF_LIMIT_FRAMES:
+               *data = g_variant_new_uint64(devc->limit_frames);
+               break;
        case SR_CONF_AVERAGING:
                *data = g_variant_new_boolean(devc->avg);
                break;
@@ -272,6 +277,9 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                ag = g_hash_table_lookup(devc->ch_ag, ch);
                *data = g_variant_new_double(ag->amplitude);
                break;
+       case SR_CONF_CAPTURE_RATIO:
+               *data = g_variant_new_uint64(devc->capture_ratio);
+               break;
        default:
                return SR_ERR_NA;
        }
@@ -279,23 +287,17 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct analog_gen *ag;
        struct sr_channel *ch;
        GSList *l;
-       int logic_pattern, analog_pattern, ret;
-       unsigned int i;
-       const char *stropt;
+       int logic_pattern, analog_pattern;
 
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_SAMPLERATE:
                devc->cur_samplerate = g_variant_get_uint64(data);
@@ -308,6 +310,9 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                devc->limit_msec = g_variant_get_uint64(data);
                devc->limit_samples = 0;
                break;
+       case SR_CONF_LIMIT_FRAMES:
+               devc->limit_frames = g_variant_get_uint64(data);
+               break;
        case SR_CONF_AVERAGING:
                devc->avg = g_variant_get_boolean(data);
                sr_dbg("%s averaging", devc->avg ? "Enabling" : "Disabling");
@@ -319,21 +324,9 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        case SR_CONF_PATTERN_MODE:
                if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               stropt = g_variant_get_string(data, NULL);
-               logic_pattern = analog_pattern = -1;
-               for (i = 0; i < ARRAY_SIZE(logic_pattern_str); i++) {
-                       if (!strcmp(stropt, logic_pattern_str[i])) {
-                               logic_pattern = i;
-                               break;
-                       }
-               }
-               for (i = 0; i < ARRAY_SIZE(analog_pattern_str); i++) {
-                       if (!strcmp(stropt, analog_pattern_str[i])) {
-                               analog_pattern = i;
-                               break;
-                       }
-               }
-               if (logic_pattern == -1 && analog_pattern == -1)
+               logic_pattern = std_str_idx(data, ARRAY_AND_SIZE(logic_pattern_str));
+               analog_pattern = std_str_idx(data, ARRAY_AND_SIZE(analog_pattern_str));
+               if (logic_pattern < 0 && analog_pattern < 0)
                        return SR_ERR_ARG;
                for (l = cg->channels; l; l = l->next) {
                        ch = l->data;
@@ -370,47 +363,31 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        ag->amplitude = g_variant_get_double(data);
                }
                break;
+       case SR_CONF_CAPTURE_RATIO:
+               devc->capture_ratio = g_variant_get_uint64(data);
+               break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct sr_channel *ch;
-       GVariant *gvar;
-       GVariantBuilder gvb;
-
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (!sdi)
-               return SR_ERR_ARG;
 
        if (!cg) {
                switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       break;
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
                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);
+                       *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
+                       break;
+               case SR_CONF_TRIGGER_MATCH:
+                       *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                        break;
                default:
                        return SR_ERR_NA;
@@ -420,18 +397,12 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                switch (key) {
                case SR_CONF_DEVICE_OPTIONS:
                        if (ch->type == SR_CHANNEL_LOGIC)
-                               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                               devopts_cg_logic, ARRAY_SIZE(devopts_cg_logic),
-                                               sizeof(uint32_t));
+                               *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_logic));
                        else if (ch->type == SR_CHANNEL_ANALOG) {
                                if (strcmp(cg->name, "Analog") == 0)
-                                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                                       devopts_cg_analog_group, ARRAY_SIZE(devopts_cg_analog_group),
-                                                       sizeof(uint32_t));
+                                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog_group));
                                else
-                                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                                       devopts_cg_analog_channel, ARRAY_SIZE(devopts_cg_analog_channel),
-                                                       sizeof(uint32_t));
+                                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog_channel));
                        }
                        else
                                return SR_ERR_BUG;
@@ -442,11 +413,9 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                                return SR_ERR_NA;
 
                        if (ch->type == SR_CHANNEL_LOGIC)
-                               *data = g_variant_new_strv(logic_pattern_str,
-                                               ARRAY_SIZE(logic_pattern_str));
+                               *data = g_variant_new_strv(ARRAY_AND_SIZE(logic_pattern_str));
                        else if (ch->type == SR_CHANNEL_ANALOG)
-                               *data = g_variant_new_strv(analog_pattern_str,
-                                               ARRAY_SIZE(analog_pattern_str));
+                               *data = g_variant_new_strv(ARRAY_AND_SIZE(analog_pattern_str));
                        else
                                return SR_ERR_BUG;
                        break;
@@ -461,15 +430,82 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
+       GSList *l;
+       struct sr_channel *ch;
+       int bitpos;
+       uint8_t mask;
        GHashTableIter iter;
        void *value;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
+       struct sr_trigger *trigger;
 
        devc = sdi->priv;
        devc->sent_samples = 0;
-
+       devc->sent_frame_samples = 0;
+
+       /* Setup triggers */
+       if ((trigger = sr_session_trigger_get(sdi->session))) {
+               int pre_trigger_samples = 0;
+               if (devc->limit_samples > 0)
+                       pre_trigger_samples = (devc->capture_ratio * devc->limit_samples) / 100;
+               devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
+               if (!devc->stl)
+                       return SR_ERR_MALLOC;
+
+               /* Disable all analog channels since using them when there are logic
+                * triggers set up would require having pre-trigger sample buffers
+                * for analog sample data.
+                */
+               for (l = sdi->channels; l; l = l->next) {
+                       ch = l->data;
+                       if (ch->type == SR_CHANNEL_ANALOG)
+                               ch->enabled = FALSE;
+               }
+       }
+       devc->trigger_fired = FALSE;
+
+       /*
+        * Determine the numbers of logic and analog channels that are
+        * involved in the acquisition. Determine an offset and a mask to
+        * remove excess logic data content before datafeed submission.
+        */
+       devc->enabled_logic_channels = 0;
+       devc->enabled_analog_channels = 0;
+       for (l = sdi->channels; l; l = l->next) {
+               ch = l->data;
+               if (!ch->enabled)
+                       continue;
+               if (ch->type == SR_CHANNEL_ANALOG) {
+                       devc->enabled_analog_channels++;
+                       continue;
+               }
+               if (ch->type != SR_CHANNEL_LOGIC)
+                       continue;
+               /*
+                * TODO: Need we create a channel map here, such that the
+                * session datafeed packets will have a dense representation
+                * of the enabled channels' data? For example store channels
+                * D3 and D5 in bit positions 0 and 1 respectively, when all
+                * other channels are disabled? The current implementation
+                * generates a sparse layout, might provide data for logic
+                * channels that are disabled while it might suppress data
+                * from enabled channels at the same time.
+                */
+               devc->enabled_logic_channels++;
+       }
+       devc->first_partial_logic_index = devc->enabled_logic_channels / 8;
+       bitpos = devc->enabled_logic_channels % 8;
+       mask = (1 << bitpos) - 1;
+       devc->first_partial_logic_mask = mask;
+       sr_dbg("num logic %zu, partial off %zu, mask 0x%02x.",
+               devc->enabled_logic_channels,
+               devc->first_partial_logic_index,
+               devc->first_partial_logic_mask);
+
+       /*
+        * Have the waveform for analog patterns pre-generated. It's
+        * supposed to be periodic, so the generator just needs to
+        * access the prepared sample data (DDS style).
+        */
        g_hash_table_iter_init(&iter, devc->ch_ag);
        while (g_hash_table_iter_next(&iter, NULL, &value))
                demo_generate_analog_pattern(value, devc->cur_samplerate);
@@ -479,6 +515,9 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
        std_session_send_df_header(sdi);
 
+       if (devc->limit_frames > 0)
+               std_session_send_frame_begin(sdi);
+
        /* We use this timestamp to decide how many more samples to send. */
        devc->start_us = g_get_monotonic_time();
        devc->spent_us = 0;
@@ -489,10 +528,21 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       sr_dbg("Stopping acquisition.");
+       struct dev_context *devc;
+
        sr_session_source_remove(sdi->session, -1);
+
+       devc = sdi->priv;
+       if (devc->limit_frames > 0)
+               std_session_send_frame_end(sdi);
+
        std_session_send_df_end(sdi);
 
+       if (devc->stl) {
+               soft_trigger_logic_free(devc->stl);
+               devc->stl = NULL;
+       }
+
        return SR_OK;
 }
 
@@ -508,8 +558,8 @@ static struct sr_dev_driver demo_driver_info = {
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
-       .dev_open = dev_open,
-       .dev_close = dev_close,
+       .dev_open = std_dummy_dev_open,
+       .dev_close = std_dummy_dev_close,
        .dev_acquisition_start = dev_acquisition_start,
        .dev_acquisition_stop = dev_acquisition_stop,
        .context = NULL,
index 7f5950b6b87188af6fa495eba402f69e8f37d8b7..ce897ad903bc0790223bc5e82182b8f41cd6695b 100644 (file)
@@ -245,6 +245,19 @@ SR_PRIV void demo_generate_analog_pattern(struct analog_gen *ag, uint64_t sample
        }
 }
 
+static uint64_t encode_number_to_gray(uint64_t nr)
+{
+       return nr ^ (nr >> 1);
+}
+
+static void set_logic_data(uint64_t bits, uint8_t *data, size_t len)
+{
+       while (len--) {
+               *data++ = bits & 0xff;
+               bits >>= 8;
+       }
+}
+
 static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
 {
        struct dev_context *devc;
@@ -253,6 +266,7 @@ static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
        uint8_t *sample;
        const uint8_t *image_col;
        size_t col_count, col_height;
+       uint64_t gray;
 
        devc = sdi->priv;
 
@@ -273,15 +287,14 @@ static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
                break;
        case PATTERN_INC:
                for (i = 0; i < size; i++) {
-                       for (j = 0; j < devc->logic_unitsize; j++) {
+                       for (j = 0; j < devc->logic_unitsize; j++)
                                devc->logic_data[i + j] = devc->step;
-                       }
                        devc->step++;
                }
                break;
        case PATTERN_WALKING_ONE:
                /* j contains the value of the highest bit */
-               j = 1 << (devc->num_logic_channels - 1);
+               j = 1 << (devc->num_logic_channels - 1);
                for (i = 0; i < size; i++) {
                        devc->logic_data[i] = devc->step;
                        if (devc->step == 0)
@@ -296,7 +309,7 @@ static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
        case PATTERN_WALKING_ZERO:
                /* Same as walking one, only with inverted output */
                /* j contains the value of the highest bit */
-               j = 1 << (devc->num_logic_channels - 1);
+               j = 1 << (devc->num_logic_channels - 1);
                for (i = 0; i < size; i++) {
                        devc->logic_data[i] = ~devc->step;
                        if (devc->step == 0)
@@ -327,12 +340,49 @@ static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
                        devc->step %= col_count;
                }
                break;
+       case PATTERN_GRAYCODE:
+               for (i = 0; i < size; i += devc->logic_unitsize) {
+                       devc->step++;
+                       devc->step &= devc->all_logic_channels_mask;
+                       gray = encode_number_to_gray(devc->step);
+                       gray &= devc->all_logic_channels_mask;
+                       set_logic_data(gray, &devc->logic_data[i], devc->logic_unitsize);
+               }
+               break;
        default:
                sr_err("Unknown pattern: %d.", devc->logic_pattern);
                break;
        }
 }
 
+/*
+ * Fixup a memory image of generated logic data before it gets sent to
+ * the session's datafeed. Mask out content from disabled channels.
+ *
+ * TODO: Need we apply a channel map, and enforce a dense representation
+ * of the enabled channels' data?
+ */
+static void logic_fixup_feed(struct dev_context *devc,
+               struct sr_datafeed_logic *logic)
+{
+       size_t fp_off;
+       uint8_t fp_mask;
+       size_t off, idx;
+       uint8_t *sample;
+
+       fp_off = devc->first_partial_logic_index;
+       fp_mask = devc->first_partial_logic_mask;
+       if (fp_off == logic->unitsize)
+               return;
+
+       for (off = 0; off < logic->length; off += logic->unitsize) {
+               sample = logic->data + off;
+               sample[fp_off] &= fp_mask;
+               for (idx = fp_off + 1; idx < logic->unitsize; idx++)
+                       sample[idx] = 0x00;
+       }
+}
+
 static void send_analog_packet(struct analog_gen *ag,
                struct sr_dev_inst *sdi, uint64_t *analog_sent,
                uint64_t analog_pos, uint64_t analog_todo)
@@ -343,13 +393,16 @@ static void send_analog_packet(struct analog_gen *ag,
        int ag_pattern_pos;
        unsigned int i;
 
+       if (!ag->ch || !ag->ch->enabled)
+               return;
+
        devc = sdi->priv;
        packet.type = SR_DF_ANALOG;
        packet.payload = &ag->packet;
 
        if (!devc->avg) {
                ag_pattern_pos = analog_pos % ag->num_samples;
-               sending_now = MIN(analog_todo, ag->num_samples-ag_pattern_pos);
+               sending_now = MIN(analog_todo, ag->num_samples - ag_pattern_pos);
                ag->packet.data = ag->pattern_data + ag_pattern_pos;
                ag->packet.num_samples = sending_now;
                sr_session_send(sdi, &packet);
@@ -358,7 +411,7 @@ static void send_analog_packet(struct analog_gen *ag,
                *analog_sent = MAX(*analog_sent, sending_now);
        } else {
                ag_pattern_pos = analog_pos % ag->num_samples;
-               to_avg = MIN(analog_todo, ag->num_samples-ag_pattern_pos);
+               to_avg = MIN(analog_todo, ag->num_samples - ag_pattern_pos);
 
                for (i = 0; i < to_avg; i++) {
                        ag->avg_val = (ag->avg_val +
@@ -366,8 +419,7 @@ static void send_analog_packet(struct analog_gen *ag,
                                          ag_pattern_pos + i)) / 2;
                        ag->num_avgs++;
                        /* Time to send averaged data? */
-                       if (devc->avg_samples > 0 &&
-                           ag->num_avgs >= devc->avg_samples)
+                       if ((devc->avg_samples > 0) && (ag->num_avgs >= devc->avg_samples))
                                goto do_send;
                }
 
@@ -403,6 +455,8 @@ SR_PRIV int demo_prepare_data(int fd, int revents, void *cb_data)
        void *value;
        uint64_t samples_todo, logic_done, analog_done, analog_sent, sending_now;
        int64_t elapsed_us, limit_us, todo_us;
+       int64_t trigger_offset;
+       int pre_trigger_samples;
 
        (void)fd;
        (void)revents;
@@ -414,7 +468,7 @@ SR_PRIV int demo_prepare_data(int fd, int revents, void *cb_data)
        if (devc->cur_samplerate <= 0
                        || (devc->num_logic_channels <= 0
                        && devc->num_analog_channels <= 0)) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return G_SOURCE_CONTINUE;
        }
 
@@ -429,20 +483,38 @@ SR_PRIV int demo_prepare_data(int fd, int revents, void *cb_data)
        /* How many samples are outstanding since the last round? */
        samples_todo = (todo_us * devc->cur_samplerate + G_USEC_PER_SEC - 1)
                        / G_USEC_PER_SEC;
+
        if (devc->limit_samples > 0) {
                if (devc->limit_samples < devc->sent_samples)
                        samples_todo = 0;
                else if (devc->limit_samples - devc->sent_samples < samples_todo)
                        samples_todo = devc->limit_samples - devc->sent_samples;
        }
+
+       if (samples_todo == 0)
+               return G_SOURCE_CONTINUE;
+
+       if (devc->limit_frames) {
+               /* Never send more samples than a frame can fit... */
+               samples_todo = MIN(samples_todo, SAMPLES_PER_FRAME);
+               /* ...or than we need to finish the current frame. */
+               samples_todo = MIN(samples_todo,
+                       SAMPLES_PER_FRAME - devc->sent_frame_samples);
+       }
+
        /* Calculate the actual time covered by this run back from the sample
         * count, rounded towards zero. This avoids getting stuck on a too-low
         * time delta with no samples being sent due to round-off.
         */
        todo_us = samples_todo * G_USEC_PER_SEC / devc->cur_samplerate;
 
-       logic_done  = devc->num_logic_channels  > 0 ? 0 : samples_todo;
+       logic_done = devc->num_logic_channels > 0 ? 0 : samples_todo;
+       if (!devc->enabled_logic_channels)
+               logic_done = samples_todo;
+
        analog_done = devc->num_analog_channels > 0 ? 0 : samples_todo;
+       if (!devc->enabled_analog_channels)
+               analog_done = samples_todo;
 
        while (logic_done < samples_todo || analog_done < samples_todo) {
                /* Logic */
@@ -450,13 +522,47 @@ SR_PRIV int demo_prepare_data(int fd, int revents, void *cb_data)
                        sending_now = MIN(samples_todo - logic_done,
                                        LOGIC_BUFSIZE / devc->logic_unitsize);
                        logic_generator(sdi, sending_now * devc->logic_unitsize);
+                       /* Check for trigger and send pre-trigger data if needed */
+                       if (devc->stl && (!devc->trigger_fired)) {
+                               trigger_offset = soft_trigger_logic_check(devc->stl,
+                                               devc->logic_data, sending_now * devc->logic_unitsize,
+                                               &pre_trigger_samples);
+                               if (trigger_offset > -1) {
+                                       devc->trigger_fired = TRUE;
+                                       logic_done = pre_trigger_samples;
+                               }
+                       } else
+                               trigger_offset = 0;
+
+                       /* Send logic samples if needed */
                        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_done += sending_now;
+
+                       if (devc->stl) {
+                               if (devc->trigger_fired && (trigger_offset < (int)sending_now)) {
+                                       /* Send after-trigger data */
+                                       logic.length = (sending_now - trigger_offset) * devc->logic_unitsize;
+                                       logic.data = devc->logic_data + trigger_offset * devc->logic_unitsize;
+                                       logic_fixup_feed(devc, &logic);
+                                       sr_session_send(sdi, &packet);
+                                       logic_done += sending_now - trigger_offset;
+                                       /* End acquisition */
+                                       sr_dbg("Triggered, stopping acquisition.");
+                                       sr_dev_acquisition_stop(sdi);
+                                       break;
+                               } else {
+                                       /* Send nothing */
+                                       logic_done += sending_now;
+                               }
+                       } else if (!devc->stl) {
+                               /* No trigger defined, send logic samples */
+                               logic.length = sending_now * devc->logic_unitsize;
+                               logic.data = devc->logic_data;
+                               logic_fixup_feed(devc, &logic);
+                               sr_session_send(sdi, &packet);
+                               logic_done += sending_now;
+                       }
                }
 
                /* Analog, one channel at a time */
@@ -472,21 +578,27 @@ SR_PRIV int demo_prepare_data(int fd, int revents, void *cb_data)
                        analog_done += analog_sent;
                }
        }
-       /* At this point, both logic_done and analog_done should be
-        * exactly equal to samples_todo, or else.
-        */
-       if (logic_done != samples_todo || analog_done != samples_todo) {
-               sr_err("BUG: Sample count mismatch.");
-               return G_SOURCE_REMOVE;
-       }
-       devc->sent_samples += samples_todo;
+
+       uint64_t min = MIN(logic_done, analog_done);
+       devc->sent_samples += min;
+       devc->sent_frame_samples += min;
        devc->spent_us += todo_us;
 
+       if (devc->limit_frames && devc->sent_frame_samples >= SAMPLES_PER_FRAME) {
+               std_session_send_frame_end(sdi);
+               devc->sent_frame_samples = 0;
+               devc->limit_frames--;
+               if (!devc->limit_frames) {
+                       sr_dbg("Requested number of frames reached.");
+                       sr_dev_acquisition_stop(sdi);
+               }
+       }
+
        if ((devc->limit_samples > 0 && devc->sent_samples >= devc->limit_samples)
                        || (limit_us > 0 && devc->spent_us >= limit_us)) {
 
                /* If we're averaging everything - now is the time to send data */
-               if (devc->avg_samples == 0) {
+               if (devc->avg && devc->avg_samples == 0) {
                        g_hash_table_iter_init(&iter, devc->ch_ag);
                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
                                ag = value;
@@ -498,7 +610,10 @@ SR_PRIV int demo_prepare_data(int fd, int revents, void *cb_data)
                        }
                }
                sr_dbg("Requested number of samples reached.");
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
+       } else if (devc->limit_frames) {
+               if (devc->sent_frame_samples == 0)
+                       std_session_send_frame_begin(sdi);
        }
 
        return G_SOURCE_CONTINUE;
index 5d78c5e7ad3266ed89b3b5acc856de2f01865f88..82fb5d23553829b62604ffcf94e5d67fd602c529 100644 (file)
 #define LOGIC_BUFSIZE                  4096
 /* Size of the analog pattern space per channel. */
 #define ANALOG_BUFSIZE                 4096
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
-       uint64_t cur_samplerate;
-       uint64_t limit_samples;
-       uint64_t limit_msec;
-       uint64_t sent_samples;
-       int64_t start_us;
-       int64_t spent_us;
-       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;
-       GHashTable *ch_ag;
-       gboolean avg; /* True if averaging is enabled */
-       uint64_t avg_samples;
-};
+/* This is a development feature: it starts a new frame every n samples. */
+#define SAMPLES_PER_FRAME              1000UL
+#define DEFAULT_LIMIT_FRAMES           0
 
 /* Logic patterns we can generate. */
-enum {
+enum logic_pattern_type {
        /**
         * Spells "sigrok" across 8 channels using '0's (with '1's as
         * "background") when displayed using the 'bits' output format.
@@ -97,19 +78,51 @@ enum {
         * something that can get recognized.
         */
        PATTERN_SQUID,
+
+       /** Gray encoded data, like rotary encoder signals. */
+       PATTERN_GRAYCODE,
 };
 
 /* Analog patterns we can generate. */
-enum {
-       /**
-        * Square wave.
-        */
+enum analog_pattern_type {
        PATTERN_SQUARE,
        PATTERN_SINE,
        PATTERN_TRIANGLE,
        PATTERN_SAWTOOTH,
 };
 
+struct dev_context {
+       uint64_t cur_samplerate;
+       uint64_t limit_samples;
+       uint64_t limit_msec;
+       uint64_t limit_frames;
+       uint64_t sent_samples;
+       uint64_t sent_frame_samples; /* Number of samples that were sent for current frame. */
+       int64_t start_us;
+       int64_t spent_us;
+       uint64_t step;
+       /* Logic */
+       int32_t num_logic_channels;
+       size_t logic_unitsize;
+       uint64_t all_logic_channels_mask;
+       /* There is only ever one logic channel group, so its pattern goes here. */
+       enum logic_pattern_type logic_pattern;
+       uint8_t logic_data[LOGIC_BUFSIZE];
+       /* Analog */
+       int32_t num_analog_channels;
+       GHashTable *ch_ag;
+       gboolean avg; /* True if averaging is enabled */
+       uint64_t avg_samples;
+       size_t enabled_logic_channels;
+       size_t enabled_analog_channels;
+       size_t first_partial_logic_index;
+       uint8_t first_partial_logic_mask;
+       /* Triggers */
+       uint64_t capture_ratio;
+       gboolean trigger_fired;
+       struct soft_trigger_logic *stl;
+};
+
 static const char *analog_pattern_str[] = {
        "square",
        "sine",
@@ -118,7 +131,8 @@ static const char *analog_pattern_str[] = {
 };
 
 struct analog_gen {
-       int pattern;
+       struct sr_channel *ch;
+       enum analog_pattern_type pattern;
        float amplitude;
        float pattern_data[ANALOG_BUFSIZE];
        unsigned int num_samples;
@@ -127,7 +141,7 @@ struct analog_gen {
        struct sr_analog_meaning meaning;
        struct sr_analog_spec spec;
        float avg_val; /* Average value */
-       unsigned num_avgs; /* Number of samples averaged */
+       unsigned int num_avgs; /* Number of samples averaged */
 };
 
 SR_PRIV void demo_generate_analog_pattern(struct analog_gen *ag, uint64_t sample_rate);
diff --git a/src/hardware/dreamsourcelab-dslogic/api.c b/src/hardware/dreamsourcelab-dslogic/api.c
new file mode 100644 (file)
index 0000000..98f4acd
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ * 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 <config.h>
+#include <math.h>
+#include "protocol.h"
+
+static const struct dslogic_profile supported_device[] = {
+       /* DreamSourceLab DSLogic */
+       { 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL,
+               "dreamsourcelab-dslogic-fx2.fw",
+               0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024},
+       /* DreamSourceLab DSCope */
+       { 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL,
+               "dreamsourcelab-dscope-fx2.fw",
+               0, "DreamSourceLab", "DSCope", 256 * 1024 * 1024},
+       /* DreamSourceLab DSLogic Pro */
+       { 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL,
+               "dreamsourcelab-dslogic-pro-fx2.fw",
+               0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024},
+       /* DreamSourceLab DSLogic Plus */
+       { 0x2a0e, 0x0020, "DreamSourceLab", "DSLogic Plus", NULL,
+               "dreamsourcelab-dslogic-plus-fx2.fw",
+               0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024},
+       /* DreamSourceLab DSLogic Basic */
+       { 0x2a0e, 0x0021, "DreamSourceLab", "DSLogic Basic", NULL,
+               "dreamsourcelab-dslogic-basic-fx2.fw",
+               0, "DreamSourceLab", "DSLogic", 256 * 1024},
+
+       ALL_ZERO
+};
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS | SR_CONF_SET | SR_CONF_GET,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CONN | SR_CONF_GET,
+       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+       SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const int32_t trigger_matches[] = {
+       SR_TRIGGER_ZERO,
+       SR_TRIGGER_ONE,
+       SR_TRIGGER_RISING,
+       SR_TRIGGER_FALLING,
+       SR_TRIGGER_EDGE,
+};
+
+static const char *signal_edges[] = {
+       [DS_EDGE_RISING] = "rising",
+       [DS_EDGE_FALLING] = "falling",
+};
+
+static const double thresholds[][2] = {
+       { 0.7, 1.4 },
+       { 1.4, 3.6 },
+};
+
+static const uint64_t samplerates[] = {
+       SR_KHZ(10),
+       SR_KHZ(20),
+       SR_KHZ(50),
+       SR_KHZ(100),
+       SR_KHZ(200),
+       SR_KHZ(500),
+       SR_MHZ(1),
+       SR_MHZ(2),
+       SR_MHZ(5),
+       SR_MHZ(10),
+       SR_MHZ(20),
+       SR_MHZ(25),
+       SR_MHZ(50),
+       SR_MHZ(100),
+       SR_MHZ(200),
+       SR_MHZ(400),
+};
+
+static gboolean is_plausible(const struct libusb_device_descriptor *des)
+{
+       int i;
+
+       for (i = 0; supported_device[i].vid; i++) {
+               if (des->idVendor != supported_device[i].vid)
+                       continue;
+               if (des->idProduct == supported_device[i].pid)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static GSList *scan(struct sr_dev_driver *di, 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_channel_group *cg;
+       struct sr_config *src;
+       const struct dslogic_profile *prof;
+       GSList *l, *devices, *conn_devices;
+       gboolean has_firmware;
+       struct libusb_device_descriptor des;
+       libusb_device **devlist;
+       struct libusb_device_handle *hdl;
+       int ret, i, j;
+       const char *conn;
+       char manufacturer[64], product[64], serial_num[64], connection_id[64];
+       char channel_name[16];
+
+       drvc = di->context;
+
+       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 DSLogic 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;
+               }
+
+               libusb_get_device_descriptor(devlist[i], &des);
+
+               if (!is_plausible(&des))
+                       continue;
+
+               if ((ret = libusb_open(devlist[i], &hdl)) < 0) {
+                       sr_warn("Failed to open potential device with "
+                               "VID:PID %04x:%04x: %s.", des.idVendor,
+                               des.idProduct, libusb_error_name(ret));
+                       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;
+               }
+
+               if (des.iSerialNumber == 0) {
+                       serial_num[0] = '\0';
+               } else if ((ret = libusb_get_string_descriptor_ascii(hdl,
+                               des.iSerialNumber, (unsigned char *) serial_num,
+                               sizeof(serial_num))) < 0) {
+                       sr_warn("Failed to get serial number string descriptor: %s.",
+                               libusb_error_name(ret));
+                       continue;
+               }
+
+               libusb_close(hdl);
+
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
+
+               prof = NULL;
+               for (j = 0; supported_device[j].vid; j++) {
+                       if (des.idVendor == supported_device[j].vid &&
+                                       des.idProduct == supported_device[j].pid) {
+                               prof = &supported_device[j];
+                               break;
+                       }
+               }
+
+               if (!prof)
+                       continue;
+
+               sdi = g_malloc0(sizeof(struct sr_dev_inst));
+               sdi->status = SR_ST_INITIALIZING;
+               sdi->vendor = g_strdup(prof->vendor);
+               sdi->model = g_strdup(prof->model);
+               sdi->version = g_strdup(prof->model_version);
+               sdi->serial_num = g_strdup(serial_num);
+               sdi->connection_id = g_strdup(connection_id);
+
+               /* Logic channels, all in one channel group. */
+               cg = g_malloc0(sizeof(struct sr_channel_group));
+               cg->name = g_strdup("Logic");
+               for (j = 0; j < NUM_CHANNELS; j++) {
+                       sprintf(channel_name, "%d", j);
+                       ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC,
+                                               TRUE, channel_name);
+                       cg->channels = g_slist_append(cg->channels, ch);
+               }
+               sdi->channel_groups = g_slist_append(NULL, cg);
+
+               devc = dslogic_dev_new();
+               devc->profile = prof;
+               sdi->priv = devc;
+               devices = g_slist_append(devices, sdi);
+
+               devc->samplerates = samplerates;
+               devc->num_samplerates = ARRAY_SIZE(samplerates);
+               has_firmware = usb_match_manuf_prod(devlist[i], "DreamSourceLab", "USB-based Instrument");
+
+               if (has_firmware) {
+                       /* Already has the firmware, so fix the new address. */
+                       sr_dbg("Found a DSLogic 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(drvc->sr_ctx, 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.%d (logical), name %s.",
+                                      libusb_get_bus_number(devlist[i]),
+                                      libusb_get_device_address(devlist[i]),
+                                      prof->firmware);
+                       }
+                       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 std_scan_complete(di, devices);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       struct sr_dev_driver *di = sdi->driver;
+       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 = dslogic_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 = dslogic_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 ((ret = dslogic_fpga_firmware_upload(sdi)) != SR_OK)
+               return ret;
+
+       if (devc->cur_samplerate == 0) {
+               /* Samplerate hasn't been set; default to the slowest one. */
+               devc->cur_samplerate = devc->samplerates[0];
+       }
+
+       if (devc->cur_threshold == 0.0) {
+               devc->cur_threshold = thresholds[1][0];
+               return dslogic_set_voltage_threshold(sdi, devc->cur_threshold);
+       }
+
+       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_BUG;
+
+       sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
+               usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
+       libusb_release_interface(usb->devhdl, USB_INTERFACE);
+       libusb_close(usb->devhdl);
+       usb->devhdl = NULL;
+
+       return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       int idx;
+
+       (void)cg;
+
+       if (!sdi)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+
+       switch (key) {
+       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;
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
+               break;
+       case SR_CONF_VOLTAGE_THRESHOLD:
+               if (!strcmp(devc->profile->model, "DSLogic")) {
+                       if ((idx = std_double_tuple_idx_d0(devc->cur_threshold,
+                                       ARRAY_AND_SIZE(thresholds))) < 0)
+                               return SR_ERR_BUG;
+                       *data = std_gvar_tuple_double(thresholds[idx][0],
+                                       thresholds[idx][1]);
+               } else {
+                       *data = std_gvar_tuple_double(devc->cur_threshold, devc->cur_threshold);
+               }
+               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;
+       case SR_CONF_CAPTURE_RATIO:
+               *data = g_variant_new_uint64(devc->capture_ratio);
+               break;
+       case SR_CONF_EXTERNAL_CLOCK:
+               *data = g_variant_new_boolean(devc->external_clock);
+               break;
+       case SR_CONF_CONTINUOUS:
+               *data = g_variant_new_boolean(devc->continuous_mode);
+               break;
+       case SR_CONF_CLOCK_EDGE:
+               idx = devc->clock_edge;
+               if (idx >= (int)ARRAY_SIZE(signal_edges))
+                       return SR_ERR_BUG;
+               *data = g_variant_new_string(signal_edges[0]);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       int idx;
+       gdouble low, high;
+
+       (void)cg;
+
+       if (!sdi)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+
+       switch (key) {
+       case SR_CONF_SAMPLERATE:
+               if ((idx = std_u64_idx(data, devc->samplerates, devc->num_samplerates)) < 0)
+                       return SR_ERR_ARG;
+               devc->cur_samplerate = devc->samplerates[idx];
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               devc->limit_samples = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_CAPTURE_RATIO:
+               devc->capture_ratio = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_VOLTAGE_THRESHOLD:
+               if (!strcmp(devc->profile->model, "DSLogic")) {
+                       if ((idx = std_double_tuple_idx(data, ARRAY_AND_SIZE(thresholds))) < 0)
+                               return SR_ERR_ARG;
+                       devc->cur_threshold = thresholds[idx][0];
+                       return dslogic_fpga_firmware_upload(sdi);
+               } else {
+                       g_variant_get(data, "(dd)", &low, &high);
+                       return dslogic_set_voltage_threshold(sdi, (low + high) / 2.0);
+               }
+               break;
+       case SR_CONF_EXTERNAL_CLOCK:
+               devc->external_clock = g_variant_get_boolean(data);
+               break;
+       case SR_CONF_CONTINUOUS:
+               devc->continuous_mode = g_variant_get_boolean(data);
+               break;
+       case SR_CONF_CLOCK_EDGE:
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edges))) < 0)
+                       return SR_ERR_ARG;
+               devc->clock_edge = idx;
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+
+       devc = (sdi) ? sdi->priv : NULL;
+
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_VOLTAGE_THRESHOLD:
+               if (!devc || !devc->profile)
+                       return SR_ERR_ARG;
+               if (!strcmp(devc->profile->model, "DSLogic"))
+                       *data = std_gvar_thresholds(ARRAY_AND_SIZE(thresholds));
+               else
+                       *data = std_gvar_min_max_step_thresholds(0.0, 5.0, 0.1);
+               break;
+       case SR_CONF_SAMPLERATE:
+               if (!devc)
+                       return SR_ERR_ARG;
+               *data = std_gvar_samplerates(devc->samplerates, devc->num_samplerates);
+               break;
+       case SR_CONF_TRIGGER_MATCH:
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
+               break;
+       case SR_CONF_CLOCK_EDGE:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(signal_edges));
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static struct sr_dev_driver dreamsourcelab_dslogic_driver_info = {
+       .name = "dreamsourcelab-dslogic",
+       .longname = "DreamSourceLab DSLogic",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = std_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 = dslogic_acquisition_start,
+       .dev_acquisition_stop = dslogic_acquisition_stop,
+       .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(dreamsourcelab_dslogic_driver_info);
diff --git a/src/hardware/dreamsourcelab-dslogic/protocol.c b/src/hardware/dreamsourcelab-dslogic/protocol.c
new file mode 100644 (file)
index 0000000..584f3b1
--- /dev/null
@@ -0,0 +1,1095 @@
+/*
+ * 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 <config.h>
+#include <math.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "protocol.h"
+
+#define DS_CMD_GET_FW_VERSION          0xb0
+#define DS_CMD_GET_REVID_VERSION       0xb1
+#define DS_CMD_START                   0xb2
+#define DS_CMD_CONFIG                  0xb3
+#define DS_CMD_SETTING                 0xb4
+#define DS_CMD_CONTROL                 0xb5
+#define DS_CMD_STATUS                  0xb6
+#define DS_CMD_STATUS_INFO             0xb7
+#define DS_CMD_WR_REG                  0xb8
+#define DS_CMD_WR_NVM                  0xb9
+#define DS_CMD_RD_NVM                  0xba
+#define DS_CMD_RD_NVM_PRE              0xbb
+#define DS_CMD_GET_HW_INFO             0xbc
+
+#define DS_START_FLAGS_STOP            (1 << 7)
+#define DS_START_FLAGS_CLK_48MHZ       (1 << 6)
+#define DS_START_FLAGS_SAMPLE_WIDE     (1 << 5)
+#define DS_START_FLAGS_MODE_LA         (1 << 4)
+
+#define DS_ADDR_COMB                   0x68
+#define DS_ADDR_EEWP                   0x70
+#define DS_ADDR_VTH                    0x78
+
+#define DS_MAX_LOGIC_DEPTH             SR_MHZ(16)
+#define DS_MAX_LOGIC_SAMPLERATE                SR_MHZ(100)
+#define DS_MAX_TRIG_PERCENT            90
+
+#define DS_MODE_TRIG_EN                        (1 << 0)
+#define DS_MODE_CLK_TYPE               (1 << 1)
+#define DS_MODE_CLK_EDGE               (1 << 2)
+#define DS_MODE_RLE_MODE               (1 << 3)
+#define DS_MODE_DSO_MODE               (1 << 4)
+#define DS_MODE_HALF_MODE              (1 << 5)
+#define DS_MODE_QUAR_MODE              (1 << 6)
+#define DS_MODE_ANALOG_MODE            (1 << 7)
+#define DS_MODE_FILTER                 (1 << 8)
+#define DS_MODE_INSTANT                        (1 << 9)
+#define DS_MODE_STRIG_MODE             (1 << 11)
+#define DS_MODE_STREAM_MODE            (1 << 12)
+#define DS_MODE_LPB_TEST               (1 << 13)
+#define DS_MODE_EXT_TEST               (1 << 14)
+#define DS_MODE_INT_TEST               (1 << 15)
+
+#define DSLOGIC_ATOMIC_SAMPLES         (sizeof(uint64_t) * 8)
+#define DSLOGIC_ATOMIC_BYTES           sizeof(uint64_t)
+
+/*
+ * The FPGA is configured with TLV tuples. Length is specified as the
+ * number of 16-bit words.
+ */
+#define _DS_CFG(variable, wordcnt) ((variable << 8) | wordcnt)
+#define DS_CFG_START                   0xf5a5f5a5
+#define DS_CFG_MODE                    _DS_CFG(0, 1)
+#define DS_CFG_DIVIDER                 _DS_CFG(1, 2)
+#define DS_CFG_COUNT                   _DS_CFG(3, 2)
+#define DS_CFG_TRIG_POS                        _DS_CFG(5, 2)
+#define DS_CFG_TRIG_GLB                        _DS_CFG(7, 1)
+#define DS_CFG_CH_EN                   _DS_CFG(8, 1)
+#define DS_CFG_TRIG                    _DS_CFG(64, 160)
+#define DS_CFG_END                     0xfa5afa5a
+
+#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;
+};
+
+struct fpga_config {
+       uint32_t sync;
+
+       uint16_t mode_header;
+       uint16_t mode;
+       uint16_t divider_header;
+       uint32_t divider;
+       uint16_t count_header;
+       uint32_t count;
+       uint16_t trig_pos_header;
+       uint32_t trig_pos;
+       uint16_t trig_glb_header;
+       uint16_t trig_glb;
+       uint16_t ch_en_header;
+       uint16_t ch_en;
+
+       uint16_t trig_header;
+       uint16_t trig_mask0[NUM_TRIGGER_STAGES];
+       uint16_t trig_mask1[NUM_TRIGGER_STAGES];
+       uint16_t trig_value0[NUM_TRIGGER_STAGES];
+       uint16_t trig_value1[NUM_TRIGGER_STAGES];
+       uint16_t trig_edge0[NUM_TRIGGER_STAGES];
+       uint16_t trig_edge1[NUM_TRIGGER_STAGES];
+       uint16_t trig_logic0[NUM_TRIGGER_STAGES];
+       uint16_t trig_logic1[NUM_TRIGGER_STAGES];
+       uint32_t trig_count[NUM_TRIGGER_STAGES];
+
+       uint32_t end_sync;
+};
+
+#pragma pack(pop)
+
+/*
+ * This should be larger than the FPGA bitstream image so that it'll get
+ * uploaded in one big operation. There seem to be issues when uploading
+ * it in chunks.
+ */
+#define FW_BUFSIZE (1024 * 1024)
+
+#define FPGA_UPLOAD_DELAY (10 * 1000)
+
+#define USB_TIMEOUT (3 * 1000)
+
+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, DS_CMD_GET_FW_VERSION, 0x0000, 0x0000,
+               (unsigned char *)vi, sizeof(struct version_info), USB_TIMEOUT);
+
+       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, DS_CMD_GET_REVID_VERSION, 0x0000, 0x0000,
+               revid, 1, USB_TIMEOUT);
+
+       if (ret < 0) {
+               sr_err("Unable to get REVID: %s.", libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int command_start_acquisition(const struct sr_dev_inst *sdi)
+{
+       struct sr_usb_dev_inst *usb;
+       struct dslogic_mode mode;
+       int ret;
+
+       mode.flags = DS_START_FLAGS_MODE_LA | DS_START_FLAGS_SAMPLE_WIDE;
+       mode.sample_delay_h = mode.sample_delay_l = 0;
+
+       usb = sdi->conn;
+       ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+                       LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000,
+                       (unsigned char *)&mode, sizeof(mode), USB_TIMEOUT);
+       if (ret < 0) {
+               sr_err("Failed to send start command: %s.", libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int command_stop_acquisition(const struct sr_dev_inst *sdi)
+{
+       struct sr_usb_dev_inst *usb;
+       struct dslogic_mode mode;
+       int ret;
+
+       mode.flags = DS_START_FLAGS_STOP;
+       mode.sample_delay_h = mode.sample_delay_l = 0;
+
+       usb = sdi->conn;
+       ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+                       LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000,
+                       (unsigned char *)&mode, sizeof(struct dslogic_mode), USB_TIMEOUT);
+       if (ret < 0) {
+               sr_err("Failed to send stop command: %s.", libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi)
+{
+       const char *name = NULL;
+       uint64_t sum;
+       struct sr_resource bitstream;
+       struct drv_context *drvc;
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       unsigned char *buf;
+       ssize_t chunksize;
+       int transferred;
+       int result, ret;
+       const uint8_t cmd[3] = {0, 0, 0};
+
+       drvc = sdi->driver->context;
+       devc = sdi->priv;
+       usb = sdi->conn;
+
+       if (!strcmp(devc->profile->model, "DSLogic")) {
+               if (devc->cur_threshold < 1.40)
+                       name = DSLOGIC_FPGA_FIRMWARE_3V3;
+               else
+                       name = DSLOGIC_FPGA_FIRMWARE_5V;
+       } else if (!strcmp(devc->profile->model, "DSLogic Pro")){
+               name = DSLOGIC_PRO_FPGA_FIRMWARE;
+       } else if (!strcmp(devc->profile->model, "DSLogic Plus")){
+               name = DSLOGIC_PLUS_FPGA_FIRMWARE;
+       } else if (!strcmp(devc->profile->model, "DSLogic Basic")){
+               name = DSLOGIC_BASIC_FPGA_FIRMWARE;
+       } else if (!strcmp(devc->profile->model, "DSCope")) {
+               name = DSCOPE_FPGA_FIRMWARE;
+       } else {
+               sr_err("Failed to select FPGA firmware.");
+               return SR_ERR;
+       }
+
+       sr_dbg("Uploading FPGA firmware '%s'.", name);
+
+       result = sr_resource_open(drvc->sr_ctx, &bitstream,
+                       SR_RESOURCE_FIRMWARE, name);
+       if (result != SR_OK)
+               return result;
+
+       /* Tell the device firmware is coming. */
+       if ((ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+                       LIBUSB_ENDPOINT_OUT, DS_CMD_CONFIG, 0x0000, 0x0000,
+                       (unsigned char *)&cmd, sizeof(cmd), USB_TIMEOUT)) < 0) {
+               sr_err("Failed to upload FPGA firmware: %s.", libusb_error_name(ret));
+               sr_resource_close(drvc->sr_ctx, &bitstream);
+               return SR_ERR;
+       }
+
+       /* Give the FX2 time to get ready for FPGA firmware upload. */
+       g_usleep(FPGA_UPLOAD_DELAY);
+
+       buf = g_malloc(FW_BUFSIZE);
+       sum = 0;
+       result = SR_OK;
+       while (1) {
+               chunksize = sr_resource_read(drvc->sr_ctx, &bitstream,
+                               buf, FW_BUFSIZE);
+               if (chunksize < 0)
+                       result = SR_ERR;
+               if (chunksize <= 0)
+                       break;
+
+               if ((ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT,
+                               buf, chunksize, &transferred, USB_TIMEOUT)) < 0) {
+                       sr_err("Unable to configure FPGA firmware: %s.",
+                                       libusb_error_name(ret));
+                       result = SR_ERR;
+                       break;
+               }
+               sum += transferred;
+               sr_spew("Uploaded %" PRIu64 "/%" PRIu64 " bytes.",
+                       sum, bitstream.size);
+
+               if (transferred != chunksize) {
+                       sr_err("Short transfer while uploading FPGA firmware.");
+                       result = SR_ERR;
+                       break;
+               }
+       }
+       g_free(buf);
+       sr_resource_close(drvc->sr_ctx, &bitstream);
+
+       if (result == SR_OK)
+               sr_dbg("FPGA firmware upload done.");
+
+       return result;
+}
+
+static unsigned int enabled_channel_count(const struct sr_dev_inst *sdi)
+{
+       unsigned int count = 0;
+       for (const GSList *l = sdi->channels; l; l = l->next) {
+               const struct sr_channel *const probe = (struct sr_channel *)l->data;
+               if (probe->enabled)
+                       count++;
+       }
+       return count;
+}
+
+static uint16_t enabled_channel_mask(const struct sr_dev_inst *sdi)
+{
+       unsigned int mask = 0;
+       for (const GSList *l = sdi->channels; l; l = l->next) {
+               const struct sr_channel *const probe = (struct sr_channel *)l->data;
+               if (probe->enabled)
+                       mask |= 1 << probe->index;
+       }
+       return mask;
+}
+
+/*
+ * Get the session trigger and configure the FPGA structure
+ * accordingly.
+ * @return @c true if any triggers are enabled, @c false otherwise.
+ */
+static bool set_trigger(const struct sr_dev_inst *sdi, struct fpga_config *cfg)
+{
+       struct sr_trigger *trigger;
+       struct sr_trigger_stage *stage;
+       struct sr_trigger_match *match;
+       struct dev_context *devc;
+       const GSList *l, *m;
+       const unsigned int num_enabled_channels = enabled_channel_count(sdi);
+       int num_trigger_stages = 0;
+
+       int channelbit, i = 0;
+       uint32_t trigger_point;
+
+       devc = sdi->priv;
+
+       cfg->ch_en = enabled_channel_mask(sdi);
+
+       for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
+               cfg->trig_mask0[i] = 0xffff;
+               cfg->trig_mask1[i] = 0xffff;
+               cfg->trig_value0[i] = 0;
+               cfg->trig_value1[i] = 0;
+               cfg->trig_edge0[i] = 0;
+               cfg->trig_edge1[i] = 0;
+               cfg->trig_logic0[i] = 2;
+               cfg->trig_logic1[i] = 2;
+               cfg->trig_count[i] = 0;
+       }
+
+       trigger_point = (devc->capture_ratio * devc->limit_samples) / 100;
+       if (trigger_point < DSLOGIC_ATOMIC_SAMPLES)
+               trigger_point = DSLOGIC_ATOMIC_SAMPLES;
+       const uint32_t mem_depth = devc->profile->mem_depth;
+       const uint32_t max_trigger_point = devc->continuous_mode ? ((mem_depth * 10) / 100) :
+               ((mem_depth * DS_MAX_TRIG_PERCENT) / 100);
+       if (trigger_point > max_trigger_point)
+               trigger_point = max_trigger_point;
+       cfg->trig_pos = trigger_point & ~(DSLOGIC_ATOMIC_SAMPLES - 1);
+
+       if (!(trigger = sr_session_trigger_get(sdi->session))) {
+               sr_dbg("No session trigger found");
+               return false;
+       }
+
+       for (l = trigger->stages; l; l = l->next) {
+               stage = l->data;
+               num_trigger_stages++;
+               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);
+                       /* Simple trigger support (event). */
+                       if (match->match == SR_TRIGGER_ONE) {
+                               cfg->trig_mask0[0] &= ~channelbit;
+                               cfg->trig_mask1[0] &= ~channelbit;
+                               cfg->trig_value0[0] |= channelbit;
+                               cfg->trig_value1[0] |= channelbit;
+                       } else if (match->match == SR_TRIGGER_ZERO) {
+                               cfg->trig_mask0[0] &= ~channelbit;
+                               cfg->trig_mask1[0] &= ~channelbit;
+                       } else if (match->match == SR_TRIGGER_FALLING) {
+                               cfg->trig_mask0[0] &= ~channelbit;
+                               cfg->trig_mask1[0] &= ~channelbit;
+                               cfg->trig_edge0[0] |= channelbit;
+                               cfg->trig_edge1[0] |= channelbit;
+                       } else if (match->match == SR_TRIGGER_RISING) {
+                               cfg->trig_mask0[0] &= ~channelbit;
+                               cfg->trig_mask1[0] &= ~channelbit;
+                               cfg->trig_value0[0] |= channelbit;
+                               cfg->trig_value1[0] |= channelbit;
+                               cfg->trig_edge0[0] |= channelbit;
+                               cfg->trig_edge1[0] |= channelbit;
+                       } else if (match->match == SR_TRIGGER_EDGE) {
+                               cfg->trig_edge0[0] |= channelbit;
+                               cfg->trig_edge1[0] |= channelbit;
+                       }
+               }
+       }
+
+       cfg->trig_glb = (num_enabled_channels << 4) | (num_trigger_stages - 1);
+
+       return num_trigger_stages != 0;
+}
+
+static int fpga_configure(const struct sr_dev_inst *sdi)
+{
+       const struct dev_context *const devc = sdi->priv;
+       const struct sr_usb_dev_inst *const usb = sdi->conn;
+       uint8_t c[3];
+       struct fpga_config cfg;
+       uint16_t mode = 0;
+       uint32_t divider;
+       int transferred, len, ret;
+
+       sr_dbg("Configuring FPGA.");
+
+       WL32(&cfg.sync, DS_CFG_START);
+       WL16(&cfg.mode_header, DS_CFG_MODE);
+       WL16(&cfg.divider_header, DS_CFG_DIVIDER);
+       WL16(&cfg.count_header, DS_CFG_COUNT);
+       WL16(&cfg.trig_pos_header, DS_CFG_TRIG_POS);
+       WL16(&cfg.trig_glb_header, DS_CFG_TRIG_GLB);
+       WL16(&cfg.ch_en_header, DS_CFG_CH_EN);
+       WL16(&cfg.trig_header, DS_CFG_TRIG);
+       WL32(&cfg.end_sync, DS_CFG_END);
+
+       /* Pass in the length of a fixed-size struct. Really. */
+       len = sizeof(struct fpga_config) / 2;
+       c[0] = len & 0xff;
+       c[1] = (len >> 8) & 0xff;
+       c[2] = (len >> 16) & 0xff;
+
+       ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+                       LIBUSB_ENDPOINT_OUT, DS_CMD_SETTING, 0x0000, 0x0000,
+                       c, sizeof(c), USB_TIMEOUT);
+       if (ret < 0) {
+               sr_err("Failed to send FPGA configure command: %s.",
+                       libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       if (set_trigger(sdi, &cfg))
+               mode |= DS_MODE_TRIG_EN;
+
+       if (devc->mode == DS_OP_INTERNAL_TEST)
+               mode |= DS_MODE_INT_TEST;
+       else if (devc->mode == DS_OP_EXTERNAL_TEST)
+               mode |= DS_MODE_EXT_TEST;
+       else if (devc->mode == DS_OP_LOOPBACK_TEST)
+               mode |= DS_MODE_LPB_TEST;
+
+       if (devc->cur_samplerate == DS_MAX_LOGIC_SAMPLERATE * 2)
+               mode |= DS_MODE_HALF_MODE;
+       else if (devc->cur_samplerate == DS_MAX_LOGIC_SAMPLERATE * 4)
+               mode |= DS_MODE_QUAR_MODE;
+
+       if (devc->continuous_mode)
+               mode |= DS_MODE_STREAM_MODE;
+       if (devc->external_clock) {
+               mode |= DS_MODE_CLK_TYPE;
+               if (devc->clock_edge == DS_EDGE_FALLING)
+                       mode |= DS_MODE_CLK_EDGE;
+       }
+       if (devc->limit_samples > DS_MAX_LOGIC_DEPTH *
+               ceil(devc->cur_samplerate * 1.0 / DS_MAX_LOGIC_SAMPLERATE)
+               && !devc->continuous_mode) {
+               /* Enable RLE for long captures.
+                * Without this, captured data present errors.
+                */
+               mode |= DS_MODE_RLE_MODE;
+       }
+
+       WL16(&cfg.mode, mode);
+       divider = ceil(DS_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate);
+       WL32(&cfg.divider, divider);
+
+       /* Number of 16-sample units. */
+       WL32(&cfg.count, devc->limit_samples / 16);
+
+       len = sizeof(struct fpga_config);
+       ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT,
+                       (unsigned char *)&cfg, len, &transferred, USB_TIMEOUT);
+       if (ret < 0 || transferred != len) {
+               sr_err("Failed to send FPGA configuration: %s.", libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int dslogic_set_voltage_threshold(const struct sr_dev_inst *sdi, double threshold)
+{
+       int ret;
+       struct dev_context *const devc = sdi->priv;
+       const struct sr_usb_dev_inst *const usb = sdi->conn;
+       const uint8_t value = (threshold / 5.0) * 255;
+       const uint16_t cmd = value | (DS_ADDR_VTH << 8);
+
+       /* Send the control command. */
+       ret = libusb_control_transfer(usb->devhdl,
+                       LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT,
+                       DS_CMD_WR_REG, 0x0000, 0x0000,
+                       (unsigned char *)&cmd, sizeof(cmd), 3000);
+       if (ret < 0) {
+               sr_err("Unable to set voltage-threshold register: %s.",
+               libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       devc->cur_threshold = threshold;
+
+       return SR_OK;
+}
+
+SR_PRIV int dslogic_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 = SR_ERR, i, device_count;
+       uint8_t revid;
+       char connection_id[64];
+
+       drvc = di->context;
+       devc = sdi->priv;
+       usb = sdi->conn;
+
+       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++) {
+               libusb_get_device_descriptor(devlist[i], &des);
+
+               if (des.idVendor != devc->profile->vid
+                   || des.idProduct != devc->profile->pid)
+                       continue;
+
+               if ((sdi->status == SR_ST_INITIALIZING) ||
+                               (sdi->status == SR_ST_INACTIVE)) {
+                       /* Check device by its physical USB bus/port address. */
+                       if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                               continue;
+
+                       if (strcmp(sdi->connection_id, connection_id))
+                               /* 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));
+                       ret = SR_ERR;
+                       break;
+               }
+
+               if (libusb_has_capability(LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) {
+                       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));
+                                       ret = SR_ERR;
+                                       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 != DSLOGIC_REQUIRED_VERSION_MAJOR) {
+                       sr_err("Expected firmware version %d.x, "
+                              "got %d.%d.", DSLOGIC_REQUIRED_VERSION_MAJOR,
+                              vi.major, vi.minor);
+                       ret = SR_ERR;
+                       break;
+               }
+
+               sr_info("Opened device on %d.%d (logical) / %s (physical), "
+                       "interface %d, firmware %d.%d.",
+                       usb->bus, usb->address, connection_id,
+                       USB_INTERFACE, vi.major, vi.minor);
+
+               sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.",
+                       revid, (revid != 1) ? " (FX2)" : "A (FX2LP)");
+
+               ret = SR_OK;
+
+               break;
+       }
+
+       libusb_free_device_list(devlist, 1);
+
+       return ret;
+}
+
+SR_PRIV struct dev_context *dslogic_dev_new(void)
+{
+       struct dev_context *devc;
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       devc->profile = NULL;
+       devc->fw_updated = 0;
+       devc->cur_samplerate = 0;
+       devc->limit_samples = 0;
+       devc->capture_ratio = 0;
+       devc->continuous_mode = FALSE;
+       devc->clock_edge = DS_EDGE_RISING;
+
+       return devc;
+}
+
+static void 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 dev_context *devc;
+
+       devc = sdi->priv;
+
+       std_session_send_df_end(sdi);
+
+       usb_source_remove(sdi->session, devc->ctx);
+
+       devc->num_transfers = 0;
+       g_free(devc->transfers);
+       g_free(devc->deinterleave_buffer);
+}
+
+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);
+
+}
+
+static void deinterleave_buffer(const uint8_t *src, size_t length,
+       uint16_t *dst_ptr, size_t channel_count, uint16_t channel_mask)
+{
+       uint16_t sample;
+
+       for (const uint64_t *src_ptr = (uint64_t*)src;
+               src_ptr < (uint64_t*)(src + length);
+               src_ptr += channel_count) {
+               for (int bit = 0; bit != 64; bit++) {
+                       const uint64_t *word_ptr = src_ptr;
+                       sample = 0;
+                       for (unsigned int channel = 0; channel != 16;
+                               channel++) {
+                               const uint16_t m = channel_mask >> channel;
+                               if (!m)
+                                       break;
+                               if ((m & 1) && ((*word_ptr++ >> bit) & UINT64_C(1)))
+                                       sample |= 1 << channel;
+                       }
+                       *dst_ptr++ = sample;
+               }
+       }
+}
+
+static void send_data(struct sr_dev_inst *sdi,
+       uint16_t *data, size_t sample_count)
+{
+       const struct sr_datafeed_logic logic = {
+               .length = sample_count * sizeof(uint16_t),
+               .unitsize = sizeof(uint16_t),
+               .data = data
+       };
+
+       const struct sr_datafeed_packet packet = {
+               .type = SR_DF_LOGIC,
+               .payload = &logic
+       };
+
+       sr_session_send(sdi, &packet);
+}
+
+static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
+{
+       struct sr_dev_inst *const sdi = transfer->user_data;
+       struct dev_context *const devc = sdi->priv;
+       const size_t channel_count = enabled_channel_count(sdi);
+       const uint16_t channel_mask = enabled_channel_mask(sdi);
+       const unsigned int cur_sample_count = DSLOGIC_ATOMIC_SAMPLES *
+               transfer->actual_length /
+               (DSLOGIC_ATOMIC_BYTES * channel_count);
+
+       gboolean packet_has_error = FALSE;
+       struct sr_datafeed_packet packet;
+       unsigned int num_samples;
+       int trigger_offset;
+
+       /*
+        * If acquisition has already ended, just free any queued up
+        * transfer that come in.
+        */
+       if (devc->acq_aborted) {
+               free_transfer(transfer);
+               return;
+       }
+
+       sr_dbg("receive_transfer(): status %s received %d bytes.",
+               libusb_error_name(transfer->status), transfer->actual_length);
+
+       /* Save incoming transfer before reusing the transfer struct. */
+
+       switch (transfer->status) {
+       case LIBUSB_TRANSFER_NO_DEVICE:
+               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.
+                        */
+                       abort_acquisition(devc);
+                       free_transfer(transfer);
+               } else {
+                       resubmit_transfer(transfer);
+               }
+               return;
+       } else {
+               devc->empty_transfer_count = 0;
+       }
+
+       if (!devc->limit_samples || devc->sent_samples < devc->limit_samples) {
+               if (devc->limit_samples && devc->sent_samples + cur_sample_count > devc->limit_samples)
+                       num_samples = devc->limit_samples - devc->sent_samples;
+               else
+                       num_samples = cur_sample_count;
+
+               /**
+                * The DSLogic emits sample data as sequences of 64-bit sample words
+                * in a round-robin i.e. 64-bits from channel 0, 64-bits from channel 1
+                * etc. for each of the enabled channels, then looping back to the
+                * channel.
+                *
+                * Because sigrok's internal representation is bit-interleaved channels
+                * we must recast the data.
+                *
+                * Hopefully in future it will be possible to pass the data on as-is.
+                */
+               if (transfer->actual_length % (DSLOGIC_ATOMIC_BYTES * channel_count) != 0)
+                       sr_err("Invalid transfer length!");
+               deinterleave_buffer(transfer->buffer, transfer->actual_length,
+                       devc->deinterleave_buffer, channel_count, channel_mask);
+
+               /* Send the incoming transfer to the session bus. */
+               if (devc->trigger_pos > devc->sent_samples
+                       && devc->trigger_pos <= devc->sent_samples + num_samples) {
+                       /* DSLogic trigger in this block. Send trigger position. */
+                       trigger_offset = devc->trigger_pos - devc->sent_samples;
+                       /* Pre-trigger samples. */
+                       send_data(sdi, devc->deinterleave_buffer, trigger_offset);
+                       devc->sent_samples += trigger_offset;
+                       /* Trigger position. */
+                       devc->trigger_pos = 0;
+                       packet.type = SR_DF_TRIGGER;
+                       packet.payload = NULL;
+                       sr_session_send(sdi, &packet);
+                       /* Post trigger samples. */
+                       num_samples -= trigger_offset;
+                       send_data(sdi, devc->deinterleave_buffer
+                               + trigger_offset, num_samples);
+                       devc->sent_samples += num_samples;
+               } else {
+                       send_data(sdi, devc->deinterleave_buffer, num_samples);
+                       devc->sent_samples += num_samples;
+               }
+       }
+
+       if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) {
+               abort_acquisition(devc);
+               free_transfer(transfer);
+       } else
+               resubmit_transfer(transfer);
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+       struct timeval tv;
+       struct drv_context *drvc;
+
+       (void)fd;
+       (void)revents;
+
+       drvc = (struct drv_context *)cb_data;
+
+       tv.tv_sec = tv.tv_usec = 0;
+       libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+       return TRUE;
+}
+
+static size_t to_bytes_per_ms(const struct sr_dev_inst *sdi)
+{
+       const struct dev_context *const devc = sdi->priv;
+       const size_t ch_count = enabled_channel_count(sdi);
+
+       if (devc->continuous_mode)
+               return (devc->cur_samplerate * ch_count) / (1000 * 8);
+
+
+       /* If we're in buffered mode, the transfer rate is not so important,
+        * but we expect to get at least 10% of the high-speed USB bandwidth.
+        */
+       return 35000000 / (1000 * 10);
+}
+
+static size_t get_buffer_size(const struct sr_dev_inst *sdi)
+{
+       /*
+        * The buffer should be large enough to hold 10ms of data and
+        * a multiple of the size of a data atom.
+        */
+       const size_t block_size = enabled_channel_count(sdi) * 512;
+       const size_t s = 10 * to_bytes_per_ms(sdi);
+       if (!block_size)
+               return s;
+       return ((s + block_size - 1) / block_size) * block_size;
+}
+
+static unsigned int get_number_of_transfers(const struct sr_dev_inst *sdi)
+{
+       /* Total buffer size should be able to hold about 100ms of data. */
+       const unsigned int s = get_buffer_size(sdi);
+       const unsigned int n = (100 * to_bytes_per_ms(sdi) + s - 1) / s;
+       return (n > NUM_SIMUL_TRANSFERS) ? NUM_SIMUL_TRANSFERS : n;
+}
+
+static unsigned int get_timeout(const struct sr_dev_inst *sdi)
+{
+       const size_t total_size = get_buffer_size(sdi) *
+               get_number_of_transfers(sdi);
+       const unsigned int timeout = total_size / to_bytes_per_ms(sdi);
+       return timeout + timeout / 4; /* Leave a headroom of 25% percent. */
+}
+
+static int start_transfers(const struct sr_dev_inst *sdi)
+{
+       const size_t channel_count = enabled_channel_count(sdi);
+       const size_t size = get_buffer_size(sdi);
+       const unsigned int num_transfers = get_number_of_transfers(sdi);
+       const unsigned int timeout = get_timeout(sdi);
+
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       struct libusb_transfer *transfer;
+       unsigned int i;
+       int ret;
+       unsigned char *buf;
+
+       devc = sdi->priv;
+       usb = sdi->conn;
+
+       devc->sent_samples = 0;
+       devc->acq_aborted = FALSE;
+       devc->empty_transfer_count = 0;
+       devc->submitted_transfers = 0;
+
+       g_free(devc->transfers);
+       devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
+       if (!devc->transfers) {
+               sr_err("USB transfers malloc failed.");
+               return SR_ERR_MALLOC;
+       }
+
+       devc->deinterleave_buffer = g_try_malloc(DSLOGIC_ATOMIC_SAMPLES *
+               (size / (channel_count * DSLOGIC_ATOMIC_BYTES)) * sizeof(uint16_t));
+       if (!devc->deinterleave_buffer) {
+               sr_err("Deinterleave buffer malloc failed.");
+               g_free(devc->deinterleave_buffer);
+               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,
+                               6 | LIBUSB_ENDPOINT_IN, buf, size,
+                               receive_transfer, (void *)sdi, timeout);
+               sr_info("submitting transfer: %d", i);
+               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++;
+       }
+
+       std_session_send_df_header(sdi);
+
+       return SR_OK;
+}
+
+static void LIBUSB_CALL trigger_receive(struct libusb_transfer *transfer)
+{
+       const struct sr_dev_inst *sdi;
+       struct dslogic_trigger_pos *tpos;
+       struct dev_context *devc;
+
+       sdi = transfer->user_data;
+       devc = sdi->priv;
+       if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
+               sr_dbg("Trigger transfer canceled.");
+               /* Terminate session. */
+               std_session_send_df_end(sdi);
+               usb_source_remove(sdi->session, devc->ctx);
+               devc->num_transfers = 0;
+               g_free(devc->transfers);
+       } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED
+                       && transfer->actual_length == sizeof(struct dslogic_trigger_pos)) {
+               tpos = (struct dslogic_trigger_pos *)transfer->buffer;
+               sr_info("tpos real_pos %d ram_saddr %d cnt_h %d cnt_l %d", tpos->real_pos,
+                       tpos->ram_saddr, tpos->remain_cnt_h, tpos->remain_cnt_l);
+               devc->trigger_pos = tpos->real_pos;
+               g_free(tpos);
+               start_transfers(sdi);
+       }
+       libusb_free_transfer(transfer);
+}
+
+SR_PRIV int dslogic_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       const unsigned int timeout = get_timeout(sdi);
+
+       struct sr_dev_driver *di;
+       struct drv_context *drvc;
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       struct dslogic_trigger_pos *tpos;
+       struct libusb_transfer *transfer;
+       int ret;
+
+       di = sdi->driver;
+       drvc = di->context;
+       devc = sdi->priv;
+       usb = sdi->conn;
+
+       devc->ctx = drvc->sr_ctx;
+       devc->sent_samples = 0;
+       devc->empty_transfer_count = 0;
+       devc->acq_aborted = FALSE;
+
+       usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc);
+
+       if ((ret = command_stop_acquisition(sdi)) != SR_OK)
+               return ret;
+
+       if ((ret = fpga_configure(sdi)) != SR_OK)
+               return ret;
+
+       if ((ret = command_start_acquisition(sdi)) != SR_OK)
+               return ret;
+
+       sr_dbg("Getting trigger.");
+       tpos = g_malloc(sizeof(struct dslogic_trigger_pos));
+       transfer = libusb_alloc_transfer(0);
+       libusb_fill_bulk_transfer(transfer, usb->devhdl, 6 | LIBUSB_ENDPOINT_IN,
+                       (unsigned char *)tpos, sizeof(struct dslogic_trigger_pos),
+                       trigger_receive, (void *)sdi, 0);
+       if ((ret = libusb_submit_transfer(transfer)) < 0) {
+               sr_err("Failed to request trigger: %s.", libusb_error_name(ret));
+               libusb_free_transfer(transfer);
+               g_free(tpos);
+               return SR_ERR;
+       }
+
+       devc->transfers = g_try_malloc0(sizeof(*devc->transfers));
+       if (!devc->transfers) {
+               sr_err("USB trigger_pos transfer malloc failed.");
+               return SR_ERR_MALLOC;
+       }
+       devc->num_transfers = 1;
+       devc->submitted_transfers++;
+       devc->transfers[0] = transfer;
+
+       return ret;
+}
+
+SR_PRIV int dslogic_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       command_stop_acquisition(sdi);
+       abort_acquisition(sdi->priv);
+       return SR_OK;
+}
diff --git a/src/hardware/dreamsourcelab-dslogic/protocol.h b/src/hardware/dreamsourcelab-dslogic/protocol.h
new file mode 100644 (file)
index 0000000..6af7c82
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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_DREAMSOURCELAB_DSLOGIC_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_DREAMSOURCELAB_DSLOGIC_PROTOCOL_H
+
+#include <glib.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libusb.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "dreamsourcelab-dslogic"
+
+#define USB_INTERFACE          0
+#define USB_CONFIGURATION      1
+
+#define MAX_RENUM_DELAY_MS     3000
+#define NUM_SIMUL_TRANSFERS    32
+#define MAX_EMPTY_TRANSFERS    (NUM_SIMUL_TRANSFERS * 2)
+
+#define NUM_CHANNELS           16
+#define NUM_TRIGGER_STAGES     16
+
+#define DSLOGIC_REQUIRED_VERSION_MAJOR 1
+
+/* 6 delay states of up to 256 clock ticks */
+#define MAX_SAMPLE_DELAY       (6 * 256)
+
+#define DSLOGIC_FPGA_FIRMWARE_5V "dreamsourcelab-dslogic-fpga-5v.fw"
+#define DSLOGIC_FPGA_FIRMWARE_3V3 "dreamsourcelab-dslogic-fpga-3v3.fw"
+#define DSCOPE_FPGA_FIRMWARE "dreamsourcelab-dscope-fpga.fw"
+#define DSLOGIC_PRO_FPGA_FIRMWARE "dreamsourcelab-dslogic-pro-fpga.fw"
+#define DSLOGIC_PLUS_FPGA_FIRMWARE "dreamsourcelab-dslogic-plus-fpga.fw"
+#define DSLOGIC_BASIC_FPGA_FIRMWARE "dreamsourcelab-dslogic-basic-fpga.fw"
+
+enum dslogic_operation_modes {
+       DS_OP_NORMAL,
+       DS_OP_INTERNAL_TEST,
+       DS_OP_EXTERNAL_TEST,
+       DS_OP_LOOPBACK_TEST,
+};
+
+enum dslogic_edge_modes {
+       DS_EDGE_RISING,
+       DS_EDGE_FALLING,
+};
+
+struct dslogic_version {
+       uint8_t major;
+       uint8_t minor;
+};
+
+struct dslogic_mode {
+       uint8_t flags;
+       uint8_t sample_delay_h;
+       uint8_t sample_delay_l;
+};
+
+struct dslogic_trigger_pos {
+       uint32_t check_id;
+       uint32_t real_pos;
+       uint32_t ram_saddr;
+       uint32_t remain_cnt_l;
+       uint32_t remain_cnt_h;
+       uint32_t status;
+       uint8_t first_block[488];
+};
+
+struct dslogic_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;
+
+       /* Memory depth in bits. */
+       uint64_t mem_depth;
+};
+
+struct dev_context {
+       const struct dslogic_profile *profile;
+       /*
+        * Since we can't keep track of a DSLogic 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;
+
+       const uint64_t *samplerates;
+       int num_samplerates;
+
+       uint64_t cur_samplerate;
+       uint64_t limit_samples;
+       uint64_t capture_ratio;
+
+       gboolean acq_aborted;
+
+       unsigned int sent_samples;
+       int submitted_transfers;
+       int empty_transfer_count;
+
+       unsigned int num_transfers;
+       struct libusb_transfer **transfers;
+       struct sr_context *ctx;
+
+       uint16_t *deinterleave_buffer;
+
+       uint16_t mode;
+       uint32_t trigger_pos;
+       gboolean external_clock;
+       gboolean continuous_mode;
+       int clock_edge;
+       double cur_threshold;
+};
+
+SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi);
+SR_PRIV int dslogic_set_voltage_threshold(const struct sr_dev_inst *sdi, double threshold);
+SR_PRIV int dslogic_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di);
+SR_PRIV struct dev_context *dslogic_dev_new(void);
+SR_PRIV int dslogic_acquisition_start(const struct sr_dev_inst *sdi);
+SR_PRIV int dslogic_acquisition_stop(struct sr_dev_inst *sdi);
+
+#endif
diff --git a/src/hardware/fluke-45/api.c b/src/hardware/fluke-45/api.c
new file mode 100644 (file)
index 0000000..e15adaa
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 John Chajecki <subs@qcontinuum.plus.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 <config.h>
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+};
+
+/* Vendor, model, number of channels, poll period */
+static const struct fluke_scpi_dmm_model supported_models[] = {
+       { "FLUKE", "45", 2, 0 },
+};
+
+static struct sr_dev_driver fluke_45_driver_info;
+
+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;
+       const struct scpi_command *cmdset = fluke_45_cmdset;
+       unsigned int i;
+       const struct fluke_scpi_dmm_model *model = NULL;
+       gchar *channel_name;
+       char *response;
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->conn = scpi;
+
+       /* Test for serial port ECHO enabled. */
+       sr_scpi_get_string(scpi, "ECHO-TEST", &response);
+       if (strcmp(response, "ECHO-TEST") == 0) {
+               sr_err("Serial port ECHO is ON. Please turn it OFF!");
+               return NULL;
+       }
+
+       /* Get device IDN. */
+       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;
+               }
+       }
+
+       /* Check IDN. */
+       for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
+               if (!g_ascii_strcasecmp(hw_info->manufacturer,
+                                       supported_models[i].vendor) &&
+                               !strcmp(hw_info->model, supported_models[i].model)) {
+                       model = &supported_models[i];
+                       break;
+               }
+       }
+       if (!model) {
+               sr_scpi_hw_info_free(hw_info);
+               return NULL;
+       }
+
+       /* Set up device parameters. */
+       sdi->vendor = g_strdup(model->vendor);
+       sdi->model = g_strdup(model->model);
+       sdi->version = g_strdup(hw_info->firmware_version);
+       sdi->serial_num = g_strdup(hw_info->serial_number);
+       sdi->conn = scpi;
+       sdi->driver = &fluke_45_driver_info;
+       sdi->inst_type = SR_INST_SCPI;
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       devc->num_channels = model->num_channels;
+       devc->cmdset = cmdset;
+
+       /* Create channels. */
+       for (i = 0; i < devc->num_channels; i++) {
+               channel_name = g_strdup_printf("P%d", i + 1);
+               sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, channel_name);
+       }
+
+       sdi->priv = devc;
+
+       return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       return sr_scpi_scan(di->context, options, probe_device);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       struct sr_scpi_dev_inst *scpi;
+       int ret;
+
+       scpi = sdi->conn;
+
+       if ((ret = sr_scpi_open(scpi) < 0)) {
+               sr_err("Failed to open SCPI device: %s.", sr_strerror(ret));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       struct sr_scpi_dev_inst *scpi;
+
+       scpi = sdi->conn;
+
+       if (!scpi)
+               return SR_ERR_BUG;
+
+       return sr_scpi_close(scpi);
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+
+       (void)cg;
+
+       devc = sdi->priv;
+
+       return sr_sw_limits_config_set(&devc->limits, key, data);
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc = sdi->priv;
+
+       (void)cg;
+
+       return sr_sw_limits_config_get(&devc->limits, key, data);
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct sr_scpi_dev_inst *scpi;
+       struct dev_context *devc;
+       int ret;
+
+       scpi = sdi->conn;
+       devc = sdi->priv;
+
+       sr_sw_limits_acquisition_start(&devc->limits);
+       std_session_send_df_header(sdi);
+
+       if ((ret = sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 10,
+                       fl45_scpi_receive_data, (void *)sdi)) != SR_OK)
+               return ret;
+
+       return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       struct sr_scpi_dev_inst *scpi;
+       double d;
+
+       scpi = sdi->conn;
+
+       /*
+        * A requested value is certainly on the way. Retrieve it now,
+        * to avoid leaving the device in a state where it's not expecting
+        * commands.
+        */
+       sr_scpi_get_double(scpi, NULL, &d);
+       sr_scpi_source_remove(sdi->session, scpi);
+
+       std_session_send_df_end(sdi);
+
+       return SR_OK;
+}
+
+static struct sr_dev_driver fluke_45_driver_info = {
+       .name = "fluke-45",
+       .longname = "Fluke 45",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(fluke_45_driver_info);
diff --git a/src/hardware/fluke-45/protocol.c b/src/hardware/fluke-45/protocol.c
new file mode 100644 (file)
index 0000000..5d9c379
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 John Chajecki <subs@qcontinuum.plus.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 <config.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <glib.h>
+#include <errno.h>
+#include <scpi.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/* Get the current state of the meter and sets analog object parameters. */
+SR_PRIV int fl45_get_status(const struct sr_dev_inst *sdi,
+               struct sr_datafeed_analog *analog, int idx)
+{
+       struct dev_context *devc;
+       char *cmd, *func;
+       int res;
+
+       res = 0;
+
+       /* Command string to read current function. */
+       cmd = g_strdup_printf("FUNC%d?", idx + 1);
+       sr_dbg("Sent command: %s.", cmd);
+
+       if (!(devc = sdi->priv))
+               return TRUE;
+
+       /* Default settings. */
+       analog[idx].meaning->mq = 0;
+       analog[idx].meaning->unit = 0;
+       analog[idx].meaning->mqflags = 0;
+
+       /* Get a response to the FUNC? command. */
+       res = fl45_scpi_get_response(sdi, cmd);
+       if (res == SR_ERR)
+               return res;
+       sr_dbg("Response to FUNC: %s.", devc->response);
+
+       /* Set up analog mq, unit and flags. */
+       if (res == SR_OK && devc->response != NULL) {
+               func = devc->response;
+               if (strcmp(func, "AAC") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_CURRENT;
+                       analog[idx].meaning->unit = SR_UNIT_AMPERE;
+                       analog[idx].meaning->mqflags = SR_MQFLAG_AC;
+               } else if (strcmp(func, "AACDC") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_CURRENT;
+                       analog[idx].meaning->unit = SR_UNIT_AMPERE;
+                       analog[idx].meaning->mqflags = SR_MQFLAG_AC;
+               } else if (strcmp(func, "ADC") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_CURRENT;
+                       analog[idx].meaning->unit = SR_UNIT_AMPERE;
+                       analog[idx].meaning->mqflags = SR_MQFLAG_DC;
+               } else if (strcmp(func, "CONT") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_CONTINUITY;
+                       analog->meaning->unit = SR_UNIT_BOOLEAN;
+               } else if (strcmp(func, "DIODE") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_VOLTAGE;
+                       analog[idx].meaning->unit = SR_UNIT_VOLT;
+                       analog[idx].meaning->mqflags = SR_MQFLAG_DIODE;
+               } else if (strcmp(func, "FREQ") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_FREQUENCY;
+                       analog[idx].meaning->unit = SR_UNIT_HERTZ;
+               } else if (strcmp(func, "OHMS") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_RESISTANCE;
+                       analog[idx].meaning->unit = SR_UNIT_OHM;
+               } else if (strcmp(func, "VAC") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_VOLTAGE;
+                       analog[idx].meaning->unit = SR_UNIT_VOLT;
+                       analog[idx].meaning->mqflags = SR_MQFLAG_AC;
+               } else if (strcmp(func, "VACDC") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_VOLTAGE;
+                       analog[idx].meaning->unit = SR_UNIT_VOLT;
+                       analog[idx].meaning->mqflags |= SR_MQFLAG_AC;
+                       analog[idx].meaning->mqflags |= SR_MQFLAG_DC;
+               } else if (strcmp(func, "VDC") == 0) {
+                       analog[idx].meaning->mq = SR_MQ_VOLTAGE;
+                       analog[idx].meaning->unit = SR_UNIT_VOLT;
+                       analog[idx].meaning->mqflags = SR_MQFLAG_DC;
+               }
+       }
+
+       /* Is the meter in autorange mode? */
+       res = fl45_scpi_get_response(sdi, "AUTO?");
+       if (res == SR_ERR)
+               return res;
+       sr_dbg("Response to AUTO: %s.", devc->response);
+       if (res == SR_OK && devc->response != NULL) {
+               if (strcmp(devc->response, "1") == 0)
+                       analog[idx].meaning->mqflags |= SR_MQFLAG_AUTORANGE;
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int fl45_get_modifiers(const struct sr_dev_inst *sdi,
+               struct sr_datafeed_analog *analog, int idx)
+{
+       struct dev_context *devc;
+       int res, mod;
+
+       if (!(devc = sdi->priv))
+               return TRUE;
+
+       /* Get modifier value. */
+       res = fl45_scpi_get_response(sdi, "MOD?");
+       if (res == SR_ERR)
+               return res;
+       sr_dbg("Response to MOD: %s.", devc->response);
+       if (res == SR_OK && devc->response != NULL) {
+               mod = atoi(devc->response);
+               if (mod & 0x01) {
+                       analog[idx].meaning->mqflags |= SR_MQFLAG_MIN;
+                       sr_dbg("MIN bit set: %s.", "1");
+               }
+               if (mod & 0x02) {
+                       analog[idx].meaning->mqflags |= SR_MQFLAG_MAX;
+                       sr_dbg("MAX bit set: %s.", "2");
+               }
+               if (mod & 0x04) {
+                       analog[idx].meaning->mqflags |= SR_MQFLAG_HOLD;
+                       sr_dbg("HOLD bit set: %s.", "4");
+               }
+               if (mod & 0x08) {
+                       sr_dbg("dB bit set: %s.", "8");
+                       analog[idx].meaning->mq = SR_MQ_POWER_FACTOR;
+                       analog[idx].meaning->unit = SR_UNIT_DECIBEL_MW;
+                       analog[idx].meaning->mqflags = 0;
+                       analog[idx].encoding->digits = 2;
+                       analog[idx].spec->spec_digits = 2;
+               }
+               if (mod & 0x10) {
+                       sr_dbg("dB Power mod bit set: %s.", "16");
+                       analog[idx].meaning->mq = SR_MQ_POWER;
+                       analog[idx].meaning->unit = SR_UNIT_DECIBEL_SPL;
+                       analog[idx].meaning->mqflags = 0;
+                       analog[idx].encoding->digits = 2;
+                       analog[idx].spec->spec_digits = 2;
+               }
+               if (mod & 0x20) {
+                       sr_dbg("REL bit set: %s.", "32");
+                       analog[idx].meaning->mqflags |= SR_MQFLAG_HOLD;
+               }
+       }
+
+       return SR_OK;
+}
+
+int get_reading_dd(char *reading, size_t size)
+{
+       int pe, pd, digits;
+       unsigned int i;
+       char expstr[3];
+       char *eptr;
+       long exp;
+
+       /* Calculate required precision. */
+
+       pe = pd = digits = 0;
+
+       /* Get positions for '.' end 'E'. */
+       for (i = 0; i < size; i++) {
+               if (reading[i] == '.')
+                       pd = i;
+               if (reading[i] == 'E') {
+                       pe = i;
+                       break;
+               }
+       }
+
+       digits = (pe - pd) - 1;
+
+       /* Get exponent element. */
+       expstr[0] = reading[pe + 1];
+       expstr[1] = reading[pe + 2];
+       expstr[2] = '\0';
+       errno = 0;
+       exp = strtol(expstr, &eptr, 10);
+       if (errno != 0)
+               return 2;
+       /* A negative exponent increses digits, a positive one reduces. */
+       exp = exp * (-1);
+
+       /* Adjust digits taking into account exponent. */
+       digits = digits + exp;
+
+       return digits;
+}
+
+SR_PRIV int fl45_scpi_receive_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_analog analog[2];
+       struct sr_analog_encoding encoding[2];
+       struct sr_analog_meaning meaning[2];
+       struct sr_analog_spec spec[2];
+       struct sr_channel *channel;
+       char *reading;
+       float fv;
+       int res, digits;
+       unsigned int i;
+       int sent_ch[2];
+
+       (void)fd;
+       (void)revents;
+
+       if (!(sdi = cb_data))
+               return TRUE;
+
+       if (!(devc = sdi->priv))
+               return TRUE;
+
+       res = 0;
+       sent_ch[0] = sent_ch[1] = 0;
+
+       /* Process the list of channels. */
+       for (i = 0; i < devc->num_channels; i++) {
+               /* Note: digits/spec_digits will be overridden later. */
+               sr_analog_init(&analog[i], &encoding[i], &meaning[i], &spec[i], 0);
+
+               /* Detect current meter function. */
+               res = fl45_get_status(sdi, analog, i);
+
+               /* Get channel data. */
+               if (i == 0)
+                       channel = sdi->channels->data;
+               else
+                       channel = sdi->channels->next->data;
+
+               /* Is channel enabled? */
+               if (analog[i].meaning->mq != 0 && channel->enabled) {
+                       /* Then get a reading from it. */
+                       if (i == 0)
+                               res = fl45_scpi_get_response(sdi, "VAL1?");
+                       if (i == 1)
+                               res = fl45_scpi_get_response(sdi, "VAL2?");
+                       /* Note: Fluke 45 sends all data in text strings. */
+                       reading = devc->response;
+
+                       /* Deal with OL reading. */
+                       if (strcmp(reading, "+1E+9") == 0) {
+                               fv = INFINITY;
+                               sr_dbg("Reading OL (infinity): %s.",
+                                       devc->response);
+                       } else if (res == SR_OK && reading != NULL) {
+                               /* Convert reading to float. */
+                               sr_dbg("Meter reading string: %s.", reading);
+                               res = sr_atof_ascii(reading, &fv);
+                               digits = get_reading_dd(reading, strlen(reading));
+                               analog[i].encoding->digits = digits;
+                               analog[i].spec->spec_digits = digits;
+
+                       } else {
+                               sr_dbg("Invalid float string: '%s'.", reading);
+                               return SR_ERR;
+                       }
+
+                       /* Are we on a little or big endian system? */
+#ifdef WORDS_BIGENDIAN
+                       analog[i].encoding->is_bigendian = TRUE;
+#else
+                       analog[i].encoding->is_bigendian = FALSE;
+#endif
+
+                       /* Apply any modifiers. */
+                       res = fl45_get_modifiers(sdi, analog, i);
+
+                       /* Channal flag. */
+                       sent_ch[i] = 1;
+
+                       /* Set up analog object. */
+                       analog[i].num_samples = 1;
+                       analog[i].data = &fv;
+                       analog[i].meaning->channels = g_slist_append(NULL, channel);
+
+                       packet.type = SR_DF_ANALOG;
+                       packet.payload = &analog[i];
+
+                       sr_session_send(sdi, &packet);
+
+                       g_slist_free(analog[i].meaning->channels);
+               }
+       }
+
+       /* Update appropriate channel limits. */
+       if (sent_ch[0] || sent_ch[1])
+               sr_sw_limits_update_samples_read(&devc->limits, 1);
+
+       /* Are we done collecting samples? */
+       if (sr_sw_limits_check(&devc->limits))
+               sr_dev_acquisition_stop(sdi);
+
+       return TRUE;
+}
+
+SR_PRIV int fl45_scpi_get_response(const struct sr_dev_inst *sdi, char *cmd)
+{
+       struct dev_context *devc;
+       devc = sdi->priv;
+
+       /* Attempt to get a SCPI reponse. */
+       if (sr_scpi_get_string(sdi->conn, cmd, &devc->response) != SR_OK)
+               return SR_ERR;
+
+       /* Deal with RS232 '=>' prompt. */
+       if (strcmp(devc->response, "=>") == 0) {
+               /*
+                * If the response is a prompt then ignore and read the next
+                * response in the buffer.
+                */
+               devc->response = NULL;
+               /* Now attempt to read again. */
+               if (sr_scpi_get_string(sdi->conn, NULL, &devc->response) != SR_OK)
+                       return SR_ERR;
+       }
+
+       /* NULL RS232 error prompts. */
+       if (strcmp(devc->response, "!>") == 0
+           || (strcmp(devc->response, "?>") == 0)) {
+               /* Unable to execute CMD. */
+               devc->response = NULL;
+       }
+
+       return SR_OK;
+}
diff --git a/src/hardware/fluke-45/protocol.h b/src/hardware/fluke-45/protocol.h
new file mode 100644 (file)
index 0000000..95c8c3f
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 John Chajecki <subs@qcontinuum.plus.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_45_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_FLUKE_45_PROTOCOL_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <scpi.h>
+
+#define LOG_PREFIX "fluke-45"
+
+#define FLUKEDMM_BUFSIZE 256
+
+/* Always USB-serial, 1ms is plenty. */
+#define SERIAL_WRITE_TIMEOUT_MS 1
+
+enum data_format {
+       /* Fluke 45 uses IEEE488v2. */
+       FORMAT_IEEE488_2,
+};
+
+enum dmm_scpi_cmds {
+       SCPI_CMD_CLS,
+       SCPI_CMD_RST,
+       SCPI_CMD_REMS,
+       SCPI_CMD_RWLS,
+       SCPI_CMD_LOCS,
+       SCPI_CMD_LWLS,
+       SCPI_CMD_REMOTE,
+       SCPI_CMD_LOCAL,
+       SCPI_CMD_SET_ACVOLTAGE,
+       SCPI_CMD_SET_ACDCVOLTAGE,
+       SCPI_CMD_SET_DCVOLTAGE,
+       SCPI_CMD_SET_ACCURRENT,
+       SCPI_CMD_SET_ACDCCURRENT,
+       SCPI_CMD_SET_DCCURRENT,
+       SCPI_CMD_SET_FREQUENCY,
+       SCPI_CMD_SET_RESISTANCE,
+       SCPI_CMD_SET_CONTINUITY,
+       SCPI_CMD_SET_DIODE,
+       SCPI_CMD_SET_AUTO,
+       SCPI_CMD_GET_AUTO,
+       SCPI_CMD_SET_FIXED,
+       SCPI_CMD_SET_RANGE,
+       SCPI_CMD_GET_RANGE_D1,
+       SCPI_CMD_GET_RANGE_D2,
+       SCPI_CMD_SET_DB,
+       SCPI_CMD_SET_DBCLR,
+       SCPI_CMD_SET_DBPOWER,
+       SCPI_CMD_SET_DBREF,
+       SCPI_CMD_GET_DBREF,
+       SCPI_CMD_SET_HOLD,
+       SCPI_CMD_SET_HOLDCLR,
+       SCPI_CMD_SET_MAX,
+       SCPI_CMD_SET_MIN,
+       SCPI_CMD_SET_MMCLR,
+       SCPI_CMD_SET_REL,
+       SCPI_CMD_SET_RELCLR,
+       SCPI_CMD_GET_MEAS_DD,
+       SCPI_CMD_GET_MEAS_D1,
+       SCPI_CMD_GET_MEAS_D2,
+       SCPI_CMD_GET_RATE,
+       SCPI_CMD_SET_RATE,
+       SCPI_CMD_SET_TRIGGER,
+       SCPI_CMD_GET_TRIGGER,
+};
+
+static const struct scpi_command fluke_45_cmdset[] = {
+       { SCPI_CMD_CLS, "*CLS" },
+       { SCPI_CMD_RST, "*RST" },
+       { SCPI_CMD_REMS, "*REMS" },
+       { SCPI_CMD_RWLS, "*RWLS" },
+       { SCPI_CMD_LOCS, "LOCS" },
+       { SCPI_CMD_LWLS, "LWLS" },
+       { SCPI_CMD_REMOTE, "REMS" },
+       { SCPI_CMD_LOCAL, "LOCS" },
+       { SCPI_CMD_SET_ACVOLTAGE, "VAC" },
+       { SCPI_CMD_SET_ACDCVOLTAGE, "VACDC" },
+       { SCPI_CMD_SET_DCVOLTAGE, "VDC" },
+       { SCPI_CMD_SET_ACCURRENT, "AAC" },
+       { SCPI_CMD_SET_ACDCCURRENT, "AACDC" },
+       { SCPI_CMD_SET_DCCURRENT, "ADC" },
+       { SCPI_CMD_SET_FREQUENCY, "FREQ" },
+       { SCPI_CMD_SET_RESISTANCE, "OHMS" },
+       { SCPI_CMD_SET_CONTINUITY, "CONT" },
+       { SCPI_CMD_SET_DIODE, "DIODE" },
+       { SCPI_CMD_SET_AUTO, "AUTO" },
+       { SCPI_CMD_GET_AUTO, "AUTO?" },
+       { SCPI_CMD_SET_FIXED, "FIXED" },
+       { SCPI_CMD_SET_RANGE, "RANGE" },
+       { SCPI_CMD_GET_RANGE_D1, "RANGE1?" },
+       { SCPI_CMD_GET_RANGE_D2, "RANGE2?" },
+       { SCPI_CMD_SET_DB, "DB" },
+       { SCPI_CMD_SET_DBCLR, "DBCLR" },
+       { SCPI_CMD_SET_DBPOWER, "DBPOWER" },
+       { SCPI_CMD_SET_DBREF, "DBREF" },
+       { SCPI_CMD_GET_DBREF, "DBREF?" },
+       { SCPI_CMD_SET_HOLD, "HOLD" },
+       { SCPI_CMD_SET_HOLDCLR, "HOLDCLR" },
+       { SCPI_CMD_SET_MAX, "MAX" },
+       { SCPI_CMD_SET_MIN, "MIN" },
+       { SCPI_CMD_SET_MMCLR, "MMCLR" },
+       { SCPI_CMD_SET_REL, "REL" },
+       { SCPI_CMD_SET_RELCLR, "RELCLR" },
+       { SCPI_CMD_GET_MEAS_DD, "MEAS?" },
+       { SCPI_CMD_GET_MEAS_D1, "MEAS1?" },
+       { SCPI_CMD_GET_MEAS_D2, "MEAS2?" },
+       { SCPI_CMD_SET_RATE, "RATE" },
+       { SCPI_CMD_GET_RATE, "RATE?" },
+       { SCPI_CMD_SET_TRIGGER, "TRIGGER" },
+       { SCPI_CMD_GET_TRIGGER, "TRIGGER?" },
+       ALL_ZERO
+};
+
+struct fluke_scpi_dmm_model {
+       const char *vendor;
+       const char *model;
+       int num_channels;
+       int poll_period; /* How often to poll, in ms. */
+};
+
+struct channel_spec {
+       const char *name;
+       /* Min, max, programming resolution, spec digits, encoding digits. */
+       double voltage[5];
+       double current[5];
+       double resistance[5];
+       double capacitance[5];
+       double conductance[5];
+       double diode[5];
+};
+
+struct channel_group_spec {
+       const char *name;
+       uint64_t channel_index_mask;
+       uint64_t features;
+};
+
+struct dmm_channel {
+       enum sr_mq mq;
+       unsigned int hw_output_idx;
+       const char *hwname;
+       int digits;
+};
+
+struct dmm_channel_instance {
+       enum sr_mq mq;
+       int command;
+       const char *prefix;
+};
+
+struct dmm_channel_group {
+       uint64_t features;
+};
+
+struct dev_context {
+       struct sr_sw_limits limits;
+       unsigned int num_channels;
+       const struct scpi_command *cmdset;
+       char *response;
+       const char *mode1;
+       const char *mode2;
+       long range1;
+       long range2;
+       long autorng;
+       const char *rate;
+       long modifiers;
+       long trigmode;
+};
+
+int get_reading_dd(char *reading, size_t size);
+
+SR_PRIV extern const struct fluke_scpi_dmm_model dmm_profiles[];
+
+SR_PRIV int fl45_scpi_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int fl45_scpi_get_response(const struct sr_dev_inst *sdi,  char *cmd);
+SR_PRIV int fl45_get_status(const struct sr_dev_inst *sdi,
+               struct sr_datafeed_analog *analog, int idx);
+SR_PRIV int fl45_get_modifiers(const struct sr_dev_inst *sdi,
+               struct sr_datafeed_analog *analog, int idx);
+
+#endif
index 0ee6c7fa64dea886e134bdca996395cfb888fb32..f533f1c5df7d7a2731f187b28779efea7c709944 100644 (file)
@@ -32,8 +32,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -87,7 +90,7 @@ static GSList *fluke_scan(struct sr_dev_driver *di, const char *conn,
 
                /* Response is first a CMD_ACK byte (ASCII '0' for OK,
                 * or '1' to signify an error. */
-               len = 128;
+               len = sizeof(buf);
                serial_readline(serial, &b, &len, 150);
                if (len != 1)
                        continue;
@@ -95,7 +98,7 @@ static GSList *fluke_scan(struct sr_dev_driver *di, const char *conn,
                        continue;
 
                /* If CMD_ACK was OK, ID string follows. */
-               len = 128;
+               len = sizeof(buf);
                serial_readline(serial, &b, &len, 850);
                if (len < 10)
                        continue;
@@ -179,41 +182,22 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return devices;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -221,15 +205,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -252,7 +232,7 @@ static struct sr_dev_driver flukedmm_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = NULL,
        .config_set = config_set,
        .config_list = config_list,
index 7500ab3116c35edbefdeb76d7b75f1b677943e46..a5d8cefe888e61aaa5446624232a5dc79862b09e 100644 (file)
@@ -324,7 +324,7 @@ static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens)
                else if (meas_char == 3)
                        devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
                else if (meas_char == 15)
-                       devc->mqflags |= SR_MQFLAG_DIODE;
+                       devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
                break;
        case 2:
                devc->mq = SR_MQ_CURRENT;
@@ -526,7 +526,7 @@ SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits)) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        }
 
index b8afc9000bf268710e82f6bd91f63cecf6b03516..36ece3c9eda9c7d7aee5a8d0b46e57afe8eca182 100644 (file)
@@ -46,12 +46,10 @@ struct flukedmm_profile {
        int timeout;
 };
 
-/* Private, per-device-instance driver context. */
 struct dev_context {
        const struct flukedmm_profile *profile;
        struct sr_sw_limits limits;
 
-       /* Runtime. */
        char buf[FLUKEDMM_BUFSIZE];
        int buflen;
        int64_t cmd_sent_at;
index 0927d3a274387d14d1fe1dd34a1377f72a73ee23..b22e57f1a883f07572dd232c7c8534366c786f88 100644 (file)
@@ -28,8 +28,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
@@ -47,14 +50,8 @@ static const struct ftdi_chip_desc ft2232h_desc = {
        .product = 0x6010,
        .samplerate_div = 20,
        .channel_names = {
-               "ADBUS0",
-               "ADBUS1",
-               "ADBUS2",
-               "ADBUS3",
-               "ADBUS4",
-               "ADBUS5",
-               "ADBUS6",
-               "ADBUS7",
+               "ADBUS0", "ADBUS1", "ADBUS2", "ADBUS3",
+               "ADBUS4", "ADBUS5", "ADBUS6", "ADBUS7",
                /* TODO: BDBUS[0..7] channels. */
                NULL
        }
@@ -65,14 +62,7 @@ static const struct ftdi_chip_desc ft232r_desc = {
        .product = 0x6001,
        .samplerate_div = 30,
        .channel_names = {
-               "TXD",
-               "RXD",
-               "RTS#",
-               "CTS#",
-               "DTR#",
-               "DSR#",
-               "DCD#",
-               "RI#",
+               "TXD", "RXD", "RTS#", "CTS#", "DTR#", "DSR#", "DCD#", "RI#",
                NULL
        }
 };
@@ -108,7 +98,6 @@ static void scan_device(struct ftdi_context *ftdic,
                return;
        }
 
-       /* Allocate memory for our private device context. */
        devc = g_malloc0(sizeof(struct dev_context));
 
        /* Allocate memory for the incoming data. */
@@ -135,7 +124,6 @@ static void scan_device(struct ftdi_context *ftdic,
        }
        sr_dbg("Found an FTDI device: %s.", model);
 
-       /* Register the device with libsigrok. */
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
        sdi->vendor = vendor;
@@ -178,8 +166,6 @@ static GSList *scan_all(struct ftdi_context *ftdic, GSList *options)
                return NULL;
        }
 
-       sr_dbg("Number of FTDI devices found: %d", ret);
-
        curdev = devlist;
        while (curdev) {
                scan_device(ftdic, curdev->dev, &devices);
@@ -213,7 +199,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                }
        }
 
-       /* Allocate memory for the FTDI context (ftdic) and initialize it. */
        ftdic = ftdi_new();
        if (!ftdic) {
                sr_err("Failed to initialize libftdi.");
@@ -242,18 +227,14 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, devices);
 }
 
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
        g_free(devc->data_buf);
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -276,23 +257,19 @@ static int dev_open(struct sr_dev_inst *sdi)
                goto err_ftdi_free;
        }
 
-       /* Purge RX/TX buffers in the FTDI chip. */
        ret = ftdi_usb_purge_buffers(devc->ftdic);
        if (ret < 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, 0x00, 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.");
 
        ret = ftdi_set_bitmode(devc->ftdic, 0x00, BITMODE_BITBANG);
        if (ret < 0) {
@@ -300,15 +277,15 @@ static int dev_open(struct sr_dev_inst *sdi)
                       ret, ftdi_get_error_string(devc->ftdic));
                goto err_dev_open_close_ftdic;
        }
-       sr_dbg("FTDI chip bitbang mode entered successfully.");
-
-       sdi->status = SR_ST_ACTIVE;
 
        return SR_OK;
+
 err_dev_open_close_ftdic:
        ftdi_usb_close(devc->ftdic);
+
 err_ftdi_free:
        ftdi_free(devc->ftdic);
+
        return SR_ERR;
 }
 
@@ -318,13 +295,12 @@ static int dev_close(struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       if (devc->ftdic) {
-               ftdi_usb_close(devc->ftdic);
-               ftdi_free(devc->ftdic);
-               devc->ftdic = NULL;
-       }
+       if (!devc->ftdic)
+               return SR_ERR_BUG;
 
-       sdi->status = SR_ST_INACTIVE;
+       ftdi_usb_close(devc->ftdic);
+       ftdi_free(devc->ftdic);
+       devc->ftdic = NULL;
 
        return SR_OK;
 }
@@ -332,16 +308,13 @@ static int dev_close(struct sr_dev_inst *sdi)
 static int config_get(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
-       char str[128];
 
        (void)cg;
 
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_SAMPLERATE:
                *data = g_variant_new_uint64(devc->cur_samplerate);
@@ -350,40 +323,32 @@ static int config_get(uint32_t key, GVariant **data,
                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);
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_set(uint32_t key, GVariant *data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
        struct dev_context *devc;
        uint64_t value;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_MSEC:
                value = g_variant_get_uint64(data);
                /* TODO: Implement. */
-               ret = SR_ERR_NA;
-               break;
+               (void)value;
+               return SR_ERR_NA;
        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_SAMPLERATE:
@@ -393,44 +358,27 @@ static int config_set(uint32_t key, GVariant *data,
                devc->cur_samplerate = value;
                return ftdi_la_set_samplerate(devc);
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
-       GVariant *gvar;
-       GVariantBuilder gvb;
-
-       (void)sdi;
-       (void)cg;
-
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
                break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -439,9 +387,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        if (!devc->ftdic)
                return SR_ERR_BUG;
 
@@ -462,10 +407,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       sr_dbg("Stopping acquisition.");
        sr_session_source_remove(sdi->session, -1);
 
        std_session_send_df_end(sdi);
index 8d81d87752e6d91829d55f781c4738b31f3d4c27..2f7a824a1d5ae93c8ab07b534299c851f9bf7103 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <ftdi.h>
 #include "protocol.h"
 
@@ -79,7 +80,7 @@ SR_PRIV int ftdi_la_receive_data(int fd, int revents, void *cb_data)
        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);
+               sr_dev_acquisition_stop(sdi);
                return FALSE;
        }
        if (bytes_read == 0) {
@@ -94,7 +95,7 @@ SR_PRIV int ftdi_la_receive_data(int fd, int revents, void *cb_data)
        if (devc->limit_samples && (n >= devc->limit_samples)) {
                send_samples(sdi, devc->limit_samples - devc->samples_sent);
                sr_info("Requested number of samples reached.");
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        } else {
                send_samples(sdi, devc->bytes_received);
index d5ba9b09cbb0149e1115d17034788fc27345d7b6..495c4dbbccf1c1264e1bce566c41a2d50d48a0bb 100644 (file)
@@ -36,7 +36,6 @@ struct ftdi_chip_desc {
        char *channel_names[];
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct ftdi_context *ftdic;
        const struct ftdi_chip_desc *desc;
index 3f80b93e73c2d904be035a686e4e9292459854fc..d107191663a820af2f7ccdb3115bb08b507f428d 100644 (file)
 
 #include <config.h>
 #include "protocol.h"
-#include "dslogic.h"
 #include <math.h>
 
 static const struct fx2lafw_profile supported_fx2[] = {
        /*
         * CWAV USBee AX
-        * EE Electronics ESLA201A
-        * ARMFLY AX-Pro
+        * ARMFLY AX-Pro (clone of the CWAV USBee AX)
+        * ARMFLY Mini-Logic (clone of the CWAV USBee AX)
+        * EE Electronics ESLA201A (clone of the CWAV USBee AX)
+        * HT USBee-AxPro (clone of the CWAV USBee AX)
+        * MCU123 USBee AX Pro clone (clone of the CWAV USBee AX)
+        * Noname LHT00SU1 (clone of the CWAV USBee AX)
+        * XZL_Studio AX (clone of the CWAV USBee AX)
         */
        { 0x08a9, 0x0014, "CWAV", "USBee AX", NULL,
                "fx2lafw-cwav-usbeeax.fw",
                DEV_CAPS_AX_ANALOG, NULL, NULL},
+
        /*
         * CWAV USBee DX
-        * XZL-Studio DX
+        * HT USBee-DxPro (clone of the CWAV USBee DX), not yet supported!
+        * XZL-Studio DX (clone of the CWAV USBee DX)
         */
        { 0x08a9, 0x0015, "CWAV", "USBee DX", NULL,
                "fx2lafw-cwav-usbeedx.fw",
@@ -54,38 +60,14 @@ static const struct fx2lafw_profile supported_fx2[] = {
                "fx2lafw-cwav-usbeezx.fw",
                0, NULL, NULL},
 
-       /* DreamSourceLab DSLogic (before FW upload) */
-       { 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL,
-               "dreamsourcelab-dslogic-fx2.fw",
-               DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, NULL, NULL},
-       /* DreamSourceLab DSLogic (after FW upload) */
-       { 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL,
-               "dreamsourcelab-dslogic-fx2.fw",
-               DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, "DreamSourceLab", "DSLogic"},
-
-       /* DreamSourceLab DSCope (before FW upload) */
-       { 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL,
-               "dreamsourcelab-dscope-fx2.fw",
-               DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, NULL, NULL},
-       /* DreamSourceLab DSCope (after FW upload) */
-       { 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL,
-               "dreamsourcelab-dscope-fx2.fw",
-               DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, "DreamSourceLab", "DSCope"},
-
-       /* DreamSourceLab DSLogic Pro (before FW upload) */
-       { 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL,
-               "dreamsourcelab-dslogic-pro-fx2.fw",
-               DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, NULL, NULL},
-       /* DreamSourceLab DSLogic Pro (after FW upload) */
-       { 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL,
-               "dreamsourcelab-dslogic-pro-fx2.fw",
-               DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, "DreamSourceLab", "DSLogic"},
-
        /*
         * Saleae Logic
-        * EE Electronics ESLA100
-        * Robomotic MiniLogic
-        * Robomotic BugLogic 3
+        * EE Electronics ESLA100 (clone of the Saleae Logic)
+        * Hantek 6022BL in LA mode (clone of the Saleae Logic)
+        * Instrustar ISDS205X in LA mode (clone of the Saleae Logic)
+        * Robomotic MiniLogic (clone of the Saleae Logic)
+        * Robomotic BugLogic 3 (clone of the Saleae Logic)
+        * MCU123 Saleae Logic clone (clone of the Saleae Logic)
         */
        { 0x0925, 0x3881, "Saleae", "Logic", NULL,
                "fx2lafw-saleae-logic.fw",
@@ -95,6 +77,7 @@ static const struct fx2lafw_profile supported_fx2[] = {
         * Default Cypress FX2 without EEPROM, e.g.:
         * Lcsoft Mini Board
         * Braintechnology USB Interface V2.x
+        * fx2grok-tiny
         */
        { 0x04B4, 0x8613, "Cypress", "FX2", NULL,
                "fx2lafw-cypress-fx2.fw",
@@ -109,6 +92,7 @@ static const struct fx2lafw_profile supported_fx2[] = {
 
        /*
         * sigrok FX2 based 8-channel logic analyzer
+        * fx2grok-flat (before and after renumeration)
         */
        { 0x1d50, 0x608c, "sigrok", "FX2 LA (8ch)", NULL,
                "fx2lafw-sigrok-fx2-8ch.fw",
@@ -121,39 +105,34 @@ static const struct fx2lafw_profile supported_fx2[] = {
                "fx2lafw-sigrok-fx2-16ch.fw",
                DEV_CAPS_16BIT, NULL, NULL },
 
-       ALL_ZERO
-};
+       /*
+        * usb-c-grok
+        */
+       { 0x1d50, 0x608f, "sigrok", "usb-c-grok", NULL,
+               "fx2lafw-usb-c-grok.fw",
+               0, NULL, NULL},
 
-static const uint32_t drvopts[] = {
-       SR_CONF_LOGIC_ANALYZER,
+       ALL_ZERO
 };
 
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t devopts[] = {
-       SR_CONF_CONTINUOUS,
-       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
-       SR_CONF_CONN | SR_CONF_GET,
-       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
-       SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
-       SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+static const uint32_t drvopts[] = {
+       SR_CONF_LOGIC_ANALYZER,
 };
 
-static const uint32_t dslogic_devopts[] = {
-       SR_CONF_CONTINUOUS | SR_CONF_SET | SR_CONF_GET,
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
-       SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_CONN | SR_CONF_GET,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
        SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
-       SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
-       SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const int32_t soft_trigger_matches[] = {
+static const int32_t trigger_matches[] = {
        SR_TRIGGER_ZERO,
        SR_TRIGGER_ONE,
        SR_TRIGGER_RISING,
@@ -161,21 +140,6 @@ static const int32_t soft_trigger_matches[] = {
        SR_TRIGGER_EDGE,
 };
 
-/* Names assigned to available edge slope choices. */
-static const char *const signal_edge_names[] = {
-       [DS_EDGE_RISING] = "rising",
-       [DS_EDGE_FALLING] = "falling",
-};
-
-static const struct {
-       int range;
-       gdouble low;
-       gdouble high;
-} volt_thresholds[] = {
-       { DS_VOLTAGE_RANGE_18_33_V, 0.7, 1.4 },
-       { DS_VOLTAGE_RANGE_5_V, 1.4, 3.6 },
-};
-
 static const uint64_t samplerates[] = {
        SR_KHZ(20),
        SR_KHZ(25),
@@ -195,25 +159,6 @@ static const uint64_t samplerates[] = {
        SR_MHZ(24),
 };
 
-static const uint64_t dslogic_samplerates[] = {
-       SR_KHZ(10),
-       SR_KHZ(20),
-       SR_KHZ(50),
-       SR_KHZ(100),
-       SR_KHZ(200),
-       SR_KHZ(500),
-       SR_MHZ(1),
-       SR_MHZ(2),
-       SR_MHZ(5),
-       SR_MHZ(10),
-       SR_MHZ(20),
-       SR_MHZ(25),
-       SR_MHZ(50),
-       SR_MHZ(100),
-       SR_MHZ(200),
-       SR_MHZ(400),
-};
-
 static gboolean is_plausible(const struct libusb_device_descriptor *des)
 {
        int i;
@@ -325,10 +270,11 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                        continue;
                }
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
-
                libusb_close(hdl);
 
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
+
                prof = NULL;
                for (j = 0; supported_fx2[j].vid; j++) {
                        if (des.idVendor == supported_fx2[j].vid &&
@@ -342,7 +288,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                        }
                }
 
-               /* Skip if the device was not found. */
                if (!prof)
                        continue;
 
@@ -386,21 +331,10 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                sdi->priv = devc;
                devices = g_slist_append(devices, sdi);
 
-               if (!strcmp(prof->model, "DSLogic")
-                               || !strcmp(prof->model, "DSLogic Pro")
-                               || !strcmp(prof->model, "DSCope")) {
-                       devc->dslogic = TRUE;
-                       devc->samplerates = dslogic_samplerates;
-                       devc->num_samplerates = ARRAY_SIZE(dslogic_samplerates);
-                       has_firmware = match_manuf_prod(devlist[i], "DreamSourceLab", "DSLogic")
-                                       || match_manuf_prod(devlist[i], "DreamSourceLab", "DSCope");
-               } else {
-                       devc->dslogic = FALSE;
-                       devc->samplerates = samplerates;
-                       devc->num_samplerates = ARRAY_SIZE(samplerates);
-                       has_firmware = match_manuf_prod(devlist[i],
-                                       "sigrok", "fx2lafw");
-               }
+               devc->samplerates = samplerates;
+               devc->num_samplerates = ARRAY_SIZE(samplerates);
+               has_firmware = usb_match_manuf_prod(devlist[i],
+                               "sigrok", "fx2lafw");
 
                if (has_firmware) {
                        /* Already has the firmware, so fix the new address. */
@@ -411,14 +345,16 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                                        libusb_get_device_address(devlist[i]), NULL);
                } else {
                        if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
-                                       USB_CONFIGURATION, prof->firmware) == SR_OK)
+                                       USB_CONFIGURATION, prof->firmware) == SR_OK) {
                                /* Store when this device's FW was updated. */
                                devc->fw_updated = g_get_monotonic_time();
-                       else
+                       } else {
                                sr_err("Firmware upload failed for "
-                                      "device %d.%d (logical).",
+                                      "device %d.%d (logical), name %s.",
                                       libusb_get_bus_number(devlist[i]),
-                                      libusb_get_device_address(devlist[i]));
+                                      libusb_get_device_address(devlist[i]),
+                                      prof->firmware);
+                       }
                        sdi->inst_type = SR_INST_USB;
                        sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
                                        0xff, NULL);
@@ -430,18 +366,14 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, devices);
 }
 
-static void clear_dev_context(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
        g_slist_free(devc->enabled_analog_channels);
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_dev_context);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -449,7 +381,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        struct sr_dev_driver *di = sdi->driver;
        struct sr_usb_dev_inst *usb;
        struct dev_context *devc;
-       const char *fpga_firmware = NULL;
        int ret;
        int64_t timediff_us, timediff_ms;
 
@@ -509,21 +440,6 @@ static int dev_open(struct sr_dev_inst *sdi)
                return SR_ERR;
        }
 
-       if (devc->dslogic) {
-               if (!strcmp(devc->profile->model, "DSLogic")) {
-                       if (devc->dslogic_voltage_threshold == DS_VOLTAGE_RANGE_18_33_V)
-                               fpga_firmware = DSLOGIC_FPGA_FIRMWARE_3V3;
-                       else
-                               fpga_firmware = DSLOGIC_FPGA_FIRMWARE_5V;
-               } else if (!strcmp(devc->profile->model, "DSLogic Pro")){
-                       fpga_firmware = DSLOGIC_PRO_FPGA_FIRMWARE;
-               } else if (!strcmp(devc->profile->model, "DSCope")) {
-                       fpga_firmware = DSCOPE_FPGA_FIRMWARE;
-               }
-
-               if ((ret = dslogic_fpga_firmware_upload(sdi, fpga_firmware)) != SR_OK)
-                       return ret;
-       }
        if (devc->cur_samplerate == 0) {
                /* Samplerate hasn't been set; default to the slowest one. */
                devc->cur_samplerate = devc->samplerates[0];
@@ -539,14 +455,13 @@ static int dev_close(struct sr_dev_inst *sdi)
        usb = sdi->conn;
 
        if (!usb->devhdl)
-               return SR_ERR;
+               return SR_ERR_BUG;
 
-       sr_info("fx2lafw: Closing device on %d.%d (logical) / %s (physical) interface %d.",
+       sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
                usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
        libusb_release_interface(usb->devhdl, USB_INTERFACE);
        libusb_close(usb->devhdl);
        usb->devhdl = NULL;
-       sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
 }
@@ -556,9 +471,6 @@ static int config_get(uint32_t key, GVariant **data,
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
-       GVariant *range[2];
-       unsigned int i;
-       char str[128];
 
        (void)cg;
 
@@ -576,18 +488,7 @@ static int config_get(uint32_t key, GVariant **data,
                        /* 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_VOLTAGE_THRESHOLD:
-               for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
-                       if (volt_thresholds[i].range != devc->dslogic_voltage_threshold)
-                               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);
-                       break;
-               }
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                break;
        case SR_CONF_LIMIT_SAMPLES:
                *data = g_variant_new_uint64(devc->limit_samples);
@@ -598,18 +499,6 @@ static int config_get(uint32_t key, GVariant **data,
        case SR_CONF_CAPTURE_RATIO:
                *data = g_variant_new_uint64(devc->capture_ratio);
                break;
-       case SR_CONF_EXTERNAL_CLOCK:
-               *data = g_variant_new_boolean(devc->dslogic_external_clock);
-               break;
-       case SR_CONF_CONTINUOUS:
-               *data = g_variant_new_boolean(devc->dslogic_continuous_mode);
-               break;
-       case SR_CONF_CLOCK_EDGE:
-               i = devc->dslogic_clock_edge;
-               if (i >= ARRAY_SIZE(signal_edge_names))
-                       return SR_ERR_BUG;
-               *data = g_variant_new_string(signal_edge_names[0]);
-               break;
        default:
                return SR_ERR_NA;
        }
@@ -617,166 +506,58 @@ static int config_get(uint32_t key, GVariant **data,
        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(uint32_t key, GVariant *data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       uint64_t arg;
-       int i, ret;
-       gdouble low, high;
+       int idx;
 
        (void)cg;
 
        if (!sdi)
                return SR_ERR_ARG;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR;
-
        devc = sdi->priv;
 
-       ret = SR_OK;
-
        switch (key) {
        case SR_CONF_SAMPLERATE:
-               arg = g_variant_get_uint64(data);
-               for (i = 0; i < devc->num_samplerates; i++) {
-                       if (devc->samplerates[i] == arg) {
-                               devc->cur_samplerate = arg;
-                               break;
-                       }
-               }
-               if (i == devc->num_samplerates)
-                       ret = SR_ERR_ARG;
+               if ((idx = std_u64_idx(data, devc->samplerates, devc->num_samplerates)) < 0)
+                       return SR_ERR_ARG;
+               devc->cur_samplerate = devc->samplerates[idx];
                break;
        case SR_CONF_LIMIT_SAMPLES:
                devc->limit_samples = g_variant_get_uint64(data);
                break;
        case SR_CONF_CAPTURE_RATIO:
                devc->capture_ratio = g_variant_get_uint64(data);
-               ret = (devc->capture_ratio > 100) ? SR_ERR : SR_OK;
-               break;
-       case SR_CONF_VOLTAGE_THRESHOLD:
-               g_variant_get(data, "(dd)", &low, &high);
-               ret = SR_ERR_ARG;
-               for (i = 0; (unsigned int)i < ARRAY_SIZE(volt_thresholds); i++) {
-                       if (fabs(volt_thresholds[i].low - low) < 0.1 &&
-                           fabs(volt_thresholds[i].high - high) < 0.1) {
-                               devc->dslogic_voltage_threshold = volt_thresholds[i].range;
-                               break;
-                       }
-               }
-               if (!strcmp(devc->profile->model, "DSLogic")) {
-                       if (devc->dslogic_voltage_threshold == DS_VOLTAGE_RANGE_5_V)
-                               ret = dslogic_fpga_firmware_upload(sdi, DSLOGIC_FPGA_FIRMWARE_5V);
-                       else
-                               ret = dslogic_fpga_firmware_upload(sdi, DSLOGIC_FPGA_FIRMWARE_3V3);
-               } else if (!strcmp(devc->profile->model, "DSLogic Pro")) {
-                       ret = dslogic_fpga_firmware_upload(sdi, DSLOGIC_PRO_FPGA_FIRMWARE);
-               }
-               break;
-       case SR_CONF_EXTERNAL_CLOCK:
-               devc->dslogic_external_clock = g_variant_get_boolean(data);
-               break;
-       case SR_CONF_CONTINUOUS:
-               devc->dslogic_continuous_mode = g_variant_get_boolean(data);
-               break;
-       case SR_CONF_CLOCK_EDGE:
-               i = lookup_index(data, signal_edge_names,
-                                  ARRAY_SIZE(signal_edge_names));
-               if (i < 0)
-                       return SR_ERR_ARG;
-               devc->dslogic_clock_edge = i;
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *gvar, *range[2];
-       GVariantBuilder gvb;
-       unsigned int i;
 
-       (void)cg;
+       devc = (sdi) ? sdi->priv : NULL;
 
        switch (key) {
        case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi) {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               } else {
-                       devc = sdi->priv;
-                       if (!devc->dslogic)
-                               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                                                 devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       else
-                               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                                                 dslogic_devopts, ARRAY_SIZE(dslogic_devopts), sizeof(uint32_t));
-               }
-               break;
-       case SR_CONF_VOLTAGE_THRESHOLD:
-               if (!sdi->priv)
-                       return SR_ERR_ARG;
-               devc = sdi->priv;
-               if (!devc->dslogic)
+               if (cg)
                        return SR_ERR_NA;
-               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;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        case SR_CONF_SAMPLERATE:
-               devc = sdi->priv;
-               g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
-               gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), devc->samplerates,
-                               devc->num_samplerates, sizeof(uint64_t));
-               g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
-               *data = g_variant_builder_end(&gvb);
+               if (!devc)
+                       return SR_ERR_NA;
+               *data = std_gvar_samplerates(devc->samplerates, devc->num_samplerates);
                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;
-       case SR_CONF_CLOCK_EDGE:
-               *data = g_variant_new_strv(signal_edge_names,
-                       ARRAY_SIZE(signal_edge_names));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                break;
        default:
                return SR_ERR_NA;
@@ -785,294 +566,8 @@ static int config_list(uint32_t key, GVariant **data,
        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;
-
-       drvc = (struct drv_context *)cb_data;
-
-       tv.tv_sec = tv.tv_usec = 0;
-       libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
-
-       return TRUE;
-}
-
-static int start_transfers(const struct sr_dev_inst *sdi)
-{
-       struct dev_context *devc;
-       struct sr_usb_dev_inst *usb;
-       struct sr_trigger *trigger;
-       struct libusb_transfer *transfer;
-       unsigned int i, num_transfers;
-       int endpoint, timeout, ret;
-       unsigned char *buf;
-       size_t size;
-
-       devc = sdi->priv;
-       usb = sdi->conn;
-
-       devc->sent_samples = 0;
-       devc->acq_aborted = FALSE;
-       devc->empty_transfer_count = 0;
-
-       if ((trigger = sr_session_trigger_get(sdi->session)) && !devc->dslogic) {
-               int pre_trigger_samples = 0;
-               if (devc->limit_samples > 0)
-                       pre_trigger_samples = devc->capture_ratio * devc->limit_samples/100;
-               devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
-               if (!devc->stl)
-                       return SR_ERR_MALLOC;
-               devc->trigger_fired = FALSE;
-       } else
-               devc->trigger_fired = TRUE;
-
-       num_transfers = fx2lafw_get_number_of_transfers(devc);
-
-       //if (devc->dslogic)
-       //      num_transfers = dslogic_get_number_of_transfers(devc);
-
-       if (devc->dslogic) {
-               if (devc->cur_samplerate == SR_MHZ(100))
-                       num_transfers = 16;
-               else if (devc->cur_samplerate == SR_MHZ(200))
-                       num_transfers = 8;
-               else if (devc->cur_samplerate == SR_MHZ(400))
-                       num_transfers = 4;
-       }
-
-       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;
-       }
-
-       timeout = fx2lafw_get_timeout(devc);
-       endpoint = devc->dslogic ? 6 : 2;
-       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,
-                               endpoint | LIBUSB_ENDPOINT_IN, buf, size,
-                               fx2lafw_receive_transfer, (void *)sdi, timeout);
-               sr_info("submitting transfer: %d", i);
-               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++;
-       }
-
-       /*
-        * If this device has analog channels and at least one of them is
-        * enabled, use mso_send_data_proc() to properly handle the analog
-        * data. Otherwise use la_send_data_proc().
-        */
-       if (g_slist_length(devc->enabled_analog_channels) > 0)
-               devc->send_data_proc = mso_send_data_proc;
-       else
-               devc->send_data_proc = la_send_data_proc;
-
-       std_session_send_df_header(sdi);
-
-       return SR_OK;
-}
-
-static void LIBUSB_CALL dslogic_trigger_receive(struct libusb_transfer *transfer)
-{
-       const struct sr_dev_inst *sdi;
-       struct dslogic_trigger_pos *tpos;
-       struct dev_context *devc;
-
-       sdi = transfer->user_data;
-       devc = sdi->priv;
-       if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
-               sr_dbg("Trigger transfer canceled.");
-               /* Terminate session. */
-               std_session_send_df_end(sdi);
-               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;
-               }
-       } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED
-                       && transfer->actual_length == sizeof(struct dslogic_trigger_pos)) {
-               tpos = (struct dslogic_trigger_pos *)transfer->buffer;
-               sr_info("tpos real_pos %d ram_saddr %d cnt %d", tpos->real_pos,
-                       tpos->ram_saddr, tpos->remain_cnt);
-               devc->trigger_pos = tpos->real_pos;
-               g_free(tpos);
-               start_transfers(sdi);
-       }
-       libusb_free_transfer(transfer);
-}
-
-static int dslogic_trigger_request(const struct sr_dev_inst *sdi)
-{
-       struct sr_usb_dev_inst *usb;
-       struct libusb_transfer *transfer;
-       struct dslogic_trigger_pos *tpos;
-       struct dev_context *devc;
-       int ret;
-
-       usb = sdi->conn;
-       devc = sdi->priv;
-
-       if ((ret = dslogic_stop_acquisition(sdi)) != SR_OK)
-               return ret;
-
-       if ((ret = dslogic_fpga_configure(sdi)) != SR_OK)
-               return ret;
-
-       /* If this is a DSLogic Pro, set the voltage threshold. */
-       if (!strcmp(devc->profile->model, "DSLogic Pro")){
-               if (devc->dslogic_voltage_threshold == DS_VOLTAGE_RANGE_18_33_V) {
-                       dslogic_set_vth(sdi, 1.4);
-               } else {
-                       dslogic_set_vth(sdi, 3.3);
-               }
-       }
-
-       if ((ret = dslogic_start_acquisition(sdi)) != SR_OK)
-               return ret;
-
-       sr_dbg("Getting trigger.");
-       tpos = g_malloc(sizeof(struct dslogic_trigger_pos));
-       transfer = libusb_alloc_transfer(0);
-       libusb_fill_bulk_transfer(transfer, usb->devhdl, 6 | LIBUSB_ENDPOINT_IN,
-                       (unsigned char *)tpos, sizeof(struct dslogic_trigger_pos),
-                       dslogic_trigger_receive, (void *)sdi, 0);
-       if ((ret = libusb_submit_transfer(transfer)) < 0) {
-               sr_err("Failed to request trigger: %s.", libusb_error_name(ret));
-               libusb_free_transfer(transfer);
-               g_free(tpos);
-               return SR_ERR;
-       }
-
-       devc->transfers = g_try_malloc0(sizeof(*devc->transfers));
-       if (!devc->transfers) {
-               sr_err("USB trigger_pos transfer malloc failed.");
-               return SR_ERR_MALLOC;
-       }
-       devc->num_transfers = 1;
-       devc->submitted_transfers++;
-       devc->transfers[0] = transfer;
-
-       return ret;
-}
-
-static int configure_channels(const struct sr_dev_inst *sdi)
-{
-       struct dev_context *devc;
-       const GSList *l;
-       int p;
-       struct sr_channel *ch;
-       uint32_t channel_mask = 0, num_analog = 0;
-
-       devc = sdi->priv;
-
-       g_slist_free(devc->enabled_analog_channels);
-       devc->enabled_analog_channels = NULL;
-
-       for (l = sdi->channels, p = 0; l; l = l->next, p++) {
-               ch = l->data;
-               if ((p <= NUM_CHANNELS) && (ch->type == SR_CHANNEL_ANALOG)
-                               && (ch->enabled)) {
-                       num_analog++;
-                       devc->enabled_analog_channels =
-                           g_slist_append(devc->enabled_analog_channels, ch);
-               } else {
-                       channel_mask |= ch->enabled << p;
-               }
-       }
-
-       /*
-        * Use wide sampling if either any of the LA channels 8..15 is enabled,
-        * and/or at least one analog channel is enabled, and/or the device
-        * is running DSLogic firmware (not fx2lafw).
-        */
-       devc->sample_wide = (channel_mask > 0xff
-                       || num_analog > 0
-                       || (devc->profile->dev_caps & DEV_CAPS_DSLOGIC_FW));
-
-       return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi)
-{
-       struct sr_dev_driver *di;
-       struct drv_context *drvc;
-       struct dev_context *devc;
-       int timeout, ret;
-       size_t size;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       di = sdi->driver;
-       drvc = di->context;
-       devc = sdi->priv;
-
-       devc->ctx = drvc->sr_ctx;
-       devc->sent_samples = 0;
-       devc->empty_transfer_count = 0;
-       devc->acq_aborted = FALSE;
-
-       if (configure_channels(sdi) != SR_OK) {
-               sr_err("Failed to configure channels.");
-               return SR_ERR;
-       }
-
-       timeout = fx2lafw_get_timeout(devc);
-       usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc);
-
-       if (devc->dslogic) {
-               dslogic_trigger_request(sdi);
-       } else {
-               size = fx2lafw_get_buffer_size(devc);
-               /* Prepare for analog sampling. */
-               if (g_slist_length(devc->enabled_analog_channels) > 0) {
-                       /* We need a buffer half the size of a transfer. */
-                       devc->logic_buffer = g_try_malloc(size / 2);
-                       devc->analog_buffer = g_try_malloc(
-                               sizeof(float) * size / 2);
-               }
-               start_transfers(sdi);
-               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)
 {
-       struct dev_context *devc;
-
-       devc = sdi->priv;
-
-       if (devc->dslogic)
-               dslogic_stop_acquisition(sdi);
-
        fx2lafw_abort_acquisition(sdi->priv);
 
        return SR_OK;
@@ -1092,7 +587,7 @@ static struct sr_dev_driver fx2lafw_driver_info = {
        .config_list = config_list,
        .dev_open = dev_open,
        .dev_close = dev_close,
-       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_start = fx2lafw_start_acquisition,
        .dev_acquisition_stop = dev_acquisition_stop,
        .context = NULL,
 };
diff --git a/src/hardware/fx2lafw/dslogic.c b/src/hardware/fx2lafw/dslogic.c
deleted file mode 100644 (file)
index a3c45be..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * 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 <config.h>
-#include <math.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include "protocol.h"
-#include "dslogic.h"
-
-/*
- * This should be larger than the FPGA bitstream image so that it'll get
- * uploaded in one big operation. There seem to be issues when uploading
- * it in chunks.
- */
-#define FW_BUFSIZE (1024 * 1024)
-
-#define FPGA_UPLOAD_DELAY (10 * 1000)
-
-#define USB_TIMEOUT (3 * 1000)
-
-SR_PRIV int dslogic_set_vth(const struct sr_dev_inst *sdi, double vth)
-{
-       struct sr_usb_dev_inst *usb;
-       int ret;
-       uint8_t cmd;
-
-       usb = sdi->conn;
-
-       cmd = (vth / 5.0) * 255;
-
-       /* Send the control command. */
-       ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
-                       LIBUSB_ENDPOINT_OUT, DS_CMD_VTH, 0x0000, 0x0000,
-                       (unsigned char *)&cmd, sizeof(cmd), 3000);
-       if (ret < 0) {
-               sr_err("Unable to send VTH command: %s.",
-               libusb_error_name(ret));
-               return SR_ERR;
-       }
-
-       return SR_OK;
-}
-
-SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi,
-               const char *name)
-{
-       uint64_t sum;
-       struct sr_resource bitstream;
-       struct drv_context *drvc;
-       struct sr_usb_dev_inst *usb;
-       unsigned char *buf;
-       ssize_t chunksize;
-       int transferred;
-       int result, ret;
-       uint8_t cmd[3];
-
-       drvc = sdi->driver->context;
-       usb = sdi->conn;
-
-       sr_dbg("Uploading FPGA firmware '%s'.", name);
-
-       result = sr_resource_open(drvc->sr_ctx, &bitstream,
-                       SR_RESOURCE_FIRMWARE, name);
-       if (result != SR_OK)
-               return result;
-
-       /* Tell the device firmware is coming. */
-       memset(cmd, 0, sizeof(cmd));
-       if ((ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
-                       LIBUSB_ENDPOINT_OUT, DS_CMD_FPGA_FW, 0x0000, 0x0000,
-                       (unsigned char *)&cmd, sizeof(cmd), USB_TIMEOUT)) < 0) {
-               sr_err("Failed to upload FPGA firmware: %s.", libusb_error_name(ret));
-               sr_resource_close(drvc->sr_ctx, &bitstream);
-               return SR_ERR;
-       }
-
-       /* Give the FX2 time to get ready for FPGA firmware upload. */
-       g_usleep(FPGA_UPLOAD_DELAY);
-
-       buf = g_malloc(FW_BUFSIZE);
-       sum = 0;
-       result = SR_OK;
-       while (1) {
-               chunksize = sr_resource_read(drvc->sr_ctx, &bitstream,
-                               buf, FW_BUFSIZE);
-               if (chunksize < 0)
-                       result = SR_ERR;
-               if (chunksize <= 0)
-                       break;
-
-               if ((ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT,
-                               buf, chunksize, &transferred, USB_TIMEOUT)) < 0) {
-                       sr_err("Unable to configure FPGA firmware: %s.",
-                                       libusb_error_name(ret));
-                       result = SR_ERR;
-                       break;
-               }
-               sum += transferred;
-               sr_spew("Uploaded %" PRIu64 "/%" PRIu64 " bytes.",
-                       sum, bitstream.size);
-
-               if (transferred != chunksize) {
-                       sr_err("Short transfer while uploading FPGA firmware.");
-                       result = SR_ERR;
-                       break;
-               }
-       }
-       g_free(buf);
-       sr_resource_close(drvc->sr_ctx, &bitstream);
-
-       if (result == SR_OK)
-               sr_dbg("FPGA firmware upload done.");
-
-       return result;
-}
-
-SR_PRIV int dslogic_start_acquisition(const struct sr_dev_inst *sdi)
-{
-       struct dev_context *devc;
-       struct sr_usb_dev_inst *usb;
-       struct dslogic_mode mode;
-       int ret;
-
-       devc = sdi->priv;
-       mode.flags = DS_START_FLAGS_MODE_LA;
-       mode.sample_delay_h = mode.sample_delay_l = 0;
-       if (devc->sample_wide)
-               mode.flags |= DS_START_FLAGS_SAMPLE_WIDE;
-
-       usb = sdi->conn;
-       ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
-                       LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000,
-                       (unsigned char *)&mode, sizeof(mode), USB_TIMEOUT);
-       if (ret < 0) {
-               sr_err("Failed to send start command: %s.", libusb_error_name(ret));
-               return SR_ERR;
-       }
-
-       return SR_OK;
-}
-
-SR_PRIV int dslogic_stop_acquisition(const struct sr_dev_inst *sdi)
-{
-       struct sr_usb_dev_inst *usb;
-       struct dslogic_mode mode;
-       int ret;
-
-       mode.flags = DS_START_FLAGS_STOP;
-       mode.sample_delay_h = mode.sample_delay_l = 0;
-
-       usb = sdi->conn;
-       ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
-                       LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000,
-                       (unsigned char *)&mode, sizeof(struct dslogic_mode), USB_TIMEOUT);
-       if (ret < 0) {
-               sr_err("Failed to send stop command: %s.", libusb_error_name(ret));
-               return SR_ERR;
-       }
-
-       return SR_OK;
-}
-
-/*
- * Get the session trigger and configure the FPGA structure
- * accordingly.
- */
-static int dslogic_set_trigger(const struct sr_dev_inst *sdi,
-       struct dslogic_fpga_config *cfg)
-{
-       struct sr_trigger *trigger;
-       struct sr_trigger_stage *stage;
-       struct sr_trigger_match *match;
-       struct dev_context *devc;
-       const GSList *l, *m;
-       int channelbit, i = 0;
-       uint16_t v16;
-
-       devc = sdi->priv;
-
-       cfg->trig_mask0[0] = 0xffff;
-       cfg->trig_mask1[0] = 0xffff;
-
-       cfg->trig_value0[0] = 0;
-       cfg->trig_value1[0] = 0;
-
-       cfg->trig_edge0[0] = 0;
-       cfg->trig_edge1[0] = 0;
-
-       cfg->trig_logic0[0] = 0;
-       cfg->trig_logic1[0] = 0;
-
-       cfg->trig_count0[0] = 0;
-       cfg->trig_count1[0] = 0;
-
-       cfg->trig_pos = 0;
-       cfg->trig_sda = 0;
-       cfg->trig_glb = 0;
-       cfg->trig_adp = cfg->count - cfg->trig_pos - 1;
-
-       for (i = 1; i < 16; i++) {
-               cfg->trig_mask0[i] = 0xff;
-               cfg->trig_mask1[i] = 0xff;
-               cfg->trig_value0[i] = 0;
-               cfg->trig_value1[i] = 0;
-               cfg->trig_edge0[i] = 0;
-               cfg->trig_edge1[i] = 0;
-               cfg->trig_count0[i] = 0;
-               cfg->trig_count1[i] = 0;
-               cfg->trig_logic0[i] = 2;
-               cfg->trig_logic1[i] = 2;
-       }
-
-       cfg->trig_pos = (uint32_t)(devc->capture_ratio / 100.0 * devc->limit_samples);
-       sr_dbg("pos: %d", cfg->trig_pos);
-
-       sr_dbg("configuring trigger");
-
-       if (!(trigger = sr_session_trigger_get(sdi->session))) {
-               sr_dbg("No session trigger found");
-               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;
-                       if (!match->channel->enabled)
-                               /* Ignore disabled channels with a trigger. */
-                               continue;
-                       channelbit = 1 << (match->channel->index);
-                       /* Simple trigger support (event). */
-                       if (match->match == SR_TRIGGER_ONE) {
-                               cfg->trig_mask0[0] &= ~channelbit;
-                               cfg->trig_mask1[0] &= ~channelbit;
-                               cfg->trig_value0[0] |= channelbit;
-                               cfg->trig_value1[0] |= channelbit;
-                       } else if (match->match == SR_TRIGGER_ZERO) {
-                               cfg->trig_mask0[0] &= ~channelbit;
-                               cfg->trig_mask1[0] &= ~channelbit;
-                       } else if (match->match == SR_TRIGGER_FALLING) {
-                               cfg->trig_mask0[0] &= ~channelbit;
-                               cfg->trig_mask1[0] &= ~channelbit;
-                               cfg->trig_edge0[0] |= channelbit;
-                               cfg->trig_edge1[0] |= channelbit;
-                       } else if (match->match == SR_TRIGGER_RISING) {
-                               cfg->trig_mask0[0] &= ~channelbit;
-                               cfg->trig_mask1[0] &= ~channelbit;
-                               cfg->trig_value0[0] |= channelbit;
-                               cfg->trig_value1[0] |= channelbit;
-                               cfg->trig_edge0[0] |= channelbit;
-                               cfg->trig_edge1[0] |= channelbit;
-                       } else if (match->match == SR_TRIGGER_EDGE) {
-                               cfg->trig_edge0[0] |= channelbit;
-                               cfg->trig_edge1[0] |= channelbit;
-                       }
-               }
-       }
-
-       v16 = RL16(&cfg->mode);
-       v16 |= 1 << 0;
-       WL16(&cfg->mode, v16);
-
-       return SR_OK;
-}
-
-SR_PRIV int dslogic_fpga_configure(const struct sr_dev_inst *sdi)
-{
-       struct dev_context *devc;
-       struct sr_usb_dev_inst *usb;
-       uint8_t c[3];
-       struct dslogic_fpga_config cfg;
-       uint16_t v16;
-       uint32_t v32;
-       int transferred, len, ret;
-
-       sr_dbg("Configuring FPGA.");
-
-       usb = sdi->conn;
-       devc = sdi->priv;
-
-       WL32(&cfg.sync, DS_CFG_START);
-       WL16(&cfg.mode_header, DS_CFG_MODE);
-       WL32(&cfg.divider_header, DS_CFG_DIVIDER);
-       WL32(&cfg.count_header, DS_CFG_COUNT);
-       WL32(&cfg.trig_pos_header, DS_CFG_TRIG_POS);
-       WL16(&cfg.trig_glb_header, DS_CFG_TRIG_GLB);
-       WL32(&cfg.trig_adp_header, DS_CFG_TRIG_ADP);
-       WL32(&cfg.trig_sda_header, DS_CFG_TRIG_SDA);
-       WL32(&cfg.trig_mask0_header, DS_CFG_TRIG_MASK0);
-       WL32(&cfg.trig_mask1_header, DS_CFG_TRIG_MASK1);
-       WL32(&cfg.trig_value0_header, DS_CFG_TRIG_VALUE0);
-       WL32(&cfg.trig_value1_header, DS_CFG_TRIG_VALUE1);
-       WL32(&cfg.trig_edge0_header, DS_CFG_TRIG_EDGE0);
-       WL32(&cfg.trig_edge1_header, DS_CFG_TRIG_EDGE1);
-       WL32(&cfg.trig_count0_header, DS_CFG_TRIG_COUNT0);
-       WL32(&cfg.trig_count1_header, DS_CFG_TRIG_COUNT1);
-       WL32(&cfg.trig_logic0_header, DS_CFG_TRIG_LOGIC0);
-       WL32(&cfg.trig_logic1_header, DS_CFG_TRIG_LOGIC1);
-       WL32(&cfg.end_sync, DS_CFG_END);
-
-       /* Pass in the length of a fixed-size struct. Really. */
-       len = sizeof(struct dslogic_fpga_config) / 2;
-       c[0] = len & 0xff;
-       c[1] = (len >> 8) & 0xff;
-       c[2] = (len >> 16) & 0xff;
-
-       ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
-                       LIBUSB_ENDPOINT_OUT, DS_CMD_CONFIG, 0x0000, 0x0000,
-                       c, 3, USB_TIMEOUT);
-       if (ret < 0) {
-               sr_err("Failed to send FPGA configure command: %s.",
-                       libusb_error_name(ret));
-               return SR_ERR;
-       }
-
-       /*
-        * 15   1 = internal test mode
-        * 14   1 = external test mode
-        * 13   1 = loopback test mode
-        * 12   1 = stream mode
-        * 11   1 = serial trigger
-        * 8-10 unused
-        * 7    1 = analog mode
-        * 6    1 = samplerate 400MHz
-        * 5    1 = samplerate 200MHz or analog mode
-        * 4    0 = logic, 1 = dso or analog
-        * 3    1 = RLE encoding (enable for more than 16 Megasamples)
-        * 1-2  00 = internal clock,
-        *      01 = external clock rising,
-        *      11 = external clock falling
-        * 0    1 = trigger enabled
-        */
-       v16 = 0x0000;
-       if (devc->dslogic_mode == DS_OP_INTERNAL_TEST)
-               v16 = 1 << 15;
-       else if (devc->dslogic_mode == DS_OP_EXTERNAL_TEST)
-               v16 = 1 << 14;
-       else if (devc->dslogic_mode == DS_OP_LOOPBACK_TEST)
-               v16 = 1 << 13;
-       if (devc->dslogic_continuous_mode)
-               v16 |= 1 << 12;
-       if (devc->dslogic_external_clock) {
-               v16 |= 1 << 1;
-               if (devc->dslogic_clock_edge == DS_EDGE_FALLING)
-                       v16 |= 1 << 2;
-       }
-       if (devc->limit_samples > DS_MAX_LOGIC_DEPTH *
-               ceil(devc->cur_samplerate * 1.0 / DS_MAX_LOGIC_SAMPLERATE)
-               && !devc->dslogic_continuous_mode) {
-               /* Enable RLE for long captures.
-                * Without this, captured data present errors.
-                */
-               v16 |= 1 << 3;
-       }
-
-       WL16(&cfg.mode, v16);
-       v32 = ceil(DS_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate);
-       WL32(&cfg.divider, v32);
-       WL32(&cfg.count, devc->limit_samples);
-
-       dslogic_set_trigger(sdi, &cfg);
-
-       len = sizeof(struct dslogic_fpga_config);
-       ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT,
-                       (unsigned char *)&cfg, len, &transferred, USB_TIMEOUT);
-       if (ret < 0 || transferred != len) {
-               sr_err("Failed to send FPGA configuration: %s.", libusb_error_name(ret));
-               return SR_ERR;
-       }
-
-       return SR_OK;
-}
-
-static int to_bytes_per_ms(struct dev_context *devc)
-{
-       if (devc->cur_samplerate > SR_MHZ(100))
-               return SR_MHZ(100) / 1000 * (devc->sample_wide ? 2 : 1);
-
-       return devc->cur_samplerate / 1000 * (devc->sample_wide ? 2 : 1);
-}
-
-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 * to_bytes_per_ms(devc);
-       // s = to_bytes_per_ms(devc->cur_samplerate);
-       return (s + 511) & ~511;
-}
-
-SR_PRIV int dslogic_get_number_of_transfers(struct dev_context *devc)
-{
-       unsigned int n;
-
-       /* Total buffer size should be able to hold about 100ms of data. */
-       n = (100 * to_bytes_per_ms(devc) / get_buffer_size(devc));
-       sr_info("New calculation: %d", n);
-
-       if (n > NUM_SIMUL_TRANSFERS)
-               return NUM_SIMUL_TRANSFERS;
-
-       return n;
-}
diff --git a/src/hardware/fx2lafw/dslogic.h b/src/hardware/fx2lafw/dslogic.h
deleted file mode 100644 (file)
index f74367a..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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_DSLOGIC_H
-#define LIBSIGROK_HARDWARE_FX2LAFW_DSLOGIC_H
-
-/* Modified protocol commands & flags used by DSLogic */
-#define DS_CMD_GET_FW_VERSION          0xb0
-#define DS_CMD_GET_REVID_VERSION       0xb1
-#define DS_CMD_START                   0xb2
-#define DS_CMD_FPGA_FW                 0xb3
-#define DS_CMD_CONFIG                  0xb4
-#define DS_CMD_VTH                     0xb8
-
-#define DS_NUM_TRIGGER_STAGES          16
-#define DS_START_FLAGS_STOP            (1 << 7)
-#define DS_START_FLAGS_CLK_48MHZ       (1 << 6)
-#define DS_START_FLAGS_SAMPLE_WIDE     (1 << 5)
-#define DS_START_FLAGS_MODE_LA         (1 << 4)
-
-#define DS_MAX_LOGIC_DEPTH             SR_MHZ(16)
-#define DS_MAX_LOGIC_SAMPLERATE                SR_MHZ(100)
-
-enum dslogic_operation_modes {
-       DS_OP_NORMAL,
-       DS_OP_INTERNAL_TEST,
-       DS_OP_EXTERNAL_TEST,
-       DS_OP_LOOPBACK_TEST,
-};
-
-enum {
-       DS_VOLTAGE_RANGE_18_33_V,       /* 1.8V and 3.3V logic */
-       DS_VOLTAGE_RANGE_5_V,           /* 5V logic */
-};
-
-enum {
-       DS_EDGE_RISING,
-       DS_EDGE_FALLING,
-};
-
-struct dslogic_version {
-       uint8_t major;
-       uint8_t minor;
-};
-
-struct dslogic_mode {
-       uint8_t flags;
-       uint8_t sample_delay_h;
-       uint8_t sample_delay_l;
-};
-
-struct dslogic_trigger_pos {
-       uint32_t real_pos;
-       uint32_t ram_saddr;
-       uint32_t remain_cnt;
-       uint8_t first_block[500];
-};
-
-/*
- * The FPGA is configured with TLV tuples. Length is specified as the
- * number of 16-bit words, and the (type, length) header is in some
- * cases padded with 0xffff.
- */
-#define _DS_CFG(variable, wordcnt) ((variable << 8) | wordcnt)
-#define _DS_CFG_PAD(variable, wordcnt) ((_DS_CFG(variable, wordcnt) << 16) | 0xffff)
-#define DS_CFG_START           0xf5a5f5a5
-#define DS_CFG_MODE            _DS_CFG(0, 1)
-#define DS_CFG_DIVIDER         _DS_CFG_PAD(1, 2)
-#define DS_CFG_COUNT           _DS_CFG_PAD(3, 2)
-#define DS_CFG_TRIG_POS                _DS_CFG_PAD(5, 2)
-#define DS_CFG_TRIG_GLB                _DS_CFG(7, 1)
-#define DS_CFG_TRIG_ADP                _DS_CFG_PAD(10, 2)
-#define DS_CFG_TRIG_SDA                _DS_CFG_PAD(12, 2)
-#define DS_CFG_TRIG_MASK0      _DS_CFG_PAD(16, 16)
-#define DS_CFG_TRIG_MASK1      _DS_CFG_PAD(17, 16)
-#define DS_CFG_TRIG_VALUE0     _DS_CFG_PAD(20, 16)
-#define DS_CFG_TRIG_VALUE1     _DS_CFG_PAD(21, 16)
-#define DS_CFG_TRIG_EDGE0      _DS_CFG_PAD(24, 16)
-#define DS_CFG_TRIG_EDGE1      _DS_CFG_PAD(25, 16)
-#define DS_CFG_TRIG_COUNT0     _DS_CFG_PAD(28, 16)
-#define DS_CFG_TRIG_COUNT1     _DS_CFG_PAD(29, 16)
-#define DS_CFG_TRIG_LOGIC0     _DS_CFG_PAD(32, 16)
-#define DS_CFG_TRIG_LOGIC1     _DS_CFG_PAD(33, 16)
-#define DS_CFG_END             0xfa5afa5a
-
-struct dslogic_fpga_config {
-       uint32_t sync;
-       uint16_t mode_header;
-       uint16_t mode;
-       uint32_t divider_header;
-       uint32_t divider;
-       uint32_t count_header;
-       uint32_t count;
-       uint32_t trig_pos_header;
-       uint32_t trig_pos;
-       uint16_t trig_glb_header;
-       uint16_t trig_glb;
-       uint32_t trig_adp_header;
-       uint32_t trig_adp;
-       uint32_t trig_sda_header;
-       uint32_t trig_sda;
-       uint32_t trig_mask0_header;
-       uint16_t trig_mask0[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_mask1_header;
-       uint16_t trig_mask1[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_value0_header;
-       uint16_t trig_value0[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_value1_header;
-       uint16_t trig_value1[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_edge0_header;
-       uint16_t trig_edge0[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_edge1_header;
-       uint16_t trig_edge1[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_count0_header;
-       uint16_t trig_count0[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_count1_header;
-       uint16_t trig_count1[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_logic0_header;
-       uint16_t trig_logic0[DS_NUM_TRIGGER_STAGES];
-       uint32_t trig_logic1_header;
-       uint16_t trig_logic1[DS_NUM_TRIGGER_STAGES];
-       uint32_t end_sync;
-};
-
-SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi,
-               const char *name);
-SR_PRIV int dslogic_start_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int dslogic_stop_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int dslogic_fpga_configure(const struct sr_dev_inst *sdi);
-SR_PRIV int dslogic_set_vth(const struct sr_dev_inst *sdi, double vth);
-SR_PRIV int dslogic_get_number_of_transfers(struct dev_context *devc);
-
-#endif
index e2112af13abc6d96a0f1893f60451d0e04e22d72..ec2b5c011b27ab94add03bfde6e211c2f76ef3f9 100644 (file)
@@ -22,7 +22,6 @@
 #include <glib.h>
 #include <glib/gstdio.h>
 #include "protocol.h"
-#include "dslogic.h"
 
 #pragma pack(push, 1)
 
@@ -61,14 +60,13 @@ static int command_get_fw_version(libusb_device_handle *devhdl,
 
 static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid)
 {
-       struct dev_context *devc = sdi->priv;
        struct sr_usb_dev_inst *usb = sdi->conn;
        libusb_device_handle *devhdl = usb->devhdl;
-       int cmd, ret;
+       int ret;
 
-       cmd = devc->dslogic ? DS_CMD_GET_REVID_VERSION : CMD_GET_REVID_VERSION;
        ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
-               LIBUSB_ENDPOINT_IN, cmd, 0x0000, 0x0000, revid, 1, USB_TIMEOUT);
+               LIBUSB_ENDPOINT_IN, CMD_GET_REVID_VERSION, 0x0000, 0x0000,
+               revid, 1, USB_TIMEOUT);
 
        if (ret < 0) {
                sr_err("Unable to get REVID: %s.", libusb_error_name(ret));
@@ -78,7 +76,7 @@ static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid)
        return SR_OK;
 }
 
-SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
+static int command_start_acquisition(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
@@ -141,49 +139,6 @@ SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-/**
- * Check the USB configuration to determine if this is an fx2lafw device.
- *
- * @return TRUE if the device's configuration profile matches fx2lafw
- *         configuration, FALSE otherwise.
- */
-SR_PRIV gboolean match_manuf_prod(libusb_device *dev, const char *manufacturer,
-               const char *product)
-{
-       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. */
-               libusb_get_device_descriptor(dev, &des);
-
-               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, manufacturer))
-                       break;
-
-               if (libusb_get_string_descriptor_ascii(hdl,
-                               des.iProduct, strdesc, sizeof(strdesc)) < 0)
-                       break;
-               if (strcmp((const char *)strdesc, product))
-                       break;
-
-               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;
@@ -192,7 +147,7 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
        struct dev_context *devc;
        struct drv_context *drvc;
        struct version_info vi;
-       int ret, i, device_count;
+       int ret = SR_ERR, i, device_count;
        uint8_t revid;
        char connection_id[64];
 
@@ -200,10 +155,6 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
        devc = sdi->priv;
        usb = sdi->conn;
 
-       if (sdi->status == SR_ST_ACTIVE)
-               /* Device is already in use. */
-               return SR_ERR;
-
        device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
        if (device_count < 0) {
                sr_err("Failed to get device list: %s.",
@@ -223,7 +174,9 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                        /*
                         * Check device by its physical USB bus/port address.
                         */
-                       usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+                       if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                               continue;
+
                        if (strcmp(sdi->connection_id, connection_id))
                                /* This is not the one. */
                                continue;
@@ -239,6 +192,7 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                } else {
                        sr_err("Failed to open device: %s.",
                               libusb_error_name(ret));
+                       ret = SR_ERR;
                        break;
                }
 
@@ -247,7 +201,8 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                                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;
+                                       ret = SR_ERR;
+                                       break;
                                }
                        }
                }
@@ -276,7 +231,6 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                        break;
                }
 
-               sdi->status = SR_ST_ACTIVE;
                sr_info("Opened device on %d.%d (logical) / %s (physical), "
                        "interface %d, firmware %d.%d.",
                        usb->bus, usb->address, connection_id,
@@ -285,14 +239,14 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.",
                        revid, (revid != 1) ? " (FX2)" : "A (FX2LP)");
 
+               ret = SR_OK;
+
                break;
        }
-       libusb_free_device_list(devlist, 1);
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR;
+       libusb_free_device_list(devlist, 1);
 
-       return SR_OK;
+       return ret;
 }
 
 SR_PRIV struct dev_context *fx2lafw_dev_new(void)
@@ -306,8 +260,6 @@ SR_PRIV struct dev_context *fx2lafw_dev_new(void)
        devc->limit_samples = 0;
        devc->capture_ratio = 0;
        devc->sample_wide = FALSE;
-       devc->dslogic_continuous_mode = FALSE;
-       devc->dslogic_clock_edge = DS_EDGE_RISING;
        devc->stl = NULL;
 
        return devc;
@@ -387,7 +339,7 @@ static void resubmit_transfer(struct libusb_transfer *transfer)
 
 }
 
-SR_PRIV void mso_send_data_proc(struct sr_dev_inst *sdi,
+static void mso_send_data_proc(struct sr_dev_inst *sdi,
        uint8_t *data, size_t length, size_t sample_width)
 {
        size_t i;
@@ -439,7 +391,7 @@ SR_PRIV void mso_send_data_proc(struct sr_dev_inst *sdi,
        sr_session_send(sdi, &analog_packet);
 }
 
-SR_PRIV void la_send_data_proc(struct sr_dev_inst *sdi,
+static void la_send_data_proc(struct sr_dev_inst *sdi,
        uint8_t *data, size_t length, size_t sample_width)
 {
        const struct sr_datafeed_logic logic = {
@@ -456,12 +408,11 @@ SR_PRIV void la_send_data_proc(struct sr_dev_inst *sdi,
        sr_session_send(sdi, &packet);
 }
 
-SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transfer)
+static void LIBUSB_CALL 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;
        unsigned int num_samples;
        int trigger_offset, cur_sample_count, unitsize;
        int pre_trigger_samples;
@@ -522,29 +473,9 @@ SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transf
                        else
                                num_samples = cur_sample_count;
 
-                       if (devc->dslogic && devc->trigger_pos > devc->sent_samples
-                               && devc->trigger_pos <= devc->sent_samples + num_samples) {
-                                       /* DSLogic trigger in this block. Send trigger position. */
-                                       trigger_offset = devc->trigger_pos - devc->sent_samples;
-                                       /* Pre-trigger samples. */
-                                       devc->send_data_proc(sdi, (uint8_t *)transfer->buffer,
-                                               trigger_offset * unitsize, unitsize);
-                                       devc->sent_samples += trigger_offset;
-                                       /* Trigger position. */
-                                       devc->trigger_pos = 0;
-                                       packet.type = SR_DF_TRIGGER;
-                                       packet.payload = NULL;
-                                       sr_session_send(sdi, &packet);
-                                       /* Post trigger samples. */
-                                       num_samples -= trigger_offset;
-                                       devc->send_data_proc(sdi, (uint8_t *)transfer->buffer
-                                                       + trigger_offset * unitsize, num_samples * unitsize, unitsize);
-                                       devc->sent_samples += num_samples;
-                       } else {
-                               devc->send_data_proc(sdi, (uint8_t *)transfer->buffer,
-                                       num_samples * unitsize, unitsize);
-                               devc->sent_samples += num_samples;
-                       }
+                       devc->send_data_proc(sdi, (uint8_t *)transfer->buffer,
+                               num_samples * unitsize, unitsize);
+                       devc->sent_samples += num_samples;
                }
        } else {
                trigger_offset = soft_trigger_logic_check(devc->stl,
@@ -572,12 +503,46 @@ SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transf
                resubmit_transfer(transfer);
 }
 
+static int configure_channels(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       const GSList *l;
+       int p;
+       struct sr_channel *ch;
+       uint32_t channel_mask = 0, num_analog = 0;
+
+       devc = sdi->priv;
+
+       g_slist_free(devc->enabled_analog_channels);
+       devc->enabled_analog_channels = NULL;
+
+       for (l = sdi->channels, p = 0; l; l = l->next, p++) {
+               ch = l->data;
+               if ((p <= NUM_CHANNELS) && (ch->type == SR_CHANNEL_ANALOG)
+                               && (ch->enabled)) {
+                       num_analog++;
+                       devc->enabled_analog_channels =
+                           g_slist_append(devc->enabled_analog_channels, ch);
+               } else {
+                       channel_mask |= ch->enabled << p;
+               }
+       }
+
+       /*
+        * Use wide sampling if either any of the LA channels 8..15 is enabled,
+        * and/or at least one analog channel is enabled.
+        */
+       devc->sample_wide = channel_mask > 0xff || num_analog > 0;
+
+       return SR_OK;
+}
+
 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)
+static size_t get_buffer_size(struct dev_context *devc)
 {
        size_t s;
 
@@ -589,13 +554,13 @@ SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc)
        return (s + 511) & ~511;
 }
 
-SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc)
+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 * to_bytes_per_ms(devc->cur_samplerate) /
-               fx2lafw_get_buffer_size(devc));
+               get_buffer_size(devc));
 
        if (n > NUM_SIMUL_TRANSFERS)
                return NUM_SIMUL_TRANSFERS;
@@ -603,13 +568,150 @@ SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc)
        return n;
 }
 
-SR_PRIV unsigned int fx2lafw_get_timeout(struct dev_context *devc)
+static unsigned int 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);
+       total_size = get_buffer_size(devc) *
+                       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. */
 }
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+       struct timeval tv;
+       struct drv_context *drvc;
+
+       (void)fd;
+       (void)revents;
+
+       drvc = (struct drv_context *)cb_data;
+
+       tv.tv_sec = tv.tv_usec = 0;
+       libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+       return TRUE;
+}
+
+static int start_transfers(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       struct sr_trigger *trigger;
+       struct libusb_transfer *transfer;
+       unsigned int i, num_transfers;
+       int timeout, ret;
+       unsigned char *buf;
+       size_t size;
+
+       devc = sdi->priv;
+       usb = sdi->conn;
+
+       devc->sent_samples = 0;
+       devc->acq_aborted = FALSE;
+       devc->empty_transfer_count = 0;
+
+       if ((trigger = sr_session_trigger_get(sdi->session))) {
+               int pre_trigger_samples = 0;
+               if (devc->limit_samples > 0)
+                       pre_trigger_samples = (devc->capture_ratio * devc->limit_samples) / 100;
+               devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
+               if (!devc->stl)
+                       return SR_ERR_MALLOC;
+               devc->trigger_fired = FALSE;
+       } else
+               devc->trigger_fired = TRUE;
+
+       num_transfers = get_number_of_transfers(devc);
+
+       size = 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;
+       }
+
+       timeout = get_timeout(devc);
+       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,
+                               receive_transfer, (void *)sdi, timeout);
+               sr_info("submitting transfer: %d", i);
+               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++;
+       }
+
+       /*
+        * If this device has analog channels and at least one of them is
+        * enabled, use mso_send_data_proc() to properly handle the analog
+        * data. Otherwise use la_send_data_proc().
+        */
+       if (g_slist_length(devc->enabled_analog_channels) > 0)
+               devc->send_data_proc = mso_send_data_proc;
+       else
+               devc->send_data_proc = la_send_data_proc;
+
+       std_session_send_df_header(sdi);
+
+       return SR_OK;
+}
+
+SR_PRIV int fx2lafw_start_acquisition(const struct sr_dev_inst *sdi)
+{
+       struct sr_dev_driver *di;
+       struct drv_context *drvc;
+       struct dev_context *devc;
+       int timeout, ret;
+       size_t size;
+
+       di = sdi->driver;
+       drvc = di->context;
+       devc = sdi->priv;
+
+       devc->ctx = drvc->sr_ctx;
+       devc->sent_samples = 0;
+       devc->empty_transfer_count = 0;
+       devc->acq_aborted = FALSE;
+
+       if (configure_channels(sdi) != SR_OK) {
+               sr_err("Failed to configure channels.");
+               return SR_ERR;
+       }
+
+       timeout = get_timeout(devc);
+       usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc);
+
+       size = get_buffer_size(devc);
+       /* Prepare for analog sampling. */
+       if (g_slist_length(devc->enabled_analog_channels) > 0) {
+               /* We need a buffer half the size of a transfer. */
+               devc->logic_buffer = g_try_malloc(size / 2);
+               devc->analog_buffer = g_try_malloc(
+                       sizeof(float) * size / 2);
+       }
+       start_transfers(sdi);
+       if ((ret = command_start_acquisition(sdi)) != SR_OK) {
+               fx2lafw_abort_acquisition(devc);
+               return ret;
+       }
+
+       return SR_OK;
+}
index 24f17a9c8da054a4ea96c0a21a69bbb86b9a5b13..1403c8ab09adf3a49ad53e3ff289c3877cd15f9e 100644 (file)
 
 #define DEV_CAPS_16BIT_POS     0
 #define DEV_CAPS_AX_ANALOG_POS 1
-#define DEV_CAPS_DSLOGIC_FW_POS 2
 
 #define DEV_CAPS_16BIT         (1 << DEV_CAPS_16BIT_POS)
 #define DEV_CAPS_AX_ANALOG     (1 << DEV_CAPS_AX_ANALOG_POS)
-#define DEV_CAPS_DSLOGIC_FW    (1 << DEV_CAPS_DSLOGIC_FW_POS)
-
-#define DSLOGIC_FPGA_FIRMWARE_5V "dreamsourcelab-dslogic-fpga-5v.fw"
-#define DSLOGIC_FPGA_FIRMWARE_3V3 "dreamsourcelab-dslogic-fpga-3v3.fw"
-#define DSCOPE_FPGA_FIRMWARE "dreamsourcelab-dscope-fpga.fw"
-#define DSLOGIC_PRO_FPGA_FIRMWARE "dreamsourcelab-dslogic-pro-fpga.fw"
 
 /* Protocol commands */
 #define CMD_GET_FW_VERSION             0xb0
@@ -105,16 +98,13 @@ struct dev_context {
         */
        int64_t fw_updated;
 
-       /* Supported samplerates */
        const uint64_t *samplerates;
        int num_samplerates;
 
-       /* Device/capture settings */
        uint64_t cur_samplerate;
        uint64_t limit_samples;
        uint64_t capture_ratio;
 
-       /* Operational settings */
        gboolean trigger_fired;
        gboolean acq_aborted;
        gboolean sample_wide;
@@ -131,30 +121,11 @@ struct dev_context {
                uint8_t *data, size_t length, size_t sample_width);
        uint8_t *logic_buffer;
        float *analog_buffer;
-
-       /* Is this a DSLogic? */
-       gboolean dslogic;
-       uint16_t dslogic_mode;
-       uint32_t trigger_pos;
-       gboolean dslogic_external_clock;
-       gboolean dslogic_continuous_mode;
-       int dslogic_clock_edge;
-       int dslogic_voltage_threshold;
 };
 
-SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV gboolean match_manuf_prod(libusb_device *dev, const char *manufacturer,
-               const char *product);
 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 int fx2lafw_start_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc);
-SR_PRIV void LIBUSB_CALL 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);
-SR_PRIV void la_send_data_proc(struct sr_dev_inst *sdi, uint8_t *data,
-               size_t length, size_t sample_width);
-SR_PRIV void mso_send_data_proc(struct sr_dev_inst *sdi, uint8_t *data,
-               size_t length, size_t sample_width);
 
 #endif
index 801429ce65c942dc8ba1bbb15f3bf12ab60d836c..f2cbd7b20ae4d0b8e4117af4f41a5aabb434df3f 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * Gossen Metrawatt Metrahit 1x/2x drivers
- *
- * @internal
- */
-
 #include <config.h>
 #include <string.h>
 #include "protocol.h"
 #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"
 
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
        SR_CONF_SERIALCOMM,
 };
 
-/** Hardware capabilities for Metrahit 1x/2x devices in send mode. */
-static const uint32_t devopts_sm[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
        SR_CONF_THERMOMETER, /**< All GMC 1x/2x multimeters seem to support this */
+};
+
+/** Hardware capabilities for Metrahit 1x/2x devices in send mode. */
+static const uint32_t devopts_sm[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
@@ -51,8 +45,6 @@ static const uint32_t devopts_sm[] = {
 
 /** Hardware capabilities for Metrahit 2x devices in bidirectional Mode. */
 static const uint32_t devopts_bd[] = {
-       SR_CONF_MULTIMETER,
-       SR_CONF_THERMOMETER, /**< All GMC 1x/2x multimeters seem to support this */
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
@@ -159,8 +151,6 @@ static GSList *scan_1x_2x_rs232(struct sr_dev_driver *di, GSList *options)
        conn = serialcomm = NULL;
        serialcomm_given = FALSE;
 
-       sr_spew("scan_1x_2x_rs232() called!");
-
        for (l = options; l; l = l->next) {
                src = l->data;
                switch (src->key) {
@@ -204,10 +194,10 @@ static GSList *scan_1x_2x_rs232(struct sr_dev_driver *di, GSList *options)
        }
 
        if (model != METRAHIT_NONE) {
-               sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(model));
+               sr_spew("%s detected!", gmc_model_str(model));
                sdi = g_malloc0(sizeof(struct sr_dev_inst));
                sdi->status = SR_ST_INACTIVE;
-               sdi->vendor = g_strdup(VENDOR_GMC);
+               sdi->vendor = g_strdup("Gossen Metrawatt");
                sdi->model = g_strdup(gmc_model_str(model));
                devc = g_malloc0(sizeof(struct dev_context));
                sr_sw_limits_init(&devc->limits);
@@ -242,8 +232,6 @@ static GSList *scan_2x_bd232(struct sr_dev_driver *di, GSList *options)
        conn = serialcomm = NULL;
        devices = NULL;
 
-       sr_spew("scan_2x_bd232() called!");
-
        for (l = options; l; l = l->next) {
                src = l->data;
                switch (src->key) {
@@ -269,7 +257,7 @@ static GSList *scan_2x_bd232(struct sr_dev_driver *di, GSList *options)
 
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
-       sdi->vendor = g_strdup(VENDOR_GMC);
+       sdi->vendor = g_strdup("Gossen Metrawatt");
        sdi->priv = devc;
 
        /* Send message 03 "Query multimeter version and status" */
@@ -297,7 +285,7 @@ static GSList *scan_2x_bd232(struct sr_dev_driver *di, GSList *options)
                devc->buflen = 0;
 
                if (devc->model != METRAHIT_NONE) {
-                       sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(devc->model));
+                       sr_spew("%s detected!", gmc_model_str(devc->model));
                        sr_sw_limits_init(&devc->limits);
                        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);
@@ -308,21 +296,17 @@ static GSList *scan_2x_bd232(struct sr_dev_driver *di, GSList *options)
                        devc = g_malloc0(sizeof(struct dev_context));
                        sdi = g_malloc0(sizeof(struct sr_dev_inst));
                        sdi->status = SR_ST_INACTIVE;
-                       sdi->vendor = g_strdup(VENDOR_GMC);
+                       sdi->vendor = g_strdup("Gossen Metrawatt");
                }
        };
 
-       /* Free last alloc if no device found */
-       if (devc->model == METRAHIT_NONE) {
-               g_free(devc);
-               sr_dev_inst_free(sdi);
-       }
+       /* Free last alloc that was done in preparation. */
+       g_free(devc);
+       sr_dev_inst_free(sdi);
 
        return std_scan_complete(di, devices);
 
 exit_err:
-       sr_info("scan_2x_bd232(): Error!");
-
        sr_serial_dev_inst_free(serial);
        g_free(devc);
        sr_dev_inst_free(sdi);
@@ -334,19 +318,16 @@ static int dev_close(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
 
-       std_serial_dev_close(sdi);
+       devc = sdi->priv;
 
-       sdi->status = SR_ST_INACTIVE;
-       if ((devc = sdi->priv))
-               devc->model = METRAHIT_NONE;
+       devc->model = METRAHIT_NONE;
 
-       return SR_OK;
+       return std_serial_dev_close(sdi);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
        struct dev_context *devc;
 
        (void)cg;
@@ -356,7 +337,6 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
 
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
@@ -368,58 +348,21 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                return SR_ERR_NA;
        }
 
-       return ret;
-}
-
-/** Implementation of config_list, auxiliary function for common parts. */
-static int config_list_common(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_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(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-                         const struct sr_channel_group *cg)
+static int config_list_sm(uint32_t 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_UINT32,
-                               devopts_sm, ARRAY_SIZE(devopts_sm), sizeof(uint32_t));
-               break;
-       default:
-               return config_list_common(key, data, sdi, cg);
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts_sm);
 }
 
 /** Implementation of config_list for Metrahit 2x bidirectional mode */
-static int config_list_bd(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-                         const struct sr_channel_group *cg)
+static int config_list_bd(uint32_t 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_UINT32,
-                               devopts_bd, ARRAY_SIZE(devopts_bd), sizeof(uint32_t));
-               break;
-       default:
-               return config_list_common(key, data, sdi, cg);
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts_bd);
 }
 
 static int dev_acquisition_start_1x_2x_rs232(const struct sr_dev_inst *sdi)
@@ -427,9 +370,6 @@ static int dev_acquisition_start_1x_2x_rs232(const struct sr_dev_inst *sdi)
        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->settings_ok = FALSE;
        devc->buflen = 0;
@@ -437,7 +377,6 @@ static int dev_acquisition_start_1x_2x_rs232(const struct sr_dev_inst *sdi)
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -450,9 +389,6 @@ static int dev_acquisition_start_2x_bd232(const struct sr_dev_inst *sdi)
        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->settings_ok = FALSE;
        devc->buflen = 0;
@@ -460,7 +396,6 @@ static int dev_acquisition_start_2x_bd232(const struct sr_dev_inst *sdi)
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -477,7 +412,7 @@ static struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan_1x_2x_rs232,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list_sm,
@@ -497,7 +432,7 @@ static struct sr_dev_driver gmc_mh_2x_bd232_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan_2x_bd232,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list_bd,
index 10fe9c09e102b6505a96e047fb1cf3c829bcf2c9..d1009b30eeca2a7eeecd5ef548e5911f21235884 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * Gossen Metrawatt Metrahit 1x/2x drivers
- *
- * @internal
- */
-
 #include <config.h>
 #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);
 
@@ -82,7 +73,7 @@ static void decode_ctmv_16(uint8_t ctmv, struct dev_context *devc)
        case 0x08: /* 1000 Diode */
                devc->mq = SR_MQ_VOLTAGE;
                devc->unit = SR_UNIT_VOLT;
-               devc->mqflags |= SR_MQFLAG_DIODE;
+               devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
                break;
        case 0x09: /* 1001 Ohm, °C */
        case 0x0a: /* 1010 kOhm */
@@ -206,7 +197,7 @@ static void decode_ctmv_18(uint8_t ctmv, struct dev_context *devc)
        case 0x05: /* 0101 Diode/Diode with buzzer */
                devc->mq = SR_MQ_VOLTAGE;
                devc->unit = SR_UNIT_VOLT;
-               devc->mqflags |= SR_MQFLAG_DIODE;
+               devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
                break;
        case 0x06: /* 0110 °C */
                devc->mq = SR_MQ_TEMPERATURE;
@@ -419,7 +410,7 @@ static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
                devc->unit = SR_UNIT_VOLT;
                if (ctmv == 0x0f) {
                        devc->mq = SR_MQ_VOLTAGE;
-                       devc->mqflags |= SR_MQFLAG_DIODE;
+                       devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
                } else {
                        devc->mq = SR_MQ_CONTINUITY;
                        devc->scale += -5;
@@ -764,8 +755,7 @@ static void process_msg_inf_10(struct sr_dev_inst *sdi)
                dgt = bc(devc->buf[5 + cnt]);
                if (dgt == 11) { /* Empty digit */
                        dgt = 0;
-               }
-               else if (dgt >= 12) { /* Overload */
+               } else if (dgt >= 12) { /* Overload */
                        devc->value = NAN;
                        devc->scale = 0;
                        break;
@@ -962,8 +952,7 @@ static int chk_msg14(struct sr_dev_inst *sdi)
                        sr_err("Device: Unknown error code!");
                }
                retc = SR_ERR_ARG;
-       }
-       else if (!isreq && ((devc->buf[1] != 0x27) || (devc->buf[2] != 0x3f))) {
+       } else if (!isreq && ((devc->buf[1] != 0x27) || (devc->buf[2] != 0x3f))) {
                sr_err("process_msg_14(): byte 1/2 unexpected!");
                retc = SR_ERR_ARG;
        }
@@ -1064,11 +1053,9 @@ SR_PRIV int process_msg14(struct sr_dev_inst *sdi)
                                devc->value = NAN;
                                devc->scale = 0;
                                break;
-                       }
-                       else if (dgt == 13) { /* FUSE */
+                       } else if (dgt == 13) { /* FUSE */
                                sr_err("FUSE!");
-                       }
-                       else if (dgt == 14) { /* Function recognition mode, OPEN */
+                       } else if (dgt == 14) { /* Function recognition mode, OPEN */
                                sr_info("Function recognition mode, OPEN!");
                                devc->value = NAN;
                                devc->scale = 0;
@@ -1144,8 +1131,7 @@ SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data)
                                        process_msg_inf_10(sdi);
                                        devc->buflen = 0;
                                        continue;
-                               }
-                               else if ((devc->buflen >= 5) &&
+                               } else if ((devc->buflen >= 5) &&
                                         (devc->buf[devc->buflen - 1] &
                                          MSGID_MASK) != MSGID_DATA) {
                                        /*
@@ -1153,8 +1139,7 @@ SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data)
                                         * of next message.
                                         */
                                        process_msg_inf_5(sdi);
-                                       devc->buf[0] =
-                                                       devc->buf[devc->buflen - 1];
+                                       devc->buf[0] = devc->buf[devc->buflen - 1];
                                        devc->buflen = 1;
                                        continue;
                                }
@@ -1175,7 +1160,7 @@ SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
@@ -1218,7 +1203,7 @@ SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        /* Request next data set, if required */
        if (sdi->status == SR_ST_ACTIVE) {
@@ -1232,8 +1217,7 @@ SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data)
                        if (devc->cmd_seq % 10 == 0) {
                                if (req_stat14(sdi, FALSE) != SR_OK)
                                        return FALSE;
-                       }
-                       else if (req_meas14(sdi) != SR_OK)
+                       } else if (req_meas14(sdi) != SR_OK)
                                return FALSE;
                }
        }
@@ -1283,9 +1267,7 @@ static void create_cmd_14(guchar addr, guchar func, guchar *params, guchar *buf)
        }
 }
 
-/** Request one measurement from 2x multimeter (msg 8).
- *
- */
+/** Request one measurement from 2x multimeter (msg 8). */
 int req_meas14(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -1361,7 +1343,8 @@ int req_stat14(const struct sr_dev_inst *sdi, gboolean power_on)
        return SR_OK;
 }
 
-/** Decode model in "send mode".
+/**
+ * Decode model in "send mode".
  *
  * @param[in] mcode Model code.
  * @return Model code.
@@ -1505,9 +1488,6 @@ SR_PRIV int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *s
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
index 52563d95e0ab9597cac755a57a59feccd4df07ca..ed759694861d2b59c8e6b75c626a7313b18012d8 100644 (file)
  * 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
 
@@ -84,15 +76,11 @@ enum model {
        METRAHIT_29S            = METRAHIT_28S + 1,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        enum model model;       /**< Model code. */
 
-       /* Acquisition settings */
        struct sr_sw_limits limits;
 
-       /* Operational state */
        gboolean settings_ok;   /**< Settings msg received yet. */
        int msg_type;       /**< Message type (MSGID_INF, ...). */
        int msg_len;        /**< Message length (valid when msg, curr. type known).*/
@@ -112,12 +100,10 @@ struct dev_context {
        int64_t req_sent_at;    /**< Request sent. */
        gboolean response_pending; /**< Request sent, response is pending. */
 
-       /* Temporary state across callbacks */
        uint8_t buf[GMC_BUFSIZE];       /**< Buffer for read callback */
        int buflen;                     /**< Data len in buf */
 };
 
-/* Forward declarations */
 SR_PRIV int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
                const struct sr_channel_group *cg);
 SR_PRIV int gmc_decode_model_bd(uint8_t mcode);
index 99c29a3e5ccc466167b310b4b1d80f5571964d96..2926bf074d91f83ea7192ce67bb45313e893b997 100644 (file)
@@ -25,8 +25,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_OSCILLOSCOPE,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
        SR_CONF_SAMPLERATE | SR_CONF_GET,
 };
@@ -98,8 +101,6 @@ static int dev_open(struct sr_dev_inst *sdi)
                return SR_ERR;
        }
 
-       sdi->status = SR_ST_ACTIVE;
-
        return SR_OK;
 }
 
@@ -107,21 +108,16 @@ static int dev_close(struct sr_dev_inst *sdi)
 {
        struct sr_scpi_dev_inst *scpi;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        scpi = sdi->conn;
-       if (scpi) {
-               if (sr_scpi_close(scpi) < 0)
-                       return SR_ERR;
-               sdi->status = SR_ST_INACTIVE;
-       }
 
-       return SR_OK;
+       if (!scpi)
+               return SR_ERR_BUG;
+
+       return sr_scpi_close(scpi);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -143,8 +139,8 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -153,9 +149,6 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        if (!sdi)
                return SR_ERR_ARG;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
@@ -169,26 +162,10 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               return SR_OK;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -199,9 +176,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        scpi = sdi->conn;
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc->state = START_ACQUISITION;
        devc->cur_acq_frame = 0;
 
@@ -220,11 +194,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
        scpi = sdi->conn;
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE) {
-               sr_err("Device inactive, can't stop acquisition.");
-               return SR_ERR;
-       }
-
        if (devc->df_started) {
                packet.type = SR_DF_FRAME_END;
                sr_session_send(sdi, &packet);
@@ -247,6 +216,7 @@ static struct sr_dev_driver gwinstek_gds_800_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 604ec54f9d0a881465f0da3b63c64cf54dec8cf0..ce9e8d416aae0d1862b689c3fdf9d579f76cf41f 100644 (file)
@@ -35,7 +35,7 @@ static int read_data(struct sr_dev_inst *sdi,
                data_size - devc->cur_rcv_buffer_position);
        if (len < 0) {
                sr_err("Read data error.");
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                devc->cur_rcv_buffer_position = 0;
                return SR_ERR;
        }
@@ -55,7 +55,7 @@ static int read_data(struct sr_dev_inst *sdi,
                return SR_OK;
        } else {
                sr_err("Too many bytes read.");
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                devc->cur_rcv_buffer_position = 0;
                return SR_ERR;
        }
@@ -96,17 +96,17 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
        case START_ACQUISITION:
                if (sr_scpi_send(scpi, ":TRIG:MOD 3") != SR_OK) {
                        sr_err("Failed to set trigger mode to SINGLE.");
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                        return TRUE;
                }
                if (sr_scpi_send(scpi, ":STOP") != SR_OK) {
                        sr_err("Failed to put the trigger system into STOP state.");
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                        return TRUE;
                }
                if (sr_scpi_send(scpi, ":RUN") != SR_OK) {
                        sr_err("Failed to put the trigger system into RUN state.");
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                        return TRUE;
                }
 
@@ -117,12 +117,12 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
                if (((struct sr_channel *)g_slist_nth_data(sdi->channels, devc->cur_acq_channel))->enabled) {
                        if (sr_scpi_send(scpi, ":ACQ%d:MEM?", devc->cur_acq_channel+1) != SR_OK) {
                                sr_err("Failed to acquire memory.");
-                               sdi->driver->dev_acquisition_stop(sdi);
+                               sr_dev_acquisition_stop(sdi);
                                return TRUE;
                        }
                        if (sr_scpi_read_begin(scpi) != SR_OK) {
                                sr_err("Could not begin reading SCPI response.");
-                               sdi->driver->dev_acquisition_stop(sdi);
+                               sr_dev_acquisition_stop(sdi);
                                return TRUE;
                        }
                        devc->state = WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE;
@@ -136,7 +136,7 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
                                        /* All frames accquired. */
                                        sr_spew("All frames acquired.");
 
-                                       sdi->driver->dev_acquisition_stop(sdi);
+                                       sr_dev_acquisition_stop(sdi);
                                        return TRUE;
                                } else {
                                        /* Start acquiring next frame. */
@@ -158,55 +158,55 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
                }
                break;
        case WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE:
-               if (read_data(sdi, scpi, devc, 1) == SR_OK) {
-                       if (devc->rcv_buffer[0] == '#')
-                               devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE;
-               }
+               if (read_data(sdi, scpi, devc, 1) < 0)
+                       break;
+               if (devc->rcv_buffer[0] == '#')
+                       devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE;
                break;
        case WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE:
-               if (read_data(sdi, scpi, devc, 1) == SR_OK) {
-                       if (devc->rcv_buffer[0] != '4' &&
-                               devc->rcv_buffer[0] != '5' &&
-                               devc->rcv_buffer[0] != '6') {
-                               sr_err("Data size digits is not 4, 5 or 6 but "
-                                      "'%c'.", devc->rcv_buffer[0]);
-                               sdi->driver->dev_acquisition_stop(sdi);
-                               return TRUE;
-                       } else {
-                               devc->data_size_digits = devc->rcv_buffer[0] - '0';
-                               devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE;
-                       }
+               if (read_data(sdi, scpi, devc, 1) < 0)
+                       break;
+               if (devc->rcv_buffer[0] != '4' &&
+                       devc->rcv_buffer[0] != '5' &&
+                       devc->rcv_buffer[0] != '6') {
+                       sr_err("Data size digits is not 4, 5 or 6 but "
+                              "'%c'.", devc->rcv_buffer[0]);
+                       sr_dev_acquisition_stop(sdi);
+                       return TRUE;
+               } else {
+                       devc->data_size_digits = devc->rcv_buffer[0] - '0';
+                       devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE;
                }
                break;
        case WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE:
-               if (read_data(sdi, scpi, devc, devc->data_size_digits) == SR_OK) {
-                       devc->rcv_buffer[devc->data_size_digits] = 0;
-                       if (sr_atoi(devc->rcv_buffer, &devc->data_size) != SR_OK) {
-                               sr_err("Could not parse data size '%s'", devc->rcv_buffer);
-                               sdi->driver->dev_acquisition_stop(sdi);
-                               return TRUE;
-                       } else
-                               devc->state = WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE;
-               }
+               if (read_data(sdi, scpi, devc, devc->data_size_digits) < 0)
+                       break;
+               devc->rcv_buffer[devc->data_size_digits] = 0;
+               if (sr_atoi(devc->rcv_buffer, &devc->data_size) != SR_OK) {
+                       sr_err("Could not parse data size '%s'", devc->rcv_buffer);
+                       sr_dev_acquisition_stop(sdi);
+                       return TRUE;
+               } else
+                       devc->state = WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE;
                break;
        case WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE:
-               if (read_data(sdi, scpi, devc, sizeof(float)) == SR_OK) {
-                       /*
-                        * Contrary to the documentation, this field is
-                        * transfered with most significant byte first!
-                        */
-                       sample_rate = RB32(devc->rcv_buffer);
-                       memcpy(&devc->sample_rate, &sample_rate, sizeof(float));
-                       devc->state = WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE;
+               if (read_data(sdi, scpi, devc, sizeof(float)) < 0)
+                       break;
+               /*
+                * Contrary to the documentation, this field is
+                * transfered with most significant byte first!
+                */
+               sample_rate = RB32(devc->rcv_buffer);
+               memcpy(&devc->sample_rate, &sample_rate, sizeof(float));
+               devc->state = WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE;
 
-                       if (!devc->df_started) {
-                               std_session_send_df_header(sdi);
+               if (!devc->df_started) {
+                       std_session_send_df_header(sdi);
 
-                               packet.type = SR_DF_FRAME_BEGIN;
-                               sr_session_send(sdi, &packet);
+                       packet.type = SR_DF_FRAME_BEGIN;
+                       sr_session_send(sdi, &packet);
 
-                               devc->df_started = TRUE;
-                       }
+                       devc->df_started = TRUE;
                }
                break;
        case WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE:
@@ -218,72 +218,73 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
                        devc->state = WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE;
                break;
        case WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE:
-               if (read_data(sdi, scpi, devc, devc->data_size - 8) == SR_OK) {
-                       /* Fetch data needed for conversion from device. */
-                       snprintf(command, sizeof(command), ":CHAN%d:SCAL?",
-                                       devc->cur_acq_channel + 1);
-                       if (sr_scpi_get_string(scpi, command, &response) != SR_OK) {
-                               sr_err("Failed to get volts per division.");
-                               sdi->driver->dev_acquisition_stop(sdi);
-                               return TRUE;
-                       }
-                       volts_per_division = g_ascii_strtod(response, &end_ptr);
-                       if (!strcmp(end_ptr, "mV"))
-                               volts_per_division *= 1.e-3;
-                       g_free(response);
+               if (read_data(sdi, scpi, devc, devc->data_size - 8) < 0)
+                       break;
 
-                       num_samples = (devc->data_size - 8) / 2;
-                       sr_spew("Received %d number of samples from channel "
-                               "%d.", num_samples, devc->cur_acq_channel + 1);
+               /* Fetch data needed for conversion from device. */
+               snprintf(command, sizeof(command), ":CHAN%d:SCAL?",
+                               devc->cur_acq_channel + 1);
+               if (sr_scpi_get_string(scpi, command, &response) != SR_OK) {
+                       sr_err("Failed to get volts per division.");
+                       sr_dev_acquisition_stop(sdi);
+                       return TRUE;
+               }
+               volts_per_division = g_ascii_strtod(response, &end_ptr);
+               if (!strcmp(end_ptr, "mV"))
+                       volts_per_division *= 1.e-3;
+               g_free(response);
 
-                       float vbit = volts_per_division * VERTICAL_DIVISIONS / 256.0;
-                       float vbitlog = log10f(vbit);
-                       int digits = -(int)vbitlog + (vbitlog < 0.0);
+               num_samples = (devc->data_size - 8) / 2;
+               sr_spew("Received %d number of samples from channel "
+                       "%d.", num_samples, devc->cur_acq_channel + 1);
 
-                       /* Convert data. */
-                       for (i = 0; i < num_samples; i++)
-                               samples[i] = ((float) ((int16_t) (RB16(&devc->rcv_buffer[i*2])))) * vbit;
+               float vbit = volts_per_division * VERTICAL_DIVISIONS / 256.0;
+               float vbitlog = log10f(vbit);
+               int digits = -(int)vbitlog + (vbitlog < 0.0);
 
-                       /* Fill frame. */
-                       sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
-                       analog.meaning->channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, devc->cur_acq_channel));
-                       analog.num_samples = num_samples;
-                       analog.data = samples;
-                       analog.meaning->mq = SR_MQ_VOLTAGE;
-                       analog.meaning->unit = SR_UNIT_VOLT;
-                       analog.meaning->mqflags = 0;
-                       packet.type = SR_DF_ANALOG;
-                       packet.payload = &analog;
-                       sr_session_send(sdi, &packet);
-                       g_slist_free(analog.meaning->channels);
+               /* Convert data. */
+               for (i = 0; i < num_samples; i++)
+                       samples[i] = ((float) ((int16_t) (RB16(&devc->rcv_buffer[i*2])))) * vbit;
 
-                       /* All channels acquired. */
-                       if (devc->cur_acq_channel == ANALOG_CHANNELS - 1) {
-                               sr_spew("All channels acquired.");
+               /* Fill frame. */
+               sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+               analog.meaning->channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, devc->cur_acq_channel));
+               analog.num_samples = num_samples;
+               analog.data = samples;
+               analog.meaning->mq = SR_MQ_VOLTAGE;
+               analog.meaning->unit = SR_UNIT_VOLT;
+               analog.meaning->mqflags = 0;
+               packet.type = SR_DF_ANALOG;
+               packet.payload = &analog;
+               sr_session_send(sdi, &packet);
+               g_slist_free(analog.meaning->channels);
 
-                               if (devc->cur_acq_frame == devc->frame_limit - 1) {
-                                       /* All frames acquired. */
-                                       sr_spew("All frames acquired.");
-                                       sdi->driver->dev_acquisition_stop(sdi);
-                                       return TRUE;
-                               } else {
-                                       /* Start acquiring next frame. */
-                                       if (devc->df_started) {
-                                               packet.type = SR_DF_FRAME_END;
-                                               sr_session_send(sdi, &packet);
+               /* All channels acquired. */
+               if (devc->cur_acq_channel == ANALOG_CHANNELS - 1) {
+                       sr_spew("All channels acquired.");
 
-                                               packet.type = SR_DF_FRAME_BEGIN;
-                                               sr_session_send(sdi, &packet);
-                                       }
-                                       devc->cur_acq_frame++;
-                                       devc->state = START_ACQUISITION;
-                               }
-                       } else {
-                               /* Start acquiring next channel. */
-                               devc->state = START_TRANSFER_OF_CHANNEL_DATA;
-                               devc->cur_acq_channel++;
+                       if (devc->cur_acq_frame == devc->frame_limit - 1) {
+                               /* All frames acquired. */
+                               sr_spew("All frames acquired.");
+                               sr_dev_acquisition_stop(sdi);
                                return TRUE;
+                       } else {
+                               /* Start acquiring next frame. */
+                               if (devc->df_started) {
+                                       packet.type = SR_DF_FRAME_END;
+                                       sr_session_send(sdi, &packet);
+
+                                       packet.type = SR_DF_FRAME_BEGIN;
+                                       sr_session_send(sdi, &packet);
+                               }
+                               devc->cur_acq_frame++;
+                               devc->state = START_ACQUISITION;
                        }
+               } else {
+                       /* Start acquiring next channel. */
+                       devc->state = START_TRANSFER_OF_CHANNEL_DATA;
+                       devc->cur_acq_channel++;
+                       return TRUE;
                }
                break;
        }
index 382e87b8cac5b3253df981486837c4a2374681be..d5c2e5a0de185698debe6d78b649234f00d0c86a 100644 (file)
@@ -44,7 +44,6 @@ enum gds_state
        WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        enum gds_state state;
        uint64_t cur_acq_frame;
diff --git a/src/hardware/gwinstek-gpd/api.c b/src/hardware/gwinstek-gpd/api.c
new file mode 100644 (file)
index 0000000..ac2fe2e
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Bastian Schmitz <bastian.schmitz@udo.edu>
+ *
+ * This program is free software: you can 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 <config.h>
+#include <string.h>
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_POWER_SUPPLY,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t devopts_cg[] = {
+       SR_CONF_VOLTAGE | SR_CONF_GET,
+       SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CURRENT | SR_CONF_GET,
+       SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const char *channel_modes[] = {
+       "Independent",
+};
+
+static const struct gpd_model models[] = {
+       { GPD_2303S, "GPD-2303S",
+               CHANMODE_INDEPENDENT,
+               2,
+               {
+                       /* Channel 1 */
+                       { { 0, 30, 0.001 }, { 0, 3, 0.001 } },
+                       /* Channel 2 */
+                       { { 0, 30, 0.001 }, { 0, 3, 0.001 } },
+               },
+       },
+};
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       const char *conn, *serialcomm;
+       const struct gpd_model *model;
+       const struct sr_config *src;
+       struct sr_channel *ch;
+       struct sr_channel_group *cg;
+       GSList *l;
+       struct sr_serial_dev_inst *serial;
+       struct sr_dev_inst *sdi;
+       char reply[50];
+       unsigned int i;
+       struct dev_context *devc;
+       char channel[10];
+       GRegex *regex;
+       GMatchInfo *match_info;
+       unsigned int cc_cv_ch1, cc_cv_ch2, track1, track2, beep, baud1, baud2;
+
+       serial = NULL;
+       match_info = NULL;
+       regex = NULL;
+       conn = NULL;
+       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 = "115200/8n1";
+       sr_info("Probing serial port %s.", conn);
+       serial = sr_serial_dev_inst_new(conn, serialcomm);
+       if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+               return NULL;
+
+       serial_flush(serial);
+       gpd_send_cmd(serial, "*IDN?\n");
+       if (gpd_receive_reply(serial, reply, sizeof(reply)) != SR_OK) {
+               sr_err("Device did not reply.");
+               goto error;
+       }
+       serial_flush(serial);
+
+       /*
+        * Returned identification string is for example:
+        * "GW INSTEK,GPD-2303S,SN:ER915277,V2.10"
+        */
+       regex = g_regex_new("GW INSTEK,(.+),SN:(.+),(V.+)", 0, 0, NULL);
+       if (!g_regex_match(regex, reply, 0, &match_info)) {
+               sr_err("Unsupported model '%s'.", reply);
+               goto error;
+       }
+
+       model = NULL;
+       for (i = 0; i < ARRAY_SIZE(models); i++) {
+               if (!strcmp(g_match_info_fetch(match_info, 1), models[i].name)) {
+                       model = &models[i];
+                       break;
+               }
+       }
+       if (!model) {
+               sr_err("Unsupported model '%s'.", reply);
+               goto error;
+       }
+
+       sr_info("Detected model '%s'.", model->name);
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->status = SR_ST_INACTIVE;
+       sdi->vendor = g_strdup("GW Instek");
+       sdi->model = g_strdup(model->name);
+       sdi->inst_type = SR_INST_SERIAL;
+       sdi->conn = serial;
+
+       for (i = 0; i < model->num_channels; i++) {
+               snprintf(channel, sizeof(channel), "CH%d", i + 1);
+               ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel);
+               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));
+       sr_sw_limits_init(&devc->limits);
+       devc->model = model;
+       devc->config = g_malloc0(sizeof(struct per_channel_config)
+                                * model->num_channels);
+       sdi->priv = devc;
+
+       serial_flush(serial);
+       gpd_send_cmd(serial, "STATUS?\n");
+       gpd_receive_reply(serial, reply, sizeof(reply));
+
+       if (sscanf(reply, "%1u%1u%1u%1u%1u%1u%1u%1u", &cc_cv_ch1,
+                       &cc_cv_ch2, &track1, &track2, &beep,
+                       &devc->output_enabled, &baud1, &baud2) != 8) {
+               sr_err("Invalid reply to STATUS: '%s'.", reply);
+               goto error;
+       }
+
+       for (i = 0; i < model->num_channels; ++i) {
+               gpd_send_cmd(serial, "ISET%d?\n", i + 1);
+               gpd_receive_reply(serial, reply, sizeof(reply));
+               if (sscanf(reply, "%f", &devc->config[i].output_current_max) != 1) {
+                       sr_err("Invalid reply to ISETn?: '%s'.", reply);
+                       goto error;
+               }
+
+               gpd_send_cmd(serial, "VSET%d?\n", i + 1);
+               gpd_receive_reply(serial, reply, sizeof(reply));
+               if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
+                       sr_err("Invalid reply to VSETn?: '%s'.", reply);
+                       goto error;
+               }
+               gpd_send_cmd(serial, "IOUT%d?\n", i + 1);
+               gpd_receive_reply(serial, reply, sizeof(reply));
+               if (sscanf(reply, "%f", &devc->config[i].output_current_last) != 1) {
+                       sr_err("Invalid reply to IOUTn?: '%s'.", reply);
+                       goto error;
+               }
+               gpd_send_cmd(serial, "VOUT%d?\n", i + 1);
+               gpd_receive_reply(serial, reply, sizeof(reply));
+               if (sscanf(reply, "%f", &devc->config[i].output_voltage_last) != 1) {
+                       sr_err("Invalid reply to VOUTn?: '%s'.", reply);
+                       goto error;
+               }
+       }
+
+       serial_close(serial);
+
+       return std_scan_complete(di, g_slist_append(NULL, sdi));
+
+error:
+       if (match_info)
+               g_match_info_free(match_info);
+       if (regex)
+               g_regex_unref(regex);
+       if (serial)
+               serial_close(serial);
+
+       return NULL;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       int ret, channel;
+       const struct dev_context *devc;
+       const struct sr_channel *ch;
+
+       if (!sdi)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+
+       if (!cg) {
+               switch (key) {
+               case SR_CONF_CHANNEL_CONFIG:
+                       *data = g_variant_new_string(
+                               channel_modes[devc->channel_mode]);
+                       break;
+               case SR_CONF_ENABLED:
+                       *data = g_variant_new_boolean(devc->output_enabled);
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
+       } else {
+               ch = cg->channels->data;
+               channel = ch->index;
+               ret = SR_OK;
+               switch (key) {
+               case SR_CONF_VOLTAGE:
+                       *data = g_variant_new_double(
+                               devc->config[channel].output_voltage_last);
+                       break;
+               case SR_CONF_VOLTAGE_TARGET:
+                       *data = g_variant_new_double(
+                               devc->config[channel].output_voltage_max);
+                       break;
+               case SR_CONF_CURRENT:
+                       *data = g_variant_new_double(
+                               devc->config[channel].output_current_last);
+                       break;
+               case SR_CONF_CURRENT_LIMIT:
+                       *data = g_variant_new_double(
+                               devc->config[channel].output_current_max);
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
+       }
+
+       return ret;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       int ret, channel;
+       const struct sr_channel *ch;
+       double dval;
+       gboolean bval;
+       struct dev_context *devc;
+
+       devc = sdi->priv;
+
+       ret = SR_OK;
+
+       switch (key) {
+       case SR_CONF_LIMIT_MSEC:
+       case SR_CONF_LIMIT_SAMPLES:
+               return sr_sw_limits_config_set(&devc->limits, key, data);
+       case SR_CONF_ENABLED:
+               bval = g_variant_get_boolean(data);
+               gpd_send_cmd(sdi->conn, "OUT%c\n", bval ? '1' : '0');
+               devc->output_enabled = bval;
+               break;
+       case SR_CONF_VOLTAGE_TARGET:
+               ch = cg->channels->data;
+               channel = ch->index;
+               dval = g_variant_get_double(data);
+               if (dval < devc->model->channels[channel].voltage[0]
+                   || dval > devc->model->channels[channel].voltage[1])
+                       return SR_ERR_ARG;
+               gpd_send_cmd(sdi->conn, "VSET%d:%05.3lf\n", channel + 1, dval);
+               devc->config[channel].output_voltage_max = dval;
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               ch = cg->channels->data;
+               channel = ch->index;
+               dval = g_variant_get_double(data);
+               if (dval < devc->model->channels[channel].current[0]
+                   || dval > devc->model->channels[channel].current[1])
+                       return SR_ERR_ARG;
+               gpd_send_cmd(sdi->conn, "ISET%d:%05.3lf\n", channel + 1, dval);
+               devc->config[channel].output_current_max = dval;
+               break;
+       default:
+               ret = SR_ERR_NA;
+               break;
+       }
+
+       return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       const struct dev_context *devc;
+       const struct sr_channel *ch;
+       int channel;
+
+       devc = (sdi) ? sdi->priv : NULL;
+
+       if (!cg) {
+               switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
+               case SR_CONF_DEVICE_OPTIONS:
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts,
+                                              drvopts, devopts);
+               case SR_CONF_CHANNEL_CONFIG:
+                       *data = g_variant_new_strv(ARRAY_AND_SIZE(channel_modes));
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
+       } else {
+               ch = cg->channels->data;
+               channel = ch->index;
+
+               switch (key) {
+               case SR_CONF_DEVICE_OPTIONS:
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
+                       break;
+               case SR_CONF_VOLTAGE_TARGET:
+                       *data = std_gvar_min_max_step_array(
+                               devc->model->channels[channel].voltage);
+                       break;
+               case SR_CONF_CURRENT_LIMIT:
+                       *data = std_gvar_min_max_step_array(
+                               devc->model->channels[channel].current);
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+
+       devc = sdi->priv;
+
+       sr_sw_limits_acquisition_start(&devc->limits);
+       std_session_send_df_header(sdi);
+
+       devc->reply_pending = FALSE;
+       devc->req_sent_at = 0;
+       serial = sdi->conn;
+       serial_source_add(sdi->session, serial, G_IO_IN, 100,
+                         gpd_receive_data, (void *)sdi);
+
+       return SR_OK;
+}
+
+static struct sr_dev_driver gwinstek_gpd_driver_info = {
+       .name = "gwinstek-gpd",
+       .longname = "GW Instek GPD",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = std_serial_dev_open,
+       .dev_close = std_serial_dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = std_serial_dev_acquisition_stop,
+       .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(gwinstek_gpd_driver_info);
diff --git a/src/hardware/gwinstek-gpd/protocol.c b/src/hardware/gwinstek-gpd/protocol.c
new file mode 100644 (file)
index 0000000..a054e91
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Bastian Schmitz <bastian.schmitz@udo.edu>
+ *
+ * This program is free software: you can 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 <config.h>
+#include <string.h>
+#include "protocol.h"
+
+SR_PRIV int gpd_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);
+
+       ret = serial_write_blocking(serial, cmdbuf, strlen(cmdbuf),
+                                   serial_timeout(serial, strlen(cmdbuf)));
+       if (ret < 0) {
+               sr_err("Error sending command: %d.", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+SR_PRIV int gpd_receive_reply(struct sr_serial_dev_inst *serial, char *buf,
+                               int buflen)
+{
+       int l_recv = 0, bufpos = 0, retc, l_startpos = 0, lines = 1;
+       gint64 start, remaining;
+       const int timeout_ms = 100;
+
+       if (!serial || (lines <= 0) || !buf || (buflen <= 0))
+               return SR_ERR_ARG;
+
+       start = g_get_monotonic_time();
+       remaining = timeout_ms;
+
+       while ((l_recv < lines) && (bufpos < (buflen + 1))) {
+               retc = serial_read_blocking(serial, &buf[bufpos], 1, remaining);
+               if (retc != 1)
+                       return SR_ERR;
+
+               if (bufpos == 0 && buf[bufpos] == '\r')
+                       continue;
+               if (bufpos == 0 && buf[bufpos] == '\n')
+                       continue;
+
+               if (buf[bufpos] == '\n') {
+                       buf[bufpos] = '\0';
+                       sr_dbg("Received line '%s'.", &buf[l_startpos]);
+                       buf[bufpos] = '\n';
+                       l_startpos = bufpos + 1;
+                       l_recv++;
+               }
+               bufpos++;
+
+               /* Reduce timeout by time elapsed. */
+               remaining = timeout_ms - ((g_get_monotonic_time() - start) / 1000);
+               if (remaining <= 0)
+                       return SR_ERR; /* Timeout. */
+       }
+
+       buf[bufpos] = '\0';
+
+       if (l_recv == lines)
+               return SR_OK;
+       else
+               return SR_ERR;
+}
+
+SR_PRIV int gpd_receive_data(int fd, int revents, void *cb_data)
+{
+       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;
+       struct sr_analog_encoding encoding;
+       struct sr_analog_meaning meaning;
+       struct sr_analog_spec spec;
+       struct sr_channel *ch;
+       unsigned int i;
+       char reply[50];
+       char *reply_esc;
+
+       (void)fd;
+
+       if (!(sdi = cb_data))
+               return TRUE;
+
+       if (!(devc = sdi->priv))
+               return TRUE;
+
+       serial = sdi->conn;
+
+       if (revents == G_IO_IN) {
+               if (!devc->reply_pending) {
+                       sr_err("No reply pending.");
+                       gpd_receive_reply(serial, reply, sizeof(reply));
+                       reply_esc = g_strescape(reply, NULL);
+                       sr_err("Unexpected data '%s'.", reply_esc);
+                       g_free(reply_esc);
+               } else {
+                       for (i = 0; i < devc->model->num_channels; i++) {
+                               packet.type = SR_DF_ANALOG;
+                               packet.payload = &analog;
+
+                               reply[0] = '\0';
+                               gpd_receive_reply(serial, reply, sizeof(reply));
+                               if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
+                                       sr_err("Invalid reply to VOUT1?: '%s'.",
+                                               reply);
+                                       return TRUE;
+                               }
+
+                               /* Send the value forward. */
+                               sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+                               analog.num_samples = 1;
+                               ch = g_slist_nth_data(sdi->channels, i);
+                               analog.meaning->channels =
+                                       g_slist_append(NULL, ch);
+                               analog.meaning->mq = SR_MQ_CURRENT;
+                               analog.meaning->unit = SR_UNIT_AMPERE;
+                               analog.meaning->mqflags = 0;
+                               analog.encoding->digits = 3;
+                               analog.spec->spec_digits = 3;
+                               analog.data = &devc->config[i].output_current_max;
+                               sr_session_send(sdi, &packet);
+
+                               reply[0] = '\0';
+                               gpd_receive_reply(serial, reply, sizeof(reply));
+                               if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
+                                       sr_err("Invalid reply to VOUT1?: '%s'.",
+                                               reply);
+                                       return TRUE;
+                               }
+
+                               /* Send the value forward. */
+                               sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+                               analog.num_samples = 1;
+                               ch = g_slist_nth_data(sdi->channels, i);
+                               analog.meaning->channels =
+                                       g_slist_append(NULL, ch);
+                               analog.meaning->mq = SR_MQ_VOLTAGE;
+                               analog.meaning->unit = SR_UNIT_VOLT;
+                               analog.meaning->mqflags = SR_MQFLAG_DC;
+                               analog.encoding->digits = 3;
+                               analog.spec->spec_digits = 3;
+                               analog.data = &devc->config[i].output_voltage_max;
+                               sr_session_send(sdi, &packet);
+                       }
+
+                       devc->reply_pending = FALSE;
+               }
+       } else {
+               if (!devc->reply_pending) {
+                       for (i = 0; i < devc->model->num_channels; i++)
+                               gpd_send_cmd(serial, "IOUT%d?\nVOUT%d?\n",
+                                       i + 1, i + 1);
+                       devc->req_sent_at = g_get_monotonic_time();
+                       devc->reply_pending = TRUE;
+               }
+       }
+
+       if (sr_sw_limits_check(&devc->limits)) {
+               sr_dev_acquisition_stop(sdi);
+               return TRUE;
+       }
+
+       return TRUE;
+}
diff --git a/src/hardware/gwinstek-gpd/protocol.h b/src/hardware/gwinstek-gpd/protocol.h
new file mode 100644 (file)
index 0000000..615bb24
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Bastian Schmitz <bastian.schmitz@udo.edu>
+ *
+ * This program is free software: you can 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_GWINSTEK_GPD_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_GWINSTEK_GPD_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "gwinstek-gpd"
+
+enum {
+       GPD_2303S,
+};
+
+/* Maximum number of output channels handled by this driver. */
+#define MAX_CHANNELS 2
+
+#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 gpd_model {
+       int modelid;
+       const char *name;
+       int channel_modes;
+       unsigned int num_channels;
+       struct channel_spec channels[MAX_CHANNELS];
+};
+
+struct per_channel_config {
+       /* Received from device. */
+       float output_voltage_last;
+       float output_current_last;
+       /* Set by frontend. */
+       float output_voltage_max;
+       float output_current_max;
+};
+
+struct dev_context {
+       /* Received from device. */
+       gboolean output_enabled;
+       int64_t req_sent_at;
+       gboolean reply_pending;
+
+       struct sr_sw_limits limits;
+       int channel_mode;
+       struct per_channel_config *config;
+       const struct gpd_model *model;
+};
+
+SR_PRIV int gpd_send_cmd(struct sr_serial_dev_inst *serial, const char *cmd, ...);
+SR_PRIV int gpd_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int gpd_receive_reply(struct sr_serial_dev_inst *serial, char *buf, int buflen);
+
+#endif
index 89042da7a4b5adda5c399ed78ca0046f8c04508c..1274b919bf61dd20d8f837e4c47da0aad21e9c3b 100644 (file)
@@ -31,15 +31,15 @@ static const char *manufacturers[] = {
        "Rohde&Schwarz",
 };
 
-static const uint32_t drvopts[] = {
-       SR_CONF_OSCILLOSCOPE,
-};
-
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
        SR_CONF_SERIALCOMM,
 };
 
+static const uint32_t drvopts[] = {
+       SR_CONF_OSCILLOSCOPE,
+};
+
 enum {
        CG_INVALID = -1,
        CG_NONE,
@@ -47,18 +47,7 @@ enum {
        CG_DIGITAL,
 };
 
-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)
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 {
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
@@ -73,7 +62,7 @@ static struct sr_dev_inst *hmo_probe_serial_device(struct sr_scpi_dev_inst *scpi
                goto fail;
        }
 
-       if (check_manufacturer(hw_info->manufacturer) != SR_OK)
+       if (std_str_idx_s(hw_info->manufacturer, ARRAY_AND_SIZE(manufacturers)) < 0)
                goto fail;
 
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
@@ -107,57 +96,40 @@ fail:
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       return sr_scpi_scan(di->context, options, hmo_probe_serial_device);
+       return sr_scpi_scan(di->context, options, probe_device);
 }
 
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
-
        hmo_scope_state_free(devc->model_state);
-
        g_free(devc->analog_groups);
        g_free(devc->digital_groups);
-
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
+       if (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;
+       return sr_scpi_close(sdi->conn);
 }
 
 static int check_channel_group(struct dev_context *devc,
                             const struct sr_channel_group *cg)
 {
-       unsigned int i;
        const struct scope_config *model;
 
        model = devc->model_config;
@@ -165,24 +137,21 @@ static int check_channel_group(struct dev_context *devc,
        if (!cg)
                return CG_NONE;
 
-       for (i = 0; i < model->analog_channels; i++)
-               if (cg == devc->analog_groups[i])
-                       return CG_ANALOG;
+       if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) >= 0)
+               return CG_ANALOG;
 
-       for (i = 0; i < model->digital_pods; i++)
-               if (cg == devc->digital_groups[i])
-                       return CG_DIGITAL;
+       if (std_cg_idx(cg, devc->digital_groups, model->digital_pods) >= 0)
+               return CG_DIGITAL;
 
        sr_err("Invalid channel group specified.");
 
        return CG_INVALID;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-                     const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret, cg_type;
-       unsigned int i;
+       int cg_type, idx;
        struct dev_context *devc;
        const struct scope_config *model;
        struct scope_state *state;
@@ -195,126 +164,73 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        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_HDIV:
                *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.");
+               if (!cg)
                        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;
-               }
+               if (cg_type != CG_ANALOG)
+                       return SR_ERR_NA;
+               if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) < 0)
+                       return SR_ERR_ARG;
+               *data = g_variant_new_int32(model->num_ydivs);
                break;
        case SR_CONF_VDIV:
-               if (cg_type == CG_NONE) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        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;
-               }
+               if (cg_type != CG_ANALOG)
+                       return SR_ERR_NA;
+               if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               *data = g_variant_new("(tt)",
+                                     (*model->vdivs)[state->analog_channels[idx].vdiv][0],
+                                     (*model->vdivs)[state->analog_channels[idx].vdiv][1]);
                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.");
+               if (!cg)
                        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;
-               }
+               if (cg_type != CG_ANALOG)
+                       return SR_ERR_NA;
+               if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               *data = g_variant_new_string((*model->coupling_options)[state->analog_channels[idx].coupling]);
                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 SR_ERR_NA;
        }
 
-       return g_variant_builder_end(&gvb);
+       return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-                     const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret, cg_type;
-       unsigned int i, j;
+       int ret, cg_type, idx, j;
        char command[MAX_COMMAND_SIZE], float_str[30];
        struct dev_context *devc;
        const struct scope_config *model;
        struct scope_state *state;
-       const char *tmp;
-       uint64_t p, q;
        double tmp_d;
        gboolean update_sample_rate;
 
@@ -330,139 +246,89 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        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;
-               }
+               if ((idx = std_str_idx(data, *model->trigger_sources, model->num_trigger_sources)) < 0)
+                       return SR_ERR_ARG;
+               state->trigger_source = idx;
+               g_snprintf(command, sizeof(command),
+                          (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SOURCE],
+                          (*model->trigger_sources)[idx]);
+               ret = sr_scpi_send(sdi->conn, command);
                break;
        case SR_CONF_VDIV:
-               if (cg_type == CG_NONE) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        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;
-               }
+               if ((idx = std_u64_tuple_idx(data, *model->vdivs, model->num_vdivs)) < 0)
+                       return SR_ERR_ARG;
+               if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               state->analog_channels[j].vdiv = idx;
+               g_ascii_formatd(float_str, sizeof(float_str), "%E",
+                       (float) (*model->vdivs)[idx][0] / (*model->vdivs)[idx][1]);
+               g_snprintf(command, sizeof(command),
+                          (*model->scpi_dialect)[SCPI_CMD_SET_VERTICAL_DIV],
+                          j + 1, float_str);
+               if (sr_scpi_send(sdi->conn, command) != SR_OK ||
+                   sr_scpi_get_opc(sdi->conn) != SR_OK)
+                       return SR_ERR;
+               ret = SR_OK;
                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;
-               }
+               if ((idx = std_u64_tuple_idx(data, *model->timebases, model->num_timebases)) < 0)
+                       return SR_ERR_ARG;
+               state->timebase = idx;
+               g_ascii_formatd(float_str, sizeof(float_str), "%E",
+                       (float) (*model->timebases)[idx][0] / (*model->timebases)[idx][1]);
+               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;
        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);
-               for (i = 0; (*model->trigger_slopes)[i]; i++) {
-                       if (g_strcmp0(tmp, (*model->trigger_slopes)[i]) != 0)
-                               continue;
-                       state->trigger_slope = i;
-                       g_snprintf(command, sizeof(command),
-                                  (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SLOPE],
-                                  (*model->trigger_slopes)[i]);
-
-                       ret = sr_scpi_send(sdi->conn, command);
-                       break;
-               }
+               if ((idx = std_str_idx(data, *model->trigger_slopes, model->num_trigger_slopes)) < 0)
+                       return SR_ERR_ARG;
+               state->trigger_slope = idx;
+               g_snprintf(command, sizeof(command),
+                          (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SLOPE],
+                          (*model->trigger_slopes)[idx]);
+               ret = sr_scpi_send(sdi->conn, command);
                break;
        case SR_CONF_COUPLING:
-               if (cg_type == CG_NONE) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        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;
-               }
+               if ((idx = std_str_idx(data, *model->coupling_options, model->num_coupling_options)) < 0)
+                       return SR_ERR_ARG;
+               if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               state->analog_channels[j].coupling = idx;
+               g_snprintf(command, sizeof(command),
+                          (*model->scpi_dialect)[SCPI_CMD_SET_COUPLING],
+                          j + 1, (*model->coupling_options)[idx]);
+               if (sr_scpi_send(sdi->conn, command) != SR_OK ||
+                   sr_scpi_get_opc(sdi->conn) != SR_OK)
+                       return SR_ERR;
+               ret = SR_OK;
                break;
        default:
                ret = SR_ERR_NA;
@@ -478,8 +344,8 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return ret;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-                      const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        int cg_type = CG_NONE;
        struct dev_context *devc = NULL;
@@ -495,53 +361,48 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
 
        switch (key) {
        case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+               *data = std_gvar_array_u32(ARRAY_AND_SIZE(scanopts));
                break;
        case SR_CONF_DEVICE_OPTIONS:
-               if (cg_type == CG_NONE) {
+               if (!cg) {
                        if (model)
-                               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       model->devopts, model->num_devopts, sizeof(uint32_t));
+                               *data = std_gvar_array_u32(*model->devopts, model->num_devopts);
                        else
-                               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+                               *data = std_gvar_array_u32(ARRAY_AND_SIZE(drvopts));
                } else if (cg_type == CG_ANALOG) {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               model->analog_devopts, model->num_analog_devopts,
-                               sizeof(uint32_t));
+                       *data = std_gvar_array_u32(*model->devopts_cg_analog, model->num_devopts_cg_analog);
                } else {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               NULL, 0, sizeof(uint32_t));
+                       *data = std_gvar_array_u32(NULL, 0);
                }
                break;
        case SR_CONF_COUPLING:
-               if (cg_type == CG_NONE)
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               *data = g_variant_new_strv(*model->coupling_options,
-                          g_strv_length((char **)*model->coupling_options));
+               if (!model)
+                       return SR_ERR_ARG;
+               *data = g_variant_new_strv(*model->coupling_options, model->num_coupling_options);
                break;
        case SR_CONF_TRIGGER_SOURCE:
                if (!model)
                        return SR_ERR_ARG;
-               *data = g_variant_new_strv(*model->trigger_sources,
-                          g_strv_length((char **)*model->trigger_sources));
+               *data = g_variant_new_strv(*model->trigger_sources, model->num_trigger_sources);
                break;
        case SR_CONF_TRIGGER_SLOPE:
                if (!model)
                        return SR_ERR_ARG;
-               *data = g_variant_new_strv(*model->trigger_slopes,
-                          g_strv_length((char **)*model->trigger_slopes));
+               *data = g_variant_new_strv(*model->trigger_slopes, model->num_trigger_slopes);
                break;
        case SR_CONF_TIMEBASE:
                if (!model)
                        return SR_ERR_ARG;
-               *data = build_tuples(model->timebases, model->num_timebases);
+               *data = std_gvar_tuple_array(*model->timebases, model->num_timebases);
                break;
        case SR_CONF_VDIV:
-               if (cg_type == CG_NONE)
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               *data = build_tuples(model->vdivs, model->num_vdivs);
+               if (!model)
+                       return SR_ERR_ARG;
+               *data = std_gvar_tuple_array(*model->vdivs, model->num_vdivs);
                break;
        default:
                return SR_ERR_NA;
@@ -648,6 +509,7 @@ static int hmo_setup_channels(const struct sr_dev_inst *sdi)
        struct sr_channel *ch;
        struct dev_context *devc;
        struct sr_scpi_dev_inst *scpi;
+       int ret;
 
        devc = sdi->priv;
        scpi = sdi->conn;
@@ -667,8 +529,10 @@ static int hmo_setup_channels(const struct sr_dev_inst *sdi)
                                   (*model->scpi_dialect)[SCPI_CMD_SET_ANALOG_CHAN_STATE],
                                   ch->index + 1, ch->enabled);
 
-                       if (sr_scpi_send(scpi, command) != SR_OK)
+                       if (sr_scpi_send(scpi, command) != SR_OK) {
+                               g_free(pod_enabled);
                                return SR_ERR;
+                       }
                        state->analog_channels[ch->index].state = ch->enabled;
                        setup_changed = TRUE;
                        break;
@@ -686,30 +550,37 @@ static int hmo_setup_channels(const struct sr_dev_inst *sdi)
                                   (*model->scpi_dialect)[SCPI_CMD_SET_DIG_CHAN_STATE],
                                   ch->index, ch->enabled);
 
-                       if (sr_scpi_send(scpi, command) != SR_OK)
+                       if (sr_scpi_send(scpi, command) != SR_OK) {
+                               g_free(pod_enabled);
                                return SR_ERR;
+                       }
 
                        state->digital_channels[ch->index] = ch->enabled;
                        setup_changed = TRUE;
                        break;
                default:
+                       g_free(pod_enabled);
                        return SR_ERR;
                }
        }
 
-       for (i = 1; i <= model->digital_pods; i++) {
-               if (state->digital_pods[i - 1] == pod_enabled[i - 1])
+       ret = SR_OK;
+       for (i = 0; i < model->digital_pods; i++) {
+               if (state->digital_pods[i] == pod_enabled[i])
                        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];
+                          i + 1, pod_enabled[i]);
+               if (sr_scpi_send(scpi, command) != SR_OK) {
+                       ret = SR_ERR;
+                       break;
+               }
+               state->digital_pods[i] = pod_enabled[i];
                setup_changed = TRUE;
        }
-
        g_free(pod_enabled);
+       if (ret != SR_OK)
+               return ret;
 
        if (setup_changed && hmo_update_sample_rate(sdi) != SR_OK)
                return SR_ERR;
@@ -727,9 +598,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct sr_scpi_dev_inst *scpi;
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        scpi = sdi->conn;
        devc = sdi->priv;
 
@@ -811,9 +679,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 
        std_session_send_df_end(sdi);
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        devc->num_frames = 0;
index f44db1a272bb4eb31e73c7802ecd246891419a06..059ae4399cc612f241b6b5d0dff7e3e1f9ec6994 100644 (file)
@@ -57,7 +57,7 @@ static const char *hameg_scpi_dialect[] = {
        [SCPI_CMD_GET_PROBE_UNIT]           = ":PROB%d:SET:ATT:UNIT?",
 };
 
-static const uint32_t hmo_devopts[] = {
+static const uint32_t devopts[] = {
        SR_CONF_OSCILLOSCOPE,
        SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_SAMPLERATE | SR_CONF_GET,
@@ -68,98 +68,46 @@ static const uint32_t hmo_devopts[] = {
        SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const uint32_t hmo_analog_devopts[] = {
+static const uint32_t devopts_cg_analog[] = {
        SR_CONF_NUM_VDIV | SR_CONF_GET,
        SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const char *hmo_coupling_options[] = {
+static const char *coupling_options[] = {
        "AC",  // AC with 50 Ohm termination (152x, 202x, 30xx, 1202)
        "ACL", // AC with 1 MOhm termination
        "DC",  // DC with 50 Ohm termination
        "DCL", // DC with 1 MOhm termination
        "GND",
-       NULL,
 };
 
 static const char *scope_trigger_slopes[] = {
        "POS",
        "NEG",
        "EITH",
-       NULL,
 };
 
-static const char *hmo_compact2_trigger_sources[] = {
-       "CH1",
-       "CH2",
-       "LINE",
-       "EXT",
-       "PATT",
-       "BUS1",
-       "BUS2",
-       "D0",
-       "D1",
-       "D2",
-       "D3",
-       "D4",
-       "D5",
-       "D6",
-       "D7",
-       NULL,
+static const char *compact2_trigger_sources[] = {
+       "CH1", "CH2",
+       "LINE", "EXT", "PATT", "BUS1", "BUS2",
+       "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
 };
 
-static const char *hmo_compact4_trigger_sources[] = {
-       "CH1",
-       "CH2",
-       "CH3",
-       "CH4",
-       "LINE",
-       "EXT",
-       "PATT",
-       "BUS1",
-       "BUS2",
-       "D0",
-       "D1",
-       "D2",
-       "D3",
-       "D4",
-       "D5",
-       "D6",
-       "D7",
-       NULL,
+static const char *compact4_trigger_sources[] = {
+       "CH1", "CH2", "CH3", "CH4",
+       "LINE", "EXT", "PATT", "BUS1", "BUS2",
+       "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
 };
 
-static const char *hmo_compact4_dig16_trigger_sources[] = {
-       "CH1",
-       "CH2",
-       "CH3",
-       "CH4",
-       "LINE",
-       "EXT",
-       "PATT",
-       "BUS1",
-       "BUS2",
-       "D0",
-       "D1",
-       "D2",
-       "D3",
-       "D4",
-       "D5",
-       "D6",
-       "D7",
-       "D8",
-       "D9",
-       "D10",
-       "D11",
-       "D12",
-       "D13",
-       "D14",
-       "D15",
-       NULL,
+static const char *compact4_dig16_trigger_sources[] = {
+       "CH1", "CH2", "CH3", "CH4",
+       "LINE", "EXT", "PATT", "BUS1", "BUS2",
+       "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+       "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15",
 };
 
-static const uint64_t hmo_timebases[][2] = {
+static const uint64_t timebases[][2] = {
        /* nanoseconds */
        { 2, 1000000000 },
        { 5, 1000000000 },
@@ -198,7 +146,7 @@ static const uint64_t hmo_timebases[][2] = {
        { 50, 1 },
 };
 
-static const uint64_t hmo_vdivs[][2] = {
+static const uint64_t vdivs[][2] = {
        /* millivolts */
        { 1, 1000 },
        { 2, 1000 },
@@ -219,29 +167,12 @@ static const uint64_t hmo_vdivs[][2] = {
 };
 
 static const char *scope_analog_channel_names[] = {
-       "CH1",
-       "CH2",
-       "CH3",
-       "CH4",
+       "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",
+       "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+       "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15",
 };
 
 static const struct scope_config scope_models[] = {
@@ -256,21 +187,26 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names,
 
-               .devopts = &hmo_devopts,
-               .num_devopts = ARRAY_SIZE(hmo_devopts),
+               .devopts = &devopts,
+               .num_devopts = ARRAY_SIZE(devopts),
 
-               .analog_devopts = &hmo_analog_devopts,
-               .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+               .devopts_cg_analog = &devopts_cg_analog,
+               .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog),
+
+               .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+               .trigger_sources = &compact2_trigger_sources,
+               .num_trigger_sources = ARRAY_SIZE(compact2_trigger_sources),
 
-               .coupling_options = &hmo_coupling_options,
-               .trigger_sources = &hmo_compact2_trigger_sources,
                .trigger_slopes = &scope_trigger_slopes,
+               .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes),
 
-               .timebases = &hmo_timebases,
-               .num_timebases = ARRAY_SIZE(hmo_timebases),
+               .timebases = &timebases,
+               .num_timebases = ARRAY_SIZE(timebases),
 
-               .vdivs = &hmo_vdivs,
-               .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+               .vdivs = &vdivs,
+               .num_vdivs = ARRAY_SIZE(vdivs),
 
                .num_xdivs = 12,
                .num_ydivs = 8,
@@ -286,21 +222,26 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names,
 
-               .devopts = &hmo_devopts,
-               .num_devopts = ARRAY_SIZE(hmo_devopts),
+               .devopts = &devopts,
+               .num_devopts = ARRAY_SIZE(devopts),
+
+               .devopts_cg_analog = &devopts_cg_analog,
+               .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog),
 
-               .analog_devopts = &hmo_analog_devopts,
-               .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+               .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+               .trigger_sources = &compact4_trigger_sources,
+               .num_trigger_sources = ARRAY_SIZE(compact4_trigger_sources),
 
-               .coupling_options = &hmo_coupling_options,
-               .trigger_sources = &hmo_compact4_trigger_sources,
                .trigger_slopes = &scope_trigger_slopes,
+               .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes),
 
-               .timebases = &hmo_timebases,
-               .num_timebases = ARRAY_SIZE(hmo_timebases),
+               .timebases = &timebases,
+               .num_timebases = ARRAY_SIZE(timebases),
 
-               .vdivs = &hmo_vdivs,
-               .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+               .vdivs = &vdivs,
+               .num_vdivs = ARRAY_SIZE(vdivs),
 
                .num_xdivs = 12,
                .num_ydivs = 8,
@@ -316,21 +257,26 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names,
 
-               .devopts = &hmo_devopts,
-               .num_devopts = ARRAY_SIZE(hmo_devopts),
+               .devopts = &devopts,
+               .num_devopts = ARRAY_SIZE(devopts),
+
+               .devopts_cg_analog = &devopts_cg_analog,
+               .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog),
+
+               .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
 
-               .analog_devopts = &hmo_analog_devopts,
-               .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+               .trigger_sources = &compact4_dig16_trigger_sources,
+               .num_trigger_sources = ARRAY_SIZE(compact4_dig16_trigger_sources),
 
-               .coupling_options = &hmo_coupling_options,
-               .trigger_sources = &hmo_compact4_dig16_trigger_sources,
                .trigger_slopes = &scope_trigger_slopes,
+               .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes),
 
-               .timebases = &hmo_timebases,
-               .num_timebases = ARRAY_SIZE(hmo_timebases),
+               .timebases = &timebases,
+               .num_timebases = ARRAY_SIZE(timebases),
 
-               .vdivs = &hmo_vdivs,
-               .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+               .vdivs = &vdivs,
+               .num_vdivs = ARRAY_SIZE(vdivs),
 
                .num_xdivs = 12,
                .num_ydivs = 8,
@@ -380,30 +326,25 @@ static void scope_state_dump(const struct scope_config *config,
 }
 
 static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi,
-               const char *command, const char *(*array)[], int *result)
+               const char *command, const char *(*array)[], unsigned int n, int *result)
 {
        char *tmp;
-       unsigned int i;
+       int idx;
 
        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) {
+       if ((idx = std_str_idx_s(tmp, *array, n)) < 0) {
                g_free(tmp);
-               return SR_ERR;
+               return SR_ERR_ARG;
        }
 
+       *result = idx;
+
+       g_free(tmp);
+
        return SR_OK;
 }
 
@@ -438,13 +379,29 @@ static int array_float_get(gchar *value, const uint64_t array[][2],
        return SR_ERR;
 }
 
-static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
+static struct sr_channel *get_channel_by_index_and_type(GSList *channel_lhead,
+                                                       int index, int type)
+{
+       while (channel_lhead) {
+               struct sr_channel *ch = channel_lhead->data;
+               if (ch->index == index && ch->type == type)
+                       return ch;
+
+               channel_lhead = channel_lhead->next;
+       }
+
+       return 0;
+}
+
+static int analog_channel_state_get(struct sr_dev_inst *sdi,
                                    const struct scope_config *config,
                                    struct scope_state *state)
 {
        unsigned int i, j;
        char command[MAX_COMMAND_SIZE];
        char *tmp_str;
+       struct sr_channel *ch;
+       struct sr_scpi_dev_inst *scpi = sdi->conn;
 
        for (i = 0; i < config->analog_channels; i++) {
                g_snprintf(command, sizeof(command),
@@ -455,6 +412,10 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
                                     &state->analog_channels[i].state) != SR_OK)
                        return SR_ERR;
 
+               ch = get_channel_by_index_and_type(sdi->channels, i, SR_CHANNEL_ANALOG);
+               if (ch)
+                       ch->enabled = state->analog_channels[i].state;
+
                g_snprintf(command, sizeof(command),
                           (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_DIV],
                           i + 1);
@@ -462,8 +423,7 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
                if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK)
                        return SR_ERR;
 
-               if (array_float_get(tmp_str, hmo_vdivs, ARRAY_SIZE(hmo_vdivs),
-                               &j) != SR_OK) {
+               if (array_float_get(tmp_str, ARRAY_AND_SIZE(vdivs), &j) != SR_OK) {
                        g_free(tmp_str);
                        sr_err("Could not determine array index for vertical div scale.");
                        return SR_ERR;
@@ -485,6 +445,7 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
                           i + 1);
 
                if (scope_state_get_array_option(scpi, command, config->coupling_options,
+                                        config->num_coupling_options,
                                         &state->analog_channels[i].coupling) != SR_OK)
                        return SR_ERR;
 
@@ -505,12 +466,14 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
        return SR_OK;
 }
 
-static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi,
+static int digital_channel_state_get(struct sr_dev_inst *sdi,
                                     const struct scope_config *config,
                                     struct scope_state *state)
 {
        unsigned int i;
        char command[MAX_COMMAND_SIZE];
+       struct sr_channel *ch;
+       struct sr_scpi_dev_inst *scpi = sdi->conn;
 
        for (i = 0; i < config->digital_channels; i++) {
                g_snprintf(command, sizeof(command),
@@ -520,6 +483,10 @@ static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi,
                if (sr_scpi_get_bool(scpi, command,
                                     &state->digital_channels[i]) != SR_OK)
                        return SR_ERR;
+
+               ch = get_channel_by_index_and_type(sdi->channels, i, SR_CHANNEL_LOGIC);
+               if (ch)
+                       ch->enabled = state->digital_channels[i];
        }
 
        for (i = 0; i < config->digital_pods; i++) {
@@ -540,7 +507,6 @@ SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
        struct scope_state *state;
        const struct scope_config *config;
-
        int tmp;
        unsigned int i;
        float tmp_float;
@@ -554,8 +520,21 @@ SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi)
        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);
+               if (!state->analog_channels[i].state)
+                       continue;
+               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])
+                               continue;
+                       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);
@@ -564,19 +543,6 @@ SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi)
                }
        }
 
-       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) {
@@ -612,10 +578,10 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
 
        sr_info("Fetching scope state");
 
-       if (analog_channel_state_get(sdi->conn, config, state) != SR_OK)
+       if (analog_channel_state_get(sdi, config, state) != SR_OK)
                return SR_ERR;
 
-       if (digital_channel_state_get(sdi->conn, config, state) != SR_OK)
+       if (digital_channel_state_get(sdi, config, state) != SR_OK)
                return SR_ERR;
 
        if (sr_scpi_get_float(sdi->conn,
@@ -628,8 +594,7 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
                        &tmp_str) != SR_OK)
                return SR_ERR;
 
-       if (array_float_get(tmp_str, hmo_timebases, ARRAY_SIZE(hmo_timebases),
-                       &i) != SR_OK) {
+       if (array_float_get(tmp_str, ARRAY_AND_SIZE(timebases), &i) != SR_OK) {
                g_free(tmp_str);
                sr_err("Could not determine array index for time base.");
                return SR_ERR;
@@ -650,12 +615,14 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
 
        if (scope_state_get_array_option(sdi->conn,
                        (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SOURCE],
-                       config->trigger_sources, &state->trigger_source) != SR_OK)
+                       config->trigger_sources, config->num_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)
+                       (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE],
+                       config->trigger_slopes, config->num_trigger_slopes,
+                       &state->trigger_slope) != SR_OK)
                return SR_ERR;
 
        if (hmo_update_sample_rate(sdi) != SR_OK)
@@ -693,11 +660,11 @@ SR_PRIV void hmo_scope_state_free(struct scope_state *state)
 
 SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
 {
-       char tmp[25];
        int model_index;
        unsigned int i, j, group;
        struct sr_channel *ch;
        struct dev_context *devc;
+       int ret;
 
        devc = sdi->priv;
        model_index = -1;
@@ -721,9 +688,13 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
 
        devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) *
                                        scope_models[model_index].analog_channels);
-
        devc->digital_groups = g_malloc0(sizeof(struct sr_channel_group*) *
                                         scope_models[model_index].digital_pods);
+       if (!devc->analog_groups || !devc->digital_groups) {
+               g_free(devc->analog_groups);
+               g_free(devc->digital_groups);
+               return SR_ERR_MALLOC;
+       }
 
        /* Add analog channels. */
        for (i = 0; i < scope_models[model_index].analog_channels; i++) {
@@ -741,15 +712,19 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
        }
 
        /* Add digital channel groups. */
+       ret = SR_OK;
        for (i = 0; i < scope_models[model_index].digital_pods; i++) {
-               g_snprintf(tmp, 25, "POD%d", i);
-
                devc->digital_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
-
-               devc->digital_groups[i]->name = g_strdup(tmp);
+               if (!devc->digital_groups[i]) {
+                       ret = SR_ERR_MALLOC;
+                       break;
+               }
+               devc->digital_groups[i]->name = g_strdup_printf("POD%d", i);
                sdi->channel_groups = g_slist_append(sdi->channel_groups,
                                   devc->digital_groups[i]);
        }
+       if (ret != SR_OK)
+               return ret;
 
        /* Add digital channels. */
        for (i = 0; i < scope_models[model_index].digital_channels; i++) {
@@ -1018,7 +993,7 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
         * the first enabled channel.
         */
        if (++devc->num_frames == devc->frame_limit) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                hmo_cleanup_logic_data(devc);
        } else {
                devc->current_channel = devc->enabled_channels;
index ccdcb90cf3d3fe4b4ddce0e8280c387c46502159..43220dd789a78c6f2ce10a9981af29a3bb34844c 100644 (file)
@@ -46,8 +46,8 @@ struct scope_config {
        const uint32_t (*devopts)[];
        const uint8_t num_devopts;
 
-       const uint32_t (*analog_devopts)[];
-       const uint8_t num_analog_devopts;
+       const uint32_t (*devopts_cg_analog)[];
+       const uint8_t num_devopts_cg_analog;
 
        const char *(*coupling_options)[];
        const uint8_t num_coupling_options;
@@ -56,6 +56,7 @@ struct scope_config {
        const uint8_t num_trigger_sources;
 
        const char *(*trigger_slopes)[];
+       const uint8_t num_trigger_slopes;
 
        const uint64_t (*timebases)[][2];
        const uint8_t num_timebases;
@@ -92,7 +93,6 @@ struct scope_state {
        uint64_t sample_rate;
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        const void *model_config;
        void *model_state;
diff --git a/src/hardware/hantek-4032l/api.c b/src/hardware/hantek-4032l/api.c
new file mode 100644 (file)
index 0000000..6ee46de
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Andreas Zschunke <andreas.zschunke@gmx.net>
+ * Copyright (C) 2017 Andrej Valek <andy@skyrain.eu>
+ * Copyright (C) 2017 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 <config.h>
+#include "protocol.h"
+
+#define USB_INTERFACE 0
+#define NUM_CHANNELS 32
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+       SR_CONF_CONN | SR_CONF_GET,
+       SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_EXTERNAL_CLOCK_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint32_t devopts_fpga_zero[] = {
+       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+       SR_CONF_CONN | SR_CONF_GET,
+};
+
+static const uint32_t devopts_cg[] = {
+       SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const char *cg_names[] = {
+       "A", "B",
+};
+
+static const char *signal_edges[] = {
+       [H4032L_CLOCK_EDGE_TYPE_RISE] = "rising",
+       [H4032L_CLOCK_EDGE_TYPE_FALL] = "falling",
+       [H4032L_CLOCK_EDGE_TYPE_BOTH] = "both",
+};
+
+static const char *ext_clock_sources[] = {
+       [H4032L_EXT_CLOCK_SOURCE_CHANNEL_A] = "ACLK",
+       [H4032L_EXT_CLOCK_SOURCE_CHANNEL_B] = "BCLK"
+};
+
+static const uint8_t ext_clock_edges[2][3] = {
+       {
+               H4032L_CLOCK_EDGE_TYPE_RISE_A,
+               H4032L_CLOCK_EDGE_TYPE_FALL_A,
+               H4032L_CLOCK_EDGE_TYPE_BOTH_A
+       },
+       {
+               H4032L_CLOCK_EDGE_TYPE_RISE_B,
+               H4032L_CLOCK_EDGE_TYPE_FALL_B,
+               H4032L_CLOCK_EDGE_TYPE_BOTH_B
+       }
+};
+
+static const int32_t trigger_matches[] = {
+       SR_TRIGGER_ZERO,
+       SR_TRIGGER_ONE,
+       SR_TRIGGER_RISING,
+       SR_TRIGGER_FALLING,
+       SR_TRIGGER_EDGE,
+};
+
+static const uint64_t samplerates[] = {
+       SR_KHZ(1),
+       SR_KHZ(2),
+       SR_KHZ(4),
+       SR_KHZ(8),
+       SR_KHZ(16),
+       SR_HZ(31250),
+       SR_HZ(62500),
+       SR_KHZ(125),
+       SR_KHZ(250),
+       SR_KHZ(500),
+       SR_KHZ(625),
+       SR_HZ(781250),
+       SR_MHZ(1),
+       SR_KHZ(1250),
+       SR_HZ(1562500),
+       SR_MHZ(2),
+       SR_KHZ(2500),
+       SR_KHZ(3125),
+       SR_MHZ(4),
+       SR_MHZ(5),
+       SR_KHZ(6250),
+       SR_MHZ(10),
+       SR_KHZ(12500),
+       SR_MHZ(20),
+       SR_MHZ(25),
+       SR_MHZ(40),
+       SR_MHZ(50),
+       SR_MHZ(80),
+       SR_MHZ(100),
+       SR_MHZ(160),
+       SR_MHZ(200),
+       SR_MHZ(320),
+       SR_MHZ(400),
+};
+
+static const uint64_t samplerates_hw[] = {
+       SR_MHZ(100),
+       SR_MHZ(50),
+       SR_MHZ(25),
+       SR_KHZ(12500),
+       SR_KHZ(6250),
+       SR_KHZ(3125),
+       SR_HZ(1562500),
+       SR_HZ(781250),
+       SR_MHZ(80),
+       SR_MHZ(40),
+       SR_MHZ(20),
+       SR_MHZ(10),
+       SR_MHZ(5),
+       SR_KHZ(2500),
+       SR_KHZ(1250),
+       SR_KHZ(625),
+       SR_MHZ(4),
+       SR_MHZ(2),
+       SR_MHZ(1),
+       SR_KHZ(500),
+       SR_KHZ(250),
+       SR_KHZ(125),
+       SR_HZ(62500),
+       SR_HZ(31250),
+       SR_KHZ(16),
+       SR_KHZ(8),
+       SR_KHZ(4),
+       SR_KHZ(2),
+       SR_KHZ(1),
+       0,
+       0,
+       0,
+       SR_MHZ(200),
+       SR_MHZ(160),
+       SR_MHZ(400),
+       SR_MHZ(320),
+};
+
+SR_PRIV struct sr_dev_driver hantek_4032l_driver_info;
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       struct drv_context *drvc = di->context;
+       GSList *l, *devices, *conn_devices;
+       libusb_device **devlist;
+       struct libusb_device_descriptor des;
+       const char *conn;
+       int i;
+       char connection_id[64];
+       struct sr_channel_group *cg;
+       struct sr_dev_inst *sdi;
+       struct sr_channel *ch;
+
+       devices = NULL;
+       conn_devices = NULL;
+       drvc->instances = NULL;
+       conn = NULL;
+
+       for (l = options; l; l = l->next) {
+               struct sr_config *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;
+
+       libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+       for (i = 0; devlist[i]; i++) {
+               if (conn) {
+                       struct sr_usb_dev_inst *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;
+               }
+
+               libusb_get_device_descriptor(devlist[i], &des);
+
+               if (des.idVendor != H4032L_USB_VENDOR ||
+                   des.idProduct != H4032L_USB_PRODUCT)
+                       continue;
+
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
+
+               sdi = g_malloc0(sizeof(struct sr_dev_inst));
+               sdi->driver = &hantek_4032l_driver_info;
+               sdi->vendor = g_strdup("Hantek");
+               sdi->model = g_strdup("4032L");
+               sdi->connection_id = g_strdup(connection_id);
+
+               struct sr_channel_group *channel_groups[2];
+               for (int j = 0; j < 2; j++) {
+                       cg = g_malloc0(sizeof(struct sr_channel_group));
+                       cg->name = g_strdup_printf("%c", 'A' + j);
+                       channel_groups[j] = cg;
+                       sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+               }
+
+               /* Assemble channel list and add channel to channel groups. */
+               for (int j = 0; j < NUM_CHANNELS; j++) {
+                       char channel_name[4];
+                       sprintf(channel_name, "%c%d", 'A' + (j & 1), j / 2);
+                       ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE, channel_name);
+                       cg = channel_groups[j & 1];
+                       cg->channels = g_slist_append(cg->channels, ch);
+               }
+
+               struct dev_context *devc = g_malloc0(sizeof(struct dev_context));
+
+               /* Initialize command packet. */
+               devc->cmd_pkt.magic = H4032L_CMD_PKT_MAGIC;
+               devc->cmd_pkt.sample_size = 16 * 1024;
+               devc->sample_rate = 0;
+
+               devc->status = H4032L_STATUS_IDLE;
+
+               devc->capture_ratio = 5;
+               devc->external_clock = FALSE;
+               devc->clock_edge = H4032L_CLOCK_EDGE_TYPE_RISE;
+
+               /* Create array of thresholds from min to max. */
+               GVariant *thresholds = std_gvar_min_max_step_thresholds(
+                       H4032L_THR_VOLTAGE_MIN, H4032L_THR_VOLTAGE_MAX,
+                       H4032L_THR_VOLTAGE_STEP);
+               /* Take default threshold value from array (FP workaround). */
+               g_variant_get_child(thresholds, H4032L_THR_VOLTAGE_DEFAULT,
+                       "(dd)", &devc->cur_threshold[0], &devc->cur_threshold[1]);
+
+               sdi->priv = devc;
+               devices = g_slist_append(devices, sdi);
+
+               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);
+       }
+
+       g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+       libusb_free_device_list(devlist, 1);
+
+       return std_scan_complete(di, devices);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       struct sr_usb_dev_inst *usb = sdi->conn;
+       int ret;
+
+       ret = h4032l_dev_open(sdi);
+       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;
+       }
+
+       /* Get FPGA version. */
+       if ((ret = h4032l_get_fpga_version(sdi)) != SR_OK)
+               return ret;
+
+       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_BUG;
+
+       sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
+               usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
+       libusb_release_interface(usb->devhdl, USB_INTERFACE);
+       libusb_close(usb->devhdl);
+       usb->devhdl = NULL;
+
+       return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc = sdi->priv;
+       struct sr_usb_dev_inst *usb;
+       int idx;
+
+       switch (key) {
+       case SR_CONF_VOLTAGE_THRESHOLD:
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if ((idx = std_str_idx_s(cg->name, ARRAY_AND_SIZE(cg_names))) < 0)
+                       return SR_ERR_CHANNEL_GROUP;
+               *data = std_gvar_tuple_double(
+                       devc->cur_threshold[idx], devc->cur_threshold[idx]);
+               break;
+       case SR_CONF_SAMPLERATE:
+               *data = g_variant_new_uint64(samplerates_hw[devc->sample_rate]);
+               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->cmd_pkt.sample_size);
+               break;
+       case SR_CONF_EXTERNAL_CLOCK:
+               *data = g_variant_new_boolean(devc->external_clock);
+               break;
+       case SR_CONF_EXTERNAL_CLOCK_SOURCE:
+               *data = g_variant_new_string(ext_clock_sources[devc->external_clock_source]);
+               break;
+       case SR_CONF_CONN:
+               if (!sdi || !(usb = sdi->conn))
+                       return SR_ERR_ARG;
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
+               break;
+       case SR_CONF_CLOCK_EDGE:
+               *data = g_variant_new_string(signal_edges[devc->clock_edge]);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       int idx;
+       struct dev_context *devc = sdi->priv;
+       struct h4032l_cmd_pkt *cmd_pkt = &devc->cmd_pkt;
+       uint64_t sample_rate, num_samples;
+       double low, high;
+
+       switch (key) {
+       case SR_CONF_SAMPLERATE:
+               idx = 0;
+               sample_rate = g_variant_get_uint64(data);
+               while (idx < (int)ARRAY_SIZE(samplerates_hw) && samplerates_hw[idx] != sample_rate)
+                       idx++;
+               if (idx == ARRAY_SIZE(samplerates_hw) || sample_rate == 0) {
+                       sr_err("Invalid sample rate.");
+                       return SR_ERR_SAMPLERATE;
+               }
+               devc->sample_rate = idx;
+               break;
+       case SR_CONF_CAPTURE_RATIO:
+               devc->capture_ratio = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               num_samples = g_variant_get_uint64(data);
+               num_samples += 511;
+               num_samples &= 0xfffffe00;
+               if (num_samples < H4043L_NUM_SAMPLES_MIN ||
+                   num_samples > H4032L_NUM_SAMPLES_MAX) {
+                       sr_err("Invalid sample range 2k...64M: %"
+                              PRIu64 ".", num_samples);
+                       return SR_ERR;
+               }
+               cmd_pkt->sample_size = num_samples;
+               break;
+       case SR_CONF_VOLTAGE_THRESHOLD:
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if ((idx = std_str_idx_s(cg->name, ARRAY_AND_SIZE(cg_names))) < 0)
+                       return SR_ERR_CHANNEL_GROUP;
+               g_variant_get(data, "(dd)", &low, &high);
+               devc->cur_threshold[idx] = (low + high) / 2.0;
+               break;
+       case SR_CONF_EXTERNAL_CLOCK:
+               devc->external_clock = g_variant_get_boolean(data);
+               break;
+       case SR_CONF_EXTERNAL_CLOCK_SOURCE:
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(ext_clock_sources))) < 0)
+                       return SR_ERR_ARG;
+               devc->external_clock_source = idx;
+               break;
+       case SR_CONF_CLOCK_EDGE:
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edges))) < 0)
+                       return SR_ERR_ARG;
+               devc->clock_edge = idx;
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc = (sdi) ? sdi->priv : NULL;
+
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               if (cg) {
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
+                       break;
+               }
+               /* Disable external clock and edges for FPGA version 0. */
+               if (devc && (!devc->fpga_version))
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts_fpga_zero);
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_SAMPLERATE:
+               *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
+               break;
+       case SR_CONF_TRIGGER_MATCH:
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
+               break;
+       case SR_CONF_VOLTAGE_THRESHOLD:
+               *data = std_gvar_min_max_step_thresholds(H4032L_THR_VOLTAGE_MIN,
+                       H4032L_THR_VOLTAGE_MAX, H4032L_THR_VOLTAGE_STEP);
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               *data = std_gvar_tuple_u64(H4043L_NUM_SAMPLES_MIN, H4032L_NUM_SAMPLES_MAX);
+               break;
+       case SR_CONF_CLOCK_EDGE:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(signal_edges));
+               break;
+       case SR_CONF_EXTERNAL_CLOCK_SOURCE:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(ext_clock_sources));
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct sr_dev_driver *di = sdi->driver;
+       struct drv_context *drvc = di->context;
+       struct dev_context *devc = sdi->priv;
+       struct sr_trigger *trigger = sr_session_trigger_get(sdi->session);
+       struct h4032l_cmd_pkt *cmd_pkt = &devc->cmd_pkt;
+
+       /* Initialize variables. */
+       devc->acq_aborted = FALSE;
+       devc->submitted_transfers = 0;
+       devc->sent_samples = 0;
+
+       /* Calculate packet ratio. */
+       cmd_pkt->pre_trigger_size = (cmd_pkt->sample_size * devc->capture_ratio) / 100;
+       devc->trigger_pos = cmd_pkt->pre_trigger_size;
+
+       /* Set clock edge, when external clock is enabled. */
+       if (devc->external_clock)
+               cmd_pkt->sample_rate = ext_clock_edges[devc->external_clock_source][devc->clock_edge];
+       else
+               cmd_pkt->sample_rate = devc->sample_rate;
+
+       /* Set pwm channel values. */
+       devc->cmd_pkt.pwm_a = h4032l_voltage2pwm(devc->cur_threshold[0]);
+       devc->cmd_pkt.pwm_b = h4032l_voltage2pwm(devc->cur_threshold[1]);
+
+       cmd_pkt->trig_flags.enable_trigger1 = 0;
+       cmd_pkt->trig_flags.enable_trigger2 = 0;
+       cmd_pkt->trig_flags.trigger_and_logic = 0;
+
+       if (trigger && trigger->stages) {
+               GSList *stages = trigger->stages;
+               struct sr_trigger_stage *stage1 = stages->data;
+               if (stages->next) {
+                       sr_err("Only one trigger stage supported for now.");
+                       return SR_ERR;
+               }
+               cmd_pkt->trig_flags.enable_trigger1 = 1;
+               cmd_pkt->trigger[0].flags.edge_type = H4032L_TRIGGER_EDGE_TYPE_DISABLED;
+               cmd_pkt->trigger[0].flags.data_range_enabled = 0;
+               cmd_pkt->trigger[0].flags.time_range_enabled = 0;
+               cmd_pkt->trigger[0].flags.combined_enabled = 0;
+               cmd_pkt->trigger[0].flags.data_range_type = H4032L_TRIGGER_DATA_RANGE_TYPE_MAX;
+               cmd_pkt->trigger[0].data_range_mask = 0;
+               cmd_pkt->trigger[0].data_range_max = 0;
+
+               /* Initialize range mask values. */
+               uint32_t range_mask = 0;
+               uint32_t range_value = 0;
+
+               GSList *channel = stage1->matches;
+               while (channel) {
+                       struct sr_trigger_match *match = channel->data;
+
+                       switch (match->match) {
+                       case SR_TRIGGER_ZERO:
+                               range_mask |= (1 << match->channel->index);
+                               break;
+                       case SR_TRIGGER_ONE:
+                               range_mask |= (1 << match->channel->index);
+                               range_value |= (1 << match->channel->index);
+                               break;
+                       case SR_TRIGGER_RISING:
+                               if (cmd_pkt->trigger[0].flags.edge_type != H4032L_TRIGGER_EDGE_TYPE_DISABLED) {
+                                       sr_err("Only one trigger signal with fall/rising/edge allowed.");
+                                       return SR_ERR;
+                               }
+                               cmd_pkt->trigger[0].flags.edge_type = H4032L_TRIGGER_EDGE_TYPE_RISE;
+                               cmd_pkt->trigger[0].flags.edge_signal = match->channel->index;
+                               break;
+                       case SR_TRIGGER_FALLING:
+                               if (cmd_pkt->trigger[0].flags.edge_type != H4032L_TRIGGER_EDGE_TYPE_DISABLED) {
+                                       sr_err("Only one trigger signal with fall/rising/edge allowed.");
+                                       return SR_ERR;
+                               }
+                               cmd_pkt->trigger[0].flags.edge_type = H4032L_TRIGGER_EDGE_TYPE_FALL;
+                               cmd_pkt->trigger[0].flags.edge_signal = match->channel->index;
+                               break;
+                       case SR_TRIGGER_EDGE:
+                               if (cmd_pkt->trigger[0].flags.edge_type != H4032L_TRIGGER_EDGE_TYPE_DISABLED) {
+                                       sr_err("Only one trigger signal with fall/rising/edge allowed.");
+                                       return SR_ERR;
+                               }
+                               cmd_pkt->trigger[0].flags.edge_type = H4032L_TRIGGER_EDGE_TYPE_TOGGLE;
+                               cmd_pkt->trigger[0].flags.edge_signal = match->channel->index;
+                               break;
+                       default:
+                               sr_err("Unknown trigger value.");
+                               return SR_ERR;
+                       }
+
+                       channel = channel->next;
+               }
+
+               /* Compress range mask value and apply range settings. */
+               if (range_mask) {
+                       cmd_pkt->trigger[0].flags.data_range_enabled = 1;
+                       cmd_pkt->trigger[0].data_range_mask |= (range_mask);
+
+                       uint32_t new_range_value = 0;
+                       uint32_t bit_mask = 1;
+                       while (range_mask) {
+                               if ((range_mask & 1) != 0) {
+                                       new_range_value <<= 1;
+                                       if ((range_value & 1) != 0)
+                                               new_range_value |= bit_mask;
+                                       bit_mask <<= 1;
+                               }
+                               range_mask >>= 1;
+                               range_value >>= 1;
+                       }
+                       cmd_pkt->trigger[0].data_range_max |= range_value;
+               }
+       }
+
+       usb_source_add(sdi->session, drvc->sr_ctx, 1000,
+               h4032l_receive_data, sdi->driver->context);
+
+       /* Start capturing. */
+       return h4032l_start(sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       /* Stop capturing. */
+       return h4032l_stop(sdi);
+}
+
+SR_PRIV struct sr_dev_driver hantek_4032l_driver_info = {
+       .name = "hantek-4032l",
+       .longname = "Hantek 4032L",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(hantek_4032l_driver_info);
diff --git a/src/hardware/hantek-4032l/protocol.c b/src/hardware/hantek-4032l/protocol.c
new file mode 100644 (file)
index 0000000..77afae0
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Andreas Zschunke <andreas.zschunke@gmx.net>
+ * Copyright (C) 2017 Andrej Valek <andy@skyrain.eu>
+ * Copyright (C) 2017 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 <config.h>
+#include "protocol.h"
+
+#define H4032L_USB_TIMEOUT 500
+
+enum h4032l_cmd {
+       CMD_RESET = 0x00b3, /* Also arms the logic analyzer. */
+       CMD_CONFIGURE = 0x2b1a,
+       CMD_STATUS = 0x4b3a,
+       CMD_GET = 0x6b5a
+};
+
+struct h4032l_status_packet {
+       uint32_t magic;
+       uint32_t values;
+       uint32_t status;
+       uint32_t usbxi_data;
+       uint32_t fpga_version;
+};
+
+static void 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]);
+       }
+
+       devc->status = H4032L_STATUS_IDLE;
+}
+
+static void finish_acquisition(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       struct drv_context *drvc = sdi->driver->context;
+
+       std_session_send_df_end(sdi);
+       usb_source_remove(sdi->session, drvc->sr_ctx);
+
+       devc->num_transfers = 0;
+       g_free(devc->transfers);
+}
+
+static void free_transfer(struct libusb_transfer *transfer)
+{
+       struct sr_dev_inst *sdi = transfer->user_data;
+       struct dev_context *devc = sdi->priv;
+       unsigned int i;
+
+       if ((transfer->buffer != (unsigned char *)&devc->cmd_pkt) &&
+           (transfer->buffer != devc->buf)) {
+               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;
+               }
+       }
+
+       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);
+}
+
+static void send_data(struct sr_dev_inst *sdi,
+       uint32_t *data, size_t sample_count)
+{
+       struct dev_context *devc = sdi->priv;
+       struct sr_datafeed_logic logic = {
+               .length = sample_count * sizeof(uint32_t),
+               .unitsize = sizeof(uint32_t),
+               .data = data
+       };
+       const struct sr_datafeed_packet packet = {
+               .type = SR_DF_LOGIC,
+               .payload = &logic
+       };
+       const struct sr_datafeed_packet trig = {
+               .type = SR_DF_TRIGGER,
+               .payload = NULL
+       };
+       size_t trigger_offset;
+
+       if (devc->trigger_pos >= devc->sent_samples &&
+               devc->trigger_pos < (devc->sent_samples + sample_count)) {
+               /* Get trigger position. */
+               trigger_offset = devc->trigger_pos - devc->sent_samples;
+               logic.length = trigger_offset * sizeof(uint32_t);
+               if (logic.length)
+                       sr_session_send(sdi, &packet);
+
+               /* Send trigger position. */
+               sr_session_send(sdi, &trig);
+
+               /* Send rest of data. */
+               logic.length = (sample_count-trigger_offset) * sizeof(uint32_t);
+               logic.data = data + trigger_offset;
+               if (logic.length)
+                       sr_session_send(sdi, &packet);
+       } else {
+               sr_session_send(sdi, &packet);
+       }
+
+       devc->sent_samples += sample_count;
+}
+
+SR_PRIV int h4032l_receive_data(int fd, int revents, void *cb_data)
+{
+       struct timeval tv;
+       struct drv_context *drvc;
+
+       (void)fd;
+       (void)revents;
+
+       drvc = (struct drv_context *)cb_data;
+
+       tv.tv_sec = tv.tv_usec = 0;
+       libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+       return TRUE;
+}
+
+void LIBUSB_CALL h4032l_data_transfer_callback(struct libusb_transfer *transfer)
+{
+       struct sr_dev_inst *const sdi = transfer->user_data;
+       struct dev_context *const devc = sdi->priv;
+       uint32_t max_samples = transfer->actual_length / sizeof(uint32_t);
+       uint32_t *buf;
+       uint32_t num_samples;
+
+       /*
+        * If acquisition has already ended, just free any queued up
+        * transfer that come in.
+        */
+       if (devc->acq_aborted) {
+               free_transfer(transfer);
+               return;
+       }
+
+       if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
+               sr_dbg("%s error: %d.", __func__, transfer->status);
+
+       /* Cancel pending transfers. */
+       if (transfer->actual_length == 0) {
+               resubmit_transfer(transfer);
+               return;
+       }
+
+       buf = (uint32_t *)transfer->buffer;
+
+       num_samples = MIN(devc->remaining_samples, max_samples);
+       devc->remaining_samples -= num_samples;
+       send_data(sdi, buf, num_samples);
+       sr_dbg("Remaining: %d %08X %08X.", devc->remaining_samples,
+               buf[0], buf[1]);
+
+       /* Close data receiving. */
+       if (devc->remaining_samples == 0) {
+               if (buf[num_samples] != H4032L_END_PACKET_MAGIC)
+                       sr_err("Mismatch magic number of end poll.");
+
+               abort_acquisition(devc);
+               free_transfer(transfer);
+       } else {
+               if (((devc->submitted_transfers - 1) * H4032L_DATA_BUFFER_SIZE) <
+                   (int32_t)(devc->remaining_samples * sizeof(uint32_t)))
+                       resubmit_transfer(transfer);
+               else
+                       free_transfer(transfer);
+       }
+}
+
+void LIBUSB_CALL h4032l_usb_callback(struct libusb_transfer *transfer)
+{
+       struct sr_dev_inst *const sdi = transfer->user_data;
+       struct dev_context *const devc = sdi->priv;
+       struct sr_usb_dev_inst *usb = sdi->conn;
+       gboolean cmd = FALSE;
+       uint32_t max_samples = transfer->actual_length / sizeof(uint32_t);
+       uint32_t *buf;
+       struct h4032l_status_packet *status;
+       uint32_t num_samples;
+       int ret;
+
+       /*
+        * If acquisition has already ended, just free any queued up
+        * transfers that come in.
+        */
+       if (devc->acq_aborted) {
+               free_transfer(transfer);
+               return;
+       }
+
+       if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
+               sr_dbg("%s error: %d.", __func__, transfer->status);
+
+       buf = (uint32_t *)transfer->buffer;
+
+       switch (devc->status) {
+       case H4032L_STATUS_IDLE:
+               sr_err("USB callback called in idle.");
+               break;
+       case H4032L_STATUS_CMD_CONFIGURE:
+               /* Select status request as next. */
+               cmd = TRUE;
+               devc->cmd_pkt.cmd = CMD_STATUS;
+               devc->status = H4032L_STATUS_CMD_STATUS;
+               break;
+       case H4032L_STATUS_CMD_STATUS:
+               /* Select status request as next. */
+               devc->status = H4032L_STATUS_RESPONSE_STATUS;
+               break;
+       case H4032L_STATUS_RESPONSE_STATUS:
+               /*
+                * Check magic and if status is complete, then select
+                * First Transfer as next.
+                */
+               status = (struct h4032l_status_packet *)transfer->buffer;
+               if (status->magic != H4032L_STATUS_PACKET_MAGIC)
+                       devc->status = H4032L_STATUS_RESPONSE_STATUS;
+               else if (status->status == 2)
+                       devc->status = H4032L_STATUS_RESPONSE_STATUS_CONTINUE;
+               else
+                       devc->status = H4032L_STATUS_RESPONSE_STATUS_RETRY;
+               break;
+       case H4032L_STATUS_RESPONSE_STATUS_RETRY:
+               devc->status = H4032L_STATUS_CMD_STATUS;
+               devc->cmd_pkt.cmd = CMD_STATUS;
+               cmd = TRUE;
+               break;
+       case H4032L_STATUS_RESPONSE_STATUS_CONTINUE:
+               devc->status = H4032L_STATUS_CMD_GET;
+               devc->cmd_pkt.cmd = CMD_GET;
+               cmd = TRUE;
+               break;
+       case H4032L_STATUS_CMD_GET:
+               devc->status = H4032L_STATUS_FIRST_TRANSFER;
+               /* Trigger has been captured. */
+               std_session_send_df_header(sdi);
+               break;
+       case H4032L_STATUS_FIRST_TRANSFER:
+               /* Drop packets until H4032L_START_PACKET_MAGIC. */
+               if (buf[0] != H4032L_START_PACKET_MAGIC) {
+                       sr_dbg("Mismatch magic number of start poll.");
+                       break;
+               }
+               devc->status = H4032L_STATUS_TRANSFER;
+               max_samples--;
+               buf++;
+               /* Fallthrough. */
+       case H4032L_STATUS_TRANSFER:
+               num_samples = MIN(devc->remaining_samples, max_samples);
+               devc->remaining_samples -= num_samples;
+               send_data(sdi, buf, num_samples);
+               sr_dbg("Remaining: %d %08X %08X.", devc->remaining_samples,
+                      buf[0], buf[1]);
+               break;
+       }
+
+       /* Start data receiving. */
+       if (devc->status == H4032L_STATUS_TRANSFER) {
+               if ((ret = h4032l_start_data_transfers(sdi)) != SR_OK) {
+                       sr_err("Can not start data transfers: %d", ret);
+                       devc->status = H4032L_STATUS_IDLE;
+               }
+       } else if (devc->status != H4032L_STATUS_IDLE) {
+               if (cmd) {
+                       /* Setup new USB cmd packet, reuse transfer object. */
+                       sr_dbg("New command: %d.", devc->status);
+                       libusb_fill_bulk_transfer(transfer, usb->devhdl,
+                               2 | LIBUSB_ENDPOINT_OUT,
+                               (unsigned char *)&devc->cmd_pkt,
+                               sizeof(struct h4032l_cmd_pkt),
+                               h4032l_usb_callback,
+                               (void *)sdi, H4032L_USB_TIMEOUT);
+               } else {
+                       /* Setup new USB poll packet, reuse transfer object. */
+                       sr_dbg("Poll: %d.", devc->status);
+                       libusb_fill_bulk_transfer(transfer, usb->devhdl,
+                               6 | LIBUSB_ENDPOINT_IN,
+                               devc->buf, ARRAY_SIZE(devc->buf),
+                               h4032l_usb_callback,
+                               (void *)sdi, H4032L_USB_TIMEOUT);
+               }
+               /* Send prepared USB packet. */
+               if ((ret = libusb_submit_transfer(transfer)) != 0) {
+                       sr_err("Failed to submit transfer: %s.",
+                              libusb_error_name(ret));
+                       devc->status = H4032L_STATUS_IDLE;
+               }
+       } else {
+               sr_dbg("Now idle.");
+       }
+
+       if (devc->status == H4032L_STATUS_IDLE)
+               free_transfer(transfer);
+}
+
+uint16_t h4032l_voltage2pwm(double voltage)
+{
+       /*
+        * word PwmA - channel A Vref PWM value, pseudocode:
+        * -6V < ThresholdVoltage < +6V
+        * Vref = 1.8 - ThresholdVoltage
+        * if Vref > 10.0
+        *      Vref = 10.0
+        * if Vref < -5.0
+        *      Vref = -5.0
+        * pwm = ToInt((Vref + 5.0) / 15.0 * 4096.0)
+        * if pwm > 4095
+        *      pwm = 4095
+        */
+       voltage = 1.8 - voltage;
+       if (voltage > 10.0)
+               voltage = 10.0;
+       else if (voltage < -5.0)
+               voltage = -5.0;
+
+       return (uint16_t) ((voltage + 5.0) * (4096.0 / 15.0));
+}
+
+SR_PRIV int h4032l_start_data_transfers(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       struct sr_usb_dev_inst *usb = sdi->conn;
+       struct libusb_transfer *transfer;
+       uint8_t *buf;
+       unsigned int num_transfers;
+       unsigned int i;
+       int ret;
+
+       devc->submitted_transfers = 0;
+
+       /*
+        * Set number of data transfers regarding to size of buffer.
+        * FPGA version 0 can't transfer multiple transfers at once.
+        */
+       if ((num_transfers = MIN(devc->remaining_samples * sizeof(uint32_t) /
+           H4032L_DATA_BUFFER_SIZE, devc->fpga_version ?
+           H4032L_DATA_TRANSFER_MAX_NUM : 1)) == 0)
+               num_transfers = 1;
+
+       g_free(devc->transfers);
+       devc->transfers = g_malloc(sizeof(*devc->transfers) * num_transfers);
+       devc->num_transfers = num_transfers;
+
+       for (i = 0; i < num_transfers; i++) {
+               buf = g_malloc(H4032L_DATA_BUFFER_SIZE);
+               transfer = libusb_alloc_transfer(0);
+
+               libusb_fill_bulk_transfer(transfer, usb->devhdl,
+                       6 | LIBUSB_ENDPOINT_IN,
+                       buf, H4032L_DATA_BUFFER_SIZE,
+                       h4032l_data_transfer_callback,
+                       (void *)sdi, H4032L_USB_TIMEOUT);
+
+               /* Send prepared usb packet. */
+               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++;
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int h4032l_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       struct sr_usb_dev_inst *usb = sdi->conn;
+       struct libusb_transfer *transfer;
+       unsigned char buf[] = {0x0f, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+       int ret;
+
+       /* Send reset command to arm the logic analyzer. */
+       if ((ret = libusb_control_transfer(usb->devhdl,
+               LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, CMD_RESET,
+               0x00, 0x00, buf, ARRAY_SIZE(buf), H4032L_USB_TIMEOUT)) < 0) {
+               sr_err("Failed to send vendor request %s.",
+                      libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       /* Wait for reset vendor request. */
+       g_usleep(20 * 1000);
+
+       /* Send configure command. */
+       devc->cmd_pkt.cmd = CMD_CONFIGURE;
+       devc->status = H4032L_STATUS_CMD_CONFIGURE;
+       devc->remaining_samples = devc->cmd_pkt.sample_size;
+
+       transfer = libusb_alloc_transfer(0);
+
+       libusb_fill_bulk_transfer(transfer, usb->devhdl,
+               2 | LIBUSB_ENDPOINT_OUT, (unsigned char *)&devc->cmd_pkt,
+               sizeof(struct h4032l_cmd_pkt), h4032l_usb_callback,
+               (void *)sdi, H4032L_USB_TIMEOUT);
+
+       if ((ret = libusb_submit_transfer(transfer)) != 0) {
+               sr_err("Failed to submit transfer: %s.",
+                      libusb_error_name(ret));
+               libusb_free_transfer(transfer);
+               return SR_ERR;
+       }
+
+       devc->transfers = g_malloc0(sizeof(*devc->transfers));
+       devc->submitted_transfers++;
+       devc->num_transfers = 1;
+       devc->transfers[0] = transfer;
+
+       return SR_OK;
+}
+
+SR_PRIV int h4032l_stop(struct sr_dev_inst *sdi)
+{
+       abort_acquisition(sdi->priv);
+
+       return SR_OK;
+}
+
+SR_PRIV int h4032l_dev_open(struct sr_dev_inst *sdi)
+{
+       struct drv_context *drvc = sdi->driver->context;
+       struct sr_usb_dev_inst *usb = sdi->conn;
+       struct libusb_device_descriptor des;
+       libusb_device **devlist;
+       int ret = SR_ERR, i, device_count;
+       char connection_id[64];
+
+       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++) {
+               libusb_get_device_descriptor(devlist[i], &des);
+
+               if (des.idVendor != H4032L_USB_VENDOR ||
+                   des.idProduct != H4032L_USB_PRODUCT)
+                       continue;
+
+               if ((sdi->status == SR_ST_INITIALIZING) ||
+                   (sdi->status == SR_ST_INACTIVE)) {
+                       /* Check device by its physical USB bus/port address. */
+                       if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                               continue;
+
+                       if (strcmp(sdi->connection_id, connection_id))
+                               /* 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));
+                       ret = SR_ERR;
+                       break;
+               }
+
+               ret = SR_OK;
+               break;
+       }
+
+       libusb_free_device_list(devlist, 1);
+       return ret;
+}
+
+SR_PRIV int h4032l_get_fpga_version(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       struct sr_usb_dev_inst *usb = sdi->conn;
+       struct h4032l_status_packet *status;
+       int transferred;
+       int i, ret;
+
+       /* Set command to status. */
+       devc->cmd_pkt.magic = H4032L_CMD_PKT_MAGIC;
+       devc->cmd_pkt.cmd = CMD_STATUS;
+
+       /* Send status request. */
+       if ((ret = libusb_bulk_transfer(usb->devhdl,
+               2 | LIBUSB_ENDPOINT_OUT, (unsigned char *)&devc->cmd_pkt,
+               sizeof(struct h4032l_cmd_pkt), &transferred, H4032L_USB_TIMEOUT)) < 0) {
+               sr_err("Unable to send FPGA version request: %s.",
+                      libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       /* Attempt to get FGPA version. */
+       for (i = 0; i < 10; i++) {
+               if ((ret = libusb_bulk_transfer(usb->devhdl,
+                       6 | LIBUSB_ENDPOINT_IN, devc->buf,
+                       ARRAY_SIZE(devc->buf), &transferred, H4032L_USB_TIMEOUT)) < 0) {
+                       sr_err("Unable to receive FPGA version: %s.",
+                              libusb_error_name(ret));
+                       return SR_ERR;
+               }
+               status = (struct h4032l_status_packet *)devc->buf;
+               if (status->magic == H4032L_STATUS_PACKET_MAGIC) {
+                       sr_dbg("FPGA version: 0x%x.", status->fpga_version);
+                       devc->fpga_version = status->fpga_version;
+                       return SR_OK;
+               }
+       }
+
+       sr_err("Unable to get FPGA version.");
+
+       return SR_ERR;
+}
diff --git a/src/hardware/hantek-4032l/protocol.h b/src/hardware/hantek-4032l/protocol.h
new file mode 100644 (file)
index 0000000..f0e46d3
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Andreas Zschunke <andreas.zschunke@gmx.net>
+ * Copyright (C) 2017 Andrej Valek <andy@skyrain.eu>
+ * Copyright (C) 2017 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/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_HANTEK_4032L_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_HANTEK_4032L_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "hantek-4032l"
+
+#define H4032L_USB_VENDOR 0x04b5
+#define H4032L_USB_PRODUCT 0x4032
+
+#define H4032L_DATA_BUFFER_SIZE (2 * 1024)
+#define H4032L_DATA_TRANSFER_MAX_NUM 32
+
+#define H4043L_NUM_SAMPLES_MIN (2 * 1024)
+#define H4032L_NUM_SAMPLES_MAX (64 * 1024 * 1024)
+
+#define H4032L_THR_VOLTAGE_MIN -6.0
+#define H4032L_THR_VOLTAGE_MAX 6.0
+#define H4032L_THR_VOLTAGE_STEP 0.1
+/*
+ * Array index of the default voltage threshold value (2.5V):
+ * (|min| / step) + (default / step) = (|-6.0| / 0.1) + (2.5 / 0.1) = 85
+ */
+#define H4032L_THR_VOLTAGE_DEFAULT 85
+
+#define H4032L_CMD_PKT_MAGIC 0x017f
+#define H4032L_STATUS_PACKET_MAGIC 0x2B1A037F
+#define H4032L_START_PACKET_MAGIC 0x2B1A027F
+#define H4032L_END_PACKET_MAGIC 0x4D3C037F
+
+enum h4032l_clock_edge_type {
+       H4032L_CLOCK_EDGE_TYPE_RISE,
+       H4032L_CLOCK_EDGE_TYPE_FALL,
+       H4032L_CLOCK_EDGE_TYPE_BOTH
+};
+
+enum h4032l_ext_clock_source {
+       H4032L_EXT_CLOCK_SOURCE_CHANNEL_A,
+       H4032L_EXT_CLOCK_SOURCE_CHANNEL_B
+};
+
+enum h4032l_clock_edge_type_channel {
+       H4032L_CLOCK_EDGE_TYPE_RISE_A = 0x24,
+       H4032L_CLOCK_EDGE_TYPE_RISE_B,
+       H4032L_CLOCK_EDGE_TYPE_BOTH_A,
+       H4032L_CLOCK_EDGE_TYPE_BOTH_B,
+       H4032L_CLOCK_EDGE_TYPE_FALL_A,
+       H4032L_CLOCK_EDGE_TYPE_FALL_B
+};
+
+enum h4032l_trigger_edge_type {
+       H4032L_TRIGGER_EDGE_TYPE_RISE,
+       H4032L_TRIGGER_EDGE_TYPE_FALL,
+       H4032L_TRIGGER_EDGE_TYPE_TOGGLE,
+       H4032L_TRIGGER_EDGE_TYPE_DISABLED
+};
+
+enum h4032l_trigger_data_range_type {
+       H4032L_TRIGGER_DATA_RANGE_TYPE_MAX,
+       H4032L_TRIGGER_DATA_RANGE_TYPE_MIN_OR_MAX,
+       H4032L_TRIGGER_DATA_RANGE_TYPE_OUT_OF_RANGE,
+       H4032L_TRIGGER_DATA_RANGE_TYPE_WITHIN_RANGE
+};
+
+enum h4032l_trigger_time_range_type {
+       H4032L_TRIGGER_TIME_RANGE_TYPE_MAX,
+       H4032L_TRIGGER_TIME_RANGE_TYPE_MIN_OR_MAX,
+       H4032L_TRIGGER_TIME_RANGE_TYPE_OUT_OF_RANGE,
+       H4032L_TRIGGER_TIME_RANGE_TYPE_WITHIN_RANGE
+};
+
+enum h4032l_trigger_data_selection {
+       H4032L_TRIGGER_DATA_SELECTION_NEXT,
+       H4032L_TRIGGER_DATA_SELECTION_CURRENT,
+       H4032L_TRIGGER_DATA_SELECTION_PREV
+};
+
+enum h4032l_status {
+       H4032L_STATUS_IDLE,
+       H4032L_STATUS_CMD_CONFIGURE,
+       H4032L_STATUS_CMD_STATUS,
+       H4032L_STATUS_RESPONSE_STATUS,
+       H4032L_STATUS_RESPONSE_STATUS_RETRY,
+       H4032L_STATUS_RESPONSE_STATUS_CONTINUE,
+       H4032L_STATUS_CMD_GET,
+       H4032L_STATUS_FIRST_TRANSFER,
+       H4032L_STATUS_TRANSFER
+};
+
+#pragma pack(push,2)
+struct h4032l_trigger {
+       struct {
+               uint32_t edge_signal:5;
+               uint32_t edge_type:2;
+               uint32_t :1;
+               uint32_t data_range_type:2;
+               uint32_t time_range_type:2;
+               uint32_t data_range_enabled:1;
+               uint32_t time_range_enabled:1;
+               uint32_t :2;
+               uint32_t data_sel:2;
+               uint32_t combined_enabled:1;
+       } flags;
+       uint32_t data_range_min;
+       uint32_t data_range_max;
+       uint32_t time_range_min;
+       uint32_t time_range_max;
+       uint32_t data_range_mask;
+       uint32_t combine_mask;
+       uint32_t combine_data;
+};
+
+struct h4032l_cmd_pkt {
+       uint16_t magic; /* 0x017f */
+       uint8_t sample_rate;
+       struct {
+               uint8_t enable_trigger1:1;
+               uint8_t enable_trigger2:1;
+               uint8_t trigger_and_logic:1;
+       } trig_flags;
+       uint16_t pwm_a;
+       uint16_t pwm_b;
+       uint16_t reserved;
+       uint32_t sample_size; /* Sample depth in bits per channel, 2k-64M, must be multiple of 512. */
+       uint32_t pre_trigger_size; /* Pretrigger buffer depth in bits, must be < sample_size. */
+       struct h4032l_trigger trigger[2];
+       uint16_t cmd;
+};
+#pragma pack(pop)
+
+struct dev_context {
+       enum h4032l_status status;
+       uint64_t sample_rate;
+       unsigned int sent_samples;
+       int submitted_transfers;
+       uint32_t remaining_samples;
+       gboolean acq_aborted;
+       struct h4032l_cmd_pkt cmd_pkt;
+       unsigned int num_transfers;
+       struct libusb_transfer **transfers;
+       uint8_t buf[512];
+       uint64_t capture_ratio;
+       uint32_t trigger_pos;
+       gboolean external_clock;
+       enum h4032l_ext_clock_source external_clock_source;
+       enum h4032l_clock_edge_type clock_edge;
+       double cur_threshold[2];
+       uint32_t fpga_version;
+};
+
+SR_PRIV int h4032l_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV uint16_t h4032l_voltage2pwm(double voltage);
+SR_PRIV void LIBUSB_CALL h4032l_usb_callback(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL h4032l_data_transfer_callback(struct libusb_transfer *transfer);
+SR_PRIV int h4032l_start_data_transfers(const struct sr_dev_inst *sdi);
+SR_PRIV int h4032l_start(const struct sr_dev_inst *sdi);
+SR_PRIV int h4032l_stop(struct sr_dev_inst *sdi);
+SR_PRIV int h4032l_dev_open(struct sr_dev_inst *sdi);
+SR_PRIV int h4032l_get_fpga_version(const struct sr_dev_inst *sdi);
+
+#endif
index a6aa68543775bc3644abc62ccde6c2f475f084a3..07fd2cbf7dcd8f806ab2aad2d7e15fe29e703984 100644 (file)
@@ -61,19 +61,33 @@ static const char *acdc_coupling[] = {
 
 static const struct hantek_6xxx_profile dev_profiles[] = {
        {
+               /* Windows: "Hantek6022BE DRIVER 1": 04b4:6022 */
                0x04b4, 0x6022, 0x1d50, 0x608e, 0x0001,
                "Hantek", "6022BE", "fx2lafw-hantek-6022be.fw",
-               dc_coupling, ARRAY_SIZE(dc_coupling), FALSE,
+               ARRAY_AND_SIZE(dc_coupling), FALSE,
+       },
+       {
+               /* Windows: "Hantek6022BE DRIVER 2": 04b5:6022 */
+               0x04b5, 0x6022, 0x1d50, 0x608e, 0x0001,
+               "Hantek", "6022BE", "fx2lafw-hantek-6022be.fw",
+               ARRAY_AND_SIZE(dc_coupling), FALSE,
        },
        {
                0x8102, 0x8102, 0x1d50, 0x608e, 0x0002,
                "Sainsmart", "DDS120", "fx2lafw-sainsmart-dds120.fw",
-               acdc_coupling, ARRAY_SIZE(acdc_coupling), TRUE,
+               ARRAY_AND_SIZE(acdc_coupling), TRUE,
        },
        {
+               /* Windows: "Hantek6022BL DRIVER 1": 04b4:602a */
                0x04b4, 0x602a, 0x1d50, 0x608e, 0x0003,
                "Hantek", "6022BL", "fx2lafw-hantek-6022bl.fw",
-               dc_coupling, ARRAY_SIZE(dc_coupling), FALSE,
+               ARRAY_AND_SIZE(dc_coupling), FALSE,
+       },
+       {
+               /* Windows: "Hantek6022BL DRIVER 2": 04b5:602a */
+               0x04b5, 0x602a, 0x1d50, 0x608e, 0x0003,
+               "Hantek", "6022BL", "fx2lafw-hantek-6022bl.fw",
+               ARRAY_AND_SIZE(dc_coupling), FALSE,
        },
        ALL_ZERO
 };
@@ -88,8 +102,6 @@ static const uint64_t vdivs[][2] = {
 
 static int read_channel(const struct sr_dev_inst *sdi, uint32_t amount);
 
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
-
 static struct sr_dev_inst *hantek_6xxx_dev_new(const struct hantek_6xxx_profile *prof)
 {
        struct sr_dev_inst *sdi;
@@ -122,10 +134,6 @@ static struct sr_dev_inst *hantek_6xxx_dev_new(const struct hantek_6xxx_profile
        devc->coupling_tab_size = prof->coupling_tab_size;
        devc->has_coupling = prof->has_coupling;
 
-       devc->sample_buf = NULL;
-       devc->sample_buf_write = 0;
-       devc->sample_buf_size = 0;
-
        devc->profile = prof;
        devc->dev_state = IDLE;
        devc->samplerate = DEFAULT_SAMPLERATE;
@@ -158,18 +166,14 @@ static int configure_channels(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static void clear_dev_context(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
        g_slist_free(devc->enabled_channels);
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_dev_context);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
@@ -223,7 +227,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
                libusb_get_device_descriptor(devlist[i], &des);
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
 
                prof = NULL;
                for (j = 0; dev_profiles[j].orig_vid; j++) {
@@ -237,11 +242,12 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                                devices = g_slist_append(devices, sdi);
                                devc = sdi->priv;
                                if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
-                                               USB_CONFIGURATION, prof->firmware) == SR_OK)
+                                               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.");
+                               } else {
+                                       sr_err("Firmware upload failed, name %s.", prof->firmware);
+                               }
                                /* Dummy USB address of 0xff will get overwritten later. */
                                sdi->conn = sr_usb_dev_inst_new(
                                                libusb_get_bus_number(devlist[i]), 0xff, NULL);
@@ -328,12 +334,11 @@ static int dev_close(struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
-       char str[128];
        const uint64_t *vdiv;
        int ch_idx;
 
@@ -366,8 +371,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                                /* 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);
+                       *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                        break;
                default:
                        return SR_ERR_NA;
@@ -394,19 +398,12 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       uint64_t p, q;
-       int tmp_int, ch_idx, ret;
-       unsigned int i;
-       const char *tmp_str;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
+       int ch_idx, idx;
 
-       ret = SR_OK;
        devc = sdi->priv;
        if (!cg) {
                switch (key) {
@@ -421,8 +418,7 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        devc->limit_samples = g_variant_get_uint64(data);
                        break;
                default:
-                       ret = SR_ERR_NA;
-                       break;
+                       return SR_ERR_NA;
                }
        } else {
                if (sdi->channel_groups->data == cg)
@@ -433,75 +429,39 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        return SR_ERR_ARG;
                switch (key) {
                case SR_CONF_VDIV:
-                       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[ch_idx] = tmp_int;
-                               hantek_6xxx_update_vdiv(sdi);
-                       } else
-                               ret = SR_ERR_ARG;
+                       if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+                               return SR_ERR_ARG;
+                       devc->voltage[ch_idx] = idx;
+                       hantek_6xxx_update_vdiv(sdi);
                        break;
                case SR_CONF_COUPLING:
-                       tmp_str = g_variant_get_string(data, NULL);
-                       for (i = 0; i < devc->coupling_tab_size; i++) {
-                               if (!strcmp(tmp_str, devc->coupling_vals[i])) {
-                                       devc->coupling[ch_idx] = i;
-                                       break;
-                               }
-                       }
-                       if (i == devc->coupling_tab_size)
-                               ret = SR_ERR_ARG;
+                       if ((idx = std_str_idx(data, devc->coupling_vals,
+                                               devc->coupling_tab_size)) < 0)
+                               return SR_ERR_ARG;
+                       devc->coupling[ch_idx] = idx;
                        break;
                default:
-                       ret = SR_ERR_NA;
-                       break;
+                       return SR_ERR_NA;
                }
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       GVariant *tuple, *rational[2];
-       GVariantBuilder gvb;
-       unsigned int i;
-       GVariant *gvar;
-       struct dev_context *devc = NULL;
-
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       } else if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
+       struct dev_context *devc;
 
-       if (sdi)
-               devc = sdi->priv;
+       devc = (sdi) ? sdi->priv : NULL;
 
        if (!cg) {
                switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       break;
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
                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);
+                       *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
                        break;
                default:
                        return SR_ERR_NA;
@@ -509,23 +469,15 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
        } else {
                switch (key) {
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
                        break;
                case SR_CONF_COUPLING:
                        if (!devc)
-                               return SR_ERR_NA;
+                               return SR_ERR_ARG;
                        *data = g_variant_new_strv(devc->coupling_vals, devc->coupling_tab_size);
                        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);
+                       *data = std_gvar_tuple_array(ARRAY_AND_SIZE(vdivs));
                        break;
                default:
                        return SR_ERR_NA;
@@ -592,7 +544,7 @@ static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
                return;
        }
 
-       for (int ch = 0; ch < 2; ch++) {
+       for (int ch = 0; ch < NUM_CHANNELS; ch++) {
                if (!devc->ch_enabled[ch])
                        continue;
 
@@ -625,27 +577,6 @@ static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
        g_free(analog.data);
 }
 
-static void send_data(struct sr_dev_inst *sdi, struct libusb_transfer *buf[], uint64_t samples)
-{
-       int i = 0;
-       uint64_t send = 0;
-       uint32_t chunk;
-
-       while (send < samples) {
-               chunk = MIN(samples - send, (uint64_t)(buf[i]->actual_length / NUM_CHANNELS));
-               send += chunk;
-               send_chunk(sdi, buf[i]->buffer, chunk);
-
-               /*
-                * Everything in this transfer was either copied to the buffer
-                * or sent to the session bus.
-                */
-               g_free(buf[i]->buffer);
-               libusb_free_transfer(buf[i]);
-               i++;
-       }
-}
-
 /*
  * 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
@@ -672,26 +603,6 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
        if (devc->dev_state != CAPTURE)
                return;
 
-       if (!devc->sample_buf) {
-               devc->sample_buf_size = 10;
-               devc->sample_buf = g_try_malloc(devc->sample_buf_size * sizeof(transfer));
-               devc->sample_buf_write = 0;
-       }
-
-       if (devc->sample_buf_write >= devc->sample_buf_size) {
-               devc->sample_buf_size += 10;
-               devc->sample_buf = g_try_realloc(devc->sample_buf,
-                               devc->sample_buf_size * sizeof(transfer));
-               if (!devc->sample_buf) {
-                       sr_err("Sample buffer malloc failed.");
-                       devc->dev_state = STOPPING;
-                       return;
-               }
-       }
-
-       devc->sample_buf[devc->sample_buf_write++] = transfer;
-       devc->samp_received += transfer->actual_length / NUM_CHANNELS;
-
        sr_spew("receive_transfer(): calculated samplerate == %" PRIu64 "ks/s",
                (uint64_t)(transfer->actual_length * 1000 /
                (g_get_monotonic_time() - devc->read_start_ts + 1) /
@@ -704,21 +615,24 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
                /* Nothing to send to the bus. */
                return;
 
+       unsigned samples_received = transfer->actual_length / NUM_CHANNELS;
+       send_chunk(sdi, transfer->buffer, samples_received);
+       devc->samp_received += samples_received;
+
+       g_free(transfer->buffer);
+       libusb_free_transfer(transfer);
+
        if (devc->limit_samples && devc->samp_received >= devc->limit_samples) {
                sr_info("Requested number of samples reached, stopping. %"
                        PRIu64 " <= %" PRIu64, devc->limit_samples,
                        devc->samp_received);
-               send_data(sdi, devc->sample_buf, devc->limit_samples);
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        } else if (devc->limit_msec && (g_get_monotonic_time() -
                        devc->aq_started) / 1000 >= devc->limit_msec) {
                sr_info("Requested time limit reached, stopping. %d <= %d",
                        (uint32_t)devc->limit_msec,
                        (uint32_t)(g_get_monotonic_time() - devc->aq_started) / 1000);
-               send_data(sdi, devc->sample_buf, devc->samp_received);
-               g_free(devc->sample_buf);
-               devc->sample_buf = NULL;
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        } else {
                read_channel(sdi, data_amount(sdi));
        }
@@ -734,7 +648,6 @@ static int read_channel(const struct sr_dev_inst *sdi, uint32_t amount)
        amount = MIN(amount, MAX_PACKET_SIZE);
        ret = hantek_6xxx_get_channeldata(sdi, receive_transfer, amount);
        devc->read_start_ts = g_get_monotonic_time();
-       devc->read_data_amount = amount;
 
        return ret;
 }
@@ -786,9 +699,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct sr_dev_driver *di = sdi->driver;
        struct drv_context *drvc = di->context;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        if (configure_channels(sdi) != SR_OK) {
@@ -818,15 +728,9 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR;
-
        devc = sdi->priv;
        devc->dev_state = STOPPING;
 
-       g_free(devc->sample_buf);
-       devc->sample_buf = NULL;
-
        return SR_OK;
 }
 
index a43f72bbb546247abcd8ce68cfce68c19478d501..75d8ab91c0f14fb4c2cfed4ce2222173e53f4660 100644 (file)
@@ -27,16 +27,12 @@ SR_PRIV int hantek_6xxx_open(struct sr_dev_inst *sdi)
        struct sr_usb_dev_inst *usb;
        struct libusb_device_descriptor des;
        libusb_device **devlist;
-       int err, i;
+       int err = SR_ERR, i;
        char connection_id[64];
 
        devc = sdi->priv;
        usb = sdi->conn;
 
-       if (sdi->status == SR_ST_ACTIVE)
-               /* Already in use. */
-               return SR_ERR;
-
        libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
        for (i = 0; devlist[i]; i++) {
                libusb_get_device_descriptor(devlist[i], &des);
@@ -50,7 +46,9 @@ SR_PRIV int hantek_6xxx_open(struct sr_dev_inst *sdi)
                        /*
                         * Check device by its physical USB bus/port address.
                         */
-                       usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+                       if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                               continue;
+
                        if (strcmp(sdi->connection_id, connection_id))
                                /* This is not the one. */
                                continue;
@@ -65,14 +63,16 @@ SR_PRIV int hantek_6xxx_open(struct sr_dev_inst *sdi)
                                usb->address = libusb_get_device_address(devlist[i]);
                        }
 
-                       sdi->status = SR_ST_ACTIVE;
                        sr_info("Opened device on %d.%d (logical) / "
                                        "%s (physical) interface %d.",
                                usb->bus, usb->address,
                                sdi->connection_id, USB_INTERFACE);
+
+                       err = SR_OK;
                } else {
                        sr_err("Failed to open device: %s.",
                               libusb_error_name(err));
+                       err = SR_ERR;
                }
 
                /* If we made it here, we handled the device (somehow). */
@@ -81,10 +81,7 @@ SR_PRIV int hantek_6xxx_open(struct sr_dev_inst *sdi)
 
        libusb_free_device_list(devlist, 1);
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR;
-
-       return SR_OK;
+       return err;
 }
 
 SR_PRIV void hantek_6xxx_close(struct sr_dev_inst *sdi)
index 73d8f868da27430072a30b674ea96440ab27da22..af66c70ef1b4a82092bd692f687d4d78af24e748 100644 (file)
 #define FLUSH_PACKET_SIZE      1024
 
 #define MIN_PACKET_SIZE                512
+#ifdef _WIN32
+#define MAX_PACKET_SIZE                (2 * 1024 * 1024)
+#else
 #define MAX_PACKET_SIZE                (12 * 1024 * 1024)
+#endif
 
 #define HANTEK_EP_IN           0x86
 #define USB_INTERFACE          0
@@ -118,11 +122,6 @@ struct dev_context {
        uint64_t aq_started;
 
        uint64_t read_start_ts;
-       uint32_t read_data_amount;
-
-       struct libusb_transfer **sample_buf;
-       uint32_t sample_buf_write;
-       uint32_t sample_buf_size;
 
        gboolean ch_enabled[NUM_CHANNELS];
        int voltage[NUM_CHANNELS];
index 6a6aa09dc5e76068e3be6021386fb8e7024d2a24..c1b18ea9ca660a52ec8b2e4113d637b3b8a1e88c 100644 (file)
@@ -58,11 +58,13 @@ static const uint32_t devopts[] = {
        SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
        SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_NUM_HDIV | SR_CONF_GET,
-       SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_BUFFERSIZE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_NUM_VDIV | SR_CONF_GET,
+       SR_CONF_TRIGGER_LEVEL | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint32_t devopts_cg[] = {
@@ -129,6 +131,28 @@ static const uint64_t timebases[][2] = {
        { 400, 1000 },
 };
 
+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(5),
+       SR_MHZ(10),
+       SR_MHZ(20),
+       SR_MHZ(25),
+       SR_MHZ(50),
+       SR_MHZ(100),
+       SR_MHZ(125),
+       /* Fast mode not supported yet.
+       SR_MHZ(200),
+       SR_MHZ(250), */
+};
+
 static const uint64_t vdivs[][2] = {
        /* millivolts */
        { 10, 1000 },
@@ -144,25 +168,18 @@ static const uint64_t vdivs[][2] = {
 };
 
 static const char *trigger_sources[] = {
-       "CH1",
-       "CH2",
-       "EXT",
+       "CH1", "CH2", "EXT",
        /* TODO: forced */
 };
 
 static const char *trigger_slopes[] = {
-       "r",
-       "f",
+       "r", "f",
 };
 
 static const char *coupling[] = {
-       "AC",
-       "DC",
-       "GND",
+       "AC", "DC", "GND",
 };
 
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
-
 static struct sr_dev_inst *dso_dev_new(const struct dso_profile *prof)
 {
        struct sr_dev_inst *sdi;
@@ -192,6 +209,7 @@ static struct sr_dev_inst *dso_dev_new(const struct dso_profile *prof)
        devc->profile = prof;
        devc->dev_state = IDLE;
        devc->timebase = DEFAULT_TIMEBASE;
+       devc->samplerate = DEFAULT_SAMPLERATE;
        devc->ch_enabled[0] = TRUE;
        devc->ch_enabled[1] = TRUE;
        devc->voltage[0] = DEFAULT_VOLTAGE;
@@ -204,7 +222,7 @@ static struct sr_dev_inst *dso_dev_new(const struct dso_profile *prof)
        devc->framesize = DEFAULT_FRAMESIZE;
        devc->triggerslope = SLOPE_POSITIVE;
        devc->triggersource = g_strdup(DEFAULT_TRIGGER_SOURCE);
-       devc->triggerposition = DEFAULT_HORIZ_TRIGGERPOS;
+       devc->capture_ratio = DEFAULT_CAPTURE_RATIO;
        sdi->priv = devc;
 
        return sdi;
@@ -220,6 +238,7 @@ static int configure_channels(const struct sr_dev_inst *sdi)
        devc = sdi->priv;
 
        g_slist_free(devc->enabled_channels);
+       devc->enabled_channels = NULL;
        devc->ch_enabled[0] = devc->ch_enabled[1] = FALSE;
        for (l = sdi->channels, p = 0; l; l = l->next, p++) {
                ch = l->data;
@@ -234,19 +253,15 @@ static int configure_channels(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static void clear_dev_context(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
        g_free(devc->triggersource);
        g_slist_free(devc->enabled_channels);
-
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_dev_context);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
@@ -300,7 +315,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
                libusb_get_device_descriptor(devlist[i], &des);
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
 
                prof = NULL;
                for (j = 0; dev_profiles[j].orig_vid; j++) {
@@ -314,11 +330,12 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                                devices = g_slist_append(devices, sdi);
                                devc = sdi->priv;
                                if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
-                                               USB_CONFIGURATION, prof->firmware) == SR_OK)
+                                               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");
+                               } else {
+                                       sr_err("Firmware upload failed, name %s", prof->firmware);
+                               }
                                /* Dummy USB address of 0xff will get overwritten later. */
                                sdi->conn = sr_usb_dev_inst_new(
                                                libusb_get_bus_number(devlist[i]), 0xff, NULL);
@@ -403,12 +420,11 @@ static int dev_close(struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
-       char str[128];
        const char *s;
        const uint64_t *vdiv;
        int ch_idx;
@@ -428,6 +444,9 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        devc = sdi->priv;
        if (!cg) {
                switch (key) {
+               case SR_CONF_TRIGGER_LEVEL:
+                       *data = g_variant_new_double(devc->voffset_trigger);
+                       break;
                case SR_CONF_CONN:
                        if (!sdi->conn)
                                return SR_ERR_ARG;
@@ -436,13 +455,15 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                                /* 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);
+                       *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                        break;
                case SR_CONF_TIMEBASE:
                        *data = g_variant_new("(tt)", timebases[devc->timebase][0],
                                        timebases[devc->timebase][1]);
                        break;
+               case SR_CONF_SAMPLERATE:
+                       *data = g_variant_new_uint64(devc->samplerate);
+                       break;
                case SR_CONF_BUFFERSIZE:
                        *data = g_variant_new_uint64(devc->framesize);
                        break;
@@ -453,8 +474,8 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                        s = (devc->triggerslope == SLOPE_POSITIVE) ? "r" : "f";
                        *data = g_variant_new_string(s);
                        break;
-               case SR_CONF_HORIZ_TRIGGERPOS:
-                       *data = g_variant_new_double(devc->triggerposition);
+               case SR_CONF_CAPTURE_RATIO:
+                       *data = g_variant_new_uint64(devc->capture_ratio);
                        break;
                default:
                        return SR_ERR_NA;
@@ -483,80 +504,61 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       double tmp_double;
-       uint64_t tmp_u64, p, q;
-       int tmp_int, ch_idx, ret;
-       unsigned int i;
-       const char *tmp_str;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
+       int ch_idx, idx;
+       float flt;
 
-       ret = SR_OK;
        devc = sdi->priv;
        if (!cg) {
                switch (key) {
                case SR_CONF_LIMIT_FRAMES:
                        devc->limit_frames = g_variant_get_uint64(data);
                        break;
+               case SR_CONF_TRIGGER_LEVEL:
+                       flt = g_variant_get_double(data);
+                       if (flt < 0.0 || flt > 1.0) {
+                               sr_err("Trigger level must be in [0.0,1.0].");
+                               return SR_ERR_ARG;
+                       }
+                       devc->voffset_trigger = flt;
+                       if (dso_set_voffsets(sdi) != SR_OK)
+                               return SR_ERR;
+                       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'))
+                       if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
                                return SR_ERR_ARG;
-                       devc->triggerslope = (tmp_str[0] == 'r')
-                               ? SLOPE_POSITIVE : SLOPE_NEGATIVE;
+                       devc->triggerslope = idx;
                        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;
+               case SR_CONF_CAPTURE_RATIO:
+                       devc->capture_ratio = g_variant_get_uint64(data);
                        break;
                case SR_CONF_BUFFERSIZE:
-                       tmp_u64 = g_variant_get_uint64(data);
-                       for (i = 0; i < NUM_BUFFER_SIZES; i++) {
-                               if (devc->profile->buffersizes[i] == tmp_u64) {
-                                       devc->framesize = tmp_u64;
-                                       break;
-                               }
-                       }
-                       if (i == NUM_BUFFER_SIZES)
-                               ret = SR_ERR_ARG;
+                       if ((idx = std_u64_idx(data, devc->profile->buffersizes, NUM_BUFFER_SIZES)) < 0)
+                               return SR_ERR_ARG;
+                       devc->framesize = devc->profile->buffersizes[idx];
                        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;
+                       if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(timebases))) < 0)
+                               return SR_ERR_ARG;
+                       devc->timebase = idx;
+                       break;
+               case SR_CONF_SAMPLERATE:
+                       if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(samplerates))) < 0)
+                               return SR_ERR_ARG;
+                       devc->samplerate = samplerates[idx];
+                       if (dso_set_trigger_samplerate(sdi) != SR_OK)
+                               return SR_ERR;
                        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;
+                       if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 0)
+                               return SR_ERR_ARG;
+                       devc->triggersource = g_strdup(trigger_sources[idx]);
                        break;
                default:
-                       ret = SR_ERR_NA;
-                       break;
+                       return SR_ERR_NA;
                }
        } else {
                if (sdi->channel_groups->data == cg)
@@ -570,90 +572,50 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        devc->filter[ch_idx] = g_variant_get_boolean(data);
                        break;
                case SR_CONF_VDIV:
-                       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[ch_idx] = tmp_int;
-                       } else
-                               ret = SR_ERR_ARG;
+                       if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+                               return SR_ERR_ARG;
+                       devc->voltage[ch_idx] = idx;
                        break;
                case SR_CONF_COUPLING:
-                       tmp_str = g_variant_get_string(data, NULL);
-                       for (i = 0; coupling[i]; i++) {
-                               if (!strcmp(tmp_str, coupling[i])) {
-                                       devc->coupling[ch_idx] = i;
-                                       break;
-                               }
-                       }
-                       if (coupling[i] == 0)
-                               ret = SR_ERR_ARG;
+                       if ((idx = std_str_idx(data, ARRAY_AND_SIZE(coupling))) < 0)
+                               return SR_ERR_ARG;
+                       devc->coupling[ch_idx] = idx;
                        break;
                default:
-                       ret = SR_ERR_NA;
-                       break;
+                       return SR_ERR_NA;
                }
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *tuple, *rational[2];
-       GVariantBuilder gvb;
-       unsigned int i;
-
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       } else if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (!sdi)
-               return SR_ERR_ARG;
 
        if (!cg) {
                switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       break;
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
                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, NUM_BUFFER_SIZES, sizeof(uint64_t));
+                       *data = std_gvar_array_u64(devc->profile->buffersizes, NUM_BUFFER_SIZES);
+                       break;
+               case SR_CONF_SAMPLERATE:
+                       *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
                        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);
+                       *data = std_gvar_tuple_array(ARRAY_AND_SIZE(timebases));
                        break;
                case SR_CONF_TRIGGER_SOURCE:
-                       *data = g_variant_new_strv(trigger_sources,
-                                       ARRAY_SIZE(trigger_sources));
+                       *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_sources));
                        break;
                case SR_CONF_TRIGGER_SLOPE:
-                       *data = g_variant_new_strv(trigger_slopes,
-                                       ARRAY_SIZE(trigger_slopes));
+                       *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_slopes));
                        break;
                default:
                        return SR_ERR_NA;
@@ -661,21 +623,13 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
        } else {
                switch (key) {
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
                        break;
                case SR_CONF_COUPLING:
-                       *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+                       *data = g_variant_new_strv(ARRAY_AND_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);
+                       *data = std_gvar_tuple_array(ARRAY_AND_SIZE(vdivs));
                        break;
                default:
                        return SR_ERR_NA;
@@ -707,7 +661,7 @@ static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
        /* TODO: Check malloc return value. */
        analog.data = g_try_malloc(num_samples * sizeof(float));
 
-       for (int ch = 0; ch < 2; ch++) {
+       for (int ch = 0; ch < NUM_CHANNELS; ch++) {
                if (!devc->ch_enabled[ch])
                        continue;
 
@@ -824,12 +778,14 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
                sr_dbg("End of frame, sending %d pre-trigger buffered samples.",
                        devc->samp_buffered);
                send_chunk(sdi, devc->framebuf, devc->samp_buffered);
+               g_free(devc->framebuf);
+               devc->framebuf = NULL;
 
                /* Mark the end of this frame. */
                packet.type = SR_DF_FRAME_END;
                sr_session_send(sdi, &packet);
 
-               if (devc->limit_frames && ++devc->num_frames == devc->limit_frames) {
+               if (devc->limit_frames && ++devc->num_frames >= devc->limit_frames) {
                        /* Terminate session */
                        devc->dev_state = STOPPING;
                } else {
@@ -914,6 +870,7 @@ static int handle_event(int fd, int revents, void *cb_data)
                /* No data yet. */
                break;
        case CAPTURE_READY_8BIT:
+       case CAPTURE_READY_2250:
                /* Remember where in the captured frame the trigger is. */
                devc->trigger_offset = trigger_offset;
 
@@ -956,9 +913,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct sr_dev_driver *di = sdi->driver;
        struct drv_context *drvc = di->context;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        if (configure_channels(sdi) != SR_OK) {
@@ -984,11 +938,9 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR;
-
        devc = sdi->priv;
        devc->dev_state = STOPPING;
+       devc->num_frames = 0;
 
        return SR_OK;
 }
index bf91de451483f19fce735d942d532bdab4b03f2c..c74187ada435f2441e7aa3b88ed926194c9d530b 100644 (file)
@@ -27,8 +27,6 @@
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
-#define NUM_CHANNELS 2
-
 static int send_begin(const struct sr_dev_inst *sdi)
 {
        struct sr_usb_dev_inst *usb;
@@ -119,10 +117,6 @@ SR_PRIV int dso_open(struct sr_dev_inst *sdi)
        devc = sdi->priv;
        usb = sdi->conn;
 
-       if (sdi->status == SR_ST_ACTIVE)
-               /* already in use */
-               return SR_ERR;
-
        libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
        for (i = 0; devlist[i]; i++) {
                libusb_get_device_descriptor(devlist[i], &des);
@@ -136,7 +130,9 @@ SR_PRIV int dso_open(struct sr_dev_inst *sdi)
                        /*
                         * Check device by its physical USB bus/port address.
                         */
-                       usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+                       if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                               continue;
+
                        if (strcmp(sdi->connection_id, connection_id))
                                /* This is not the one. */
                                continue;
@@ -190,7 +186,6 @@ SR_PRIV void dso_close(struct sr_dev_inst *sdi)
        libusb_close(usb->devhdl);
        usb->devhdl = NULL;
        sdi->status = SR_ST_INACTIVE;
-
 }
 
 static int get_channel_offsets(const struct sr_dev_inst *sdi)
@@ -247,7 +242,161 @@ static int get_channel_offsets(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi)
+static void dso2250_set_triggerpos(int value, int long_buffer, uint8_t dest[], int offset)
+{
+       uint32_t min, max;
+       uint32_t tmp, diff;
+
+       min = long_buffer ? 0 : 0x7d7ff;
+       max = 0x7ffff;
+
+       diff = max - min;
+       tmp = min + diff * value / 100;
+       sr_dbg("2250 trigger pos: %3d%% * [0x%x,0x%x] == 0x%x", value, min, max, tmp);
+
+       dest[offset + 0] = tmp & 0xff;
+       dest[offset + 1] = (tmp >> 8) & 0xff;
+       dest[offset + 2] = (tmp >> 16) & 0x7;
+}
+
+/* See http://openhantek.sourceforge.net/doc/namespaceHantek.html#ac1cd181814cf3da74771c29800b39028 */
+static int dso2250_set_trigger_samplerate(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       int ret, tmp;
+       uint64_t base;
+       uint8_t cmdstring[12];
+       int trig;
+
+       devc = sdi->priv;
+       usb = sdi->conn;
+
+       memset(cmdstring, 0, sizeof(cmdstring));
+       /* Command */
+       cmdstring[0] = CMD_2250_SET_TRIGGERSOURCE;
+       sr_dbg("Trigger source %s.", devc->triggersource);
+       if (!strcmp("CH2", devc->triggersource))
+               tmp = 3;
+       else if (!strcmp("CH1", devc->triggersource))
+               tmp = 2;
+       else if (!strcmp("EXT", devc->triggersource))
+               tmp = 0;
+       else {
+               sr_err("Invalid trigger source: '%s'.", devc->triggersource);
+               return SR_ERR_ARG;
+       }
+       cmdstring[2] = tmp;
+
+       sr_dbg("Trigger slope: %d.", devc->triggerslope);
+       cmdstring[2] |= (devc->triggerslope == SLOPE_NEGATIVE ? 1 : 0) << 3;
+
+       if (send_begin(sdi) != SR_OK)
+               return SR_ERR;
+
+       if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+                       cmdstring, 8, &tmp, 100)) != 0) {
+               sr_err("Failed to set trigger/samplerate: %s.",
+                      libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       /* Frame size */
+       sr_dbg("Frame size: %d.", devc->framesize);
+       cmdstring[0] = CMD_2250_SET_RECORD_LENGTH;
+       cmdstring[2] = (devc->framesize == FRAMESIZE_SMALL) ? 0x01 : 0x02;
+
+       if (send_begin(sdi) != SR_OK)
+               return SR_ERR;
+
+       if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+                       cmdstring, 4, &tmp, 100)) != 0) {
+               sr_err("Failed to set record length: %s.",
+                      libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       memset(cmdstring, 0, sizeof(cmdstring));
+       cmdstring[0] = CMD_2250_SET_SAMPLERATE;
+       base = 100e6;
+       if (devc->samplerate > base) {
+               /* Timebase fast */
+               sr_err("Sample rate > 100MHz not yet supported.");
+               return SR_ERR_ARG;
+       }
+
+       tmp = base / devc->samplerate;
+       if (tmp) {
+               /* Downsampling on */
+               cmdstring[2] |= 2;
+               /* Downsampler = 1comp((Base / Samplerate) - 2)
+                *  Base == 100Msa resp. 200MSa
+                *
+                * Example for 500kSa/s:
+                *  100e6 / 500e3 => 200
+                *  200 - 2 => 198
+                *  1comp(198) => ff39 */
+               tmp -= 2;
+               tmp = ~tmp;
+               sr_dbg("Down sampler value: 0x%x.", tmp & 0xffff);
+               cmdstring[4] = (tmp >> 0) & 0xff;
+               cmdstring[5] = (tmp >> 8) & 0xff;
+       }
+
+       if (send_begin(sdi) != SR_OK)
+               return SR_ERR;
+
+       if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+                       cmdstring, 8, &tmp, 100)) != 0) {
+               sr_err("Failed to set sample rate: %s.",
+                      libusb_error_name(ret));
+               return SR_ERR;
+       }
+       sr_dbg("Sent CMD_2250_SET_SAMPLERATE.");
+
+       /* Enabled channels: 00=CH1, 01=CH2, 10=both. */
+       memset(cmdstring, 0, sizeof(cmdstring));
+       cmdstring[0] = CMD_2250_SET_CHANNELS;
+       sr_dbg("Channels: CH1=%d, CH2=%d.", devc->ch_enabled[0], devc->ch_enabled[1]);
+       cmdstring[2] = (devc->ch_enabled[0] ? 0 : 1) + (devc->ch_enabled[1] ? 2 : 0);
+
+       if (send_begin(sdi) != SR_OK)
+               return SR_ERR;
+
+       if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+                       cmdstring, 4, &tmp, 100)) != 0) {
+               sr_err("Failed to set channels: %s.",
+                      libusb_error_name(ret));
+               return SR_ERR;
+       }
+       sr_dbg("Sent CMD_2250_SET_CHANNELS.");
+
+       /* Trigger slope: 0=positive, 1=negative. */
+       memset(cmdstring, 0, sizeof(cmdstring));
+       cmdstring[0] = CMD_2250_SET_TRIGGERPOS_AND_BUFFER;
+
+       sr_dbg("Capture ratio: %" PRIu64 ".", devc->capture_ratio);
+       trig = devc->capture_ratio;
+       dso2250_set_triggerpos(trig,
+                       devc->framesize != FRAMESIZE_SMALL, cmdstring, 2);
+       dso2250_set_triggerpos(100 - trig,
+                       devc->framesize != FRAMESIZE_SMALL, cmdstring, 6);
+
+       if (send_begin(sdi) != SR_OK)
+               return SR_ERR;
+
+       /* TODO: 12 bytes according to documentation? */
+       if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+                       cmdstring, 10, &tmp, 100)) != 0) {
+               sr_err("Failed to set trigger position: %s.",
+                      libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
@@ -258,9 +407,12 @@ static int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi)
        uint16_t timebase_large[] = { 0xffff, 0x0000, 0xfffc, 0xfff7, 0xffe8,
                0xffce, 0xff9d, 0xff07, 0xfe0d, 0xfc19, 0xf63d, 0xec79 };
 
+       devc = sdi->priv;
+       if (devc->profile->fw_pid == 0x2250)
+               return dso2250_set_trigger_samplerate(sdi);
+
        sr_dbg("Preparing CMD_SET_TRIGGER_SAMPLERATE.");
 
-       devc = sdi->priv;
        usb = sdi->conn;
 
        memset(cmdstring, 0, sizeof(cmdstring));
@@ -344,8 +496,8 @@ static int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi)
        cmdstring[5] = (tmp >> 8) & 0xff;
 
        /* Horizontal trigger position */
-       sr_dbg("Trigger position: %3.2f.", devc->triggerposition);
-       tmp = 0x77fff + 0x8000 * devc->triggerposition;
+       sr_dbg("Capture ratio: %" PRIu64 ".", devc->capture_ratio);
+       tmp = 0x77fff + 0x8000 * devc->capture_ratio / 100;
        cmdstring[6] = tmp & 0xff;
        cmdstring[7] = (tmp >> 8) & 0xff;
        cmdstring[10] = (tmp >> 16) & 0xff;
@@ -419,10 +571,16 @@ static int dso_set_voltage(const struct sr_dev_inst *sdi)
 
        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 */
+       if (devc->profile->fw_pid == 0x2250) {
+               cmdstring[1] = 0x00;
+               cmdstring[2] = 0x08;
+       } else {
+               cmdstring[1] = 0x0f;
+               cmdstring[2] = 0x30;
+       }
+
+       /* CH1 volts/div is encoded in bits 0-1. */
        sr_dbg("CH1 vdiv index: %d.", devc->voltage[0]);
        switch (devc->voltage[0]) {
        case VDIV_1V:
@@ -442,7 +600,7 @@ static int dso_set_voltage(const struct sr_dev_inst *sdi)
                break;
        }
 
-       /* CH2 volts/div is encoded in bits 2-3 */
+       /* CH2 volts/div is encoded in bits 2-3. */
        sr_dbg("CH2 vdiv index: %d.", devc->voltage[1]);
        switch (devc->voltage[1]) {
        case VDIV_1V:
@@ -532,7 +690,7 @@ static int dso_set_relays(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static int dso_set_voffsets(const struct sr_dev_inst *sdi)
+SR_PRIV int dso_set_voffsets(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
@@ -634,7 +792,6 @@ SR_PRIV int dso_force_trigger(const struct sr_dev_inst *sdi)
 
 SR_PRIV int dso_init(const struct sr_dev_inst *sdi)
 {
-
        sr_dbg("Initializing DSO.");
 
        if (get_channel_offsets(sdi) != SR_OK)
index 9cab61b351d533b0f2c1031d73a2fabad65bd553..21131613144c3d4dd82480c2ff26746720f9154e 100644 (file)
 #define DEFAULT_VOLTAGE         VDIV_500MV
 #define DEFAULT_FRAMESIZE       FRAMESIZE_SMALL
 #define DEFAULT_TIMEBASE        TIME_100us
+#define DEFAULT_SAMPLERATE      SR_KHZ(10)
 #define DEFAULT_TRIGGER_SOURCE  "CH1"
 #define DEFAULT_COUPLING        COUPLING_DC
-#define DEFAULT_HORIZ_TRIGGERPOS 0.5
+#define DEFAULT_CAPTURE_RATIO   100
 #define DEFAULT_VERT_OFFSET     0.5
 #define DEFAULT_VERT_TRIGGERPOS 0.5
 
@@ -51,6 +52,8 @@
 /* All models have this for their "fast" mode. */
 #define FRAMESIZE_SMALL         (10 * 1024)
 
+#define NUM_CHANNELS            2
+
 enum control_requests {
        CTRL_READ_EEPROM = 0xa2,
        CTRL_GETSPEED = 0xb2,
@@ -60,17 +63,27 @@ enum control_requests {
 };
 
 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,
+       CMD_SET_FILTERS                     = 0x0,
+       CMD_SET_TRIGGER_SAMPLERATE          = 0x1,
+       CMD_FORCE_TRIGGER                   = 0x2,
+       CMD_CAPTURE_START                   = 0x3,
+       CMD_ENABLE_TRIGGER                  = 0x4,
+       CMD_GET_CHANNELDATA                 = 0x5,
+       CMD_GET_CAPTURESTATE                = 0x6,
+       CMD_SET_VOLTAGE                     = 0x7,
        /* unused */
-       CMD_SET_LOGICALDATA,
-       CMD_GET_LOGICALDATA,
+       CMD_SET_LOGICALDATA                 = 0x8,
+       CMD_GET_LOGICALDATA                 = 0x9,
+       CMD__UNUSED1                        = 0xa,
+       /*
+        * For the following and other specials please see
+        * http://openhantek.sourceforge.net/doc/namespaceHantek.html#ac1cd181814cf3da74771c29800b39028
+        */
+       CMD_2250_SET_CHANNELS               = 0xb,
+       CMD_2250_SET_TRIGGERSOURCE          = 0xc,
+       CMD_2250_SET_RECORD_LENGTH          = 0xd,
+       CMD_2250_SET_SAMPLERATE             = 0xe,
+       CMD_2250_SET_TRIGGERPOS_AND_BUFFER  = 0xf,
 };
 
 /* Must match the coupling table. */
@@ -128,6 +141,7 @@ enum capturestates {
        CAPTURE_EMPTY = 0,
        CAPTURE_FILLING = 1,
        CAPTURE_READY_8BIT = 2,
+       CAPTURE_READY_2250 = 3,
        CAPTURE_READY_9BIT = 7,
        CAPTURE_TIMEOUT = 127,
        CAPTURE_UNKNOWN = 255,
@@ -176,6 +190,7 @@ struct dev_context {
        int dev_state;
 
        /* Oscilloscope settings. */
+       uint64_t samplerate;
        int timebase;
        gboolean ch_enabled[2];
        int voltage[2];
@@ -189,7 +204,7 @@ struct dev_context {
        gboolean filter[2];
        int triggerslope;
        char *triggersource;
-       float triggerposition;
+       uint64_t capture_ratio;
        int triggermode;
 
        /* Frame transfer */
@@ -209,5 +224,7 @@ SR_PRIV int dso_get_capturestate(const struct sr_dev_inst *sdi,
 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);
+SR_PRIV int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi);
+SR_PRIV int dso_set_voffsets(const struct sr_dev_inst *sdi);
 
 #endif
index 3bcc723bd59742438c4e7c9889162077e56610e0..64fdab54deef5cbc875d57f3b3c07fbb84a498c5 100644 (file)
@@ -205,8 +205,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        sr_scpi_send(scpi, "TRIG HOLD");
        sr_scpi_get_float(scpi, "NPLC?", &devc->nplc);
 
-       sdi->status = SR_ST_ACTIVE;
-
        return SR_OK;
 }
 
@@ -214,9 +212,6 @@ static int dev_close(struct sr_dev_inst *sdi)
 {
        struct sr_scpi_dev_inst *scpi = sdi->conn;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        /* Disable scan-advance (preserve relay life). */
        sr_scpi_send(scpi, "SADV HOLD");
        /* Switch back to auto-triggering. */
@@ -224,22 +219,18 @@ static int dev_close(struct sr_dev_inst *sdi)
 
        sr_scpi_close(scpi);
 
-       sdi->status = SR_ST_INACTIVE;
-
        return SR_OK;
 }
 
 static int config_get(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
        struct dev_context *devc;
 
        (void)cg;
 
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_ADC_POWERLINE_CYCLES:
                *data = g_variant_new_double(devc->nplc);
@@ -248,14 +239,12 @@ static int config_get(uint32_t key, GVariant **data,
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_set(uint32_t key, GVariant *data,
-                     const struct sr_dev_inst *sdi,
-                     const struct sr_channel_group *cg)
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
        enum sr_mq mq;
        enum sr_mqflag mq_flags;
        struct dev_context *devc;
@@ -263,12 +252,8 @@ static int config_set(uint32_t key, GVariant *data,
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
                devc->limit_samples = g_variant_get_uint64(data);
@@ -278,54 +263,26 @@ static int config_set(uint32_t key, GVariant *data,
                mq = g_variant_get_uint32(tuple_child);
                tuple_child = g_variant_get_child_value(data, 1);
                mq_flags = g_variant_get_uint64(tuple_child);
-               ret = hp_3457a_set_mq(sdi, mq, mq_flags);
                g_variant_unref(tuple_child);
-               break;
+               return hp_3457a_set_mq(sdi, mq, mq_flags);
        case SR_CONF_ADC_POWERLINE_CYCLES:
-               ret = hp_3457a_set_nplc(sdi, g_variant_get_double(data));
-               break;
+               return hp_3457a_set_nplc(sdi, g_variant_get_double(data));
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
-
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       } else if ((key == SR_CONF_DEVICE_OPTIONS) && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       } else if ((key == SR_CONF_DEVICE_OPTIONS) && !cg) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* From here on, we're only concerned with channel group config. */
-       if (!cg)
-               return SR_ERR_NA;
-
        /*
         * TODO: Implement channel group configuration when adding support for
         * plug-in cards.
         */
 
-       ret = SR_OK;
-       switch (key) {
-       default:
-               ret = SR_ERR_NA;
-       }
-
-       return ret;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static void create_channel_index_list(GSList *channels, GArray **arr)
@@ -367,9 +324,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        GArray *ch_list;
        GSList *channels;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        scpi = sdi->conn;
        devc = sdi->priv;
 
@@ -405,8 +359,13 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                return SR_ERR_ARG;
        }
 
-       devc->current_channel = devc->active_channels->data;
        devc->num_active_channels = g_slist_length(devc->active_channels);
+       if (!devc->num_active_channels) {
+               sr_err("Need at least one active channel!");
+               g_slist_free(devc->active_channels);
+               return SR_ERR_ARG;
+       }
+       devc->current_channel = devc->active_channels->data;
 
        hp_3457a_select_input(sdi, front_selected ? CONN_FRONT : CONN_REAR);
 
@@ -445,6 +404,7 @@ static struct sr_dev_driver hp_3457a_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index dd8d6f8fdbeb75df1f3873fe5e68d5f27036a156..f0197290f3b6d0e4825194335309d8a1a0df8d42 100644 (file)
@@ -25,6 +25,7 @@
 static int set_mq_volt(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
 static int set_mq_amp(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
 static int set_mq_ohm(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+
 /*
  * The source for the frequency measurement can be either AC voltage, AC+DC
  * voltage, AC current, or AC+DC current. Configuring this is not yet
@@ -435,7 +436,7 @@ SR_PRIV int hp_3457a_receive_data(int fd, int revents, void *cb_data)
                ret = sr_scpi_get_double(scpi, NULL, &devc->last_channel_sync);
                if (ret != SR_OK) {
                        sr_err("Cannot check channel synchronization.");
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                        return FALSE;
                }
                devc->acq_state = ACQ_GOT_CHANNEL_SYNC;
@@ -456,7 +457,7 @@ SR_PRIV int hp_3457a_receive_data(int fd, int revents, void *cb_data)
                        sr_err("Expected channel %u, but device says %u",
                               chanc->index,
                               (unsigned int)devc->last_channel_sync);
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                        return FALSE;
                }
                /* All is good. Back to business. */
@@ -464,7 +465,7 @@ SR_PRIV int hp_3457a_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return FALSE;
        }
 
index dda19a23610e7e75e7df82f579da648bd059cb80..3fef0efaf8044132c25888dfcbc8da5d0402b5bf 100644 (file)
@@ -57,13 +57,10 @@ enum channel_conn {
        CONN_REAR,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        /* Information about rear card option, or NULL if unknown */
        const struct rear_card_info *rear_card;
 
-       /* Acquisition settings */
        enum sr_mq measurement_mq;
        enum sr_mqflag measurement_mq_flags;
        enum sr_unit measurement_unit;
@@ -73,7 +70,6 @@ struct dev_context {
        unsigned int num_active_channels;
        struct sr_channel *current_channel;
 
-       /* Operational state */
        enum acquisition_state acq_state;
        enum channel_conn input_loc;
        uint64_t num_samples;
diff --git a/src/hardware/hp-3478a/api.c b/src/hardware/hp-3478a/api.c
new file mode 100644 (file)
index 0000000..c87affd
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "scpi.h"
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const struct {
+       enum sr_mq mq;
+       enum sr_mqflag mqflag;
+} mqopts[] = {
+       {SR_MQ_VOLTAGE, SR_MQFLAG_DC},
+       {SR_MQ_VOLTAGE, SR_MQFLAG_DC | SR_MQFLAG_AUTORANGE},
+       {SR_MQ_VOLTAGE, SR_MQFLAG_AC | SR_MQFLAG_RMS},
+       {SR_MQ_VOLTAGE, SR_MQFLAG_AC | SR_MQFLAG_RMS | SR_MQFLAG_AUTORANGE},
+       {SR_MQ_CURRENT, SR_MQFLAG_DC},
+       {SR_MQ_CURRENT, SR_MQFLAG_DC | SR_MQFLAG_AUTORANGE},
+       {SR_MQ_CURRENT, SR_MQFLAG_AC | SR_MQFLAG_RMS},
+       {SR_MQ_CURRENT, SR_MQFLAG_AC | SR_MQFLAG_RMS | SR_MQFLAG_AUTORANGE},
+       {SR_MQ_RESISTANCE, 0},
+       {SR_MQ_RESISTANCE, 0 | SR_MQFLAG_AUTORANGE},
+       {SR_MQ_RESISTANCE, SR_MQFLAG_FOUR_WIRE},
+       {SR_MQ_RESISTANCE, SR_MQFLAG_FOUR_WIRE | SR_MQFLAG_AUTORANGE},
+};
+
+SR_PRIV struct sr_dev_driver hp_3478a_driver_info;
+
+static int create_front_channel(struct sr_dev_inst *sdi, int chan_idx)
+{
+       struct sr_channel *channel;
+       struct channel_context *chanc;
+
+       chanc = g_malloc(sizeof(*chanc));
+       chanc->location = TERMINAL_FRONT;
+
+       channel = sr_channel_new(sdi, chan_idx++, SR_CHANNEL_ANALOG, TRUE, "P1");
+       channel->priv = chanc;
+
+       return chan_idx;
+}
+
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
+{
+       int ret;
+       struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->vendor = g_strdup("Hewlett-Packard");
+       sdi->model = g_strdup("3478A");
+       sdi->conn = scpi;
+       sdi->driver = &hp_3478a_driver_info;
+       sdi->inst_type = SR_INST_SCPI;
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       sr_sw_limits_init(&devc->limits);
+       sdi->priv = devc;
+
+       /* Get actual status (function, digits, ...). */
+       ret = hp_3478a_get_status_bytes(sdi);
+       if (ret != SR_OK)
+               return NULL;
+
+       create_front_channel(sdi, 0);
+
+       return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       return sr_scpi_scan(di->context, options, probe_device);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       return sr_scpi_open(sdi->conn);
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       return sr_scpi_close(sdi->conn);
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       int ret;
+       GVariant *arr[2];
+
+       (void)cg;
+
+       devc = sdi->priv;
+
+       switch (key) {
+       case SR_CONF_LIMIT_SAMPLES:
+       case SR_CONF_LIMIT_MSEC:
+               return sr_sw_limits_config_get(&devc->limits, key, data);
+       case SR_CONF_MEASURED_QUANTITY:
+               ret = hp_3478a_get_status_bytes(sdi);
+               if (ret != SR_OK)
+                       return ret;
+               arr[0] = g_variant_new_uint32(devc->measurement_mq);
+               arr[1] = g_variant_new_uint64(devc->measurement_mq_flags);
+               *data = g_variant_new_tuple(arr, 2);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       enum sr_mq mq;
+       enum sr_mqflag mq_flags;
+       GVariant *tuple_child;
+
+       (void)cg;
+
+       devc = sdi->priv;
+
+       switch (key) {
+       case SR_CONF_LIMIT_SAMPLES:
+       case SR_CONF_LIMIT_MSEC:
+               return sr_sw_limits_config_set(&devc->limits, key, data);
+       case SR_CONF_MEASURED_QUANTITY:
+               tuple_child = g_variant_get_child_value(data, 0);
+               mq = g_variant_get_uint32(tuple_child);
+               tuple_child = g_variant_get_child_value(data, 1);
+               mq_flags = g_variant_get_uint64(tuple_child);
+               g_variant_unref(tuple_child);
+               return hp_3478a_set_mq(sdi, mq, mq_flags);
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       unsigned int i;
+       GVariant *gvar, *arr[2];
+       GVariantBuilder gvb;
+
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_MEASURED_QUANTITY:
+               /*
+                * TODO: move to std.c as
+                *       SR_PRIV GVariant *std_gvar_measured_quantities()
+                */
+               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+               for (i = 0; i < ARRAY_SIZE(mqopts); i++) {
+                       arr[0] = g_variant_new_uint32(mqopts[i].mq);
+                       arr[1] = g_variant_new_uint64(mqopts[i].mqflag);
+                       gvar = g_variant_new_tuple(arr, 2);
+                       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)
+{
+       struct sr_scpi_dev_inst *scpi;
+       struct dev_context *devc;
+
+       scpi = sdi->conn;
+       devc = sdi->priv;
+
+       sr_sw_limits_acquisition_start(&devc->limits);
+       std_session_send_df_header(sdi);
+
+       /*
+        * NOTE: For faster readings, there are some things one can do:
+        *     - Turn off the display: sr_scpi_send(scpi, "D3SIGROK").
+        *     - Set the line frequency to 60Hz via switch (back of the unit).
+        *     - Set to 3.5 digits measurement (add config key SR_CONF_DIGITS).
+        */
+
+       /* Set to internal trigger. */
+       sr_scpi_send(scpi, "T1");
+       /* Get device status. */
+       hp_3478a_get_status_bytes(sdi);
+
+       return sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 100,
+                       hp_3478a_receive_data, (void *)sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       struct sr_scpi_dev_inst *scpi;
+
+       scpi = sdi->conn;
+
+       sr_scpi_source_remove(sdi->session, scpi);
+       std_session_send_df_end(sdi);
+
+       /* Set to internal trigger. */
+       sr_scpi_send(scpi, "T1");
+       /* Turn on display. */
+       sr_scpi_send(scpi, "D1");
+
+       return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver hp_3478a_driver_info = {
+       .name = "hp-3478a",
+       .longname = "HP 3478A",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .context = NULL,
+};
+
+SR_REGISTER_DEV_DRIVER(hp_3478a_driver_info);
diff --git a/src/hardware/hp-3478a/protocol.c b/src/hardware/hp-3478a/protocol.c
new file mode 100644 (file)
index 0000000..3ddce58
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017-2018 Frank Stettner <frank-stettner@gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+#include "scpi.h"
+#include "protocol.h"
+
+static int set_mq_volt(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+static int set_mq_amp(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+static int set_mq_ohm(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+
+static const struct {
+       enum sr_mq mq;
+       int (*set_mode)(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+} sr_mq_to_cmd_map[] = {
+       { SR_MQ_VOLTAGE, set_mq_volt },
+       { SR_MQ_CURRENT, set_mq_amp },
+       { SR_MQ_RESISTANCE, set_mq_ohm },
+};
+
+static int set_mq_volt(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags)
+{
+       if ((flags & SR_MQFLAG_AC) != SR_MQFLAG_AC &&
+               (flags & SR_MQFLAG_DC) != SR_MQFLAG_DC)
+               return SR_ERR_NA;
+
+       return sr_scpi_send(scpi, "%s",
+               ((flags & SR_MQFLAG_AC) == SR_MQFLAG_AC) ? "F2" : "F1");
+}
+
+static int set_mq_amp(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags)
+{
+       if ((flags & SR_MQFLAG_AC) != SR_MQFLAG_AC &&
+               (flags & SR_MQFLAG_DC) != SR_MQFLAG_DC)
+               return SR_ERR_NA;
+
+       return sr_scpi_send(scpi, "%s", (flags & SR_MQFLAG_AC) ? "F6" : "F5");
+}
+
+static int set_mq_ohm(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags)
+{
+       return sr_scpi_send(scpi, "%s",
+               (flags & SR_MQFLAG_FOUR_WIRE) ? "F4" : "F3");
+}
+
+SR_PRIV int hp_3478a_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq,
+                               enum sr_mqflag mq_flags)
+{
+       int ret;
+       size_t i;
+       struct sr_scpi_dev_inst *scpi = sdi->conn;
+       struct dev_context *devc = sdi->priv;
+
+       /* No need to send command if we're not changing measurement type. */
+       if (devc->measurement_mq == mq &&
+               ((devc->measurement_mq_flags & mq_flags) == mq_flags))
+               return SR_OK;
+
+       for (i = 0; i < ARRAY_SIZE(sr_mq_to_cmd_map); i++) {
+               if (sr_mq_to_cmd_map[i].mq != mq)
+                       continue;
+
+               ret = sr_mq_to_cmd_map[i].set_mode(scpi, mq_flags);
+               if (ret != SR_OK)
+                       return ret;
+
+               ret = hp_3478a_get_status_bytes(sdi);
+               return ret;
+       }
+
+       return SR_ERR_NA;
+}
+
+static int parse_range_vdc(struct dev_context *devc, uint8_t range_byte)
+{
+       if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_30MV)
+               devc->enc_digits = devc->spec_digits - 2;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_300MV)
+               devc->enc_digits = devc->spec_digits - 3;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_3V)
+               devc->enc_digits = devc->spec_digits - 1;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_30V)
+               devc->enc_digits = devc->spec_digits - 2;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_300V)
+               devc->enc_digits = devc->spec_digits - 3;
+       else
+               return SR_ERR_DATA;
+
+       return SR_OK;
+}
+
+static int parse_range_vac(struct dev_context *devc, uint8_t range_byte)
+{
+       if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VAC_300MV)
+               devc->enc_digits = devc->spec_digits - 3;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VAC_3V)
+               devc->enc_digits = devc->spec_digits - 1;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VAC_30V)
+               devc->enc_digits = devc->spec_digits - 2;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VAC_300V)
+               devc->enc_digits = devc->spec_digits - 3;
+       else
+               return SR_ERR_DATA;
+
+       return SR_OK;
+}
+
+static int parse_range_a(struct dev_context *devc, uint8_t range_byte)
+{
+       if ((range_byte & SB1_RANGE_BLOCK) == RANGE_A_300MA)
+               devc->enc_digits = devc->spec_digits - 3;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_A_3A)
+               devc->enc_digits = devc->spec_digits - 1;
+       else
+               return SR_ERR_DATA;
+
+       return SR_OK;
+}
+
+static int parse_range_ohm(struct dev_context *devc, uint8_t range_byte)
+{
+       if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_30R)
+               devc->enc_digits = devc->spec_digits - 2;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_300R)
+               devc->enc_digits = devc->spec_digits - 3;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_3KR)
+               devc->enc_digits = devc->spec_digits - 1;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_30KR)
+               devc->enc_digits = devc->spec_digits - 2;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_300KR)
+               devc->enc_digits = devc->spec_digits - 3;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_3MR)
+               devc->enc_digits = devc->spec_digits - 1;
+       else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_30MR)
+               devc->enc_digits = devc->spec_digits - 2;
+       else
+               return SR_ERR_DATA;
+
+       return SR_OK;
+}
+
+static int parse_function_byte(struct dev_context *devc, uint8_t function_byte)
+{
+       /* Digits / Resolution (spec_digits must be set before range parsing) */
+       if ((function_byte & SB1_DIGITS_BLOCK) == DIGITS_5_5)
+               devc->spec_digits = 6;
+       else if ((function_byte & SB1_DIGITS_BLOCK) == DIGITS_4_5)
+               devc->spec_digits = 5;
+       else if ((function_byte & SB1_DIGITS_BLOCK) == DIGITS_3_5)
+               devc->spec_digits = 4;
+       else
+               return SR_ERR_DATA;
+
+       /* Function + Range */
+       devc->measurement_mq_flags = 0;
+       if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_VDC) {
+               devc->measurement_mq = SR_MQ_VOLTAGE;
+               devc->measurement_mq_flags |= SR_MQFLAG_DC;
+               devc->measurement_unit = SR_UNIT_VOLT;
+               parse_range_vdc(devc, function_byte);
+       } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_VAC) {
+               devc->measurement_mq = SR_MQ_VOLTAGE;
+               devc->measurement_mq_flags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+               devc->measurement_unit = SR_UNIT_VOLT;
+               parse_range_vac(devc, function_byte);
+       } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_2WR) {
+               devc->measurement_mq = SR_MQ_RESISTANCE;
+               devc->measurement_unit = SR_UNIT_OHM;
+               parse_range_ohm(devc, function_byte);
+       } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_4WR) {
+               devc->measurement_mq = SR_MQ_RESISTANCE;
+               devc->measurement_mq_flags |= SR_MQFLAG_FOUR_WIRE;
+               devc->measurement_unit = SR_UNIT_OHM;
+               parse_range_ohm(devc, function_byte);
+       } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_ADC) {
+               devc->measurement_mq = SR_MQ_CURRENT;
+               devc->measurement_mq_flags |= SR_MQFLAG_DC;
+               devc->measurement_unit = SR_UNIT_AMPERE;
+               parse_range_a(devc, function_byte);
+       } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_AAC) {
+               devc->measurement_mq = SR_MQ_CURRENT;
+               devc->measurement_mq_flags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+               devc->measurement_unit = SR_UNIT_AMPERE;
+               parse_range_a(devc, function_byte);
+       } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_EXR) {
+               devc->measurement_mq = SR_MQ_RESISTANCE;
+               devc->measurement_unit = SR_UNIT_OHM;
+               parse_range_ohm(devc, function_byte);
+       }
+
+       return SR_OK;
+}
+
+static int parse_status_byte(struct dev_context *devc, uint8_t status_byte)
+{
+       devc->trigger = TRIGGER_UNDEFINED;
+
+       /* External Trigger */
+       if ((status_byte & STATUS_EXT_TRIGGER) == STATUS_EXT_TRIGGER)
+               devc->trigger = TRIGGER_EXTERNAL;
+
+       /* Cal RAM */
+       if ((status_byte & STATUS_CAL_RAM) == STATUS_CAL_RAM)
+               devc->calibration = TRUE;
+       else
+               devc->calibration = FALSE;
+
+       /* Front/Rear terminals */
+       if ((status_byte & STATUS_FRONT_TERMINAL) == STATUS_FRONT_TERMINAL)
+               devc->terminal = TERMINAL_FRONT;
+       else
+               devc->terminal = TERMINAL_REAR;
+
+       /* 50Hz / 60Hz */
+       if ((status_byte & STATUS_50HZ) == STATUS_50HZ)
+               devc->line = LINE_50HZ;
+       else
+               devc->line = LINE_60HZ;
+
+       /* Auto-Zero */
+       if ((status_byte & STATUS_AUTO_ZERO) == STATUS_AUTO_ZERO)
+               devc->auto_zero = TRUE;
+       else
+               devc->auto_zero = FALSE;
+
+       /* Auto-Range */
+       if ((status_byte & STATUS_AUTO_RANGE) == STATUS_AUTO_RANGE)
+               devc->measurement_mq_flags |= SR_MQFLAG_AUTORANGE;
+       else
+               devc->measurement_mq_flags &= ~SR_MQFLAG_AUTORANGE;
+
+       /* Internal trigger */
+       if ((status_byte & STATUS_INT_TRIGGER) == STATUS_INT_TRIGGER)
+               devc->trigger = TRIGGER_INTERNAL;
+
+       return SR_OK;
+}
+
+static int parse_srq_byte(uint8_t sqr_byte)
+{
+       (void)sqr_byte;
+
+#if 0
+       /* The ServiceReQuest register isn't used at the moment. */
+
+       /* PON SRQ */
+       if ((sqr_byte & SRQ_POWER_ON) == SRQ_POWER_ON)
+               sr_spew("Power On SRQ or clear msg received");
+
+       /* Cal failed SRQ */
+       if ((sqr_byte & SRQ_CAL_FAILED) == SRQ_CAL_FAILED)
+               sr_spew("CAL failed SRQ");
+
+       /* Keyboard SRQ */
+       if ((sqr_byte & SRQ_KEYBORD) == SRQ_KEYBORD)
+               sr_spew("Keyboard SRQ");
+
+       /* Hardware error SRQ */
+       if ((sqr_byte & SRQ_HARDWARE_ERR) == SRQ_HARDWARE_ERR)
+               sr_spew("Hardware error SRQ");
+
+       /* Syntax error SRQ */
+       if ((sqr_byte & SRQ_SYNTAX_ERR) == SRQ_SYNTAX_ERR)
+               sr_spew("Syntax error SRQ");
+
+       /* Every reading is available to the bus SRQ */
+       if ((sqr_byte & SRQ_BUS_AVAIL) == SRQ_BUS_AVAIL)
+               sr_spew("Every reading is available to the bus SRQ");
+#endif
+
+       return SR_OK;
+}
+
+static int parse_error_byte(uint8_t error_byte)
+{
+       int ret;
+
+       ret = SR_OK;
+
+       /* A/D link */
+       if ((error_byte & ERROR_AD_LINK) == ERROR_AD_LINK) {
+               sr_err("Failure in the A/D link");
+               ret = SR_ERR;
+       }
+
+       /* A/D Self Test */
+       if ((error_byte & ERROR_AD_SELF_TEST) == ERROR_AD_SELF_TEST) {
+               sr_err("A/D has failed its internal Self Test");
+               ret = SR_ERR;
+       }
+
+       /* A/D slope error */
+       if ((error_byte & ERROR_AD_SLOPE) == ERROR_AD_SLOPE) {
+               sr_err("There has been an A/D slope error");
+               ret = SR_ERR;
+       }
+
+       /* ROM Selt Test */
+       if ((error_byte & ERROR_ROM_SELF_TEST) == ERROR_ROM_SELF_TEST) {
+               sr_err("The ROM Self Test has failed");
+               ret = SR_ERR;
+       }
+
+       /* RAM Selt Test */
+       if ((error_byte & ERROR_RAM_SELF_TEST) == ERROR_RAM_SELF_TEST) {
+               sr_err("The RAM Self Test has failed");
+               ret = SR_ERR;
+       }
+
+       /* Selt Test */
+       if ((error_byte & ERROR_SELF_TEST) == ERROR_SELF_TEST) {
+               sr_err("Self Test: Any of the CAL RAM locations have bad "
+                      "checksums, or a range with a bad checksum is selected");
+               ret = SR_ERR;
+       }
+
+       return ret;
+}
+
+SR_PRIV int hp_3478a_get_status_bytes(const struct sr_dev_inst *sdi)
+{
+       int ret;
+       char *response;
+       uint8_t function_byte, status_byte, srq_byte, error_byte;
+       struct sr_scpi_dev_inst *scpi = sdi->conn;
+       struct dev_context *devc = sdi->priv;
+
+       ret = sr_scpi_get_string(scpi, "B", &response);
+       if (ret != SR_OK)
+               return ret;
+
+       if (!response)
+               return SR_ERR;
+
+       function_byte = (uint8_t)response[0];
+       status_byte = (uint8_t)response[1];
+       srq_byte = (uint8_t)response[2];
+       error_byte = (uint8_t)response[3];
+
+       g_free(response);
+
+       parse_function_byte(devc, function_byte);
+       parse_status_byte(devc, status_byte);
+       parse_srq_byte(srq_byte);
+       ret = parse_error_byte(error_byte);
+
+       return ret;
+}
+
+static void acq_send_measurement(struct sr_dev_inst *sdi)
+{
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_analog analog;
+       struct sr_analog_encoding encoding;
+       struct sr_analog_meaning meaning;
+       struct sr_analog_spec spec;
+       struct dev_context *devc;
+       float f;
+
+       devc = sdi->priv;
+
+       packet.type = SR_DF_ANALOG;
+       packet.payload = &analog;
+
+       sr_analog_init(&analog, &encoding, &meaning, &spec, devc->enc_digits);
+
+       /* TODO: Implement NAN, depending on counts, range and value. */
+       f = devc->measurement;
+       analog.num_samples = 1;
+       analog.data = &f;
+
+       encoding.unitsize = sizeof(float);
+       encoding.is_float = TRUE;
+       encoding.digits = devc->enc_digits;
+
+       meaning.mq = devc->measurement_mq;
+       meaning.mqflags = devc->measurement_mq_flags;
+       meaning.unit = devc->measurement_unit;
+       meaning.channels = sdi->channels;
+
+       spec.spec_digits = devc->spec_digits;
+
+       sr_session_send(sdi, &packet);
+}
+
+SR_PRIV int hp_3478a_receive_data(int fd, int revents, void *cb_data)
+{
+       struct sr_scpi_dev_inst *scpi;
+       struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+
+       (void)fd;
+       (void)revents;
+
+       if (!(sdi = cb_data) || !(devc = sdi->priv))
+               return TRUE;
+
+       scpi = sdi->conn;
+
+       /*
+        * This is necessary to get the actual range for the encoding digits.
+        * When SPoll is implemmented, this can be done via SPoll.
+        */
+       if (hp_3478a_get_status_bytes(sdi) != SR_OK)
+               return FALSE;
+
+       /*
+        * TODO: Implement GPIB-SPoll, to get notified by a SRQ when a new
+        *       measurement is available. This is necessary, because when
+        *       switching ranges, there could be a timeout.
+        */
+       if (sr_scpi_get_double(scpi, NULL, &devc->measurement) != SR_OK)
+               return FALSE;
+
+       acq_send_measurement(sdi);
+       sr_sw_limits_update_samples_read(&devc->limits, 1);
+
+       if (sr_sw_limits_check(&devc->limits))
+               sr_dev_acquisition_stop(sdi);
+
+       return TRUE;
+}
diff --git a/src/hardware/hp-3478a/protocol.h b/src/hardware/hp-3478a/protocol.h
new file mode 100644 (file)
index 0000000..baf622b
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_HP_3478A_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_HP_3478A_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "hp-3478a"
+
+#define SB1_FUNCTION_BLOCK     0b11100000
+#define SB1_RANGE_BLOCK                0b00011100
+#define SB1_DIGITS_BLOCK       0b00000011
+
+/* Status Byte 1 (Function) */
+enum sb1_function {
+       FUNCTION_VDC                    = 0b00100000,
+       FUNCTION_VAC                    = 0b01000000,
+       FUNCTION_2WR                    = 0b01100000,
+       FUNCTION_4WR                    = 0b10000000,
+       FUNCTION_ADC                    = 0b10100000,
+       FUNCTION_AAC                    = 0b11000000,
+       FUNCTION_EXR                    = 0b11100000,
+};
+
+/* Status Byte 1 (Range V DC) */
+enum sb1_range_vdc {
+       RANGE_VDC_30MV                  = 0b00000100,
+       RANGE_VDC_300MV                 = 0b00001000,
+       RANGE_VDC_3V                    = 0b00001100,
+       RANGE_VDC_30V                   = 0b00010000,
+       RANGE_VDC_300V                  = 0b00010100,
+};
+
+/* Status Byte 1 (Range V AC) */
+enum sb1_range_vac {
+       RANGE_VAC_300MV                 = 0b00000100,
+       RANGE_VAC_3V                    = 0b00001000,
+       RANGE_VAC_30V                   = 0b00001100,
+       RANGE_VAC_300V                  = 0b00010000,
+};
+
+/* Status Byte 1 (Range A) */
+enum sb1_range_a {
+       RANGE_A_300MA                   = 0b00000100,
+       RANGE_A_3A                      = 0b00001000,
+};
+
+/* Status Byte 1 (Range Ohm) */
+enum sb1_range_ohm {
+       RANGE_OHM_30R                   = 0b00000100,
+       RANGE_OHM_300R                  = 0b00001000,
+       RANGE_OHM_3KR                   = 0b00001100,
+       RANGE_OHM_30KR                  = 0b00010000,
+       RANGE_OHM_300KR                 = 0b00010100,
+       RANGE_OHM_3MR                   = 0b00011000,
+       RANGE_OHM_30MR                  = 0b00011100,
+};
+
+/* Status Byte 1 (Digits) */
+enum sb1_digits {
+       DIGITS_5_5                      = 0b00000001,
+       DIGITS_4_5                      = 0b00000010,
+       DIGITS_3_5                      = 0b00000011,
+};
+
+/* Status Byte 2 */
+enum sb2_status {
+       STATUS_INT_TRIGGER              = (1 << 0),
+       STATUS_AUTO_RANGE               = (1 << 1),
+       STATUS_AUTO_ZERO                = (1 << 2),
+       STATUS_50HZ                     = (1 << 3),
+       STATUS_FRONT_TERMINAL           = (1 << 4),
+       STATUS_CAL_RAM                  = (1 << 5),
+       STATUS_EXT_TRIGGER              = (1 << 6),
+};
+
+/* Status Byte 3 (Serial Poll Mask) */
+enum sb3_srq {
+       SRQ_BUS_AVAIL                   = (1 << 0),
+       SRQ_SYNTAX_ERR                  = (1 << 2),
+       SRQ_HARDWARE_ERR                = (1 << 3),
+       SRQ_KEYBORD                     = (1 << 4),
+       SRQ_CAL_FAILED                  = (1 << 5),
+       SRQ_POWER_ON                    = (1 << 7),
+};
+
+/* Status Byte 4 (Error) */
+enum sb4_error {
+       ERROR_SELF_TEST                 = (1 << 0),
+       ERROR_RAM_SELF_TEST             = (1 << 1),
+       ERROR_ROM_SELF_TEST             = (1 << 2),
+       ERROR_AD_SLOPE                  = (1 << 3),
+       ERROR_AD_SELF_TEST              = (1 << 4),
+       ERROR_AD_LINK                   = (1 << 5),
+};
+
+/* Channel connector (front terminals or rear terminals. */
+enum terminal_connector {
+       TERMINAL_FRONT,
+       TERMINAL_REAR,
+};
+
+/* Possible triggers */
+enum trigger_state {
+       TRIGGER_UNDEFINED,
+       TRIGGER_EXTERNAL,
+       TRIGGER_INTERNAL,
+};
+
+/* Possible line frequencies */
+enum line_freq {
+       LINE_50HZ,
+       LINE_60HZ,
+};
+
+struct dev_context {
+       struct sr_sw_limits limits;
+
+       double measurement;
+       enum sr_mq measurement_mq;
+       enum sr_mqflag measurement_mq_flags;
+       enum sr_unit measurement_unit;
+       uint8_t enc_digits;
+       uint8_t spec_digits;
+
+       enum terminal_connector terminal;
+       enum trigger_state trigger;
+       enum line_freq line;
+       gboolean auto_zero;
+       gboolean calibration;
+};
+
+struct channel_context {
+       int index;
+       enum terminal_connector location;
+};
+
+SR_PRIV int hp_3478a_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq,
+                               enum sr_mqflag mq_flags);
+SR_PRIV int hp_3478a_get_status_bytes(const struct sr_dev_inst *sdi);
+SR_PRIV int hp_3478a_receive_data(int fd, int revents, void *cb_data);
+
+#endif
index 56acd856eb620e447a1460b87ed173b44865169c..f3d94d2003d386de64760e37d31d4499f20b2bb0 100644 (file)
@@ -39,7 +39,7 @@ static const uint32_t devopts[] = {
        SR_CONF_BUFFERSIZE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const uint32_t cgopts[] = {
+static const uint32_t devopts_cg[] = {
        SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_PROBE_FACTOR | SR_CONF_GET | SR_CONF_SET,
@@ -218,10 +218,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, devices);
 }
 
-static void clear_private(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc = priv;
-
        g_slist_free(devc->enabled_channel);
 }
 
@@ -238,7 +236,7 @@ static int dev_clear(const struct sr_dev_driver *di)
                }
        }
 
-       return std_dev_clear(di, clear_private);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -246,9 +244,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        struct dev_context *devc = sdi->priv;
        int i;
 
-       if (sdi->status != SR_ST_INACTIVE)
-               goto fail1;
-
        if (ieee1284_open(sdi->conn, 0, &i) != E1284_OK)
                goto fail1;
 
@@ -265,8 +260,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        if (!devc->samples)
                goto fail3;
 
-       sdi->status = SR_ST_ACTIVE;
-
        return SR_OK;
 
 fail3:
@@ -282,82 +275,24 @@ static int dev_close(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_OK;
-
        g_free(devc->samples);
        hung_chang_dso_2100_reset_port(sdi->conn);
        ieee1284_release(sdi->conn);
        ieee1284_close(sdi->conn);
-       sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
 }
 
-static int find_in_array(GVariant *data, const GVariantType *type,
-                        const void *arr, int n)
-{
-       const char * const *sarr;
-       const char *s;
-       const uint64_t *u64arr;
-       const uint8_t *u8arr;
-       uint64_t u64;
-       uint8_t u8;
-       int i;
-
-       if (!g_variant_is_of_type(data, type))
-               return -1;
-
-       switch (g_variant_classify(data)) {
-       case G_VARIANT_CLASS_STRING:
-               s = g_variant_get_string(data, NULL);
-               sarr = arr;
-
-               for (i = 0; i < n; i++)
-                       if (!strcmp(s, sarr[i]))
-                               return i;
-               break;
-       case G_VARIANT_CLASS_UINT64:
-               u64 = g_variant_get_uint64(data);
-               u64arr = arr;
-
-               for (i = 0; i < n; i++)
-                       if (u64 == u64arr[i])
-                               return i;
-               break;
-       case G_VARIANT_CLASS_BYTE:
-               u8 = g_variant_get_byte(data);
-               u8arr = arr;
-
-               for (i = 0; i < n; i++)
-                       if (u8 == u8arr[i])
-                               return i;
-       default:
-               break;
-       }
-
-       return -1;
-}
-
-static int reverse_map(uint8_t u, const uint8_t *arr, int n)
-{
-       GVariant *v = g_variant_new_byte(u);
-       int i = find_in_array(v, G_VARIANT_TYPE_BYTE, arr, n);
-       g_variant_unref(v);
-       return i;
-}
-
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
        struct parport *port;
-       int ret, i, ch = -1;
+       int idx, ch = -1;
 
        if (cg) /* sr_config_get will validate cg using config_list */
                ch = ((struct sr_channel *)cg->channels->data)->index;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_CONN:
                port = sdi->conn;
@@ -370,177 +305,128 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                *data = g_variant_new_uint64(samplerates[devc->rate]);
                break;
        case SR_CONF_TRIGGER_SOURCE:
-               i = reverse_map(devc->cctl[0] & 0xC0, trigger_sources_map,
-                               ARRAY_SIZE(trigger_sources_map));
-               if (i == -1)
-                       ret = SR_ERR;
-               else
-                       *data = g_variant_new_string(trigger_sources[i]);
+               if ((idx = std_u8_idx_s(devc->cctl[0] & 0xC0, ARRAY_AND_SIZE(trigger_sources_map))) < 0)
+                       return SR_ERR_BUG;
+               *data = g_variant_new_string(trigger_sources[idx]);
                break;
        case SR_CONF_TRIGGER_SLOPE:
                if (devc->edge >= ARRAY_SIZE(trigger_slopes))
-                       ret = SR_ERR;
-               else
-                       *data = g_variant_new_string(trigger_slopes[devc->edge]);
+                       return SR_ERR;
+               *data = g_variant_new_string(trigger_slopes[devc->edge]);
                break;
        case SR_CONF_BUFFERSIZE:
                *data = g_variant_new_uint64(buffersizes[devc->last_step]);
                break;
        case SR_CONF_VDIV:
-               if (ch == -1) {
-                       ret = SR_ERR_CHANNEL_GROUP;
-               } else {
-                       i = reverse_map(devc->cctl[ch] & 0x33, vdivs_map,
-                                       ARRAY_SIZE(vdivs_map));
-                       if (i == -1)
-                               ret = SR_ERR;
-                       else
-                               *data = g_variant_new("(tt)", vdivs[i][0],
-                                                     vdivs[i][1]);
-               }
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if ((idx = std_u8_idx_s(devc->cctl[ch] & 0x33, ARRAY_AND_SIZE(vdivs_map))) < 0)
+                       return SR_ERR_BUG;
+               *data = g_variant_new("(tt)", vdivs[idx][0], vdivs[idx][1]);
                break;
        case SR_CONF_COUPLING:
-               if (ch == -1) {
-                       ret = SR_ERR_CHANNEL_GROUP;
-               } else {
-                       i = reverse_map(devc->cctl[ch] & 0x0C, coupling_map,
-                                       ARRAY_SIZE(coupling_map));
-                       if (i == -1)
-                               ret = SR_ERR;
-                       else
-                               *data = g_variant_new_string(coupling[i]);
-               }
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if ((idx = std_u8_idx_s(devc->cctl[ch] & 0x0C, ARRAY_AND_SIZE(coupling_map))) < 0)
+                       return SR_ERR_BUG;
+               *data = g_variant_new_string(coupling[idx]);
                break;
        case SR_CONF_PROBE_FACTOR:
-               if (ch == -1)
-                       ret = SR_ERR_CHANNEL_GROUP;
-               else
-                       *data = g_variant_new_uint64(devc->probe[ch]);
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               *data = g_variant_new_uint64(devc->probe[ch]);
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
-       int ret, i, ch = -1;
-       uint64_t u, v;
+       int idx, ch = -1;
+       uint64_t u;
 
        if (cg) /* sr_config_set will validate cg using config_list */
                ch = ((struct sr_channel *)cg->channels->data)->index;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_FRAMES:
                devc->frame_limit = g_variant_get_uint64(data);
                break;
        case SR_CONF_SAMPLERATE:
-               i = find_in_array(data, G_VARIANT_TYPE_UINT64,
-                                 samplerates, ARRAY_SIZE(samplerates));
-               if (i == -1)
-                       ret = SR_ERR_ARG;
-               else
-                       devc->rate = i;
+               if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(samplerates))) < 0)
+                       return SR_ERR_ARG;
+               devc->rate = idx;
                break;
        case SR_CONF_TRIGGER_SOURCE:
-               i = find_in_array(data, G_VARIANT_TYPE_STRING,
-                                 trigger_sources, ARRAY_SIZE(trigger_sources));
-               if (i == -1)
-                       ret = SR_ERR_ARG;
-               else
-                       devc->cctl[0] = (devc->cctl[0] & 0x3F)
-                                     | trigger_sources_map[i];
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 0)
+                       return SR_ERR_ARG;
+               devc->cctl[0] = (devc->cctl[0] & 0x3F) | trigger_sources_map[idx];
                break;
        case SR_CONF_TRIGGER_SLOPE:
-               i = find_in_array(data, G_VARIANT_TYPE_STRING,
-                                 trigger_slopes, ARRAY_SIZE(trigger_slopes));
-               if (i == -1)
-                       ret = SR_ERR_ARG;
-               else
-                       devc->edge = i;
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
+                       return SR_ERR_ARG;
+               devc->edge = idx;
                break;
        case SR_CONF_BUFFERSIZE:
-               i = find_in_array(data, G_VARIANT_TYPE_UINT64,
-                                 buffersizes, ARRAY_SIZE(buffersizes));
-               if (i == -1)
-                       ret = SR_ERR_ARG;
-               else
-                       devc->last_step = i;
+               if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(buffersizes))) < 0)
+                       return SR_ERR_ARG;
+               devc->last_step = idx;
                break;
        case SR_CONF_VDIV:
-               if (ch == -1) {
-                       ret = SR_ERR_CHANNEL_GROUP;
-               } else if (!g_variant_is_of_type(data, G_VARIANT_TYPE("(tt)"))) {
-                       ret = SR_ERR_ARG;
-               } else {
-                       g_variant_get(data, "(tt)", &u, &v);
-                       for (i = 0; i < (int)ARRAY_SIZE(vdivs); i++)
-                               if (vdivs[i][0] == u && vdivs[i][1] == v)
-                                       break;
-                       if (i == ARRAY_SIZE(vdivs))
-                               ret = SR_ERR_ARG;
-                       else
-                               devc->cctl[ch] = (devc->cctl[ch] & 0xCC)
-                                              | vdivs_map[i];
-               }
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if (!g_variant_is_of_type(data, G_VARIANT_TYPE("(tt)")))
+                       return SR_ERR_ARG;
+               if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+                       return SR_ERR_ARG;
+               devc->cctl[ch] = (devc->cctl[ch] & 0xCC) | vdivs_map[idx];
                break;
        case SR_CONF_COUPLING:
-               if (ch == -1) {
-                       ret = SR_ERR_CHANNEL_GROUP;
-               } else {
-                       i = find_in_array(data, G_VARIANT_TYPE_STRING,
-                                         coupling, ARRAY_SIZE(coupling));
-                       if (i == -1)
-                               ret = SR_ERR_ARG;
-                       else
-                               devc->cctl[ch] = (devc->cctl[ch] & 0xF3)
-                                              | coupling_map[i];
-               }
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(coupling))) < 0)
+                       return SR_ERR_ARG;
+               devc->cctl[ch] = (devc->cctl[ch] & 0xF3) | coupling_map[idx];
                break;
        case SR_CONF_PROBE_FACTOR:
-               if (ch == -1) {
-                       ret = SR_ERR_CHANNEL_GROUP;
-               } else {
-                       u = g_variant_get_uint64(data);
-                       if (!u)
-                               ret = SR_ERR_ARG;
-                       else
-                               devc->probe[ch] = u;
-               }
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               u = g_variant_get_uint64(data);
+               if (!u)
+                       return SR_ERR_ARG;
+               devc->probe[ch] = u;
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_channel_set(const struct sr_dev_inst *sdi,
-                             struct sr_channel *ch,
-                             unsigned int changes)
+       struct sr_channel *ch, unsigned int changes)
 {
        struct dev_context *devc = sdi->priv;
        uint8_t v;
 
-       if (changes & SR_CHANNEL_SET_ENABLED) {
-               if (ch->enabled) {
-                       v = devc->channel | (1 << ch->index);
-                       if (v & (v - 1))
-                               return SR_ERR;
-                       devc->channel = v;
-                       devc->enabled_channel->data = ch;
-               } else {
-                       devc->channel &= ~(1 << ch->index);
-               }
+       if (!(changes & SR_CHANNEL_SET_ENABLED))
+               return SR_OK;
+
+       if (ch->enabled) {
+               v = devc->channel | (1 << ch->index);
+               if (v & (v - 1))
+                       return SR_ERR;
+               devc->channel = v;
+               devc->enabled_channel->data = ch;
+       } else {
+               devc->channel &= ~(1 << ch->index);
        }
+
        return SR_OK;
 }
 
@@ -549,9 +435,6 @@ static int config_commit(const struct sr_dev_inst *sdi)
        uint8_t state = hung_chang_dso_2100_read_mbox(sdi->conn, 0.02);
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        switch (state) {
        case 0x03:
        case 0x14:
@@ -560,10 +443,12 @@ static int config_commit(const struct sr_dev_inst *sdi)
                break;
        case 0x00:
                state = 0x01;
+               /* Fallthrough */
        default:
                ret = hung_chang_dso_2100_move_to(sdi, 1);
                if (ret != SR_OK)
                        return ret;
+               /* Fallthrough */
        case 0x01:
                hung_chang_dso_2100_write_mbox(sdi->conn, 4);
        }
@@ -573,16 +458,13 @@ static int config_commit(const struct sr_dev_inst *sdi)
        return hung_chang_dso_2100_move_to(sdi, state);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       GVariantBuilder gvb;
-       GVariant *gvar, *rational[2];
        GSList *l;
-       int i;
 
        switch (key) {
-               case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_SCAN_OPTIONS:
        case SR_CONF_DEVICE_OPTIONS:
                break;
        case SR_CONF_SAMPLERATE:
@@ -608,49 +490,29 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
 
        switch (key) {
        case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, NO_OPTS, NO_OPTS);
        case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi)
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               else if (!cg)
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               else
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       cgopts, ARRAY_SIZE(cgopts), sizeof(uint32_t));
+               if (!cg)
+                       return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
+               *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
                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);
+               *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
                break;
        case SR_CONF_TRIGGER_SOURCE:
-               *data = g_variant_new_strv(trigger_sources, ARRAY_SIZE(trigger_sources));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_sources));
                break;
        case SR_CONF_TRIGGER_SLOPE:
-               *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_slopes));
                break;
        case SR_CONF_BUFFERSIZE:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
-                               buffersizes, ARRAY_SIZE(buffersizes), sizeof(uint64_t));
+               *data = std_gvar_array_u64(ARRAY_AND_SIZE(buffersizes));
                break;
        case SR_CONF_VDIV:
-               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-               for (i = 0; i < (int)ARRAY_SIZE(vdivs); i++) {
-                       rational[0] = g_variant_new_uint64(vdivs[i][0]);
-                       rational[1] = g_variant_new_uint64(vdivs[i][1]);
-                       gvar = g_variant_new_tuple(rational, 2);
-                       g_variant_builder_add_value(&gvb, gvar);
-               }
-               *data = g_variant_builder_end(&gvb);
+               *data = std_gvar_tuple_array(ARRAY_AND_SIZE(vdivs));
                break;
        case SR_CONF_COUPLING:
-               *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(coupling));
                break;
        }
 
@@ -662,9 +524,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc = sdi->priv;
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        if (devc->channel) {
                static const float res_array[] = {0.5, 1, 2, 5};
                static const uint8_t relays[] = {100, 10, 10, 1};
@@ -690,11 +549,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-SR_PRIV int hung_chang_dso_2100_dev_acquisition_stop(const struct sr_dev_inst *sdi)
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        std_session_send_df_end(sdi);
        sr_session_source_remove(sdi->session, -1);
        hung_chang_dso_2100_move_to(sdi, 1);
@@ -702,11 +558,6 @@ SR_PRIV int hung_chang_dso_2100_dev_acquisition_stop(const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int dev_acquisition_stop(struct sr_dev_inst *sdi)
-{
-       return hung_chang_dso_2100_dev_acquisition_stop(sdi);
-}
-
 static struct sr_dev_driver hung_chang_dso_2100_driver_info = {
        .name = "hung-chang-dso-2100",
        .longname = "Hung-Chang DSO-2100",
index 160e5a0c63f1de492d9b45eb3b86f5c1837f51d9..69efb14d3c332b125334490b4ebc901ed25794be 100644 (file)
@@ -420,7 +420,7 @@ static int read_subframe(const struct sr_dev_inst *sdi, uint8_t *buf)
 SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data)
 {
        struct sr_datafeed_packet packet = { .type = SR_DF_FRAME_BEGIN };
-       const struct sr_dev_inst *sdi;
+       struct sr_dev_inst *sdi;
        struct dev_context *devc;
        uint8_t state, buf[1000];
 
@@ -464,7 +464,7 @@ SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data)
        sr_session_send(sdi, &packet);
 
        if (++devc->frame >= devc->frame_limit)
-               hung_chang_dso_2100_dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        else
                hung_chang_dso_2100_move_to(sdi, 0x21);
 
index f210b650a3d6e1bea2e9f6327016e2abab3e9962..11de7b7254337946b68ea33edb1c2a5116971f80 100644 (file)
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "hung-chang-dso-2100"
+
 #define MAX_RETRIES 4
 #define NUM_CHANNELS 2
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Acquisition settings */
        GSList *enabled_channel;
        uint8_t channel;
        uint8_t rate;
@@ -42,7 +41,6 @@ struct dev_context {
        uint8_t offset[2];
        uint8_t gain[2];
 
-       /* Operational state */
        uint64_t frame_limit;
        uint64_t frame;
        uint64_t probe[2];
@@ -51,7 +49,6 @@ struct dev_context {
        uint8_t retries;
        gboolean adc2;
 
-       /* Temporary state across callbacks */
        float *samples;
        float factor;
        gboolean state_known;
@@ -63,6 +60,5 @@ SR_PRIV void hung_chang_dso_2100_write_mbox(struct parport *port, uint8_t val);
 SR_PRIV uint8_t hung_chang_dso_2100_read_mbox(struct parport *port, float timeout);
 SR_PRIV int hung_chang_dso_2100_move_to(const struct sr_dev_inst *sdi, uint8_t target);
 SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data);
-SR_PRIV int hung_chang_dso_2100_dev_acquisition_stop(const struct sr_dev_inst *sdi);
 
 #endif
index c82422a553ed38dc1a6bf7c810130e6812727643..b911f03d182b224cd9a48c5db8edb50a99e349ff 100644 (file)
 #include <config.h>
 #include "protocol.h"
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
@@ -101,8 +104,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
                sdi = g_malloc0(sizeof(struct sr_dev_inst));
                sdi->status = SR_ST_INACTIVE;
-               sdi->vendor = g_strdup(VENDOR_NAME);
-               sdi->model = g_strdup(MODEL_NAME);
+               sdi->vendor = g_strdup("IKALOGIC");
+               sdi->model = g_strdup("Scanalogic-2");
                sdi->version = g_strdup_printf("%u.%u", dev_info.fw_ver_major, dev_info.fw_ver_minor);
                sdi->serial_num = g_strdup_printf("%d", dev_info.serial);
                sdi->priv = devc;
@@ -152,22 +155,15 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, devices);
 }
 
-static void clear_dev_context(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       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(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, &clear_dev_context);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -185,10 +181,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        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) {
@@ -232,8 +224,6 @@ static int dev_open(struct sr_dev_inst *sdi)
                return SR_ERR;
        }
 
-       sdi->status = SR_ST_ACTIVE;
-
        return SR_OK;
 }
 
@@ -244,26 +234,23 @@ static int dev_close(struct sr_dev_inst *sdi)
        usb = sdi->conn;
 
        if (!usb->devhdl)
-               return SR_OK;
+               return SR_ERR_BUG;
 
        libusb_release_interface(usb->devhdl, USB_INTERFACE);
        libusb_close(usb->devhdl);
 
        usb->devhdl = NULL;
-       sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       int ret;
 
        (void)cg;
 
-       ret = SR_OK;
        devc = sdi->priv;
 
        switch (key) {
@@ -277,79 +264,56 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       uint64_t samplerate, limit_samples, capture_ratio;
-       int ret;
+       struct dev_context *devc;
+       uint64_t samplerate, limit_samples;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
+       devc = sdi->priv;
 
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
                limit_samples = g_variant_get_uint64(data);
-               ret = sl2_set_limit_samples(sdi, limit_samples);
-               break;
+               return sl2_set_limit_samples(sdi, limit_samples);
        case SR_CONF_SAMPLERATE:
                samplerate = g_variant_get_uint64(data);
-               ret = sl2_set_samplerate(sdi, samplerate);
-               break;
+               return sl2_set_samplerate(sdi, samplerate);
        case SR_CONF_CAPTURE_RATIO:
-               capture_ratio = g_variant_get_uint64(data);
-               ret = sl2_set_capture_ratio(sdi, capture_ratio);
+               devc->capture_ratio = g_variant_get_uint64(data);
                break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates(ARRAY_AND_SIZE(sl2_samplerates));
                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));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                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);
+               *data = std_gvar_tuple_u64(0, MAX_SAMPLES);
                break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -361,9 +325,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        unsigned int i, j;
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
        drvc = di->context;
 
@@ -432,8 +393,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        usb_source_add(sdi->session, drvc->sr_ctx, 100,
                        ikalogic_scanalogic2_receive_data, (void *)sdi);
 
-       sr_dbg("Acquisition started successfully.");
-
        std_session_send_df_header(sdi);
 
        devc->next_state = STATE_SAMPLE;
@@ -443,11 +402,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       sr_dbg("Stopping acquisition.");
-
        sdi->status = SR_ST_STOPPING;
 
        return SR_OK;
index 45d60db507855837b000e36bf64acc4988e88ea5..b1a85b03a621110d64a16c4397d2f01f1524be7f 100644 (file)
@@ -41,7 +41,7 @@ static void abort_acquisition(struct sr_dev_inst *sdi)
 
        std_session_send_df_end(sdi);
 
-       sdi->driver->dev_close(sdi);
+       sr_dev_close(sdi);
 }
 
 static void buffer_sample_data(const struct sr_dev_inst *sdi)
@@ -531,25 +531,6 @@ SR_PRIV int sl2_convert_trigger(const struct sr_dev_inst *sdi)
        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)
 {
@@ -640,10 +621,6 @@ SR_PRIV int sl2_get_device_info(struct sr_dev_driver *di,
        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);
index d7a273574d78276d1504dbb4f616a62d16d42dfa..3a14b20b415b740600b7656ff873a96e71794d17 100644 (file)
@@ -29,9 +29,6 @@
 
 #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_MS                 (5 * 1000)
@@ -120,7 +117,6 @@ enum {
        STATE_WAIT_DEVICE_READY
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        /* Current selected samplerate. */
        uint64_t samplerate;
@@ -142,7 +138,7 @@ struct dev_context {
        uint8_t trigger_channel;
        uint8_t trigger_type;
 
-       unsigned int capture_ratio;
+       uint64_t capture_ratio;
 
        /* Time that the trigger will be delayed in milliseconds. */
        uint16_t after_trigger_delay;
@@ -222,8 +218,6 @@ SR_PRIV int sl2_set_samplerate(const struct sr_dev_inst *sdi,
 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);
index 4a9491b896ce2719f3d5f16f33c782958ac48d0a..f29e5c39ae327c24b37a4ade4047cecc4410bc98 100644 (file)
 
 #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 uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-/* Channels are numbered 1-9. */
 static const char *channel_names[] = {
        "1", "2", "3", "4", "5", "6", "7", "8", "9",
 };
@@ -43,23 +43,16 @@ static const char *channel_names[] = {
 /* Note: The IKALOGIC ScanaPLUS always samples at 100MHz. */
 static const uint64_t samplerates[1] = { SR_MHZ(100) };
 
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
-
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
-
        ftdi_free(devc->ftdic);
        g_free(devc->compressed_buf);
        g_free(devc->sample_buf);
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
@@ -71,7 +64,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
        (void)options;
 
-       /* Allocate memory for our private device context. */
        devc = g_malloc0(sizeof(struct dev_context));
 
        /* Allocate memory for the incoming compressed samples. */
@@ -86,13 +78,11 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                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) {
@@ -103,24 +93,22 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                goto err_free_ftdic;
        }
 
-       /* Register the device with libsigrok. */
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
-       sdi->vendor = g_strdup(USB_VENDOR_NAME);
-       sdi->model = g_strdup(USB_MODEL_NAME);
+       sdi->vendor = g_strdup("IKALOGIC");
+       sdi->model = g_strdup("ScanaPLUS");
        sdi->priv = devc;
 
        for (i = 0; i < ARRAY_SIZE(channel_names); i++)
                sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_names[i]);
 
-       /* Close device. We'll reopen it again when we need it. */
        scanaplus_close(devc);
 
        return std_scan_complete(di, g_slist_append(NULL, sdi));
 
        scanaplus_close(devc);
 err_free_ftdic:
-       ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
+       ftdi_free(devc->ftdic);
 err_free_sample_buf:
        g_free(devc->sample_buf);
 err_free_compressed_buf:
@@ -138,16 +126,13 @@ static int dev_open(struct sr_dev_inst *sdi)
 
        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) {
@@ -155,51 +140,40 @@ static int dev_open(struct sr_dev_inst *sdi)
                       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) {
@@ -209,37 +183,25 @@ static int dev_open(struct sr_dev_inst *sdi)
        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;
+       return scanaplus_close(devc);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        (void)sdi;
        (void)cg;
@@ -256,16 +218,13 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
@@ -277,13 +236,9 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                /* 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:
@@ -293,27 +248,14 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
                break;
        default:
                return SR_ERR_NA;
@@ -327,9 +269,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        int ret;
        struct dev_context *devc;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        if (!devc->ftdic)
@@ -358,7 +297,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       sr_dbg("Stopping acquisition.");
        sr_session_source_remove(sdi->session, -1);
        std_session_send_df_end(sdi);
 
index 39dcfa2cd46ec7f16c37f908d25942dbfb4d23e1..c0e253626a67370b72afa060b6aa025946911bdb 100644 (file)
@@ -319,7 +319,7 @@ SR_PRIV int scanaplus_receive_data(int fd, int revents, void *cb_data)
        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);
+               sr_dev_acquisition_stop(sdi);
                return FALSE;
        }
        if (bytes_read == 0) {
@@ -358,12 +358,12 @@ SR_PRIV int scanaplus_receive_data(int fd, int revents, void *cb_data)
        if (devc->limit_samples && (n >= devc->limit_samples)) {
                send_samples(sdi, devc->limit_samples - devc->samples_sent);
                sr_info("Requested number of samples reached.");
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        } else if (devc->limit_msec && (n >= max)) {
                send_samples(sdi, max - devc->samples_sent);
                sr_info("Requested time limit reached.");
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        } else {
                send_samples(sdi, devc->bytes_received / 2);
index f797832449cbbe9c5341c3eae744520fcd1c6f2f..fde2e4e2ef8a174265d64292f4a08e5b3b08759a 100644 (file)
 
 #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;
 
        uint8_t *compressed_buf;
diff --git a/src/hardware/ipdbg-la/api.c b/src/hardware/ipdbg-la/api.c
new file mode 100644 (file)
index 0000000..59f3f56
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Eva Kissling <eva.kissling@bluewin.ch>
+ *
+ * This program is free software: you can 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 <config.h>
+#include "protocol.h"
+
+static const uint32_t drvopts[] = {
+       SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+       SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const int32_t trigger_matches[] = {
+       SR_TRIGGER_ZERO,
+       SR_TRIGGER_ONE,
+       SR_TRIGGER_RISING,
+       SR_TRIGGER_FALLING,
+       SR_TRIGGER_EDGE,
+};
+
+SR_PRIV struct sr_dev_driver ipdbg_la_driver_info;
+
+static void ipdbg_la_split_addr_port(const char *conn, char **addr,
+       char **port)
+{
+       char **strs = g_strsplit(conn, "/", 3);
+
+       *addr = g_strdup(strs[1]);
+       *port = g_strdup(strs[2]);
+
+       g_strfreev(strs);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       struct drv_context *drvc;
+       GSList *devices;
+
+       devices = NULL;
+       drvc = di->context;
+       drvc->instances = NULL;
+       const char *conn;
+       struct sr_config *src;
+       GSList *l;
+
+       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;
+
+       struct ipdbg_la_tcp *tcp = ipdbg_la_tcp_new();
+
+       ipdbg_la_split_addr_port(conn, &tcp->address, &tcp->port);
+
+       if (!tcp->address)
+               return NULL;
+
+       if (ipdbg_la_tcp_open(tcp) != SR_OK)
+               return NULL;
+
+       ipdbg_la_send_reset(tcp);
+       ipdbg_la_send_reset(tcp);
+
+       if (ipdbg_la_request_id(tcp) != SR_OK)
+               return NULL;
+
+       struct sr_dev_inst *sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->status = SR_ST_INACTIVE;
+       sdi->vendor = g_strdup("ipdbg.org");
+       sdi->model = g_strdup("IPDBG LA");
+       sdi->version = g_strdup("v1.0");
+       sdi->driver = di;
+
+       struct dev_context *devc = ipdbg_la_dev_new();
+       sdi->priv = devc;
+
+       ipdbg_la_get_addrwidth_and_datawidth(tcp, devc);
+
+       sr_dbg("addr_width = %d, data_width = %d\n", devc->addr_width,
+               devc->data_width);
+       sr_dbg("limit samples = %" PRIu64 "\n", devc->limit_samples_max);
+
+       for (uint32_t i = 0; i < devc->data_width; i++) {
+               char *name = g_strdup_printf("CH%d", i);
+               sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
+               g_free(name);
+       }
+
+       sdi->inst_type = SR_INST_USER;
+       sdi->conn = tcp;
+
+       ipdbg_la_tcp_close(tcp);
+
+       devices = g_slist_append(devices, sdi);
+
+       return std_scan_complete(di, devices);
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+       struct drv_context *drvc = di->context;
+       struct sr_dev_inst *sdi;
+       GSList *l;
+
+       if (drvc) {
+               for (l = drvc->instances; l; l = l->next) {
+                       sdi = l->data;
+                       struct ipdbg_la_tcp *tcp = sdi->conn;
+                       if (tcp) {
+                               ipdbg_la_tcp_close(tcp);
+                               ipdbg_la_tcp_free(tcp);
+                               g_free(tcp);
+                       }
+                       sdi->conn = NULL;
+               }
+       }
+
+       return std_dev_clear(di);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       struct ipdbg_la_tcp *tcp = sdi->conn;
+
+       if (!tcp)
+               return SR_ERR;
+
+       if (ipdbg_la_tcp_open(tcp) != SR_OK)
+               return SR_ERR;
+
+       return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       /* Should be called before a new call to scan(). */
+       struct ipdbg_la_tcp *tcp = sdi->conn;
+
+       if (tcp)
+               ipdbg_la_tcp_close(tcp);
+
+       sdi->conn = NULL;
+
+       return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc = sdi->priv;
+
+       (void)cg;
+
+       switch (key) {
+       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;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc = sdi->priv;
+
+       (void)cg;
+
+       switch (key) {
+       case SR_CONF_CAPTURE_RATIO:
+               devc->capture_ratio = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               devc->limit_samples = g_variant_get_uint64(data);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_TRIGGER_MATCH:
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct ipdbg_la_tcp *tcp = sdi->conn;
+       struct dev_context *devc = sdi->priv;
+
+       ipdbg_la_convert_trigger(sdi);
+       ipdbg_la_send_trigger(devc, tcp);
+       ipdbg_la_send_delay(devc, tcp);
+
+       /* If the device stops sending for longer than it takes to send a byte,
+        * that means it's finished. But wait at least 100 ms to be safe.
+        */
+       sr_session_source_add(sdi->session, tcp->socket, G_IO_IN, 100,
+               ipdbg_la_receive_data, (struct sr_dev_inst *)sdi);
+
+       ipdbg_la_send_start(tcp);
+
+       return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       struct ipdbg_la_tcp *tcp = sdi->conn;
+       struct dev_context *devc = sdi->priv;
+
+       uint8_t byte;
+
+       if (devc->num_transfers > 0) {
+               while (devc->num_transfers <
+                       (devc->limit_samples_max * devc->data_width_bytes)) {
+                       ipdbg_la_tcp_receive(tcp, &byte);
+                       devc->num_transfers++;
+               }
+       }
+
+       ipdbg_la_send_reset(tcp);
+       ipdbg_la_abort_acquisition(sdi);
+
+       return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver ipdbg_la_driver_info = {
+       .name = "ipdbg-la",
+       .longname = "IPDBG LA",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .context = NULL,
+};
+
+SR_REGISTER_DEV_DRIVER(ipdbg_la_driver_info);
diff --git a/src/hardware/ipdbg-la/protocol.c b/src/hardware/ipdbg-la/protocol.c
new file mode 100644 (file)
index 0000000..4c8f33b
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Eva Kissling <eva.kissling@bluewin.ch>
+ *
+ * This program is free software: you can 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 <config.h>
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "protocol.h"
+
+#define BUFFER_SIZE 4
+
+/* Top-level command opcodes */
+#define CMD_SET_TRIGGER            0x00
+#define CMD_CFG_TRIGGER            0xF0
+#define CMD_CFG_LA                 0x0F
+#define CMD_START                  0xFE
+#define CMD_RESET                  0xEE
+
+#define CMD_GET_BUS_WIDTHS         0xAA
+#define CMD_GET_LA_ID              0xBB
+#define CMD_ESCAPE                 0x55
+
+/* Trigger subfunction command opcodes */
+#define CMD_TRIG_MASKS             0xF1
+#define CMD_TRIG_MASK              0xF3
+#define CMD_TRIG_VALUE             0xF7
+
+#define CMD_TRIG_MASKS_LAST        0xF9
+#define CMD_TRIG_MASK_LAST         0xFB
+#define CMD_TRIG_VALUE_LAST        0xFF
+
+#define CMD_TRIG_SELECT_EDGE_MASK  0xF5
+#define CMD_TRIG_SET_EDGE_MASK     0xF6
+
+/* LA subfunction command opcodes */
+#define CMD_LA_DELAY               0x1F
+
+static gboolean data_available(struct ipdbg_la_tcp *tcp)
+{
+#ifdef _WIN32
+       u_long bytes_available;
+       ioctlsocket(tcp->socket, FIONREAD, &bytes_available);
+       return (bytes_available > 0);
+#else
+       int status;
+
+       if (ioctl(tcp->socket, FIONREAD, &status) < 0) {        // TIOCMGET
+               sr_err("FIONREAD failed: %s\n", g_strerror(errno));
+               return FALSE;
+       }
+
+       return (status < 1) ? FALSE : TRUE;
+#endif
+}
+
+SR_PRIV struct ipdbg_la_tcp *ipdbg_la_tcp_new(void)
+{
+       struct ipdbg_la_tcp *tcp;
+
+       tcp = g_malloc0(sizeof(struct ipdbg_la_tcp));
+       tcp->address = NULL;
+       tcp->port = NULL;
+       tcp->socket = -1;
+
+       return tcp;
+}
+
+SR_PRIV void ipdbg_la_tcp_free(struct ipdbg_la_tcp *tcp)
+{
+       g_free(tcp->address);
+       g_free(tcp->port);
+}
+
+SR_PRIV int ipdbg_la_tcp_open(struct ipdbg_la_tcp *tcp)
+{
+       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:%s: %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,
+                       g_strerror(errno));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_tcp_close(struct ipdbg_la_tcp *tcp)
+{
+       int ret = SR_OK;
+
+       if (close(tcp->socket) < 0)
+               ret = SR_ERR;
+
+       tcp->socket = -1;
+
+       return ret;
+}
+
+static int tcp_send(struct ipdbg_la_tcp *tcp, const uint8_t *buf, size_t len)
+{
+       int out;
+       out = send(tcp->socket, (const char *)buf, len, 0);
+
+       if (out < 0) {
+               sr_err("Send error: %s", g_strerror(errno));
+               return SR_ERR;
+       }
+
+       if (out < (int)len)
+               sr_dbg("Only sent %d/%d bytes of data.", out, (int)len);
+
+       return SR_OK;
+}
+
+static int tcp_receive_blocking(struct ipdbg_la_tcp *tcp,
+       uint8_t *buf, int bufsize)
+{
+       int received = 0;
+       int error_count = 0;
+
+       /* Timeout after 500ms of not receiving data */
+       while ((received < bufsize) && (error_count < 500)) {
+               if (ipdbg_la_tcp_receive(tcp, buf) > 0) {
+                       buf++;
+                       received++;
+               } else {
+                       error_count++;
+                       g_usleep(1000);  /* Sleep for 1ms */
+               }
+       }
+
+       return received;
+}
+
+SR_PRIV int ipdbg_la_tcp_receive(struct ipdbg_la_tcp *tcp,
+       uint8_t *buf)
+{
+       int received = 0;
+
+       if (data_available(tcp)) {
+               while (received < 1) {
+                       int len = recv(tcp->socket, (char *)buf, 1, 0);
+
+                       if (len < 0) {
+                               sr_err("Receive error: %s", g_strerror(errno));
+                               return SR_ERR;
+                       } else
+                               received += len;
+               }
+
+               return received;
+       } else
+               return -1;
+}
+
+SR_PRIV int ipdbg_la_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;
+
+       devc = sdi->priv;
+
+       devc->num_stages = 0;
+       devc->num_transfers = 0;
+       devc->raw_sample_buf = NULL;
+
+       for (uint64_t i = 0; i < devc->data_width_bytes; i++) {
+               devc->trigger_mask[i] = 0;
+               devc->trigger_value[i] = 0;
+               devc->trigger_mask_last[i] = 0;
+               devc->trigger_value_last[i] = 0;
+               devc->trigger_edge_mask[i] = 0;
+       }
+
+       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;
+                       int byte_idx = match->channel->index / 8;
+                       uint8_t match_bit = 1 << (match->channel->index % 8);
+
+                       if (!match->channel->enabled)
+                               /* Ignore disabled channels with a trigger. */
+                               continue;
+
+                       if (match->match == SR_TRIGGER_ONE) {
+                               devc->trigger_value[byte_idx] |= match_bit;
+                               devc->trigger_mask[byte_idx] |= match_bit;
+                               devc->trigger_mask_last[byte_idx] &= ~match_bit;
+                               devc->trigger_edge_mask[byte_idx] &= ~match_bit;
+                       } else if (match->match == SR_TRIGGER_ZERO) {
+                               devc->trigger_value[byte_idx] &= ~match_bit;
+                               devc->trigger_mask[byte_idx] |= match_bit;
+                               devc->trigger_mask_last[byte_idx] &= ~match_bit;
+                               devc->trigger_edge_mask[byte_idx] &= ~match_bit;
+                       } else if (match->match == SR_TRIGGER_RISING) {
+                               devc->trigger_value[byte_idx] |= match_bit;
+                               devc->trigger_value_last[byte_idx] &=
+                                   ~match_bit;
+                               devc->trigger_mask[byte_idx] |= match_bit;
+                               devc->trigger_mask_last[byte_idx] |= match_bit;
+                               devc->trigger_edge_mask[byte_idx] &= ~match_bit;
+                       } else if (match->match == SR_TRIGGER_FALLING) {
+                               devc->trigger_value[byte_idx] &= ~match_bit;
+                               devc->trigger_value_last[byte_idx] |= match_bit;
+                               devc->trigger_mask[byte_idx] |= match_bit;
+                               devc->trigger_mask_last[byte_idx] |= match_bit;
+                               devc->trigger_edge_mask[byte_idx] &= ~match_bit;
+                       } else if (match->match == SR_TRIGGER_EDGE) {
+                               devc->trigger_mask[byte_idx] &= ~match_bit;
+                               devc->trigger_mask_last[byte_idx] &= ~match_bit;
+                               devc->trigger_edge_mask[byte_idx] |= match_bit;
+                       }
+               }
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_receive_data(int fd, int revents, void *cb_data)
+{
+       const struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+
+       (void)fd;
+       (void)revents;
+
+       sdi = cb_data;
+       if (!sdi)
+               return FALSE;
+
+       if (!(devc = sdi->priv))
+               return FALSE;
+
+       struct ipdbg_la_tcp *tcp = sdi->conn;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_logic logic;
+
+       if (!devc->raw_sample_buf) {
+               devc->raw_sample_buf =
+                       g_try_malloc(devc->limit_samples * devc->data_width_bytes);
+               if (!devc->raw_sample_buf) {
+                       sr_err("Sample buffer malloc failed.");
+                       return FALSE;
+               }
+       }
+
+       if (devc->num_transfers <
+               (devc->limit_samples_max * devc->data_width_bytes)) {
+               uint8_t byte;
+
+               if (ipdbg_la_tcp_receive(tcp, &byte) == 1) {
+                       if (devc->num_transfers <
+                               (devc->limit_samples * devc->data_width_bytes))
+                               devc->raw_sample_buf[devc->num_transfers] = byte;
+
+                       devc->num_transfers++;
+               }
+       } else {
+               if (devc->delay_value > 0) {
+                       /* There are pre-trigger samples, send those first. */
+                       packet.type = SR_DF_LOGIC;
+                       packet.payload = &logic;
+                       logic.length = devc->delay_value * devc->data_width_bytes;
+                       logic.unitsize = devc->data_width_bytes;
+                       logic.data = devc->raw_sample_buf;
+                       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->limit_samples - devc->delay_value) *
+                       devc->data_width_bytes;
+               logic.unitsize = devc->data_width_bytes;
+               logic.data = devc->raw_sample_buf +
+                       (devc->delay_value * devc->data_width_bytes);
+               sr_session_send(cb_data, &packet);
+
+               g_free(devc->raw_sample_buf);
+               devc->raw_sample_buf = NULL;
+
+               ipdbg_la_abort_acquisition(sdi);
+       }
+
+       return TRUE;
+}
+
+static int send_escaping(struct ipdbg_la_tcp *tcp, uint8_t *data_to_send,
+       uint32_t length)
+{
+       uint8_t escape = CMD_ESCAPE;
+
+       while (length--) {
+               uint8_t payload = *data_to_send++;
+
+               if (payload == CMD_RESET)
+                       if (tcp_send(tcp, &escape, 1) != SR_OK)
+                               sr_warn("Couldn't send escape");
+
+               if (payload == CMD_ESCAPE)
+                       if (tcp_send(tcp, &escape, 1) != SR_OK)
+                               sr_warn("Couldn't send escape");
+
+               if (tcp_send(tcp, &payload, 1) != SR_OK)
+                       sr_warn("Couldn't send data");
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_send_delay(struct dev_context *devc,
+       struct ipdbg_la_tcp *tcp)
+{
+       devc->delay_value = (devc->limit_samples / 100.0) * devc->capture_ratio;
+
+       uint8_t buf;
+       buf = CMD_CFG_LA;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_LA_DELAY;
+       tcp_send(tcp, &buf, 1);
+
+       uint8_t delay_buf[4] = { devc->delay_value & 0x000000ff,
+               (devc->delay_value >> 8) & 0x000000ff,
+               (devc->delay_value >> 16) & 0x000000ff,
+               (devc->delay_value >> 24) & 0x000000ff
+       };
+
+       for (uint64_t i = 0; i < devc->addr_width_bytes; i++)
+               send_escaping(tcp, &(delay_buf[devc->addr_width_bytes - 1 - i]), 1);
+
+       return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_send_trigger(struct dev_context *devc,
+       struct ipdbg_la_tcp *tcp)
+{
+       uint8_t buf;
+
+       /* Mask */
+       buf = CMD_CFG_TRIGGER;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_MASKS;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_MASK;
+       tcp_send(tcp, &buf, 1);
+
+       for (size_t i = 0; i < devc->data_width_bytes; i++)
+               send_escaping(tcp,
+                       devc->trigger_mask + devc->data_width_bytes - 1 - i, 1);
+
+       /* Value */
+       buf = CMD_CFG_TRIGGER;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_MASKS;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_VALUE;
+       tcp_send(tcp, &buf, 1);
+
+       for (size_t i = 0; i < devc->data_width_bytes; i++)
+               send_escaping(tcp,
+                       devc->trigger_value + devc->data_width_bytes - 1 - i, 1);
+
+       /* Mask_last */
+       buf = CMD_CFG_TRIGGER;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_MASKS_LAST;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_MASK_LAST;
+       tcp_send(tcp, &buf, 1);
+
+       for (size_t i = 0; i < devc->data_width_bytes; i++)
+               send_escaping(tcp,
+                       devc->trigger_mask_last + devc->data_width_bytes - 1 - i, 1);
+
+       /* Value_last */
+       buf = CMD_CFG_TRIGGER;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_MASKS_LAST;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_VALUE_LAST;
+       tcp_send(tcp, &buf, 1);
+
+       for (size_t i = 0; i < devc->data_width_bytes; i++)
+               send_escaping(tcp,
+                       devc->trigger_value_last + devc->data_width_bytes - 1 - i, 1);
+
+       /* Edge_mask */
+       buf = CMD_CFG_TRIGGER;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_SELECT_EDGE_MASK;
+       tcp_send(tcp, &buf, 1);
+       buf = CMD_TRIG_SET_EDGE_MASK;
+       tcp_send(tcp, &buf, 1);
+
+       for (size_t i = 0; i < devc->data_width_bytes; i++)
+               send_escaping(tcp,
+                       devc->trigger_edge_mask + devc->data_width_bytes - 1 - i, 1);
+
+       return SR_OK;
+}
+
+SR_PRIV void ipdbg_la_get_addrwidth_and_datawidth(
+       struct ipdbg_la_tcp *tcp, struct dev_context *devc)
+{
+       uint8_t buf[8];
+       uint8_t read_cmd = CMD_GET_BUS_WIDTHS;
+
+       if (tcp_send(tcp, &read_cmd, 1) != SR_OK)
+               sr_warn("Can't send read command");
+
+       if (tcp_receive_blocking(tcp, buf, 8) != 8)
+               sr_warn("Can't get address and data width from device");
+
+       devc->data_width = buf[0] & 0x000000FF;
+       devc->data_width |= (buf[1] << 8) & 0x0000FF00;
+       devc->data_width |= (buf[2] << 16) & 0x00FF0000;
+       devc->data_width |= (buf[3] << 24) & 0xFF000000;
+
+       devc->addr_width = buf[4] & 0x000000FF;
+       devc->addr_width |= (buf[5] << 8) & 0x0000FF00;
+       devc->addr_width |= (buf[6] << 16) & 0x00FF0000;
+       devc->addr_width |= (buf[7] << 24) & 0xFF000000;
+
+       const uint8_t host_word_size = 8;
+
+       devc->data_width_bytes =
+               (devc->data_width + host_word_size - 1) / host_word_size;
+       devc->addr_width_bytes =
+               (devc->addr_width + host_word_size - 1) / host_word_size;
+
+       devc->limit_samples_max = (0x01 << devc->addr_width);
+       devc->limit_samples = devc->limit_samples_max;
+
+       devc->trigger_mask = g_malloc0(devc->data_width_bytes);
+       devc->trigger_value = g_malloc0(devc->data_width_bytes);
+       devc->trigger_mask_last = g_malloc0(devc->data_width_bytes);
+       devc->trigger_value_last = g_malloc0(devc->data_width_bytes);
+       devc->trigger_edge_mask = g_malloc0(devc->data_width_bytes);
+}
+
+SR_PRIV struct dev_context *ipdbg_la_dev_new(void)
+{
+       struct dev_context *devc;
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       devc->capture_ratio = 50;
+
+       return devc;
+}
+
+SR_PRIV int ipdbg_la_send_reset(struct ipdbg_la_tcp *tcp)
+{
+       uint8_t buf = CMD_RESET;
+       if (tcp_send(tcp, &buf, 1) != SR_OK)
+               sr_warn("Couldn't send reset");
+
+       return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_request_id(struct ipdbg_la_tcp *tcp)
+{
+       uint8_t buf = CMD_GET_LA_ID;
+       if (tcp_send(tcp, &buf, 1) != SR_OK)
+               sr_warn("Couldn't send ID request");
+
+       char id[4];
+       if (tcp_receive_blocking(tcp, (uint8_t *)id, 4) != 4) {
+               sr_err("Couldn't read device ID");
+               return SR_ERR;
+       }
+
+       if (strncmp(id, "IDBG", 4)) {
+               sr_err("Invalid device ID: expected 'IDBG', got '%c%c%c%c'.",
+                       id[0], id[1], id[2], id[3]);
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV void ipdbg_la_abort_acquisition(const struct sr_dev_inst *sdi)
+{
+       struct ipdbg_la_tcp *tcp = sdi->conn;
+
+       sr_session_source_remove(sdi->session, tcp->socket);
+
+       std_session_send_df_end(sdi);
+}
+
+SR_PRIV int ipdbg_la_send_start(struct ipdbg_la_tcp *tcp)
+{
+       uint8_t buf = CMD_START;
+
+       if (tcp_send(tcp, &buf, 1) != SR_OK)
+               sr_warn("Couldn't send start");
+
+       return SR_OK;
+}
diff --git a/src/hardware/ipdbg-la/protocol.h b/src/hardware/ipdbg-la/protocol.h
new file mode 100644 (file)
index 0000000..875a8cf
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Eva Kissling <eva.kissling@bluewin.ch>
+ *
+ * This program is free software: you can 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_IPDBG_LA_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_IPDBG_LA_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ipdbg-la"
+
+struct ipdbg_la_tcp {
+       char *address;
+       char *port;
+       int socket;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+       uint32_t data_width;
+       uint32_t data_width_bytes;
+       uint32_t addr_width;
+       uint32_t addr_width_bytes;
+
+       uint64_t limit_samples;
+       uint64_t limit_samples_max;
+       uint8_t capture_ratio;
+       uint8_t *trigger_mask;
+       uint8_t *trigger_value;
+       uint8_t *trigger_mask_last;
+       uint8_t *trigger_value_last;
+       uint8_t *trigger_edge_mask;
+       uint64_t delay_value;
+       int num_stages;
+       uint64_t num_transfers;
+       uint8_t *raw_sample_buf;
+};
+
+SR_PRIV struct ipdbg_la_tcp *ipdbg_la_tcp_new(void);
+SR_PRIV void ipdbg_la_tcp_free(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_tcp_open(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_tcp_close(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_tcp_receive(struct ipdbg_la_tcp *tcp, uint8_t *buf);
+
+SR_PRIV int ipdbg_la_convert_trigger(const struct sr_dev_inst *sdi);
+
+SR_PRIV struct dev_context *ipdbg_la_dev_new(void);
+SR_PRIV void ipdbg_la_get_addrwidth_and_datawidth(
+       struct ipdbg_la_tcp *tcp, struct dev_context *devc);
+SR_PRIV int ipdbg_la_send_reset(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_request_id(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_send_start(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_send_trigger(struct dev_context *devc,
+       struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_send_delay(struct dev_context *devc,
+       struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV void ipdbg_la_abort_acquisition(const struct sr_dev_inst *sdi);
+
+#endif
index 30bb1b2a578bb05996817dae2655cedcaf61253b..2836840446d0842dc433af475da8849fcc1503f6 100644 (file)
 #include "protocol.h"
 
 #define USB_CONN "1041.8101"
-#define VENDOR "Kecheng"
 #define USB_INTERFACE 0
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_SOUNDLEVELMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_SAMPLE_INTERVAL | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
@@ -47,18 +49,15 @@ SR_PRIV const uint64_t kecheng_kc_330b_sample_intervals[][2] = {
 };
 
 static const char *weight_freq[] = {
-       "A",
-       "C",
+       "A", "C",
 };
 
 static const char *weight_time[] = {
-       "F",
-       "S",
+       "F", "S",
 };
 
 static const char *data_sources[] = {
-       "Live",
-       "Memory",
+       "Live", "Memory",
 };
 
 static int scan_kecheng(struct sr_dev_driver *di,
@@ -122,7 +121,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                                continue;
                        sdi = g_malloc0(sizeof(struct sr_dev_inst));
                        sdi->status = SR_ST_INACTIVE;
-                       sdi->vendor = g_strdup(VENDOR);
+                       sdi->vendor = g_strdup("Kecheng");
                        sdi->model = model; /* Already g_strndup()'d. */
                        sdi->inst_type = SR_INST_USB;
                        sdi->conn = l->data;
@@ -171,9 +170,8 @@ static int dev_open(struct sr_dev_inst *sdi)
                sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
                return SR_ERR;
        }
-       sdi->status = SR_ST_ACTIVE;
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
@@ -184,8 +182,7 @@ static int dev_close(struct sr_dev_inst *sdi)
        usb = sdi->conn;
 
        if (!usb->devhdl)
-               /* Nothing to do. */
-               return SR_OK;
+               return SR_ERR_BUG;
 
        /* This allows a frontend to configure the device without ever
         * doing an acquisition step. */
@@ -196,16 +193,14 @@ static int dev_close(struct sr_dev_inst *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 config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *rational[2];
        const uint64_t *si;
 
        (void)cg;
@@ -217,14 +212,11 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                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);
+               *data = std_gvar_tuple_u64(si[0], si[1]);
                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");
@@ -250,114 +242,69 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       uint64_t p, q;
-       unsigned int i;
-       int tmp, ret;
-       const char *tmp_str;
+       int idx;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        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;
+               if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(kecheng_kc_330b_sample_intervals))) < 0)
+                       return SR_ERR_ARG;
+               devc->sample_interval = idx;
+               devc->config_dirty = TRUE;
                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
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_freq))) < 0)
                        return SR_ERR_ARG;
                devc->mqflags &= ~(SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
-               devc->mqflags |= tmp;
+               devc->mqflags |= (weight_freq[idx][0] == 'A') ? SR_MQFLAG_SPL_FREQ_WEIGHT_A : SR_MQFLAG_SPL_FREQ_WEIGHT_C;
                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
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_time))) < 0)
                        return SR_ERR_ARG;
                devc->mqflags &= ~(SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
-               devc->mqflags |= tmp;
+               devc->mqflags |= (weight_time[idx][0] == 'F') ? SR_MQFLAG_SPL_TIME_WEIGHT_F : SR_MQFLAG_SPL_TIME_WEIGHT_S;
                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;
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+                       return SR_ERR_ARG;
+               devc->data_source = idx;
                devc->config_dirty = TRUE;
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
        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);
+               *data = std_gvar_tuple_array(ARRAY_AND_SIZE(kecheng_kc_330b_sample_intervals));
                break;
        case SR_CONF_SPL_WEIGHT_FREQ:
-               *data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_freq));
                break;
        case SR_CONF_SPL_WEIGHT_TIME:
-               *data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_time));
                break;
        case SR_CONF_DATA_SOURCE:
-               *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
                break;
        default:
                return SR_ERR_NA;
@@ -375,14 +322,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct sr_datafeed_meta meta;
        struct sr_config *src;
        struct sr_usb_dev_inst *usb;
-       GVariant *gvar, *rational[2];
+       GVariant *gvar;
        const uint64_t *si;
        int req_len, buf_len, len, ret;
        unsigned char buf[9];
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        drvc = di->context;
        devc = sdi->priv;
        usb = sdi->conn;
@@ -420,9 +364,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                        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);
+               gvar = std_gvar_tuple_u64(si[0], si[1]);
+
                src = sr_config_new(SR_CONF_SAMPLE_INTERVAL, gvar);
                packet.type = SR_DF_META;
                packet.payload = &meta;
@@ -478,9 +421,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
 
-       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;
 
@@ -506,7 +446,7 @@ static struct sr_dev_driver kecheng_kc_330b_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 886be6f742c013e4bab8922179aa409288812ec6..1bbe5746b36ec2fe666afc23202c13dc93166829 100644 (file)
@@ -20,6 +20,7 @@
 #include <config.h>
 #include <string.h>
 #include "protocol.h"
+
 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)
@@ -67,7 +68,7 @@ SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data)
                        if (ret != 0 || len != 1) {
                                sr_dbg("Failed to request new acquisition: %s",
                                                libusb_error_name(ret));
-                               sdi->driver->dev_acquisition_stop(sdi);
+                               sr_dev_acquisition_stop(sdi);
                                return TRUE;
                        }
                        libusb_submit_transfer(devc->xfer);
@@ -88,7 +89,7 @@ SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data)
                if (ret != 0 || len != 4) {
                        sr_dbg("Failed to request next chunk: %s",
                                        libusb_error_name(ret));
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                        return TRUE;
                }
                libusb_submit_transfer(devc->xfer);
@@ -135,7 +136,7 @@ SR_PRIV void LIBUSB_CALL kecheng_kc_330b_receive_transfer(struct libusb_transfer
        switch (transfer->status) {
        case LIBUSB_TRANSFER_NO_DEVICE:
                /* USB device was unplugged. */
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return;
        case LIBUSB_TRANSFER_COMPLETED:
        case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
@@ -156,7 +157,7 @@ SR_PRIV void LIBUSB_CALL kecheng_kc_330b_receive_transfer(struct libusb_transfer
                        send_data(sdi, fvalue, 1);
                        devc->num_samples++;
                        if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
-                               sdi->driver->dev_acquisition_stop(sdi);
+                               sr_dev_acquisition_stop(sdi);
                        } else {
                                /* let USB event handler fire off another
                                 * request when the time is right. */
@@ -176,7 +177,7 @@ SR_PRIV void LIBUSB_CALL kecheng_kc_330b_receive_transfer(struct libusb_transfer
                        send_data(sdi, fvalue, 1);
                        devc->num_samples += num_samples;
                        if (devc->num_samples >= devc->stored_samples) {
-                               sdi->driver->dev_acquisition_stop(sdi);
+                               sr_dev_acquisition_stop(sdi);
                        } else {
                                /* let USB event handler fire off another
                                 * request when the time is right. */
index e2090d84dfdc999fa1ffe4d1fa490e7cd099ce00..e750ff472ce6d801f080a973883f6ed321d57ca5 100644 (file)
@@ -66,9 +66,7 @@ enum {
        DEVICE_INACTIVE,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Acquisition settings */
        uint64_t limit_samples;
        int sample_interval;
        int alarm_low;
@@ -76,7 +74,6 @@ struct dev_context {
        enum sr_mqflag mqflags;
        int data_source;
 
-       /* Operational state */
        int state;
        gboolean config_dirty;
        uint64_t num_samples;
@@ -84,9 +81,7 @@ struct dev_context {
        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);
index 167bf1bc9958c8180b83908c2c47f95c5f27ae17..e369a44cf5e585722dbec8acf544b8a90b8119e8 100644 (file)
@@ -28,8 +28,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_SCALE,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -79,7 +82,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        serial_flush(serial);
 
        sr_spew("Set O1 mode (continuous values, stable and unstable ones).");
-       if (serial_write_nonblocking(serial, "O1\r\n", 4) != 4)
+       if (serial_write_blocking(serial, "O1\r\n", 4, 0) < 0)
                goto scan_cleanup;
        /* Device replies with "A00\r\n" (OK) or "E01\r\n" (Error). Ignore. */
 
@@ -110,41 +113,22 @@ scan_cleanup:
        return std_scan_complete(di, devices);
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -152,21 +136,17 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
        serial = sdi->conn;
 
        sr_spew("Set O1 mode (continuous values, stable and unstable ones).");
-       if (serial_write_nonblocking(serial, "O1\r\n", 4) != 4)
+       if (serial_write_blocking(serial, "O1\r\n", 4, 0) < 0)
                return SR_ERR;
        /* Device replies with "A00\r\n" (OK) or "E01\r\n" (Error). Ignore. */
 
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* Poll every 50ms, or whenever some data comes in. */
        serial_source_add(sdi->session, serial, G_IO_IN, 50,
                      kern_scale_receive_data, (void *)sdi);
 
@@ -184,6 +164,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                        .cleanup = std_cleanup, \
                        .scan = scan, \
                        .dev_list = std_dev_list, \
+                       .dev_clear = std_dev_clear, \
                        .config_get = NULL, \
                        .config_set = config_set, \
                        .config_list = config_list, \
index 97b557f9fdbd5cfafc826826a72adedcffb0a5a3..722e06256ee3a177464e404409205f2d3be359f8 100644 (file)
@@ -65,7 +65,7 @@ static void handle_new_data(struct sr_dev_inst *sdi, void *info)
 {
        struct scale_info *scale;
        struct dev_context *devc;
-       int len, i, offset = 0;
+       int len, offset;
        struct sr_serial_dev_inst *serial;
 
        scale = (struct scale_info *)sdi->driver;
@@ -85,6 +85,7 @@ static void handle_new_data(struct sr_dev_inst *sdi, void *info)
        devc->buflen += len;
 
        /* Now look for packets in that data. */
+       offset = 0;
        while ((devc->buflen - offset) >= scale->packet_size) {
                if (scale->packet_valid(devc->buf + offset)) {
                        handle_packet(devc->buf + offset, sdi, info);
@@ -95,8 +96,8 @@ static void handle_new_data(struct sr_dev_inst *sdi, void *info)
        }
 
        /* 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];
+       if (offset < devc->buflen)
+               memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
        devc->buflen -= offset;
 }
 
@@ -125,7 +126,7 @@ SR_PRIV int kern_scale_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index 934aff02064a5c3660ff5e9264ebc67eabca7aa3..836eb91b0404014ac7670cae80467f656743271c 100644 (file)
@@ -46,7 +46,6 @@ struct scale_info {
 
 #define SCALE_BUFSIZE 256
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits limits;
 
index fc57725cbf8f47c64546ea2c83b4507d73ca3279..eac10b8052aa20c47de7c62e933ec980be23c41a 100644 (file)
@@ -2,6 +2,7 @@
  * This file is part of the libsigrok project.
  *
  * Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae@gmail.com>
+ * Copyright (C) 2018 Frank Stettner <frank-stettner@gmx.net>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <config.h>
 #include "protocol.h"
 
-static const uint32_t drvopts[] = {
-       /* Device class */
-       SR_CONF_POWER_SUPPLY,
-};
-
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
-       /* Device class */
+static const uint32_t drvopts[] = {
        SR_CONF_POWER_SUPPLY,
-       /* Acquisition modes. */
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
-       /* Device configuration */
        SR_CONF_VOLTAGE | SR_CONF_GET,
        SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_CURRENT | SR_CONF_GET,
@@ -59,6 +55,16 @@ static const struct korad_kaxxxxp_model models[] = {
        /* Sometimes the KA3005P has an extra 0x01 after the ID. */
        {KORAD_KA3005P_0X01, "Korad", "KA3005P",
                "KORADKA3005PV2.0\x01", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+       {KORAD_KD3005P, "Korad", "KD3005P",
+               "KORAD KD3005P V2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+       {KORAD_KD3005P_V20_NOSP, "Korad", "KD3005P",
+               "KORADKD3005PV2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+       {RND_320K30PV, "RND", "KA3005P",
+               "RND 320-KA3005P V2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+       {TENMA_72_2540_V20, "Tenma", "72-2540",
+               "TENMA72-2540V2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+       {TENMA_72_2540_V21, "Tenma", "72-2540",
+               "TENMA 72-2540 V2.1", 1, {0, 31, 0.01}, {0, 5, 0.001}},
        ALL_ZERO
 };
 
@@ -130,7 +136,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        sr_dbg("Found: %s %s (idx %d, ID '%s').", models[model_id].vendor,
                models[model_id].name, model_id, models[model_id].id);
 
-       /* Init device instance, etc. */
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
        sdi->vendor = g_strdup(models[model_id].vendor);
@@ -138,12 +143,13 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        sdi->inst_type = SR_INST_SERIAL;
        sdi->conn = serial;
 
-       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
+       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
+       sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I");
 
        devc = g_malloc0(sizeof(struct dev_context));
        sr_sw_limits_init(&devc->limits);
+       g_mutex_init(&devc->rw_mutex);
        devc->model = &models[model_id];
-       devc->reply[5] = 0;
        devc->req_sent_at = 0;
        sdi->priv = devc;
 
@@ -180,28 +186,36 @@ static int config_get(uint32_t key, GVariant **data,
        case SR_CONF_LIMIT_MSEC:
                return sr_sw_limits_config_get(&devc->limits, key, data);
        case SR_CONF_VOLTAGE:
+               korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_VOLTAGE, devc);
                *data = g_variant_new_double(devc->voltage);
                break;
        case SR_CONF_VOLTAGE_TARGET:
+               korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_VOLTAGE_MAX, devc);
                *data = g_variant_new_double(devc->voltage_max);
                break;
        case SR_CONF_CURRENT:
+               korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_CURRENT, devc);
                *data = g_variant_new_double(devc->current);
                break;
        case SR_CONF_CURRENT_LIMIT:
+               korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_CURRENT_MAX, devc);
                *data = g_variant_new_double(devc->current_max);
                break;
        case SR_CONF_ENABLED:
+               korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_OUTPUT, devc);
                *data = g_variant_new_boolean(devc->output_enabled);
                break;
        case SR_CONF_REGULATION:
                /* Dual channel not supported. */
+               korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_STATUS, devc);
                *data = g_variant_new_string((devc->cc_mode[0]) ? "CC" : "CV");
                break;
        case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+               korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_OCP, devc);
                *data = g_variant_new_boolean(devc->ocp_enabled);
                break;
        case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+               korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_OVP, devc);
                *data = g_variant_new_boolean(devc->ovp_enabled);
                break;
        default:
@@ -220,9 +234,6 @@ static int config_set(uint32_t key, GVariant *data,
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
@@ -234,8 +245,7 @@ static int config_set(uint32_t key, GVariant *data,
                if (dval < devc->model->voltage[0] || dval > devc->model->voltage[1])
                        return SR_ERR_ARG;
                devc->voltage_max = dval;
-               devc->target = KAXXXXP_VOLTAGE_MAX;
-               if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+               if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_VOLTAGE_MAX, devc) < 0)
                        return SR_ERR;
                break;
        case SR_CONF_CURRENT_LIMIT:
@@ -243,30 +253,26 @@ static int config_set(uint32_t key, GVariant *data,
                if (dval < devc->model->current[0] || dval > devc->model->current[1])
                        return SR_ERR_ARG;
                devc->current_max = dval;
-               devc->target = KAXXXXP_CURRENT_MAX;
-               if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+               if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_CURRENT_MAX, devc) < 0)
                        return SR_ERR;
                break;
        case SR_CONF_ENABLED:
                bval = g_variant_get_boolean(data);
                /* Set always so it is possible turn off with sigrok-cli. */
                devc->output_enabled = bval;
-               devc->target = KAXXXXP_OUTPUT;
-               if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+               if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_OUTPUT, devc) < 0)
                        return SR_ERR;
                break;
        case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
                bval = g_variant_get_boolean(data);
                devc->ocp_enabled = bval;
-               devc->target = KAXXXXP_OCP;
-               if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+               if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_OCP, devc) < 0)
                        return SR_ERR;
                break;
        case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
                bval = g_variant_get_boolean(data);
                devc->ovp_enabled = bval;
-               devc->target = KAXXXXP_OVP;
-               if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+               if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_OVP, devc) < 0)
                        return SR_ERR;
                break;
        default:
@@ -280,57 +286,22 @@ static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *gvar;
-       GVariantBuilder gvb;
-       double dval;
-       int idx;
-
-       (void)cg;
-
-       /* Always available (with or without sdi). */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* Return drvopts without sdi (and devopts with sdi, see below). */
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* Every other key needs an sdi. */
-       if (!sdi)
-               return SR_ERR_ARG;
 
-       devc = sdi->priv;
+       devc = (sdi) ? sdi->priv : NULL;
 
        switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        case SR_CONF_VOLTAGE_TARGET:
-               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-               /* Min, max, step. */
-               for (idx = 0; idx < 3; idx++) {
-                       dval = devc->model->voltage[idx];
-                       gvar = g_variant_new_double(dval);
-                       g_variant_builder_add_value(&gvb, gvar);
-               }
-               *data = g_variant_builder_end(&gvb);
+               if (!devc || !devc->model)
+                       return SR_ERR_ARG;
+               *data = std_gvar_min_max_step_array(devc->model->voltage);
                break;
        case SR_CONF_CURRENT_LIMIT:
-               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-               /* Min, max, step. */
-               for (idx = 0; idx < 3; idx++) {
-                       dval = devc->model->current[idx];
-                       gvar = g_variant_new_double(dval);
-                       g_variant_builder_add_value(&gvb, gvar);
-               }
-               *data = g_variant_builder_end(&gvb);
+               if (!devc || !devc->model)
+                       return SR_ERR_ARG;
+               *data = std_gvar_min_max_step_array(devc->model->current);
                break;
        default:
                return SR_ERR_NA;
@@ -339,20 +310,27 @@ static int config_list(uint32_t key, GVariant **data,
        return SR_OK;
 }
 
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+
+       devc = (sdi) ? sdi->priv : NULL;
+       if (devc)
+               g_mutex_clear(&devc->rw_mutex);
+
+       return std_serial_dev_close(sdi);
+}
+
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       devc->reply_pending = FALSE;
        devc->req_sent_at = 0;
        serial = sdi->conn;
        serial_source_add(sdi->session, serial, G_IO_IN,
@@ -370,11 +348,12 @@ static struct sr_dev_driver korad_kaxxxxp_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
        .dev_open = std_serial_dev_open,
-       .dev_close = std_serial_dev_close,
+       .dev_close = dev_close,
        .dev_acquisition_start = dev_acquisition_start,
        .dev_acquisition_stop = std_serial_dev_acquisition_stop,
        .context = NULL,
index 4705403fb2c7e8d50ecc6974d9cdf041aa24b091..da5abe73e47a6765d6031a8c175aafdbd200c913 100644 (file)
@@ -2,6 +2,7 @@
  * This file is part of the libsigrok project.
  *
  * Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae@gmail.com>
+ * Copyright (C) 2018 Frank Stettner <frank-stettner@gmx.net>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -78,21 +79,22 @@ static void give_device_time_to_process(struct dev_context *devc)
 }
 
 SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial,
-                               struct dev_context *devc)
+                               int target, struct dev_context *devc)
 {
-       char msg[21];
+       char *msg;
        const char *cmd;
        float value;
        int ret;
 
+       g_mutex_lock(&devc->rw_mutex);
        give_device_time_to_process(devc);
 
-       msg[20] = 0;
-       switch (devc->target) {
+       switch (target) {
        case KAXXXXP_CURRENT:
        case KAXXXXP_VOLTAGE:
        case KAXXXXP_STATUS:
-               sr_err("Can't set measurable parameter.");
+               sr_err("Can't set measurable parameter %d.", target);
+               g_mutex_unlock(&devc->rw_mutex);
                return SR_ERR;
        case KAXXXXP_CURRENT_MAX:
                cmd = "ISET1:%05.3f";
@@ -123,6 +125,7 @@ SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial,
                if (devc->program < 1 || devc->program > 5) {
                        sr_err("Only programs 1-5 supported and %d isn't "
                               "between them.", devc->program);
+                       g_mutex_unlock(&devc->rw_mutex);
                        return SR_ERR;
                }
                value = devc->program;
@@ -132,130 +135,94 @@ SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial,
                if (devc->program < 1 || devc->program > 5) {
                        sr_err("Only programs 1-5 supported and %d isn't "
                               "between them.", devc->program);
+                       g_mutex_unlock(&devc->rw_mutex);
                        return SR_ERR;
                }
                value = devc->program;
                break;
        default:
-               sr_err("Don't know how to set %d.", devc->target);
+               sr_err("Don't know how to set %d.", target);
+               g_mutex_unlock(&devc->rw_mutex);
                return SR_ERR;
        }
 
+       msg = g_malloc0(20 + 1);
        if (cmd)
-               snprintf(msg, 20, cmd, value);
+               sr_snprintf_ascii(msg, 20, cmd, value);
 
        ret = korad_kaxxxxp_send_cmd(serial, msg);
        devc->req_sent_at = g_get_monotonic_time();
-       devc->reply_pending = FALSE;
+       g_free(msg);
+
+       g_mutex_unlock(&devc->rw_mutex);
 
        return ret;
 }
 
-SR_PRIV int korad_kaxxxxp_query_value(struct sr_serial_dev_inst *serial,
-                               struct dev_context *devc)
+SR_PRIV int korad_kaxxxxp_get_value(struct sr_serial_dev_inst *serial,
+                               int target, struct dev_context *devc)
 {
-       int ret;
+       int ret, count;
+       char reply[6];
+       float *value;
+       char status_byte;
 
+       g_mutex_lock(&devc->rw_mutex);
        give_device_time_to_process(devc);
 
-       switch (devc->target) {
+       value = NULL;
+       count = 5;
+
+       switch (target) {
        case KAXXXXP_CURRENT:
                /* Read current from device. */
                ret = korad_kaxxxxp_send_cmd(serial, "IOUT1?");
+               value = &(devc->current);
                break;
        case KAXXXXP_CURRENT_MAX:
                /* Read set current from device. */
                ret = korad_kaxxxxp_send_cmd(serial, "ISET1?");
+               value = &(devc->current_max);
                break;
        case KAXXXXP_VOLTAGE:
                /* Read voltage from device. */
                ret = korad_kaxxxxp_send_cmd(serial, "VOUT1?");
+               value = &(devc->voltage);
                break;
        case KAXXXXP_VOLTAGE_MAX:
                /* Read set voltage from device. */
                ret = korad_kaxxxxp_send_cmd(serial, "VSET1?");
+               value = &(devc->voltage_max);
                break;
        case KAXXXXP_STATUS:
        case KAXXXXP_OUTPUT:
+       case KAXXXXP_OCP:
+       case KAXXXXP_OVP:
                /* Read status from device. */
                ret = korad_kaxxxxp_send_cmd(serial, "STATUS?");
+               count = 1;
                break;
        default:
-               sr_err("Don't know how to query %d.", devc->target);
+               sr_err("Don't know how to query %d.", target);
+               g_mutex_unlock(&devc->rw_mutex);
                return SR_ERR;
        }
 
        devc->req_sent_at = g_get_monotonic_time();
-       devc->reply_pending = TRUE;
-
-       return ret;
-}
-
-SR_PRIV int korad_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
-                               struct dev_context *devc)
-{
-       int ret;
-
-       for (devc->target = KAXXXXP_CURRENT;
-                       devc->target <= KAXXXXP_STATUS; devc->target++) {
-               if ((ret = korad_kaxxxxp_query_value(serial, devc)) < 0)
-                       return ret;
-               if ((ret = korad_kaxxxxp_get_reply(serial, devc)) < 0)
-                       return ret;
-       }
-
-       return ret;
-}
 
-SR_PRIV int korad_kaxxxxp_get_reply(struct sr_serial_dev_inst *serial,
-                               struct dev_context *devc)
-{
-       double value;
-       int count, ret;
-       float *target;
-       char status_byte;
-
-       target = NULL;
-       count = 5;
-
-       switch (devc->target) {
-       case KAXXXXP_CURRENT:
-               /* Read current from device. */
-               target = &(devc->current);
-               break;
-       case KAXXXXP_CURRENT_MAX:
-               /* Read set current from device. */
-               target = &(devc->current_max);
-               break;
-       case KAXXXXP_VOLTAGE:
-               /* Read voltage from device. */
-               target = &(devc->voltage);
-               break;
-       case KAXXXXP_VOLTAGE_MAX:
-               /* Read set voltage from device. */
-               target = &(devc->voltage_max);
-               break;
-       case KAXXXXP_STATUS:
-       case KAXXXXP_OUTPUT:
-               /* Read status from device. */
-               count = 1;
-               break;
-       default:
-               sr_err("Don't know where to put repply %d.", devc->target);
-       }
-
-       if ((ret = korad_kaxxxxp_read_chars(serial, count, devc->reply)) < 0)
+       if ((ret = korad_kaxxxxp_read_chars(serial, count, reply)) < 0) {
+               g_mutex_unlock(&devc->rw_mutex);
                return ret;
+       }
 
-       devc->reply[count] = 0;
+       reply[count] = 0;
 
-       if (target) {
-               value = g_ascii_strtod(devc->reply, NULL);
-               *target = (float)value;
-               sr_dbg("value: %f",value);
+       if (value) {
+               sr_atof_ascii((const char *)&reply, value);
+               sr_dbg("value: %f", *value);
        } else {
                /* We have status reply. */
-               status_byte = devc->reply[0];
+               status_byte = reply[0];
                /* Constant current */
                devc->cc_mode[0] = !(status_byte & (1 << 0)); /* Channel one */
                devc->cc_mode[1] = !(status_byte & (1 << 1)); /* Channel two */
@@ -284,41 +251,45 @@ SR_PRIV int korad_kaxxxxp_get_reply(struct sr_serial_dev_inst *serial,
                        (status_byte & (1 << 6)) ? "enabled" : "disabled",
                        (status_byte & (1 << 7)) ? "true" : "false");
        }
+
        /* Read the sixth byte from ISET? BUG workaround. */
-       if (devc->target == KAXXXXP_CURRENT_MAX)
+       if (target == KAXXXXP_CURRENT_MAX)
                serial_read_blocking(serial, &status_byte, 1, 10);
-       devc->reply_pending = FALSE;
+
+       g_mutex_unlock(&devc->rw_mutex);
+
+       return ret;
+}
+
+SR_PRIV int korad_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
+                               struct dev_context *devc)
+{
+       int ret, target;
+
+       for (target = KAXXXXP_CURRENT;
+                       target <= KAXXXXP_STATUS; target++) {
+               if ((ret = korad_kaxxxxp_get_value(serial, target, devc)) < 0)
+                       return ret;
+       }
 
        return ret;
 }
 
 static void next_measurement(struct dev_context *devc)
 {
-       switch (devc->target) {
+       switch (devc->acquisition_target) {
        case KAXXXXP_CURRENT:
-               devc->target = KAXXXXP_VOLTAGE;
-               break;
-       case KAXXXXP_CURRENT_MAX:
-               devc->target = KAXXXXP_CURRENT;
+               devc->acquisition_target = KAXXXXP_VOLTAGE;
                break;
        case KAXXXXP_VOLTAGE:
-               devc->target = KAXXXXP_STATUS;
-               break;
-       case KAXXXXP_VOLTAGE_MAX:
-               devc->target = KAXXXXP_CURRENT;
-               break;
-       /* Read back what was set. */
-       case KAXXXXP_BEEP:
-       case KAXXXXP_OCP:
-       case KAXXXXP_OVP:
-       case KAXXXXP_OUTPUT:
-               devc->target = KAXXXXP_STATUS;
+               devc->acquisition_target = KAXXXXP_STATUS;
                break;
        case KAXXXXP_STATUS:
-               devc->target = KAXXXXP_CURRENT;
+               devc->acquisition_target = KAXXXXP_CURRENT;
                break;
        default:
-               devc->target = KAXXXXP_CURRENT;
+               devc->acquisition_target = KAXXXXP_CURRENT;
+               sr_err("Invalid target for next acquisition.");
        }
 }
 
@@ -332,9 +303,10 @@ SR_PRIV int korad_kaxxxxp_receive_data(int fd, int revents, void *cb_data)
        struct sr_analog_encoding encoding;
        struct sr_analog_meaning meaning;
        struct sr_analog_spec spec;
-       uint64_t elapsed_us;
+       GSList *l;
 
        (void)fd;
+       (void)revents;
 
        if (!(sdi = cb_data))
                return TRUE;
@@ -344,63 +316,43 @@ SR_PRIV int korad_kaxxxxp_receive_data(int fd, int revents, void *cb_data)
 
        serial = sdi->conn;
 
-       if (revents == G_IO_IN) {
-               /* Get the value. */
-               korad_kaxxxxp_get_reply(serial, devc);
-
-               /* Note: digits/spec_digits will be overridden later. */
-               sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
-
-               /* Send the value forward. */
-               packet.type = SR_DF_ANALOG;
-               packet.payload = &analog;
-               analog.meaning->channels = sdi->channels;
-               analog.num_samples = 1;
-               if (devc->target == KAXXXXP_CURRENT) {
-                       analog.meaning->mq = SR_MQ_CURRENT;
-                       analog.meaning->unit = SR_UNIT_AMPERE;
-                       analog.meaning->mqflags = 0;
-                       analog.encoding->digits = 3;
-                       analog.spec->spec_digits = 3;
-                       analog.data = &devc->current;
-                       sr_session_send(sdi, &packet);
-               }
-               if (devc->target == KAXXXXP_VOLTAGE) {
-                       analog.meaning->mq = SR_MQ_VOLTAGE;
-                       analog.meaning->unit = SR_UNIT_VOLT;
-                       analog.meaning->mqflags = SR_MQFLAG_DC;
-                       analog.encoding->digits = 2;
-                       analog.spec->spec_digits = 2;
-                       analog.data = &devc->voltage;
-                       sr_session_send(sdi, &packet);
-                       sr_sw_limits_update_samples_read(&devc->limits, 1);
-               }
-               next_measurement(devc);
-       } else {
-               /* Time out */
-               if (!devc->reply_pending) {
-                       if (korad_kaxxxxp_query_value(serial, devc) < 0)
-                               return TRUE;
-                       devc->req_sent_at = g_get_monotonic_time();
-                       devc->reply_pending = TRUE;
-               }
-       }
-
-       if (sr_sw_limits_check(&devc->limits)) {
-               sdi->driver->dev_acquisition_stop(sdi);
-               return TRUE;
+       /* Get the value. */
+       korad_kaxxxxp_get_value(serial, devc->acquisition_target, devc);
+
+       /* Note: digits/spec_digits will be overridden later. */
+       sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+
+       /* Send the value forward. */
+       packet.type = SR_DF_ANALOG;
+       packet.payload = &analog;
+       analog.num_samples = 1;
+       l = g_slist_copy(sdi->channels);
+       if (devc->acquisition_target == KAXXXXP_CURRENT) {
+               l = g_slist_remove_link(l, g_slist_nth(l, 0));
+               analog.meaning->channels = l;
+               analog.meaning->mq = SR_MQ_CURRENT;
+               analog.meaning->unit = SR_UNIT_AMPERE;
+               analog.meaning->mqflags = 0;
+               analog.encoding->digits = 3;
+               analog.spec->spec_digits = 3;
+               analog.data = &devc->current;
+               sr_session_send(sdi, &packet);
+       } else if (devc->acquisition_target == KAXXXXP_VOLTAGE) {
+               l = g_slist_remove_link(l, g_slist_nth(l, 1));
+               analog.meaning->channels = l;
+               analog.meaning->mq = SR_MQ_VOLTAGE;
+               analog.meaning->unit = SR_UNIT_VOLT;
+               analog.meaning->mqflags = SR_MQFLAG_DC;
+               analog.encoding->digits = 2;
+               analog.spec->spec_digits = 2;
+               analog.data = &devc->voltage;
+               sr_session_send(sdi, &packet);
+               sr_sw_limits_update_samples_read(&devc->limits, 1);
        }
+       next_measurement(devc);
 
-       /* 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;
-               }
-
-       }
+       if (sr_sw_limits_check(&devc->limits))
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index e88360e7edcf0f10b0dc734746e766fac6a1114b..34c6465a62c17db91be8167a12fdc962a9ea7f9f 100644 (file)
@@ -2,6 +2,7 @@
  * This file is part of the libsigrok project.
  *
  * Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae@gmail.com>
+ * Copyright (C) 2018 Frank Stettner <frank-stettner@gmx.net>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- * Korad KAxxxxP power supply driver
- * @internal
- */
-
 #ifndef LIBSIGROK_HARDWARE_KORAD_KAXXXXP_PROTOCOL_H
 #define LIBSIGROK_HARDWARE_KORAD_KAXXXXP_PROTOCOL_H
 
@@ -41,6 +36,11 @@ enum {
        VELLEMAN_LABPS3005D,
        KORAD_KA3005P,
        KORAD_KA3005P_0X01,
+       KORAD_KD3005P,
+       KORAD_KD3005P_V20_NOSP,
+       RND_320K30PV,
+       TENMA_72_2540_V20,
+       TENMA_72_2540_V21,
        /* Support for future devices with this protocol. */
 };
 
@@ -70,17 +70,13 @@ enum {
        KAXXXXP_RECALL,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        const struct korad_kaxxxxp_model *model; /**< Model information. */
 
-       /* Acquisition settings */
        struct sr_sw_limits limits;
        int64_t req_sent_at;
-       gboolean reply_pending;
+       GMutex rw_mutex;
 
-       /* Operational state */
        float current;          /**< Last current value [A] read from device. */
        float current_max;      /**< Output current set. */
        float voltage;          /**< Last voltage value [V] read from device. */
@@ -92,10 +88,8 @@ struct dev_context {
        gboolean ocp_enabled;    /**< Output current protection enabled. */
        gboolean ovp_enabled;    /**< Output voltage protection enabled. */
 
-       /* Temporary state across callbacks */
-       int target;              /**< What reply to expect. */
+       int acquisition_target;  /**< What reply to expect. */
        int program;             /**< Program to store or recall. */
-       char reply[6];
 };
 
 SR_PRIV int korad_kaxxxxp_send_cmd(struct sr_serial_dev_inst *serial,
@@ -103,11 +97,9 @@ SR_PRIV int korad_kaxxxxp_send_cmd(struct sr_serial_dev_inst *serial,
 SR_PRIV int korad_kaxxxxp_read_chars(struct sr_serial_dev_inst *serial,
                                        int count, char *buf);
 SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial,
-                                       struct dev_context *devc);
-SR_PRIV int korad_kaxxxxp_query_value(struct sr_serial_dev_inst *serial,
-                                       struct dev_context *devc);
-SR_PRIV int korad_kaxxxxp_get_reply(struct sr_serial_dev_inst *serial,
-                                       struct dev_context *devc);
+                                       int target, struct dev_context *devc);
+SR_PRIV int korad_kaxxxxp_get_value(struct sr_serial_dev_inst *serial,
+                                       int target, struct dev_context *devc);
 SR_PRIV int korad_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
                                        struct dev_context *devc);
 SR_PRIV int korad_kaxxxxp_receive_data(int fd, int revents, void *cb_data);
index 96ed3616f1b40c235bc849f17d73d9879b87f0e0..c64882c0bdb9913086bb24cd376b5da2c50e58d6 100644 (file)
@@ -28,9 +28,12 @@ static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_THERMOMETER,
        SR_CONF_HYGROMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONN | SR_CONF_GET,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_DATALOG | SR_CONF_GET | SR_CONF_SET,
@@ -97,9 +100,8 @@ static int dev_open(struct sr_dev_inst *sdi)
                sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
                return SR_ERR;
        }
-       sdi->status = SR_ST_ACTIVE;
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
@@ -109,24 +111,21 @@ static int dev_close(struct sr_dev_inst *sdi)
        usb = sdi->conn;
 
        if (!usb->devhdl)
-               /* Nothing to do. */
-               return SR_OK;
+               return SR_ERR_BUG;
 
        libusb_release_interface(usb->devhdl, LASCAR_INTERFACE);
        libusb_close(usb->devhdl);
        usb->devhdl = NULL;
-       sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
        int ret;
-       char str[128];
 
        (void)cg;
 
@@ -136,8 +135,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                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);
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                break;
        case SR_CONF_DATALOG:
                if (!sdi)
@@ -156,56 +154,36 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       int ret;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
-       ret = SR_OK;
+
        switch (key) {
        case SR_CONF_DATALOG:
                if (g_variant_get_boolean(data))
-                       ret = lascar_start_logging(sdi);
+                       return lascar_start_logging(sdi);
                else
-                       ret = lascar_stop_logging(sdi);
+                       return lascar_stop_logging(sdi);
                break;
        case SR_CONF_LIMIT_SAMPLES:
                devc->limit_samples = g_variant_get_uint64(data);
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static void LIBUSB_CALL mark_xfer(struct libusb_transfer *xfer)
@@ -293,9 +271,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        int ret;
        unsigned char cmd[3], resp[4], *buf;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        drvc = di->context;
        devc = sdi->priv;
        usb = sdi->conn;
@@ -395,13 +370,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-SR_PRIV int dev_acquisition_stop(struct sr_dev_inst *sdi)
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       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? */
 
@@ -416,7 +386,7 @@ SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index fc24fbe77cd81780201ea786afdbc61d33a526f7..dcd83e8e035421c1fc34e0d0afd24d76d17ecc8b 100644 (file)
@@ -298,51 +298,53 @@ static struct sr_dev_inst *lascar_identify(unsigned char *config)
 
        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 (!modelid)
+               return sdi;
 
-               if (profile->logformat == LOG_UNSUPPORTED) {
-                       sr_dbg("unsupported EL-USB logformat for %s", profile->modelname);
-                       return NULL;
+       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;
+       }
 
-               sdi = g_malloc0(sizeof(struct sr_dev_inst));
-               sdi->status = SR_ST_INACTIVE;
-               sdi->vendor = g_strdup(LASCAR_VENDOR);
-               sdi->model = g_strdup(profile->modelname);
-               sdi->version = g_strdup(firmware);
-
-               if (profile->logformat == LOG_TEMP_RH) {
-                       /* Model this as two channels: temperature and humidity. */
-                       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "Temp");
-                       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "Hum");
-               } else if (profile->logformat == LOG_CO) {
-                       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CO");
-               } else {
-                       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
-               }
+       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;
+       }
 
-               devc = g_malloc0(sizeof(struct dev_context));
-               sdi->priv = devc;
-               devc->profile = profile;
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->status = SR_ST_INACTIVE;
+       sdi->vendor = g_strdup("Lascar");
+       sdi->model = g_strdup(profile->modelname);
+       sdi->version = g_strdup(firmware);
+
+       if (profile->logformat == LOG_TEMP_RH) {
+               /* Model this as two channels: temperature and humidity. */
+               sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "Temp");
+               sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "Hum");
+       } else if (profile->logformat == LOG_CO) {
+               sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CO");
+       } else {
+               sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
        }
 
+       devc = g_malloc0(sizeof(struct dev_context));
+       sdi->priv = devc;
+       devc->profile = profile;
+
        return sdi;
 }
 
@@ -410,10 +412,13 @@ static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
                packet.type = SR_DF_ANALOG;
                packet.payload = &analog;
                analog.meaning->mqflags = 0;
-               if (!(temp = g_try_malloc(sizeof(float) * samples)))
-                       break;
-               if (!(rh = g_try_malloc(sizeof(float) * samples)))
+               temp = g_try_malloc(sizeof(float) * samples);
+               rh = g_try_malloc(sizeof(float) * samples);
+               if (!temp || !rh) {
+                       g_free(temp);
+                       g_free(rh);
                        break;
+               }
                for (i = 0, j = 0; i < samples; i++) {
                        /* Both Celsius and Fahrenheit stored at base -40. */
                        if (devc->temp_unit == 0)
@@ -528,7 +533,7 @@ SR_PRIV void LIBUSB_CALL lascar_el_usb_receive_transfer(struct libusb_transfer *
        switch (transfer->status) {
        case LIBUSB_TRANSFER_NO_DEVICE:
                /* USB device was unplugged. */
-               dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return;
        case LIBUSB_TRANSFER_COMPLETED:
        case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
@@ -547,7 +552,7 @@ SR_PRIV void LIBUSB_CALL lascar_el_usb_receive_transfer(struct libusb_transfer *
                                devc->rcvd_bytes, devc->log_size,
                                devc->rcvd_samples, devc->logged_samples);
                if (devc->rcvd_bytes >= devc->log_size)
-                       dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
        }
 
        if (sdi->status == SR_ST_ACTIVE) {
@@ -557,7 +562,7 @@ SR_PRIV void LIBUSB_CALL lascar_el_usb_receive_transfer(struct libusb_transfer *
                               libusb_error_name(ret));
                        g_free(transfer->buffer);
                        libusb_free_transfer(transfer);
-                       dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                }
        } else {
                /* This was the last transfer we're going to receive, so
index c4eec74b629288f844ece0ab8406456990b8a6d8..bc7e6ebd442449c6afb474d84b4f5d40e8ec76b0 100644 (file)
@@ -26,7 +26,6 @@
 
 #define LOG_PREFIX "lascar-el-usb"
 
-#define LASCAR_VENDOR "Lascar"
 #define LASCAR_INTERFACE 0
 #define LASCAR_EP_IN 0x82
 #define LASCAR_EP_OUT 2
@@ -39,9 +38,9 @@
 #define SLEEP_US_LONG (5 * 1000)
 #define SLEEP_US_SHORT (1 * 1000)
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        const struct elusb_profile *profile;
+
        /* Generic EL-USB */
        unsigned char config[MAX_CONFIGBLOCK_SIZE];
        unsigned int log_size;
@@ -50,6 +49,7 @@ struct dev_context {
        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. */
@@ -79,6 +79,5 @@ SR_PRIV void LIBUSB_CALL lascar_el_usb_receive_transfer(struct libusb_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);
 
 #endif
index db423e4b749c29f01b2e7639bb3bf2308d4f571f..6425e0f5fcedaeb95cdbbcedb545b165dcf952fd 100644 (file)
 #define UNKNOWN_ADDRESS 0xff
 #define MAX_RENUM_DELAY_MS 3000
 
-static const uint32_t devopts[] = {
+#define NUM_CHANNELS 16
+
+static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
@@ -86,7 +91,7 @@ static struct sr_dev_inst *create_device(struct sr_usb_dev_inst *usb,
        sdi->inst_type = SR_INST_USB;
        sdi->conn = usb;
 
-       for (i = 0; i < 16; i++) {
+       for (i = 0; i < NUM_CHANNELS; i++) {
                snprintf(channel_name, sizeof(channel_name), "D%i", i);
                sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
        }
@@ -129,7 +134,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                if (des.idVendor != LOGICSTUDIO16_VID)
                        continue;
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
 
                usb = NULL;
 
@@ -191,9 +197,6 @@ static int open_device(struct sr_dev_inst *sdi)
        size_t i;
        int r;
 
-       if (sdi->status == SR_ST_ACTIVE)
-               return SR_ERR;
-
        drvc = sdi->driver->context;
        usb = sdi->conn;
 
@@ -208,7 +211,8 @@ static int open_device(struct sr_dev_inst *sdi)
                        des.idProduct != LOGICSTUDIO16_PID_HAVE_FIRMWARE)
                        continue;
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
 
                /*
                 * Check if this device is the same one that we associated
@@ -340,15 +344,13 @@ static int dev_close(struct sr_dev_inst *sdi)
        }
 
        if (!usb->devhdl)
-               return SR_ERR;
+               return SR_ERR_BUG;
 
        libusb_release_interface(usb->devhdl, 0);
 
        libusb_close(usb->devhdl);
        usb->devhdl = NULL;
 
-       sdi->status = SR_ST_INACTIVE;
-
        return SR_OK;
 }
 
@@ -390,16 +392,11 @@ static int config_set(uint32_t key, GVariant *data,
 
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        switch (key) {
        case SR_CONF_SAMPLERATE:
                return lls_set_samplerate(sdi, g_variant_get_uint64(data));
        case SR_CONF_CAPTURE_RATIO:
                devc->capture_ratio = g_variant_get_uint64(data);
-               if (devc->capture_ratio > 100)
-                       return SR_ERR;
                break;
        default:
                return SR_ERR_NA;
@@ -411,30 +408,14 @@ static int config_set(uint32_t key, GVariant *data,
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       GVariantBuilder vb;
-       GVariant *var;
-
-       (void)sdi;
-       (void)cg;
-
        switch (key) {
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts),
-                               sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
        case SR_CONF_SAMPLERATE:
-               g_variant_builder_init(&vb, G_VARIANT_TYPE("a{sv}"));
-               var = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
-                               samplerates, ARRAY_SIZE(samplerates),
-                               sizeof(uint64_t));
-               g_variant_builder_add(&vb, "{sv}", "samplerates", var);
-               *data = g_variant_builder_end(&vb);
+               *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
                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));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                break;
        default:
                return SR_ERR_NA;
@@ -472,9 +453,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct drv_context *drvc;
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        drvc = sdi->driver->context;
 
        if ((ret = lls_start_acquisition(sdi)) < 0)
@@ -488,9 +466,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        return lls_stop_acquisition(sdi);
 }
 
@@ -502,6 +477,7 @@ static struct sr_dev_driver lecroy_logicstudio_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 78376a45ef1747c2a5e070edd516c278e803bc1c..7b3a41a69d30c026a3272b87dafd3f895a5390d9 100644 (file)
@@ -310,7 +310,7 @@ static int fetch_samples_async(const struct sr_dev_inst *sdi)
        prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples + 0x01);
        prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples | 0x02);
 
-       return write_registers_async(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd),
+       return write_registers_async(sdi, 0x12, 5444, ARRAY_AND_SIZE(cmd),
                        handle_fetch_samples_done);
 }
 
@@ -522,7 +522,7 @@ static int upload_trigger(const struct sr_dev_inst *sdi,
                        prep_regw(&regs[k++], REG_TRIGGER_CFG, value);
                }
 
-               if (write_registers_sync(sdi, 0x12, 5444, regs, ARRAY_SIZE(regs))) {
+               if (write_registers_sync(sdi, 0x12, 5444, ARRAY_AND_SIZE(regs))) {
                        sr_err("Failed to upload trigger config.");
                        return SR_ERR;
                }
@@ -1127,7 +1127,7 @@ SR_PRIV int lls_start_acquisition(const struct sr_dev_inst *sdi)
 
        total_samples = devc->num_thousand_samples * 1000;
 
-       pre_trigger_samples = total_samples * devc->capture_ratio / 100;
+       pre_trigger_samples = (total_samples * devc->capture_ratio) / 100;
        post_trigger_samples = total_samples - pre_trigger_samples;
 
        pre_trigger_tr = transform_sample_count(devc, pre_trigger_samples);
@@ -1162,7 +1162,7 @@ SR_PRIV int lls_start_acquisition(const struct sr_dev_inst *sdi)
        devc->magic_arm_trigger = 0x0c;
        prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger | 0x01);
 
-       return write_registers_sync(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd));
+       return write_registers_sync(sdi, 0x12, 5444, ARRAY_AND_SIZE(cmd));
 }
 
 SR_PRIV int lls_stop_acquisition(const struct sr_dev_inst *sdi)
@@ -1182,5 +1182,5 @@ SR_PRIV int lls_stop_acquisition(const struct sr_dev_inst *sdi)
 
        assert(i == ARRAY_SIZE(cmd));
 
-       return write_registers_sync(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd));
+       return write_registers_sync(sdi, 0x12, 5444, ARRAY_AND_SIZE(cmd));
 }
index 1ac3d0fc0bf69e386513396aa271f6adcba3cbcd..f6a0ea0992b91a46ec1872fa8cc0ab794c878f46 100644 (file)
@@ -33,7 +33,6 @@
 
 struct samplerate_info;
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct libusb_transfer *intr_xfer;
        struct libusb_transfer *bulk_xfer;
index 9dfb0b643f3f1325fb14e93a578ce7117e842ecd..8551351a11a2994270879e9b461b566e08881e70 100644 (file)
@@ -46,24 +46,13 @@ static const uint32_t devopts[] = {
        SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const uint32_t analog_devopts[] = {
+static const uint32_t devopts_cg_analog[] = {
        SR_CONF_NUM_VDIV | SR_CONF_GET,
        SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-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 *probe_serial_device(struct sr_scpi_dev_inst *scpi)
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 {
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
@@ -78,7 +67,7 @@ static struct sr_dev_inst *probe_serial_device(struct sr_scpi_dev_inst *scpi)
                goto fail;
        }
 
-       if (check_manufacturer(hw_info->manufacturer) != SR_OK)
+       if (std_str_idx_s(hw_info->manufacturer, ARRAY_AND_SIZE(manufacturers)) < 0)
                goto fail;
 
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
@@ -94,7 +83,6 @@ static struct sr_dev_inst *probe_serial_device(struct sr_scpi_dev_inst *scpi)
        hw_info = NULL;
 
        devc = g_malloc0(sizeof(struct dev_context));
-
        sdi->priv = devc;
 
        if (lecroy_xstream_init_device(sdi) != SR_OK)
@@ -112,57 +100,40 @@ fail:
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       return sr_scpi_scan(di->context, options, probe_serial_device);
+       return sr_scpi_scan(di->context, options, probe_device);
 }
 
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
-
        lecroy_xstream_state_free(devc->model_state);
-
        g_free(devc->analog_groups);
-
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
+       if (sr_scpi_open(sdi->conn) != SR_OK)
                return SR_ERR;
 
        if (lecroy_xstream_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;
+       return sr_scpi_close(sdi->conn);
 }
 
 static int config_get(uint32_t key, GVariant **data,
-       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+               const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
-       unsigned int i;
+       int idx;
        struct dev_context *devc;
        const struct scope_config *model;
        struct scope_state *state;
@@ -172,7 +143,6 @@ static int config_get(uint32_t key, GVariant **data,
 
        devc = sdi->priv;
 
-       ret = SR_ERR_NA;
        model = devc->model_config;
        state = devc->model_state;
        *data = NULL;
@@ -180,100 +150,60 @@ static int config_get(uint32_t key, GVariant **data,
        switch (key) {
        case SR_CONF_NUM_HDIV:
                *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].p,
-                               model->timebases[state->timebase].q);
-               ret = SR_OK;
+                               (*model->timebases)[state->timebase][0],
+                               (*model->timebases)[state->timebase][1]);
                break;
        case SR_CONF_NUM_VDIV:
-               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;
-               }
+               if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) < 0)
+                       return SR_ERR_ARG;
+               *data = g_variant_new_int32(model->num_ydivs);
                break;
        case SR_CONF_VDIV:
-               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].p,
-                               model->vdivs[state->analog_channels[i].vdiv].q);
-                       ret = SR_OK;
-               }
+               if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               *data = g_variant_new("(tt)",
+                               (*model->vdivs)[state->analog_channels[idx].vdiv][0],
+                               (*model->vdivs)[state->analog_channels[idx].vdiv][1]);
                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:
-               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;
-               }
+               if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               *data = g_variant_new_string((*model->coupling_options)[state->analog_channels[idx].coupling]);
                break;
        case SR_CONF_SAMPLERATE:
                *data = g_variant_new_uint64(state->sample_rate);
-               ret = SR_OK;
                break;
        case SR_CONF_ENABLED:
                *data = g_variant_new_boolean(FALSE);
-               ret = SR_OK;
                break;
        default:
-               ret = SR_ERR_NA;
-       }
-
-       return ret;
-}
-
-static GVariant *build_tuples(const struct sr_rational *array, 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].p);
-               rational[1] = g_variant_new_uint64(array[i].q);
-
-               /* FIXME: Valgrind reports a memory leak here. */
-               g_variant_builder_add_value(&gvb, g_variant_new_tuple(rational, 2));
+               return SR_ERR_NA;
        }
 
-       return g_variant_builder_end(&gvb);
+       return SR_OK;
 }
 
 static int config_set(uint32_t key, GVariant *data,
-       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+               const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
-       unsigned int i, j;
+       int ret, idx, j;
        char command[MAX_COMMAND_SIZE];
        struct dev_context *devc;
        const struct scope_config *model;
        struct scope_state *state;
-       const char *tmp;
-       int64_t p;
-       uint64_t q;
        double tmp_d;
-       gboolean update_sample_rate;
 
        if (!sdi)
                return SR_ERR_ARG;
@@ -282,9 +212,6 @@ static int config_set(uint32_t key, GVariant *data,
 
        model = devc->model_config;
        state = devc->model_state;
-       update_sample_rate = FALSE;
-
-       ret = SR_ERR_NA;
 
        switch (key) {
        case SR_CONF_LIMIT_FRAMES:
@@ -292,58 +219,32 @@ static int config_set(uint32_t key, GVariant *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),
-                                       "SET TRIGGER SOURCE %s",
-                                       (*model->trigger_sources)[i]);
-
-                       ret = sr_scpi_send(sdi->conn, command);
-                       break;
-               }
+               if ((idx = std_str_idx(data, *model->trigger_sources, model->num_trigger_sources)) < 0)
+                       return SR_ERR_ARG;
+               state->trigger_source = idx;
+               g_snprintf(command, sizeof(command),
+                               "TRIG_SELECT EDGE,SR,%s", (*model->trigger_sources)[idx]);
+               ret = sr_scpi_send(sdi->conn, command);
                break;
        case SR_CONF_VDIV:
-               g_variant_get(data, "(tt)", &p, &q);
-
-               for (i = 0; i < model->num_vdivs; i++) {
-                       if (p != model->vdivs[i].p || q != model->vdivs[i].q)
-                               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_snprintf(command, sizeof(command),
-                                               "C%d:VDIV %E", j, (float)p/q);
-
-                               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;
-               }
+               if ((idx = std_u64_tuple_idx(data, *model->vdivs, model->num_vdivs)) < 0)
+                       return SR_ERR_ARG;
+               if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               state->analog_channels[j].vdiv = idx;
+               g_snprintf(command, sizeof(command),
+                               "C%d:VDIV %E", j + 1, (float) (*model->vdivs)[idx][0] / (*model->vdivs)[idx][1]);
+               if (sr_scpi_send(sdi->conn, command) != SR_OK || sr_scpi_get_opc(sdi->conn) != SR_OK)
+                       return SR_ERR;
+               ret = SR_OK;
                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].p ||
-                           q != model->timebases[i].q)
-                               continue;
-                       state->timebase = i;
-                       g_snprintf(command, sizeof(command),
-                                       "TIME_DIV %E", (float)p/q);
-
-                       ret = sr_scpi_send(sdi->conn, command);
-                       update_sample_rate = TRUE;
-                       break;
-               }
+               if ((idx = std_u64_tuple_idx(data, *model->timebases, model->num_timebases)) < 0)
+                       return SR_ERR_ARG;
+               state->timebase = idx;
+               g_snprintf(command, sizeof(command),
+                               "TIME_DIV %E", (float) (*model->timebases)[idx][0] / (*model->timebases)[idx][1]);
+               ret = sr_scpi_send(sdi->conn, command);
                break;
        case SR_CONF_HORIZ_TRIGGERPOS:
                tmp_d = g_variant_get_double(data);
@@ -353,51 +254,34 @@ static int config_set(uint32_t key, GVariant *data,
 
                state->horiz_triggerpos = tmp_d;
                tmp_d = -(tmp_d - 0.5) *
-                       ((double)model->timebases[state->timebase].p /
-                        model->timebases[state->timebase].q)
-                        * model->num_xdivs;
+                               ((double)(*model->timebases)[state->timebase][0] /
+                               (*model->timebases)[state->timebase][1])
+                               * model->num_xdivs;
 
                g_snprintf(command, sizeof(command), "TRIG POS %e S", tmp_d);
 
                ret = sr_scpi_send(sdi->conn, command);
                break;
        case SR_CONF_TRIGGER_SLOPE:
-               tmp = g_variant_get_string(data, NULL);
-               for (i = 0; (*model->trigger_slopes)[i]; i++) {
-                       if (g_strcmp0(tmp, (*model->trigger_slopes)[i]) != 0)
-                               continue;
-                       state->trigger_slope = i;
-                       g_snprintf(command, sizeof(command),
-                                       "SET TRIGGER SLOPE %s",
-                                       (*model->trigger_slopes)[i]);
-
-                       ret = sr_scpi_send(sdi->conn, command);
-                       break;
-               }
+               if ((idx = std_str_idx(data, *model->trigger_slopes, model->num_trigger_slopes)) < 0)
+                       return SR_ERR_ARG;
+               state->trigger_slope = idx;
+               g_snprintf(command, sizeof(command),
+                               "%s:TRIG_SLOPE %s", (*model->trigger_sources)[state->trigger_source],
+                               (*model->trigger_slopes)[idx]);
+               ret = sr_scpi_send(sdi->conn, command);
                break;
        case SR_CONF_COUPLING:
-               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),
-                                               "C%d:COUPLING %s", 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;
-               }
+               if ((idx = std_str_idx(data, *model->coupling_options, model->num_coupling_options)) < 0)
+                       return SR_ERR_ARG;
+               if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               state->analog_channels[j].coupling = idx;
+               g_snprintf(command, sizeof(command), "C%d:COUPLING %s",
+                               j + 1, (*model->coupling_options)[idx]);
+               if (sr_scpi_send(sdi->conn, command) != SR_OK || sr_scpi_get_opc(sdi->conn) != SR_OK)
+                       return SR_ERR;
+               ret = SR_OK;
                break;
        default:
                ret = SR_ERR_NA;
@@ -407,83 +291,65 @@ static int config_set(uint32_t key, GVariant *data,
        if (ret == SR_OK)
                ret = sr_scpi_get_opc(sdi->conn);
 
-       if (ret == SR_OK && update_sample_rate)
-               ret = lecroy_xstream_update_sample_rate(sdi);
-
        return ret;
 }
 
-static int config_list(uint32_t key, GVariant **data,
-       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+static int config_channel_set(const struct sr_dev_inst *sdi,
+       struct sr_channel *ch, unsigned int changes)
 {
-       struct dev_context *devc = NULL;
-       const struct scope_config *model = NULL;
-
-       (void)cg;
-
-       /* SR_CONF_SCAN_OPTIONS is always valid, regardless of sdi or channel group. */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
+       /* Currently we only handle SR_CHANNEL_SET_ENABLED. */
+       if (changes != SR_CHANNEL_SET_ENABLED)
+               return SR_ERR_NA;
 
-       /* If sdi is NULL, nothing except SR_CONF_DEVICE_OPTIONS can be provided. */
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
+       return lecroy_xstream_channel_state_set(sdi, ch->index, ch->enabled);
+}
 
-        /* Every other option requires a valid device instance. */
-        if (!sdi)
-                return SR_ERR_ARG;
+static int config_list(uint32_t key, GVariant **data,
+               const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       const struct scope_config *model;
 
-       devc = sdi->priv;
-       model = devc->model_config;
+       devc = (sdi) ? sdi->priv : NULL;
+       model = (devc) ? devc->model_config : NULL;
 
        switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, NO_OPTS, NO_OPTS);
        case SR_CONF_DEVICE_OPTIONS:
-               if (!cg) {
-                       /* If cg is NULL, only the SR_CONF_DEVICE_OPTIONS that are not
-                        * specific to a channel group must be returned. */
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                        return SR_OK;
-               }
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       analog_devopts, ARRAY_SIZE(analog_devopts),
-                       sizeof(uint32_t));
+               if (!cg)
+                       return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
+               *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog));
                break;
        case SR_CONF_COUPLING:
-               *data = g_variant_new_strv(*model->coupling_options,
-                          g_strv_length((char **)*model->coupling_options));
+               if (!model)
+                       return SR_ERR_ARG;
+               *data = g_variant_new_strv(*model->coupling_options, model->num_coupling_options);
                break;
        case SR_CONF_TRIGGER_SOURCE:
                if (!model)
                        return SR_ERR_ARG;
-               *data = g_variant_new_strv(*model->trigger_sources,
-                          g_strv_length((char **)*model->trigger_sources));
+               *data = g_variant_new_strv(*model->trigger_sources, model->num_trigger_sources);
                break;
        case SR_CONF_TRIGGER_SLOPE:
                if (!model)
                        return SR_ERR_ARG;
-               *data = g_variant_new_strv(*model->trigger_slopes,
-                          g_strv_length((char **)*model->trigger_slopes));
+               *data = g_variant_new_strv(*model->trigger_slopes, model->num_trigger_slopes);
                break;
        case SR_CONF_TIMEBASE:
                if (!model)
                        return SR_ERR_ARG;
-               *data = build_tuples(model->timebases, model->num_timebases);
+               *data = std_gvar_tuple_array(*model->timebases, model->num_timebases);
                break;
        case SR_CONF_VDIV:
                if (!model)
                        return SR_ERR_ARG;
-               *data = build_tuples(model->vdivs, model->num_vdivs);
+               *data = std_gvar_tuple_array(*model->vdivs, model->num_vdivs);
                break;
        default:
                return SR_ERR_NA;
        }
+
        return SR_OK;
 }
 
@@ -495,20 +361,25 @@ SR_PRIV int lecroy_xstream_request_data(const struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
+       /*
+        * We may be left with an invalid current_channel if acquisition was
+        * already stopped but we are processing the last pending events.
+        */
+       if (!devc->current_channel)
+               return SR_ERR_NA;
+
        ch = devc->current_channel->data;
 
        if (ch->type != SR_CHANNEL_ANALOG)
                return SR_ERR;
 
-       g_snprintf(command, sizeof(command),
-               "COMM_FORMAT DEF9,WORD,BIN;C%d:WAVEFORM?", ch->index + 1);
+       g_snprintf(command, sizeof(command), "C%d:WAVEFORM?", ch->index + 1);
        return sr_scpi_send(sdi->conn, command);
 }
 
 static int setup_channels(const struct sr_dev_inst *sdi)
 {
        GSList *l;
-       gboolean setup_changed;
        char command[MAX_COMMAND_SIZE];
        struct scope_state *state;
        struct sr_channel *ch;
@@ -518,7 +389,6 @@ static int setup_channels(const struct sr_dev_inst *sdi)
        devc = sdi->priv;
        scpi = sdi->conn;
        state = devc->model_state;
-       setup_changed = FALSE;
 
        for (l = sdi->channels; l; l = l->next) {
                ch = l->data;
@@ -527,21 +397,18 @@ static int setup_channels(const struct sr_dev_inst *sdi)
                        if (ch->enabled == state->analog_channels[ch->index].state)
                                break;
                        g_snprintf(command, sizeof(command), "C%d:TRACE %s",
-                                  ch->index + 1, ch->enabled ? "ON" : "OFF");
+                                       ch->index + 1, ch->enabled ? "ON" : "OFF");
 
                        if (sr_scpi_send(scpi, command) != SR_OK)
                                return SR_ERR;
+
                        state->analog_channels[ch->index].state = ch->enabled;
-                       setup_changed = TRUE;
                        break;
                default:
                        return SR_ERR;
                }
        }
 
-       if (setup_changed && lecroy_xstream_update_sample_rate(sdi) != SR_OK)
-               return SR_ERR;
-
        return SR_OK;
 }
 
@@ -550,39 +417,32 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        GSList *l;
        struct sr_channel *ch;
        struct dev_context *devc;
+       struct scope_state *state;
        int ret;
        struct sr_scpi_dev_inst *scpi;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
        scpi = sdi->conn;
+
        /* Preset empty results. */
        g_slist_free(devc->enabled_channels);
        devc->enabled_channels = NULL;
+       state = devc->model_state;
+       state->sample_rate = 0;
 
-       /*
-        * Contruct the list of enabled channels. Determine the highest
-        * number of digital pods involved in the acquisition.
-        */
-
+       /* Contruct the list of enabled channels. */
        for (l = sdi->channels; l; l = l->next) {
                ch = l->data;
                if (!ch->enabled)
                        continue;
-               /* Only add a single digital channel per group (pod). */
-               devc->enabled_channels = g_slist_append(
-                       devc->enabled_channels, ch);
+
+               devc->enabled_channels = g_slist_append(devc->enabled_channels, ch);
        }
 
        if (!devc->enabled_channels)
                return SR_ERR;
 
-       /*
-        * Configure the analog channels and the
-        * corresponding digital pods.
-        */
+       /* Configure the analog channels. */
        if (setup_channels(sdi) != SR_OK) {
                sr_err("Failed to setup channel configuration!");
                ret = SR_ERR;
@@ -616,9 +476,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 
        std_session_send_df_end(sdi);
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        devc->num_frames = 0;
@@ -641,6 +498,7 @@ static struct sr_dev_driver lecroy_xstream_driver_info = {
        .dev_clear = dev_clear,
        .config_get = config_get,
        .config_set = config_set,
+       .config_channel_set = config_channel_set,
        .config_list = config_list,
        .dev_open = dev_open,
        .dev_close = dev_close,
index ec7e958c932c871e79fbff32d03747cf0da41fdb..8a2129a71bb28ecd3b5291cba8f1a3e8bcdd8897 100644 (file)
@@ -75,37 +75,28 @@ struct lecroy_wavedesc {
 } __attribute__((packed));
 
 static const char *coupling_options[] = {
-       "A1M", // AC with 1 MOhm termination
-       "D50", // DC with 50 Ohm termination
-       "D1M", // DC with 1 MOhm termination
+       "A1M", ///< AC with 1 MOhm termination
+       "D50", ///< DC with 50 Ohm termination
+       "D1M", ///< DC with 1 MOhm termination
        "GND",
        "OVL",
-       NULL,
 };
 
 static const char *scope_trigger_slopes[] = {
-       "POS",
-       "NEG",
-       NULL,
+       "POS", "NEG",
 };
 
 static const char *trigger_sources[] = {
-       "C1",
-       "C2",
-       "C3",
-       "C4",
-       "LINE",
-       "EXT",
-       NULL,
+       "C1", "C2", "C3", "C4", "LINE", "EXT",
 };
 
-static const struct sr_rational timebases[] = {
+static const uint64_t timebases[][2] = {
        /* picoseconds */
-       { 20, 1000000000000 },
-       { 50, 1000000000000 },
-       { 100, 1000000000000 },
-       { 200, 1000000000000 },
-       { 500, 1000000000000 },
+       { 20, UINT64_C(1000000000000) },
+       { 50, UINT64_C(1000000000000) },
+       { 100, UINT64_C(1000000000000) },
+       { 200, UINT64_C(1000000000000) },
+       { 500, UINT64_C(1000000000000) },
        /* nanoseconds */
        { 1, 1000000000 },
        { 2, 1000000000 },
@@ -149,7 +140,7 @@ static const struct sr_rational timebases[] = {
        { 1000, 1 },
 };
 
-static const struct sr_rational vdivs[] = {
+static const uint64_t vdivs[][2] = {
        /* millivolts */
        { 1, 1000 },
        { 2, 1000 },
@@ -170,27 +161,30 @@ static const struct sr_rational vdivs[] = {
 };
 
 static const char *scope_analog_channel_names[] = {
-       "CH1",
-       "CH2",
-       "CH3",
-       "CH4",
+       "CH1", "CH2", "CH3", "CH4",
 };
 
 static const struct scope_config scope_models[] = {
        {
-               .name = { "WP7000", "WP7100", "WP7200", "WP7300" },
+                /* Default config */
+               .name = {NULL},
 
                .analog_channels = 4,
                .analog_names = &scope_analog_channel_names,
 
                .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
+
                .trigger_sources = &trigger_sources,
+               .num_trigger_sources = ARRAY_SIZE(trigger_sources),
+
                .trigger_slopes = &scope_trigger_slopes,
+               .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes),
 
-               .timebases = timebases,
+               .timebases = &timebases,
                .num_timebases = ARRAY_SIZE(timebases),
 
-               .vdivs = vdivs,
+               .vdivs = &vdivs,
                .num_vdivs = ARRAY_SIZE(vdivs),
 
                .num_xdivs = 10,
@@ -199,22 +193,22 @@ static const struct scope_config scope_models[] = {
 };
 
 static void scope_state_dump(const struct scope_config *config,
-                            struct scope_state *state)
+               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].p,
-                                       config->vdivs[state->analog_channels[i].vdiv].q);
+               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);
+                               i + 1, state->analog_channels[i].state ? "On" : "Off",
+                               (*config->coupling_options)[state->analog_channels[i].coupling],
+                               tmp, state->analog_channels[i].vertical_offset);
        }
 
-       tmp = sr_period_string(config->timebases[state->timebase].p,
-                               config->timebases[state->timebase].q);
+       tmp = sr_period_string((*config->timebases)[state->timebase][0],
+                       (*config->timebases)[state->timebase][1]);
        sr_info("Current timebase: %s", tmp);
        g_free(tmp);
 
@@ -223,17 +217,17 @@ static void scope_state_dump(const struct scope_config *config,
        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);
+                       (*config->trigger_sources)[state->trigger_source],
+                       (*config->trigger_slopes)[state->trigger_slope],
+                       state->horiz_triggerpos);
 }
 
 static int scope_state_get_array_option(const char *resp,
-                                       const char *(*array)[], int *result)
+               const char *(*array)[], unsigned int n, int *result)
 {
        unsigned int i;
 
-       for (i = 0; (*array)[i]; i++) {
+       for (i = 0; i < n; i++) {
                if (!g_strcmp0(resp, (*array)[i])) {
                        *result = i;
                        return SR_OK;
@@ -254,16 +248,18 @@ static int scope_state_get_array_option(const char *resp,
  *
  * @return SR_ERR on any parsing error, SR_OK otherwise.
  */
-static int array_float_get(gchar *value, const struct sr_rational *aval,
+static int array_float_get(gchar *value, const uint64_t array[][2],
                int array_len, unsigned int *result)
 {
        struct sr_rational rval;
+       struct sr_rational aval;
 
        if (sr_parse_rational(value, &rval) != SR_OK)
                return SR_ERR;
 
        for (int i = 0; i < array_len; i++) {
-               if (sr_rational_eq(&rval, aval + i)) {
+               sr_rational_set(&aval, array[i][0], array[i][1]);
+               if (sr_rational_eq(&rval, &aval)) {
                        *result = i;
                        return SR_OK;
                }
@@ -273,8 +269,7 @@ static int array_float_get(gchar *value, const struct sr_rational *aval,
 }
 
 static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
-                                   const struct scope_config *config,
-                                   struct scope_state *state)
+               const struct scope_config *config, struct scope_state *state)
 {
        unsigned int i, j;
        char command[MAX_COMMAND_SIZE];
@@ -292,7 +287,7 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
                if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK)
                        return SR_ERR;
 
-                if (array_float_get(tmp_str, vdivs, ARRAY_SIZE(vdivs), &j) != SR_OK) {
+               if (array_float_get(tmp_str, ARRAY_AND_SIZE(vdivs), &j) != SR_OK) {
                        g_free(tmp_str);
                        sr_err("Could not determine array index for vertical div scale.");
                        return SR_ERR;
@@ -313,6 +308,7 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
 
 
                if (scope_state_get_array_option(tmp_str, config->coupling_options,
+                                config->num_coupling_options,
                                 &state->analog_channels[i].coupling) != SR_OK)
                        return SR_ERR;
 
@@ -322,24 +318,70 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
        return SR_OK;
 }
 
-SR_PRIV int lecroy_xstream_update_sample_rate(const struct sr_dev_inst *sdi)
+SR_PRIV int lecroy_xstream_channel_state_set(const struct sr_dev_inst *sdi,
+               const int ch_index, gboolean ch_state)
+{
+       GSList *l;
+       struct sr_channel *ch;
+       struct dev_context *devc = NULL;
+       struct scope_state *state;
+       char command[MAX_COMMAND_SIZE];
+       gboolean chan_found;
+       int result;
+
+       result = SR_OK;
+
+       devc = sdi->priv;
+       state = devc->model_state;
+       chan_found = FALSE;
+
+       for (l = sdi->channels; l; l = l->next) {
+               ch = l->data;
+
+               switch (ch->type) {
+               case SR_CHANNEL_ANALOG:
+                       if (ch->index == ch_index) {
+                               g_snprintf(command, sizeof(command), "C%d:TRACE %s", ch_index + 1,
+                                               (ch_state ? "ON" : "OFF"));
+                               if ((sr_scpi_send(sdi->conn, command) != SR_OK ||
+                                               sr_scpi_get_opc(sdi->conn) != SR_OK)) {
+                                       result = SR_ERR;
+                                       break;
+                               }
+
+                               ch->enabled = ch_state;
+                               state->analog_channels[ch->index].state = ch_state;
+                               chan_found = TRUE;
+                               break;
+                       }
+                       break;
+               default:
+                       result = SR_ERR_NA;
+               }
+       }
+
+       if ((result == SR_OK) && !chan_found)
+               result = SR_ERR_BUG;
+
+       return result;
+}
+
+SR_PRIV int lecroy_xstream_update_sample_rate(const struct sr_dev_inst *sdi,
+               int num_of_samples)
 {
        struct dev_context *devc;
        struct scope_state *state;
        const struct scope_config *config;
-       float memsize, timediv;
+       double time_div;
 
        devc = sdi->priv;
-       state = devc->model_state;
        config = devc->model_config;
+       state = devc->model_state;
 
-       if (sr_scpi_get_float(sdi->conn, "MEMORY_SIZE?", &memsize) != SR_OK)
-               return SR_ERR;
-
-       if (sr_scpi_get_float(sdi->conn, "TIME_DIV?", &timediv) != SR_OK)
+       if (sr_scpi_get_double(sdi->conn, "TIME_DIV?", &time_div) != SR_OK)
                return SR_ERR;
 
-       state->sample_rate = 1 / ((timediv * config->num_xdivs) / memsize);
+       state->sample_rate = num_of_samples / (time_div * config->num_xdivs);
 
        return SR_OK;
 }
@@ -366,7 +408,7 @@ SR_PRIV int lecroy_xstream_state_get(struct sr_dev_inst *sdi)
        if (sr_scpi_get_string(sdi->conn, "TIME_DIV?", &tmp_str) != SR_OK)
                return SR_ERR;
 
-       if (array_float_get(tmp_str, timebases, ARRAY_SIZE(timebases), &i) != SR_OK) {
+       if (array_float_get(tmp_str, ARRAY_AND_SIZE(timebases), &i) != SR_OK) {
                g_free(tmp_str);
                sr_err("Could not determine array index for timbase scale.");
                return SR_ERR;
@@ -394,21 +436,20 @@ SR_PRIV int lecroy_xstream_state_get(struct sr_dev_inst *sdi)
                i++;
        }
 
-       if (!trig_source || scope_state_get_array_option(trig_source, config->trigger_sources, &state->trigger_source) != SR_OK)
+       if (!trig_source || scope_state_get_array_option(trig_source,
+                       config->trigger_sources, config->num_trigger_sources,
+                       &state->trigger_source) != SR_OK)
                return SR_ERR;
 
        g_snprintf(command, sizeof(command), "%s:TRIG_SLOPE?", trig_source);
        if (sr_scpi_get_string(sdi->conn, command, &tmp_str) != SR_OK)
                return SR_ERR;
 
-       if (scope_state_get_array_option(tmp_str,
-               config->trigger_slopes, &state->trigger_slope) != SR_OK)
+       if (scope_state_get_array_option(tmp_str, config->trigger_slopes,
+                       config->num_trigger_slopes, &state->trigger_slope) != SR_OK)
                return SR_ERR;
 
-       if (sr_scpi_get_float(sdi->conn, "TRIG_DELAY?", &state->horiz_triggerpos) != SR_OK)
-               return SR_ERR;
-
-       if (lecroy_xstream_update_sample_rate(sdi) != SR_OK)
+       if (sr_scpi_get_float(sdi->conn, "TRIG_DELAY?", &state->horiz_triggerpos) != SR_OK)
                return SR_ERR;
 
        sr_info("Fetching finished.");
@@ -459,10 +500,16 @@ SR_PRIV int lecroy_xstream_init_device(struct sr_dev_inst *sdi)
        }
 
        if (model_index == -1) {
-               sr_dbg("Unsupported LeCroy device.");
-               return SR_ERR_NA;
+               sr_dbg("Unknown LeCroy device, using default config.");
+               for (i = 0; i < ARRAY_SIZE(scope_models); i++)
+                       if (scope_models[i].name[0] == NULL)
+                               model_index = i;
        }
 
+       /* Set the desired response and format modes. */
+       sr_scpi_send(sdi->conn, "COMM_HEADER OFF");
+       sr_scpi_send(sdi->conn, "COMM_FORMAT DEF9,WORD,BIN");
+
        devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) *
                                scope_models[model_index].analog_channels);
 
@@ -476,7 +523,7 @@ SR_PRIV int lecroy_xstream_init_device(struct sr_dev_inst *sdi)
                g_snprintf(command, sizeof(command), "C%d:VDIV?", i + 1);
 
                ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, channel_enabled,
-                          (*scope_models[model_index].analog_names)[i]);
+                       (*scope_models[model_index].analog_names)[i]);
 
                devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
 
@@ -485,24 +532,18 @@ SR_PRIV int lecroy_xstream_init_device(struct sr_dev_inst *sdi)
                devc->analog_groups[i]->channels = g_slist_append(NULL, ch);
 
                sdi->channel_groups = g_slist_append(sdi->channel_groups,
-                                                  devc->analog_groups[i]);
+                       devc->analog_groups[i]);
        }
 
        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;
-
-       /* Set the desired response mode. */
-       sr_scpi_send(sdi->conn, "COMM_HEADER OFF,WORD,BIN");
+       devc->model_state = scope_state_new(devc->model_config);
 
        return SR_OK;
 }
 
 static int lecroy_waveform_2_x_to_analog(GByteArray *data,
-                                        struct lecroy_wavedesc *desc,
-                                        struct sr_datafeed_analog *analog)
+               struct lecroy_wavedesc *desc, struct sr_datafeed_analog *analog)
 {
        struct sr_analog_encoding *encoding = analog->encoding;
        struct sr_analog_meaning *meaning = analog->meaning;
@@ -514,9 +555,9 @@ static int lecroy_waveform_2_x_to_analog(GByteArray *data,
        data_float = g_malloc(desc->version_2_x.wave_array_count * sizeof(float));
        num_samples = desc->version_2_x.wave_array_count;
 
-       waveform_data = (int16_t *)(data->data +
-                                   + desc->version_2_x.wave_descriptor_length
-                                   + desc->version_2_x.user_text_len);
+       waveform_data = (int16_t*)(data->data +
+               + desc->version_2_x.wave_descriptor_length
+               + desc->version_2_x.user_text_len);
 
        for (i = 0; i < num_samples; i++)
                data_float[i] = (float)waveform_data[i]
@@ -554,31 +595,31 @@ static int lecroy_waveform_2_x_to_analog(GByteArray *data,
 }
 
 static int lecroy_waveform_to_analog(GByteArray *data,
-                                    struct sr_datafeed_analog *analog)
+               struct sr_datafeed_analog *analog)
 {
        struct lecroy_wavedesc *desc;
 
        if (data->len < sizeof(struct lecroy_wavedesc))
                return SR_ERR;
 
-       desc = (struct lecroy_wavedesc *)data->data;
+       desc = (struct lecroy_wavedesc*)data->data;
 
        if (!strncmp(desc->template_name, "LECROY_2_2", 16) ||
            !strncmp(desc->template_name, "LECROY_2_3", 16)) {
                return lecroy_waveform_2_x_to_analog(data, desc, analog);
        }
 
-       sr_err("Waveformat template '%.16s' not supported.",
-              desc->template_name);
-
+       sr_err("Waveformat template '%.16s' not supported.", desc->template_name);
        return SR_ERR;
 }
 
 SR_PRIV int lecroy_xstream_receive_data(int fd, int revents, void *cb_data)
 {
+       char command[MAX_COMMAND_SIZE];
        struct sr_channel *ch;
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
+       struct scope_state *state;
        struct sr_datafeed_packet packet;
        GByteArray *data;
        struct sr_datafeed_analog analog;
@@ -599,22 +640,14 @@ SR_PRIV int lecroy_xstream_receive_data(int fd, int revents, void *cb_data)
                return TRUE;
 
        ch = devc->current_channel->data;
-
-       /*
-        * Send "frame begin" packet upon reception of data for the
-        * first enabled channel.
-        */
-       if (devc->current_channel == devc->enabled_channels) {
-               packet.type = SR_DF_FRAME_BEGIN;
-               sr_session_send(sdi, &packet);
-       }
+       state = devc->model_state;
 
        if (ch->type != SR_CHANNEL_ANALOG)
                return SR_ERR;
 
        /* Pass on the received data of the channel(s). */
        if (sr_scpi_read_data(sdi->conn, buf, 4) != 4) {
-               sr_err("Reading header failed.");
+               sr_err("Reading header failed, scope probably didn't send any data.");
                return TRUE;
        }
 
@@ -631,6 +664,33 @@ SR_PRIV int lecroy_xstream_receive_data(int fd, int revents, void *cb_data)
        if (lecroy_waveform_to_analog(data, &analog) != SR_OK)
                return SR_ERR;
 
+       if (analog.num_samples == 0) {
+               g_free(analog.data);
+
+               /* No data available, we have to acquire data first. */
+               g_snprintf(command, sizeof(command), "ARM;WAIT;*OPC;C%d:WAVEFORM?", ch->index + 1);
+               sr_scpi_send(sdi->conn, command);
+
+               state->sample_rate = 0;
+               return TRUE;
+       } else {
+               /* Update sample rate if needed. */
+               if (state->sample_rate == 0)
+                       if (lecroy_xstream_update_sample_rate(sdi, analog.num_samples) != SR_OK) {
+                               g_free(analog.data);
+                               return SR_ERR;
+                       }
+       }
+
+       /*
+        * Send "frame begin" packet upon reception of data for the
+        * first enabled channel.
+        */
+       if (devc->current_channel == devc->enabled_channels) {
+               packet.type = SR_DF_FRAME_BEGIN;
+               sr_session_send(sdi, &packet);
+       }
+
        meaning.channels = g_slist_append(NULL, ch);
        packet.payload = &analog;
        packet.type = SR_DF_ANALOG;
@@ -661,10 +721,16 @@ SR_PRIV int lecroy_xstream_receive_data(int fd, int revents, void *cb_data)
         * number of frames, or continue reception by starting over at
         * the first enabled channel.
         */
-       if (++devc->num_frames == devc->frame_limit) {
-               sdi->driver->dev_acquisition_stop(sdi);
+       devc->num_frames++;
+       if (devc->frame_limit && (devc->num_frames == devc->frame_limit)) {
+               sr_dev_acquisition_stop(sdi);
        } else {
                devc->current_channel = devc->enabled_channels;
+
+               /* Wait for trigger, then begin fetching data. */
+               g_snprintf(command, sizeof(command), "ARM;WAIT;*OPC");
+               sr_scpi_send(sdi->conn, command);
+
                lecroy_xstream_request_data(sdi);
        }
 
index 0200c197d4b3659ff7dc0572bfbdc32e2a482e4f..f639dfffa665fd4fa7bf8549d044d1bb302b7be4 100644 (file)
@@ -45,11 +45,12 @@ struct scope_config {
        const uint8_t num_trigger_sources;
 
        const char *(*trigger_slopes)[];
+       const uint8_t num_trigger_slopes;
 
-       const struct sr_rational *timebases;
+       const uint64_t (*timebases)[][2];
        const uint8_t num_timebases;
 
-       const struct sr_rational *vdivs;
+       const uint64_t (*vdivs)[][2];
        const uint8_t num_vdivs;
 
        const uint8_t num_xdivs;
@@ -76,7 +77,6 @@ struct scope_state {
        uint64_t sample_rate;
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        const void *model_config;
        void *model_state;
@@ -96,6 +96,9 @@ SR_PRIV int lecroy_xstream_receive_data(int fd, int revents, void *cb_data);
 
 SR_PRIV void lecroy_xstream_state_free(struct scope_state *state);
 SR_PRIV int lecroy_xstream_state_get(struct sr_dev_inst *sdi);
-SR_PRIV int lecroy_xstream_update_sample_rate(const struct sr_dev_inst *sdi);
+SR_PRIV int lecroy_xstream_channel_state_set(const struct sr_dev_inst *sdi,
+               const int ch_index, gboolean ch_state);
+SR_PRIV int lecroy_xstream_update_sample_rate(const struct sr_dev_inst *sdi,
+               int num_of_samples);
 
 #endif
index 7924b2311a2b1a4f72b056f37253682454243a8d..dd3ff3c80c8923195b18f253f16f4b560f0f61cd 100644 (file)
 #include <config.h>
 #include "protocol.h"
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_OSCILLOSCOPE,
        SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_TRIGGER_TYPE | SR_CONF_LIST,
@@ -50,6 +53,10 @@ static const uint64_t samplerates[] = {
        SR_HZ(100),
 };
 
+static const char *trigger_slopes[2] = {
+       "r", "f",
+};
+
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
        int i;
@@ -180,8 +187,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        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);
@@ -201,8 +206,8 @@ static int dev_open(struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(int key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -224,10 +229,9 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
        return SR_OK;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(int key, 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;
@@ -238,82 +242,51 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        switch (key) {
        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;
-                       ret = SR_OK;
+                       return SR_ERR_ARG;
                }
+               devc->limit_samples = num_samples;
                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;
-               }
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
+                       return SR_ERR_ARG;
+               devc->trigger_slope = idx;
                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;
+                       return SR_ERR_ARG;
                }
+               trigger_pos = (int)pos;
+               devc->trigger_holdoff[0] = trigger_pos & 0xff;
                break;
        case SR_CONF_RLE:
-               ret = SR_OK;
                break;
        default:
-               ret = SR_ERR_NA;
-               break;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+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_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
                break;
        case SR_CONF_TRIGGER_TYPE:
                *data = g_variant_new_string(TRIGGER_TYPE);
@@ -330,9 +303,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        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) {
@@ -391,7 +361,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-/* This stops acquisition on ALL devices, ignoring dev_index. */
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        stop_acquisition(sdi);
@@ -407,6 +376,7 @@ static struct sr_dev_driver link_mso19_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 29aacbdab0642b33b32df487f343df5b4cf9aa53..8721ec2b0c4761e4abf675de1e0a722821575057 100644 (file)
@@ -48,8 +48,8 @@ SR_PRIV int mso_send_control_message(struct sr_serial_dev_inst *serial,
        p += sizeof(mso_head);
 
        for (i = 0; i < n; i++) {
-               *(uint16_t *) p = g_htons(payload[i]);
-               p += 2;
+               WB16(p, payload[i]);
+               p += sizeof(uint16_t);
        }
        memcpy(p, mso_foot, sizeof(mso_foot));
 
@@ -405,7 +405,7 @@ SR_PRIV int mso_receive_data(int fd, int revents, void *cb_data)
 
        if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
                sr_info("Requested number of samples reached.");
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        }
 
        return TRUE;
index 4c8a66b26a22f0e241501be5dcc2e50f27492cea..d6eb872b8a9d14f7dd2f0aef89e5e7524cbb4907 100644 (file)
@@ -84,7 +84,6 @@ struct mso_prototrig {
        uint8_t spimode;
 };
 
-/* Private, per-device-instance driver context. */
 struct dev_context {
        /* info */
        uint8_t hwmodel;
@@ -97,10 +96,11 @@ struct dev_context {
        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;
@@ -200,12 +200,7 @@ static const struct rate_map rate_map[] = {
 
 /* FIXME: Determine corresponding voltages */
 static const uint16_t la_threshold_map[] = {
-       0x8600,
-       0x8770,
-       0x88ff,
-       0x8c70,
-       0x8eff,
-       0x8fff,
+       0x8600, 0x8770, 0x88ff, 0x8c70, 0x8eff, 0x8fff,
 };
 
 #endif
index ee478033f3cd2b16fa25e0a59c7409d3fc5d2e3d..765d9abc78682a905cde53a680cab51ae7fd0414 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * <em>Manson HCS-3xxx series</em> power supply driver
- *
- * @internal
- */
-
 #include <config.h>
 #include "protocol.h"
 
-static const uint32_t drvopts[] = {
-       /* Device class */
-       SR_CONF_POWER_SUPPLY,
-};
-
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
-       /* Device class */
+static const uint32_t drvopts[] = {
        SR_CONF_POWER_SUPPLY,
-       /* Acquisition modes. */
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
-       /* Device configuration */
        SR_CONF_VOLTAGE | SR_CONF_GET,
        SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_CURRENT | SR_CONF_GET,
@@ -66,6 +53,7 @@ static const struct hcs_model models[] = {
        { 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_3304, "HCS-3304-USB", "HCS-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 } },
@@ -138,7 +126,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        }
        g_strfreev(tokens);
 
-       /* Init device instance, etc. */
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
        sdi->vendor = g_strdup("Manson");
@@ -186,8 +173,8 @@ exit_err:
        return NULL;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -224,8 +211,8 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        gboolean bval;
@@ -233,9 +220,6 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
@@ -266,11 +250,15 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                break;
        case SR_CONF_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))
+
+               if (hcs_send_cmd(sdi->conn, "SOUT%1d\r", !bval) < 0) {
+                       sr_err("Could not send SR_CONF_ENABLED command.");
                        return SR_ERR;
+               }
+               if (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0) {
+                       sr_err("Could not read SR_CONF_ENABLED reply.");
+                       return SR_ERR;
+               }
                devc->output_enabled = bval;
                break;
        default:
@@ -280,66 +268,29 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
+       const double *a;
        struct dev_context *devc;
-       GVariant *gvar;
-       GVariantBuilder gvb;
-       double dval;
-       int idx;
 
-       (void)cg;
-
-       /* Always available (with or without sdi). */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* Return drvopts without sdi (and devopts with sdi, see below). */
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* Every other key needs an sdi. */
-       if (!sdi)
-               return SR_ERR_ARG;
-       devc = sdi->priv;
+       devc = (sdi) ? sdi->priv : NULL;
 
        switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        case SR_CONF_VOLTAGE_TARGET:
-               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);
+               if (!devc || !devc->model)
+                       return SR_ERR_ARG;
+               a = devc->model->voltage;
+               *data = std_gvar_min_max_step(a[0], devc->voltage_max_device, a[2]);
                break;
        case SR_CONF_CURRENT_LIMIT:
-               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);
+               if (!devc || !devc->model)
+                       return SR_ERR_ARG;
+               a = devc->model->current;
+               *data = std_gvar_min_max_step(a[0], devc->current_max_device, a[2]);
                break;
        default:
                return SR_ERR_NA;
@@ -353,9 +304,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->limits);
@@ -364,7 +312,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        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);
@@ -380,6 +327,7 @@ static struct sr_dev_driver manson_hcs_3xxx_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index d36e25042ccc93e2a9e59a7801ae3f3f48e6b1a4..30017d1cdcbaeea092676db753ca31bdab4d8b42 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * <em>Manson HCS-3xxx Series</em> power supply driver
- *
- * @internal
- */
-
 #include <config.h>
 #include "protocol.h"
 
@@ -236,7 +228,7 @@ SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits)) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        }
 
index 93cc54755b43410d77ffd9f7af59e84478019eb9..da47afb0c56f92eaf0e10874ef534a7005114289 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @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
 
@@ -66,7 +58,6 @@ struct hcs_model {
        double current[3]; /**< Min, max, step */
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        const struct hcs_model *model; /**< Model information. */
 
index 3ded72e9f49acabacc825dcff98bbb024dc2c9d3..54444d623c6d1484bfbe31f3da3d99d512a91c33 100644 (file)
@@ -23,7 +23,7 @@
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
        SR_CONF_SERIALCOMM,
-       SR_CONF_MODBUSADDR
+       SR_CONF_MODBUSADDR,
 };
 
 static const uint32_t drvopts[] = {
@@ -140,7 +140,7 @@ static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus)
        sdi->status = SR_ST_INACTIVE;
        sdi->vendor = g_strdup("Maynuo");
        sdi->model = g_strdup(model->name);
-       sdi->version = g_strdup_printf("v%d.%d", version/10, version%10);
+       sdi->version = g_strdup_printf("v%d.%d", version / 10, version % 10);
        sdi->conn = modbus;
        sdi->driver = &maynuo_m97_driver_info;
        sdi->inst_type = SR_INST_MODBUS;
@@ -203,8 +203,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        if (sr_modbus_open(modbus) < 0)
                return SR_ERR;
 
-       sdi->status = SR_ST_ACTIVE;
-
        maynuo_m97_set_bit(modbus, PC1, 1);
 
        return SR_OK;
@@ -215,32 +213,27 @@ static int dev_close(struct sr_dev_inst *sdi)
        struct dev_context *devc;
        struct sr_modbus_dev_inst *modbus;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        modbus = sdi->conn;
 
-       if (modbus) {
-               devc = sdi->priv;
-               if (devc->expecting_registers) {
-                       /* Wait for the last data that was requested from the device. */
-                       uint16_t registers[devc->expecting_registers];
-                       sr_modbus_read_holding_registers(modbus, -1,
-                               devc->expecting_registers, registers);
-               }
+       if (!modbus)
+               return SR_ERR_BUG;
 
-               maynuo_m97_set_bit(modbus, PC1, 0);
+       devc = sdi->priv;
 
-               if (sr_modbus_close(modbus) < 0)
-                       return SR_ERR;
-               sdi->status = SR_ST_INACTIVE;
+       if (devc->expecting_registers) {
+               /* Wait for the last data that was requested from the device. */
+               uint16_t registers[devc->expecting_registers];
+               sr_modbus_read_holding_registers(modbus, -1,
+                       devc->expecting_registers, registers);
        }
 
-       return SR_OK;
+       maynuo_m97_set_bit(modbus, PC1, 0);
+
+       return sr_modbus_close(modbus);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_modbus_dev_inst *modbus;
@@ -323,110 +316,68 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return ret;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_modbus_dev_inst *modbus;
-       int ret;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        modbus = sdi->conn;
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
-               ret = sr_sw_limits_config_set(&devc->limits, key, data);
-               break;
+               return sr_sw_limits_config_set(&devc->limits, key, data);
        case SR_CONF_ENABLED:
-               ret = maynuo_m97_set_input(modbus, g_variant_get_boolean(data));
-               break;
+               return maynuo_m97_set_input(modbus, g_variant_get_boolean(data));
        case SR_CONF_VOLTAGE_TARGET:
-               ret = maynuo_m97_set_float(modbus, UFIX, g_variant_get_double(data));
-               break;
+               return maynuo_m97_set_float(modbus, UFIX, g_variant_get_double(data));
        case SR_CONF_CURRENT_LIMIT:
-               ret = maynuo_m97_set_float(modbus, IFIX, g_variant_get_double(data));
-               break;
+               return maynuo_m97_set_float(modbus, IFIX, g_variant_get_double(data));
        case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
-               ret = maynuo_m97_set_float(modbus, UMAX, g_variant_get_double(data));
-               break;
+               return maynuo_m97_set_float(modbus, UMAX, g_variant_get_double(data));
        case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
-               ret = maynuo_m97_set_float(modbus, IMAX, g_variant_get_double(data));
-               break;
+               return maynuo_m97_set_float(modbus, IMAX, g_variant_get_double(data));
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariantBuilder gvb;
-       int ret;
-
-       /* Always available, even without sdi. */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       } else if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
 
-       if (!sdi)
-               return SR_ERR_ARG;
-       devc = sdi->priv;
+       devc = (sdi) ? sdi->priv : NULL;
 
-       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_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       break;
-               default:
-                       return SR_ERR_NA;
-               }
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        } else {
                switch (key) {
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
                        break;
                case SR_CONF_VOLTAGE_TARGET:
-                       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-                       /* Min, max, write resolution. */
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.0));
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(devc->model->max_voltage));
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.001));
-                       *data = g_variant_builder_end(&gvb);
+                       if (!devc || !devc->model)
+                               return SR_ERR_ARG;
+                       *data = std_gvar_min_max_step(0.0, devc->model->max_voltage, 0.001);
                        break;
                case SR_CONF_CURRENT_LIMIT:
-                       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-                       /* Min, max, step. */
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.0));
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(devc->model->max_current));
-                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.0001));
-                       *data = g_variant_builder_end(&gvb);
+                       if (!devc || !devc->model)
+                               return SR_ERR_ARG;
+                       *data = std_gvar_min_max_step(0.0, devc->model->max_current, 0.0001);
                        break;
                default:
                        return SR_ERR_NA;
                }
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -435,9 +386,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct sr_modbus_dev_inst *modbus;
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        modbus = sdi->conn;
        devc = sdi->priv;
 
@@ -455,9 +403,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct sr_modbus_dev_inst *modbus;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        std_session_send_df_end(sdi);
 
        modbus = sdi->conn;
@@ -474,6 +419,7 @@ static struct sr_dev_driver maynuo_m97_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 04fa294835c564916c0ee8d9e18541e3ed21b01e..c369fa6ceab689532a253239ebc784a3257c2a8a 100644 (file)
@@ -196,7 +196,7 @@ SR_PRIV int maynuo_m97_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits)) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        }
 
index a2e39f585bde5a2ef0560135c350ae5ec625e3e4..31589d901d745e19312e51b1df260efa32fa378d 100644 (file)
@@ -34,15 +34,9 @@ struct maynuo_m97_model {
        unsigned int max_power;
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        const struct maynuo_m97_model *model;
-
-       /* Acquisition settings */
        struct sr_sw_limits limits;
-
-       /* Operational state */
        int expecting_registers;
 };
 
index d755bea7e8d6e104afac3f8728f87a3d4edcf02f..9e33f0ad83f2e2cd35aa89d6e131691d4d4104c3 100644 (file)
@@ -116,61 +116,41 @@ static GSList *scan(GSList *options, int idx)
        if (!conn)
                return NULL;
 
-       if (serialcomm) {
-               /* Use the provided comm specs. */
+       if (serialcomm)
                devices = mic_scan(conn, serialcomm, idx);
-       } else {
-               /* Try the default. */
+       else
                devices = mic_scan(conn, mic_devs[idx].conn, idx);
-       }
 
        return devices;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg, int idx)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg, int idx)
 {
-       (void)cg;
-
-       switch (key) {
-       case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi && !mic_devs[idx].has_humidity) {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts_temp, ARRAY_SIZE(drvopts_temp),
-                               sizeof(uint32_t));
-               } else if (!sdi && mic_devs[idx].has_humidity) {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts_temp_hum, ARRAY_SIZE(drvopts_temp_hum),
-                               sizeof(uint32_t));
-               } else {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               }
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       /*
+        * We can't use the ternary operator here! The result would contain
+        * sizeof((cond) ? A : B) where A/B are arrays of different type/size.
+        * The ternary operator always returns the "common" type of A and B,
+        * which would be a pointer instead of either the A or B arrays.
+        * Thus, sizeof() would yield the size of a pointer, not the size
+        * of either the A or B array, which is not what we want.
+        */
+       if (mic_devs[idx].has_humidity)
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts_temp_hum, devopts);
+       else
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts_temp, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, int idx)
@@ -178,15 +158,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, 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;
 
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -219,6 +195,7 @@ static struct sr_dev_driver ID##_driver_info = { \
        .cleanup = std_cleanup, \
        .scan = scan_##ID_UPPER, \
        .dev_list = std_dev_list, \
+       .dev_clear = std_dev_clear, \
        .config_get = NULL, \
        .config_set = config_set, \
        .config_list = config_list_##ID_UPPER, \
index 65490537609f03f936f261f8ad12065e2621cfe9..2218f32b2908d3f3538db02489fa5429a2da83b1 100644 (file)
@@ -150,7 +150,7 @@ 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;
+       int len, offset;
 
        devc = sdi->priv;
        serial = sdi->conn;
@@ -166,6 +166,7 @@ static void handle_new_data(struct sr_dev_inst *sdi, int idx)
        devc->buflen += len;
 
        /* Now look for packets in that data. */
+       offset = 0;
        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);
@@ -176,8 +177,8 @@ static void handle_new_data(struct sr_dev_inst *sdi, int idx)
        }
 
        /* 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];
+       if (offset < devc->buflen)
+               memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
        devc->buflen -= offset;
 }
 
@@ -210,7 +211,7 @@ static int receive_data(int fd, int revents, int idx, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index d4ad06d914b2d067055966584c385f37f238bb7d..c9ba0944a6aaf68004d2bf1e74fe069d485ec90f 100644 (file)
@@ -51,7 +51,6 @@ extern SR_PRIV const struct mic_dev_info mic_devs[];
 
 #define SERIAL_BUFSIZE 256
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits limits;
 
index 4ec35ff58f35b3a203c7edfd2b7928e7e399bb67..46407cc5f01cd95b498bad0eb744fd9d4a385cd9 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * <em>Motech LPS-30x series</em> power supply driver
- *
- * @internal
- */
-
 #include <config.h>
 #include <ctype.h>
 #include <math.h>
 #include <string.h>
 #include "protocol.h"
+
 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 capabilities generic. */
-static const uint32_t drvopts[] = {
-       /* Device class */
-       SR_CONF_POWER_SUPPLY,
-};
-
-/** Driver scanning options. */
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
        SR_CONF_SERIALCOMM,
 };
 
-/** Hardware capabilities generic. */
-static const uint32_t devopts[] = {
-       /* Device class */
+static const uint32_t drvopts[] = {
        SR_CONF_POWER_SUPPLY,
-       /* Acquisition modes. */
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
-       /* Device configuration */
        SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
 /** Hardware capabilities channel 1, 2. */
-static const uint32_t devopts_ch12[] = {
+static const uint32_t devopts_cg_ch12[] = {
        SR_CONF_VOLTAGE | SR_CONF_GET,
        SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_CURRENT | SR_CONF_GET,
@@ -75,8 +57,8 @@ static const uint32_t devopts_ch12[] = {
        SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
 };
 
-/** Hardware capabilities channel 3. (LPS-304/305 only). */
-static const uint32_t devopts_ch3[] = {
+/** Hardware capabilities channel 3 (LPS-304/305 only). */
+static const uint32_t devopts_cg_ch3[] = {
        SR_CONF_VOLTAGE | SR_CONF_GET,
        SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
 };
@@ -133,8 +115,7 @@ static const struct lps_modelspec models[] = {
        },
 };
 
-/** Send command to device with va_list.
- */
+/** 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;
@@ -155,8 +136,7 @@ SR_PRIV int lps_send_va(struct sr_serial_dev_inst *serial, const char *fmt, va_l
        return SR_OK;
 }
 
-/** Send command to device.
- */
+/** Send command to device. */
 SR_PRIV int lps_send_req(struct sr_serial_dev_inst *serial, const char *fmt, ...)
 {
        int retc;
@@ -367,8 +347,7 @@ SR_PRIV int lps_read_reply(struct sr_serial_dev_inst *serial, char **buf, int *b
        return SR_ERR; /* Timeout! */
 }
 
-/** Scan for LPS-300 series device.
- */
+/** 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;
@@ -386,8 +365,6 @@ static GSList *do_scan(lps_modelid modelid, struct sr_dev_driver *drv, GSList *o
        devc = NULL;
        conn = serialcomm = NULL;
 
-       sr_spew("scan() called!");
-
        /* Process and check options. */
        if (sr_serial_extract_options(options, &conn, &serialcomm) != SR_OK)
                return NULL;
@@ -444,7 +421,7 @@ static GSList *do_scan(lps_modelid modelid, struct sr_dev_driver *drv, GSList *o
 
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
-       sdi->vendor = g_strdup(VENDOR_MOTECH);
+       sdi->vendor = g_strdup("Motech");
        sdi->model = g_strdup(models[modelid].modelstr);
        sdi->version = g_strdup(verstr);
        sdi->inst_type = SR_INST_SERIAL;
@@ -464,7 +441,7 @@ static GSList *do_scan(lps_modelid modelid, struct sr_dev_driver *drv, GSList *o
                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);
+               snprintf(channel, sizeof(channel), "CG%d", cnt + 1);
                cg->name = g_strdup(channel);
                cg->priv = NULL;
                cg->channels = g_slist_append(NULL, ch);
@@ -498,7 +475,7 @@ static GSList *scan_lps301(struct sr_dev_driver *di, GSList *options)
        return do_scan(LPS_301, di, options);
 }
 
-static void dev_clear_private(struct dev_context *devc)
+static void clear_helper(struct dev_context *devc)
 {
        int ch_idx;
 
@@ -507,13 +484,13 @@ static void dev_clear_private(struct dev_context *devc)
                g_slist_free(devc->channel_status[ch_idx].info);
 }
 
-static int dev_clear_lps301(const struct sr_dev_driver *di)
+static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, (std_dev_clear_callback)dev_clear_private);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_channel *ch;
@@ -525,7 +502,6 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        devc = sdi->priv;
 
        if (!cg) {
-               /* No channel group: global options. */
                switch (key) {
                case SR_CONF_LIMIT_SAMPLES:
                case SR_CONF_LIMIT_MSEC:
@@ -540,6 +516,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                /* 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_VOLTAGE:
                        *data = g_variant_new_double(devc->channel_status[ch_idx].output_voltage_last);
@@ -564,20 +541,15 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        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;
 
@@ -587,34 +559,25 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                return SR_ERR_NA;
 
        if (!cg) {
-               /* No channel group: global options. */
                switch (key) {
                case SR_CONF_LIMIT_MSEC:
                case SR_CONF_LIMIT_SAMPLES:
                        return sr_sw_limits_config_set(&devc->limits, key, data);
                case SR_CONF_CHANNEL_CONFIG:
-                       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)
+                       if ((idx = std_str_idx(data, ARRAY_AND_SIZE(channel_modes))) < 0)
                                return SR_ERR_ARG;
+                       if (devc->model->modelid <= LPS_303 && idx != 0)
+                               break; /* Only first setting possible for smaller models. */
+                       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);
                        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;
@@ -649,7 +612,6 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                                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_ENABLED:
                        bval = g_variant_get_boolean(data);
                        if (bval == devc->channel_status[ch_idx].output_enabled) /* Nothing to do. */
@@ -673,48 +635,29 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_channel *ch;
-       int ch_idx, i;
-       GVariant *gvar;
-       GVariantBuilder gvb;
+       int ch_idx;
 
-       /* Driver options, no device instance necessary. */
-       switch (key) {
-       case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       case SR_CONF_DEVICE_OPTIONS:
-               if (sdi)
-                       break;
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       default:
-               if (!sdi)
-                       return SR_ERR_ARG;
-               devc = sdi->priv;
-               break;
-       }
+       devc = (sdi) ? sdi->priv : NULL;
 
-       /* Device options, independent from channel groups. */
        if (!cg) {
                switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       return SR_OK;
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
                case SR_CONF_CHANNEL_CONFIG:
+                       if (!devc || !devc->model)
+                               return SR_ERR_ARG;
                        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));
+                               *data = g_variant_new_strv(ARRAY_AND_SIZE(channel_modes));
                        }
                        return SR_OK;
                default:
@@ -722,35 +665,26 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                }
        }
 
-       /* Device options, depending on channel groups. */
+       /* 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_DEVICE_OPTIONS:
                if ((ch_idx == 0) || (ch_idx == 1)) /* CH1, CH2 */
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts_ch12, ARRAY_SIZE(devopts_ch12), sizeof(uint32_t));
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_ch12));
                else /* Must be CH3 */
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts_ch3, ARRAY_SIZE(devopts_ch3), sizeof(uint32_t));
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_ch3));
                break;
        case SR_CONF_VOLTAGE_TARGET:
-               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);
+               if (!devc || !devc->model)
+                       return SR_ERR_ARG;
+               *data = std_gvar_min_max_step_array(devc->model->channels[ch_idx].voltage);
                break;
        case SR_CONF_CURRENT_LIMIT:
-               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);
+               if (!devc || !devc->model)
+                       return SR_ERR_ARG;
+               *data = std_gvar_min_max_step_array(devc->model->channels[ch_idx].current);
                break;
        default:
                return SR_ERR_NA;
@@ -764,9 +698,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        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;
@@ -792,7 +723,7 @@ static struct sr_dev_driver motech_lps_301_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan_lps301,
        .dev_list = std_dev_list,
-       .dev_clear = dev_clear_lps301,
+       .dev_clear = dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 4de6f0e9d4b5fb7546fa85a17c172ffd943b602a..b31f2ee1d33ad83bcf48f33e8881a32f55290f4b 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * <em>Motech LPS-30x series</em> power supply driver
- *
- * @internal
- */
-
 #include <config.h>
 #include <errno.h>
 #include <string.h>
@@ -84,6 +76,8 @@ static void process_line(struct sr_dev_inst *sdi)
        int auxint;
 
        devc = sdi->priv;
+       if (!devc)
+               return;
 
        switch (devc->acq_req_pending) {
        case 0: /* Should not happen... */
@@ -95,13 +89,15 @@ static void process_line(struct sr_dev_inst *sdi)
                case AQ_U2:
                case AQ_I1:
                case AQ_I2:
-                       if (sr_atod(devc->buf, &dbl) != SR_OK) {
+                       dbl = 0.0;
+                       if (sr_atod_ascii(devc->buf, &dbl) != SR_OK) {
                                sr_err("Failed to convert '%s' to double, errno=%d %s",
                                        devc->buf, errno, g_strerror(errno));
                                dbl = 0.0;
                        }
                        break;
                case AQ_STATUS:
+                       auxint = 0;
                        if (sr_atoi(devc->buf, &auxint) != SR_OK) {
                                sr_err("Failed to convert '%s' to int, errno=%d %s",
                                        devc->buf, errno, g_strerror(errno));
@@ -193,7 +189,7 @@ SR_PRIV int motech_lps_30x_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        /* Only request the next packet if required. */
        if (!((sdi->status == SR_ST_ACTIVE) && (devc->acq_running)))
index cb74148f468dcf82c75871cef191747c8239f164..53aa0325b2dfd9e04e6ae335032f2fad9364937e 100644 (file)
  * 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 <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
+#define LOG_PREFIX "motech-lps-30x"
+
 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. */
@@ -93,22 +85,17 @@ struct channel_status {
        gdouble output_current_max;
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        const struct lps_modelspec *model;
 
-       /* Acquisition status */
        gboolean acq_running;           /**< Acquisition is running. */
        struct sr_sw_limits limits;
        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. */
        gchar buf[LINELEN_MAX]; /**< Buffer for read callback */
        int buflen;             /**< Data len in buf */
index eb2ba25cf81bb3a8116b130b2c4bfb5bc86283c4..d155bc36d657b3b24f5f0817ae81caf5caa9e8b2 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/** @file
- *  Norma DM9x0/Siemens B102x DMMs driver.
- *  @internal
- */
-
 #include <config.h>
 #include "protocol.h"
 
@@ -30,8 +25,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -112,6 +110,7 @@ static GSList *scan(struct sr_dev_driver *drv, GSList *options)
                if (serial_write_blocking(serial, req, strlen(req),
                                serial_timeout(serial, strlen(req))) < 0) {
                        sr_err("Unable to send identification request.");
+                       g_free(buf);
                        return NULL;
                }
                len = BUF_MAX;
@@ -161,41 +160,22 @@ static GSList *scan(struct sr_dev_driver *drv, GSList *options)
        return std_scan_complete(drv, devices);
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -203,15 +183,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* 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);
@@ -227,7 +203,7 @@ static struct sr_dev_driver norma_dmm_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = NULL,
        .config_set = config_set,
        .config_list = config_list,
@@ -247,7 +223,7 @@ static struct sr_dev_driver siemens_b102x_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = NULL,
        .config_set = config_set,
        .config_list = config_list,
index de31828a706bcdc1f96f5b7155347ee5526ae564..39924176d484c4ddab04f126a4d245c8dba888c0 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * Norma DM9x0/Siemens B102x DMMs driver.
- *
- * @internal
- */
-
 #include <config.h>
 #include "protocol.h"
 
@@ -417,7 +409,7 @@ SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits)) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        } else {
                /* Request next package. */
                if (devc->last_req_pending) {
index bec6107046b51e0a11f5f66a6e7aca1b973c1f78..9891741ee88e0101b34be1bf51deb37c316f7ec2 100644 (file)
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-/**
- * @file
- *
- * Norma DM9x0/Siemens B102x DMMs driver.
- *
- * @internal
- */
-
 #define LOG_PREFIX "norma-dmm"
 
 #define NMADMM_BUFSIZE 256
@@ -57,21 +49,16 @@ struct nmadmm_req {
 /** Strings for requests. */
 extern const struct nmadmm_req nmadmm_requests[];
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        int type;               /**< DM9x0, e.g. 5 = DM950 */
 
-       /* Acquisition settings */
        struct sr_sw_limits limits;
 
-       /* 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 */
        uint8_t buf[NMADMM_BUFSIZE];    /**< Buffer for read callback */
        int buflen;                     /**< Data len in buf */
 };
index 5e74a5236e745b56de2bfde26e3baa8ebebf212f..fc0d9ebdb3afe6813e7e3e26fed2258dc449e37d 100644 (file)
 
 #define SERIALCOMM "115200/8n1"
 
-static const uint32_t drvopts[] = {
-       SR_CONF_LOGIC_ANALYZER,
-};
-
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
        SR_CONF_SERIALCOMM,
 };
 
+static const uint32_t drvopts[] = {
+       SR_CONF_LOGIC_ANALYZER,
+};
+
 static const uint32_t devopts[] = {
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
@@ -187,8 +187,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, g_slist_append(NULL, sdi));
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -198,6 +198,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                return SR_ERR_ARG;
 
        devc = sdi->priv;
+
        switch (key) {
        case SR_CONF_SAMPLERATE:
                *data = g_variant_new_uint64(devc->cur_samplerate);
@@ -226,20 +227,16 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        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 (key) {
@@ -247,21 +244,15 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                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;
+               return ols_set_samplerate(sdi, g_variant_get_uint64(data));
        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)
-                       ret = SR_ERR;
-               else
-                       ret = SR_OK;
                break;
        case SR_CONF_EXTERNAL_CLOCK:
                if (g_variant_get_boolean(data)) {
@@ -271,28 +262,24 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        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)) {
+               } 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;
+                       return SR_ERR;
                }
+               devc->flag_reg &= ~FLAG_INTERNAL_TEST_MODE;
+               devc->flag_reg &= ~FLAG_EXTERNAL_TEST_MODE;
+               devc->flag_reg |= flag;
                break;
        case SR_CONF_SWAP:
                if (g_variant_get_boolean(data)) {
@@ -302,9 +289,7 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        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.");
@@ -313,52 +298,32 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        sr_info("Disabling RLE.");
                        devc->flag_reg &= ~FLAG_RLE;
                }
-               ret = SR_OK;
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi)
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               else
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
                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));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                break;
        case SR_CONF_PATTERN_MODE:
-               *data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(patterns));
                break;
        case SR_CONF_LIMIT_SAMPLES:
                if (!sdi)
@@ -379,12 +344,9 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                        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);
+
+               *data = std_gvar_tuple_u64(MIN_NUM_SAMPLES,
+                       (num_ols_changrp) ? devc->max_samples / num_ols_changrp : MIN_NUM_SAMPLES);
                break;
        default:
                return SR_ERR_NA;
@@ -439,9 +401,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        int num_ols_changrp;
        int ret, i;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
        serial = sdi->conn;
 
@@ -570,7 +529,7 @@ static struct sr_dev_driver ols_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 4525ba2ccb750707bfa9db8dfa31402d00a176cc..cfe98cb4667497ec6c8f1970d887f5bd3429c976 100644 (file)
@@ -26,7 +26,7 @@
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-#define LOG_PREFIX "ols"
+#define LOG_PREFIX "openbench-logic-sniffer"
 
 #define NUM_CHANNELS               32
 #define NUM_TRIGGER_STAGES         4
 #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;
+       uint64_t capture_ratio;
        int trigger_at;
        uint32_t channel_mask;
        uint32_t trigger_mask[NUM_TRIGGER_STAGES];
@@ -86,7 +83,6 @@ struct dev_context {
        int num_stages;
        uint16_t flag_reg;
 
-       /* Operational states */
        unsigned int num_transfers;
        unsigned int num_samples;
        int num_bytes;
@@ -94,7 +90,6 @@ struct dev_context {
        int cnt_samples;
        int cnt_samples_rle;
 
-       /* Temporary variables */
        unsigned int rle_count;
        unsigned char sample[4];
        unsigned char tmp_sample[4];
index b0f62505d5cdb9ca63b91a49616b76281e4a0153..68e88b4f0b8461d258811fb19e37e8f235403f1e 100644 (file)
@@ -18,8 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <string.h>
 #include <config.h>
+#include <string.h>
 #include "protocol.h"
 
 #define SERIALCOMM "115200/8n1"
@@ -43,13 +43,11 @@ static const uint32_t devopts[] = {
 };
 
 static const char *weight_freq[] = {
-       "A",
-       "C",
+       "A", "C",
 };
 
 static const char *weight_time[] = {
-       "F",
-       "S",
+       "F", "S",
 };
 
 static const uint64_t meas_ranges[][2] = {
@@ -60,8 +58,7 @@ static const uint64_t meas_ranges[][2] = {
 };
 
 static const char *data_sources[] = {
-       "Live",
-       "Memory",
+       "Live", "Memory",
 };
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
@@ -103,16 +100,10 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, g_slist_append(NULL, sdi));
 }
 
-static int dev_clear(const struct sr_dev_driver *di)
-{
-       return std_dev_clear(di, NULL);
-}
-
 static int config_get(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *range[2];
        uint64_t low, high;
        uint64_t tmp;
        int ret;
@@ -123,7 +114,7 @@ static int config_get(uint32_t key, GVariant **data,
                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);
@@ -147,11 +138,10 @@ static int config_get(uint32_t key, GVariant **data,
                        return SR_ERR;
                break;
        case SR_CONF_SPL_MEASUREMENT_RANGE:
-               if ((ret = pce_322a_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);
-               }
+               if ((ret = pce_322a_meas_range_get(sdi, &low, &high)) == SR_OK)
+                       *data = std_gvar_tuple_u64(low, high);
+               else
+                       return ret;
                break;
        case SR_CONF_POWER_OFF:
                *data = g_variant_new_boolean(FALSE);
@@ -166,139 +156,77 @@ static int config_get(uint32_t key, GVariant **data,
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
        const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       uint64_t tmp_u64, low, high;
-       unsigned int i;
-       int ret;
-       const char *tmp_str;
+       int idx;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
-       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;
+               devc->limit_samples = g_variant_get_uint64(data);
                break;
        case SR_CONF_SPL_WEIGHT_FREQ:
-               tmp_str = g_variant_get_string(data, NULL);
-               if (!strcmp(tmp_str, "A"))
-                       ret = pce_322a_weight_freq_set(sdi,
-                                       SR_MQFLAG_SPL_FREQ_WEIGHT_A);
-               else if (!strcmp(tmp_str, "C"))
-                       ret = pce_322a_weight_freq_set(sdi,
-                                       SR_MQFLAG_SPL_FREQ_WEIGHT_C);
-               else
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_freq))) < 0)
                        return SR_ERR_ARG;
-               break;
+               return pce_322a_weight_freq_set(sdi, (weight_freq[idx][0] == 'A') ?
+                       SR_MQFLAG_SPL_FREQ_WEIGHT_A : SR_MQFLAG_SPL_FREQ_WEIGHT_C);
        case SR_CONF_SPL_WEIGHT_TIME:
-               tmp_str = g_variant_get_string(data, NULL);
-               if (!strcmp(tmp_str, "F"))
-                       ret = pce_322a_weight_time_set(sdi,
-                                       SR_MQFLAG_SPL_TIME_WEIGHT_F);
-               else if (!strcmp(tmp_str, "S"))
-                       ret = pce_322a_weight_time_set(sdi,
-                                       SR_MQFLAG_SPL_TIME_WEIGHT_S);
-               else
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_time))) < 0)
                        return SR_ERR_ARG;
-               break;
+               return pce_322a_weight_time_set(sdi, (weight_time[idx][0] == 'F') ?
+                       SR_MQFLAG_SPL_TIME_WEIGHT_F : SR_MQFLAG_SPL_TIME_WEIGHT_S);
        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 = pce_322a_meas_range_set(sdi, low, high);
-                               break;
-                       }
-               }
-               break;
+               if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(meas_ranges))) < 0)
+                       return SR_ERR_ARG;
+               return pce_322a_meas_range_set(sdi, meas_ranges[idx][0], meas_ranges[idx][1]);
        case SR_CONF_POWER_OFF:
                if (g_variant_get_boolean(data))
-                       ret = pce_322a_power_off(sdi);
+                       return pce_322a_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;
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+                       return SR_ERR_ARG;
+               devc->cur_data_source = idx;
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_list(uint32_t 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)cg;
-
-       ret = SR_OK;
-       if (!sdi) {
-               switch (key) {
-               case SR_CONF_SCAN_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-                       break;
-               case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-                       break;
-               default:
-                       return SR_ERR_NA;
-               }
-       } else {
-               switch (key) {
-               case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_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;
-               }
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_SPL_WEIGHT_FREQ:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_freq));
+               break;
+       case SR_CONF_SPL_WEIGHT_TIME:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_time));
+               break;
+       case SR_CONF_SPL_MEASUREMENT_RANGE:
+               *data = std_gvar_tuple_array(ARRAY_AND_SIZE(meas_ranges));
+               break;
+       case SR_CONF_DATA_SOURCE:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
+               break;
+       default:
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -328,16 +256,12 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        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->buffer_len = 0;
        devc->memory_state = MEM_STATE_REQUEST_MEMORY_USAGE;
 
        std_session_send_df_header(sdi);
 
-       /* Poll every 150ms, or whenever some data comes in. */
        serial = sdi->conn;
        serial_source_add(sdi->session, serial, G_IO_IN, 150,
                        pce_322a_receive_data, (void *)sdi);
@@ -353,7 +277,7 @@ static struct sr_dev_driver pce_322a_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = dev_clear,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 6dc9deeb7da8c746f8fc19188f60cf6e4b3e9598..ee637e802dcb82b390276ab8a8169520f4643b15 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include <config.h>
+#include <string.h>
 #include "protocol.h"
 
 static int send_command(const struct sr_dev_inst *sdi, uint16_t command)
@@ -32,10 +33,7 @@ static int send_command(const struct sr_dev_inst *sdi, uint16_t command)
        if (!(serial = sdi->conn))
                return SR_ERR;
 
-       if (serial_write_nonblocking(serial, (const void *)buffer, 2) != 2)
-               return SR_ERR;
-
-       return SR_OK;
+       return serial_write_blocking(serial, (const void *)buffer, 2, 0);
 }
 
 static int send_long_command(const struct sr_dev_inst *sdi, uint32_t command)
@@ -51,10 +49,7 @@ static int send_long_command(const struct sr_dev_inst *sdi, uint32_t command)
        if (!(serial = sdi->conn))
                return SR_ERR;
 
-       if (serial_write_nonblocking(serial, (const void *)buffer, 4) != 4)
-               return SR_ERR;
-
-       return SR_OK;
+       return serial_write_blocking(serial, (const void *)buffer, 4, 0);
 }
 
 static void send_data(const struct sr_dev_inst *sdi, float sample)
@@ -82,7 +77,7 @@ static void send_data(const struct sr_dev_inst *sdi, float sample)
        devc->num_samples++;
        /* Limiting number of samples is only supported for live data. */
        if (devc->cur_data_source == DATA_SOURCE_LIVE && devc->limit_samples && devc->num_samples >= devc->limit_samples)
-               sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
+               sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
 }
 
 static void process_measurement(const struct sr_dev_inst *sdi)
@@ -140,15 +135,13 @@ static void process_memory_measurement(const struct sr_dev_inst *sdi)
 static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c)
 {
        struct dev_context *devc;
-       unsigned int i;
 
        devc = sdi->priv;
 
        if (devc->buffer_len < BUFFER_SIZE) {
                devc->buffer[devc->buffer_len++] = c;
        } else {
-               for (i = 1; i < BUFFER_SIZE; i++)
-                       devc->buffer[i - 1] = devc->buffer[i];
+               memmove(devc->buffer, devc->buffer + 1, BUFFER_SIZE - 1);
                devc->buffer[BUFFER_SIZE - 1] = c;
        }
 
@@ -162,15 +155,13 @@ static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c)
 static void process_usage_byte(const struct sr_dev_inst *sdi, uint8_t c)
 {
        struct dev_context *devc;
-       unsigned int i;
 
        devc = sdi->priv;
 
        if (devc->buffer_len < MEM_USAGE_BUFFER_SIZE) {
                devc->buffer[devc->buffer_len++] = c;
        } else {
-               for (i = 1; i < MEM_USAGE_BUFFER_SIZE; i++)
-                       devc->buffer[i - 1] = devc->buffer[i];
+               memmove(devc->buffer, devc->buffer + 1, MEM_USAGE_BUFFER_SIZE - 1);
                devc->buffer[MEM_USAGE_BUFFER_SIZE - 1] = c;
        }
 
@@ -193,15 +184,13 @@ static void process_usage_byte(const struct sr_dev_inst *sdi, uint8_t c)
 static void process_memory_byte(const struct sr_dev_inst *sdi, uint8_t c)
 {
        struct dev_context *devc;
-       unsigned int i;
 
        devc = sdi->priv;
 
        if (devc->buffer_len < MEM_DATA_BUFFER_SIZE) {
                devc->buffer[devc->buffer_len++] = c;
        } else {
-               for (i = 1; i < MEM_DATA_BUFFER_SIZE; i++)
-                       devc->buffer[i - 1] = devc->buffer[i];
+               memmove(devc->buffer, devc->buffer + 1, MEM_DATA_BUFFER_SIZE - 1);
                devc->buffer[MEM_DATA_BUFFER_SIZE - 1] = c;
        }
 
index 094b3ab47793949d93c8c49fe51c84b5639a9fb3..338e37e3af454b7d676194eb685d88814ad64544 100644 (file)
@@ -68,17 +68,13 @@ enum {
        MEM_STATE_GET_MEMORY_BLOCK,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        uint64_t cur_mqflags;
        uint8_t cur_meas_range;
 
-       /* Acquisition settings */
        uint8_t cur_data_source;
        uint64_t limit_samples;
 
-       /* Operational state */
        uint64_t num_samples;
 
        /* Memory reading state */
@@ -88,7 +84,6 @@ struct dev_context {
        uint16_t memory_block_counter; /* Number of memory blocks retrieved so far. */
        uint8_t memory_block_cursor; /* Number of bytes retrieved in current memory block. */
 
-       /* Temporary state across callbacks. */
        uint8_t buffer[BUFFER_SIZE];
        int buffer_len;
        int buffer_skip; /* Number of bytes to skip in memory mode. */
index 269b99f7c2c8287ac6173d81c37dbbd86169019b..8d8fe5a9e697500280e216e46bdb3b67d9b28a3b 100644 (file)
 #include <config.h>
 #include "protocol.h"
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
@@ -88,31 +91,24 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
        devices = NULL;
 
-       /* Allocate memory for our private device context. */
        devc = g_malloc0(sizeof(struct dev_context));
 
-       /* Device-specific settings */
        devc->max_samplebytes = 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;
 
-       /* Allocate memory for the incoming ftdi data. */
        devc->ftdi_buf = g_malloc0(FTDI_BUF_SIZE);
 
-       /* Allocate memory for the FTDI context (ftdic) and initialize it. */
        if (!(devc->ftdic = ftdi_new())) {
                sr_err("Failed to initialize libftdi.");
                goto err_free_ftdi_buf;;
        }
 
-       /* Try to open the FTDI device */
-       if (p_ols_open(devc) != SR_OK) {
+       if (p_ols_open(devc) != SR_OK)
                goto err_free_ftdic;
-       }
 
        /* The discovery procedure is like this: first send the Reset
         * command (0x00) 5 times, since the device could be anywhere
@@ -163,7 +159,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                goto err_close_ftdic;
        }
 
-       /* Close device. We'll reopen it again when we need it. */
        p_ols_close(devc);
 
        /* Parse the metadata. */
@@ -181,7 +176,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 err_close_ftdic:
        p_ols_close(devc);
 err_free_ftdic:
-       ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
+       ftdi_free(devc->ftdic);
 err_free_ftdi_buf:
        g_free(devc->ftdi_buf);
        g_free(devc);
@@ -189,23 +184,19 @@ err_free_ftdi_buf:
        return NULL;
 }
 
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
-
        ftdi_free(devc->ftdic);
        g_free(devc->ftdi_buf);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -215,6 +206,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                return SR_ERR_ARG;
 
        devc = sdi->priv;
+
        switch (key) {
        case SR_CONF_SAMPLERATE:
                *data = g_variant_new_uint64(devc->cur_samplerate);
@@ -246,20 +238,16 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        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 (key) {
@@ -267,21 +255,15 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                tmp_u64 = g_variant_get_uint64(data);
                if (tmp_u64 < samplerates[0] || tmp_u64 > samplerates[1])
                        return SR_ERR_SAMPLERATE;
-               ret = p_ols_set_samplerate(sdi, g_variant_get_uint64(data));
-               break;
+               return p_ols_set_samplerate(sdi, g_variant_get_uint64(data));
        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)
-                       ret = SR_ERR;
-               else
-                       ret = SR_OK;
                break;
        case SR_CONF_EXTERNAL_CLOCK:
                if (g_variant_get_boolean(data)) {
@@ -291,28 +273,24 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        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)) {
+               } 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;
+                       return SR_ERR;
                }
+               devc->flag_reg &= ~FLAG_INTERNAL_TEST_MODE;
+               devc->flag_reg &= ~FLAG_EXTERNAL_TEST_MODE;
+               devc->flag_reg |= flag;
                break;
        case SR_CONF_SWAP:
                if (g_variant_get_boolean(data)) {
@@ -322,9 +300,7 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        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.");
@@ -333,44 +309,31 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        sr_info("Disabling RLE.");
                        devc->flag_reg &= ~FLAG_RLE;
                }
-               ret = SR_OK;
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *gvar, *grange[2];
-       GVariantBuilder gvb;
        int num_pols_changrp, i;
 
-       (void)cg;
-
        switch (key) {
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
                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));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                break;
        case SR_CONF_PATTERN_MODE:
-               *data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(patterns));
                break;
        case SR_CONF_LIMIT_SAMPLES:
                if (!sdi)
@@ -394,12 +357,9 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                /* 3 channel groups takes as many bytes as 4 channel groups */
                if (num_pols_changrp == 3)
                        num_pols_changrp = 4;
-               grange[0] = g_variant_new_uint64(MIN_NUM_SAMPLES);
-               if (num_pols_changrp)
-                       grange[1] = g_variant_new_uint64(devc->max_samplebytes / num_pols_changrp);
-               else
-                       grange[1] = g_variant_new_uint64(MIN_NUM_SAMPLES);
-               *data = g_variant_new_tuple(grange, 2);
+
+               *data = std_gvar_tuple_u64(MIN_NUM_SAMPLES,
+                       (num_pols_changrp) ? devc->max_samplebytes / num_pols_changrp : MIN_NUM_SAMPLES);
                break;
        default:
                return SR_ERR_NA;
@@ -414,32 +374,16 @@ static int dev_open(struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       if (p_ols_open(devc) != SR_OK) {
-               return SR_ERR;
-       } else {
-               sdi->status = SR_ST_ACTIVE;
-               return SR_OK;
-       }
+       return p_ols_open(devc);
 }
 
 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 = p_ols_close(devc);
-       } else {
-               sr_spew("Status not ACTIVE, nothing to do.");
-       }
-
-       sdi->status = SR_ST_INACTIVE;
-
-       return ret;
+       return p_ols_close(devc);
 }
 
 static int set_trigger(const struct sr_dev_inst *sdi, int stage)
@@ -523,9 +467,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        int num_pols_changrp, samplespercount;
        int ret, i;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        pols_channel_mask(sdi);
@@ -682,7 +623,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       sr_dbg("Stopping acquisition.");
        write_shortcommand(devc, CMD_RESET);
        write_shortcommand(devc, CMD_RESET);
        write_shortcommand(devc, CMD_RESET);
index 9948944d2681703ceedac4306d49740bbbbc6c30..62515050511b67ead751998e9f7bdd44ee100cc0 100644 (file)
@@ -73,16 +73,13 @@ SR_PRIV int p_ols_open(struct dev_context *devc)
 
        /* Note: Caller checks devc and devc->ftdic. */
 
-       /* Select interface B, otherwise communication will fail. */
        ret = ftdi_set_interface(devc->ftdic, INTERFACE_B);
        if (ret < 0) {
                sr_err("Failed to set FTDI interface B (%d): %s", ret,
                       ftdi_get_error_string(devc->ftdic));
                return SR_ERR;
        }
-       sr_dbg("FTDI chip interface B set successfully.");
 
-       /* 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) {
@@ -92,47 +89,39 @@ SR_PRIV int p_ols_open(struct dev_context *devc)
                               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_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_open_close_ftdic;
        }
-       sr_dbg("FTDI chip bitmode reset successfully.");
 
-       /* Set the FTDI latency timer to 16. */
        ret = ftdi_set_latency_timer(devc->ftdic, 16);
        if (ret < 0) {
                sr_err("Failed to set FTDI latency timer (%d): %s.",
                       ret, ftdi_get_error_string(devc->ftdic));
                goto err_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_open_close_ftdic;
        }
-       sr_dbg("FTDI chip read data chunk size set successfully.");
 
        return SR_OK;
 
 err_open_close_ftdic:
        ftdi_usb_close(devc->ftdic);
+
        return SR_ERR;
 }
 
@@ -221,7 +210,7 @@ SR_PRIV struct sr_dev_inst *p_ols_get_metadata(uint8_t *buf, int bytes_read, str
        uint8_t key, type, token;
        GString *tmp_str, *devname, *version;
        guchar tmp_c;
-       int index, i;
+       int index;
 
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
@@ -275,10 +264,8 @@ SR_PRIV struct sr_dev_inst *p_ols_get_metadata(uint8_t *buf, int bytes_read, str
                        break;
                case 1:
                        /* 32-bit unsigned integer */
-                       tmp_int = 0;
-                       for (i = 0; i < 4; i++) {
-                               tmp_int = (tmp_int << 8) | buf[index++];
-                       }
+                       tmp_int = RB32(&buf[index]);
+                       index += sizeof(uint32_t);
                        sr_dbg("Got metadata key 0x%.2x value 0x%.8x.",
                               key, tmp_int);
                        switch (token) {
@@ -424,7 +411,7 @@ SR_PRIV int p_ols_receive_data(int fd, int revents, void *cb_data)
                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);
+                       sr_dev_acquisition_stop(sdi);
                        return FALSE;
                }
                if (bytes_read == 0) {
@@ -672,7 +659,7 @@ SR_PRIV int p_ols_receive_data(int fd, int revents, void *cb_data)
                }
                g_free(devc->raw_sample_buf);
 
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        }
 
        return TRUE;
index c2bd2ccb902ba5cb91f79bcbd8254b32b9dd78ba..1ac0d06d6f1c23f3127a76abfb8d095cb611fdef 100644 (file)
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-#define LOG_PREFIX "p-ols"
+#define LOG_PREFIX "pipistrello-ols"
 
 #define USB_VENDOR_ID          0x0403
 #define USB_DEVICE_ID          0x6010
-#define USB_VENDOR_NAME                "Saanlima"
 #define USB_IPRODUCT           "Pipistrello LX45"
 
 #define FTDI_BUF_SIZE          (16 * 1024)
 #define FLAG_FILTER                (1 << 1)
 #define FLAG_DEMUX                 (1 << 0)
 
-/* Private, per-device-instance driver context. */
 struct dev_context {
-       /** FTDI device context (used by libftdi). */
        struct ftdi_context *ftdic;
        uint8_t *ftdi_buf;
 
-       /* Fixed device settings */
        int max_channels;
        uint32_t max_samplebytes;
        uint32_t max_samplerate;
        uint32_t protocol_version;
 
-       /* Acquisition settings */
        uint64_t cur_samplerate;
        uint32_t cur_samplerate_divider;
        uint32_t max_samples;
        uint64_t limit_samples;
-       int capture_ratio;
+       uint64_t capture_ratio;
        int trigger_at;
        uint32_t channel_mask;
        uint32_t trigger_mask[NUM_TRIGGER_STAGES];
@@ -101,7 +96,6 @@ struct dev_context {
        int num_stages;
        uint16_t flag_reg;
 
-       /* Operational states */
        unsigned int num_transfers;
        unsigned int num_samples;
        int num_bytes;
@@ -109,7 +103,6 @@ struct dev_context {
        unsigned int cnt_samples;
        int cnt_samples_rle;
 
-       /* Temporary variables */
        unsigned int rle_count;
        unsigned char sample[4];
        unsigned char tmp_sample[4];
diff --git a/src/hardware/rdtech-dps/api.c b/src/hardware/rdtech-dps/api.c
new file mode 100644 (file)
index 0000000..e321ce3
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 James Churchill <pelrun@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 <config.h>
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
+       SR_CONF_MODBUSADDR,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_POWER_SUPPLY,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_VOLTAGE | SR_CONF_GET,
+       SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CURRENT | SR_CONF_GET,
+       SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_REGULATION | SR_CONF_GET,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
+       SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+};
+
+/* Model ID, model name, max current, max voltage, max power */
+static const struct rdtech_dps_model supported_models[] = {
+       { 3005, "DPS3005",  3, 50,  160 },
+       { 5005, "DPS5005",  5, 50,  250 },
+       { 5205, "DPH5005",  5, 50,  250 },
+       { 5015, "DPS5015", 15, 50,  750 },
+       { 5020, "DPS5020", 20, 50, 1000 },
+       { 8005, "DPS8005",  5, 80,  408 },
+};
+
+static struct sr_dev_driver rdtech_dps_driver_info;
+
+static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus)
+{
+       const struct rdtech_dps_model *model = NULL;
+       struct dev_context *devc;
+       struct sr_dev_inst *sdi;
+       uint16_t id, version;
+       unsigned int i;
+
+       int ret = rdtech_dps_get_model_version(modbus, &id, &version);
+       if (ret != SR_OK)
+               return NULL;
+       for (i = 0; i < ARRAY_SIZE(supported_models); i++)
+               if (id == supported_models[i].id) {
+                       model = &supported_models[i];
+                       break;
+               }
+       if (model == NULL) {
+               sr_err("Unknown model: %d.", id);
+               return NULL;
+       }
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->status = SR_ST_INACTIVE;
+       sdi->vendor = g_strdup("RDTech");
+       sdi->model = g_strdup(model->name);
+       sdi->version = g_strdup_printf("v%d", version);
+       sdi->conn = modbus;
+       sdi->driver = &rdtech_dps_driver_info;
+       sdi->inst_type = SR_INST_MODBUS;
+
+       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
+       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "I");
+       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P");
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       sr_sw_limits_init(&devc->limits);
+       devc->model = model;
+       devc->expecting_registers = 0;
+
+       sdi->priv = devc;
+
+       return sdi;
+}
+
+static int config_compare(gconstpointer a, gconstpointer b)
+{
+       const struct sr_config *ac = a, *bc = b;
+       return ac->key != bc->key;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       struct sr_config default_serialcomm = {
+               .key = SR_CONF_SERIALCOMM,
+               .data = g_variant_new_string("9600/8n1"),
+       };
+       struct sr_config default_modbusaddr = {
+               .key = SR_CONF_MODBUSADDR,
+               .data = g_variant_new_uint64(1),
+       };
+       GSList *opts = options, *devices;
+
+       if (!g_slist_find_custom(options, &default_serialcomm, config_compare))
+               opts = g_slist_prepend(opts, &default_serialcomm);
+       if (!g_slist_find_custom(options, &default_modbusaddr, config_compare))
+               opts = g_slist_prepend(opts, &default_modbusaddr);
+
+       devices = sr_modbus_scan(di->context, opts, probe_device);
+
+       while (opts != options)
+               opts = g_slist_delete_link(opts, opts);
+       g_variant_unref(default_serialcomm.data);
+       g_variant_unref(default_modbusaddr.data);
+
+       return devices;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       struct sr_modbus_dev_inst *modbus = sdi->conn;
+
+       if (sr_modbus_open(modbus) < 0)
+               return SR_ERR;
+
+       rdtech_dps_set_reg(modbus, REG_LOCK, 1);
+
+       return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
+
+       modbus = sdi->conn;
+
+       if (!modbus)
+               return SR_ERR_BUG;
+
+       devc = sdi->priv;
+
+       if (devc->expecting_registers) {
+               /* Wait for the last data that was requested from the device. */
+               uint16_t registers[devc->expecting_registers];
+               sr_modbus_read_holding_registers(modbus, -1,
+                       devc->expecting_registers, registers);
+       }
+
+       rdtech_dps_set_reg(modbus, REG_LOCK, 0);
+
+       return sr_modbus_close(modbus);
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
+       int ret;
+       uint16_t ivalue;
+
+       (void)cg;
+
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
+       ret = SR_OK;
+       switch (key) {
+       case SR_CONF_LIMIT_SAMPLES:
+       case SR_CONF_LIMIT_MSEC:
+               ret = sr_sw_limits_config_get(&devc->limits, key, data);
+               break;
+       case SR_CONF_ENABLED:
+               if ((ret = rdtech_dps_get_reg(modbus, REG_ENABLE, &ivalue)) == SR_OK)
+                       *data = g_variant_new_boolean(ivalue);
+               break;
+       case SR_CONF_REGULATION:
+               if ((ret = rdtech_dps_get_reg(modbus, REG_CV_CC, &ivalue)) != SR_OK)
+                       break;
+               *data = g_variant_new_string((ivalue == MODE_CC) ? "CC" : "CV");
+               break;
+       case SR_CONF_VOLTAGE:
+               if ((ret = rdtech_dps_get_reg(modbus, REG_UOUT, &ivalue)) == SR_OK)
+                       *data = g_variant_new_double((float)ivalue / 100.0f);
+               break;
+       case SR_CONF_VOLTAGE_TARGET:
+               if ((ret = rdtech_dps_get_reg(modbus, REG_USET, &ivalue)) == SR_OK)
+                       *data = g_variant_new_double((float)ivalue / 100.0f);
+               break;
+       case SR_CONF_CURRENT:
+               if ((ret = rdtech_dps_get_reg(modbus, REG_IOUT, &ivalue)) == SR_OK)
+                       *data = g_variant_new_double((float)ivalue / 100.0f);
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               if ((ret = rdtech_dps_get_reg(modbus, REG_ISET, &ivalue)) == SR_OK)
+                       *data = g_variant_new_double((float)ivalue / 1000.0f);
+               break;
+       case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+               *data = g_variant_new_boolean(TRUE);
+               break;
+       case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE:
+               if ((ret = rdtech_dps_get_reg(modbus, REG_PROTECT, &ivalue)) == SR_OK)
+                       *data = g_variant_new_boolean(ivalue == STATE_OVP);
+               break;
+       case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+               if ((ret = rdtech_dps_get_reg(modbus, PRE_OVPSET, &ivalue)) == SR_OK)
+                       *data = g_variant_new_double((float)ivalue / 100.0f);
+               break;
+       case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+               *data = g_variant_new_boolean(TRUE);
+               break;
+       case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE:
+               if ((ret = rdtech_dps_get_reg(modbus, REG_PROTECT, &ivalue)) == SR_OK)
+                       *data = g_variant_new_boolean(ivalue == STATE_OCP);
+               break;
+       case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+               if ((ret = rdtech_dps_get_reg(modbus, PRE_OCPSET, &ivalue)) == SR_OK)
+                       *data = g_variant_new_double((float)ivalue / 1000.0f);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return ret;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
+
+       (void)cg;
+
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
+       switch (key) {
+       case SR_CONF_LIMIT_SAMPLES:
+       case SR_CONF_LIMIT_MSEC:
+               return sr_sw_limits_config_set(&devc->limits, key, data);
+       case SR_CONF_ENABLED:
+               return rdtech_dps_set_reg(modbus, REG_ENABLE, g_variant_get_boolean(data));
+       case SR_CONF_VOLTAGE_TARGET:
+               return rdtech_dps_set_reg(modbus, REG_USET, g_variant_get_double(data) * 100);
+       case SR_CONF_CURRENT_LIMIT:
+               return rdtech_dps_set_reg(modbus, REG_ISET, g_variant_get_double(data) * 1000);
+       case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+               return rdtech_dps_set_reg(modbus, PRE_OVPSET, g_variant_get_double(data) * 100);
+       case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+               return rdtech_dps_set_reg(modbus, PRE_OCPSET, g_variant_get_double(data) * 1000);
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+
+       devc = (sdi) ? sdi->priv : NULL;
+
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_VOLTAGE_TARGET:
+               *data = std_gvar_min_max_step(0.0, devc->model->max_voltage, 0.001);
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               *data = std_gvar_min_max_step(0.0, devc->model->max_current, 0.0001);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
+       int ret;
+
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
+       if ((ret = sr_modbus_source_add(sdi->session, modbus, G_IO_IN, 10,
+                       rdtech_dps_receive_data, (void *)sdi)) != SR_OK)
+               return ret;
+
+       sr_sw_limits_acquisition_start(&devc->limits);
+       std_session_send_df_header(sdi);
+
+       return rdtech_dps_capture_start(sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       struct sr_modbus_dev_inst *modbus;
+
+       std_session_send_df_end(sdi);
+
+       modbus = sdi->conn;
+       sr_modbus_source_remove(sdi->session, modbus);
+
+       return SR_OK;
+}
+
+static struct sr_dev_driver rdtech_dps_driver_info = {
+       .name = "rdtech-dps",
+       .longname = "RDTech DPS/DPH series power supply",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(rdtech_dps_driver_info);
diff --git a/src/hardware/rdtech-dps/protocol.c b/src/hardware/rdtech-dps/protocol.c
new file mode 100644 (file)
index 0000000..4df1a70
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 James Churchill <pelrun@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 <config.h>
+#include "protocol.h"
+
+SR_PRIV int rdtech_dps_get_reg(struct sr_modbus_dev_inst *modbus,
+               uint16_t address, uint16_t *value)
+{
+       uint16_t registers[1];
+       int ret = sr_modbus_read_holding_registers(modbus, address, 1, registers);
+       *value = RB16(registers + 0);
+       return ret;
+}
+
+SR_PRIV int rdtech_dps_set_reg(struct sr_modbus_dev_inst *modbus,
+               uint16_t address, uint16_t value)
+{
+       uint16_t registers[1];
+       WB16(registers, value);
+       return sr_modbus_write_multiple_registers(modbus, address, 1, registers);
+}
+
+SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
+               uint16_t *model, uint16_t *version)
+{
+       uint16_t registers[2];
+       int ret;
+       ret = sr_modbus_read_holding_registers(modbus, REG_MODEL, 2, registers);
+       if (ret == SR_OK) {
+               *model = RB16(registers + 0);
+               *version = RB16(registers + 1);
+               sr_info("RDTech PSU model: %d version: %d", *model, *version);
+       }
+       return ret;
+}
+
+static void send_value(const struct sr_dev_inst *sdi, struct sr_channel *ch,
+               float value, enum sr_mq mq, enum sr_unit unit, int digits)
+{
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_analog analog;
+       struct sr_analog_encoding encoding;
+       struct sr_analog_meaning meaning;
+       struct sr_analog_spec spec;
+
+       sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+       analog.meaning->channels = g_slist_append(NULL, ch);
+       analog.num_samples = 1;
+       analog.data = &value;
+       analog.meaning->mq = mq;
+       analog.meaning->unit = unit;
+       analog.meaning->mqflags = SR_MQFLAG_DC;
+
+       packet.type = SR_DF_ANALOG;
+       packet.payload = &analog;
+       sr_session_send(sdi, &packet);
+       g_slist_free(analog.meaning->channels);
+}
+
+SR_PRIV int rdtech_dps_capture_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
+       int ret;
+
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
+       if ((ret = sr_modbus_read_holding_registers(modbus, REG_UOUT, 3, NULL)) == SR_OK)
+               devc->expecting_registers = 2;
+       return ret;
+}
+
+SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
+{
+       struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
+       struct sr_datafeed_packet packet;
+       uint16_t registers[3];
+
+       (void)fd;
+       (void)revents;
+
+       if (!(sdi = cb_data))
+               return TRUE;
+
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
+       devc->expecting_registers = 0;
+       if (sr_modbus_read_holding_registers(modbus, -1, 3, registers) == SR_OK) {
+               packet.type = SR_DF_FRAME_BEGIN;
+               sr_session_send(sdi, &packet);
+
+               send_value(sdi, sdi->channels->data,
+                       RB16(registers + 0) / 100.0f,
+                       SR_MQ_VOLTAGE, SR_UNIT_VOLT, 3);
+               send_value(sdi, sdi->channels->next->data,
+                       RB16(registers + 1) / 1000.0f,
+                       SR_MQ_CURRENT, SR_UNIT_AMPERE, 4);
+               send_value(sdi, sdi->channels->next->next->data,
+                       RB16(registers + 2) / 100.0f,
+                       SR_MQ_POWER, SR_UNIT_WATT, 3);
+
+               packet.type = SR_DF_FRAME_END;
+               sr_session_send(sdi, &packet);
+               sr_sw_limits_update_samples_read(&devc->limits, 1);
+       }
+
+       if (sr_sw_limits_check(&devc->limits)) {
+               sr_dev_acquisition_stop(sdi);
+               return TRUE;
+       }
+
+       rdtech_dps_capture_start(sdi);
+       return TRUE;
+}
diff --git a/src/hardware/rdtech-dps/protocol.h b/src/hardware/rdtech-dps/protocol.h
new file mode 100644 (file)
index 0000000..3224e1b
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 James Churchill <pelrun@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_RDTECH_DPS_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_RDTECH_DPS_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "rdtech-dps"
+
+struct rdtech_dps_model {
+       unsigned int id;
+       const char *name;
+       unsigned int max_current;
+       unsigned int max_voltage;
+       unsigned int max_power;
+};
+
+struct dev_context {
+       const struct rdtech_dps_model *model;
+       struct sr_sw_limits limits;
+       int expecting_registers;
+};
+
+enum rdtech_dps_register {
+       REG_USET       = 0x00, /* Mirror of 0x50 */
+       REG_ISET       = 0x01, /* Mirror of 0x51 */
+       REG_UOUT       = 0x02,
+       REG_IOUT       = 0x03,
+       REG_POWER      = 0x04,
+       REG_UIN        = 0x05,
+       REG_LOCK       = 0x06,
+       REG_PROTECT    = 0x07,
+       REG_CV_CC      = 0x08,
+       REG_ENABLE     = 0x09,
+       REG_BACKLIGHT  = 0x0A, /* Mirror of 0x55 */
+       REG_MODEL      = 0x0B,
+       REG_VERSION    = 0x0C,
+
+       REG_PRESET     = 0x23, /* Loads a preset into preset 0. */
+
+/*
+ * Add (preset * 0x10) to each of the following, for preset 1-9.
+ * Preset 0 regs below are the active output settings.
+ */
+       PRE_USET       = 0x50,
+       PRE_ISET       = 0x51,
+       PRE_OVPSET     = 0x52,
+       PRE_OCPSET     = 0x53,
+       PRE_OPPSET     = 0x54,
+       PRE_BACKLIGHT  = 0x55,
+       PRE_DISABLE    = 0x56, /* Disable output if 0 is copied here from a preset (1 is no change). */
+       PRE_BOOT       = 0x57, /* Enable output at boot if 1. */
+};
+
+enum rdtech_dps_state {
+       STATE_NORMAL = 0,
+       STATE_OVP    = 1,
+       STATE_OCP    = 2,
+       STATE_OPP    = 3,
+};
+
+enum rdtech_dps_mode {
+       MODE_CV      = 0,
+       MODE_CC      = 1,
+};
+
+SR_PRIV int rdtech_dps_get_reg(struct sr_modbus_dev_inst *modbus, uint16_t address, uint16_t *value);
+SR_PRIV int rdtech_dps_set_reg(struct sr_modbus_dev_inst *modbus, uint16_t address, uint16_t value);
+
+SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
+               uint16_t *model, uint16_t *version);
+
+SR_PRIV int rdtech_dps_capture_start(const struct sr_dev_inst *sdi);
+SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data);
+
+#endif
index f58d150d02694cc0d32a6cb2c0cdbe2f29861a87..bdc15b7de387e96b00133545b9cb02044beb235c 100644 (file)
@@ -34,7 +34,7 @@
 
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
-       SR_CONF_SERIALCOMM
+       SR_CONF_SERIALCOMM,
 };
 
 static const uint32_t drvopts[] = {
@@ -53,7 +53,7 @@ static const uint32_t devopts[] = {
        SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const uint32_t analog_devopts[] = {
+static const uint32_t devopts_cg_analog[] = {
        SR_CONF_NUM_VDIV | SR_CONF_GET,
        SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
@@ -126,56 +126,30 @@ static const uint64_t vdivs[][2] = {
        { 100, 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_sources_2_chans[] = {
+       "CH1", "CH2",
+       "EXT", "AC Line",
+       "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+       "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15",
+};
+
+static const char *trigger_sources_4_chans[] = {
+       "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",
+       "r", "f",
 };
 
 static const char *coupling[] = {
-       "AC",
-       "DC",
-       "GND",
+       "AC", "DC", "GND",
 };
 
 static const uint64_t probe_factor[] = {
-       1,
-       2,
-       5,
-       10,
-       20,
-       50,
-       100,
-       200,
-       500,
-       1000,
+       1, 2, 5, 10, 20, 50, 100, 200, 500, 1000,
 };
 
 /* Do not change the order of entries */
@@ -185,6 +159,16 @@ static const char *data_sources[] = {
        "Segmented",
 };
 
+static const struct rigol_ds_command std_cmd[] = {
+       { CMD_GET_HORIZ_TRIGGERPOS, ":TIM:OFFS?" },
+       { CMD_SET_HORIZ_TRIGGERPOS, ":TIM:OFFS %s" },
+};
+
+static const struct rigol_ds_command mso7000a_cmd[] = {
+       { CMD_GET_HORIZ_TRIGGERPOS, ":TIM:POS?" },
+       { CMD_SET_HORIZ_TRIGGERPOS, ":TIM:POS %s" },
+};
+
 enum vendor {
        RIGOL,
        AGILENT,
@@ -197,6 +181,8 @@ enum series {
        DS2000A,
        DSO1000,
        DS1000Z,
+       DS4000,
+       MSO7000A,
 };
 
 /* short name, full name */
@@ -206,8 +192,8 @@ static const struct rigol_ds_vendor supported_vendors[] = {
 };
 
 #define VENDOR(x) &supported_vendors[x]
-/* vendor, series, protocol, max timebase, min vdiv, number of horizontal divs,
- * live waveform samples, memory buffer samples */
+/* vendor, series/name, protocol, data format, 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},
@@ -221,66 +207,78 @@ static const struct rigol_ds_series supported_series[] = {
                {50, 1}, {2, 1000}, 12, 600, 20480},
        [DS1000Z] = {VENDOR(RIGOL), "DS1000Z", PROTOCOL_V4, FORMAT_IEEE488_2,
                {50, 1}, {1, 1000}, 12, 1200, 12000000},
+       [DS4000] = {VENDOR(RIGOL), "DS4000", PROTOCOL_V4, FORMAT_IEEE488_2,
+               {1000, 1}, {1, 1000}, 14, 1400, 14000},
+       [MSO7000A] = {VENDOR(AGILENT), "MSO7000A", PROTOCOL_V4, FORMAT_IEEE488_2,
+               {50, 1}, {2, 1000}, 10, 1000, 8000000},
 };
 
 #define SERIES(x) &supported_series[x]
+/*
+ * Use a macro to select the correct list of trigger sources and its length
+ * based on the number of analog channels and presence of digital channels.
+ */
+#define CH_INFO(num, digital) \
+       num, digital, trigger_sources_##num##_chans, \
+       digital ? ARRAY_SIZE(trigger_sources_##num##_chans) : (num + 2)
 /* 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(DS2000A), "MSO2072A", {5, 1000000000}, 2, true},
-       {SERIES(DS2000A), "MSO2102A", {5, 1000000000}, 2, true},
-       {SERIES(DS2000A), "MSO2202A", {2, 1000000000}, 2, true},
-       {SERIES(DS2000A), "MSO2302A", {1, 1000000000}, 2, true},
-       {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},
-       {SERIES(DS1000Z), "DS1054Z", {5, 1000000000}, 4, false},
-       {SERIES(DS1000Z), "DS1074Z", {5, 1000000000}, 4, false},
-       {SERIES(DS1000Z), "DS1104Z", {5, 1000000000}, 4, false},
-       {SERIES(DS1000Z), "DS1074Z-S", {5, 1000000000}, 4, false},
-       {SERIES(DS1000Z), "DS1104Z-S", {5, 1000000000}, 4, false},
-       {SERIES(DS1000Z), "DS1074Z Plus", {5, 1000000000}, 4, false},
-       {SERIES(DS1000Z), "DS1104Z Plus", {5, 1000000000}, 4, false},
-       {SERIES(DS1000Z), "MSO1074Z", {5, 1000000000}, 4, true},
-       {SERIES(DS1000Z), "MSO1104Z", {5, 1000000000}, 4, true},
-       {SERIES(DS1000Z), "MSO1074Z-S", {5, 1000000000}, 4, true},
-       {SERIES(DS1000Z), "MSO1104Z-S", {5, 1000000000}, 4, true},
+       {SERIES(VS5000), "VS5022", {20, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(VS5000), "VS5042", {10, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(VS5000), "VS5062", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(VS5000), "VS5102", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(VS5000), "VS5202", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(VS5000), "VS5022D", {20, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(VS5000), "VS5042D", {10, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(VS5000), "VS5062D", {5, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(VS5000), "VS5102D", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(VS5000), "VS5202D", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(DS1000), "DS1052E", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS1000), "DS1102E", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS1000), "DS1152E", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS1000), "DS1052D", {5, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(DS1000), "DS1102D", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(DS1000), "DS1152D", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(DS2000), "DS2072", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS2000), "DS2102", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS2000), "DS2202", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS2000), "DS2302", {1, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS2000A), "DS2072A", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS2000A), "DS2102A", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS2000A), "DS2202A", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS2000A), "DS2302A", {1, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DS2000A), "MSO2072A", {5, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(DS2000A), "MSO2102A", {5, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(DS2000A), "MSO2202A", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(DS2000A), "MSO2302A", {1, 1000000000}, CH_INFO(2, true), std_cmd},
+       {SERIES(DSO1000), "DSO1002A", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DSO1000), "DSO1004A", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DSO1000), "DSO1012A", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DSO1000), "DSO1014A", {2, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DSO1000), "DSO1022A", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+       {SERIES(DSO1000), "DSO1024A", {2, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DS1000Z), "DS1054Z", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DS1000Z), "DS1074Z", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DS1000Z), "DS1104Z", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DS1000Z), "DS1074Z-S", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DS1000Z), "DS1104Z-S", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DS1000Z), "DS1074Z Plus", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DS1000Z), "DS1104Z Plus", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+       {SERIES(DS1000Z), "MSO1074Z", {5, 1000000000}, CH_INFO(4, true), std_cmd},
+       {SERIES(DS1000Z), "MSO1104Z", {5, 1000000000}, CH_INFO(4, true), std_cmd},
+       {SERIES(DS1000Z), "MSO1074Z-S", {5, 1000000000}, CH_INFO(4, true), std_cmd},
+       {SERIES(DS1000Z), "MSO1104Z-S", {5, 1000000000}, CH_INFO(4, true), std_cmd},
+       {SERIES(DS4000), "DS4024", {1, 1000000000}, CH_INFO(4, false), std_cmd},
+       /* TODO: Digital channels are not yet supported on MSO7000A. */
+       {SERIES(MSO7000A), "MSO7034A", {2, 1000000000}, CH_INFO(4, false), mso7000a_cmd},
 };
 
 static struct sr_dev_driver rigol_ds_driver_info;
 
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
        unsigned int i;
 
-       devc = priv;
        g_free(devc->data);
        g_free(devc->buffer);
        for (i = 0; i < ARRAY_SIZE(devc->coupling); i++)
@@ -288,12 +286,11 @@ static void clear_helper(void *priv)
        g_free(devc->trigger_source);
        g_free(devc->trigger_slope);
        g_free(devc->analog_groups);
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
@@ -400,18 +397,18 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
                                devc->digital_group);
        }
 
-       for (i = 0; i < NUM_TIMEBASE; i++) {
+       for (i = 0; i < ARRAY_SIZE(timebases); 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++) {
+       for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
                if (!memcmp(&devc->model->series->min_vdiv,
                                        &vdivs[i], sizeof(uint64_t[2]))) {
                        devc->vdivs = &vdivs[i];
-                       devc->num_vdivs = NUM_VDIV - i;
+                       devc->num_vdivs = ARRAY_SIZE(vdivs) - i;
                }
        }
 
@@ -445,8 +442,6 @@ static int dev_open(struct sr_dev_inst *sdi)
                return SR_ERR;
        }
 
-       sdi->status = SR_ST_ACTIVE;
-
        return SR_OK;
 }
 
@@ -455,22 +450,16 @@ 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 (!scpi)
+               return SR_ERR_BUG;
+
        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;
+       return sr_scpi_close(scpi);
 }
 
 static int analog_frame_size(const struct sr_dev_inst *sdi)
@@ -513,8 +502,8 @@ static int digital_frame_size(const struct sr_dev_inst *sdi)
        }
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_channel *ch;
@@ -657,46 +646,34 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       uint64_t p, q;
+       uint64_t p;
        double t_dbl;
-       unsigned int i, j;
-       int ret;
+       int ret, idx, i;
        const char *tmp_str;
        char buffer[16];
 
        devc = sdi->priv;
 
-       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 (key) {
        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')) {
-                       sr_err("Unknown trigger slope: '%s'.",
-                              (tmp_str) ? tmp_str : "NULL");
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
                        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;
+               devc->trigger_slope = g_strdup((trigger_slopes[idx][0] == 'r') ? "POS" : "NEG");
+               return rigol_ds_config_set(sdi, ":TRIG:EDGE:SLOP %s", devc->trigger_slope);
        case SR_CONF_HORIZ_TRIGGERPOS:
                t_dbl = g_variant_get_double(data);
                if (t_dbl < 0.0 || t_dbl > 1.0) {
@@ -708,127 +685,73 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                 * 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;
+               return rigol_ds_config_set(sdi,
+                       devc->model->cmds[CMD_SET_HORIZ_TRIGGERPOS].str, buffer);
        case SR_CONF_TRIGGER_LEVEL:
                t_dbl = g_variant_get_double(data);
                g_ascii_formatd(buffer, sizeof(buffer), "%.3f", t_dbl);
                ret = rigol_ds_config_set(sdi, ":TRIG:EDGE:LEV %s", buffer);
                if (ret == SR_OK)
                        devc->trigger_level = t_dbl;
-               break;
+               return ret;
        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) {
-                       sr_err("Invalid timebase index: %d.", i);
-                       ret = SR_ERR_ARG;
-               }
-               break;
+               if ((idx = std_u64_tuple_idx(data, devc->timebases, devc->num_timebases)) < 0)
+                       return SR_ERR_ARG;
+               devc->timebase = (float)devc->timebases[idx][0] / devc->timebases[idx][1];
+               g_ascii_formatd(buffer, sizeof(buffer), "%.9f",
+                               devc->timebase);
+               return rigol_ds_config_set(sdi, ":TIM:SCAL %s", buffer);
        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)) {
-                       sr_err("Invalid trigger source index: %d.", i);
-                       ret = SR_ERR_ARG;
-               }
-               break;
+               if ((idx = std_str_idx(data, devc->model->trigger_sources, devc->model->num_trigger_sources)) < 0)
+                       return SR_ERR_ARG;
+               g_free(devc->trigger_source);
+               devc->trigger_source = g_strdup(devc->model->trigger_sources[idx]);
+               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;
+               return rigol_ds_config_set(sdi, ":TRIG:EDGE:SOUR %s", tmp_str);
        case SR_CONF_VDIV:
-               if (!cg) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               }
-               g_variant_get(data, "(tt)", &p, &q);
-               for (i = 0; i < devc->model->analog_channels; 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);
-                               }
-                               sr_err("Invalid vdiv index: %d.", j);
-                               return SR_ERR_ARG;
-                       }
-               }
-               sr_dbg("Didn't set vdiv, unknown channel(group).");
-               return SR_ERR_NA;
+               if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+                       return SR_ERR_ARG;
+               devc->vdiv[i] = (float)vdivs[idx][0] / vdivs[idx][1];
+               g_ascii_formatd(buffer, sizeof(buffer), "%.3f", devc->vdiv[i]);
+               return rigol_ds_config_set(sdi, ":CHAN%d:SCAL %s", i + 1, buffer);
        case SR_CONF_COUPLING:
-               if (!cg) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               }
-               tmp_str = g_variant_get_string(data, NULL);
-               for (i = 0; i < devc->model->analog_channels; 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]);
-                                       }
-                               }
-                               sr_err("Invalid coupling index: %d.", j);
-                               return SR_ERR_ARG;
-                       }
-               }
-               sr_dbg("Didn't set coupling, unknown channel(group).");
-               return SR_ERR_NA;
+               if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(coupling))) < 0)
+                       return SR_ERR_ARG;
+               g_free(devc->coupling[i]);
+               devc->coupling[i] = g_strdup(coupling[idx]);
+               return rigol_ds_config_set(sdi, ":CHAN%d:COUP %s", i + 1, devc->coupling[i]);
        case SR_CONF_PROBE_FACTOR:
-               if (!cg) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               }
+               if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(probe_factor))) < 0)
+                       return SR_ERR_ARG;
                p = g_variant_get_uint64(data);
-               for (i = 0; i < devc->model->analog_channels; i++) {
-                       if (cg == devc->analog_groups[i]) {
-                               for (j = 0; j < ARRAY_SIZE(probe_factor); j++) {
-                                       if (p == probe_factor[j]) {
-                                               devc->attenuation[i] = p;
-                                               ret = rigol_ds_config_set(sdi, ":CHAN%d:PROB %"PRIu64,
-                                                                         i + 1, p);
-                                               if (ret == SR_OK)
-                                                       rigol_ds_get_dev_cfg_vertical(sdi);
-                                               return ret;
-                                       }
-                               }
-                               sr_err("Invalid probe factor: %"PRIu64".", p);
-                               return SR_ERR_ARG;
-                       }
-               }
-               sr_dbg("Didn't set probe factor, unknown channel(group).");
-               return SR_ERR_NA;
+               devc->attenuation[i] = probe_factor[idx];
+               ret = rigol_ds_config_set(sdi, ":CHAN%d:PROB %"PRIu64, i + 1, p);
+               if (ret == SR_OK)
+                       rigol_ds_get_dev_cfg_vertical(sdi);
+               return ret;
        case SR_CONF_DATA_SOURCE:
                tmp_str = g_variant_get_string(data, NULL);
                if (!strcmp(tmp_str, "Live"))
@@ -848,97 +771,50 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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;
-
-       /* SR_CONF_SCAN_OPTIONS is always valid, regardless of sdi or channel group. */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* If sdi is NULL, nothing except SR_CONF_DEVICE_OPTIONS can be provided. */
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* Every other option requires a valid device instance. */
-       if (!sdi)
-               return SR_ERR_ARG;
-       devc = sdi->priv;
+       struct dev_context *devc;
 
-       /* 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;
-       }
+       devc = (sdi) ? sdi->priv : NULL;
 
        switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
        case SR_CONF_DEVICE_OPTIONS:
-               if (!cg) {
-                       /* If cg is NULL, only the SR_CONF_DEVICE_OPTIONS that are not
-                        * specific to a channel group must be returned. */
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-                       return SR_OK;
-               }
+               if (!cg)
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+               if (!devc)
+                       return SR_ERR_ARG;
                if (cg == devc->digital_group) {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               NULL, 0, sizeof(uint32_t));
+                       *data = std_gvar_array_u32(NULL, 0);
                        return SR_OK;
                } else {
-                       for (i = 0; i < devc->model->analog_channels; i++) {
-                               if (cg == devc->analog_groups[i]) {
-                                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                               analog_devopts, ARRAY_SIZE(analog_devopts), sizeof(uint32_t));
-                                       return SR_OK;
-                               }
-                       }
-                       return SR_ERR_NA;
+                       if (std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels) < 0)
+                               return SR_ERR_ARG;
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog));
+                       return SR_OK;
                }
                break;
        case SR_CONF_COUPLING:
-               if (!cg) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               }
-               *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(coupling));
                break;
        case SR_CONF_PROBE_FACTOR:
-               if (!cg) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               }
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
-                       probe_factor, ARRAY_SIZE(probe_factor), sizeof(uint64_t));
+               *data = std_gvar_array_u64(ARRAY_AND_SIZE(probe_factor));
                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.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               }
-               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-               for (i = 0; i < devc->num_vdivs; 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);
+               *data = std_gvar_tuple_array(devc->vdivs, devc->num_vdivs);
                break;
        case SR_CONF_TIMEBASE:
                if (!devc)
@@ -946,24 +822,16 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                        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);
+               *data = std_gvar_tuple_array(devc->timebases, devc->num_timebases);
                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);
+               *data = g_variant_new_strv(devc->model->trigger_sources, devc->model->num_trigger_sources);
                break;
        case SR_CONF_TRIGGER_SLOPE:
-               *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_slopes));
                break;
        case SR_CONF_DATA_SOURCE:
                if (!devc)
@@ -977,7 +845,7 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                        *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));
+                       *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
                        break;
                }
                break;
@@ -997,9 +865,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        gboolean some_digital;
        GSList *l;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        scpi = sdi->conn;
        devc = sdi->priv;
 
@@ -1119,11 +984,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE) {
-               sr_err("Device inactive, can't stop acquisition.");
-               return SR_ERR;
-       }
-
        std_session_send_df_end(sdi);
 
        g_slist_free(devc->enabled_channels);
index 39462c280ba1830ec208ac5390cebaa1b64e9621..a533f12a1f12451d8acd609ccd407ab8bb0f9cf3 100644 (file)
@@ -221,7 +221,7 @@ static int rigol_ds_check_stop(const struct sr_dev_inst *sdi)
                return SR_OK;
 
        if (ch->type == SR_CHANNEL_LOGIC) {
-               if (rigol_ds_config_set(sdi->conn, ":WAV:SOUR LA") != SR_OK)
+               if (rigol_ds_config_set(sdi, ":WAV:SOUR LA") != SR_OK)
                        return SR_ERR;
        } else {
                if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
@@ -337,8 +337,12 @@ SR_PRIV int rigol_ds_capture_start(const struct sr_dev_inst *sdi)
        if (!(devc = sdi->priv))
                return SR_ERR;
 
-       sr_dbg("Starting data capture for frameset %" PRIu64 " of %" PRIu64,
-              devc->num_frames + 1, devc->limit_frames);
+       if (devc->limit_frames == 0)
+               sr_dbg("Starting data capture for frameset %" PRIu64,
+                      devc->num_frames + 1);
+       else
+               sr_dbg("Starting data capture for frameset %" PRIu64 " of %"
+                      PRIu64, devc->num_frames + 1, devc->limit_frames);
 
        switch (devc->model->series->protocol) {
        case PROTOCOL_V1:
@@ -440,7 +444,7 @@ SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi)
                break;
        case PROTOCOL_V3:
                if (ch->type == SR_CHANNEL_LOGIC) {
-                       if (rigol_ds_config_set(sdi->conn, ":WAV:SOUR LA") != SR_OK)
+                       if (rigol_ds_config_set(sdi, ":WAV:SOUR LA") != SR_OK)
                                return SR_ERR;
                } else {
                        if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
@@ -474,10 +478,20 @@ SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi)
 
        if (devc->model->series->protocol >= PROTOCOL_V3 &&
                        ch->type == SR_CHANNEL_ANALOG) {
+               /* Vertical increment. */
+               if (sr_scpi_get_float(sdi->conn, ":WAV:YINC?",
+                               &devc->vert_inc[ch->index]) != SR_OK)
+                       return SR_ERR;
+               /* Vertical origin. */
+               if (sr_scpi_get_float(sdi->conn, ":WAV:YOR?",
+                       &devc->vert_origin[ch->index]) != SR_OK)
+                       return SR_ERR;
                /* Vertical reference. */
                if (sr_scpi_get_int(sdi->conn, ":WAV:YREF?",
                                &devc->vert_reference[ch->index]) != SR_OK)
                        return SR_ERR;
+       } else if (ch->type == SR_CHANNEL_ANALOG) {
+               devc->vert_inc[ch->index] = devc->vdiv[ch->index] / 25.6;
        }
 
        rigol_ds_set_wait_event(devc, WAIT_BLOCK);
@@ -557,7 +571,7 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
        struct sr_analog_meaning meaning;
        struct sr_analog_spec spec;
        struct sr_datafeed_logic logic;
-       double vdiv, offset;
+       double vdiv, offset, origin;
        int len, i, vref;
        struct sr_channel *ch;
        gsize expected_data_bytes;
@@ -631,10 +645,10 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
                                /* Still reading the header. */
                                return TRUE;
                        if (len == -1) {
-                               sr_err("Read error, aborting capture.");
+                               sr_err("Error while reading block header, aborting capture.");
                                packet.type = SR_DF_FRAME_END;
                                sr_session_send(sdi, &packet);
-                               sdi->driver->dev_acquisition_stop(sdi);
+                               sr_dev_acquisition_stop(sdi);
                                return TRUE;
                        }
                        /* At slow timebases in live capture the DS2072
@@ -664,10 +678,10 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
        len = sr_scpi_read_data(scpi, (char *)devc->buffer, len);
 
        if (len == -1) {
-               sr_err("Read error, aborting capture.");
+               sr_err("Error while reading block data, aborting capture.");
                packet.type = SR_DF_FRAME_END;
                sr_session_send(sdi, &packet);
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        }
 
@@ -677,11 +691,12 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
 
        if (ch->type == SR_CHANNEL_ANALOG) {
                vref = devc->vert_reference[ch->index];
-               vdiv = devc->vdiv[ch->index] / 25.6;
+               vdiv = devc->vert_inc[ch->index];
+               origin = devc->vert_origin[ch->index];
                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;
+                               devc->data[i] = ((int)devc->buffer[i] - vref - origin) * vdiv;
                else
                        for (i = 0; i < len; i++)
                                devc->data[i] = (128 - devc->buffer[i]) * vdiv - offset;
@@ -723,11 +738,12 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
                        if (devc->data_source != DATA_SOURCE_LIVE)
                                rigol_ds_set_wait_event(devc, WAIT_BLOCK);
                }
-               if (!sr_scpi_read_complete(scpi)) {
+               /* End acquisition when data for all channels is acquired. */
+               if (!sr_scpi_read_complete(scpi) && !devc->channel_entry->next) {
                        sr_err("Read should have been completed");
                        packet.type = SR_DF_FRAME_END;
                        sr_session_send(sdi, &packet);
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                        return TRUE;
                }
                devc->num_block_read = 0;
@@ -765,7 +781,7 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
 
                if (++devc->num_frames == devc->limit_frames) {
                        /* Last frame, stop capture. */
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                } else {
                        /* Get the next frame, starting with the first channel. */
                        devc->channel_entry = devc->enabled_channels;
@@ -867,7 +883,8 @@ SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi)
        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)
+       if (sr_scpi_get_float(sdi->conn, devc->model->cmds[CMD_GET_HORIZ_TRIGGERPOS].str,
+                       &devc->horiz_triggerpos) != SR_OK)
                return SR_ERR;
        sr_dbg("Current horizontal trigger position %g", devc->horiz_triggerpos);
 
index e77797047eac5c160d21ff21e4d1e1b424f5b394..86669b40de0ebbb6e5727d86340e136b4ad35604 100644 (file)
@@ -74,12 +74,25 @@ struct rigol_ds_series {
        int buffer_samples;
 };
 
+enum cmds {
+       CMD_GET_HORIZ_TRIGGERPOS,
+       CMD_SET_HORIZ_TRIGGERPOS,
+};
+
+struct rigol_ds_command {
+       int cmd;
+       const char *str;
+};
+
 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;
+       const char **trigger_sources;
+       unsigned int num_trigger_sources;
+       const struct rigol_ds_command *cmds;
 };
 
 enum wait_events {
@@ -89,9 +102,7 @@ enum wait_events {
        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;
 
@@ -120,15 +131,15 @@ struct dev_context {
        float attenuation[MAX_ANALOG_CHANNELS];
        float vdiv[MAX_ANALOG_CHANNELS];
        int vert_reference[MAX_ANALOG_CHANNELS];
+       float vert_origin[MAX_ANALOG_CHANNELS];
        float vert_offset[MAX_ANALOG_CHANNELS];
+       float vert_inc[MAX_ANALOG_CHANNELS];
        char *trigger_source;
        float horiz_triggerpos;
        char *trigger_slope;
        float trigger_level;
        char *coupling[MAX_ANALOG_CHANNELS];
 
-       /* Operational state */
-
        /* Number of frames received in total. */
        uint64_t num_frames;
        /* GSList entry for the current channel. */
index 7b971b2b4231c3ac1cc3aa13d8392d169bb4ebd0..6a719e878015dd5b45baa793478f8f93e620f89f 100644 (file)
@@ -105,7 +105,7 @@ static int rs_init_device(struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static struct sr_dev_inst *rs_probe_serial_device(struct sr_scpi_dev_inst *scpi)
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 {
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
@@ -152,34 +152,17 @@ fail:
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       return sr_scpi_scan(di->context, options, rs_probe_serial_device);
-}
-
-static int dev_clear(const struct sr_dev_driver *di)
-{
-       return std_dev_clear(di, NULL);
+       return sr_scpi_scan(di->context, options, probe_device);
 }
 
 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;
-
-       sdi->status = SR_ST_ACTIVE;
-
-       return SR_OK;
+       return sr_scpi_open(sdi->conn);
 }
 
 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;
+       return sr_scpi_close(sdi->conn);
 }
 
 static int config_get(uint32_t key, GVariant **data,
@@ -215,9 +198,6 @@ static int config_set(uint32_t key, GVariant *data,
        if (!sdi)
                return SR_ERR_ARG;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        switch (key) {
        case SR_CONF_OUTPUT_FREQUENCY:
                value_f = g_variant_get_double(data);
@@ -237,38 +217,7 @@ static int config_set(uint32_t key, GVariant *data,
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       (void)sdi;
-       (void)cg;
-
-       /* Return drvopts without sdi (and devopts with sdi, see below). */
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       switch (key) {
-       case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi)
-{
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 SR_PRIV struct sr_dev_driver rohde_schwarz_sme_0x_driver_info = {
@@ -279,13 +228,13 @@ SR_PRIV struct sr_dev_driver rohde_schwarz_sme_0x_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = dev_clear,
+       .dev_clear = std_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_start = std_dummy_dev_acquisition_start,
        .dev_acquisition_stop = std_serial_dev_acquisition_stop,
        .context = NULL,
 };
index 7b66c814de32d46e9ffd24faab3d0fc2bb59a309..65e5c6d193bcaa43ce1d19923eb6261cc9f995be 100644 (file)
@@ -41,9 +41,7 @@ struct rs_device_model {
        double power_min;
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        const struct rs_device_model *model_config;
 };
 
diff --git a/src/hardware/saleae-logic-pro/api.c b/src/hardware/saleae-logic-pro/api.c
new file mode 100644 (file)
index 0000000..7b8582d
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Jan Luebbe <jluebbe@lasnet.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 <config.h>
+#include <glib.h>
+#include <string.h>
+#include "protocol.h"
+
+#define BUF_COUNT 512
+#define BUF_SIZE (16 * 1024)
+#define BUF_TIMEOUT 1000
+
+SR_PRIV struct sr_dev_driver saleae_logic_pro_driver_info;
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_CONN | SR_CONF_GET,
+       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const char *channel_names[] = {
+       "0", "1", "2", "3", "4", "5", "6", "7",
+       "8", "9", "10", "11", "12", "13", "14", "15",
+};
+
+static const uint64_t samplerates[] = {
+       SR_MHZ(1),
+       SR_MHZ(2),
+       SR_KHZ(2500),
+       SR_MHZ(10),
+       SR_MHZ(25),
+       SR_MHZ(50),
+};
+
+#define FW_HEADER_SIZE 7
+#define FW_MAX_PART_SIZE (4 * 1024)
+
+static int upload_firmware(struct sr_context *ctx, libusb_device *dev, const char *name)
+{
+       struct libusb_device_handle *hdl = NULL;
+       unsigned char *firmware = NULL;
+       int ret = SR_ERR;
+       size_t fw_size, fw_offset = 0;
+       uint32_t part_address = 0;
+       uint16_t part_size = 0;
+       uint8_t part_final = 0;
+
+       firmware = sr_resource_load(ctx, SR_RESOURCE_FIRMWARE,
+                                   name, &fw_size, 256 * 1024);
+       if (!firmware)
+               goto out;
+
+       sr_info("Uploading firmware '%s'.", name);
+
+       if (libusb_open(dev, &hdl) != 0)
+               goto out;
+
+       while ((fw_offset + FW_HEADER_SIZE) <= fw_size) {
+               part_size = GUINT16_FROM_LE(*(uint16_t*)(firmware + fw_offset));
+               part_address = GUINT32_FROM_LE(*(uint32_t*)(firmware + fw_offset + 2));
+               part_final = *(uint8_t*)(firmware + fw_offset + 6);
+               if (part_size > FW_MAX_PART_SIZE) {
+                       sr_err("Part too large (%d).", part_size);
+                       goto out;
+               }
+               fw_offset += FW_HEADER_SIZE;
+               if ((fw_offset + part_size) > fw_size) {
+                       sr_err("Truncated firmware file.");
+                       goto out;
+               }
+               ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR |
+                                             LIBUSB_ENDPOINT_OUT, 0xa0,
+                                             part_address & 0xffff, part_address >> 16,
+                                             firmware + fw_offset, part_size,
+                                             100);
+               if (ret < 0) {
+                       sr_err("Unable to send firmware to device: %s.",
+                              libusb_error_name(ret));
+                       ret = SR_ERR;
+                       goto out;
+               }
+               if (part_size)
+                       sr_spew("Uploaded %d bytes.", part_size);
+               else
+                       sr_info("Started firmware at 0x%x.", part_address);
+               fw_offset += part_size;
+       }
+
+       if ((!part_final) || (part_size != 0)) {
+               sr_err("Missing final part.");
+               goto out;
+       }
+
+       ret = SR_OK;
+
+       sr_info("Firmware upload done.");
+
+ out:
+       if (hdl)
+               libusb_close(hdl);
+
+       g_free(firmware);
+
+       return ret;
+}
+
+static gboolean scan_firmware(libusb_device *dev)
+{
+       struct libusb_device_descriptor des;
+       struct libusb_device_handle *hdl;
+       gboolean ret;
+       unsigned char strdesc[64];
+
+       hdl = NULL;
+       ret = FALSE;
+
+       libusb_get_device_descriptor(dev, &des);
+
+       if (libusb_open(dev, &hdl) != 0)
+               goto out;
+
+       if (libusb_get_string_descriptor_ascii(hdl,
+               des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
+               goto out;
+       if (strcmp((const char *)strdesc, "Saleae"))
+               goto out;
+
+       if (libusb_get_string_descriptor_ascii(hdl,
+               des.iProduct, strdesc, sizeof(strdesc)) < 0)
+               goto out;
+       if (strcmp((const char *)strdesc, "Logic Pro"))
+               goto out;
+
+       ret = TRUE;
+
+out:
+       if (hdl)
+               libusb_close(hdl);
+
+       return ret;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       struct drv_context *drvc;
+       struct dev_context *devc;
+       struct sr_dev_inst *sdi;
+       GSList *devices, *conn_devices;
+       libusb_device **devlist;
+       struct libusb_device_descriptor des;
+       const char *conn;
+       char connection_id[64];
+       gboolean fw_loaded = FALSE;
+
+       devices = NULL;
+       conn_devices = NULL;
+       drvc = di->context;
+       drvc->instances = NULL;
+
+       conn = NULL;
+       for (GSList *l = options; l; l = l->next) {
+               struct sr_config *src = l->data;
+
+               switch (src->key) {
+               case SR_CONF_CONN:
+                       conn = g_variant_get_string(src->data, NULL);
+                       break;
+               }
+       }
+
+       libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+       for (unsigned int i = 0; devlist[i]; i++) {
+               libusb_get_device_descriptor(devlist[i], &des);
+
+               if (des.idVendor != 0x21a9 || des.idProduct != 0x1006)
+                       continue;
+
+               if (!scan_firmware(devlist[i])) {
+                       const char *fwname;
+                       sr_info("Found a Logic Pro 16 device (no firmware loaded).");
+                       fwname = "saleae-logicpro16-fx3.fw";
+                       if (upload_firmware(drvc->sr_ctx, devlist[i],
+                                           fwname) != SR_OK) {
+                               sr_err("Firmware upload failed, name %s.", fwname);
+                               continue;
+                       };
+                       fw_loaded = TRUE;
+               }
+
+       }
+       if (fw_loaded) {
+               /* Give the device some time to come back and scan again */
+               libusb_free_device_list(devlist, 1);
+               g_usleep(500 * 1000);
+               libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+       }
+       if (conn)
+               conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+       for (unsigned int i = 0; devlist[i]; i++) {
+               if (conn_devices) {
+                       struct sr_usb_dev_inst *usb = NULL;
+                       GSList *l;
+
+                       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;
+               }
+
+               libusb_get_device_descriptor(devlist[i], &des);
+
+               if (des.idVendor != 0x21a9 || des.idProduct != 0x1006)
+                       continue;
+
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
+
+               sdi = g_malloc0(sizeof(struct sr_dev_inst));
+               sdi->status = SR_ST_INITIALIZING;
+               sdi->vendor = g_strdup("Saleae");
+               sdi->model = g_strdup("Logic Pro 16");
+               sdi->connection_id = g_strdup(connection_id);
+
+               for (unsigned int j = 0; j < ARRAY_SIZE(channel_names); j++)
+                       sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE,
+                                      channel_names[j]);
+
+               sr_dbg("Found a Logic Pro 16 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);
+
+               devc = g_malloc0(sizeof(struct dev_context));
+               sdi->priv = devc;
+               devices = g_slist_append(devices, sdi);
+
+       }
+       g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+       libusb_free_device_list(devlist, 1);
+
+       return std_scan_complete(di, devices);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       struct sr_dev_driver *di = sdi->driver;
+       struct drv_context *drvc = di->context;
+       struct dev_context *devc = sdi->priv;
+       struct sr_usb_dev_inst *usb = sdi->conn;
+       int ret;
+
+       if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
+               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;
+       }
+
+       /* Configure default samplerate. */
+       if (devc->dig_samplerate == 0)
+               devc->dig_samplerate = samplerates[3];
+
+       return saleae_logic_pro_init(sdi);
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       sr_usb_close(sdi->conn);
+
+       return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct sr_usb_dev_inst *usb;
+       struct dev_context *devc;
+
+       (void)cg;
+
+       switch (key) {
+       case SR_CONF_CONN:
+               if (!sdi || !sdi->conn)
+                       return SR_ERR_ARG;
+               usb = sdi->conn;
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
+               break;
+       case SR_CONF_SAMPLERATE:
+               if (!sdi)
+                       return SR_ERR;
+               devc = sdi->priv;
+               *data = g_variant_new_uint64(devc->dig_samplerate);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+
+       (void)cg;
+
+       devc = sdi->priv;
+
+       switch (key) {
+       case SR_CONF_SAMPLERATE:
+               devc->dig_samplerate = g_variant_get_uint64(data);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_SAMPLERATE:
+               *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static void dev_acquisition_abort(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       unsigned int i;
+
+       for (i = 0; i < devc->num_transfers; i++) {
+               if (devc->transfers[i])
+                       libusb_cancel_transfer(devc->transfers[i]);
+       }
+}
+
+static int dev_acquisition_handle(int fd, int revents, void *cb_data)
+{
+       struct sr_dev_inst *sdi = cb_data;
+       struct drv_context *drvc = sdi->driver->context;
+       struct timeval tv = ALL_ZERO;
+
+       (void)fd;
+
+       libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+       /* Handle timeout */
+       if (!revents)
+               sr_dev_acquisition_stop(sdi);
+
+       return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       struct drv_context *drvc = sdi->driver->context;
+       struct libusb_transfer *transfer;
+       struct sr_usb_dev_inst *usb;
+       uint8_t *buf;
+       unsigned int i, ret;
+
+       ret = saleae_logic_pro_prepare(sdi);
+       if (ret != SR_OK)
+               return ret;
+
+       usb = sdi->conn;
+
+       devc->conv_buffer = g_malloc(CONV_BUFFER_SIZE);
+
+       devc->num_transfers = BUF_COUNT;
+       devc->transfers = g_malloc0(sizeof(*devc->transfers) * BUF_COUNT);
+       for (i = 0; i < devc->num_transfers; i++) {
+               buf = g_malloc(BUF_SIZE);
+               transfer = libusb_alloc_transfer(0);
+               libusb_fill_bulk_transfer(transfer, usb->devhdl,
+                       2 | LIBUSB_ENDPOINT_IN, buf, BUF_SIZE,
+                       saleae_logic_pro_receive_data, (void *)sdi, 0);
+               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);
+                       dev_acquisition_abort(sdi);
+                       return SR_ERR;
+               }
+               devc->transfers[i] = transfer;
+               devc->submitted_transfers++;
+       }
+
+       usb_source_add(sdi->session, drvc->sr_ctx, BUF_TIMEOUT, dev_acquisition_handle, (void *)sdi);
+
+       std_session_send_df_header(sdi);
+
+       saleae_logic_pro_start(sdi);
+       if (ret != SR_OK)
+               return ret;
+
+       return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       struct drv_context *drvc = sdi->driver->context;
+
+       saleae_logic_pro_stop(sdi);
+
+       std_session_send_df_end(sdi);
+
+       usb_source_remove(sdi->session, drvc->sr_ctx);
+
+       g_free(devc->conv_buffer);
+
+       return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver saleae_logic_pro_driver_info = {
+       .name = "saleae-logic-pro",
+       .longname = "Saleae Logic Pro",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .context = NULL,
+};
+
+SR_REGISTER_DEV_DRIVER(saleae_logic_pro_driver_info);
diff --git a/src/hardware/saleae-logic-pro/protocol.c b/src/hardware/saleae-logic-pro/protocol.c
new file mode 100644 (file)
index 0000000..351032a
--- /dev/null
@@ -0,0 +1,911 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Jan Luebbe <jluebbe@lasnet.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 <config.h>
+#include <string.h>
+#include "protocol.h"
+
+#define COMMAND_START_CAPTURE  0x01
+#define COMMAND_STOP_CAPTURE   0x02
+#define COMMAND_READ_EEPROM    0x07
+#define COMMAND_INIT_BITSTREAM  0x7e
+#define COMMAND_SEND_BITSTREAM  0x7f
+#define COMMAND_WRITE_REG      0x80
+#define COMMAND_READ_REG       0x81
+#define COMMAND_READ_TEMP      0x86
+#define COMMAND_WRITE_I2C      0x87
+#define COMMAND_READ_I2C       0x88
+#define COMMAND_WAKE_I2C       0x89
+#define COMMAND_READ_FW_VER    0x8b
+
+#define REG_ADC_IDX            0x03
+#define REG_ADC_VAL_LSB                0x04
+#define REG_ADC_VAL_MSB                0x05
+#define REG_LED_RED            0x0f
+#define REG_LED_GREEN          0x10
+#define REG_LED_BLUE           0x11
+#define REG_STATUS             0x40
+
+static void iterate_lfsr(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       uint32_t lfsr = devc->lfsr;
+       int i, max;
+
+       max = (lfsr & 0x1f) + 34;
+       for (i = 0; i <= max; i++) {
+               lfsr = (lfsr >> 1) |            \
+                       ((lfsr ^                \
+                         (lfsr >> 1) ^         \
+                         (lfsr >> 21) ^        \
+                         (lfsr >> 31)          \
+                         ) << 31);
+       }
+       sr_spew("Iterate 0x%08x -> 0x%08x", devc->lfsr, lfsr);
+       devc->lfsr = lfsr;
+}
+
+static void encrypt(const struct sr_dev_inst *sdi, const uint8_t *in, uint8_t *out, uint16_t len)
+{
+       struct dev_context *devc = sdi->priv;
+       uint32_t lfsr = devc->lfsr;
+       uint8_t value, mask;
+       int i;
+
+       for (i = 0; i < len; i++) {
+               value = in[i];
+               mask = lfsr >> (i % 4 * 8);
+               if (i == 0)
+                       value = (value & 0x28) | ((value ^ mask) & ~0x28);
+               else
+                       value = value ^ mask;
+               out[i] = value;
+       }
+       iterate_lfsr(sdi);
+}
+
+static void decrypt(const struct sr_dev_inst *sdi, uint8_t *data, uint16_t len)
+{
+       struct dev_context *devc = sdi->priv;
+       uint32_t lfsr = devc->lfsr;
+       int i;
+
+       for (i = 0; i < len; i++)
+               data[i] ^= (lfsr >> (i % 4 * 8));
+       iterate_lfsr(sdi);
+}
+
+static int transact(const struct sr_dev_inst *sdi,
+                   const uint8_t *req, uint16_t req_len,
+                   uint8_t *rsp, uint16_t rsp_len)
+{
+       struct sr_usb_dev_inst *usb = sdi->conn;
+       uint8_t *req_enc;
+       uint8_t rsp_dummy[1] = {};
+       int ret, xfer;
+
+       if (req_len < 2 || req_len > 1024 || rsp_len > 128 ||
+           !req || (rsp_len > 0 && !rsp))
+               return SR_ERR_ARG;
+
+       req_enc = g_malloc(req_len);
+       encrypt(sdi, req, req_enc, req_len);
+
+       ret = libusb_bulk_transfer(usb->devhdl, 1, req_enc, req_len, &xfer, 1000);
+       if (ret != 0) {
+               sr_dbg("Failed to send request 0x%02x: %s.",
+                      req[1], libusb_error_name(ret));
+               return SR_ERR;
+       }
+       if (xfer != req_len) {
+               sr_dbg("Failed to send request 0x%02x: incorrect length "
+                      "%d != %d.", req[1], xfer, req_len);
+               return SR_ERR;
+       }
+
+       if (req[0] == 0x20) { /* Reseed. */
+               return SR_OK;
+       } else if (rsp_len == 0) {
+               rsp = rsp_dummy;
+               rsp_len = sizeof(rsp_dummy);
+       }
+
+       ret = libusb_bulk_transfer(usb->devhdl, 0x80 | 1, rsp, rsp_len,
+                                  &xfer, 1000);
+       if (ret != 0) {
+               sr_dbg("Failed to receive response to request 0x%02x: %s.",
+                      req[1], libusb_error_name(ret));
+               return SR_ERR;
+       }
+       if (xfer != rsp_len) {
+               sr_dbg("Failed to receive response to request 0x%02x: "
+                      "incorrect length %d != %d.", req[1], xfer, rsp_len);
+               return SR_ERR;
+       }
+
+       decrypt(sdi, rsp, rsp_len);
+
+       return SR_OK;
+}
+
+static int reseed(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       uint8_t req[] = {0x20, 0x24, 0x4b, 0x35, 0x8e};
+
+       devc->lfsr = 0;
+       return transact(sdi, req, sizeof(req), NULL, 0);
+}
+
+static int write_regs(const struct sr_dev_inst *sdi, uint8_t (*regs)[2], uint8_t cnt)
+{
+       uint8_t req[64];
+       int i;
+
+       if (cnt < 1 || cnt > 30)
+               return SR_ERR_ARG;
+
+       req[0] = 0x00;
+       req[1] = COMMAND_WRITE_REG;
+       req[2] = cnt;
+
+       for (i = 0; i < cnt; i++) {
+               req[3 + 2 * i] = regs[i][0];
+               req[4 + 2 * i] = regs[i][1];
+       }
+
+       return transact(sdi, req, 3 + (2 * cnt), NULL, 0);
+}
+
+static int write_reg(const struct sr_dev_inst *sdi,
+                    uint8_t address, uint8_t value)
+{
+       uint8_t regs[2] = {address, value};
+
+       return write_regs(sdi, &regs, 1);
+}
+
+static int read_regs(const struct sr_dev_inst *sdi,
+                    const uint8_t *regs, uint8_t *values,
+                    uint8_t cnt)
+{
+       uint8_t req[33];
+
+       if (cnt < 1 || cnt > 30)
+               return SR_ERR_ARG;
+
+       req[0] = 0x00;
+       req[1] = COMMAND_READ_REG;
+       req[2] = cnt;
+       if (cnt)
+               memcpy(&req[3], regs, cnt);
+
+       return transact(sdi, req, 3 + cnt, values, cnt);
+}
+
+static int read_reg(const struct sr_dev_inst *sdi,
+                   uint8_t address, uint8_t *value)
+{
+       return read_regs(sdi, &address, value, 1);
+}
+
+static int write_adc(const struct sr_dev_inst *sdi,
+                    uint8_t address, uint16_t value)
+{
+       uint8_t regs[][2] = {
+               {REG_ADC_IDX, address},
+               {REG_ADC_VAL_LSB, value},
+               {REG_ADC_VAL_MSB, value >> 8},
+       };
+
+       return write_regs(sdi, ARRAY_AND_SIZE(regs));
+}
+
+static int read_eeprom(const struct sr_dev_inst *sdi,
+                      uint16_t address, uint8_t *data, uint16_t len)
+{
+       uint8_t req[8] = {
+               0x00, COMMAND_READ_EEPROM,
+               0x33, 0x81, /* Unknown values */
+               address, address >> 8,
+               len, len >> 8
+       };
+
+       return transact(sdi, req, sizeof(req), data, len);
+}
+
+static int read_eeprom_serial(const struct sr_dev_inst *sdi,
+                             uint8_t data[8])
+{
+       return read_eeprom(sdi, 0x08, data, 0x8);
+}
+
+static int read_eeprom_magic(const struct sr_dev_inst *sdi,
+                            uint8_t data[16])
+{
+       return read_eeprom(sdi, 0x10, data, 0x10);
+}
+
+static int read_temperature(const struct sr_dev_inst *sdi, int8_t *temp)
+{
+       uint8_t req[2] = {0x00, COMMAND_READ_TEMP};
+
+       return transact(sdi, req, sizeof(req), (uint8_t*)temp, 1);
+}
+
+static int get_firmware_version(const struct sr_dev_inst *sdi)
+{
+       uint8_t req[2] = {0x00, COMMAND_READ_FW_VER};
+       uint8_t rsp[128] = {};
+       int ret;
+
+       ret = transact(sdi, req, sizeof(req), rsp, sizeof(rsp));
+       if (ret == SR_OK) {
+               rsp[63] = 0;
+               sr_dbg("fw-version: %s", rsp);
+       }
+
+       return ret;
+}
+
+static int read_i2c(const struct sr_dev_inst *sdi, uint8_t *data, uint8_t len)
+{
+       uint8_t req[5];
+       uint8_t rsp[1 + 128];
+       int ret;
+
+       if (len < 1 || len > 128 || !data)
+               return SR_ERR_ARG;
+
+       req[0] = 0x00;
+       req[1] = COMMAND_READ_I2C;
+       req[2] = 0xc0; /* Fixed address */
+       req[3] = len;
+       req[4] = 0; /* Len MSB? */
+
+       ret = transact(sdi, req, sizeof(req), rsp, 1 + len);
+       if (ret != SR_OK)
+               return ret;
+       if (rsp[0] != 0x02) {
+               sr_dbg("Failed to do I2C read (0x%02x).", rsp[0]);
+               return SR_ERR;
+       }
+
+       memcpy(data, rsp + 1, len);
+       return SR_OK;
+}
+
+static int write_i2c(const struct sr_dev_inst *sdi, const uint8_t *data, uint8_t len)
+{
+       uint8_t req[5 + 128];
+       uint8_t rsp[1];
+       int ret;
+
+       if (len < 1 || len > 128 || !data)
+               return SR_ERR_ARG;
+
+       req[0] = 0x00;
+       req[1] = COMMAND_WRITE_I2C;
+       req[2] = 0xc0; /* Fixed address */
+       req[3] = len;
+       req[4] = 0; /* Len MSB? */
+       memcpy(req + 5, data, len);
+
+       ret = transact(sdi, req, 5 + len, rsp, sizeof(rsp));
+       if (ret != SR_OK)
+               return ret;
+       if (rsp[0] != 0x02) {
+               sr_dbg("Failed to do I2C write (0x%02x).", rsp[0]);
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int wake_i2c(const struct sr_dev_inst *sdi)
+{
+       uint8_t req[] = {0x00, COMMAND_WAKE_I2C};
+       uint8_t rsp[1] = {};
+       uint8_t i2c_rsp[1 + 1 + 2] = {};
+       int ret;
+
+       ret = transact(sdi, req, sizeof(req), rsp, sizeof(rsp));
+       if (ret != SR_OK)
+               return ret;
+       if (rsp[0] != 0x00) {
+               sr_dbg("Failed to do I2C wake trigger (0x%02x).", rsp[0]);
+               return SR_ERR;
+       }
+
+       ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp));
+       if (ret != SR_OK) {
+               return ret;
+       }
+       if (i2c_rsp[1] != 0x11) {
+               sr_dbg("Failed to do I2C wake read (0x%02x).", i2c_rsp[0]);
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int crypto_random(const struct sr_dev_inst *sdi, uint8_t *data)
+{
+       uint8_t i2c_req[8] = {0x03, 0x07, 0x1b, 0x00, 0x00, 0x00, 0x24, 0xcd};
+       uint8_t i2c_rsp[1 + 32 + 2] = {};
+       int ret;
+
+       ret = write_i2c(sdi, i2c_req, sizeof(i2c_req));
+       if (ret != SR_OK)
+               return ret;
+
+       g_usleep(100000); /* TODO: Poll instead. */
+
+       ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp));
+       if (ret != SR_OK)
+               return ret;
+
+       if (data)
+               memcpy(data, i2c_rsp + 1, 32);
+
+       return SR_OK;
+}
+
+static int crypto_nonce(const struct sr_dev_inst *sdi, uint8_t *data)
+{
+       uint8_t i2c_req[6 + 20 + 2] = {0x03, 0x1b, 0x16, 0x00, 0x00, 0x00};
+       uint8_t i2c_rsp[1 + 32 + 2] = {};
+       int ret;
+
+       /* CRC */
+       i2c_req[26] = 0x7d;
+       i2c_req[27] = 0xe0;
+
+       ret = write_i2c(sdi, i2c_req, sizeof(i2c_req));
+       if (ret != SR_OK)
+               return ret;
+
+       g_usleep(100000); /* TODO: Poll instead. */
+
+       ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp));
+       if (ret != SR_OK)
+               return ret;
+
+       if (data)
+               memcpy(data, i2c_rsp + 1, 32);
+
+       return SR_OK;
+}
+
+static int crypto_sign(const struct sr_dev_inst *sdi, uint8_t *data, uint8_t *crc)
+{
+       uint8_t i2c_req[8] = {0x03, 0x07, 0x41, 0x80, 0x00, 0x00, 0x28, 0x05};
+       uint8_t i2c_rsp[1 + 64 + 2] = {};
+       int ret;
+
+       ret = write_i2c(sdi, i2c_req, sizeof(i2c_req));
+       if (ret != SR_OK)
+               return ret;
+
+       g_usleep(100000); /* TODO: Poll instead. */
+
+       ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp));
+       if (ret != SR_OK)
+               return ret;
+
+       memcpy(data, i2c_rsp + 1, 64);
+       memcpy(crc, i2c_rsp + 1 + 64, 2);
+
+       return SR_OK;
+}
+
+static int authenticate(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       uint8_t random[32] = {};
+       uint8_t nonce[32] = {};
+       uint8_t sig[64] = {};
+       uint8_t sig_crc[64] = {};
+       uint32_t lfsr;
+       int i, ret;
+
+       ret = wake_i2c(sdi);
+       if (ret != SR_OK)
+               return ret;
+
+       ret = crypto_random(sdi, random);
+       if (ret != SR_OK)
+               return ret;
+       sr_dbg("random: 0x%02x 0x%02x 0x%02x 0x%02x", random[0], random[1], random[2], random[3]);
+
+       ret = crypto_nonce(sdi, nonce);
+       if (ret != SR_OK)
+               return ret;
+       sr_dbg("nonce: 0x%02x 0x%02x 0x%02x 0x%02x", nonce[0], nonce[1], nonce[2], nonce[3]);
+
+       ret = crypto_nonce(sdi, nonce);
+       if (ret != SR_OK)
+               return ret;
+       sr_dbg("nonce: 0x%02x 0x%02x 0x%02x 0x%02x", nonce[0], nonce[1], nonce[2], nonce[3]);
+
+       ret = crypto_sign(sdi, sig, sig_crc);
+       if (ret != SR_OK)
+               return ret;
+       sr_dbg("sig: 0x%02x 0x%02x 0x%02x 0x%02x", sig[0], sig[1], sig[2], sig[3]);
+       sr_dbg("sig crc: 0x%02x 0x%02x", sig_crc[0], sig_crc[1]);
+
+       lfsr = 0;
+       for (i = 0; i < 28; i++)
+               lfsr ^= nonce[i] << (8 * (i % 4));
+       lfsr ^= sig_crc[0] | sig_crc[1] << 8;
+
+       sr_dbg("Authenticate 0x%08x -> 0x%08x", devc->lfsr, lfsr);
+       devc->lfsr = lfsr;
+
+       return SR_OK;
+}
+
+static int upload_bitstream_part(const struct sr_dev_inst *sdi,
+                                const uint8_t *data, uint16_t len)
+{
+       uint8_t req[4 + 1020];
+       uint8_t rsp[1];
+       int ret;
+
+       if (len < 1 || len > 1020 || !data)
+               return SR_ERR_ARG;
+
+       req[0] = 0x00;
+       req[1] = COMMAND_SEND_BITSTREAM;
+       req[2] = len;
+       req[3] = len >> 8;
+       memcpy(req + 4, data, len);
+
+       ret = transact(sdi, req, 4 + len, rsp, sizeof(rsp));
+       if (ret != SR_OK)
+               return ret;
+       if (rsp[0] != 0x00) {
+               sr_dbg("Failed to do bitstream upload (0x%02x).", rsp[0]);
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int upload_bitstream(const struct sr_dev_inst *sdi,
+                           const char *name)
+{
+       struct drv_context *drvc = sdi->driver->context;
+       unsigned char *bitstream = NULL;
+       uint8_t req[2];
+       uint8_t rsp[1];
+       uint8_t reg_val;
+       int ret = SR_ERR;
+       size_t bs_size, bs_offset = 0, bs_part_size;
+
+       bitstream = sr_resource_load(drvc->sr_ctx, SR_RESOURCE_FIRMWARE,
+                                    name, &bs_size, 512 * 1024);
+       if (!bitstream)
+               goto out;
+
+       sr_info("Uploading bitstream '%s'.", name);
+
+       req[0] = 0x00;
+       req[1] = COMMAND_INIT_BITSTREAM;
+
+       ret = transact(sdi, req, sizeof(req), rsp, sizeof(rsp));
+       if (ret != SR_OK)
+               return ret;
+       if (rsp[0] != 0x00) {
+               sr_err("Failed to start bitstream upload (0x%02x).", rsp[0]);
+               ret = SR_ERR;
+               goto out;
+       }
+
+       while (bs_offset < bs_size) {
+               bs_part_size = MIN(bs_size - bs_offset, 1020);
+               sr_spew("Uploading %zd bytes.", bs_part_size);
+               ret = upload_bitstream_part(sdi, bitstream + bs_offset, bs_part_size);
+               if (ret != SR_OK)
+                       goto out;
+               bs_offset += bs_part_size;
+       }
+
+       sr_info("Bitstream upload done.");
+
+       /* Check a scratch register? */
+       ret = write_reg(sdi, 0x7f, 0xaa);
+       if (ret != SR_OK)
+               goto out;
+       ret = read_reg(sdi, 0x7f, &reg_val);
+       if (ret != SR_OK)
+               goto out;
+       if (reg_val != 0xaa) {
+               sr_err("Failed FPGA register read-back (0x%02x != 0xaa).", rsp[0]);
+               ret = SR_ERR;
+               goto out;
+       }
+
+ out:
+       g_free(bitstream);
+
+       return ret;
+}
+
+#if 0
+static int set_led(const struct sr_dev_inst *sdi, uint8_t r, uint8_t g, uint8_t b)
+{
+       uint8_t regs[][2] = {
+               {REG_LED_RED, r},
+               {REG_LED_GREEN, g},
+               {REG_LED_BLUE, b},
+       };
+
+       authenticate(sdi);
+
+       return write_regs(sdi, ARRAY_AND_SIZE(regs));
+}
+#endif
+
+static int configure_channels(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       const struct sr_channel *c;
+       const GSList *l;
+       uint16_t mask;
+
+       devc->dig_channel_cnt = 0;
+       devc->dig_channel_mask = 0;
+       for (l = sdi->channels; l; l = l->next) {
+               c = l->data;
+               if (!c->enabled)
+                       continue;
+
+               mask = 1 << c->index;
+               devc->dig_channel_masks[devc->dig_channel_cnt++] = mask;
+               devc->dig_channel_mask |= mask;
+
+       }
+       sr_dbg("%d channels enabled (0x%04x)",
+              devc->dig_channel_cnt, devc->dig_channel_mask);
+
+       return SR_OK;
+}
+
+SR_PRIV int saleae_logic_pro_init(const struct sr_dev_inst *sdi)
+{
+       uint8_t reg_val;
+       uint8_t dummy[8];
+       uint8_t serial[8];
+       uint8_t magic[16];
+       int8_t temperature;
+       int ret, i;
+
+       ret = reseed(sdi);
+       if (ret != SR_OK)
+               return ret;
+
+       ret = get_firmware_version(sdi);
+       if (ret != SR_OK)
+               return ret;
+
+       sr_dbg("read serial");
+       ret = read_eeprom_serial(sdi, serial);
+       if (ret != SR_OK)
+               return ret;
+
+       /* Check if we need to upload the bitstream. */
+       ret = read_reg(sdi, 0x7f, &reg_val);
+       if (ret != SR_OK)
+               return ret;
+       if (reg_val == 0xaa) {
+               sr_info("Skipping bitstream upload.");
+       } else {
+               ret = upload_bitstream(sdi, "saleae-logicpro16-fpga.bitstream");
+               if (ret != SR_OK)
+                       return ret;
+       }
+
+       /* Reset the ADC? */
+       sr_dbg("reset ADC");
+       ret = write_reg(sdi, 0x00, 0x00);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_reg(sdi, 0x00, 0x80);
+       if (ret != SR_OK)
+               return ret;
+
+       sr_dbg("init ADC");
+       ret = write_adc(sdi, 0x11, 0x0444);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x12, 0x0777);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x25, 0x0000);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x45, 0x0000);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x2a, 0x1111);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x2b, 0x1111);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x46, 0x0004);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x50, 0x0000);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x55, 0x0020);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_adc(sdi, 0x56, 0x0000);
+       if (ret != SR_OK)
+               return ret;
+
+       ret = write_reg(sdi, 0x15, 0x00);
+       if (ret != SR_OK)
+               return ret;
+
+       ret = write_adc(sdi, 0x0f, 0x0100);
+       if (ret != SR_OK)
+               return ret;
+
+       /* Resets? */
+       sr_dbg("resets");
+       ret = write_reg(sdi, 0x00, 0x02); /* bit 1 */
+       if (ret != SR_OK)
+               return ret;
+       ret = write_reg(sdi, 0x00, 0x00);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_reg(sdi, 0x00, 0x04); /* bit 2 */
+       if (ret != SR_OK)
+               return ret;
+       ret = write_reg(sdi, 0x00, 0x00);
+       if (ret != SR_OK)
+               return ret;
+       ret = write_reg(sdi, 0x00, 0x08); /* bit 3 */
+       if (ret != SR_OK)
+               return ret;
+       ret = write_reg(sdi, 0x00, 0x00);
+       if (ret != SR_OK)
+               return ret;
+
+       sr_dbg("read dummy");
+       for (i = 0; i < 8; i++) {
+               ret = read_reg(sdi, 0x41 + i, &dummy[i]);
+               if (ret != SR_OK)
+                       return ret;
+       }
+
+       /* Read and write back magic EEPROM value. */
+       sr_dbg("read/write magic");
+       ret = read_eeprom_magic(sdi, magic);
+       if (ret != SR_OK)
+               return ret;
+       for (i = 0; i < 16; i++) {
+               ret = write_reg(sdi, 0x17, magic[i]);
+               if (ret != SR_OK)
+                       return ret;
+       }
+
+       ret = read_temperature(sdi, &temperature);
+       if (ret != SR_OK)
+               return ret;
+       sr_dbg("temperature = %d", temperature);
+
+       /* Setting the LED doesn't work yet. */
+       /* set_led(sdi, 0x00, 0x00, 0xff); */
+
+       return SR_OK;
+}
+
+SR_PRIV int saleae_logic_pro_prepare(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+       uint8_t regs_unknown[][2] = {
+               {0x03, 0x0f},
+               {0x04, 0x00},
+               {0x05, 0x00},
+       };
+       uint8_t regs_config[][2] = {
+               {0x00, 0x00},
+               {0x08, 0x00}, /* Analog channel mask (LSB) */
+               {0x09, 0x00}, /* Analog channel mask (MSB) */
+               {0x06, 0x01}, /* Digital channel mask (LSB) */
+               {0x07, 0x00}, /* Digital channel mask (MSB) */
+               {0x0a, 0x00}, /* Analog sample rate? */
+               {0x0b, 0x64}, /* Digital sample rate? */
+               {0x0c, 0x00},
+               {0x0d, 0x00}, /* Analog mux rate? */
+               {0x0e, 0x01}, /* Digital mux rate? */
+               {0x12, 0x04},
+               {0x13, 0x00},
+               {0x14, 0xff}, /* Pre-divider? */
+       };
+       uint8_t start_req[] = {0x00, 0x01};
+       uint8_t start_rsp[2] = {};
+
+       configure_channels(sdi);
+
+       /* Digital channel mask and muxing */
+       regs_config[3][1] = devc->dig_channel_mask;
+       regs_config[4][1] = devc->dig_channel_mask >> 8;
+       regs_config[9][1] = devc->dig_channel_cnt;
+
+       /* Samplerate */
+       switch (devc->dig_samplerate) {
+       case SR_MHZ(1):
+               regs_config[6][1] = 0x64;
+               break;
+       case SR_MHZ(2):
+               regs_config[6][1] = 0x32;
+               break;
+       case SR_KHZ(2500):
+               regs_config[6][1] = 0x28;
+               break;
+       case SR_MHZ(10):
+               regs_config[6][1] = 0x0a;
+               break;
+       case SR_MHZ(25):
+               regs_config[6][1] = 0x04;
+               regs_config[12][1] = 0x80;
+               break;
+       case SR_MHZ(50):
+               regs_config[6][1] = 0x02;
+               regs_config[12][1] = 0x40;
+               break;
+       default:
+               return SR_ERR_ARG;
+       }
+
+       authenticate(sdi);
+
+       write_reg(sdi, 0x15, 0x03);
+       write_regs(sdi, ARRAY_AND_SIZE(regs_unknown));
+       write_regs(sdi, ARRAY_AND_SIZE(regs_config));
+
+       transact(sdi, start_req, sizeof(start_req), start_rsp, sizeof(start_rsp));
+
+       return SR_OK;
+}
+
+SR_PRIV int saleae_logic_pro_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc = sdi->priv;
+
+       devc->conv_size = 0;
+       devc->batch_index = 0;
+
+       write_reg(sdi, 0x00, 0x01);
+
+       return SR_OK;
+}
+
+SR_PRIV int saleae_logic_pro_stop(const struct sr_dev_inst *sdi)
+{
+       uint8_t stop_req[] = {0x00, 0x02};
+       uint8_t stop_rsp[2] = {};
+       uint8_t status;
+       int ret;
+
+       write_reg(sdi, 0x00, 0x00);
+       transact(sdi, stop_req, sizeof(stop_req), stop_rsp, sizeof(stop_rsp));
+
+       ret = read_reg(sdi, 0x40, &status);
+       if (ret != SR_OK)
+               return ret;
+       if (status != 0x20) {
+               sr_err("Capture error (status reg = 0x%02x).", status);
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static void saleae_logic_pro_send_data(const struct sr_dev_inst *sdi,
+                                     void *data, size_t length, size_t unitsize)
+{
+       const struct sr_datafeed_logic logic = {
+               .length = length,
+               .unitsize = unitsize,
+               .data = data
+       };
+
+       const struct sr_datafeed_packet packet = {
+               .type = SR_DF_LOGIC,
+               .payload = &logic
+       };
+
+       sr_session_send(sdi, &packet);
+}
+
+/*
+ * One batch from the device consists of 32 samples per active digital channel.
+ * This stream of batches is packed into USB packets with 16384 bytes each.
+ */
+static void saleae_logic_pro_convert_data(const struct sr_dev_inst *sdi,
+                                        const uint32_t *src, size_t srccnt)
+{
+       struct dev_context *devc = sdi->priv;
+       uint8_t *dst = devc->conv_buffer;
+       uint32_t samples;
+       uint16_t channel_mask;
+       unsigned int sample_index, batch_index;
+       uint16_t *dst_batch;
+
+       /* Copy partial batch to the beginning. */
+       memcpy(dst, dst + devc->conv_size, CONV_BATCH_SIZE);
+       /* Reset converted size. */
+       devc->conv_size = 0;
+
+       batch_index = devc->batch_index;
+       while (srccnt--) {
+               samples = *src++;
+               dst_batch = (uint16_t*)dst;
+
+               /* First index of the batch. */
+               if (batch_index == 0)
+                       memset(dst, 0, CONV_BATCH_SIZE);
+
+               /* Convert one channel. */
+               channel_mask = devc->dig_channel_masks[batch_index];
+               for (sample_index = 0; sample_index <= 31; sample_index++)
+                       if ((samples >> (31 - sample_index)) & 1)
+                               dst_batch[sample_index] |= channel_mask;
+
+               /* Last index of the batch. */
+               if (++batch_index == devc->dig_channel_cnt) {
+                       devc->conv_size += CONV_BATCH_SIZE;
+                       batch_index = 0;
+                       dst += CONV_BATCH_SIZE;
+               }
+       }
+       devc->batch_index = batch_index;
+}
+
+SR_PRIV void LIBUSB_CALL saleae_logic_pro_receive_data(struct libusb_transfer *transfer)
+{
+       const struct sr_dev_inst *sdi = transfer->user_data;
+       struct dev_context *devc = sdi->priv;
+       int ret;
+
+       switch (transfer->status) {
+       case LIBUSB_TRANSFER_NO_DEVICE:
+               sr_dbg("FIXME no device");
+               return;
+       case LIBUSB_TRANSFER_COMPLETED:
+       case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */
+               break;
+       default:
+               /* FIXME */
+               return;
+       }
+
+       saleae_logic_pro_convert_data(sdi, (uint32_t*)transfer->buffer, 16 * 1024 / 4);
+       saleae_logic_pro_send_data(sdi, devc->conv_buffer, devc->conv_size, 2);
+
+       if ((ret = libusb_submit_transfer(transfer)) != LIBUSB_SUCCESS)
+               sr_dbg("FIXME resubmit failed");
+}
diff --git a/src/hardware/saleae-logic-pro/protocol.h b/src/hardware/saleae-logic-pro/protocol.h
new file mode 100644 (file)
index 0000000..9844454
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Jan Luebbe <jluebbe@lasnet.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_SALEAE_LOGIC_PRO_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SALEAE_LOGIC_PRO_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "saleae-logic-pro"
+
+/* 16 channels * 32 samples */
+#define CONV_BATCH_SIZE (2 * 32)
+
+/*
+ * One packet + one partial conversion: Worst case is only one active
+ * channel converted to 2 bytes per sample, with 8 * 16384 samples per packet.
+ */
+#define CONV_BUFFER_SIZE (2 * 8 * 16384 + CONV_BATCH_SIZE)
+
+struct dev_context {
+       unsigned int dig_channel_cnt;
+       uint16_t dig_channel_mask;
+       uint16_t dig_channel_masks[16];
+       uint64_t dig_samplerate;
+
+       uint32_t lfsr;
+
+       unsigned int num_transfers;
+       unsigned int submitted_transfers;
+       struct libusb_transfer **transfers;
+
+       uint8_t *conv_buffer;
+       unsigned int conv_size;
+       unsigned int batch_index;
+};
+
+SR_PRIV int saleae_logic_pro_init(const struct sr_dev_inst *sdi);
+SR_PRIV int saleae_logic_pro_prepare(const struct sr_dev_inst *sdi);
+SR_PRIV int saleae_logic_pro_start(const struct sr_dev_inst *sdi);
+SR_PRIV int saleae_logic_pro_stop(const struct sr_dev_inst *sdi);
+SR_PRIV void LIBUSB_CALL saleae_logic_pro_receive_data(struct libusb_transfer *transfer);
+
+#endif
index e6b752d1fcb3f7bc18d8a2425bdec5ba02b12e9c..99916e44e1343cbfedaffc961dba3250178b80bc 100644 (file)
@@ -43,8 +43,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_CONN | SR_CONF_GET,
@@ -54,7 +57,7 @@ static const uint32_t devopts[] = {
        SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
 };
 
-static const int32_t soft_trigger_matches[] = {
+static const int32_t trigger_matches[] = {
        SR_TRIGGER_ZERO,
        SR_TRIGGER_ONE,
        SR_TRIGGER_RISING,
@@ -69,11 +72,14 @@ static const char *channel_names[] = {
 
 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 },
+} thresholds_ranges[] = {
+       { VOLTAGE_RANGE_18_33_V, },
+       { VOLTAGE_RANGE_5_V, },
+};
+
+static const double thresholds[][2] = {
+       { 0.7, 1.4 },
+       { 1.4, 3.6 },
 };
 
 static const uint64_t samplerates[] = {
@@ -182,7 +188,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
                libusb_get_device_descriptor(devlist[i], &des);
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
 
                if (des.idVendor != LOGIC16_VID || des.idProduct != LOGIC16_PID)
                        continue;
@@ -212,11 +219,12 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                                libusb_get_device_address(devlist[i]), NULL);
                } else {
                        if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
-                                       USB_CONFIGURATION, FX2_FIRMWARE) == SR_OK)
+                                       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.");
+                       } else {
+                               sr_err("Firmware upload failed, name %s.", FX2_FIRMWARE);
+                       }
                        sdi->inst_type = SR_INST_USB;
                        sdi->conn = sr_usb_dev_inst_new(
                                libusb_get_bus_number(devlist[i]), 0xff, NULL);
@@ -235,17 +243,13 @@ static int logic16_dev_open(struct sr_dev_inst *sdi)
        struct sr_usb_dev_inst *usb;
        struct libusb_device_descriptor des;
        struct drv_context *drvc;
-       int ret, i, device_count;
+       int ret = SR_ERR, i, device_count;
        char connection_id[64];
 
        di = sdi->driver;
        drvc = di->context;
        usb = sdi->conn;
 
-       if (sdi->status == SR_ST_ACTIVE)
-               /* Device is already in use. */
-               return SR_ERR;
-
        device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
        if (device_count < 0) {
                sr_err("Failed to get device list: %s.",
@@ -264,7 +268,9 @@ static int logic16_dev_open(struct sr_dev_inst *sdi)
                        /*
                         * Check device by its physical USB bus/port address.
                         */
-                       usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+                       if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                               continue;
+
                        if (strcmp(sdi->connection_id, connection_id))
                                /* This is not the one. */
                                continue;
@@ -280,6 +286,7 @@ static int logic16_dev_open(struct sr_dev_inst *sdi)
                } else {
                        sr_err("Failed to open device: %s.",
                               libusb_error_name(ret));
+                       ret = SR_ERR;
                        break;
                }
 
@@ -287,13 +294,16 @@ static int logic16_dev_open(struct sr_dev_inst *sdi)
                if (ret == LIBUSB_ERROR_BUSY) {
                        sr_err("Unable to claim USB interface. Another "
                               "program or driver has already claimed it.");
+                       ret = SR_ERR;
                        break;
                } else if (ret == LIBUSB_ERROR_NO_DEVICE) {
                        sr_err("Device has been disconnected.");
+                       ret = SR_ERR;
                        break;
                } else if (ret != 0) {
                        sr_err("Unable to claim interface: %s.",
                               libusb_error_name(ret));
+                       ret = SR_ERR;
                        break;
                }
 
@@ -302,15 +312,17 @@ static int logic16_dev_open(struct sr_dev_inst *sdi)
                        break;
                }
 
-               sdi->status = SR_ST_ACTIVE;
                sr_info("Opened device on %d.%d (logical) / %s (physical), interface %d.",
                        usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
 
+               ret = SR_OK;
+
                break;
        }
+
        libusb_free_device_list(devlist, 1);
 
-       if (sdi->status != SR_ST_ACTIVE) {
+       if (ret != SR_OK) {
                if (usb->devhdl) {
                        libusb_release_interface(usb->devhdl, USB_INTERFACE);
                        libusb_close(usb->devhdl);
@@ -377,32 +389,28 @@ static int dev_close(struct sr_dev_inst *sdi)
        struct sr_usb_dev_inst *usb;
 
        usb = sdi->conn;
+
        if (!usb->devhdl)
-               return SR_ERR;
+               return SR_ERR_BUG;
 
        sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
                usb->bus, usb->address, sdi->connection_id, 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 config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        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)
@@ -412,8 +420,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                        /* 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);
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                break;
        case SR_CONF_SAMPLERATE:
                if (!sdi)
@@ -431,41 +438,30 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                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)
+               for (i = 0; i < ARRAY_SIZE(thresholds); i++) {
+                       if (devc->selected_voltage_range != thresholds_ranges[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;
+                       *data = std_gvar_tuple_double(thresholds[i][0], thresholds[i][1]);
+                       return SR_OK;
                }
-               break;
+               return SR_ERR;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       gdouble low, high;
-       int ret;
-       unsigned int i;
+       int idx;
 
        (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);
@@ -475,76 +471,40 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                break;
        case SR_CONF_CAPTURE_RATIO:
                devc->capture_ratio = g_variant_get_uint64(data);
-               ret = (devc->capture_ratio > 100) ? SR_ERR : SR_OK;
                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;
-                       }
-               }
+               if ((idx = std_double_tuple_idx(data, ARRAY_AND_SIZE(thresholds))) < 0)
+                       return SR_ERR_ARG;
+               devc->selected_voltage_range = thresholds_ranges[idx].range;
                break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        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);
+               *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
                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);
+               *data = std_gvar_thresholds(ARRAY_AND_SIZE(thresholds));
                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));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static void abort_acquisition(struct dev_context *devc)
@@ -674,9 +634,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        unsigned char *buf;
        size_t size, convsize;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        drvc = di->context;
        devc = sdi->priv;
        usb = sdi->conn;
@@ -695,7 +652,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        if ((trigger = sr_session_trigger_get(sdi->session))) {
                int pre_trigger_samples = 0;
                if (devc->limit_samples > 0)
-                       pre_trigger_samples = devc->capture_ratio * devc->limit_samples/100;
+                       pre_trigger_samples = (devc->capture_ratio * devc->limit_samples) / 100;
                devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
                if (!devc->stl)
                        return SR_ERR_MALLOC;
@@ -775,9 +732,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        ret = logic16_abort_acquisition(sdi);
 
        abort_acquisition(sdi->priv);
@@ -793,7 +747,7 @@ static struct sr_dev_driver saleae_logic16_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 28a4c52f387a04f41ec91a97ea7a542f8e818fc8..a3dc0900f04fb8d6f63967ae592fc12b9ff336d4 100644 (file)
@@ -913,47 +913,51 @@ SR_PRIV void LIBUSB_CALL logic16_receive_transfer(struct libusb_transfer *transf
        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. */
+       if (new_samples <= 0) {
+               resubmit_transfer(transfer);
+               return;
+       }
+
+       /* At least one new sample. */
+       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(sdi, &packet);
+               devc->sent_samples += new_samples;
+       } else {
+               trigger_offset = soft_trigger_logic_check(devc->stl,
+                               devc->convbuffer, new_samples * 2, &pre_trigger_samples);
+               if (trigger_offset > -1) {
+                       devc->sent_samples += pre_trigger_samples;
                        packet.type = SR_DF_LOGIC;
                        packet.payload = &logic;
+                       num_samples = new_samples - trigger_offset;
                        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;
+                                       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;
+                       logic.data = devc->convbuffer + trigger_offset * 2;
                        sr_session_send(sdi, &packet);
-                       devc->sent_samples += new_samples;
-               } else {
-                       trigger_offset = soft_trigger_logic_check(devc->stl,
-                                       devc->convbuffer, new_samples * 2, &pre_trigger_samples);
-                       if (trigger_offset > -1) {
-                               devc->sent_samples += pre_trigger_samples;
-                               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(sdi, &packet);
-                               devc->sent_samples += num_samples;
-
-                               devc->trigger_fired = TRUE;
-                       }
-               }
+                       devc->sent_samples += num_samples;
 
-               if (devc->limit_samples &&
-                               (uint64_t)devc->sent_samples >= devc->limit_samples) {
-                       devc->sent_samples = -2;
-                       free_transfer(transfer);
-                       return;
+                       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);
 }
index 9eead9a9e772ffc80b728c12c058f1df36cbf758..003358169871a1f144833f5c4641966413cfc3e3 100644 (file)
@@ -41,7 +41,6 @@ enum fpga_variant {
        FPGA_VARIANT_MCUPRO    /* mcupro clone v4.6 with Actel FPGA */
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        /** Distinguishing between original Logic16 and clones */
        enum fpga_variant fpga_variant;
index 57bdcb81b7df9339c033e787ce1c95958c797fb3..d1dab458bd1b2d5b49e02913852e09485a91eefb 100644 (file)
@@ -2,6 +2,7 @@
  * This file is part of the libsigrok project.
  *
  * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -99,6 +100,7 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi,
 
        devc = g_malloc0(sizeof(struct dev_context));
        devc->device = device;
+       sr_sw_limits_init(&devc->limits);
        sdi->priv = devc;
 
        if (device->num_channels) {
@@ -127,7 +129,7 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi,
        for (ch_num = 0; ch_num < num_channels; ch_num++) {
                /* Create one channel per measurable output unit. */
                for (i = 0; i < ARRAY_SIZE(pci); i++) {
-                       if (!scpi_cmd_get(devc->device->commands, pci[i].command))
+                       if (!sr_scpi_cmd_get(devc->device->commands, pci[i].command))
                                continue;
                        g_snprintf(ch_name, 16, "%s%s", pci[i].prefix,
                                        channels[ch_num].name);
@@ -164,7 +166,7 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi,
        sr_scpi_hw_info_free(hw_info);
        hw_info = NULL;
 
-       scpi_cmd(sdi, devc->device->commands, SCPI_CMD_LOCAL);
+       sr_scpi_cmd(sdi, devc->device->commands, 0, NULL, SCPI_CMD_LOCAL);
 
        return sdi;
 }
@@ -251,23 +253,19 @@ static int dev_open(struct sr_dev_inst *sdi)
        struct sr_scpi_dev_inst *scpi;
        GVariant *beeper;
 
-       if (sdi->status != SR_ST_INACTIVE)
-               return SR_ERR;
-
        scpi = sdi->conn;
        if (sr_scpi_open(scpi) < 0)
                return SR_ERR;
 
-       sdi->status = SR_ST_ACTIVE;
-
        devc = sdi->priv;
-       scpi_cmd(sdi, devc->device->commands, SCPI_CMD_REMOTE);
+       sr_scpi_cmd(sdi, devc->device->commands, 0, NULL, SCPI_CMD_REMOTE);
        devc->beeper_was_set = FALSE;
-       if (scpi_cmd_resp(sdi, devc->device->commands, &beeper,
-                       G_VARIANT_TYPE_BOOLEAN, SCPI_CMD_BEEPER) == SR_OK) {
+       if (sr_scpi_cmd_resp(sdi, devc->device->commands, 0, NULL,
+                       &beeper, G_VARIANT_TYPE_BOOLEAN, SCPI_CMD_BEEPER) == SR_OK) {
                if (g_variant_get_boolean(beeper)) {
                        devc->beeper_was_set = TRUE;
-                       scpi_cmd(sdi, devc->device->commands, SCPI_CMD_BEEPER_DISABLE);
+                       sr_scpi_cmd(sdi, devc->device->commands,
+                               0, NULL, SCPI_CMD_BEEPER_DISABLE);
                }
                g_variant_unref(beeper);
        }
@@ -280,43 +278,39 @@ 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;
-
        devc = sdi->priv;
        scpi = sdi->conn;
-       if (scpi) {
-               if (devc->beeper_was_set)
-                       scpi_cmd(sdi, devc->device->commands, SCPI_CMD_BEEPER_ENABLE);
-               scpi_cmd(sdi, devc->device->commands, SCPI_CMD_LOCAL);
-               sr_scpi_close(scpi);
-               sdi->status = SR_ST_INACTIVE;
-       }
 
-       return SR_OK;
+       if (!scpi)
+               return SR_ERR_BUG;
+
+       if (devc->beeper_was_set)
+               sr_scpi_cmd(sdi, devc->device->commands,
+                       0, NULL, SCPI_CMD_BEEPER_ENABLE);
+       sr_scpi_cmd(sdi, devc->device->commands, 0, NULL, SCPI_CMD_LOCAL);
+
+       return sr_scpi_close(scpi);
 }
 
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
        g_free(devc->channels);
        g_free(devc->channel_groups);
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        const GVariantType *gvtype;
        unsigned int i;
+       int channel_group_cmd;
+       char *channel_group_name;
        int cmd, ret;
        const char *s;
 
@@ -407,13 +401,23 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        case SR_CONF_REGULATION:
                gvtype = G_VARIANT_TYPE_STRING;
                cmd = SCPI_CMD_GET_OUTPUT_REGULATION;
+               break;
+       default:
+               return sr_sw_limits_config_get(&devc->limits, key, data);
        }
        if (!gvtype)
                return SR_ERR_NA;
 
-       if (cg)
-               select_channel(sdi, cg->channels->data);
-       ret = scpi_cmd_resp(sdi, devc->device->commands, data, gvtype, cmd);
+       channel_group_cmd = 0;
+       channel_group_name = NULL;
+       if (cg) {
+               channel_group_cmd = SCPI_CMD_SELECT_CHANNEL;
+               channel_group_name = g_strdup(cg->name);
+       }
+
+       ret = sr_scpi_cmd_resp(sdi, devc->device->commands,
+               channel_group_cmd, channel_group_name, data, gvtype, cmd);
+       g_free(channel_group_name);
 
        if (cmd == SCPI_CMD_GET_OUTPUT_REGULATION) {
                /*
@@ -440,126 +444,131 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return ret;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        double d;
+       int channel_group_cmd;
+       char *channel_group_name;
        int ret;
 
        if (!sdi)
                return SR_ERR_ARG;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       if (cg)
-               /* Channel group specified. */
-               select_channel(sdi, cg->channels->data);
+       channel_group_cmd = 0;
+       channel_group_name = NULL;
+       if (cg) {
+               channel_group_cmd = SCPI_CMD_SELECT_CHANNEL;
+               channel_group_name = g_strdup(cg->name);
+       }
 
        devc = sdi->priv;
 
        switch (key) {
        case SR_CONF_ENABLED:
                if (g_variant_get_boolean(data))
-                       ret = scpi_cmd(sdi, devc->device->commands,
+                       ret = sr_scpi_cmd(sdi, devc->device->commands,
+                                       channel_group_cmd, channel_group_name,
                                        SCPI_CMD_SET_OUTPUT_ENABLE);
                else
-                       ret = scpi_cmd(sdi, devc->device->commands,
+                       ret = sr_scpi_cmd(sdi, devc->device->commands,
+                                       channel_group_cmd, channel_group_name,
                                        SCPI_CMD_SET_OUTPUT_DISABLE);
                break;
        case SR_CONF_VOLTAGE_TARGET:
                d = g_variant_get_double(data);
-               ret = scpi_cmd(sdi, devc->device->commands,
+               ret = sr_scpi_cmd(sdi, devc->device->commands,
+                               channel_group_cmd, channel_group_name,
                                SCPI_CMD_SET_VOLTAGE_TARGET, d);
                break;
        case SR_CONF_OUTPUT_FREQUENCY_TARGET:
                d = g_variant_get_double(data);
-               ret = scpi_cmd(sdi, devc->device->commands,
+               ret = sr_scpi_cmd(sdi, devc->device->commands,
+                               channel_group_cmd, channel_group_name,
                                SCPI_CMD_SET_FREQUENCY_TARGET, d);
                break;
        case SR_CONF_CURRENT_LIMIT:
                d = g_variant_get_double(data);
-               ret = scpi_cmd(sdi, devc->device->commands,
+               ret = sr_scpi_cmd(sdi, devc->device->commands,
+                               channel_group_cmd, channel_group_name,
                                SCPI_CMD_SET_CURRENT_LIMIT, d);
                break;
        case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
                if (g_variant_get_boolean(data))
-                       ret = scpi_cmd(sdi, devc->device->commands,
+                       ret = sr_scpi_cmd(sdi, devc->device->commands,
+                                       channel_group_cmd, channel_group_name,
                                        SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_ENABLE);
                else
-                       ret = scpi_cmd(sdi, devc->device->commands,
+                       ret = sr_scpi_cmd(sdi, devc->device->commands,
+                                       channel_group_cmd, channel_group_name,
                                        SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_DISABLE);
                break;
        case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
                d = g_variant_get_double(data);
-               ret = scpi_cmd(sdi, devc->device->commands,
+               ret = sr_scpi_cmd(sdi, devc->device->commands,
+                               channel_group_cmd, channel_group_name,
                                SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, d);
                break;
        case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
                if (g_variant_get_boolean(data))
-                       ret = scpi_cmd(sdi, devc->device->commands,
+                       ret = sr_scpi_cmd(sdi, devc->device->commands,
+                                       channel_group_cmd, channel_group_name,
                                        SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE);
                else
-                       ret = scpi_cmd(sdi, devc->device->commands,
+                       ret = sr_scpi_cmd(sdi, devc->device->commands,
+                                       channel_group_cmd, channel_group_name,
                                        SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE);
                break;
        case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
                d = g_variant_get_double(data);
-               ret = scpi_cmd(sdi, devc->device->commands,
+               ret = sr_scpi_cmd(sdi, devc->device->commands,
+                               channel_group_cmd, channel_group_name,
                                SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD, d);
                break;
        case SR_CONF_OVER_TEMPERATURE_PROTECTION:
                if (g_variant_get_boolean(data))
-                       ret = scpi_cmd(sdi, devc->device->commands,
+                       ret = sr_scpi_cmd(sdi, devc->device->commands,
+                                       channel_group_cmd, channel_group_name,
                                        SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_ENABLE);
                else
-                       ret = scpi_cmd(sdi, devc->device->commands,
+                       ret = sr_scpi_cmd(sdi, devc->device->commands,
+                                       channel_group_cmd, channel_group_name,
                                        SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_DISABLE);
                break;
        default:
-               ret = SR_ERR_NA;
+               ret = sr_sw_limits_config_set(&devc->limits, key, data);
        }
 
+       g_free(channel_group_name);
+
        return ret;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        struct sr_channel *ch;
        const struct channel_spec *ch_spec;
-       GVariant *gvar;
-       GVariantBuilder gvb;
-       int ret, i;
+       int i;
        const char *s[16];
 
-       /* Always available, even without sdi. */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       } else if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (!sdi)
-               return SR_ERR_ARG;
-       devc = sdi->priv;
+       devc = (sdi) ? sdi->priv : NULL;
 
-       ret = SR_OK;
        if (!cg) {
-               /* No channel group: global options. */
                switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devc->device->devopts, devc->device->num_devopts,
-                                       sizeof(uint32_t));
+                       return std_opts_config_list(key, data, sdi, cg,
+                               ARRAY_AND_SIZE(scanopts),
+                               ARRAY_AND_SIZE(drvopts),
+                               (devc && devc->device) ? devc->device->devopts : NULL,
+                               (devc && devc->device) ? devc->device->num_devopts : 0);
                        break;
                case SR_CONF_CHANNEL_CONFIG:
+                       if (!devc || !devc->device)
+                               return SR_ERR_ARG;
                        /* Not used. */
                        i = 0;
                        if (devc->device->features & PPS_INDEPENDENT)
@@ -581,7 +590,6 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                        return SR_ERR_NA;
                }
        } else {
-               /* Channel group specified. */
                /*
                 * Per-channel-group options depending on a channel are actually
                 * done with the first channel. Channel groups in PPS can have
@@ -589,86 +597,54 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
                 * specification for use in series or parallel mode.
                 */
                ch = cg->channels->data;
+               if (!devc || !devc->device)
+                       return SR_ERR_ARG;
+               ch_spec = &(devc->device->channels[ch->index]);
 
                switch (key) {
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devc->device->devopts_cg, devc->device->num_devopts_cg,
-                                       sizeof(uint32_t));
+                       *data = std_gvar_array_u32(devc->device->devopts_cg, devc->device->num_devopts_cg);
                        break;
                case SR_CONF_VOLTAGE_TARGET:
-                       ch_spec = &(devc->device->channels[ch->index]);
-                       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-                       /* Min, max, write resolution. */
-                       for (i = 0; i < 3; i++) {
-                               gvar = g_variant_new_double(ch_spec->voltage[i]);
-                               g_variant_builder_add_value(&gvb, gvar);
-                       }
-                       *data = g_variant_builder_end(&gvb);
+                       *data = std_gvar_min_max_step_array(ch_spec->voltage);
                        break;
                case SR_CONF_OUTPUT_FREQUENCY_TARGET:
-                       ch_spec = &(devc->device->channels[ch->index]);
-                       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-                       /* Min, max, write resolution. */
-                       for (i = 0; i < 3; i++) {
-                               gvar = g_variant_new_double(ch_spec->frequency[i]);
-                               g_variant_builder_add_value(&gvb, gvar);
-                       }
-                       *data = g_variant_builder_end(&gvb);
+                       *data = std_gvar_min_max_step_array(ch_spec->frequency);
                        break;
                case SR_CONF_CURRENT_LIMIT:
-                       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-                       /* Min, max, step. */
-                       for (i = 0; i < 3; i++) {
-                               ch_spec = &(devc->device->channels[ch->index]);
-                               gvar = g_variant_new_double(ch_spec->current[i]);
-                               g_variant_builder_add_value(&gvb, gvar);
-                       }
-                       *data = g_variant_builder_end(&gvb);
+                       *data = std_gvar_min_max_step_array(ch_spec->current);
+                       break;
+               case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+                       *data = std_gvar_min_max_step_array(ch_spec->ovp);
+                       break;
+               case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+                       *data = std_gvar_min_max_step_array(ch_spec->ocp);
                        break;
                default:
                        return SR_ERR_NA;
                }
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct sr_scpi_dev_inst *scpi;
-       struct sr_channel *ch;
-       struct pps_channel *pch;
-       int cmd, ret;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
+       int ret;
 
        devc = sdi->priv;
        scpi = sdi->conn;
 
+       /* Prime the pipe with the first channel. */
+       devc->cur_acquisition_channel = sr_next_enabled_channel(sdi, NULL);
+
        if ((ret = sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 10,
                        scpi_pps_receive_data, (void *)sdi)) != SR_OK)
                return ret;
        std_session_send_df_header(sdi);
-
-       /* Prime the pipe with the first channel's fetch. */
-       ch = sr_next_enabled_channel(sdi, NULL);
-       pch = ch->priv;
-       if ((ret = select_channel(sdi, ch)) < 0)
-               return ret;
-       if (pch->mq == SR_MQ_VOLTAGE)
-               cmd = SCPI_CMD_GET_MEAS_VOLTAGE;
-       else if (pch->mq == SR_MQ_FREQUENCY)
-               cmd = SCPI_CMD_GET_MEAS_FREQUENCY;
-       else if (pch->mq == SR_MQ_CURRENT)
-               cmd = SCPI_CMD_GET_MEAS_CURRENT;
-       else if (pch->mq == SR_MQ_POWER)
-               cmd = SCPI_CMD_GET_MEAS_POWER;
-       else
-               return SR_ERR;
-       scpi_cmd(sdi, devc->device->commands, cmd, pch->hwname);
+       sr_sw_limits_acquisition_start(&devc->limits);
 
        return SR_OK;
 }
@@ -676,19 +652,9 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct sr_scpi_dev_inst *scpi;
-       float f;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
 
        scpi = sdi->conn;
 
-       /*
-        * A requested value is certainly on the way. Retrieve it now,
-        * to avoid leaving the device in a state where it's not expecting
-        * commands.
-        */
-       sr_scpi_get_float(scpi, NULL, &f);
        sr_scpi_source_remove(sdi->session, scpi);
 
        std_session_send_df_end(sdi);
index 75be9efdf525c5d08a0eecdb0ea3d7d0e543e12d..6fdff9bb9b9c03e5f1585de62f421058ce83784c 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
  * Copyright (C) 2015 Google, Inc.
  * (Written by Alexandru Gagniuc <mrnuke@google.com> for Google, Inc.)
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
 #define CH_IDX(x) (1 << x)
 #define FREQ_DC_ONLY {0, 0, 0, 0, 0}
-
-static const uint32_t devopts_none[] = { };
+#define NO_OVP_LIMITS {0, 0, 0, 0, 0}
+#define NO_OCP_LIMITS {0, 0, 0, 0, 0}
 
 /* Agilent/Keysight N5700A series */
 static const uint32_t agilent_n5700a_devopts[] = {
        SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint32_t agilent_n5700a_devopts_cg[] = {
@@ -49,11 +52,11 @@ static const struct channel_group_spec agilent_n5700a_cg[] = {
 };
 
 static const struct channel_spec agilent_n5767a_ch[] = {
-       { "1", { 0, 60, 0.0072, 3, 4 }, { 0, 25, 0.003, 3, 4 }, { 0, 1500 }, FREQ_DC_ONLY },
+       { "1", { 0, 60, 0.0072, 3, 4 }, { 0, 25, 0.003, 3, 4 }, { 0, 1500 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
 };
 
 static const struct channel_spec agilent_n5763a_ch[] = {
-       { "1", { 0, 12.5, 0.0015, 3, 4 }, { 0, 120, 0.0144, 3, 4 }, { 0, 1500 }, FREQ_DC_ONLY },
+       { "1", { 0, 12.5, 0.0015, 3, 4 }, { 0, 120, 0.0144, 3, 4 }, { 0, 1500 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
 };
 
 /*
@@ -86,6 +89,8 @@ static const struct scpi_command agilent_n5700a_cmd[] = {
 /* Chroma 61600 series AC source */
 static const uint32_t chroma_61604_devopts[] = {
        SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint32_t chroma_61604_devopts_cg[] = {
@@ -100,7 +105,7 @@ static const uint32_t chroma_61604_devopts_cg[] = {
 };
 
 static const struct channel_spec chroma_61604_ch[] = {
-       { "1", { 0, 300, 0.1, 1, 1 }, { 0, 16, 0.1, 2, 2 }, { 0, 2000, 0, 1, 1 }, { 1.0, 1000.0, 0.01 } },
+       { "1", { 0, 300, 0.1, 1, 1 }, { 0, 16, 0.1, 2, 2 }, { 0, 2000, 0, 1, 1 }, { 1.0, 1000.0, 0.01 }, NO_OVP_LIMITS, NO_OCP_LIMITS },
 };
 
 static const struct channel_group_spec chroma_61604_cg[] = {
@@ -130,9 +135,10 @@ static const struct scpi_command chroma_61604_cmd[] = {
 };
 
 /* Chroma 62000 series DC source */
-
 static const uint32_t chroma_62000_devopts[] = {
        SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint32_t chroma_62000_devopts_cg[] = {
@@ -205,9 +211,9 @@ static int chroma_62000p_probe_channels(struct sr_dev_inst *sdi,
        channel = g_malloc0(sizeof(struct channel_spec));
        channel->name = "1";
        channel->voltage[0] = channel->current[0] = channel->power[0] = 0.0;
-       channel->voltage[1] = (float)volts;
-       channel->current[1] = (float)amps;
-       channel->power[1]   = (float)watts;
+       channel->voltage[1] = volts;
+       channel->current[1] = amps;
+       channel->power[1]   = watts;
        channel->voltage[2] = channel->current[2] = 0.01;
        channel->voltage[3] = channel->voltage[4] = 3;
        channel->current[3] = channel->current[4] = 4;
@@ -221,10 +227,80 @@ static int chroma_62000p_probe_channels(struct sr_dev_inst *sdi,
        return SR_OK;
 }
 
+/* Rigol DP700 series */
+static const uint32_t rigol_dp700_devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t rigol_dp700_devopts_cg[] = {
+       SR_CONF_REGULATION | SR_CONF_GET,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
+       SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_VOLTAGE | SR_CONF_GET,
+       SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CURRENT | SR_CONF_GET,
+       SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const struct channel_spec rigol_dp711_ch[] = {
+       { "1", { 0, 30, 0.01, 3, 3 }, { 0, 5, 0.01, 3, 3 }, { 0, 150, 0, 3, 3 }, FREQ_DC_ONLY, { 0.01, 33, 0.01}, { 0.01, 5.5, 0.01 } },
+};
+
+static const struct channel_spec rigol_dp712_ch[] = {
+       { "1", { 0, 50, 0.01, 3, 3 }, { 0, 3, 0.01, 3, 3 }, { 0, 150, 0, 3, 3 }, FREQ_DC_ONLY, { 0.01, 55, 0.01}, { 0.01, 3.3, 0.01 } },
+};
+
+static const struct channel_group_spec rigol_dp700_cg[] = {
+       { "1", CH_IDX(0), PPS_OVP | PPS_OCP },
+};
+
+/* Same as the DP800 series, except for the missing :SYST:OTP* commands. */
+static const struct scpi_command rigol_dp700_cmd[] = {
+       { SCPI_CMD_REMOTE, "SYST:REMOTE" },
+       { SCPI_CMD_LOCAL, "SYST:LOCAL" },
+       { SCPI_CMD_BEEPER, "SYST:BEEP:STAT?" },
+       { SCPI_CMD_BEEPER_ENABLE, "SYST:BEEP:STAT ON" },
+       { SCPI_CMD_BEEPER_DISABLE, "SYST:BEEP:STAT OFF" },
+       { SCPI_CMD_SELECT_CHANNEL, ":INST:NSEL %s" },
+       { SCPI_CMD_GET_MEAS_VOLTAGE, ":MEAS:VOLT?" },
+       { SCPI_CMD_GET_MEAS_CURRENT, ":MEAS:CURR?" },
+       { SCPI_CMD_GET_MEAS_POWER, ":MEAS:POWE?" },
+       { SCPI_CMD_GET_VOLTAGE_TARGET, ":SOUR:VOLT?" },
+       { SCPI_CMD_SET_VOLTAGE_TARGET, ":SOUR:VOLT %.6f" },
+       { SCPI_CMD_GET_CURRENT_LIMIT, ":SOUR:CURR?" },
+       { SCPI_CMD_SET_CURRENT_LIMIT, ":SOUR:CURR %.6f" },
+       { SCPI_CMD_GET_OUTPUT_ENABLED, ":OUTP?" },
+       { SCPI_CMD_SET_OUTPUT_ENABLE, ":OUTP ON" },
+       { SCPI_CMD_SET_OUTPUT_DISABLE, ":OUTP OFF" },
+       { SCPI_CMD_GET_OUTPUT_REGULATION, ":OUTP:MODE?" },
+       { SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ENABLED, ":OUTP:OVP?" },
+       { SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_ENABLE, ":OUTP:OVP ON" },
+       { SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_DISABLE, ":OUTP:OVP OFF" },
+       { SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ACTIVE, ":OUTP:OVP:QUES?" },
+       { SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":OUTP:OVP:VAL?" },
+       { SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":OUTP:OVP:VAL %.6f" },
+       { SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ENABLED, ":OUTP:OCP?" },
+       { SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE, ":OUTP:OCP:STAT ON" },
+       { SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE, ":OUTP:OCP:STAT OFF" },
+       { SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ACTIVE, ":OUTP:OCP:QUES?" },
+       { SCPI_CMD_GET_OVER_CURRENT_PROTECTION_THRESHOLD, ":OUTP:OCP:VAL?" },
+       { SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD, ":OUTP:OCP:VAL %.6f" },
+       ALL_ZERO
+};
+
 /* Rigol DP800 series */
 static const uint32_t rigol_dp800_devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint32_t rigol_dp800_devopts_cg[] = {
@@ -243,20 +319,20 @@ static const uint32_t rigol_dp800_devopts_cg[] = {
 };
 
 static const struct channel_spec rigol_dp821a_ch[] = {
-       { "1", { 0, 60, 0.001, 3, 3 }, { 0, 1, 0.0001, 4, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY },
-       { "2", { 0,  8, 0.001, 3, 3 }, { 0, 10, 0.001, 3, 3 }, { 0, 80, 0, 3, 3 }, FREQ_DC_ONLY },
+       { "1", { 0, 60, 0.001, 3, 3 }, { 0, 1, 0.0001, 4, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+       { "2", { 0,  8, 0.001, 3, 3 }, { 0, 10, 0.001, 3, 3 }, { 0, 80, 0, 3, 3 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
 };
 
 static const struct channel_spec rigol_dp831_ch[] = {
-       { "1", { 0,   8, 0.001, 3, 4 }, { 0, 5, 0.0003, 3, 4 }, { 0, 40, 0, 3, 4 }, FREQ_DC_ONLY },
-       { "2", { 0,  30, 0.001, 3, 4 }, { 0, 2, 0.0001, 3, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY },
-       { "3", { 0, -30, 0.001, 3, 4 }, { 0, 2, 0.0001, 3, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY },
+       { "1", { 0,   8, 0.001, 3, 4 }, { 0, 5, 0.0003, 3, 4 }, { 0, 40, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+       { "2", { 0,  30, 0.001, 3, 4 }, { 0, 2, 0.0001, 3, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+       { "3", { 0, -30, 0.001, 3, 4 }, { 0, 2, 0.0001, 3, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
 };
 
 static const struct channel_spec rigol_dp832_ch[] = {
-       { "1", { 0, 30, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY },
-       { "2", { 0, 30, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY },
-       { "3", { 0,  5, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY },
+       { "1", { 0, 30, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+       { "2", { 0, 30, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+       { "3", { 0,  5, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
 };
 
 static const struct channel_group_spec rigol_dp820_cg[] = {
@@ -307,33 +383,60 @@ static const struct scpi_command rigol_dp800_cmd[] = {
 };
 
 /* HP 663xx series */
-
 static const uint32_t hp_6630a_devopts[] = {
        SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t hp_6630a_devopts_cg[] = {
        SR_CONF_ENABLED | SR_CONF_SET,
        SR_CONF_VOLTAGE | SR_CONF_GET,
        SR_CONF_CURRENT | SR_CONF_GET,
        SR_CONF_VOLTAGE_TARGET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_CURRENT_LIMIT | SR_CONF_SET | SR_CONF_LIST,
-       SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_SET,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_SET,
 };
 
-static const uint32_t hp_6632b_devopts[] = {
+static const uint32_t hp_6630b_devopts[] = {
        SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t hp_6630b_devopts_cg[] = {
        SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_VOLTAGE | SR_CONF_GET,
        SR_CONF_CURRENT | SR_CONF_GET,
        SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const struct channel_spec hp_6633a_ch[] = {
-       { "1", { 0, 51.188, 0.0125, 3, 4 }, { 0, 2.0475, 0.0005, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY },
+       { "1", { 0, 51.188, 0.0125, 3, 4 }, { 0, 2.0475, 0.0005, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY, { 0, 55, 0.25 }, NO_OCP_LIMITS },
+};
+
+static const struct channel_spec hp_6631b_ch[] = {
+       { "1", { 0, 8.19, 0.002, 3, 4 }, { 0, 10.237, 0.00263, 4, 5 }, { 0, 83.84103 }, FREQ_DC_ONLY, { 0, 12, 0.06 }, NO_OCP_LIMITS },
 };
 
 static const struct channel_spec hp_6632b_ch[] = {
-       { "1", { 0, 20.475, 0.005, 3, 4 }, { 0, 5.1188, 0.00132, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY },
+       { "1", { 0, 20.475, 0.005, 3, 4 }, { 0, 5.1188, 0.00132, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY, { 0, 22, 0.1 }, NO_OCP_LIMITS },
+};
+
+static const struct channel_spec hp_66332a_ch[] = {
+       { "1", { 0, 20.475, 0.005, 3, 4 }, { 0, 5.1188, 0.00132, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY, { 0, 22, 0.1 }, NO_OCP_LIMITS },
+};
+
+static const struct channel_spec hp_6633b_ch[] = {
+       { "1", { 0, 51.188, 0.0125, 3, 4 }, { 0, 2.0475, 0.000526, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY, { 0, 55, 0.25 }, NO_OCP_LIMITS },
+};
+
+static const struct channel_spec hp_6634b_ch[] = {
+       { "1", { 0, 102.38, 0.025, 3, 4 }, { 0, 1.0238, 0.000263, 4, 5 }, { 0, 104.81664 }, FREQ_DC_ONLY, { 0, 110, 0.5 }, NO_OCP_LIMITS },
 };
 
 static const struct channel_group_spec hp_663xx_cg[] = {
@@ -353,7 +456,9 @@ static const struct scpi_command hp_6630a_cmd[] = {
        ALL_ZERO
 };
 
-static const struct scpi_command hp_6632b_cmd[] = {
+static const struct scpi_command hp_6630b_cmd[] = {
+       { SCPI_CMD_REMOTE, "SYST:REM" },
+       { SCPI_CMD_LOCAL, "SYST:LOC" },
        { SCPI_CMD_GET_OUTPUT_ENABLED, "OUTP:STAT?" },
        { SCPI_CMD_SET_OUTPUT_ENABLE, "OUTP:STAT ON" },
        { SCPI_CMD_SET_OUTPUT_DISABLE, "OUTP:STAT OFF" },
@@ -363,12 +468,19 @@ static const struct scpi_command hp_6632b_cmd[] = {
        { SCPI_CMD_SET_VOLTAGE_TARGET, ":SOUR:VOLT %.6f" },
        { SCPI_CMD_GET_CURRENT_LIMIT, ":SOUR:CURR?" },
        { SCPI_CMD_SET_CURRENT_LIMIT, ":SOUR:CURR %.6f" },
+       { SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ENABLED, ":CURR:PROT:STAT?" },
+       { SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE, ":CURR:PROT:STAT 1" },
+       { SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE, ":CURR:PROT:STAT 0" },
+       { SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":VOLT:PROT?" },
+       { SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":VOLT:PROT %.6f" },
        ALL_ZERO
 };
 
 /* Philips/Fluke PM2800 series */
 static const uint32_t philips_pm2800_devopts[] = {
        SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint32_t philips_pm2800_devopts_cg[] = {
@@ -395,9 +507,9 @@ enum philips_pm2800_modules {
 
 static const struct philips_pm2800_module_spec {
        /* Min, max, programming resolution. */
-       float voltage[5];
-       float current[5];
-       float power[5];
+       double voltage[5];
+       double current[5];
+       double power[5];
 } philips_pm2800_module_specs[] = {
        /* Autoranging modules. */
        [PM2800_MOD_30V_10A] = { { 0, 30, 0.0075, 2, 4 }, { 0, 10, 0.0025, 2, 4 }, { 0, 60 } },
@@ -483,7 +595,7 @@ static int philips_pm2800_probe_channels(struct sr_dev_inst *sdi,
                                spec->current[0], spec->current[1],
                                spec->power[0], spec->power[1]);
                (*channels)[i].name = (char *)philips_pm2800_names[i];
-               memcpy(&((*channels)[i].voltage), spec, sizeof(float) * 15);
+               memcpy(&((*channels)[i].voltage), spec, sizeof(double) * 15);
                (*channel_groups)[i].name = (char *)philips_pm2800_names[i];
                (*channel_groups)[i].channel_index_mask = 1 << i;
                (*channel_groups)[i].features = PPS_OTP | PPS_OVP | PPS_OCP;
@@ -517,6 +629,8 @@ static const struct scpi_command philips_pm2800_cmd[] = {
 
 static const uint32_t rs_hmc8043_devopts[] = {
        SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint32_t rs_hmc8043_devopts_cg[] = {
@@ -531,9 +645,9 @@ static const uint32_t rs_hmc8043_devopts_cg[] = {
 };
 
 static const struct channel_spec rs_hmc8043_ch[] = {
-       { "1", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY },
-       { "2", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY },
-       { "3", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY },
+       { "1", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+       { "2", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+       { "3", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
 };
 
 static const struct channel_group_spec rs_hmc8043_cg[] = {
@@ -572,6 +686,7 @@ SR_PRIV const struct scpi_pps pps_profiles[] = {
                agilent_n5700a_cmd,
                .probe_channels = NULL,
        },
+
        /* Agilent N5767A */
        { "Agilent", "N5767A", 0,
                ARRAY_AND_SIZE(agilent_n5700a_devopts),
@@ -581,6 +696,7 @@ SR_PRIV const struct scpi_pps pps_profiles[] = {
                agilent_n5700a_cmd,
                .probe_channels = NULL,
        },
+
        /* Chroma 61604 */
        { "Chroma", "61604", 0,
                ARRAY_AND_SIZE(chroma_61604_devopts),
@@ -590,6 +706,7 @@ SR_PRIV const struct scpi_pps pps_profiles[] = {
                chroma_61604_cmd,
                .probe_channels = NULL,
        },
+
        /* Chroma 62000 series */
        { "Chroma", "620[0-9]{2}P-[0-9]{2,3}-[0-9]{1,3}", 0,
                ARRAY_AND_SIZE(chroma_62000_devopts),
@@ -599,23 +716,82 @@ SR_PRIV const struct scpi_pps pps_profiles[] = {
                chroma_62000_cmd,
                .probe_channels = chroma_62000p_probe_channels,
        },
+
        /* HP 6633A */
        { "HP", "6633A", 0,
                ARRAY_AND_SIZE(hp_6630a_devopts),
-               ARRAY_AND_SIZE(devopts_none),
+               ARRAY_AND_SIZE(hp_6630a_devopts_cg),
                ARRAY_AND_SIZE(hp_6633a_ch),
                ARRAY_AND_SIZE(hp_663xx_cg),
                hp_6630a_cmd,
                .probe_channels = NULL,
        },
 
+       /* HP 6631B */
+       { "HP", "6631B", PPS_OVP | PPS_OCP | PPS_OTP,
+               ARRAY_AND_SIZE(hp_6630b_devopts),
+               ARRAY_AND_SIZE(hp_6630b_devopts_cg),
+               ARRAY_AND_SIZE(hp_6631b_ch),
+               ARRAY_AND_SIZE(hp_663xx_cg),
+               hp_6630b_cmd,
+               .probe_channels = NULL,
+       },
+
        /* HP 6632B */
-       { "HP", "6632B", 0,
-               ARRAY_AND_SIZE(hp_6632b_devopts),
-               ARRAY_AND_SIZE(devopts_none),
+       { "HP", "6632B", PPS_OVP | PPS_OCP | PPS_OTP,
+               ARRAY_AND_SIZE(hp_6630b_devopts),
+               ARRAY_AND_SIZE(hp_6630b_devopts_cg),
                ARRAY_AND_SIZE(hp_6632b_ch),
                ARRAY_AND_SIZE(hp_663xx_cg),
-               hp_6632b_cmd,
+               hp_6630b_cmd,
+               .probe_channels = NULL,
+       },
+
+       /* HP 66332A */
+       { "HP", "66332A", PPS_OVP | PPS_OCP | PPS_OTP,
+               ARRAY_AND_SIZE(hp_6630b_devopts),
+               ARRAY_AND_SIZE(hp_6630b_devopts_cg),
+               ARRAY_AND_SIZE(hp_66332a_ch),
+               ARRAY_AND_SIZE(hp_663xx_cg),
+               hp_6630b_cmd,
+               .probe_channels = NULL,
+       },
+
+       /* HP 6633B */
+       { "HP", "6633B", PPS_OVP | PPS_OCP | PPS_OTP,
+               ARRAY_AND_SIZE(hp_6630b_devopts),
+               ARRAY_AND_SIZE(hp_6630b_devopts_cg),
+               ARRAY_AND_SIZE(hp_6633b_ch),
+               ARRAY_AND_SIZE(hp_663xx_cg),
+               hp_6630b_cmd,
+               .probe_channels = NULL,
+       },
+
+       /* HP 6634B */
+       { "HP", "6634B", PPS_OVP | PPS_OCP | PPS_OTP,
+               ARRAY_AND_SIZE(hp_6630b_devopts),
+               ARRAY_AND_SIZE(hp_6630b_devopts_cg),
+               ARRAY_AND_SIZE(hp_6634b_ch),
+               ARRAY_AND_SIZE(hp_663xx_cg),
+               hp_6630b_cmd,
+               .probe_channels = NULL,
+       },
+
+       /* Rigol DP700 series */
+       { "Rigol", "^DP711$", 0,
+               ARRAY_AND_SIZE(rigol_dp700_devopts),
+               ARRAY_AND_SIZE(rigol_dp700_devopts_cg),
+               ARRAY_AND_SIZE(rigol_dp711_ch),
+               ARRAY_AND_SIZE(rigol_dp700_cg),
+               rigol_dp700_cmd,
+               .probe_channels = NULL,
+       },
+       { "Rigol", "^DP712$", 0,
+               ARRAY_AND_SIZE(rigol_dp700_devopts),
+               ARRAY_AND_SIZE(rigol_dp700_devopts_cg),
+               ARRAY_AND_SIZE(rigol_dp712_ch),
+               ARRAY_AND_SIZE(rigol_dp700_cg),
+               rigol_dp700_cmd,
                .probe_channels = NULL,
        },
 
index 4c7a649cda1f2aec73c8756b69b6af82123c3d10..477175872430d7da9db7b608ff26eb72c80106f2 100644 (file)
 #include "scpi.h"
 #include "protocol.h"
 
-SR_PRIV int select_channel(const struct sr_dev_inst *sdi, struct sr_channel *ch)
-{
-       struct dev_context *devc;
-       struct pps_channel *cur_pch, *new_pch;
-       int ret;
-
-       if (g_slist_length(sdi->channels) == 1)
-               return SR_OK;
-
-       devc = sdi->priv;
-       if (ch == devc->cur_channel)
-               return SR_OK;
-
-       new_pch = ch->priv;
-       if (devc->cur_channel) {
-               cur_pch = devc->cur_channel->priv;
-               if (cur_pch->hw_output_idx == new_pch->hw_output_idx) {
-                       /* Same underlying output channel. */
-                       devc->cur_channel = ch;
-                       return SR_OK;
-               }
-       }
-
-       if ((ret = scpi_cmd(sdi, devc->device->commands, SCPI_CMD_SELECT_CHANNEL,
-                       new_pch->hwname)) >= 0)
-               devc->cur_channel = ch;
-
-       return ret;
-}
-
 SR_PRIV int scpi_pps_receive_data(int fd, int revents, void *cb_data)
 {
        struct dev_context *devc;
@@ -62,12 +32,15 @@ SR_PRIV int scpi_pps_receive_data(int fd, int revents, void *cb_data)
        struct sr_analog_encoding encoding;
        struct sr_analog_meaning meaning;
        struct sr_analog_spec spec;
-       const struct sr_dev_inst *sdi;
-       struct sr_channel *next_channel;
-       struct sr_scpi_dev_inst *scpi;
+       struct sr_dev_inst *sdi;
+       int channel_group_cmd;
+       const char *channel_group_name;
        struct pps_channel *pch;
        const struct channel_spec *ch_spec;
+       int ret;
        float f;
+       GVariant *gvdata;
+       const GVariantType *gvtype;
        int cmd;
 
        (void)fd;
@@ -79,58 +52,78 @@ SR_PRIV int scpi_pps_receive_data(int fd, int revents, void *cb_data)
        if (!(devc = sdi->priv))
                return TRUE;
 
-       scpi = sdi->conn;
-
-       /* Retrieve requested value for this state. */
-       if (sr_scpi_get_float(scpi, NULL, &f) == SR_OK) {
-               pch = devc->cur_channel->priv;
-               ch_spec = &devc->device->channels[pch->hw_output_idx];
-               packet.type = SR_DF_ANALOG;
-               packet.payload = &analog;
-               /* Note: digits/spec_digits will be overridden later. */
-               sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
-               analog.meaning->channels = g_slist_append(NULL, devc->cur_channel);
-               analog.num_samples = 1;
-               analog.meaning->mq = pch->mq;
-               if (pch->mq == SR_MQ_VOLTAGE) {
-                       analog.meaning->unit = SR_UNIT_VOLT;
-                       analog.encoding->digits = ch_spec->voltage[4];
-                       analog.spec->spec_digits = ch_spec->voltage[3];
-               } else if (pch->mq == SR_MQ_CURRENT) {
-                       analog.meaning->unit = SR_UNIT_AMPERE;
-                       analog.encoding->digits = ch_spec->current[4];
-                       analog.spec->spec_digits = ch_spec->current[3];
-               } else if (pch->mq == SR_MQ_POWER) {
-                       analog.meaning->unit = SR_UNIT_WATT;
-                       analog.encoding->digits = ch_spec->power[4];
-                       analog.spec->spec_digits = ch_spec->power[3];
-               }
-               analog.meaning->mqflags = SR_MQFLAG_DC;
-               analog.data = &f;
-               sr_session_send(sdi, &packet);
-               g_slist_free(analog.meaning->channels);
-       }
+       pch = devc->cur_acquisition_channel->priv;
 
-       if (g_slist_length(sdi->channels) > 1) {
-               next_channel = sr_next_enabled_channel(sdi, devc->cur_channel);
-               if (select_channel(sdi, next_channel) != SR_OK) {
-                       sr_err("Failed to select channel %s", next_channel->name);
-                       return FALSE;
-               }
+       channel_group_cmd = 0;
+       channel_group_name = NULL;
+       if (g_slist_length(sdi->channel_groups) > 1) {
+               channel_group_cmd = SCPI_CMD_SELECT_CHANNEL;
+               channel_group_name = pch->hwname;
        }
 
-       pch = devc->cur_channel->priv;
-       if (pch->mq == SR_MQ_VOLTAGE)
+       if (pch->mq == SR_MQ_VOLTAGE) {
+               gvtype = G_VARIANT_TYPE_DOUBLE;
                cmd = SCPI_CMD_GET_MEAS_VOLTAGE;
-       else if (pch->mq == SR_MQ_FREQUENCY)
+       } else if (pch->mq == SR_MQ_FREQUENCY) {
+               gvtype = G_VARIANT_TYPE_DOUBLE;
                cmd = SCPI_CMD_GET_MEAS_FREQUENCY;
-       else if (pch->mq == SR_MQ_CURRENT)
+       } else if (pch->mq == SR_MQ_CURRENT) {
+               gvtype = G_VARIANT_TYPE_DOUBLE;
                cmd = SCPI_CMD_GET_MEAS_CURRENT;
-       else if (pch->mq == SR_MQ_POWER)
+       } else if (pch->mq == SR_MQ_POWER) {
+               gvtype = G_VARIANT_TYPE_DOUBLE;
                cmd = SCPI_CMD_GET_MEAS_POWER;
-       else
+       } else {
                return SR_ERR;
-       scpi_cmd(sdi, devc->device->commands, cmd);
+       }
+
+       ret = sr_scpi_cmd_resp(sdi, devc->device->commands,
+               channel_group_cmd, channel_group_name, &gvdata, gvtype, cmd);
+
+       if (ret != SR_OK)
+               return ret;
+
+       ch_spec = &devc->device->channels[pch->hw_output_idx];
+       packet.type = SR_DF_ANALOG;
+       packet.payload = &analog;
+       /* Note: digits/spec_digits will be overridden later. */
+       sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+       analog.meaning->channels = g_slist_append(NULL, devc->cur_acquisition_channel);
+       analog.num_samples = 1;
+       analog.meaning->mq = pch->mq;
+       if (pch->mq == SR_MQ_VOLTAGE) {
+               analog.meaning->unit = SR_UNIT_VOLT;
+               analog.encoding->digits = ch_spec->voltage[4];
+               analog.spec->spec_digits = ch_spec->voltage[3];
+       } else if (pch->mq == SR_MQ_CURRENT) {
+               analog.meaning->unit = SR_UNIT_AMPERE;
+               analog.encoding->digits = ch_spec->current[4];
+               analog.spec->spec_digits = ch_spec->current[3];
+       } else if (pch->mq == SR_MQ_POWER) {
+               analog.meaning->unit = SR_UNIT_WATT;
+               analog.encoding->digits = ch_spec->power[4];
+               analog.spec->spec_digits = ch_spec->power[3];
+       }
+       analog.meaning->mqflags = SR_MQFLAG_DC;
+       f = (float)g_variant_get_double(gvdata);
+       g_variant_unref(gvdata);
+       analog.data = &f;
+       sr_session_send(sdi, &packet);
+       g_slist_free(analog.meaning->channels);
+
+       /* Next channel. */
+       if (g_slist_length(sdi->channels) > 1) {
+               devc->cur_acquisition_channel =
+                       sr_next_enabled_channel(sdi, devc->cur_acquisition_channel);
+       }
+
+       if (devc->cur_acquisition_channel == sr_next_enabled_channel(sdi, NULL))
+               /* First enabled channel, so each channel has been sampled */
+               sr_sw_limits_update_samples_read(&devc->limits, 1);
+
+       /* Stop if limits have been hit. */
+       if (sr_sw_limits_check(&devc->limits))
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index 80593ceca8ad54ed205e7db214a9bcd56f4f64d2..cf0dbdd5aa227a2815269378fabb917b888d6513 100644 (file)
@@ -2,6 +2,7 @@
  * This file is part of the libsigrok project.
  *
  * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,7 +30,7 @@
 #define LOG_PREFIX "scpi-pps"
 
 enum pps_scpi_cmds {
-       SCPI_CMD_REMOTE,
+       SCPI_CMD_REMOTE = 1,
        SCPI_CMD_LOCAL,
        SCPI_CMD_BEEPER,
        SCPI_CMD_BEEPER_ENABLE,
@@ -101,10 +102,12 @@ struct scpi_pps {
 struct channel_spec {
        const char *name;
        /* Min, max, programming resolution, spec digits, encoding digits. */
-       float voltage[5];
-       float current[5];
-       float power[5];
-       float frequency[5];
+       double voltage[5];
+       double current[5];
+       double power[5];
+       double frequency[5];
+       double ovp[5];
+       double ocp[5];
 };
 
 struct channel_group_spec {
@@ -136,18 +139,15 @@ enum acq_states {
        STATE_STOP,
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        const struct scpi_pps *device;
 
-       /* Operational state */
        gboolean beeper_was_set;
        struct channel_spec *channels;
        struct channel_group_spec *channel_groups;
 
-       /* Temporary state across callbacks */
-       struct sr_channel *cur_channel;
+       struct sr_channel *cur_acquisition_channel;
+       struct sr_sw_limits limits;
 };
 
 SR_PRIV extern unsigned int num_pps_profiles;
index e39480e95eba1420c908ac12a665618776ca0ebe..3c501ade1e2654945227f42a3c3736534266a045 100644 (file)
@@ -34,8 +34,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -53,6 +56,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        int dropped, ret;
        size_t len;
        uint8_t buf[128];
+       size_t ch_idx;
+       char ch_name[12];
 
        dmm = (struct dmm_info *)di;
 
@@ -128,7 +133,23 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        sdi->inst_type = SR_INST_SERIAL;
        sdi->conn = serial;
        sdi->priv = devc;
-       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
+       dmm->channel_count = 1;
+       if (dmm->packet_parse == sr_metex14_4packets_parse)
+               dmm->channel_count = 4;
+       if (dmm->packet_parse == sr_eev121gw_3displays_parse) {
+               dmm->channel_count = EEV121GW_DISPLAY_COUNT;
+               dmm->channel_formats = eev121gw_channel_formats;
+       }
+       for (ch_idx = 0; ch_idx < dmm->channel_count; ch_idx++) {
+               size_t ch_num;
+               const char *fmt;
+               fmt = "P%zu";
+               if (dmm->channel_formats && dmm->channel_formats[ch_idx])
+                       fmt = dmm->channel_formats[ch_idx];
+               ch_num = ch_idx + 1;
+               snprintf(ch_name, sizeof(ch_name), fmt, ch_num);
+               sr_channel_new(sdi, ch_idx, SR_CHANNEL_ANALOG, TRUE, ch_name);
+       }
        devices = g_slist_append(devices, sdi);
 
 scan_cleanup:
@@ -137,41 +158,22 @@ scan_cleanup:
        return std_scan_complete(di, devices);
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -179,15 +181,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->limits);
        std_session_send_df_header(sdi);
 
-       /* Poll every 50ms, or whenever some data comes in. */
        serial = sdi->conn;
        serial_source_add(sdi->session, serial, G_IO_IN, 50,
                      receive_data, (void *)sdi);
@@ -206,6 +204,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                        .cleanup = std_cleanup, \
                        .scan = scan, \
                        .dev_list = std_dev_list, \
+                       .dev_clear = std_dev_clear, \
                        .config_get = NULL, \
                        .config_set = config_set, \
                        .config_list = config_list, \
@@ -216,7 +215,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                        .context = NULL, \
                }, \
                VENDOR, MODEL, CONN, BAUDRATE, PACKETSIZE, TIMEOUT, DELAY, \
-               REQUEST, VALID, PARSE, DETAILS, sizeof(struct CHIPSET##_info) \
+               REQUEST, 1, NULL, VALID, PARSE, DETAILS, sizeof(struct CHIPSET##_info) \
        }).di
 
 SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
@@ -262,6 +261,13 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
                sr_dtm0660_packet_valid, sr_dtm0660_parse, NULL
        ),
        /* }}} */
+       /* eev121gw based meters {{{ */
+       DMM(
+               "eevblog-121gw", eev121gw, "EEVblog", "121GW",
+               "115200/8n1", 115200, EEV121GW_PACKET_SIZE, 0, 0, NULL,
+               sr_eev121gw_packet_valid, sr_eev121gw_3displays_parse, NULL
+       ),
+       /* }}} */
        /* es519xx based meters {{{ */
        DMM(
                "iso-tech-idm103n", es519xx,
@@ -395,6 +401,12 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
        ),
        /* }}} */
        /* fs9922 based meters {{{ */
+       DMM(
+               "sparkfun-70c", fs9922,
+               "SparkFun", "70C", "2400/8n1/rts=0/dtr=1",
+               2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+               sr_fs9922_packet_valid, sr_fs9922_parse, NULL
+       ),
        DMM(
                "uni-t-ut61b-ser", fs9922,
                "UNI-T", "UT61B (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
@@ -413,6 +425,12 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
                2400, FS9922_PACKET_SIZE, 0, 0, NULL,
                sr_fs9922_packet_valid, sr_fs9922_parse, NULL
        ),
+       DMM(
+               "victor-dmm-ser", fs9922,
+               "Victor", "Victor DMMs (Mini-USB cable)", "2400/8n1",
+               2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+               sr_fs9922_packet_valid, sr_fs9922_parse, NULL
+       ),
        DMM(
                /*
                 * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
@@ -435,6 +453,15 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
                NULL
        ),
        /* }}} */
+       /* ms8250d based meters {{{ */
+       DMM(
+               "mastech-ms8250d", ms8250d,
+               "MASTECH", "MS8250D", "2400/8n1/rts=0/dtr=1",
+               2400, MS8250D_PACKET_SIZE, 0, 0, NULL,
+               sr_ms8250d_packet_valid, sr_ms8250d_parse,
+               NULL
+       ),
+       /* }}} */
        /* metex14 based meters {{{ */
        DMM(
                "mastech-mas345", metex14,
@@ -450,6 +477,13 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
                sr_metex14_packet_valid, sr_metex14_parse,
                NULL
        ),
+       DMM(
+               "metex-m3860m", metex14,
+               "Metex", "M-3860M", "9600/7n2/rts=0/dtr=1", 9600,
+               4 * METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+               sr_metex14_4packets_valid, sr_metex14_4packets_parse,
+               NULL
+       ),
        DMM(
                "metex-m4650cr", metex14,
                "Metex", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
@@ -478,6 +512,13 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
                sr_metex14_packet_valid, sr_metex14_parse,
                NULL
        ),
+       DMM(
+               "peaktech-4390a", metex14,
+               "PeakTech", "4390A", "9600/7n2/rts=0/dtr=1", 9600,
+               4 * METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+               sr_metex14_4packets_valid, sr_metex14_4packets_parse,
+               NULL
+       ),
        DMM(
                "radioshack-22-168", metex14,
                "RadioShack", "22-168", "1200/7n2/rts=0/dtr=1", 1200,
@@ -606,6 +647,15 @@ SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
                sr_vc870_packet_valid, sr_vc870_parse, NULL
        ),
        /* }}} */
+       /* vc96 based meters {{{ */
+       DMM(
+               "voltcraft-vc96", vc96,
+               "Voltcraft", "VC-96", "1200/8n2", 1200,
+               VC96_PACKET_SIZE, 0, 0, NULL,
+               sr_vc96_packet_valid, sr_vc96_parse,
+               NULL
+       ),
+       /* }}} */
        /*
         * The list is sorted. Add new items in the respective chip's group.
         */
index 1d1ca4823208c5aaa86c1ac8495d6806d44779a2..5e8912bf3d21c61906b25b0b21e7bd23b084c8dd 100644 (file)
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
-static void log_dmm_packet(const uint8_t *buf)
+static void log_dmm_packet(const uint8_t *buf, size_t len)
 {
-       sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x "
-              "%02x %02x %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],
-              buf[14], buf[15], buf[16], buf[17], buf[18], buf[19], buf[20],
-              buf[21], buf[22]);
+       GString *text;
+
+       text = sr_hexdump_new(buf, len);
+       sr_dbg("DMM packet: %s", text->str);
+       sr_hexdump_free(text);
 }
 
 static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi,
@@ -49,31 +47,43 @@ static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi,
        struct sr_analog_meaning meaning;
        struct sr_analog_spec spec;
        struct dev_context *devc;
+       gboolean sent_sample;
+       struct sr_channel *channel;
+       size_t ch_idx;
 
        dmm = (struct dmm_info *)sdi->driver;
 
-       log_dmm_packet(buf);
+       log_dmm_packet(buf, dmm->packet_size);
        devc = sdi->priv;
 
-       /* Note: digits/spec_digits will be overridden by the DMM parsers. */
-       sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
-
-       analog.meaning->channels = sdi->channels;
-       analog.num_samples = 1;
-       analog.meaning->mq = 0;
-
-       dmm->packet_parse(buf, &floatval, &analog, info);
-       analog.data = &floatval;
-
-       /* If this DMM needs additional handling, call the resp. function. */
-       if (dmm->dmm_details)
-               dmm->dmm_details(&analog, info);
+       sent_sample = FALSE;
+       memset(info, 0, dmm->info_size);
+       for (ch_idx = 0; ch_idx < dmm->channel_count; ch_idx++) {
+               /* Note: digits/spec_digits will be overridden by the DMM parsers. */
+               sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+
+               channel = g_slist_nth_data(sdi->channels, ch_idx);
+               analog.meaning->channels = g_slist_append(NULL, channel);
+               analog.num_samples = 1;
+               analog.meaning->mq = 0;
+
+               dmm->packet_parse(buf, &floatval, &analog, info);
+               analog.data = &floatval;
+
+               /* If this DMM needs additional handling, call the resp. function. */
+               if (dmm->dmm_details)
+                       dmm->dmm_details(&analog, info);
+
+               if (analog.meaning->mq != 0 && channel->enabled) {
+                       /* Got a measurement. */
+                       packet.type = SR_DF_ANALOG;
+                       packet.payload = &analog;
+                       sr_session_send(sdi, &packet);
+                       sent_sample = TRUE;
+               }
+       }
 
-       if (analog.meaning->mq != 0) {
-               /* Got a measurement. */
-               packet.type = SR_DF_ANALOG;
-               packet.payload = &analog;
-               sr_session_send(sdi, &packet);
+       if (sent_sample) {
                sr_sw_limits_update_samples_read(&devc->limits, 1);
        }
 }
@@ -116,7 +126,7 @@ static void handle_new_data(struct sr_dev_inst *sdi, void *info)
 {
        struct dmm_info *dmm;
        struct dev_context *devc;
-       int len, i, offset = 0;
+       int len, offset;
        struct sr_serial_dev_inst *serial;
 
        dmm = (struct dmm_info *)sdi->driver;
@@ -136,6 +146,7 @@ static void handle_new_data(struct sr_dev_inst *sdi, void *info)
        devc->buflen += len;
 
        /* Now look for packets in that data. */
+       offset = 0;
        while ((devc->buflen - offset) >= dmm->packet_size) {
                if (dmm->packet_valid(devc->buf + offset)) {
                        handle_packet(devc->buf + offset, sdi, info);
@@ -154,8 +165,8 @@ static void handle_new_data(struct sr_dev_inst *sdi, void *info)
        }
 
        /* 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];
+       if (devc->buflen > offset)
+               memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
        devc->buflen -= offset;
 }
 
@@ -188,7 +199,7 @@ int receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index 9749345604589328e7b619b3e0f55b39cf145ca5..decd429d89969acd6bd50e090cbcdd48da92eca4 100644 (file)
@@ -47,6 +47,10 @@ struct dmm_info {
        int64_t req_delay_ms;
        /** Packet request function. */
        int (*packet_request)(struct sr_serial_dev_inst *);
+       /** Number of channels / displays. */
+       size_t channel_count;
+       /** (Optional) printf formats for channel names. */
+       const char **channel_formats;
        /** Packet validation function. */
        gboolean (*packet_valid)(const uint8_t *);
        /** Packet parsing function. */
@@ -60,7 +64,6 @@ struct dmm_info {
 
 #define DMM_BUFSIZE 256
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits limits;
 
index 580add493ae3b42cfb36302f52e8bd59e18bb32d..1beeefbbc6608b2bc6cd0791b02290039b740e54 100644 (file)
@@ -35,7 +35,7 @@ struct lcr_es51919_info {
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, es51919_serial_clean);
+       return std_dev_clear_with_callback(di, es51919_serial_clean);
 }
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
diff --git a/src/hardware/siglent-sds/api.c b/src/hardware/siglent-sds/api.c
new file mode 100644 (file)
index 0000000..ce72881
--- /dev/null
@@ -0,0 +1,945 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 mhooijboer <marchelh@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 <config.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
+#include "scpi.h"
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_OSCILLOSCOPE,
+       SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_LEVEL | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_NUM_HDIV | SR_CONF_GET | SR_CONF_LIST,
+       SR_CONF_SAMPLERATE | SR_CONF_GET,
+       SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_AVERAGING | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint32_t devopts_cg_analog[] = {
+       SR_CONF_NUM_VDIV | SR_CONF_GET,
+       SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_PROBE_FACTOR | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint64_t timebases[][2] = {
+       /* nanoseconds */
+       { 1, 1000000000 },
+       { 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 },
+       { 100, 1 },
+};
+
+static const uint64_t vdivs[][2] = {
+       /* microvolts */
+       { 500, 100000 },
+       /* 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 },
+       { 20, 1 },
+       { 50, 1 },
+       { 100, 1 },
+};
+
+static const char *trigger_sources[] = {
+       "CH1", "CH2", "Ext", "Ext /5", "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[] = {
+       "A1M AC 1 Meg",
+       "A50 AC 50 Ohm",
+       "D1M DC 1 Meg",
+       "D50 DC 50 Ohm",
+       "GND",
+};
+
+static const uint64_t probe_factor[] = {
+       1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000,
+};
+
+static const uint64_t averages[] = {
+       4, 16, 32, 64, 128, 256, 512, 1024,
+};
+
+/* Do not change the order of entries. */
+static const char *data_sources[] = {
+       "Display",
+       "History",
+};
+
+enum vendor {
+       SIGLENT,
+};
+
+enum series {
+       SDS1000CML,
+       SDS1000CNL,
+       SDS1000DL,
+       SDS1000X,
+       SDS1000XP,
+       SDS1000XE,
+       SDS2000X,
+};
+
+/* short name, full name */
+static const struct siglent_sds_vendor supported_vendors[] = {
+       [SIGLENT] = {"Siglent", "Siglent Technologies"},
+};
+
+#define VENDOR(x) &supported_vendors[x]
+/* vendor, series, protocol, max timebase, min vdiv, number of horizontal divs,
+ * number of vertical divs, live waveform samples, memory buffer samples */
+static const struct siglent_sds_series supported_series[] = {
+       [SDS1000CML] = {VENDOR(SIGLENT), "SDS1000CML", NON_SPO_MODEL,
+               { 50, 1 }, { 2, 1000 }, 18, 8, 1400363},
+       [SDS1000CNL] = {VENDOR(SIGLENT), "SDS1000CNL", NON_SPO_MODEL,
+               { 50, 1 }, { 2, 1000 }, 18, 8, 1400363},
+       [SDS1000DL] = {VENDOR(SIGLENT), "SDS1000DL", NON_SPO_MODEL,
+               { 50, 1 }, { 2, 1000 }, 18, 8, 1400363},
+       [SDS1000X] = {VENDOR(SIGLENT), "SDS1000X", SPO_MODEL,
+               { 50, 1 }, { 500, 100000 }, 14, 8, 14000363},
+       [SDS1000XP] = {VENDOR(SIGLENT), "SDS1000X+", SPO_MODEL,
+               { 50, 1 }, { 500, 100000 }, 14, 8, 14000363},
+       [SDS1000XE] = {VENDOR(SIGLENT), "SDS1000XE", ESERIES,
+               { 50, 1 }, { 500, 100000 }, 14, 8, 14000363},
+       [SDS2000X] = {VENDOR(SIGLENT), "SDS2000X", SPO_MODEL,
+               { 50, 1 }, { 500, 100000 }, 14, 8, 14000363},
+};
+
+#define SERIES(x) &supported_series[x]
+/* series, model, min timebase, analog channels, digital */
+static const struct siglent_sds_model supported_models[] = {
+       { SERIES(SDS1000CML), "SDS1152CML", { 20, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CML), "SDS1102CML", { 10, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CML), "SDS1072CML", { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CNL), "SDS1202CNL", { 20, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CNL), "SDS1102CNL", { 10, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CNL), "SDS1072CNL", { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1202DL", { 20, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1102DL", { 10, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1022DL", { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1052DL", { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1052DL+", { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000X), "SDS1102X", { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000XP), "SDS1102X+", { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000X), "SDS1202X", { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000XP), "SDS1202X+", { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000XE), "SDS1202X-E", { 1, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000XE), "SDS1104X-E", { 1, 1000000000 }, 4, true, 16 },
+       { SERIES(SDS1000XE), "SDS1204X-E", { 1, 1000000000 }, 4, true, 16 },
+       { SERIES(SDS2000X), "SDS2072X", { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS2000X), "SDS2074X", { 2, 1000000000 }, 4, false, 0 },
+       { SERIES(SDS2000X), "SDS2102X", { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS2000X), "SDS2104X", { 2, 1000000000 }, 4, false, 0 },
+       { SERIES(SDS2000X), "SDS2202X", { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS2000X), "SDS2204X", { 2, 1000000000 }, 4, false, 0 },
+       { SERIES(SDS2000X), "SDS2302X", { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS2000X), "SDS2304X", { 2, 1000000000 }, 4, false, 0 },
+};
+
+SR_PRIV struct sr_dev_driver siglent_sds_driver_info;
+
+static void clear_helper(void *priv)
+{
+       struct dev_context *devc;
+
+       devc = priv;
+       if (!devc)
+               return;
+       g_free(devc->analog_groups);
+       g_free(devc->enabled_channels);
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+       return std_dev_clear_with_callback(di, clear_helper);
+}
+
+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;
+       unsigned int i;
+       const struct siglent_sds_model *model;
+       gchar *channel_name;
+
+       sr_dbg("Setting Communication Headers to off.");
+       if (sr_scpi_send(scpi, "CHDR OFF") != SR_OK)
+               return NULL;
+
+       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;
+               }
+       }
+
+       model = NULL;
+       for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
+               if (!strcmp(hw_info->model, supported_models[i].name)) {
+                       model = &supported_models[i];
+                       break;
+               }
+       }
+
+       if (!model) {
+               sr_scpi_hw_info_free(hw_info);
+               return NULL;
+       }
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->vendor = g_strdup(model->series->vendor->name);
+       sdi->model = g_strdup(model->name);
+       sdi->version = g_strdup(hw_info->firmware_version);
+       sdi->conn = scpi;
+       sdi->driver = &siglent_sds_driver_info;
+       sdi->inst_type = SR_INST_SCPI;
+       sdi->serial_num = g_strdup(hw_info->serial_number);
+       devc = g_malloc0(sizeof(struct dev_context));
+       devc->limit_frames = 1;
+       devc->model = model;
+
+       sr_scpi_hw_info_free(hw_info);
+
+       devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group *) *
+               model->analog_channels);
+
+       for (i = 0; i < model->analog_channels; i++) {
+               channel_name = g_strdup_printf("CH%d", i + 1);
+               ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_name);
+
+               devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
+
+               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) {
+               devc->digital_group = g_malloc0(sizeof(struct sr_channel_group));
+
+               for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+                       channel_name = g_strdup_printf("D%d", i);
+                       ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
+                       g_free(channel_name);
+                       devc->digital_group->channels = g_slist_append(
+                               devc->digital_group->channels, ch);
+               }
+               devc->digital_group->name = g_strdup("LA");
+               sdi->channel_groups = g_slist_append(sdi->channel_groups,
+                       devc->digital_group);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(timebases); 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 < ARRAY_SIZE(vdivs); i++) {
+               devc->vdivs = &vdivs[i];
+               if (!memcmp(&devc->model->series->min_vdiv,
+                       &vdivs[i], sizeof(uint64_t[2]))) {
+                       devc->vdivs = &vdivs[i];
+                       devc->num_vdivs = ARRAY_SIZE(vdivs) - i;
+                       break;
+               }
+       }
+
+       devc->buffer = g_malloc(devc->model->series->buffer_samples);
+       sr_dbg("Setting device context buffer size: %i.", devc->model->series->buffer_samples);
+       devc->data = g_malloc(devc->model->series->buffer_samples * sizeof(float));
+
+       devc->data_source = DATA_SOURCE_SCREEN;
+
+       sdi->priv = devc;
+
+       return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       /* TODO: Implement RPC call for LXI device discovery. */
+       return sr_scpi_scan(di->context, options, probe_device);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       int ret;
+       struct sr_scpi_dev_inst *scpi = sdi->conn;
+
+       if ((ret = sr_scpi_open(scpi)) < 0) {
+               sr_err("Failed to open SCPI device: %s.", sr_strerror(ret));
+               return SR_ERR;
+       }
+
+       if ((ret = siglent_sds_get_dev_cfg(sdi)) < 0) {
+               sr_err("Failed to get device config: %s.", sr_strerror(ret));
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       return sr_scpi_close(sdi->conn);
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       struct sr_channel *ch;
+       const char *tmp_str;
+       int analog_channel = -1;
+       float smallest_diff = INFINITY;
+       int idx = -1;
+       unsigned i;
+
+       if (!sdi)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+
+       /* 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 (key) {
+       case SR_CONF_NUM_HDIV:
+               *data = g_variant_new_int32(devc->model->series->num_horizontal_divs);
+               break;
+       case SR_CONF_NUM_VDIV:
+               *data = g_variant_new_int32(devc->num_vdivs);
+               break;
+       case SR_CONF_LIMIT_FRAMES:
+               *data = g_variant_new_uint64(devc->limit_frames);
+               break;
+       case SR_CONF_DATA_SOURCE:
+               if (devc->data_source == DATA_SOURCE_SCREEN)
+                       *data = g_variant_new_string("Screen");
+               else if (devc->data_source == DATA_SOURCE_HISTORY)
+                       *data = g_variant_new_string("History");
+               break;
+       case SR_CONF_SAMPLERATE:
+               siglent_sds_get_dev_cfg_horizontal(sdi);
+               *data = g_variant_new_uint64(devc->samplerate);
+               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
+                       tmp_str = devc->trigger_source;
+               *data = g_variant_new_string(tmp_str);
+               break;
+       case SR_CONF_TRIGGER_SLOPE:
+               if (!strncmp(devc->trigger_slope, "POS", 3)) {
+                       tmp_str = "r";
+               } else if (!strncmp(devc->trigger_slope, "NEG", 3)) {
+                       tmp_str = "f";
+               } else {
+                       sr_dbg("Unknown trigger slope: '%s'.", devc->trigger_slope);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new_string(tmp_str);
+               break;
+       case SR_CONF_TRIGGER_LEVEL:
+               *data = g_variant_new_double(devc->trigger_level);
+               break;
+       case SR_CONF_HORIZ_TRIGGERPOS:
+               *data = g_variant_new_double(devc->horiz_triggerpos);
+               break;
+       case SR_CONF_TIMEBASE:
+               for (i = 0; i < devc->num_timebases; i++) {
+                       float tb, diff;
+
+                       tb = (float)devc->timebases[i][0] / devc->timebases[i][1];
+                       diff = fabs(devc->timebase - tb);
+                       if (diff < smallest_diff) {
+                               smallest_diff = diff;
+                               idx = i;
+                       }
+               }
+               if (idx < 0) {
+                       sr_dbg("Negative timebase index: %d.", idx);
+                       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) {
+                       sr_dbg("Negative analog channel: %d.", analog_channel);
+                       return SR_ERR_NA;
+               }
+               for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+                       float vdiv = (float)vdivs[i][0] / vdivs[i][1];
+                       float diff = fabsf(devc->vdiv[analog_channel] - vdiv);
+                       if (diff < smallest_diff) {
+                               smallest_diff = diff;
+                               idx = i;
+                       }
+               }
+               if (idx < 0) {
+                       sr_dbg("Negative vdiv index: %d.", idx);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new("(tt)", vdivs[idx][0], vdivs[idx][1]);
+               break;
+       case SR_CONF_COUPLING:
+               if (analog_channel < 0) {
+                       sr_dbg("Negative analog channel: %d.", analog_channel);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new_string(devc->coupling[analog_channel]);
+               break;
+       case SR_CONF_PROBE_FACTOR:
+               if (analog_channel < 0) {
+                       sr_dbg("Negative analog channel: %d.", analog_channel);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new_uint64(devc->attenuation[analog_channel]);
+               break;
+       case SR_CONF_AVERAGING:
+               *data = g_variant_new_boolean(devc->average_enabled);
+               break;
+       case SR_CONF_AVG_SAMPLES:
+               *data = g_variant_new_uint64(devc->average_samples);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+       uint64_t p;
+       double t_dbl;
+       int i;
+       int ret, idx;
+       const char *tmp_str;
+       char buffer[16];
+       char *cmd = "";
+       char cmd4[4];
+
+       devc = sdi->priv;
+
+       /* 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 (key) {
+       case SR_CONF_LIMIT_FRAMES:
+               devc->limit_frames = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_TRIGGER_SLOPE:
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
+                       return SR_ERR_ARG;
+               g_free(devc->trigger_slope);
+               devc->trigger_slope = g_strdup((trigger_slopes[idx][0] == 'r') ? "POS" : "NEG");
+               return siglent_sds_config_set(sdi, "%s:TRSL %s",
+                       devc->trigger_source, devc->trigger_slope);
+       case SR_CONF_HORIZ_TRIGGERPOS:
+               t_dbl = g_variant_get_double(data);
+               if (t_dbl < 0.0 || t_dbl > 1.0) {
+                       sr_err("Invalid horiz. trigger position: %g.", t_dbl);
+                       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);
+               return siglent_sds_config_set(sdi, ":TIM:OFFS %s", buffer);
+       case SR_CONF_TRIGGER_LEVEL:
+               t_dbl = g_variant_get_double(data);
+               g_ascii_formatd(buffer, sizeof(buffer), "%.3f", t_dbl);
+               ret = siglent_sds_config_set(sdi, ":TRIG:EDGE:LEV %s", buffer);
+               if (ret == SR_OK)
+                       devc->trigger_level = t_dbl;
+               break;
+       case SR_CONF_TIMEBASE:
+               if ((idx = std_u64_tuple_idx(data, devc->timebases, devc->num_timebases)) < 0)
+                       return SR_ERR_ARG;
+               devc->timebase = (float)devc->timebases[idx][0] / devc->timebases[idx][1];
+               p = devc->timebases[idx][0];
+               switch (devc->timebases[idx][1]) {
+               case 1:
+                       cmd = g_strdup_printf("%" PRIu64 "S", p);
+                       break;
+               case 1000:
+                       cmd = g_strdup_printf("%" PRIu64 "MS", p);
+                       break;
+               case 1000000:
+                       cmd = g_strdup_printf("%" PRIu64 "US", p);
+                       break;
+               case 1000000000:
+                       cmd = g_strdup_printf("%" PRIu64 "NS", p);
+                       break;
+               }
+               ret = siglent_sds_config_set(sdi, "TDIV %s", cmd);
+               g_free(cmd);
+               return ret;
+       case SR_CONF_TRIGGER_SOURCE:
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 0)
+                       return SR_ERR_ARG;
+               g_free(devc->trigger_source);
+               devc->trigger_source = g_strdup(trigger_sources[idx]);
+               if (!strcmp(devc->trigger_source, "AC Line"))
+                       tmp_str = "LINE";
+               else if (!strcmp(devc->trigger_source, "CH1"))
+                       tmp_str = "C1";
+               else if (!strcmp(devc->trigger_source, "CH2"))
+                       tmp_str = "C2";
+               else if (!strcmp(devc->trigger_source, "CH3"))
+                       tmp_str = "C3";
+               else if (!strcmp(devc->trigger_source, "CH4"))
+                       tmp_str = "C4";
+               else if (!strcmp(devc->trigger_source, "Ext"))
+                       tmp_str = "EX";
+               else if (!strcmp(devc->trigger_source, "Ext /5"))
+                       tmp_str = "EX5";
+               else
+                       tmp_str = (char *)devc->trigger_source;
+               return siglent_sds_config_set(sdi, "TRSE EDGE,SR,%s,OFF", tmp_str);
+       case SR_CONF_VDIV:
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+                       return SR_ERR_ARG;
+               devc->vdiv[i] = (float)vdivs[idx][0] / vdivs[idx][1];
+               p = vdivs[idx][0];
+               switch (vdivs[idx][1]) {
+               case 1:
+                       cmd = g_strdup_printf("%" PRIu64 "V", p);
+                       break;
+               case 1000:
+                       cmd = g_strdup_printf("%" PRIu64 "MV", p);
+                       break;
+               case 100000:
+                       cmd = g_strdup_printf("%" PRIu64 "UV", p);
+                       break;
+               }
+               ret = siglent_sds_config_set(sdi, "C%d:VDIV %s", i + 1, cmd);
+               g_free(cmd);
+               return ret;
+       case SR_CONF_COUPLING:
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(coupling))) < 0)
+                       return SR_ERR_ARG;
+               g_free(devc->coupling[i]);
+               devc->coupling[i] = g_strdup(coupling[idx]);
+               strncpy(cmd4, devc->coupling[i], 3);
+               cmd4[3] = 0;
+               return siglent_sds_config_set(sdi, "C%d:CPL %s", i + 1, cmd4);
+       case SR_CONF_PROBE_FACTOR:
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(probe_factor))) < 0)
+                       return SR_ERR_ARG;
+               p = g_variant_get_uint64(data);
+               devc->attenuation[i] = probe_factor[idx];
+               ret = siglent_sds_config_set(sdi, "C%d:ATTN %" PRIu64, i + 1, p);
+               if (ret == SR_OK)
+                       siglent_sds_get_dev_cfg_vertical(sdi);
+               return ret;
+       case SR_CONF_DATA_SOURCE:
+               tmp_str = g_variant_get_string(data, NULL);
+               if (!strcmp(tmp_str, "Display"))
+                       devc->data_source = DATA_SOURCE_SCREEN;
+               else if (devc->model->series->protocol >= SPO_MODEL
+                       && !strcmp(tmp_str, "History"))
+                       devc->data_source = DATA_SOURCE_HISTORY;
+               else {
+                       sr_err("Unknown data source: '%s'.", tmp_str);
+                       return SR_ERR;
+               }
+               break;
+       case SR_CONF_SAMPLERATE:
+               siglent_sds_get_dev_cfg_horizontal(sdi);
+               data = g_variant_new_uint64(devc->samplerate);
+               break;
+       case SR_CONF_AVERAGING:
+               devc->average_enabled = g_variant_get_boolean(data);
+               sr_dbg("%s averaging", devc->average_enabled ? "Enabling" : "Disabling");
+               break;
+       case SR_CONF_AVG_SAMPLES:
+               devc->average_samples = g_variant_get_uint64(data);
+               sr_dbg("Setting averaging rate to %" PRIu64, devc->average_samples);
+               break;  
+       default:
+               return SR_ERR_NA;
+       }
+
+       return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+
+       devc = (sdi) ? sdi->priv : NULL;
+
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               if (!cg)
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+               if (!devc)
+                       return SR_ERR_ARG;
+               if (cg == devc->digital_group) {
+                       *data = std_gvar_array_u32(NULL, 0);
+                       return SR_OK;
+               } else {
+                       if (std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels) < 0)
+                               return SR_ERR_ARG;
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog));
+                       return SR_OK;
+               }
+               break;
+       case SR_CONF_COUPLING:
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(coupling));
+               break;
+       case SR_CONF_PROBE_FACTOR:
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               *data = std_gvar_array_u64(ARRAY_AND_SIZE(probe_factor));
+               break;
+       case SR_CONF_VDIV:
+               if (!devc)
+                       /* Can't know this until we have the exact model. */
+                       return SR_ERR_ARG;
+               if (!cg)
+                       return SR_ERR_CHANNEL_GROUP;
+               *data = std_gvar_tuple_array(devc->vdivs, devc->num_vdivs);
+               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;
+               *data = std_gvar_tuple_array(devc->timebases, devc->num_timebases);
+               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) : 5);
+               break;
+       case SR_CONF_TRIGGER_SLOPE:
+               *data = g_variant_new_strv(ARRAY_AND_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) {
+               /* TODO: Check what must be done here for the data source buffer sizes. */
+               case NON_SPO_MODEL:
+                       *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 1);
+                       break;
+               case SPO_MODEL:
+               case ESERIES:
+                       *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
+                       break;
+               }
+               break;
+       case SR_CONF_NUM_HDIV:
+               *data = g_variant_new_int32(devc->model->series->num_horizontal_divs);
+               break;
+       case SR_CONF_AVG_SAMPLES:
+               *data = std_gvar_array_u64(ARRAY_AND_SIZE(averages));
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct sr_scpi_dev_inst *scpi;
+       struct dev_context *devc;
+       struct sr_channel *ch;
+       struct sr_datafeed_packet packet;
+       gboolean some_digital;
+       GSList *l, *d;
+
+       scpi = sdi->conn;
+       devc = sdi->priv;
+
+       devc->num_frames = 0;
+       some_digital = FALSE;
+
+       /*
+        * Check if there are any logic channels enabled, if so then enable
+        * the MSO, otherwise skip the digital channel setup. Enable and
+        * disable channels on the device is very slow and it is faster when
+        * checked in a small loop without the actual actions.
+        */
+       for (d = sdi->channels; d; d = d->next) {
+               ch = d->data;
+               if (ch->type == SR_CHANNEL_LOGIC && ch->enabled)
+                       some_digital = TRUE;
+       }
+
+       for (l = sdi->channels; l; l = l->next) {
+               ch = l->data;
+               if (ch->type == SR_CHANNEL_ANALOG) {
+                       if (ch->enabled)
+                               devc->enabled_channels = g_slist_append(
+                                       devc->enabled_channels, ch);
+                       if (ch->enabled != devc->analog_channels[ch->index]) {
+                               /* Enabled channel is currently disabled, or vice versa. */
+                               if (siglent_sds_config_set(sdi, "C%d:TRA %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 && some_digital) {
+                       if (ch->enabled) {
+                               /* Turn on LA module if currently off and digital channels are enabled. */
+                               if (!devc->la_enabled) {
+                                       if (siglent_sds_config_set(sdi, "DI:SW?") != SR_OK)
+                                               return SR_ERR;
+                                       devc->la_enabled = TRUE;
+                               }
+                               devc->enabled_channels = g_slist_append(
+                                       devc->enabled_channels, ch);
+                       }
+                       /* Enabled channel is currently disabled, or vice versa. */
+                       if (siglent_sds_config_set(sdi, "D%d:TRA %s", ch->index,
+                               ch->enabled ? "ON" : "OFF") != SR_OK)
+                               return SR_ERR;
+                       devc->digital_channels[ch->index] = ch->enabled;
+               }
+       }
+       if (!devc->enabled_channels)
+               return SR_ERR;
+       /* Turn off LA module if on and no digital channels selected. */
+       if (devc->la_enabled && !some_digital)
+               if (siglent_sds_config_set(sdi, "DGST OFF") != SR_OK) {
+                       devc->la_enabled = FALSE;
+                       g_usleep(500000);
+                       return SR_ERR;
+               }
+
+       // devc->analog_frame_size = devc->model->series->buffer_samples;
+       // devc->digital_frame_size = devc->model->series->buffer_samples;
+
+       siglent_sds_get_dev_cfg_horizontal(sdi);
+       switch (devc->model->series->protocol) {
+       case SPO_MODEL:
+               if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK)
+                       return SR_ERR;
+               if (devc->average_enabled) {
+                       if (siglent_sds_config_set(sdi, "ACQW AVERAGE,%i", devc->average_samples) != SR_OK)
+                               return SR_ERR;
+               } else {
+                       if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK)
+                               return SR_ERR;
+               }
+               break;
+       case NON_SPO_MODEL:
+               /* TODO: Implement CML/CNL/DL models. */
+               if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK)
+                       return SR_ERR;
+               if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK)
+                       return SR_ERR;
+               break;
+       default:
+               break;
+       }
+
+       sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 7000,
+               siglent_sds_receive, (void *) sdi);
+
+       std_session_send_df_header(sdi);
+
+       devc->channel_entry = devc->enabled_channels;
+
+       if (siglent_sds_capture_start(sdi) != SR_OK)
+               return SR_ERR;
+
+       /* Start of first frame. */
+       packet.type = SR_DF_FRAME_BEGIN;
+       sr_session_send(sdi, &packet);
+
+       return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_scpi_dev_inst *scpi;
+
+       devc = sdi->priv;
+
+       std_session_send_df_end(sdi);
+
+       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 siglent_sds_driver_info = {
+       .name = "siglent-sds",
+       .longname = "Siglent SDS1000/SDS2000",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .context = NULL,
+};
+
+SR_REGISTER_DEV_DRIVER(siglent_sds_driver_info);
diff --git a/src/hardware/siglent-sds/protocol.c b/src/hardware/siglent-sds/protocol.c
new file mode 100644 (file)
index 0000000..3f1749a
--- /dev/null
@@ -0,0 +1,945 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 mhooijboer <marchelh@gmail.com>
+ * 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/>.
+ */
+
+#define _GNU_SOURCE
+
+#include <config.h>
+#include <errno.h>
+#include <glib.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+#include "protocol.h"
+
+/* Set the next event to wait for in siglent_sds_receive(). */
+static void siglent_sds_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 siglent_sds_event_wait(const struct sr_dev_inst *sdi)
+{
+       char *buf;
+       long s;
+       int out;
+       struct dev_context *devc;
+       time_t start;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+       start = time(NULL);
+
+       s = 10000; /* Sleep time for status refresh. */
+       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, ":INR?", &buf) != SR_OK)
+                               return SR_ERR;
+                       sr_atoi(buf, &out);
+                       g_usleep(s);
+               } while (out == 0);
+
+               sr_dbg("Device triggered.");
+
+               if ((devc->timebase < 0.51) && (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 * 1000);
+                       sr_spew("Sleeping for %ld usecs after trigger, "
+                               "to let the acq buffer in the device fill", s);
+                       g_usleep(s);
+               }
+       }
+       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, ":INR?", &buf) != SR_OK)
+                               return SR_ERR;
+                       sr_atoi(buf, &out);
+                       g_usleep(s);
+               /* XXX
+                * Now this loop condition looks suspicious! A bitwise
+                * OR of a variable and a non-zero literal should be
+                * non-zero. Logical AND of several non-zero values
+                * should be non-zero. Are many parts of the condition
+                * not taking effect? Was some different condition meant
+                * to get encoded? This needs review, and adjustment.
+                */
+               } while (out != DEVICE_STATE_TRIG_RDY || out != DEVICE_STATE_DATA_TRIG_RDY || out != DEVICE_STATE_STOPPED);
+
+               sr_dbg("Device triggered.");
+
+               siglent_sds_set_wait_event(devc, WAIT_NONE);
+       }
+
+       return SR_OK;
+}
+
+static int siglent_sds_trigger_wait(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+       return siglent_sds_event_wait(sdi);
+}
+
+/* Wait for scope to got to "Stop" in single shot mode. */
+static int siglent_sds_stop_wait(const struct sr_dev_inst *sdi)
+{
+       return siglent_sds_event_wait(sdi);
+}
+
+/* Send a configuration setting. */
+SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi, const char *format, ...)
+{
+       va_list args;
+       int ret;
+
+       va_start(args, format);
+       ret = sr_scpi_send_variadic(sdi->conn, format, args);
+       va_end(args);
+
+       return ret;
+}
+
+/* Start capturing a new frameset. */
+SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+       switch (devc->model->series->protocol) {
+       case SPO_MODEL:
+               if (devc->data_source == DATA_SOURCE_SCREEN) {
+                       char *buf;
+                       int out;
+
+                       sr_dbg("Starting data capture for active frameset %" PRIu64 " of %" PRIu64,
+                               devc->num_frames + 1, devc->limit_frames);
+                       if (siglent_sds_config_set(sdi, "ARM") != SR_OK)
+                               return SR_ERR;
+                       if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+                               return SR_ERR;
+                       sr_atoi(buf, &out);
+                       if (out == DEVICE_STATE_TRIG_RDY) {
+                               siglent_sds_set_wait_event(devc, WAIT_TRIGGER);
+                       } else if (out == DEVICE_STATE_DATA_TRIG_RDY) {
+                               sr_spew("Device triggered.");
+                               siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+                               return SR_OK;
+                       } else {
+                               sr_spew("Device did not enter ARM mode.");
+                               return SR_ERR;
+                       }
+               } else { /* TODO: Implement history retrieval. */
+                       unsigned int framecount;
+                       char buf[200];
+                       int ret;
+
+                       sr_dbg("Starting data capture for history frameset.");
+                       if (siglent_sds_config_set(sdi, "FPAR?") != SR_OK)
+                               return SR_ERR;
+                       ret = sr_scpi_read_data(sdi->conn, buf, 200);
+                       if (ret < 0) {
+                               sr_err("Read error while reading data header.");
+                               return SR_ERR;
+                       }
+                       memcpy(&framecount, buf + 40, 4);
+                       if (devc->limit_frames > framecount)
+                               sr_err("Frame limit higher than frames in buffer of device!");
+                       else if (devc->limit_frames == 0)
+                               devc->limit_frames = framecount;
+                       sr_dbg("Starting data capture for history frameset %" PRIu64 " of %" PRIu64,
+                               devc->num_frames + 1, devc->limit_frames);
+                       if (siglent_sds_config_set(sdi, "FRAM %i", devc->num_frames + 1) != SR_OK)
+                               return SR_ERR;
+                       if (siglent_sds_channel_start(sdi) != SR_OK)
+                               return SR_ERR;
+                       siglent_sds_set_wait_event(devc, WAIT_STOP);
+               }
+               break;
+       case ESERIES:
+               if (devc->data_source == DATA_SOURCE_SCREEN) {
+                       char *buf;
+                       int out;
+
+                       sr_dbg("Starting data capture for active frameset %" PRIu64 " of %" PRIu64,
+                               devc->num_frames + 1, devc->limit_frames);
+                       if (siglent_sds_config_set(sdi, "ARM") != SR_OK)
+                               return SR_ERR;
+                       if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+                               return SR_ERR;
+                       sr_atoi(buf, &out);
+                       if (out == DEVICE_STATE_TRIG_RDY) {
+                               siglent_sds_set_wait_event(devc, WAIT_TRIGGER);
+                       } else if (out == DEVICE_STATE_DATA_TRIG_RDY) {
+                               sr_spew("Device triggered.");
+                               siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+                               return SR_OK;
+                       } else {
+                               sr_spew("Device did not enter ARM mode.");
+                               return SR_ERR;
+                       }
+               } else { /* TODO: Implement history retrieval. */
+                       unsigned int framecount;
+                       char buf[200];
+                       int ret;
+
+                       sr_dbg("Starting data capture for history frameset.");
+                       if (siglent_sds_config_set(sdi, "FPAR?") != SR_OK)
+                               return SR_ERR;
+                       ret = sr_scpi_read_data(sdi->conn, buf, 200);
+                       if (ret < 0) {
+                               sr_err("Read error while reading data header.");
+                               return SR_ERR;
+                       }
+                       memcpy(&framecount, buf + 40, 4);
+                       if (devc->limit_frames > framecount)
+                               sr_err("Frame limit higher than frames in buffer of device!");
+                       else if (devc->limit_frames == 0)
+                               devc->limit_frames = framecount;
+                       sr_dbg("Starting data capture for history frameset %" PRIu64 " of %" PRIu64,
+                               devc->num_frames + 1, devc->limit_frames);
+                       if (siglent_sds_config_set(sdi, "FRAM %i", devc->num_frames + 1) != SR_OK)
+                               return SR_ERR;
+                       if (siglent_sds_channel_start(sdi) != SR_OK)
+                               return SR_ERR;
+                       siglent_sds_set_wait_event(devc, WAIT_STOP);
+               }
+               break;
+       case NON_SPO_MODEL:
+               siglent_sds_set_wait_event(devc, WAIT_TRIGGER);
+               break;
+       }
+
+       return SR_OK;
+}
+
+/* Start reading data from the current channel. */
+SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_channel *ch;
+       const char *s;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+       ch = devc->channel_entry->data;
+
+       sr_dbg("Start reading data from channel %s.", ch->name);
+
+       switch (devc->model->series->protocol) {
+       case NON_SPO_MODEL:
+       case SPO_MODEL:
+               s = (ch->type == SR_CHANNEL_LOGIC) ? "D%d:WF?" : "C%d:WF? ALL";
+               if (sr_scpi_send(sdi->conn, s, ch->index + 1) != SR_OK)
+                       return SR_ERR;
+               siglent_sds_set_wait_event(devc, WAIT_NONE);
+               break;
+       case ESERIES:
+               if (ch->type == SR_CHANNEL_ANALOG) {
+                       if (sr_scpi_send(sdi->conn, "C%d:WF? ALL",
+                               ch->index + 1) != SR_OK)
+                               return SR_ERR;
+               }
+               siglent_sds_set_wait_event(devc, WAIT_NONE);
+               if (sr_scpi_read_begin(sdi->conn) != SR_OK)
+                       return TRUE;
+               siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+               break;
+       }
+
+       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 siglent_sds_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;
+       int ret, desc_length;
+       int block_offset = 15; /* Offset for descriptor block. */
+       long data_length = 0;
+
+       /* Read header from device. */
+       ret = sr_scpi_read_data(scpi, buf, SIGLENT_HEADER_SIZE);
+       if (ret < SIGLENT_HEADER_SIZE) {
+               sr_err("Read error while reading data header.");
+               return SR_ERR;
+       }
+       sr_dbg("Device returned %i bytes.", ret);
+       devc->num_header_bytes += ret;
+       buf += block_offset; /* Skip to start descriptor block. */
+
+       /* Parse WaveDescriptor header. */
+       memcpy(&desc_length, buf + 36, 4); /* Descriptor block length */
+       memcpy(&data_length, buf + 60, 4); /* Data block length */
+
+       devc->block_header_size = desc_length + 15;
+       devc->num_samples = data_length;
+
+       sr_dbg("Received data block header: '%s' -> block length %d.", buf, ret);
+
+       return ret;
+}
+
+static int siglent_sds_get_digital(const struct sr_dev_inst *sdi, struct sr_channel *ch)
+{
+       struct sr_scpi_dev_inst *scpi = sdi->conn;
+       struct dev_context *devc = sdi->priv;
+       GArray *tmp_samplebuf; /* Temp buffer while iterating over the scope samples */
+       char *buf = (char *)devc->buffer; /* Buffer from scope */
+       uint8_t tmp_value; /* Holding temp value from data */
+       GArray *data_low_channels, *data_high_channels, *buffdata;
+       GSList *l;
+       gboolean low_channels; /* Lower channels enabled */
+       gboolean high_channels; /* Higher channels enabled */
+       int len, channel_index;
+       long samples_index;
+
+       len = 0;
+       channel_index = 0;
+       low_channels = FALSE;
+       high_channels = FALSE;
+       data_low_channels = g_array_new(FALSE, TRUE, sizeof(uint8_t));
+       data_high_channels = g_array_new(FALSE, TRUE, sizeof(uint8_t));
+
+       for (l = sdi->channels; l; l = l->next) {
+               ch = l->data;
+               samples_index = 0;
+               if (ch->type == SR_CHANNEL_LOGIC) {
+                       if (ch->enabled) {
+                               if (sr_scpi_send(sdi->conn, "D%d:WF? DAT2", ch->index) != SR_OK)
+                                       return SR_ERR;
+                               if (sr_scpi_read_begin(scpi) != SR_OK)
+                                       return TRUE;
+                               len = sr_scpi_read_data(scpi, buf, -1);
+                               if (len < 0)
+                                       return TRUE;
+                               len -= 15;
+                               buffdata = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len);
+                               buf += 15; /* Skipping the data header. */
+                               g_array_append_vals(buffdata, buf, len);
+                               tmp_samplebuf = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len); /* New temp buffer. */
+                               for (uint64_t cur_sample_index = 0; cur_sample_index < devc->memory_depth_digital; cur_sample_index++) {
+                                       char sample = (char)g_array_index(buffdata, uint8_t, cur_sample_index);
+                                       for (int ii = 0; ii < 8; ii++, sample >>= 1) {
+                                               if (ch->index < 8) {
+                                                       channel_index = ch->index;
+                                                       if (data_low_channels->len <= samples_index) {
+                                                               tmp_value = 0; /* New sample. */
+                                                               low_channels = TRUE; /* We have at least one enabled low channel. */
+                                                       } else {
+                                                               /* Get previous stored sample from low channel buffer. */
+                                                               tmp_value = g_array_index(data_low_channels, uint8_t, samples_index);
+                                                       }
+                                               } else {
+                                                       channel_index = ch->index - 8;
+                                                       if (data_high_channels->len <= samples_index) {
+                                                               tmp_value = 0; /* New sample. */
+                                                               high_channels = TRUE; /* We have at least one enabled high channel. */
+                                                       } else {
+                                                               /* Get previous stored sample from high channel buffer. */
+                                                               tmp_value = g_array_index(data_high_channels, uint8_t, samples_index);
+                                                       }
+                                               }
+                                               /* Check if the current scope sample bit is set. */
+                                               if (sample & 0x1)
+                                                       tmp_value |= 1UL << channel_index; /* Set current scope sample bit based on channel index. */
+                                               g_array_append_val(tmp_samplebuf, tmp_value);
+                                               samples_index++;
+                                       }
+                               }
+
+                               /* Clear the buffers to prepare for the new samples */
+                               if (ch->index < 8) {
+                                       g_array_free(data_low_channels, FALSE);
+                                       data_low_channels = g_array_new(FALSE, FALSE, sizeof(uint8_t));
+                               } else {
+                                       g_array_free(data_high_channels, FALSE);
+                                       data_high_channels = g_array_new(FALSE, FALSE, sizeof(uint8_t));
+                               }
+
+                               /* Storing the converted temp values from the the scope into the buffers. */
+                               for (long index = 0; index < tmp_samplebuf->len; index++) {
+                                       uint8_t value = g_array_index(tmp_samplebuf, uint8_t, index);
+                                       if (ch->index < 8)
+                                               g_array_append_val(data_low_channels, value);
+                                       else
+                                               g_array_append_val(data_high_channels, value);
+                               }
+                               g_array_free(tmp_samplebuf, TRUE);
+                               g_array_free(buffdata, TRUE);
+                       }
+               }
+       }
+
+       /* Combining the lower and higher channel buffers into one buffer for sigrok. */
+       devc->dig_buffer = g_array_new(FALSE, FALSE, sizeof(uint8_t));
+       for (uint64_t index = 0; index < devc->memory_depth_digital; index++) {
+               uint8_t value;
+               if (low_channels) {
+                       value = g_array_index(data_low_channels, uint8_t, index);
+                       g_array_append_val(devc->dig_buffer, value);
+               } else {
+                       value = 0;
+                       g_array_append_val(devc->dig_buffer, value);
+               }
+               if (high_channels) {
+                       value = g_array_index(data_high_channels, uint8_t, index);
+                       g_array_append_val(devc->dig_buffer, value);
+               } else {
+                       value = 0;
+                       g_array_append_val(devc->dig_buffer, value);
+               }
+       }
+
+       g_array_free(data_low_channels, TRUE);
+       g_array_free(data_high_channels, TRUE);
+
+       return len;
+}
+
+SR_PRIV int siglent_sds_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_analog_encoding encoding;
+       struct sr_analog_meaning meaning;
+       struct sr_analog_spec spec;
+       struct sr_datafeed_logic logic;
+       struct sr_channel *ch;
+       int len, i;
+       float wait;
+       gboolean read_complete = false;
+
+       (void)fd;
+
+       if (!(sdi = cb_data))
+               return TRUE;
+
+       if (!(devc = sdi->priv))
+               return TRUE;
+
+       scpi = sdi->conn;
+
+       if (!(revents == G_IO_IN || revents == 0))
+               return TRUE;
+
+       switch (devc->wait_event) {
+       case WAIT_NONE:
+               break;
+       case WAIT_TRIGGER:
+               if (siglent_sds_trigger_wait(sdi) != SR_OK)
+                       return TRUE;
+               if (siglent_sds_channel_start(sdi) != SR_OK)
+                       return TRUE;
+               return TRUE;
+       case WAIT_BLOCK:
+               if (siglent_sds_channel_start(sdi) != SR_OK)
+                       return TRUE;
+               break;
+       case WAIT_STOP:
+               if (siglent_sds_stop_wait(sdi) != SR_OK)
+                       return TRUE;
+               if (siglent_sds_channel_start(sdi) != SR_OK)
+                       return TRUE;
+               return TRUE;
+       default:
+               sr_err("BUG: Unknown event target encountered.");
+               break;
+       }
+
+       ch = devc->channel_entry->data;
+       len = 0;
+
+       if (ch->type == SR_CHANNEL_ANALOG) {
+               if (devc->num_block_bytes == 0) {
+                       /* Wait for the device to fill its output buffers. */
+                       switch (devc->model->series->protocol) {
+                       case NON_SPO_MODEL:
+                       case SPO_MODEL:
+                               /* The older models need more time to prepare the the output buffers due to CPU speed. */
+                               wait = (devc->memory_depth_analog * 2.5);
+                               sr_dbg("Waiting %.f0 ms for device to prepare the output buffers", wait / 1000);
+                               g_usleep(wait);
+                               if (sr_scpi_read_begin(scpi) != SR_OK)
+                                       return TRUE;
+                               break;
+                       case ESERIES:
+                               /* The newer models (ending with the E) have faster CPUs but still need time when a slow timebase is selected. */
+                               if (sr_scpi_read_begin(scpi) != SR_OK)
+                                       return TRUE;
+                               wait = ((devc->timebase * devc->model->series->num_horizontal_divs) * 100000);
+                               sr_dbg("Waiting %.f0 ms for device to prepare the output buffers", wait / 1000);
+                               g_usleep(wait);
+                               break;
+                       }
+
+                       sr_dbg("New block with header expected.");
+                       len = siglent_sds_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(sdi, &packet);
+                               sdi->driver->dev_acquisition_stop(sdi);
+                               return TRUE;
+                       }
+                       devc->num_block_bytes = len;
+                       devc->num_block_read = 0;
+
+                       if (len == -1) {
+                               sr_err("Read error, aborting capture.");
+                               packet.type = SR_DF_FRAME_END;
+                               sr_session_send(sdi, &packet);
+                               sdi->driver->dev_acquisition_stop(sdi);
+                               return TRUE;
+                       }
+
+                       do {
+                               read_complete = false;
+                               if (devc->num_block_bytes > devc->num_samples) {
+                                       /* We received all data as one block. */
+                                       /* Offset the data block buffer past the IEEE header and description header. */
+                                       devc->buffer += devc->block_header_size;
+                                       len = devc->num_samples;
+                               } else {
+                                       sr_dbg("Requesting: %li bytes.", devc->num_samples - devc->num_block_bytes);
+                                       len = sr_scpi_read_data(scpi, (char *)devc->buffer, devc->num_samples-devc->num_block_bytes);
+                                       if (len == -1) {
+                                               sr_err("Read error, aborting capture.");
+                                               packet.type = SR_DF_FRAME_END;
+                                               sr_session_send(sdi, &packet);
+                                               sdi->driver->dev_acquisition_stop(sdi);
+                                               return TRUE;
+                                       }
+                                       devc->num_block_read++;
+                                       devc->num_block_bytes += len;
+                               }
+                               sr_dbg("Received block: %i, %d bytes.", devc->num_block_read, len);
+                               if (ch->type == SR_CHANNEL_ANALOG) {
+                                       float vdiv = devc->vdiv[ch->index];
+                                       float offset = devc->vert_offset[ch->index];
+                                       GArray *float_data;
+                                       static GArray *data;
+                                       float voltage, vdivlog;
+                                       int digits;
+
+                                       data = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len);
+                                       g_array_append_vals(data, devc->buffer, len);
+                                       float_data = g_array_new(FALSE, FALSE, sizeof(float));
+                                       for (i = 0; i < len; i++) {
+                                               voltage = (float)g_array_index(data, int8_t, i) / 25;
+                                               voltage = ((vdiv * voltage) - offset);
+                                               g_array_append_val(float_data, voltage);
+                                       }
+                                       vdivlog = log10f(vdiv);
+                                       digits = -(int) vdivlog + (vdivlog < 0.0);
+                                       sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+                                       analog.meaning->channels = g_slist_append(NULL, ch);
+                                       analog.num_samples = float_data->len;
+                                       analog.data = (float *)float_data->data;
+                                       analog.meaning->mq = SR_MQ_VOLTAGE;
+                                       analog.meaning->unit = SR_UNIT_VOLT;
+                                       analog.meaning->mqflags = 0;
+                                       packet.type = SR_DF_ANALOG;
+                                       packet.payload = &analog;
+                                       sr_session_send(sdi, &packet);
+                                       g_slist_free(analog.meaning->channels);
+                                       g_array_free(data, TRUE);
+                               }
+                               len = 0;
+                               if (devc->num_samples == (devc->num_block_bytes - SIGLENT_HEADER_SIZE)) {
+                                       sr_dbg("Transfer has been completed.");
+                                       devc->num_header_bytes = 0;
+                                       devc->num_block_bytes = 0;
+                                       read_complete = true;
+                                       if (!sr_scpi_read_complete(scpi)) {
+                                               sr_err("Read should have been completed.");
+                                               packet.type = SR_DF_FRAME_END;
+                                               sr_session_send(sdi, &packet);
+                                               sdi->driver->dev_acquisition_stop(sdi);
+                                               return TRUE;
+                                       }
+                                       devc->num_block_read = 0;
+                               } else {
+                                       sr_dbg("%" PRIu64 " of %" PRIu64 " block bytes read.",
+                                               devc->num_block_bytes, devc->num_samples);
+                               }
+                       } while (!read_complete);
+
+                       if (devc->channel_entry->next) {
+                               /* We got the frame for this channel, now get the next channel. */
+                               devc->channel_entry = devc->channel_entry->next;
+                               siglent_sds_channel_start(sdi);
+                       } else {
+                               /* Done with this frame. */
+                               packet.type = SR_DF_FRAME_END;
+                               sr_session_send(sdi, &packet);
+                               if (++devc->num_frames == devc->limit_frames) {
+                                       /* Last frame, stop capture. */
+                                       sdi->driver->dev_acquisition_stop(sdi);
+                               } else {
+                                       /* Get the next frame, starting with the first channel. */
+                                       devc->channel_entry = devc->enabled_channels;
+                                       siglent_sds_capture_start(sdi);
+
+                                       /* Start of next frame. */
+                                       packet.type = SR_DF_FRAME_BEGIN;
+                                       sr_session_send(sdi, &packet);
+                               }
+                       }
+               }
+       } else {
+               if (!siglent_sds_get_digital(sdi, ch))
+                       return TRUE;
+               logic.length = devc->dig_buffer->len;
+               logic.unitsize = 2;
+               logic.data = devc->dig_buffer->data;
+               packet.type = SR_DF_LOGIC;
+               packet.payload = &logic;
+               sr_session_send(sdi, &packet);
+               packet.type = SR_DF_FRAME_END;
+               sr_session_send(sdi, &packet);
+               sdi->driver->dev_acquisition_stop(sdi);
+
+               if (++devc->num_frames == devc->limit_frames) {
+                       /* Last frame, stop capture. */
+                       sdi->driver->dev_acquisition_stop(sdi);
+               } else {
+                       /* Get the next frame, starting with the first channel. */
+                       devc->channel_entry = devc->enabled_channels;
+                       siglent_sds_capture_start(sdi);
+
+                       /* Start of next frame. */
+                       packet.type = SR_DF_FRAME_BEGIN;
+                       sr_session_send(sdi, &packet);
+               }
+       }
+
+       // sr_session_send(sdi, &packet);
+       // packet.type = SR_DF_FRAME_END;
+       // sr_session_send(sdi, &packet);
+       // sdi->driver->dev_acquisition_stop(sdi);
+
+       return TRUE;
+}
+
+SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_channel *ch;
+       char *cmd, *response;
+       unsigned int i;
+       int res, num_tokens;
+       gchar **tokens;
+       int len;
+       float trigger_pos;
+
+       devc = sdi->priv;
+
+       /* Analog channel state. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%i:TRA?", i + 1);
+               res = sr_scpi_get_bool(sdi->conn, cmd, &devc->analog_channels[i]);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+               ch = g_slist_nth_data(sdi->channels, i);
+               ch->enabled = devc->analog_channels[i];
+       }
+       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) {
+               gboolean status;
+
+               sr_dbg("Check logic analyzer channel state.");
+               devc->la_enabled = FALSE;
+               cmd = g_strdup_printf("DI:SW?");
+               res = sr_scpi_get_bool(sdi->conn, cmd, &status);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+               sr_dbg("Logic analyzer status: %s", status ? "On" : "Off");
+               if (status) {
+                       devc->la_enabled = TRUE;
+                       for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+                               cmd = g_strdup_printf("D%i:TRA?", i);
+                               res = sr_scpi_get_bool(sdi->conn, cmd, &devc->digital_channels[i]);
+                               g_free(cmd);
+                               if (res != SR_OK)
+                                       return SR_ERR;
+                               ch = g_slist_nth_data(sdi->channels, i + devc->model->analog_channels);
+                               ch->enabled = devc->digital_channels[i];
+                               sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "On" : "Off");
+                       }
+               } else {
+                       for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+                               ch = g_slist_nth_data(sdi->channels, i + devc->model->analog_channels);
+                               devc->digital_channels[i] = FALSE;
+                               ch->enabled = devc->digital_channels[i];
+                               sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "On" : "Off");
+                       }
+               }
+       }
+
+       /* Timebase. */
+       if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current timebase: %g.", devc->timebase);
+
+       /* Probe attenuation. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%d:ATTN?", i + 1);
+               res = sr_scpi_get_float(sdi->conn, cmd, &devc->attenuation[i]);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+       }
+       sr_dbg("Current probe attenuation:");
+       for (i = 0; i < devc->model->analog_channels; i++)
+               sr_dbg("CH%d %g", i + 1, devc->attenuation[i]);
+
+       /* Vertical gain and offset. */
+       if (siglent_sds_get_dev_cfg_vertical(sdi) != SR_OK)
+               return SR_ERR;
+
+       /* Coupling. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%d:CPL?", 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. */
+       response = NULL;
+       tokens = NULL;
+       if (sr_scpi_get_string(sdi->conn, "TRSE?", &response) != SR_OK)
+               return SR_ERR;
+       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_DATA;
+       }
+       g_free(response);
+       devc->trigger_source = g_strstrip(g_strdup(tokens[2]));
+       sr_dbg("Current trigger source: %s.", devc->trigger_source);
+
+       /* TODO: Horizontal trigger position. */
+       response = "";
+       trigger_pos = 0;
+       // if (sr_scpi_get_string(sdi->conn, g_strdup_printf("%s:TRDL?", devc->trigger_source), &response) != SR_OK)
+       //      return SR_ERR;
+       // len = strlen(response);
+       len = strlen(tokens[4]);
+       if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "us")) {
+               trigger_pos = atof(tokens[4]) / SR_GHZ(1);
+               sr_dbg("Current trigger position us %s.", tokens[4] );
+       } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "ns")) {
+               trigger_pos = atof(tokens[4]) / SR_MHZ(1);
+               sr_dbg("Current trigger position ms %s.", tokens[4] );
+       } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "ms")) {
+               trigger_pos = atof(tokens[4]) / SR_KHZ(1);
+               sr_dbg("Current trigger position ns %s.", tokens[4] );
+       } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "s")) {
+               trigger_pos = atof(tokens[4]);
+               sr_dbg("Current trigger position s %s.", tokens[4] );
+       };
+       devc->horiz_triggerpos = trigger_pos;
+
+       sr_dbg("Current horizontal trigger position %.10f.", devc->horiz_triggerpos);
+
+       /* Trigger slope. */
+       cmd = g_strdup_printf("%s:TRSL?", devc->trigger_source);
+       res = sr_scpi_get_string(sdi->conn, cmd, &devc->trigger_slope);
+       g_free(cmd);
+       if (res != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current trigger slope: %s.", devc->trigger_slope);
+
+       /* Trigger level, only when analog channel. */
+       if (g_str_has_prefix(tokens[2], "C")) {
+               cmd = g_strdup_printf("%s:TRLV?", devc->trigger_source);
+               res = sr_scpi_get_float(sdi->conn, cmd, &devc->trigger_level);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+               sr_dbg("Current trigger level: %g.", devc->trigger_level);
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       char *cmd;
+       unsigned int i;
+       int res;
+
+       devc = sdi->priv;
+
+       /* Vertical gain. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%d:VDIV?", 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]);
+
+       /* Vertical offset. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%d:OFST?", 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]);
+
+       return SR_OK;
+}
+
+SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       char *cmd;
+       int res;
+       char *sample_points_string;
+       float samplerate_scope, fvalue;
+
+       devc = sdi->priv;
+
+       switch (devc->model->series->protocol) {
+       case SPO_MODEL:
+       case NON_SPO_MODEL:
+               cmd = g_strdup_printf("SANU? C1");
+               res = sr_scpi_get_string(sdi->conn, cmd, &sample_points_string);
+               g_free(cmd);
+               samplerate_scope = 0;
+               fvalue = 0;
+               if (res != SR_OK)
+                       return SR_ERR;
+               if (g_strstr_len(sample_points_string, -1, "Mpts") != NULL) {
+                       sample_points_string[strlen(sample_points_string) - 4] = '\0';
+                       if (sr_atof_ascii(sample_points_string, &fvalue) != SR_OK) {
+                               sr_dbg("Invalid float converted from scope response.");
+                               return SR_ERR;
+                       }
+                       samplerate_scope = fvalue * 1000000;
+               } else if (g_strstr_len(sample_points_string, -1, "Kpts") != NULL) {
+                       sample_points_string[strlen(sample_points_string) - 4] = '\0';
+                       if (sr_atof_ascii(sample_points_string, &fvalue) != SR_OK) {
+                               sr_dbg("Invalid float converted from scope response.");
+                               return SR_ERR;
+                       }
+                       samplerate_scope = fvalue * 10000;
+               } else {
+                       samplerate_scope = fvalue;
+               }
+               g_free(sample_points_string);
+               devc->memory_depth_analog = samplerate_scope;
+               break;
+       case ESERIES:
+               cmd = g_strdup_printf("SANU? C1");
+               if (sr_scpi_get_float(sdi->conn, cmd, &fvalue) != SR_OK)
+                       return SR_ERR;
+               devc->memory_depth_analog = (long)fvalue;
+               if (devc->la_enabled) {
+                       cmd = g_strdup_printf("SANU? D0");
+                       if (sr_scpi_get_float(sdi->conn, cmd, &fvalue) != SR_OK)
+                               return SR_ERR;
+                       devc->memory_depth_digital = (long)fvalue;
+               }
+               g_free(cmd);
+               break;
+       };
+
+       /* Get the timebase. */
+       if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK)
+               return SR_ERR;
+
+       sr_dbg("Current timebase: %g.", devc->timebase);
+       devc->samplerate = devc->memory_depth_analog / (devc->timebase * devc->model->series->num_horizontal_divs);
+       sr_dbg("Current samplerate: %0f.", devc->samplerate);
+       sr_dbg("Current memory depth: %lu.", devc->memory_depth_analog);
+
+       return SR_OK;
+}
diff --git a/src/hardware/siglent-sds/protocol.h b/src/hardware/siglent-sds/protocol.h
new file mode 100644 (file)
index 0000000..0916520
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 mhooijboer <marchelh@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_SIGLENT_SDS_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SIGLENT_SDS_PROTOCOL_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "siglent-sds"
+
+/* Size of acquisition buffers */
+//#define ACQ_BUFFER_SIZE (6000000)
+#define ACQ_BUFFER_SIZE (18000000)
+
+#define SIGLENT_HEADER_SIZE 363
+#define SIGLENT_DIG_HEADER_SIZE 346
+
+/* Maximum number of samples to retrieve at once. */
+#define ACQ_BLOCK_SIZE (30 * 1000)
+
+#define MAX_ANALOG_CHANNELS 4
+#define MAX_DIGITAL_CHANNELS 16
+
+#define DEVICE_STATE_STOPPED 0         /* Scope is in stopped state bit */
+#define DEVICE_STATE_DATA_ACQ 1                /* A new signal has been acquired bit */
+#define DEVICE_STATE_TRIG_RDY 8192     /* Trigger is ready bit */
+#define DEVICE_STATE_DATA_TRIG_RDY 8193        /* Trigger is ready bit */
+
+enum protocol_version {
+       SPO_MODEL,
+       NON_SPO_MODEL,
+       ESERIES,
+};
+
+enum data_source {
+       DATA_SOURCE_SCREEN,
+       DATA_SOURCE_HISTORY,
+};
+
+struct siglent_sds_vendor {
+       const char *name;
+       const char *full_name;
+};
+
+struct siglent_sds_series {
+       const struct siglent_sds_vendor *vendor;
+       const char *name;
+       enum protocol_version protocol;
+       uint64_t max_timebase[2];
+       uint64_t min_vdiv[2];
+       int num_horizontal_divs;
+       int num_vertical_divs;
+       int buffer_samples;
+};
+
+struct siglent_sds_model {
+       const struct siglent_sds_series *series;
+       const char *name;
+       uint64_t min_timebase[2];
+       unsigned int analog_channels;
+       bool has_digital;
+       unsigned int digital_channels;
+};
+
+enum wait_events {
+       WAIT_NONE,      /* Don't wait */
+       WAIT_TRIGGER,   /* Wait for trigger */
+       WAIT_BLOCK,     /* Wait for block data (only when reading sample mem) */
+       WAIT_STOP,      /* Wait for scope stopping (only single shots) */
+};
+
+struct dev_context {
+       /* Device model */
+       const struct siglent_sds_model *model;
+
+       /* 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;
+       struct sr_channel_group *digital_group;
+
+       /* Acquisition settings */
+       GSList *enabled_channels;
+       uint64_t limit_frames;
+       uint64_t average_samples;
+       gboolean average_enabled;
+       enum data_source data_source;
+       uint64_t analog_frame_size;
+       uint64_t digital_frame_size;
+       uint64_t num_samples;
+       uint64_t memory_depth_analog;
+       uint64_t memory_depth_digital;
+       long block_header_size;
+       float samplerate;
+
+       /* Device settings */
+       gboolean analog_channels[MAX_ANALOG_CHANNELS];
+       gboolean digital_channels[MAX_DIGITAL_CHANNELS];
+       gboolean la_enabled;
+       float timebase;
+       float attenuation[MAX_ANALOG_CHANNELS];
+       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;
+       float trigger_level;
+       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 data blocks bytes already read. */
+       uint64_t num_block_bytes;
+       /* Number of data blocks read. */
+       int 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;
+       GArray *dig_buffer;
+};
+
+SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi,
+       const char *format, ...);
+SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data);
+SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi);
+
+#endif
index 2b20db838e99cf5441f0bb40d415b63e94c9abce..231df4dbf270b2181c6f8ea3f7f59e98aa6116f1 100644 (file)
 #include <libsigrok-internal.h>
 #include "protocol.h"
 
-/* Supported device scan options.
- */
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-/* Driver capabilities.
- */
 static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
 };
 
-/* Supported trigger match conditions.
- */
 static const int32_t trigger_matches[] = {
        SR_TRIGGER_ZERO,
        SR_TRIGGER_ONE,
@@ -47,22 +41,16 @@ static const int32_t trigger_matches[] = {
        SR_TRIGGER_FALLING,
 };
 
-/* Names assigned to available trigger sources.
- */
-static const char *const trigger_source_names[] = {
+static const char *trigger_sources[] = {
        [TRIGGER_CHANNELS] = "CH",
        [TRIGGER_EXT_TRG] = "TRG",
 };
 
-/* Names assigned to available edge slope choices.
- */
-static const char *const signal_edge_names[] = {
+static const char *signal_edges[] = {
        [EDGE_POSITIVE] = "r",
        [EDGE_NEGATIVE] = "f",
 };
 
-/* Create a new sigrok device instance for the indicated LWLA model.
- */
 static struct sr_dev_inst *dev_inst_new(const struct model_info *model)
 {
        struct sr_dev_inst *sdi;
@@ -70,7 +58,6 @@ static struct sr_dev_inst *dev_inst_new(const struct model_info *model)
        int i;
        char name[8];
 
-       /* Initialize private device context. */
        devc = g_malloc0(sizeof(struct dev_context));
        devc->model = model;
        devc->active_fpga_config = FPGA_NOCONF;
@@ -78,16 +65,13 @@ static struct sr_dev_inst *dev_inst_new(const struct model_info *model)
        devc->samplerate = model->samplerates[0];
        devc->channel_mask = (UINT64_C(1) << model->num_channels) - 1;
 
-       /* Create sigrok device instance. */
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->status = SR_ST_INACTIVE;
-       sdi->vendor = g_strdup(VENDOR_NAME);
+       sdi->vendor = g_strdup("SysClk");
        sdi->model = g_strdup(model->name);
        sdi->priv = devc;
 
-       /* Generate list of logic channels. */
        for (i = 0; i < model->num_channels; i++) {
-               /* The LWLA series simply number channels from CH1 to CHxx. */
                g_snprintf(name, sizeof(name), "CH%d", i + 1);
                sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
        }
@@ -148,8 +132,6 @@ static struct sr_dev_inst *dev_inst_new_matching(GSList *conn_matches,
        return sdi;
 }
 
-/* Scan for SysClk LWLA devices and create a device instance for each one.
- */
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
        GSList *conn_devices, *devices, *node;
@@ -203,30 +185,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, devices);
 }
 
-/* Destroy the private device context.
- */
-static void clear_dev_context(void *priv)
-{
-       struct dev_context *devc;
-
-       devc = priv;
-
-       if (devc->acquisition) {
-               sr_err("Cannot clear device context during acquisition!");
-               return; /* Leak and pray. */
-       }
-       sr_dbg("Device context cleared.");
-
-       g_free(devc);
-}
-
-/* Destroy all device instances.
- */
-static int dev_clear(const struct sr_dev_driver *di)
-{
-       return std_dev_clear(di, &clear_dev_context);
-}
-
 /* Drain any pending data from the USB transfer buffers on the device.
  * This may be necessary e.g. after a crash or generally to clean up after
  * an abnormal condition.
@@ -260,8 +218,6 @@ static int drain_usb(struct sr_usb_dev_inst *usb, unsigned int endpoint)
        return SR_OK;
 }
 
-/* Open and initialize device.
- */
 static int dev_open(struct sr_dev_inst *sdi)
 {
        struct drv_context *drvc;
@@ -273,11 +229,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        devc = sdi->priv;
        usb = sdi->conn;
 
-       if (sdi->status != SR_ST_INACTIVE) {
-               sr_err("Device already open.");
-               return SR_ERR;
-       }
-
        /* Try the whole shebang three times, fingers crossed. */
        for (i = 0; i < 3; i++) {
                ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
@@ -332,8 +283,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        return ret;
 }
 
-/* Shutdown and close device.
- */
 static int dev_close(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -343,10 +292,6 @@ static int dev_close(struct sr_dev_inst *sdi)
        devc = sdi->priv;
        usb = sdi->conn;
 
-       if (sdi->status == SR_ST_INACTIVE) {
-               sr_dbg("Device already closed.");
-               return SR_OK;
-       }
        if (devc->acquisition) {
                sr_err("Cannot close device during acquisition!");
                /* Request stop, leak handle, and prepare for the worst. */
@@ -354,14 +299,14 @@ static int dev_close(struct sr_dev_inst *sdi)
                return SR_ERR_BUG;
        }
 
-       sdi->status = SR_ST_INACTIVE;
-
        /* Download of the shutdown bitstream, if any. */
        ret = (*devc->model->apply_fpga_config)(sdi);
        if (ret != SR_OK)
                sr_warn("Unable to shut down device.");
 
-       libusb_release_interface(usb->devhdl, USB_INTERFACE);
+       if (usb->devhdl)
+               libusb_release_interface(usb->devhdl, USB_INTERFACE);
+
        sr_usb_close(usb);
 
        return ret;
@@ -382,10 +327,8 @@ static int has_devopt(const struct model_info *model, uint32_t key)
        return FALSE;
 }
 
-/* Read device configuration setting.
- */
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-                     const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        unsigned int idx;
@@ -419,21 +362,21 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                break;
        case SR_CONF_CLOCK_EDGE:
                idx = devc->cfg_clock_edge;
-               if (idx >= ARRAY_SIZE(signal_edge_names))
+               if (idx >= ARRAY_SIZE(signal_edges))
                        return SR_ERR_BUG;
-               *data = g_variant_new_string(signal_edge_names[idx]);
+               *data = g_variant_new_string(signal_edges[idx]);
                break;
        case SR_CONF_TRIGGER_SOURCE:
                idx = devc->cfg_trigger_source;
-               if (idx >= ARRAY_SIZE(trigger_source_names))
+               if (idx >= ARRAY_SIZE(trigger_sources))
                        return SR_ERR_BUG;
-               *data = g_variant_new_string(trigger_source_names[idx]);
+               *data = g_variant_new_string(trigger_sources[idx]);
                break;
        case SR_CONF_TRIGGER_SLOPE:
                idx = devc->cfg_trigger_slope;
-               if (idx >= ARRAY_SIZE(signal_edge_names))
+               if (idx >= ARRAY_SIZE(signal_edges))
                        return SR_ERR_BUG;
-               *data = g_variant_new_string(signal_edge_names[idx]);
+               *data = g_variant_new_string(signal_edges[idx]);
                break;
        default:
                /* Must not happen for a key listed in devopts. */
@@ -443,31 +386,8 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        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;
-}
-
-/* Write device configuration setting.
- */
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-                     const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        uint64_t value;
        struct dev_context *devc;
@@ -511,23 +431,17 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                        ? CLOCK_EXT_CLK : CLOCK_INTERNAL;
                break;
        case SR_CONF_CLOCK_EDGE:
-               idx = lookup_index(data, signal_edge_names,
-                                  ARRAY_SIZE(signal_edge_names));
-               if (idx < 0)
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edges))) < 0)
                        return SR_ERR_ARG;
                devc->cfg_clock_edge = idx;
                break;
        case SR_CONF_TRIGGER_SOURCE:
-               idx = lookup_index(data, trigger_source_names,
-                                  ARRAY_SIZE(trigger_source_names));
-               if (idx < 0)
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 0)
                        return SR_ERR_ARG;
                devc->cfg_trigger_source = idx;
                break;
        case SR_CONF_TRIGGER_SLOPE:
-               idx = lookup_index(data, signal_edge_names,
-                                  ARRAY_SIZE(signal_edge_names));
-               if (idx < 0)
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edges))) < 0)
                        return SR_ERR_ARG;
                devc->cfg_trigger_slope = idx;
                break;
@@ -539,10 +453,8 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-/* Apply channel configuration change.
- */
 static int config_channel_set(const struct sr_dev_inst *sdi,
-                             struct sr_channel *ch, unsigned int changes)
+       struct sr_channel *ch, unsigned int changes)
 {
        uint64_t channel_bit;
        struct dev_context *devc;
@@ -570,8 +482,7 @@ static int config_channel_set(const struct sr_dev_inst *sdi,
        return SR_OK;
 }
 
-/* Derive trigger masks from the session's trigger configuration.
- */
+/* Derive trigger masks from the session's trigger configuration. */
 static int prepare_trigger_masks(const struct sr_dev_inst *sdi)
 {
        uint64_t trigger_mask, trigger_values, trigger_edge_mask;
@@ -636,8 +547,6 @@ static int prepare_trigger_masks(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-/* Apply current device configuration to the hardware.
- */
 static int config_commit(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -645,9 +554,6 @@ static int config_commit(const struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        if (devc->acquisition) {
                sr_err("Acquisition still in progress?");
                return SR_ERR;
@@ -666,68 +572,40 @@ static int config_commit(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-/* List available choices for a configuration setting.
- */
 static int config_list(uint32_t key, GVariant **data,
-                      const struct sr_dev_inst *sdi,
-                      const struct sr_channel_group *cg)
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *gvar;
-       GVariantBuilder gvb;
 
-       (void)cg;
+       devc = (sdi) ? sdi->priv : NULL;
 
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(scanopts[0]));
-               return SR_OK;
-       }
-       if (!sdi) {
-               if (key != SR_CONF_DEVICE_OPTIONS)
-                       return SR_ERR_ARG;
-
-               /* List driver capabilities. */
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       drvopts, ARRAY_SIZE(drvopts), sizeof(drvopts[0]));
-               return SR_OK;
-       }
-
-       devc = sdi->priv;
-
-       /* List the model's device options. */
-       if (key == SR_CONF_DEVICE_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       devc->model->devopts, devc->model->num_devopts,
-                       sizeof(devc->model->devopts[0]));
-               return SR_OK;
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return std_opts_config_list(key, data, sdi, cg,
+                       ARRAY_AND_SIZE(scanopts), ARRAY_AND_SIZE(drvopts),
+                       (devc) ? devc->model->devopts : NULL,
+                       (devc) ? devc->model->num_devopts : 0);
        }
 
+       if (!devc)
+               return SR_ERR_ARG;
        if (!has_devopt(devc->model, key | SR_CONF_LIST))
                return SR_ERR_NA;
 
        switch (key) {
        case SR_CONF_SAMPLERATE:
-               g_variant_builder_init(&gvb, G_VARIANT_TYPE_VARDICT);
-               gvar = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
-                       devc->model->samplerates, devc->model->num_samplerates,
-                       sizeof(devc->model->samplerates[0]));
-               g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
-               *data = g_variant_builder_end(&gvb);
+               *data = std_gvar_samplerates(devc->model->samplerates, devc->model->num_samplerates);
                break;
        case SR_CONF_TRIGGER_MATCH:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-                       trigger_matches, ARRAY_SIZE(trigger_matches),
-                       sizeof(trigger_matches[0]));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                break;
        case SR_CONF_TRIGGER_SOURCE:
-               *data = g_variant_new_strv(trigger_source_names,
-                       ARRAY_SIZE(trigger_source_names));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_sources));
                break;
        case SR_CONF_TRIGGER_SLOPE:
        case SR_CONF_CLOCK_EDGE:
-               *data = g_variant_new_strv(signal_edge_names,
-                       ARRAY_SIZE(signal_edge_names));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(signal_edges));
                break;
        default:
                /* Must not happen for a key listed in devopts. */
@@ -743,35 +621,23 @@ static int config_list(uint32_t key, GVariant **data,
  */
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       sr_info("Starting acquisition.");
-
        return lwla_start_acquisition(sdi);
 }
 
-/* Request that a running capture operation be stopped.
- */
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
 
        devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        if (devc->state != STATE_IDLE && !devc->cancel_requested) {
                devc->cancel_requested = TRUE;
-               sr_dbg("Stopping acquisition.");
+               sr_dbg("Requesting cancel.");
        }
 
        return SR_OK;
 }
 
-/* SysClk LWLA driver descriptor.
- */
 static struct sr_dev_driver sysclk_lwla_driver_info = {
        .name = "sysclk-lwla",
        .longname = "SysClk LWLA series",
@@ -780,7 +646,7 @@ static struct sr_dev_driver sysclk_lwla_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = dev_clear,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_channel_set = config_channel_set,
index 6e6413fb9c87babcaa138c33814cd6bbde43e436..dd36f87fc33c9b6f96a6b88b338b97304a5ca113 100644 (file)
@@ -169,7 +169,7 @@ SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
        command[0] = LWLA_WORD(CMD_READ_REG);
        command[1] = LWLA_WORD(reg);
 
-       ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+       ret = lwla_send_command(usb, ARRAY_AND_SIZE(command));
        if (ret != SR_OK)
                return ret;
 
@@ -197,7 +197,7 @@ SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
        command[2] = LWLA_WORD_0(value);
        command[3] = LWLA_WORD_1(value);
 
-       return lwla_send_command(usb, command, ARRAY_SIZE(command));
+       return lwla_send_command(usb, ARRAY_AND_SIZE(command));
 }
 
 SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
index 35e33f79ef8a8cf48069559cfb26925467f9abd5..bf5318d1e64d4c7c7cd21d0ea5a89f43d2121aa4 100644 (file)
@@ -69,8 +69,7 @@ struct sr_usb_dev_inst;
  */
 #define PACKET_SIZE            (5000 * 4 * 5)
 
-/** LWLA protocol command ID codes.
- */
+/** LWLA protocol command ID codes. */
 enum command_id {
        CMD_READ_REG    = 1,
        CMD_WRITE_REG   = 2,
@@ -89,22 +88,19 @@ enum status_flag {
        STATUS_MEM_AVAIL = 1 << 6,
 };
 
-/** LWLA1034 run-length encoding states.
- */
+/** LWLA1034 run-length encoding states. */
 enum rle_state {
        RLE_STATE_DATA,
        RLE_STATE_LEN
 };
 
-/** Register address/value pair.
- */
+/** Register address/value pair. */
 struct regval {
        unsigned int reg;
        uint32_t val;
 };
 
-/** LWLA sample acquisition and decompression state.
- */
+/** LWLA sample acquisition and decompression state. */
 struct acquisition_state {
        uint64_t samples_max;   /* maximum number of samples to process */
        uint64_t samples_done;  /* number of samples sent to the session bus */
index d16a35e71175a2c8f58f18a5bf8d180921d1a442..1c0391f574db34bfcb6fc838bbf37bbb5f2da46e 100644 (file)
 #include "lwla.h"
 #include "protocol.h"
 
-/* Number of logic channels.
- */
+/* Number of logic channels. */
 #define NUM_CHANNELS   16
 
-/* Unit size for the sigrok logic datafeed.
- */
+/* Unit size for the sigrok logic datafeed. */
 #define UNIT_SIZE      ((NUM_CHANNELS + 7) / 8)
 
-/* Size of the acquisition buffer in device memory units.
- */
+/* Size of the acquisition buffer in device memory units. */
 #define MEMORY_DEPTH   (256 * 1024)    /* 256k x 32 bit */
 
-/* Capture memory read start address.
- */
+/* Capture memory read start address. */
 #define READ_START_ADDR        2
 
-/* Number of device memory units (32 bit) to read at a time.
- */
+/* Number of device memory units (32 bit) to read at a time. */
 #define READ_CHUNK_LEN 250
 
-/** LWLA1016 register addresses.
- */
+/** LWLA1016 register addresses. */
 enum reg_addr {
        REG_CHAN_MASK   = 0x1000, /* bit mask of enabled channels */
 
@@ -64,15 +58,13 @@ enum reg_addr {
        REG_DIV_COUNT   = 0x10BC, /* write */
 };
 
-/** Flag bits for REG_MEM_CTRL.
- */
+/** Flag bits for REG_MEM_CTRL. */
 enum mem_ctrl_flag {
        MEM_CTRL_RESET  = 1 << 0,
        MEM_CTRL_WRITE  = 1 << 1,
 };
 
-/** Flag bits for REG_CAP_CTRL.
- */
+/** Flag bits for REG_CAP_CTRL. */
 enum cap_ctrl_flag {
        CAP_CTRL_FIFO32_FULL    = 1 << 0, /* "fifo32_ful" bit */
        CAP_CTRL_FIFO64_FULL    = 1 << 1, /* "fifo64_ful" bit */
@@ -83,22 +75,19 @@ enum cap_ctrl_flag {
        CAP_CTRL_CNTR_NOT_ENDR  = 1 << 6, /* "cntr_not_endr" bit */
 };
 
-/* Available FPGA configurations.
- */
+/* Available FPGA configurations. */
 enum fpga_config {
        FPGA_100 = 0,   /* 100 MS/s, no compression */
        FPGA_100_TS,    /* 100 MS/s, timing-state mode */
 };
 
-/* FPGA bitstream resource filenames.
- */
+/* FPGA bitstream resource filenames. */
 static const char bitstream_map[][32] = {
        [FPGA_100]      = "sysclk-lwla1016-100.rbf",
        [FPGA_100_TS]   = "sysclk-lwla1016-100-ts.rbf",
 };
 
-/* Demangle incoming sample data from the transfer buffer.
- */
+/* Demangle incoming sample data from the transfer buffer. */
 static void read_response(struct acquisition_state *acq)
 {
        uint32_t *in_p, *out_p;
@@ -136,8 +125,7 @@ static void read_response(struct acquisition_state *acq)
        acq->samples_done += run_samples;
 }
 
-/* Demangle and decompress incoming sample data from the transfer buffer.
- */
+/* Demangle and decompress incoming sample data from the transfer buffer. */
 static void read_response_rle(struct acquisition_state *acq)
 {
        uint32_t *in_p;
@@ -209,7 +197,7 @@ static int test_read_memory(const struct sr_dev_inst *sdi,
        command[3] = LWLA_WORD_0(count);
        command[4] = LWLA_WORD_1(count);
 
-       ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+       ret = lwla_send_command(usb, ARRAY_AND_SIZE(command));
        if (ret != SR_OK)
                return ret;
 
@@ -233,8 +221,7 @@ static int test_read_memory(const struct sr_dev_inst *sdi,
        return SR_OK;
 }
 
-/* Select and transfer FPGA bitstream for the current configuration.
- */
+/* Select and transfer FPGA bitstream for the current configuration. */
 static int apply_fpga_config(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -259,8 +246,7 @@ static int apply_fpga_config(const struct sr_dev_inst *sdi)
        return ret;
 }
 
-/* Perform initialization self test.
- */
+/* Perform initialization self test. */
 static int device_init_check(const struct sr_dev_inst *sdi)
 {
        static const struct regval mem_reset[] = {
@@ -283,7 +269,7 @@ static int device_init_check(const struct sr_dev_inst *sdi)
                return SR_ERR;
        }
 
-       ret = lwla_write_regs(sdi->conn, mem_reset, ARRAY_SIZE(mem_reset));
+       ret = lwla_write_regs(sdi->conn, ARRAY_AND_SIZE(mem_reset));
        if (ret != SR_OK)
                return ret;
 
@@ -332,7 +318,7 @@ static int setup_acquisition(const struct sr_dev_inst *sdi)
        if (ret != SR_OK)
                return ret;
 
-       ret = lwla_write_regs(usb, capture_init, ARRAY_SIZE(capture_init));
+       ret = lwla_write_regs(usb, ARRAY_AND_SIZE(capture_init));
        if (ret != SR_OK)
                return ret;
 
@@ -443,8 +429,7 @@ static int handle_response(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-/* Model descriptor for the LWLA1016.
- */
+/* Model descriptor for the LWLA1016. */
 SR_PRIV const struct model_info lwla1016_info = {
        .name = "LWLA1016",
        .num_channels = NUM_CHANNELS,
index a4bda3683016c9f4918ba8c0e08ad1ba1092c280..0f5215a5524e94fd5860f5c80b4db05519c7af8e 100644 (file)
 #include "lwla.h"
 #include "protocol.h"
 
-/* Number of logic channels.
- */
+/* Number of logic channels. */
 #define NUM_CHANNELS   34
 
-/* Bit mask covering all logic channels.
- */
+/* Bit mask covering all logic channels. */
 #define ALL_CHANNELS_MASK      ((UINT64_C(1) << NUM_CHANNELS) - 1)
 
-/* Unit size for the sigrok logic datafeed.
- */
+/* Unit size for the sigrok logic datafeed. */
 #define UNIT_SIZE      ((NUM_CHANNELS + 7) / 8)
 
-/* Size of the acquisition buffer in device memory units.
- */
+/* Size of the acquisition buffer in device memory units. */
 #define MEMORY_DEPTH   (256 * 1024)    /* 256k x 36 bit */
 
-/* Capture memory read start address.
- */
+/* Capture memory read start address. */
 #define READ_START_ADDR        4
 
 /* Number of device memory units (36 bit) to read at a time. Slices of 8
@@ -56,8 +51,7 @@
  */
 #define READ_CHUNK_LEN (28 * 8)
 
-/* Bit mask for the RLE repeat-count-follows flag.
- */
+/* Bit mask for the RLE repeat-count-follows flag. */
 #define RLE_FLAG_LEN_FOLLOWS   (UINT64_C(1) << 35)
 
 /* Start index and count for bulk long register reads.
@@ -67,8 +61,7 @@
 #define READ_LREGS_START       LREG_MEM_FILL
 #define READ_LREGS_COUNT       (LREG_STATUS + 1 - READ_LREGS_START)
 
-/** LWLA1034 register addresses.
- */
+/** LWLA1034 register addresses. */
 enum reg_addr {
        REG_MEM_CTRL    = 0x1074, /* capture buffer control */
        REG_MEM_FILL    = 0x1078, /* capture buffer fill level */
@@ -82,15 +75,13 @@ enum reg_addr {
        REG_LONG_HIGH   = 0x10BC, /* long register high word */
 };
 
-/** Flag bits for REG_MEM_CTRL.
- */
+/** Flag bits for REG_MEM_CTRL. */
 enum mem_ctrl_flag {
        MEM_CTRL_WRITE   = 1 << 0, /* "wr1rd0" bit */
        MEM_CTRL_CLR_IDX = 1 << 1, /* "clr_idx" bit */
 };
 
-/* LWLA1034 long register addresses.
- */
+/* LWLA1034 long register addresses. */
 enum long_reg_addr {
        LREG_CHAN_MASK  = 0,    /* channel enable mask */
        LREG_DIV_COUNT  = 1,    /* clock divider max count */
@@ -107,8 +98,7 @@ enum long_reg_addr {
        LREG_TEST_ID    = 100,  /* constant test ID */
 };
 
-/** Flag bits for LREG_CAP_CTRL.
- */
+/** Flag bits for LREG_CAP_CTRL. */
 enum cap_ctrl_flag {
        CAP_CTRL_TRG_EN       = 1 << 0, /* "trg_en" bit */
        CAP_CTRL_CLR_TIMEBASE = 1 << 2, /* "do_clr_timebase" bit */
@@ -117,8 +107,7 @@ enum cap_ctrl_flag {
        CAP_CTRL_CLR_COUNTER  = 1 << 6, /* "clr_cntr0" bit */
 };
 
-/* Available FPGA configurations.
- */
+/* Available FPGA configurations. */
 enum fpga_config {
        FPGA_OFF = 0,   /* FPGA shutdown config */
        FPGA_INT,       /* internal clock config */
@@ -126,8 +115,7 @@ enum fpga_config {
        FPGA_EXTNEG,    /* external clock, falling edge config */
 };
 
-/* FPGA bitstream resource filenames.
- */
+/* FPGA bitstream resource filenames. */
 static const char bitstream_map[][32] = {
        [FPGA_OFF]      = "sysclk-lwla1034-off.rbf",
        [FPGA_INT]      = "sysclk-lwla1034-int.rbf",
@@ -135,8 +123,7 @@ static const char bitstream_map[][32] = {
        [FPGA_EXTNEG]   = "sysclk-lwla1034-extneg.rbf",
 };
 
-/* Read 64-bit long register.
- */
+/* Read 64-bit long register. */
 static int read_long_reg(const struct sr_usb_dev_inst *usb,
                         uint32_t addr, uint64_t *value)
 {
@@ -164,8 +151,7 @@ static int read_long_reg(const struct sr_usb_dev_inst *usb,
        return SR_OK;
 }
 
-/* Queue access sequence for a long register write.
- */
+/* Queue access sequence for a long register write. */
 static void queue_long_regval(struct acquisition_state *acq,
                              uint32_t addr, uint64_t value)
 {
@@ -175,8 +161,7 @@ static void queue_long_regval(struct acquisition_state *acq,
        lwla_queue_regval(acq, REG_LONG_STROBE, 0);
 }
 
-/* Helper to fill in the long register bulk write command.
- */
+/* Helper to fill in the long register bulk write command. */
 static inline void bulk_long_set(struct acquisition_state *acq,
                                 unsigned int idx, uint64_t value)
 {
@@ -186,8 +171,7 @@ static inline void bulk_long_set(struct acquisition_state *acq,
        acq->xfer_buf_out[4 * idx + 6] = LWLA_WORD_3(value);
 }
 
-/* Helper for dissecting the response to a long register bulk read.
- */
+/* Helper for dissecting the response to a long register bulk read. */
 static inline uint64_t bulk_long_get(const struct acquisition_state *acq,
                                     unsigned int idx)
 {
@@ -290,7 +274,7 @@ static int detect_short_transfer_quirk(const struct sr_dev_inst *sdi)
        command[1] = LWLA_WORD(0);
        command[2] = LWLA_WORD(lreg_count);
 
-       ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+       ret = lwla_send_command(usb, ARRAY_AND_SIZE(command));
        if (ret != SR_OK)
                return ret;
 
@@ -317,8 +301,7 @@ static int detect_short_transfer_quirk(const struct sr_dev_inst *sdi)
        return SR_ERR;
 }
 
-/* Select and transfer FPGA bitstream for the current configuration.
- */
+/* Select and transfer FPGA bitstream for the current configuration. */
 static int apply_fpga_config(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -347,8 +330,7 @@ static int apply_fpga_config(const struct sr_dev_inst *sdi)
        return ret;
 }
 
-/* Perform initialization self test.
- */
+/* Perform initialization self test. */
 static int device_init_check(const struct sr_dev_inst *sdi)
 {
        uint64_t value;
@@ -369,8 +351,7 @@ static int device_init_check(const struct sr_dev_inst *sdi)
        return detect_short_transfer_quirk(sdi);
 }
 
-/* Set up the device in preparation for an acquisition session.
- */
+/* Set up the device in preparation for an acquisition session. */
 static int setup_acquisition(const struct sr_dev_inst *sdi)
 {
        static const struct regval capture_init[] = {
@@ -392,7 +373,7 @@ static int setup_acquisition(const struct sr_dev_inst *sdi)
        usb = sdi->conn;
        acq = devc->acquisition;
 
-       ret = lwla_write_regs(usb, capture_init, ARRAY_SIZE(capture_init));
+       ret = lwla_write_regs(usb, ARRAY_AND_SIZE(capture_init));
        if (ret != SR_OK)
                return ret;
 
@@ -565,8 +546,7 @@ static int handle_response(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-/** Model descriptor for the LWLA1034.
- */
+/** Model descriptor for the LWLA1034. */
 SR_PRIV const struct model_info lwla1034_info = {
        .name = "LWLA1034",
        .num_channels = NUM_CHANNELS,
index 85997249143213304062396e5bea67361ec1a8ae..45c0795210629e812f25981425a95b9666795447 100644 (file)
@@ -22,8 +22,7 @@
 #include "protocol.h"
 #include "lwla.h"
 
-/* Submit an already filled-in USB transfer.
- */
+/* Submit an already filled-in USB transfer. */
 static int submit_transfer(struct dev_context *devc,
                           struct libusb_transfer *xfer)
 {
@@ -40,8 +39,7 @@ static int submit_transfer(struct dev_context *devc,
        return SR_OK;
 }
 
-/* Set up transfer for the next register in a write sequence.
- */
+/* Set up transfer for the next register in a write sequence. */
 static void next_reg_write(struct acquisition_state *acq)
 {
        struct regval *regval;
@@ -56,8 +54,7 @@ static void next_reg_write(struct acquisition_state *acq)
        acq->xfer_out->length = 4 * sizeof(acq->xfer_buf_out[0]);
 }
 
-/* Set up transfer for the next register in a read sequence.
- */
+/* Set up transfer for the next register in a read sequence. */
 static void next_reg_read(struct acquisition_state *acq)
 {
        unsigned int addr;
@@ -70,8 +67,7 @@ static void next_reg_read(struct acquisition_state *acq)
        acq->xfer_out->length = 2 * sizeof(acq->xfer_buf_out[0]);
 }
 
-/* Decode the response to a register read request.
- */
+/* Decode the response to a register read request. */
 static int read_reg_response(struct acquisition_state *acq)
 {
        uint32_t value;
@@ -87,8 +83,7 @@ static int read_reg_response(struct acquisition_state *acq)
        return SR_OK;
 }
 
-/* Enter a new state and submit the corresponding request to the device.
- */
+/* Enter a new state and submit the corresponding request to the device. */
 static int submit_request(const struct sr_dev_inst *sdi,
                          enum protocol_state state)
 {
@@ -123,8 +118,7 @@ static int submit_request(const struct sr_dev_inst *sdi,
        return submit_transfer(devc, acq->xfer_out);
 }
 
-/* Evaluate and act on the response to a capture status request.
- */
+/* Evaluate and act on the response to a capture status request. */
 static void handle_status_response(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -160,8 +154,7 @@ static void handle_status_response(const struct sr_dev_inst *sdi)
        }
 }
 
-/* Evaluate and act on the response to a capture length request.
- */
+/* Evaluate and act on the response to a capture length request. */
 static void handle_length_response(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -191,8 +184,7 @@ static void handle_length_response(const struct sr_dev_inst *sdi)
        submit_request(sdi, STATE_READ_PREPARE);
 }
 
-/* Evaluate and act on the response to a capture memory read request.
- */
+/* Evaluate and act on the response to a capture memory read request. */
 static void handle_read_response(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -250,8 +242,7 @@ static void handle_read_response(const struct sr_dev_inst *sdi)
        submit_request(sdi, STATE_READ_FINISH);
 }
 
-/* Destroy and unset the acquisition state record.
- */
+/* Destroy and unset the acquisition state record. */
 static void clear_acquisition_state(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
@@ -269,8 +260,7 @@ static void clear_acquisition_state(const struct sr_dev_inst *sdi)
        }
 }
 
-/* USB I/O source callback.
- */
+/* USB I/O source callback. */
 static int transfer_event(int fd, int revents, void *cb_data)
 {
        const struct sr_dev_inst *sdi;
@@ -321,8 +311,7 @@ static int transfer_event(int fd, int revents, void *cb_data)
        return G_SOURCE_REMOVE;
 }
 
-/* USB output transfer completion callback.
- */
+/* USB output transfer completion callback. */
 static void LIBUSB_CALL transfer_out_completed(struct libusb_transfer *transfer)
 {
        const struct sr_dev_inst *sdi;
@@ -386,8 +375,7 @@ static void LIBUSB_CALL transfer_out_completed(struct libusb_transfer *transfer)
        }
 }
 
-/* USB input transfer completion callback.
- */
+/* USB input transfer completion callback. */
 static void LIBUSB_CALL transfer_in_completed(struct libusb_transfer *transfer)
 {
        const struct sr_dev_inst *sdi;
@@ -448,8 +436,7 @@ static void LIBUSB_CALL transfer_in_completed(struct libusb_transfer *transfer)
        }
 }
 
-/* Set up the acquisition state record.
- */
+/* Set up the acquisition state record. */
 static int init_acquisition_state(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
index ebfc704a97417b7737f8cd1c7fb11ac0fba93639..0db4bc79eb19fa7b71e7380019fe1cea84306949 100644 (file)
 #ifndef LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H
 #define LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H
 
-#define LOG_PREFIX "sysclk-lwla"
-
 #include <stdint.h>
 #include <libusb.h>
 #include <glib.h>
 #include <libsigrok/libsigrok.h>
 #include <libsigrok-internal.h>
 
-#define VENDOR_NAME    "SysClk"
+#define LOG_PREFIX "sysclk-lwla"
 
 /* Maximum configurable sample count limit.
  * Due to compression, there is no meaningful hardware limit the driver
 
 struct acquisition_state;
 
-/* USB vendor and product IDs.
- */
+/* USB vendor and product IDs. */
 enum {
        USB_VID_SYSCLK   = 0x2961,
        USB_PID_LWLA1016 = 0x6688,
        USB_PID_LWLA1034 = 0x6689,
 };
 
-/* USB device characteristics.
- */
+/* USB device characteristics. */
 enum {
        USB_CONFIG      = 1,
        USB_INTERFACE   = 0,
        USB_TIMEOUT_MS  = 1000,
 };
 
-/** USB device end points.
- */
+/** USB device end points. */
 enum usb_endpoint {
        EP_COMMAND = 2,
        EP_CONFIG  = 4,
        EP_REPLY   = 6 | LIBUSB_ENDPOINT_IN
 };
 
-/** LWLA1034 clock sources.
- */
+/** LWLA1034 clock sources. */
 enum clock_source {
        CLOCK_INTERNAL = 0,
        CLOCK_EXT_CLK,
 };
 
-/** LWLA1034 trigger sources.
- */
+/** LWLA1034 trigger sources. */
 enum trigger_source {
        TRIGGER_CHANNELS = 0,
        TRIGGER_EXT_TRG,
 };
 
-/** Edge choices for the LWLA1034 external clock and trigger inputs.
- */
+/** Edge choices for the LWLA1034 external clock and trigger inputs. */
 enum signal_edge {
        EDGE_POSITIVE = 0,
        EDGE_NEGATIVE,
@@ -96,8 +88,7 @@ enum {
        FPGA_NOCONF = -1,
 };
 
-/** Acquisition protocol states.
- */
+/** Acquisition protocol states. */
 enum protocol_state {
        /* idle states */
        STATE_IDLE = 0,
@@ -114,8 +105,6 @@ enum protocol_state {
        STATE_READ_REQUEST,
 };
 
-/** Private, per-device-instance driver context.
- */
 struct dev_context {
        uint64_t samplerate;    /* requested samplerate */
        uint64_t limit_msec;    /* requested capture duration in ms */
@@ -142,8 +131,7 @@ struct dev_context {
        enum signal_edge cfg_trigger_slope;     /* ext trigger slope setting */
 };
 
-/** LWLA model descriptor.
- */
+/** LWLA model descriptor. */
 struct model_info {
        char name[12];
        int num_channels;
index 4cfadf4b3e43678e333b4c34e56252b285250101..71a14106fcf4f1b17c718a06ab041bc88c7aa5bf 100644 (file)
@@ -29,8 +29,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_ENERGYMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -121,41 +124,22 @@ scan_cleanup:
        return std_scan_complete(di, devices);
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->sw_limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -163,16 +147,12 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct sr_serial_dev_inst *serial = sdi->conn;
        struct dev_context *devc;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        sr_sw_limits_acquisition_start(&devc->sw_limits);
 
        std_session_send_df_header(sdi);
 
-       /* Poll every 50ms, or whenever some data comes in. */
        serial_source_add(sdi->session, serial, G_IO_IN, 50,
                        teleinfo_receive_data, (void *)sdi);
 
@@ -187,7 +167,7 @@ static struct sr_dev_driver teleinfo_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = NULL,
        .config_set = config_set,
        .config_list = config_list,
index 88fbfac36838280303eaa2e2b2afa4c4d10276c2..c87ea8a1f10b37b55491600b6414617d5e3ec0fc 100644 (file)
@@ -96,35 +96,34 @@ static void teleinfo_handle_measurement(struct sr_dev_inst *sdi,
                return;
        }
 
-       if (!strcmp(label, "ADCO")) {
+       if (!strcmp(label, "ADCO"))
                sr_sw_limits_update_samples_read(&devc->sw_limits, 1);
-       } else if (!strcmp(label, "BASE")) {
+       else if (!strcmp(label, "BASE"))
                teleinfo_send_value(sdi, "BASE", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "HCHP")) {
+       else if (!strcmp(label, "HCHP"))
                teleinfo_send_value(sdi, "HP"  , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "HCHC")) {
+       else if (!strcmp(label, "HCHC"))
                teleinfo_send_value(sdi, "HC"  , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "EJPHN")) {
+       else if (!strcmp(label, "EJPHN"))
                teleinfo_send_value(sdi, "HN"  , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "EJPHPM")) {
+       else if (!strcmp(label, "EJPHPM"))
                teleinfo_send_value(sdi, "HPM" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "BBRHPJB")) {
+       else if (!strcmp(label, "BBRHPJB"))
                teleinfo_send_value(sdi, "HPJB", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "BBRHPJW")) {
+       else if (!strcmp(label, "BBRHPJW"))
                teleinfo_send_value(sdi, "HPJW", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "BBRHPJR")) {
+       else if (!strcmp(label, "BBRHPJR"))
                teleinfo_send_value(sdi, "HPJR", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "BBRHCJB")) {
+       else if (!strcmp(label, "BBRHCJB"))
                teleinfo_send_value(sdi, "HCJB", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "BBRHCJW")) {
+       else if (!strcmp(label, "BBRHCJW"))
                teleinfo_send_value(sdi, "HCJW", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "BBRHCJR")) {
+       else if (!strcmp(label, "BBRHCJR"))
                teleinfo_send_value(sdi, "HCJR", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
-       } else if (!strcmp(label, "IINST")) {
+       else if (!strcmp(label, "IINST"))
                teleinfo_send_value(sdi, "IINST", v, SR_MQ_CURRENT, SR_UNIT_AMPERE);
-       } else if (!strcmp(label, "PAPP")) {
+       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,
@@ -221,7 +220,7 @@ SR_PRIV int teleinfo_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->sw_limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index f450547ec6b664b00d75a2ec6507eb2ab84b38fd..b617074fd74293b311181a6780f7d041d05756dd 100644 (file)
@@ -37,15 +37,9 @@ enum optarif {
 
 #define TELEINFO_BUF_SIZE 256
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Acquisition settings */
        struct sr_sw_limits sw_limits;
-
-       /* Operational state */
        enum optarif optarif; /**< The device mode (which measures are reported) */
-
-       /* Temporary state across callbacks */
        uint8_t buf[TELEINFO_BUF_SIZE];
        int buf_len;
 };
index 90f589d73ecc019b77dd0bc0a4cd0d990e957d99..43a5f2513f67ca4822356fa00e74e5b52fd5e150 100644 (file)
 #include "protocol.h"
 
 #define SERIALCOMM "115200/8n1"
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
 
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_GET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET | SR_CONF_GET,
@@ -107,9 +109,10 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                if (strncmp(manufacturer, "testo", 5))
                        continue;
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
 
-               /* Hardcode the 435 for now.*/
+               /* Hardcode the 435 for now. */
                if (strcmp(product, "testo 435/635/735"))
                        continue;
 
@@ -117,7 +120,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                sdi->status = SR_ST_INACTIVE;
                sdi->vendor = g_strdup("Testo");
                sdi->model = g_strdup("435/635/735");
-               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);
@@ -163,7 +165,6 @@ static int dev_open(struct sr_dev_inst *sdi)
                sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
                return SR_ERR;
        }
-       sdi->status = SR_ST_ACTIVE;
 
        return SR_OK;
 }
@@ -173,24 +174,22 @@ static int dev_close(struct sr_dev_inst *sdi)
        struct sr_usb_dev_inst *usb;
 
        usb = sdi->conn;
+
        if (!usb->devhdl)
-               /* Nothing to do. */
-               return SR_OK;
+               return SR_ERR_BUG;
 
        libusb_release_interface(usb->devhdl, 0);
        libusb_close(usb->devhdl);
        usb->devhdl = NULL;
-       sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
        struct sr_usb_dev_inst *usb;
-       char str[128];
 
        (void)cg;
 
@@ -199,8 +198,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                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);
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                break;
        case SR_CONF_LIMIT_MSEC:
        case SR_CONF_LIMIT_SAMPLES:
@@ -212,39 +210,20 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        return sr_sw_limits_config_set(&devc->sw_limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static void receive_data(struct sr_dev_inst *sdi, unsigned char *data, int len)
@@ -285,7 +264,7 @@ static void receive_data(struct sr_dev_inst *sdi, unsigned char *data, int len)
 
        devc->reply_size = 0;
        if (sr_sw_limits_check(&devc->sw_limits))
-               dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        else
                testo_request_packet(sdi);
 
@@ -305,7 +284,7 @@ SR_PRIV void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
 
        if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
                /* USB device was unplugged. */
-               dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
                /* First two bytes in any transfer are FTDI status bytes. */
                if (transfer->actual_length > 2)
@@ -320,7 +299,7 @@ SR_PRIV void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
                               libusb_error_name(ret));
                        g_free(transfer->buffer);
                        libusb_free_transfer(transfer);
-                       dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                }
        } else {
                /* This was the last transfer we're going to receive, so
@@ -347,7 +326,7 @@ static int handle_events(int fd, int revents, void *cb_data)
        drvc = di->context;
 
        if (sr_sw_limits_check(&devc->sw_limits))
-               dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        if (sdi->status == SR_ST_STOPPING) {
                usb_source_remove(sdi->session, drvc->sr_ctx);
@@ -373,10 +352,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        unsigned char *buf;
 
        drvc = di->context;
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-
        devc = sdi->priv;
        usb = sdi->conn;
        devc->reply_size = 0;
@@ -412,9 +387,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        sdi->status = SR_ST_STOPPING;
 
        return SR_OK;
@@ -428,6 +400,7 @@ static struct sr_dev_driver testo_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 396612fdc3a479377ae05e3ac7a529046486abdf..def73d42b5d1e6376002e57658b17802518a9e4c 100644 (file)
@@ -67,7 +67,7 @@ SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi)
        usb = sdi->conn;
 
        sr_dbg("Probing for channels.");
-       if (sdi->driver->dev_open(sdi) != SR_OK)
+       if (sr_dev_open(sdi) != SR_OK)
                return SR_ERR;
        if (testo_set_serial_params(usb) != SR_OK)
                return SR_ERR;
@@ -108,7 +108,7 @@ SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi)
                        /* Got a complete packet. */
                        break;
        }
-       sdi->driver->dev_close(sdi);
+       sr_dev_close(sdi);
 
        if (packet[6] > MAX_CHANNELS) {
                sr_err("Device says it has %d channels!", packet[6]);
@@ -158,7 +158,7 @@ SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi)
                        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);
+               sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
                return SR_ERR;
        }
        sr_dbg("Requested new packet.");
@@ -170,8 +170,8 @@ SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi)
  * 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)
 {
+       static const unsigned char check[] = { 0x21, 0, 0, 0, 1 };
        int i;
-       unsigned char check[] = { 0x21, 0, 0, 0, 1 };
 
        if (len < 5)
                return FALSE;
index 654eb6977a7fabbb5d4fc591ae60f351f5dfa681..a6def0b5343330bcdc1ed7367e5c0c95738a3d3c 100644 (file)
@@ -51,19 +51,13 @@ struct testo_model {
        const uint8_t *request;
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /* Model-specific information */
        const struct testo_model *model;
-
-       /* Acquisition settings */
        struct sr_sw_limits sw_limits;
 
-       /* Operational state */
        uint8_t channel_units[MAX_CHANNELS];
        int num_channels;
 
-       /* Temporary state across callbacks */
        struct libusb_transfer *out_transfer;
        uint8_t reply[MAX_REPLY_SIZE];
        int reply_size;
index 445b4274863a57b9fccae4dea75e0fcdee731624..bc6fa18e1bbd1987e8eee7ef01d33594d7935709 100644 (file)
@@ -31,8 +31,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_SOUNDLEVELMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -79,8 +82,10 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
        serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-       if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+       if (serial_open(serial, SERIAL_RDWR) != SR_OK) {
+               g_free(sdi);
                return NULL;
+       }
 
        sdi->inst_type = SR_INST_SERIAL;
        sdi->conn = serial;
@@ -91,41 +96,22 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        return std_scan_complete(di, g_slist_append(NULL, sdi));
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -133,14 +119,10 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        struct dev_context *devc = sdi->priv;
        struct sr_serial_dev_inst *serial;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        std_session_send_df_header(sdi);
 
        sr_sw_limits_acquisition_start(&devc->limits);
 
-       /* 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);
@@ -156,7 +138,7 @@ static struct sr_dev_driver tondaj_sl_814_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = NULL,
        .config_set = config_set,
        .config_list = config_list,
index 589161652ed79ca6eb9446216c95665a589eca88..13f043056255b7d42fcbae3f1370e2486a615dd9 100644 (file)
@@ -205,7 +205,7 @@ SR_PRIV int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data)
        }
 
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index 8aa8ad9728aebcafea1cbbe8d0a91a4fde3336c7..b7cbd2180ef990aa063197d1c1ef95c466ad9430 100644 (file)
@@ -26,7 +26,6 @@
 
 #define LOG_PREFIX "tondaj-sl-814"
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits limits;
        int state;
index 2a2fc663f6c5208603c4159ce4472678b1a867b7..6ffb485324758be0577015e48eeab57ee2b66ad1 100644 (file)
@@ -28,8 +28,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_GET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET | SR_CONF_GET,
@@ -98,29 +101,16 @@ static int dev_open(struct sr_dev_inst *sdi)
        struct sr_dev_driver *di;
        struct drv_context *drvc;
        struct sr_usb_dev_inst *usb;
-       int ret;
 
        di = sdi->driver;
        drvc = di->context;
        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;
+       return sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -131,26 +121,10 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -163,7 +137,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
        std_session_send_df_header(sdi);
 
-       sr_session_source_add(sdi->session, -1, 0, 10 /* poll_timeout */,
+       sr_session_source_add(sdi->session, -1, 0, 10,
                        uni_t_dmm_receive_data, (void *)sdi);
 
        return SR_OK;
@@ -171,7 +145,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       sr_dbg("Stopping acquisition.");
        std_session_send_df_end(sdi);
        sr_session_source_remove(sdi->session, -1);
 
@@ -189,11 +162,12 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
                        .cleanup = std_cleanup, \
                        .scan = scan, \
                        .dev_list = std_dev_list, \
+                       .dev_clear = std_dev_clear, \
                        .config_get = NULL, \
                        .config_set = config_set, \
                        .config_list = config_list, \
                        .dev_open = dev_open, \
-                       .dev_close = dev_close, \
+                       .dev_close = std_dummy_dev_close /* TODO */, \
                        .dev_acquisition_start = dev_acquisition_start, \
                        .dev_acquisition_stop = dev_acquisition_stop, \
                        .context = NULL, \
index c0ed21f3fbbfa687ff0f5b2ed6b0709c21cb76be..bd231160fee51633708e60bcbf64159fc296c98d 100644 (file)
@@ -103,7 +103,6 @@ static int hid_chip_init(struct sr_dev_inst *sdi, uint16_t baudrate)
 
        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) {
@@ -111,18 +110,13 @@ static int hid_chip_init(struct sr_dev_inst *sdi, uint16_t baudrate)
                               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 */
@@ -158,8 +152,6 @@ static int hid_chip_init(struct sr_dev_inst *sdi, uint16_t baudrate)
                return SR_ERR;
        }
 
-       sr_dbg("Successfully sent initial HID feature report.");
-
        return SR_OK;
 }
 
@@ -172,10 +164,11 @@ static void log_8byte_chunk(const uint8_t *buf)
 
 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]);
+       GString *text;
+
+       text = sr_hexdump_new(buf, 14);
+       sr_dbg("DMM packet:   %s", text->str);
+       sr_hexdump_free(text);
 }
 
 static int get_and_handle_data(struct sr_dev_inst *sdi)
@@ -266,8 +259,8 @@ static int get_and_handle_data(struct sr_dev_inst *sdi)
        }
 
        /* Move remaining bytes to beginning of buffer. */
-       for (i = 0; i < devc->buflen - devc->bufoffset; i++)
-               pbuf[i] = pbuf[devc->bufoffset + i];
+       if (devc->bufoffset < devc->buflen)
+               memmove(pbuf, pbuf + devc->bufoffset, devc->buflen - devc->bufoffset);
        devc->buflen -= devc->bufoffset;
 
        return SR_OK;
@@ -290,7 +283,7 @@ SR_PRIV int uni_t_dmm_receive_data(int fd, int revents, void *cb_data)
 
        /* Abort acquisition if we acquired enough samples. */
        if (sr_sw_limits_check(&devc->limits))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
index 4158833f0ee72cfae6bd32de521e774610e664f0..4258d413d4a3bad72fee264d3f4adb56b2c5cc78 100644 (file)
@@ -45,7 +45,6 @@ struct dmm_info {
 
 #define DMM_BUFSIZE            256
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits limits;
 
index 25f142b49d9da9a9ff60cacbf430c89c21b50c42..8c11497582c480057600c16f48f741bdb95901d4 100644 (file)
@@ -25,8 +25,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_THERMOMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
@@ -37,8 +40,7 @@ static const char *channel_names[] = {
 };
 
 static const char *data_sources[] = {
-       "Live",
-       "Memory",
+       "Live", "Memory",
 };
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
@@ -72,8 +74,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                for (l = usb_devices; l; l = l->next) {
                        sdi = g_malloc0(sizeof(struct sr_dev_inst));
                        sdi->status = SR_ST_INACTIVE;
-                       sdi->vendor = g_strdup(VENDOR);
-                       sdi->model = g_strdup(MODEL);
+                       sdi->vendor = g_strdup("UNI-T");
+                       sdi->model = g_strdup("UT32x");
                        sdi->inst_type = SR_INST_USB;
                        sdi->conn = l->data;
                        for (i = 0; i < ARRAY_SIZE(channel_names); i++)
@@ -127,9 +129,8 @@ static int dev_open(struct sr_dev_inst *sdi)
                sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
                return SR_ERR;
        }
-       sdi->status = SR_ST_ACTIVE;
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
@@ -137,20 +138,19 @@ static int dev_close(struct sr_dev_inst *sdi)
        struct sr_usb_dev_inst *usb;
 
        usb = sdi->conn;
+
        if (!usb->devhdl)
-               /* Nothing to do. */
-               return SR_OK;
+               return SR_ERR_BUG;
 
        libusb_release_interface(usb->devhdl, USB_INTERFACE);
        libusb_close(usb->devhdl);
        usb->devhdl = NULL;
-       sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
@@ -174,17 +174,14 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       const char *tmp_str;
+       int idx;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
@@ -192,13 +189,9 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                devc->limit_samples = g_variant_get_uint64(data);
                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;
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+                       return SR_ERR_ARG;
+               devc->data_source = idx;
                break;
        default:
                return SR_ERR_NA;
@@ -207,23 +200,15 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
        case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        case SR_CONF_DATA_SOURCE:
-               *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
                break;
        default:
                return SR_ERR_NA;
@@ -241,9 +226,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        int len, ret;
        unsigned char cmd[2];
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        drvc = di->context;
        devc = sdi->priv;
        usb = sdi->conn;
@@ -296,9 +278,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       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;
 
@@ -313,7 +292,7 @@ static struct sr_dev_driver uni_t_ut32x_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index a5d6f62b3351336d6d106059631a40bac50614e1..6e04dd9b9038731f0e26b0f1d608f1912abff3f9 100644 (file)
@@ -140,10 +140,8 @@ static void process_packet(struct sr_dev_inst *sdi)
         * 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(sdi);
-       }
-
+       if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
+               sr_dev_acquisition_stop(sdi);
 }
 
 SR_PRIV void LIBUSB_CALL uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer)
index 969ccfa65d3a4212cd8759914899d1b422706cae..59f81eaf4cbf0d908e4ac9657d0c4e34d26d3d91 100644 (file)
@@ -29,8 +29,6 @@
 
 #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
 
@@ -48,18 +46,14 @@ enum {
        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;
 
-       /* Temporary state across callbacks */
        unsigned char packet[32];
        int packet_len;
 };
index ff0302d913ef9d9574bb24eb9e7ceff2270362ea..d415a3f9cc86c4809db8dd44898a1c222dec3d19 100644 (file)
 
 #define VICTOR_VID 0x1244
 #define VICTOR_PID 0xd237
-#define VICTOR_VENDOR "Victor"
 #define VICTOR_INTERFACE 0
 #define VICTOR_ENDPOINT (LIBUSB_ENDPOINT_IN | 1)
 
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+};
 
 static const uint32_t drvopts[] = {
        SR_CONF_MULTIMETER,
 };
 
-static const uint32_t scanopts[] = {
-       SR_CONF_CONN,
-};
-
 static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
@@ -72,11 +69,12 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                if (des.idVendor != VICTOR_VID || des.idProduct != VICTOR_PID)
                        continue;
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
 
                sdi = g_malloc0(sizeof(struct sr_dev_inst));
                sdi->status = SR_ST_INACTIVE;
-               sdi->vendor = g_strdup(VICTOR_VENDOR);
+               sdi->vendor = g_strdup("Victor");
                sdi->connection_id = g_strdup(connection_id);
                devc = g_malloc0(sizeof(struct dev_context));
                sr_sw_limits_init(&devc->limits);
@@ -107,8 +105,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        if (ret != SR_OK)
                return ret;
 
-       /* 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.",
@@ -122,7 +118,6 @@ static int dev_open(struct sr_dev_inst *sdi)
                sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
                return SR_ERR;
        }
-       sdi->status = SR_ST_ACTIVE;
 
        return SR_OK;
 }
@@ -134,23 +129,20 @@ static int dev_close(struct sr_dev_inst *sdi)
        usb = sdi->conn;
 
        if (!usb->devhdl)
-               /* Nothing to do. */
-               return SR_OK;
+               return SR_ERR_BUG;
 
        libusb_release_interface(usb->devhdl, VICTOR_INTERFACE);
        libusb_close(usb->devhdl);
        usb->devhdl = NULL;
-       sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc = sdi->priv;
        struct sr_usb_dev_inst *usb;
-       char str[128];
 
        (void)cg;
 
@@ -159,8 +151,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                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);
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
                break;
        case SR_CONF_LIMIT_SAMPLES:
        case SR_CONF_LIMIT_MSEC:
@@ -172,45 +163,22 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        return sr_sw_limits_config_set(&devc->limits, key, data);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi)
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               else
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
 }
 
 static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
@@ -223,13 +191,13 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
        devc = sdi->priv;
        if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
                /* USB device was unplugged. */
-               dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(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 (sr_sw_limits_check(&devc->limits))
-                               dev_acquisition_stop(sdi);
+                               sr_dev_acquisition_stop(sdi);
                }
        }
        /* Anything else is either an error or a timeout, which is fine:
@@ -242,7 +210,7 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
                               libusb_error_name(ret));
                        g_free(transfer->buffer);
                        libusb_free_transfer(transfer);
-                       dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                }
        } else {
                /* This was the last transfer we're going to receive, so
@@ -269,7 +237,7 @@ static int handle_events(int fd, int revents, void *cb_data)
        drvc = di->context;
 
        if (sr_sw_limits_check(&devc->limits))
-               dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        if (sdi->status == SR_ST_STOPPING) {
                usb_source_remove(sdi->session, drvc->sr_ctx);
@@ -293,9 +261,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        int ret;
        unsigned char *buf;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        usb = sdi->conn;
 
        std_session_send_df_header(sdi);
@@ -324,11 +289,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       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;
@@ -342,7 +302,7 @@ static struct sr_dev_driver victor_dmm_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 13a9befa2322f636b7b7d4c3b3b194d048d91a60..23dfe7ab4e711c02756144bfa36411fa07233a92 100644 (file)
@@ -185,7 +185,7 @@ static void decode_buf(struct sr_dev_inst *sdi, unsigned char *data)
                if (is_diode) {
                        meaning.mq = SR_MQ_VOLTAGE;
                        meaning.unit = SR_UNIT_VOLT;
-                       meaning.mqflags |= SR_MQFLAG_DIODE;
+                       meaning.mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
                        if (ivalue < 0)
                                fvalue = NAN;
                } else {
@@ -269,13 +269,13 @@ static void decode_buf(struct sr_dev_inst *sdi, unsigned char *data)
 
 SR_PRIV int victor_dmm_receive_data(struct sr_dev_inst *sdi, unsigned char *buf)
 {
+       static const unsigned char obfuscation[DMM_DATA_SIZE] = "jodenxunickxia";
+       static const unsigned char shuffle[DMM_DATA_SIZE] = {
+               6, 13, 5, 11, 2, 7, 9, 8, 3, 10, 12, 0, 4, 1
+       };
        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) {
index 5c07d37320ad88bf5f5dea51e4ef1c59677e7f67..fe665e43f320c1d08a58265531fc3d579b7ee676 100644 (file)
@@ -28,7 +28,6 @@
 
 #define DMM_DATA_SIZE 14
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        struct sr_sw_limits limits;
 };
index c58906f85d352bc59014ca6171a74adc622f895b..f80e83dceb7e91537252d05b37a516e12e225ac3 100644 (file)
 static struct sr_dev_driver yokogawa_dlm_driver_info;
 
 static const char *MANUFACTURER_ID = "YOKOGAWA";
-static const char *MANUFACTURER_NAME = "Yokogawa";
 
-static const uint32_t dlm_scanopts[] = {
+static const uint32_t scanopts[] = {
        SR_CONF_CONN,
 };
 
-static const uint32_t dlm_drvopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_LOGIC_ANALYZER,
        SR_CONF_OSCILLOSCOPE,
 };
 
-static const uint32_t dlm_devopts[] = {
+static const uint32_t devopts[] = {
        SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
        SR_CONF_SAMPLERATE | SR_CONF_GET,
        SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
@@ -47,13 +46,13 @@ static const uint32_t dlm_devopts[] = {
        SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const uint32_t dlm_analog_devopts[] = {
+static const uint32_t devopts_cg_analog[] = {
        SR_CONF_NUM_VDIV | SR_CONF_GET,
        SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const uint32_t dlm_digital_devopts[] = {
+static const uint32_t devopts_cg_digital[] = {
 };
 
 enum {
@@ -63,7 +62,7 @@ enum {
        CG_DIGITAL,
 };
 
-static struct sr_dev_inst *probe_usbtmc_device(struct sr_scpi_dev_inst *scpi)
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 {
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
@@ -87,7 +86,7 @@ static struct sr_dev_inst *probe_usbtmc_device(struct sr_scpi_dev_inst *scpi)
                goto fail;
 
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
-       sdi->vendor = g_strdup(MANUFACTURER_NAME);
+       sdi->vendor = g_strdup("Yokogawa");
        sdi->model = g_strdup(model_name);
        sdi->version = g_strdup(hw_info->firmware_version);
 
@@ -118,50 +117,35 @@ fail:
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       return sr_scpi_scan(di->context, options, probe_usbtmc_device);
+       return sr_scpi_scan(di->context, options, probe_device);
 }
 
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
 {
-       struct dev_context *devc;
-
-       devc = priv;
-
        dlm_scope_state_destroy(devc->model_state);
-
        g_free(devc->analog_groups);
        g_free(devc->digital_groups);
-       g_free(devc);
 }
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, clear_helper);
+       return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
-       if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
+       if (sr_scpi_open(sdi->conn) != SR_OK)
                return SR_ERR;
 
        if (dlm_scope_state_query(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;
+       return sr_scpi_close(sdi->conn);
 }
 
 /**
@@ -178,31 +162,30 @@ static int dev_close(struct sr_dev_inst *sdi)
 static int check_channel_group(struct dev_context *devc,
                        const struct sr_channel_group *cg)
 {
-       unsigned int i;
        const struct scope_config *model;
 
+       if (!devc)
+               return CG_INVALID;
        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;
+       if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) >= 0)
+               return CG_ANALOG;
 
-       for (i = 0; i < model->pods; i++)
-               if (cg == devc->digital_groups[i])
-                       return CG_DIGITAL;
+       if (std_cg_idx(cg, devc->digital_groups, model->pods) >= 0)
+               return CG_DIGITAL;
 
        sr_err("Invalid channel group specified.");
+
        return CG_INVALID;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret, cg_type;
-       unsigned int i;
+       int ret, cg_type, idx;
        struct dev_context *devc;
        const struct scope_config *model;
        struct scope_state *state;
@@ -230,34 +213,24 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                ret = SR_OK;
                break;
        case SR_CONF_NUM_VDIV:
-               if (cg_type == CG_NONE) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               } else if (cg_type == CG_ANALOG) {
-                               *data = g_variant_new_int32(model->num_ydivs);
-                               ret = SR_OK;
-                               break;
-               } else {
-                       ret = SR_ERR_NA;
-               }
+               if (cg_type != CG_ANALOG)
+                       return SR_ERR_NA;
+               *data = g_variant_new_int32(model->num_ydivs);
+               ret = SR_OK;
                break;
        case SR_CONF_VDIV:
-               ret = SR_ERR_NA;
-               if (cg_type == CG_NONE) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               } else if (cg_type != CG_ANALOG)
-                       break;
-
-               for (i = 0; i < model->analog_channels; i++) {
-                       if (cg != devc->analog_groups[i])
-                               continue;
-                       *data = g_variant_new("(tt)",
-                                       dlm_vdivs[state->analog_states[i].vdiv][0],
-                                       dlm_vdivs[state->analog_states[i].vdiv][1]);
-                       ret = SR_OK;
-                       break;
-               }
+               if (cg_type != CG_ANALOG)
+                       return SR_ERR_NA;
+               if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               *data = g_variant_new("(tt)",
+                               dlm_vdivs[state->analog_states[idx].vdiv][0],
+                               dlm_vdivs[state->analog_states[idx].vdiv][1]);
+               ret = SR_OK;
                break;
        case SR_CONF_TRIGGER_SOURCE:
                *data = g_variant_new_string((*model->trigger_sources)[state->trigger_source]);
@@ -272,20 +245,14 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                ret = SR_OK;
                break;
        case SR_CONF_COUPLING:
-               ret = SR_ERR_NA;
-               if (cg_type == CG_NONE) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               } else if (cg_type != CG_ANALOG)
-                       break;
-
-               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_states[i].coupling]);
-                       ret = SR_OK;
-                       break;
-               }
+               if (cg_type != CG_ANALOG)
+                       return SR_ERR_NA;
+               if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               *data = g_variant_new_string((*model->coupling_options)[state->analog_states[idx].coupling]);
+               ret = SR_OK;
                break;
        case SR_CONF_SAMPLERATE:
                *data = g_variant_new_uint64(state->sample_rate);
@@ -298,36 +265,14 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return ret;
 }
 
-static GVariant *build_tuples(const uint64_t (*array)[][2], unsigned int n)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       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(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
-{
-       int ret, cg_type;
-       unsigned int i, j;
+       int ret, cg_type, idx, j;
        char float_str[30];
        struct dev_context *devc;
        const struct scope_config *model;
        struct scope_state *state;
-       const char *tmp;
-       uint64_t p, q;
        double tmp_d;
        gboolean update_sample_rate;
 
@@ -341,67 +286,41 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        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;
-                       /* TODO: A and B trigger support possible? */
-                       ret = dlm_trigger_source_set(sdi->conn, (*model->trigger_sources)[i]);
-                       break;
-               }
+               if ((idx = std_str_idx(data, *model->trigger_sources, model->num_trigger_sources)) < 0)
+                       return SR_ERR_ARG;
+               state->trigger_source = idx;
+               /* TODO: A and B trigger support possible? */
+               ret = dlm_trigger_source_set(sdi->conn, (*model->trigger_sources)[idx]);
                break;
        case SR_CONF_VDIV:
-               if (cg_type == CG_NONE) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               }
-
-               g_variant_get(data, "(tt)", &p, &q);
-
-               for (i = 0; i < ARRAY_SIZE(dlm_vdivs); i++) {
-                       if (p != dlm_vdivs[i][0] ||
-                                       q != dlm_vdivs[i][1])
-                               continue;
-                       for (j = 1; j <= model->analog_channels; j++) {
-                               if (cg != devc->analog_groups[j - 1])
-                                       continue;
-                               state->analog_states[j - 1].vdiv = i;
-                               g_ascii_formatd(float_str, sizeof(float_str),
-                                               "%E", (float) p / q);
-                               if (dlm_analog_chan_vdiv_set(sdi->conn, j, float_str) != SR_OK ||
-                                               sr_scpi_get_opc(sdi->conn) != SR_OK)
-                                       return SR_ERR;
-
-                               break;
-                       }
-
-                       ret = SR_OK;
-                       break;
-               }
+               if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(dlm_vdivs))) < 0)
+                       return SR_ERR_ARG;
+               if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               state->analog_states[j].vdiv = idx;
+               g_ascii_formatd(float_str, sizeof(float_str),
+                               "%E", (float) dlm_vdivs[idx][0] / dlm_vdivs[idx][1]);
+               if (dlm_analog_chan_vdiv_set(sdi->conn, j + 1, float_str) != SR_OK ||
+                               sr_scpi_get_opc(sdi->conn) != SR_OK)
+                       return SR_ERR;
+               ret = SR_OK;
                break;
        case SR_CONF_TIMEBASE:
-               g_variant_get(data, "(tt)", &p, &q);
-
-               for (i = 0; i < ARRAY_SIZE(dlm_timebases); i++) {
-                       if (p != dlm_timebases[i][0] ||
-                                       q != dlm_timebases[i][1])
-                               continue;
-                       state->timebase = i;
-                       g_ascii_formatd(float_str, sizeof(float_str),
-                                       "%E", (float) p / q);
-                       ret = dlm_timebase_set(sdi->conn, float_str);
-                       update_sample_rate = TRUE;
-                       break;
-               }
+               if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(dlm_timebases))) < 0)
+                       return SR_ERR_ARG;
+               state->timebase = idx;
+               g_ascii_formatd(float_str, sizeof(float_str),
+                               "%E", (float) dlm_timebases[idx][0] / dlm_timebases[idx][1]);
+               ret = dlm_timebase_set(sdi->conn, float_str);
+               update_sample_rate = TRUE;
                break;
        case SR_CONF_HORIZ_TRIGGERPOS:
                tmp_d = g_variant_get_double(data);
@@ -420,42 +339,24 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
                ret = dlm_horiz_trigger_pos_set(sdi->conn, float_str);
                break;
        case SR_CONF_TRIGGER_SLOPE:
-               tmp = g_variant_get_string(data, NULL);
-
-               if (!tmp || !(tmp[0] == 'f' || tmp[0] == 'r'))
+               if ((idx = std_str_idx(data, ARRAY_AND_SIZE(dlm_trigger_slopes))) < 0)
                        return SR_ERR_ARG;
-
                /* Note: See dlm_trigger_slopes[] in protocol.c. */
-               state->trigger_slope = (tmp[0] == 'r') ?
-                               SLOPE_POSITIVE : SLOPE_NEGATIVE;
-
+               state->trigger_slope = idx;
                ret = dlm_trigger_slope_set(sdi->conn, state->trigger_slope);
                break;
        case SR_CONF_COUPLING:
-               if (cg_type == CG_NONE) {
-                       sr_err("No channel group specified.");
+               if (!cg)
                        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_states[j-1].coupling = i;
-
-                               if (dlm_analog_chan_coupl_set(sdi->conn, j, tmp) != SR_OK ||
-                                               sr_scpi_get_opc(sdi->conn) != SR_OK)
-                                       return SR_ERR;
-                               break;
-                       }
-
-                       ret = SR_OK;
-                       break;
-               }
+               if ((idx = std_str_idx(data, *model->coupling_options, model->num_coupling_options)) < 0)
+                       return SR_ERR_ARG;
+               if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+                       return SR_ERR_ARG;
+               state->analog_states[j].coupling = idx;
+               if (dlm_analog_chan_coupl_set(sdi->conn, j + 1, (*model->coupling_options)[idx]) != SR_OK ||
+                               sr_scpi_get_opc(sdi->conn) != SR_OK)
+                       return SR_ERR;
+               ret = SR_OK;
                break;
        default:
                ret = SR_ERR_NA;
@@ -472,7 +373,7 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
 }
 
 static int config_channel_set(const struct sr_dev_inst *sdi,
-                       struct sr_channel *ch, unsigned int changes)
+       struct sr_channel *ch, unsigned int changes)
 {
        /* Currently we only handle SR_CHANNEL_SET_ENABLED. */
        if (changes != SR_CHANNEL_SET_ENABLED)
@@ -481,57 +382,35 @@ static int config_channel_set(const struct sr_dev_inst *sdi,
        return dlm_channel_state_set(sdi, ch->index, ch->enabled);
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        int cg_type = CG_NONE;
-       struct dev_context *devc = NULL;
-       const struct scope_config *model = NULL;
-
-       /* SR_CONF_SCAN_OPTIONS is always valid, regardless of sdi or channel group. */
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               dlm_scanopts, ARRAY_SIZE(dlm_scanopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* If sdi is NULL, nothing except SR_CONF_DEVICE_OPTIONS can be provided. */
-       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               dlm_drvopts, ARRAY_SIZE(dlm_drvopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       if (!sdi)
-               return SR_ERR_ARG;
+       struct dev_context *devc;
+       const struct scope_config *model;
 
-       devc = sdi->priv;
-       model = devc->model_config;
+       devc = (sdi) ? sdi->priv : NULL;
+       model = (devc) ? devc->model_config : NULL;
 
-       /*
-        * If cg is NULL, only the SR_CONF_DEVICE_OPTIONS that are not
-        * specific to a channel group must be returned.
-        */
        if (!cg) {
                switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
                case SR_CONF_DEVICE_OPTIONS:
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                                       dlm_devopts, ARRAY_SIZE(dlm_devopts), sizeof(uint32_t));
-                       return SR_OK;
+                       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
                case SR_CONF_TIMEBASE:
-                       *data = build_tuples(&dlm_timebases, ARRAY_SIZE(dlm_timebases));
+                       *data = std_gvar_tuple_array(ARRAY_AND_SIZE(dlm_timebases));
                        return SR_OK;
                case SR_CONF_TRIGGER_SOURCE:
                        if (!model)
                                return SR_ERR_ARG;
-                       *data = g_variant_new_strv(*model->trigger_sources,
-                                       g_strv_length((char **)*model->trigger_sources));
+                       *data = g_variant_new_strv(*model->trigger_sources, model->num_trigger_sources);
                        return SR_OK;
                case SR_CONF_TRIGGER_SLOPE:
-                       *data = g_variant_new_strv(dlm_trigger_slopes,
-                                       g_strv_length((char **)dlm_trigger_slopes));
+                       *data = g_variant_new_strv(ARRAY_AND_SIZE(dlm_trigger_slopes));
                        return SR_OK;
                case SR_CONF_NUM_HDIV:
+                       if (!model)
+                               return SR_ERR_ARG;
                        *data = g_variant_new_uint32(model->num_xdivs);
                        return SR_OK;
                default:
@@ -544,27 +423,22 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
 
        switch (key) {
        case SR_CONF_DEVICE_OPTIONS:
-               if (cg_type == CG_ANALOG) {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               dlm_analog_devopts, ARRAY_SIZE(dlm_analog_devopts), sizeof(uint32_t));
-               } else if (cg_type == CG_DIGITAL) {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               dlm_digital_devopts, ARRAY_SIZE(dlm_digital_devopts), sizeof(uint32_t));
-               } else {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               NULL, 0, sizeof(uint32_t));
-               }
+               if (cg_type == CG_ANALOG)
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog));
+               else if (cg_type == CG_DIGITAL)
+                       *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_digital));
+               else
+                       *data = std_gvar_array_u32(NULL, 0);
                break;
        case SR_CONF_COUPLING:
-               if (cg_type == CG_NONE)
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               *data = g_variant_new_strv(*model->coupling_options,
-                               g_strv_length((char **)*model->coupling_options));
+               *data = g_variant_new_strv(*model->coupling_options, model->num_coupling_options);
                break;
        case SR_CONF_VDIV:
-               if (cg_type == CG_NONE)
+               if (!cg)
                        return SR_ERR_CHANNEL_GROUP;
-               *data = build_tuples(&dlm_vdivs, ARRAY_SIZE(dlm_vdivs));
+               *data = std_gvar_tuple_array(ARRAY_AND_SIZE(dlm_vdivs));
                break;
        default:
                return SR_ERR_NA;
@@ -612,9 +486,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        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;
@@ -647,7 +518,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        devc->current_channel = devc->enabled_channels;
        dlm_channel_data_request(sdi);
 
-       /* Call our callback when data comes in or after 5ms. */
        sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 5,
                        dlm_data_receive, (void *)sdi);
 
@@ -660,9 +530,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 
        std_session_send_df_end(sdi);
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        devc->num_frames = 0;
index cf885f7ac01f984493437ca563cba2c4a4f93bcd..1bd863ee1545a645139111eaa78993657172758d 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- *
- * <em>Yokogawa DL/DLM series</em> oscilloscope driver
- * @internal
- */
-
 #include <config.h>
 #include "scpi.h"
 #include "protocol.h"
 
-static const char *dlm_coupling_options[] = {
-       "AC",
-       "DC",
-       "DC50",
-       "GND",
-       NULL,
+static const char *coupling_options[] = {
+       "AC", "DC", "DC50", "GND",
 };
 
-static const char *dlm_2ch_trigger_sources[] = {
-       "1",
-       "2",
-       "LINE",
-       "EXT",
-       NULL,
+static const char *trigger_sources_2ch[] = {
+       "1", "2", "LINE", "EXT",
 };
 
 /* TODO: Is BITx handled correctly or is Dx required? */
-static const char *dlm_4ch_trigger_sources[] = {
-       "1",
-       "2",
-       "3",
-       "4",
-       "LINE",
-       "EXT",
-       "BIT1",
-       "BIT2",
-       "BIT3",
-       "BIT4",
-       "BIT5",
-       "BIT6",
-       "BIT7",
-       "BIT8",
-       NULL,
+static const char *trigger_sources_4ch[] = {
+       "1", "2", "3", "4",
+       "LINE", "EXT", "BIT1",
+       "BIT2", "BIT3", "BIT4", "BIT5", "BIT6", "BIT7", "BIT8",
 };
 
 /* Note: Values must correlate to the trigger_slopes values. */
-const char *dlm_trigger_slopes[3] = {
-       "r",
-       "f",
-       NULL,
+const char *dlm_trigger_slopes[2] = {
+       "r", "f",
 };
 
 const uint64_t dlm_timebases[36][2] = {
@@ -137,56 +108,18 @@ const uint64_t dlm_vdivs[17][2] = {
 };
 
 static const char *scope_analog_channel_names[] = {
-       "1",
-       "2",
-       "3",
-       "4",
+       "1", "2", "3", "4",
 };
 
 static const char *scope_digital_channel_names_8[] = {
-       "D0",
-       "D1",
-       "D2",
-       "D3",
-       "D4",
-       "D5",
-       "D6",
-       "D7",
+       "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
 };
 
 static const char *scope_digital_channel_names_32[] = {
-       "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",
+       "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",
 };
 
 static const struct scope_config scope_models[] = {
@@ -200,8 +133,11 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names_8,
 
-               .coupling_options = &dlm_coupling_options,
-               .trigger_sources = &dlm_2ch_trigger_sources,
+               .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+               .trigger_sources = &trigger_sources_2ch,
+               .num_trigger_sources = ARRAY_SIZE(trigger_sources_2ch),
 
                .num_xdivs = 10,
                .num_ydivs = 8,
@@ -216,8 +152,11 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names_8,
 
-               .coupling_options = &dlm_coupling_options,
-               .trigger_sources = &dlm_4ch_trigger_sources,
+               .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+               .trigger_sources = &trigger_sources_4ch,
+               .num_trigger_sources = ARRAY_SIZE(trigger_sources_4ch),
 
                .num_xdivs = 10,
                .num_ydivs = 8,
@@ -234,8 +173,11 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = NULL,
 
-               .coupling_options = &dlm_coupling_options,
-               .trigger_sources = &dlm_4ch_trigger_sources,
+               .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+               .trigger_sources = &trigger_sources_4ch,
+               .num_trigger_sources = ARRAY_SIZE(trigger_sources_4ch),
 
                .num_xdivs = 10,
                .num_ydivs = 8,
@@ -250,8 +192,11 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names_32,
 
-               .coupling_options = &dlm_coupling_options,
-               .trigger_sources = &dlm_4ch_trigger_sources,
+               .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+               .trigger_sources = &trigger_sources_4ch,
+               .num_trigger_sources = ARRAY_SIZE(trigger_sources_4ch),
 
                .num_xdivs = 10,
                .num_ydivs = 8,
@@ -266,8 +211,11 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names_32,
 
-               .coupling_options = &dlm_coupling_options,
-               .trigger_sources = &dlm_4ch_trigger_sources,
+               .coupling_options = &coupling_options,
+               .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+               .trigger_sources = &trigger_sources_4ch,
+               .num_trigger_sources = ARRAY_SIZE(trigger_sources_4ch),
 
                .num_xdivs = 10,
                .num_ydivs = 8,
@@ -334,13 +282,13 @@ static void scope_state_dump(const struct scope_config *config,
  * @return SR_ERR when value couldn't be found, SR_OK otherwise.
  */
 static int array_option_get(char *value, const char *(*array)[],
-               int *result)
+               unsigned int n, int *result)
 {
        unsigned int i;
 
        *result = -1;
 
-       for (i = 0; (*array)[i]; i++)
+       for (i = 0; i < n; i++)
                if (!g_strcmp0(value, (*array)[i])) {
                        *result = i;
                        break;
@@ -461,7 +409,7 @@ static int analog_channel_state_get(const struct sr_dev_inst *sdi,
                if (dlm_analog_chan_vdiv_get(scpi, i + 1, &response) != SR_OK)
                        return SR_ERR;
 
-               if (array_float_get(response, dlm_vdivs, ARRAY_SIZE(dlm_vdivs),
+               if (array_float_get(response, ARRAY_AND_SIZE(dlm_vdivs),
                                &j) != SR_OK) {
                        g_free(response);
                        return SR_ERR;
@@ -488,6 +436,7 @@ static int analog_channel_state_get(const struct sr_dev_inst *sdi,
                }
 
                if (array_option_get(response, config->coupling_options,
+                               config->num_coupling_options,
                                &state->analog_states[i].coupling) != SR_OK) {
                        g_free(response);
                        return SR_ERR;
@@ -699,8 +648,7 @@ SR_PRIV int dlm_scope_state_query(struct sr_dev_inst *sdi)
        if (dlm_timebase_get(sdi->conn, &response) != SR_OK)
                return SR_ERR;
 
-       if (array_float_get(response, dlm_timebases,
-                       ARRAY_SIZE(dlm_timebases), &i) != SR_OK) {
+       if (array_float_get(response, ARRAY_AND_SIZE(dlm_timebases), &i) != SR_OK) {
                g_free(response);
                return SR_ERR;
        }
@@ -724,7 +672,7 @@ SR_PRIV int dlm_scope_state_query(struct sr_dev_inst *sdi)
        }
 
        if (array_option_get(response, config->trigger_sources,
-                       &state->trigger_source) != SR_OK) {
+                       config->num_trigger_sources, &state->trigger_source) != SR_OK) {
                g_free(response);
                return SR_ERR;
        }
@@ -821,7 +769,6 @@ SR_PRIV int dlm_model_get(char *model_id, char **model_name, int *model_index)
  */
 SR_PRIV int dlm_device_init(struct sr_dev_inst *sdi, int model_index)
 {
-       char tmp[25];
        int i;
        struct sr_channel *ch;
        struct dev_context *devc;
@@ -830,9 +777,13 @@ SR_PRIV int dlm_device_init(struct sr_dev_inst *sdi, int model_index)
 
        devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) *
                        scope_models[model_index].analog_channels);
-
        devc->digital_groups = g_malloc0(sizeof(struct sr_channel_group*) *
                        scope_models[model_index].pods);
+       if (!devc->analog_groups || !devc->digital_groups) {
+               g_free(devc->analog_groups);
+               g_free(devc->digital_groups);
+               return SR_ERR_MALLOC;
+       }
 
        /* Add analog channels, each in its own group. */
        for (i = 0; i < scope_models[model_index].analog_channels; i++) {
@@ -851,13 +802,10 @@ SR_PRIV int dlm_device_init(struct sr_dev_inst *sdi, int model_index)
 
        /* Add digital channel groups. */
        for (i = 0; i < scope_models[model_index].pods; i++) {
-               g_snprintf(tmp, sizeof(tmp), "POD%d", i);
-
                devc->digital_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
                if (!devc->digital_groups[i])
                        return SR_ERR_MALLOC;
-
-               devc->digital_groups[i]->name = g_strdup(tmp);
+               devc->digital_groups[i]->name = g_strdup_printf("POD%d", i);
                sdi->channel_groups = g_slist_append(sdi->channel_groups,
                                devc->digital_groups[i]);
        }
@@ -1171,7 +1119,7 @@ SR_PRIV int dlm_data_receive(int fd, int revents, void *cb_data)
                 * As of now we only support importing the current acquisition
                 * data so we're going to stop at this point.
                 */
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
                return TRUE;
        } else
                devc->current_channel = devc->current_channel->next;
index 80def3f9de550d752f54dc7371f0525ae9e13190..6b27385d216dbaa902e87e8014e638b12dcefb0b 100644 (file)
@@ -31,6 +31,7 @@
 #include "protocol_wrappers.h"
 
 #define LOG_PREFIX "yokogawa-dlm"
+
 #define MAX_INSTRUMENT_VERSIONS 8
 
 #define RECEIVE_BUFFER_SIZE 4096
@@ -48,7 +49,7 @@ enum trigger_slopes {
        SLOPE_NEGATIVE
 };
 
-extern const char *dlm_trigger_slopes[3];
+extern const char *dlm_trigger_slopes[2];
 extern const uint64_t dlm_timebases[36][2];
 extern const uint64_t dlm_vdivs[17][2];
 
@@ -95,7 +96,6 @@ struct scope_state {
        uint32_t samples_per_frame;
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
        const void *model_config;
        void *model_state;
index 3d0c3a41a2107d741ba107f9ddeb079ec9940003..7a4041a16143f7d6dc84d7b35af3d4e224692a8f 100644 (file)
@@ -20,7 +20,6 @@
 #include <config.h>
 #include "protocol.h"
 
-#define VENDOR_NAME                    "ZEROPLUS"
 #define USB_INTERFACE                  0
 #define USB_CONFIGURATION              1
 #define NUM_TRIGGER_STAGES             4
@@ -43,6 +42,7 @@ struct zp_model {
  */
 static const struct zp_model zeroplus_models[] = {
        {0x0c12, 0x7002, "LAP-16128U",    16, 128,  200},
+       {0x0c12, 0x7007, "LAP-16032U",    16, 32,   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},
@@ -50,7 +50,9 @@ static const struct zp_model zeroplus_models[] = {
        {0x0c12, 0x700d, "LAP-C(322000)", 32, 2048, 200},
        {0x0c12, 0x700e, "LAP-C(16032)",  16, 32,   100},
        {0x0c12, 0x7016, "LAP-C(162000)", 16, 2048, 200},
-       {0x0c12, 0x7100, "AKIP-9101", 16, 256, 200},
+       {0x0c12, 0x7025, "LAP-C(16128+)", 16, 128,  200},
+       {0x0c12, 0x7064, "Logian-16L",    16, 128,  200},
+       {0x0c12, 0x7100, "AKIP-9101",     16, 256,  200},
        ALL_ZERO
 };
 
@@ -127,8 +129,6 @@ const uint64_t samplerates_200[] = {
        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;
@@ -196,7 +196,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
 
                libusb_close(hdl);
 
-               usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+               if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                       continue;
 
                prof = NULL;
                for (j = 0; j < zeroplus_models[j].vid; j++) {
@@ -205,20 +206,18 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                                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. */
                sdi = g_malloc0(sizeof(struct sr_dev_inst));
                sdi->status = SR_ST_INACTIVE;
-               sdi->vendor = g_strdup(VENDOR_NAME);
+               sdi->vendor = g_strdup("ZEROPLUS");
                sdi->model = g_strdup(prof->model_name);
                sdi->serial_num = g_strdup(serial_num);
                sdi->connection_id = g_strdup(connection_id);
 
-               /* Allocate memory for our private driver context. */
                devc = g_malloc0(sizeof(struct dev_context));
                sdi->priv = devc;
                devc->prof = prof;
@@ -234,7 +233,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                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++)
                        sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE,
                                        channel_names[j]);
@@ -266,8 +264,6 @@ static int dev_open(struct sr_dev_inst *sdi)
        if (ret != SR_OK)
                return ret;
 
-       sdi->status = SR_ST_ACTIVE;
-
        ret = libusb_set_configuration(usb->devhdl, USB_CONFIGURATION);
        if (ret < 0) {
                sr_err("Unable to set USB configuration %d: %s.",
@@ -323,7 +319,7 @@ static int dev_close(struct sr_dev_inst *sdi)
        usb = sdi->conn;
 
        if (!usb->devhdl)
-               return SR_ERR;
+               return SR_ERR_BUG;
 
        sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
                usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
@@ -331,16 +327,14 @@ static int dev_close(struct sr_dev_inst *sdi)
        libusb_reset_device(usb->devhdl);
        libusb_close(usb->devhdl);
        usb->devhdl = NULL;
-       sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *range[2];
 
        (void)cg;
 
@@ -357,9 +351,7 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
                *data = g_variant_new_uint64(devc->capture_ratio);
                break;
        case SR_CONF_VOLTAGE_THRESHOLD:
-               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);
+               *data = std_gvar_tuple_double(devc->cur_threshold, devc->cur_threshold);
                break;
        default:
                return SR_ERR_NA;
@@ -368,17 +360,14 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
        gdouble low, high;
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        switch (key) {
@@ -387,7 +376,8 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        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));
+               devc->capture_ratio = g_variant_get_uint64(data);
+               break;
        case SR_CONF_VOLTAGE_THRESHOLD:
                g_variant_get(data, "(dd)", &low, &high);
                return set_voltage_threshold(devc, (low + high) / 2.0);
@@ -398,68 +388,37 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct dev_context *devc;
-       GVariant *gvar, *grange[2];
-       GVariantBuilder gvb;
-       double v;
-       GVariant *range[2];
-
-       (void)cg;
 
        switch (key) {
        case SR_CONF_DEVICE_OPTIONS:
-               if (!sdi) {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               } else {
-                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               }
-               break;
+               return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
        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 {
+               if (devc->prof->max_sampling_freq == 100)
+                       *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates_100));
+               else if (devc->prof->max_sampling_freq == 200)
+                       *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates_200));
+               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));
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
                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);
+               *data = std_gvar_min_max_step_thresholds(-6.0, 6.0, 0.1);
                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);
+               *data = std_gvar_tuple_u64(0, devc->max_sample_depth);
                break;
        default:
                return SR_ERR_NA;
@@ -490,9 +449,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        unsigned int discard;
        int trigger_now;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
        if (analyzer_add_triggers(sdi) != SR_OK) {
@@ -591,8 +547,9 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
                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 (res != PACKET_SIZE)
+                       sr_warn("Tried to read %d bytes, actually read %d.",
+                               PACKET_SIZE, res);
 
                if (discard >= PACKET_SIZE / 4) {
                        discard -= PACKET_SIZE / 4;
@@ -647,7 +604,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-/* TODO: This stops acquisition on ALL devices, ignoring dev_index. */
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
        struct sr_usb_dev_inst *usb;
@@ -669,7 +625,7 @@ static struct sr_dev_driver zeroplus_logic_cube_driver_info = {
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = NULL,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 6e0e96149711ce0f4d4d8e96c9212c518efd54b0..bc517c35acbf526d9c513d11fb50dd9e15417e8a 100644 (file)
@@ -79,20 +79,6 @@ SR_PRIV int set_limit_samples(struct dev_context *devc, uint64_t samples)
        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)
@@ -118,7 +104,7 @@ SR_PRIV void set_triggerbar(struct dev_context *devc)
                trigger_depth = devc->limit_samples;
 
        if (devc->trigger)
-               triggerbar = trigger_depth * devc->capture_ratio / 100;
+               triggerbar = (trigger_depth * devc->capture_ratio) / 100;
        else
                triggerbar = 0;
 
index cd83f9b1c37660e77a4ddcda9dd8acb8cc70bbb9..e179f1f8a2280b86b89e5f5e1c811ab6f52eaa1e 100644 (file)
@@ -28,9 +28,8 @@
 #include "libsigrok-internal.h"
 #include "analyzer.h"
 
-#define LOG_PREFIX "zeroplus"
+#define LOG_PREFIX "zeroplus-logic-cube"
 
-/* Private, per-device-instance driver context. */
 struct dev_context {
        uint64_t cur_samplerate;
        uint64_t max_samplerate;
@@ -43,7 +42,7 @@ struct dev_context {
        //uint8_t trigger_value[NUM_TRIGGER_STAGES];
        // uint8_t trigger_buffer[NUM_TRIGGER_STAGES];
        int trigger;
-       unsigned int capture_ratio;
+       uint64_t capture_ratio;
        double cur_threshold;
        const struct zp_model *prof;
 };
@@ -51,7 +50,6 @@ struct dev_context {
 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);
 
diff --git a/src/hardware/zketech-ebd-usb/api.c b/src/hardware/zketech-ebd-usb/api.c
new file mode 100644 (file)
index 0000000..4dbe841
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Sven Bursch-Osewold <sb_git@bursch.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 <config.h>
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_ELECTRONIC_LOAD,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+};
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       struct dev_context *devc;
+       GSList *l;
+       struct sr_dev_inst *sdi;
+       const char *conn, *serialcomm;
+       struct sr_config *src;
+       struct sr_serial_dev_inst *serial;
+       uint8_t reply[MSG_LEN];
+
+       conn = NULL;
+       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/8e1";
+
+       serial = sr_serial_dev_inst_new(conn, serialcomm);
+       if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+               return NULL;
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->status = SR_ST_INACTIVE;
+       sdi->vendor = g_strdup("ZKETECH");
+       sdi->model = g_strdup("EBD-USB");
+       sdi->inst_type = SR_INST_SERIAL;
+       sdi->conn = serial;
+
+       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
+       sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I");
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       g_mutex_init(&devc->rw_mutex);
+       devc->current_limit = 0;
+       devc->running = FALSE;
+       devc->load_activated = FALSE;
+       sr_sw_limits_init(&devc->limits);
+       sdi->priv = devc;
+
+       /* Starting device. */
+       ebd_init(serial, devc);
+       int ret = ebd_read_chars(serial, MSG_LEN, reply);
+       if (ret != MSG_LEN || reply[MSG_FRAME_BEGIN_POS] != MSG_FRAME_BEGIN \
+                       || reply[MSG_FRAME_END_POS] != MSG_FRAME_END) {
+               sr_warn("Invalid message received!");
+               ret = SR_ERR;
+       }
+       ebd_stop(serial, devc);
+
+       serial_close(serial);
+
+       if (ret < 0)
+               return NULL;
+
+       return std_scan_complete(di, g_slist_append(NULL, sdi));
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+
+       devc = (sdi) ? sdi->priv : NULL;
+       if (devc)
+               g_mutex_clear(&devc->rw_mutex);
+
+       return std_serial_dev_close(sdi);
+}
+
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       int ret;
+       struct dev_context *devc;
+       float fvalue;
+
+       (void)cg;
+
+       if (!sdi || !data)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+
+       switch (key) {
+       case SR_CONF_LIMIT_SAMPLES:
+       case SR_CONF_LIMIT_MSEC:
+               return sr_sw_limits_config_get(&devc->limits, key, data);
+       case SR_CONF_CURRENT_LIMIT:
+               ret = ebd_get_current_limit(sdi, &fvalue);
+               if (ret == SR_OK)
+                       *data = g_variant_new_double(fvalue);
+               return ret;
+       default:
+               return SR_ERR_NA;
+       }
+}
+
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       double value;
+       struct dev_context *devc;
+
+       (void)data;
+       (void)cg;
+
+       devc = sdi->priv;
+
+       switch (key) {
+       case SR_CONF_LIMIT_MSEC:
+       case SR_CONF_LIMIT_SAMPLES:
+               return sr_sw_limits_config_set(&devc->limits, key, data);
+       case SR_CONF_CURRENT_LIMIT:
+               value = g_variant_get_double(data);
+               if (value < 0.0 || value > 4.0)
+                       return SR_ERR_ARG;
+               return ebd_set_current_limit(sdi, value);
+       default:
+               return SR_ERR_NA;
+       }
+}
+
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_CURRENT_LIMIT:
+               *data = std_gvar_min_max_step(0.0, 4.0, 0.01);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+
+       devc = sdi->priv;
+       serial = sdi->conn;
+
+       sr_sw_limits_acquisition_start(&devc->limits);
+       std_session_send_df_header(sdi);
+
+       ebd_init(serial, devc);
+       if (!ebd_current_is0(devc))
+               ebd_loadstart(serial, devc);
+
+       serial_source_add(sdi->session, serial, G_IO_IN, 100,
+               ebd_receive_data, (void *)sdi);
+
+       return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       ebd_loadstop(sdi->conn, sdi->priv);
+
+       return std_serial_dev_acquisition_stop(sdi);
+}
+
+SR_PRIV struct sr_dev_driver zketech_ebd_usb_driver_info = {
+       .name = "zketech-ebd-usb",
+       .longname = "ZKETECH EBD-USB",
+       .api_version = 1,
+       .init = std_init,
+       .cleanup = std_cleanup,
+       .scan = scan,
+       .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = std_serial_dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(zketech_ebd_usb_driver_info);
diff --git a/src/hardware/zketech-ebd-usb/protocol.c b/src/hardware/zketech-ebd-usb/protocol.c
new file mode 100644 (file)
index 0000000..0c99ea5
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Sven Bursch-Osewold <sb_git@bursch.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 <config.h>
+#include "protocol.h"
+
+/* Log a byte-array as hex values. */
+static void log_buf(const char *message, uint8_t buf[], size_t count)
+{
+       char buffer[count * 2 + 1];
+
+       for (size_t j = 0; j < count; j++)
+               sprintf(&buffer[2 * j], "%02X", buf[j]);
+
+       buffer[count * 2] = 0;
+
+       sr_dbg("%s: %s [%zu bytes]", message, buffer, count);
+}
+
+/* Send a command to the device. */
+static int send_cmd(struct sr_serial_dev_inst *serial, uint8_t buf[], size_t count)
+{
+       int ret;
+
+       log_buf("Sending", buf, count);
+       ret = serial_write_blocking(serial, buf, count, 0);
+       if (ret < 0) {
+               sr_err("Error sending command: %d.", ret);
+               return ret;
+       }
+
+       return (ret == (int)count) ? SR_OK : SR_ERR;
+}
+
+/* Decode high byte and low byte into a float. */
+static float decode_value(uint8_t hi, uint8_t lo, float divisor)
+{
+       return ((float)hi * 240.0 + (float)lo) / divisor;
+}
+
+/* Encode a float into high byte and low byte. */
+static void encode_value(float current, uint8_t *hi, uint8_t *lo, float divisor)
+{
+       int value;
+
+       value = (int)(current * divisor);
+       sr_dbg("Value %d %d %d", value, value / 240, value % 240);
+       *hi = value / 240;
+       *lo = value % 240;
+}
+
+/* Send updated configuration values to the load. */
+static int send_cfg(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+       uint8_t send[] = { 0xfa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8 };
+
+       encode_value(devc->current_limit, &send[2], &send[3], 1000.0);
+
+       send[8] = send[1] ^ send[2] ^ send[3] ^ send[4] ^ send[5] ^ \
+                       send[6] ^ send[7];
+
+       return send_cmd(serial, send, 10);
+}
+
+/* Send the init/connect sequence; drive starts sending voltage and current. */
+SR_PRIV int ebd_init(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+       uint8_t init[] = { 0xfa, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf8 };
+
+       (void)devc;
+
+       int ret = send_cmd(serial, init, 10);
+       if (ret == SR_OK)
+               devc->running = TRUE;
+
+       return ret;
+}
+
+/* Start the load functionality. */
+SR_PRIV int ebd_loadstart(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+       uint8_t start[] = { 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8 };
+       int ret;
+
+       ret = send_cmd(serial, start, 10);
+       sr_dbg("Current limit: %f.", devc->current_limit);
+       if (ebd_current_is0(devc))
+               return SR_OK;
+
+       ret = send_cfg(serial, devc);
+       if (ret == SR_OK) {
+               sr_dbg("Load activated.");
+               devc->load_activated = TRUE;
+       }
+
+       return ret;
+}
+
+/* Stop the load functionality. */
+SR_PRIV int ebd_loadstop(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+       int ret;
+       uint8_t stop[] = { 0xfa, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xF8 };
+
+       ret = send_cmd(serial, stop, 10);
+       if (ret == SR_OK)
+               devc->load_activated = FALSE;
+
+       return ret;
+}
+
+/* Stop the drive. */
+SR_PRIV int ebd_stop(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+       uint8_t stop[] = { 0xfa, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xF8 };
+       int ret;
+
+       (void) devc;
+
+       ret = send_cmd(serial, stop, 10);
+       if (ret == SR_OK) {
+               devc->load_activated = FALSE;
+               devc->running= FALSE;
+       }
+
+       return ret;
+}
+
+/** Read count bytes from the serial connection. */
+SR_PRIV int ebd_read_chars(struct sr_serial_dev_inst *serial, int count, uint8_t *buf)
+{
+       int ret, received, turns;
+
+       received = 0;
+       turns = 0;
+
+       do {
+               ret = serial_read_blocking(serial, buf + received,
+                       count - received, serial_timeout(serial, count));
+               if (ret < 0) {
+                       sr_err("Error %d reading %d bytes.", ret, count);
+                       return ret;
+               }
+               received += ret;
+               turns++;
+       } while ((received < count) && (turns < 100));
+
+       log_buf("Received", buf, received);
+
+       return received;
+}
+
+SR_PRIV int ebd_receive_data(int fd, int revents, void *cb_data)
+{
+       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;
+       struct sr_analog_encoding encoding;
+       struct sr_analog_meaning meaning;
+       struct sr_analog_spec spec;
+       float voltage, current, current_limit;
+       GSList *l;
+
+       (void)revents;
+       (void)fd;
+
+       if (!(sdi = cb_data))
+               return FALSE;
+
+       if (!(devc = sdi->priv))
+               return FALSE;
+
+       serial = sdi->conn;
+
+       uint8_t reply[MSG_LEN];
+       int ret = ebd_read_chars(serial, MSG_LEN, reply);
+
+       /* Tests for correct message. */
+       if (ret != MSG_LEN) {
+               sr_err("Message invalid [Len].");
+               return (ret < 0) ? ret : SR_ERR;
+       }
+
+       uint8_t xor = reply[1] ^ reply[2] ^ reply[3] ^ reply[4] ^ \
+                     reply[5] ^ reply[6] ^ reply[7] ^ reply[8] ^ \
+                     reply[9] ^ reply[10] ^ reply[11] ^ reply[12] ^ \
+                     reply[13] ^ reply[14] ^ reply[15] ^ reply[16];
+
+       if (reply[MSG_FRAME_BEGIN_POS] != MSG_FRAME_BEGIN || \
+                       reply[MSG_FRAME_END_POS] != MSG_FRAME_END || \
+                       xor != reply[MSG_CHECKSUM_POS]) {
+               sr_err("Message invalid [XOR, BEGIN/END].");
+               return SR_ERR;
+       }
+
+       /* Calculate values. */
+       sr_dbg("V: %02X %02X A: %02X %02X -- Limit %02X %02X", reply[4],
+               reply[5], reply[2], reply[3], reply[10], reply[11]);
+
+       voltage = decode_value(reply[4], reply[5], 1000.0);
+       current = decode_value(reply[2], reply[3], 10000.0);
+       current_limit = decode_value(reply[10], reply[11], 1000.0);
+
+       sr_dbg("Voltage %f", voltage);
+       sr_dbg("Current %f", current);
+       sr_dbg("Current limit %f", current_limit);
+
+       /* Begin frame. */
+       packet.type = SR_DF_FRAME_BEGIN;
+       packet.payload = NULL;
+       sr_session_send(sdi, &packet);
+
+       sr_analog_init(&analog, &encoding, &meaning, &spec, 4);
+
+       packet.type = SR_DF_ANALOG;
+       packet.payload = &analog;
+       analog.num_samples = 1;
+
+       /* Voltage */
+       l = g_slist_copy(sdi->channels);
+       l = g_slist_remove_link(l, g_slist_nth(l, 1));
+       meaning.channels = l;
+       meaning.mq = SR_MQ_VOLTAGE;
+       meaning.mqflags = SR_MQFLAG_DC;
+       meaning.unit = SR_UNIT_VOLT;
+       analog.data = &voltage;
+       sr_session_send(sdi, &packet);
+       g_slist_free(l);
+
+       /* Current */
+       l = g_slist_copy(sdi->channels);
+       l = g_slist_remove_link(l, g_slist_nth(l, 0));
+       meaning.channels = l;
+       meaning.mq = SR_MQ_CURRENT;
+       meaning.mqflags = SR_MQFLAG_DC;
+       meaning.unit = SR_UNIT_AMPERE;
+       analog.data = &current;
+       sr_session_send(sdi, &packet);
+       g_slist_free(l);
+
+       /* End frame. */
+       packet.type = SR_DF_FRAME_END;
+       packet.payload = NULL;
+       sr_session_send(sdi, &packet);
+
+       sr_sw_limits_update_samples_read(&devc->limits, 1);
+       if (sr_sw_limits_check(&devc->limits))
+               sr_dev_acquisition_stop(sdi);
+
+       return TRUE;
+}
+
+SR_PRIV int ebd_get_current_limit(const struct sr_dev_inst *sdi, float *current)
+{
+       struct dev_context *devc;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+       g_mutex_lock(&devc->rw_mutex);
+       *current = devc->current_limit;
+       g_mutex_unlock(&devc->rw_mutex);
+
+       return SR_OK;
+}
+
+SR_PRIV int ebd_set_current_limit(const struct sr_dev_inst *sdi, float current)
+{
+       struct dev_context *devc;
+       int ret;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+       g_mutex_lock(&devc->rw_mutex);
+       devc->current_limit = current;
+
+       if (!devc->running) {
+               sr_dbg("Setting current limit later.");
+               g_mutex_unlock(&devc->rw_mutex);
+               return SR_OK;
+       }
+
+       sr_dbg("Setting current limit to %fV.", current);
+
+       if (devc->load_activated) {
+               if (ebd_current_is0(devc)) {
+                       /* Stop load. */
+                       ret = ebd_loadstop(sdi->conn, devc);
+               } else {
+                       /* Send new current. */
+                       ret = send_cfg(sdi->conn, devc);
+               }
+       } else {
+               if (ebd_current_is0(devc)) {
+                       /* Nothing to do. */
+                       ret = SR_OK;
+               } else {
+                       /* Start load. */
+                       ret = ebd_loadstart(sdi->conn, devc);
+               }
+       }
+
+       g_mutex_unlock(&devc->rw_mutex);
+
+       return ret;
+}
+
+SR_PRIV gboolean ebd_current_is0(struct dev_context *devc)
+{
+       return devc->current_limit < 0.001;
+}
diff --git a/src/hardware/zketech-ebd-usb/protocol.h b/src/hardware/zketech-ebd-usb/protocol.h
new file mode 100644 (file)
index 0000000..004ce5f
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Sven Bursch-Osewold <sb_git@bursch.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_ZKETECH_EBD_USB_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_ZKETECH_EBD_USB_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "zketech-ebd-usb"
+
+#define MSG_LEN 19
+#define MSG_CHECKSUM_POS 17
+#define MSG_FRAME_BEGIN 0xfa
+#define MSG_FRAME_BEGIN_POS 0
+#define MSG_FRAME_END 0xf8
+#define MSG_FRAME_END_POS 18
+
+struct dev_context {
+       struct sr_sw_limits limits;
+       GMutex rw_mutex;
+       float current_limit;
+       gboolean running;
+       gboolean load_activated;
+};
+
+/* Communication via serial. */
+SR_PRIV int ebd_read_chars(struct sr_serial_dev_inst *serial, int count, uint8_t *buf);
+
+/* Commands. */
+SR_PRIV int ebd_init(struct sr_serial_dev_inst *serial, struct dev_context *devc);
+SR_PRIV int ebd_loadstart(struct sr_serial_dev_inst *serial, struct dev_context *devc);
+SR_PRIV int ebd_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int ebd_stop(struct sr_serial_dev_inst *serial, struct dev_context *devc);
+SR_PRIV int ebd_loadstop(struct sr_serial_dev_inst *serial, struct dev_context *devc);
+
+/* Configuration. */
+SR_PRIV int ebd_get_current_limit(const struct sr_dev_inst *sdi, float *current);
+SR_PRIV int ebd_set_current_limit(const struct sr_dev_inst *sdi, float current);
+SR_PRIV gboolean ebd_current_is0(struct dev_context *devc);
+
+#endif
index deff5a7b2a9b703203ccf011256647d68e25f8ea..5f7e2371475c634910f81253f998dfddf67bcbcc 100644 (file)
@@ -62,6 +62,7 @@ static struct sr_key_info sr_key_info_config[] = {
        {SR_CONF_ELECTRONIC_LOAD, SR_T_STRING, NULL, "Electronic load", NULL},
        {SR_CONF_SCALE, SR_T_STRING, NULL, "Scale", NULL},
        {SR_CONF_SIGNAL_GENERATOR, SR_T_STRING, NULL, "Signal generator", NULL},
+       {SR_CONF_POWERMETER, SR_T_STRING, NULL, "Power meter", NULL},
 
        /* Driver scan options */
        {SR_CONF_CONN, SR_T_STRING, "conn",
@@ -176,8 +177,12 @@ static struct sr_key_info sr_key_info_config[] = {
                "Under-voltage condition", NULL},
        {SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE, SR_T_BOOL, "uvc_active",
                "Under-voltage condition active", NULL},
+       {SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD, SR_T_FLOAT, "uvc_threshold",
+               "Under-voltage condition threshold", NULL},
        {SR_CONF_TRIGGER_LEVEL, SR_T_FLOAT, "triggerlevel",
                "Trigger level", NULL},
+       {SR_CONF_EXTERNAL_CLOCK_SOURCE, SR_T_STRING, "external_clock_source",
+               "External clock source", NULL},
 
        /* Special stuff */
        {SR_CONF_SESSIONFILE, SR_T_STRING, "sessionfile",
@@ -394,6 +399,8 @@ SR_API int sr_driver_init(struct sr_context *ctx, struct sr_dev_driver *driver)
                return SR_ERR_ARG;
        }
 
+       /* No log message here, too verbose and not very useful. */
+
        if ((ret = driver->init(driver, ctx)) < 0)
                sr_err("Failed to initialize the driver: %d.", ret);
 
@@ -527,8 +534,7 @@ SR_API GSList *sr_driver_scan(struct sr_dev_driver *driver, GSList *options)
 
        l = driver->scan(driver, options);
 
-       sr_spew("Scan of '%s' found %d devices.", driver->name,
-               g_slist_length(l));
+       sr_spew("Scan found %d devices (%s).", g_slist_length(l), driver->name);
 
        return l;
 }
@@ -548,6 +554,8 @@ SR_PRIV void sr_hw_cleanup_all(const struct sr_context *ctx)
        if (!ctx)
                return;
 
+       sr_dbg("Cleaning up all drivers.");
+
        drivers = sr_driver_list(ctx);
        for (i = 0; drivers[i]; i++) {
                if (drivers[i]->cleanup)
@@ -581,7 +589,6 @@ SR_PRIV struct sr_config *sr_config_new(uint32_t key, GVariant *data)
  */
 SR_PRIV void sr_config_free(struct sr_config *src)
 {
-
        if (!src || !src->data) {
                sr_err("%s: invalid data!", __func__);
                return;
@@ -589,11 +596,49 @@ SR_PRIV void sr_config_free(struct sr_config *src)
 
        g_variant_unref(src->data);
        g_free(src);
+}
+
+/** @private */
+SR_PRIV int sr_dev_acquisition_start(struct sr_dev_inst *sdi)
+{
+       if (!sdi || !sdi->driver) {
+               sr_err("%s: Invalid arguments.", __func__);
+               return SR_ERR_ARG;
+       }
+
+       if (sdi->status != SR_ST_ACTIVE) {
+               sr_err("%s: Device instance not active, can't start.",
+                       sdi->driver->name);
+               return SR_ERR_DEV_CLOSED;
+       }
+
+       sr_dbg("%s: Starting acquisition.", sdi->driver->name);
+
+       return sdi->driver->dev_acquisition_start(sdi);
+}
 
+/** @private */
+SR_PRIV int sr_dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       if (!sdi || !sdi->driver) {
+               sr_err("%s: Invalid arguments.", __func__);
+               return SR_ERR_ARG;
+       }
+
+       if (sdi->status != SR_ST_ACTIVE) {
+               sr_err("%s: Device instance not active, can't stop.",
+                       sdi->driver->name);
+               return SR_ERR_DEV_CLOSED;
+       }
+
+       sr_dbg("%s: Stopping acquisition.", sdi->driver->name);
+
+       return sdi->driver->dev_acquisition_stop(sdi);
 }
 
 static void log_key(const struct sr_dev_inst *sdi,
-       const struct sr_channel_group *cg, uint32_t key, int op, GVariant *data)
+       const struct sr_channel_group *cg, uint32_t key, unsigned int op,
+       GVariant *data)
 {
        const char *opstr;
        const struct sr_key_info *srci;
@@ -615,7 +660,7 @@ static void log_key(const struct sr_dev_inst *sdi,
 
 static int check_key(const struct sr_dev_driver *driver,
                const struct sr_dev_inst *sdi, const struct sr_channel_group *cg,
-               uint32_t key, int op, GVariant *data)
+               uint32_t key, unsigned int op, GVariant *data)
 {
        const struct sr_key_info *srci;
        gsize num_opts, i;
@@ -626,9 +671,9 @@ static int check_key(const struct sr_dev_driver *driver,
        const char *opstr;
 
        if (sdi && cg)
-               suffix = " for this device and channel group";
+               suffix = " for this device instance and channel group";
        else if (sdi)
-               suffix = " for this device";
+               suffix = " for this device instance";
        else
                suffix = "";
 
@@ -650,6 +695,15 @@ static int check_key(const struct sr_dev_driver *driver,
                        return SR_ERR_ARG;
                }
                break;
+       case SR_CONF_CAPTURE_RATIO:
+               /* Capture ratio must always be between 0 and 100. */
+               if (op != SR_CONF_SET || !data)
+                       break;
+               if (g_variant_get_uint64(data) > 100) {
+                       sr_err("Capture ratio must be 0..100.");
+                       return SR_ERR_ARG;
+               }
+               break;
        }
 
        if (sr_config_list(driver, sdi, cg, SR_CONF_DEVICE_OPTIONS, &gvar_opts) != SR_OK) {
@@ -732,6 +786,10 @@ SR_API int sr_config_get(const struct sr_dev_driver *driver,
                g_variant_ref_sink(*data);
        }
 
+       if (ret == SR_ERR_CHANNEL_GROUP)
+               sr_err("%s: No channel group specified.",
+                       (sdi) ? sdi->driver->name : "unknown");
+
        return ret;
 }
 
@@ -767,7 +825,11 @@ SR_API int sr_config_set(const struct sr_dev_inst *sdi,
                ret = SR_ERR;
        else if (!sdi->driver->config_set)
                ret = SR_ERR_ARG;
-       else if (check_key(sdi->driver, sdi, cg, key, SR_CONF_SET, data) != SR_OK)
+       else if (sdi->status != SR_ST_ACTIVE) {
+               sr_err("%s: Device instance not active, can't set config.",
+                       sdi->driver->name);
+               ret = SR_ERR_DEV_CLOSED;
+       } else if (check_key(sdi->driver, sdi, cg, key, SR_CONF_SET, data) != SR_OK)
                return SR_ERR_ARG;
        else if ((ret = sr_variant_type_check(key, data)) == SR_OK) {
                log_key(sdi, cg, key, SR_CONF_SET, data);
@@ -776,6 +838,10 @@ SR_API int sr_config_set(const struct sr_dev_inst *sdi,
 
        g_variant_unref(data);
 
+       if (ret == SR_ERR_CHANNEL_GROUP)
+               sr_err("%s: No channel group specified.",
+                       (sdi) ? sdi->driver->name : "unknown");
+
        return ret;
 }
 
@@ -796,7 +862,11 @@ SR_API int sr_config_commit(const struct sr_dev_inst *sdi)
                ret = SR_ERR;
        else if (!sdi->driver->config_commit)
                ret = SR_OK;
-       else
+       else if (sdi->status != SR_ST_ACTIVE) {
+               sr_err("%s: Device instance not active, can't commit config.",
+                       sdi->driver->name);
+               ret = SR_ERR_DEV_CLOSED;
+       } else
                ret = sdi->driver->config_commit(sdi);
 
        return ret;
@@ -806,18 +876,22 @@ SR_API int sr_config_commit(const struct sr_dev_inst *sdi)
  * List all possible values for a configuration key.
  *
  * @param[in] driver The sr_dev_driver struct to query. Must not be NULL.
- * @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] sdi (optional) If the key is specific to a device instance, this
+ *            must contain a pointer to the struct sr_dev_inst to be checked.
  *            Otherwise it must be NULL. If sdi is != NULL, sdi->priv must
  *            also be != NULL.
- * @param[in] cg The channel group on the device for which to list the
- *                    values, or NULL.
+ * @param[in] cg The channel group on the device instance for which to list
+ *            the values, or NULL. If this device instance doesn't
+ *            have channel groups, this must not be != NULL.
+ *            If cg is NULL, this function will return the "common" device
+ *            instance options that are channel-group independent. Otherwise
+ *            it will return the channel-group specific options.
  * @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.
+ *                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.
@@ -836,21 +910,50 @@ SR_API int sr_config_list(const struct sr_dev_driver *driver,
 
        if (!driver || !data)
                return SR_ERR;
-       else if (!driver->config_list)
+
+       if (!driver->config_list)
                return SR_ERR_ARG;
-       else if (key != SR_CONF_SCAN_OPTIONS && key != SR_CONF_DEVICE_OPTIONS) {
+
+       if (key != SR_CONF_SCAN_OPTIONS && key != SR_CONF_DEVICE_OPTIONS) {
                if (check_key(driver, sdi, cg, key, SR_CONF_LIST, NULL) != SR_OK)
                        return SR_ERR_ARG;
        }
+
        if (sdi && !sdi->priv) {
                sr_err("Can't list config (sdi != NULL, sdi->priv == NULL).");
                return SR_ERR;
        }
+
+       if (key != SR_CONF_SCAN_OPTIONS && key != SR_CONF_DEVICE_OPTIONS && !sdi) {
+               sr_err("Config keys other than SR_CONF_SCAN_OPTIONS and "
+                      "SR_CONF_DEVICE_OPTIONS always need an sdi.");
+               return SR_ERR_ARG;
+       }
+
+       if (cg && sdi && !sdi->channel_groups) {
+               sr_err("Can't list config for channel group, there are none.");
+               return SR_ERR_ARG;
+       }
+
+       if (cg && sdi && !g_slist_find(sdi->channel_groups, cg)) {
+               sr_err("If a channel group is specified, it must be a valid one.");
+               return SR_ERR_ARG;
+       }
+
+       if (cg && !sdi) {
+               sr_err("Need sdi when a channel group is specified.");
+               return SR_ERR_ARG;
+       }
+
        if ((ret = driver->config_list(key, data, sdi, cg)) == SR_OK) {
                log_key(sdi, cg, key, SR_CONF_LIST, *data);
                g_variant_ref_sink(*data);
        }
 
+       if (ret == SR_ERR_CHANNEL_GROUP)
+               sr_err("%s: No channel group specified.",
+                       (sdi) ? sdi->driver->name : "unknown");
+
        return ret;
 }
 
index 91e4d3c434e45cdcfa65b3597e2f16c8476af9cc..da664a85e6f140d14de0c8796a6adc76e3034e8c 100644 (file)
 
 #define LOG_PREFIX "input/binary"
 
-#define MAX_CHUNK_SIZE       4096
+#define CHUNK_SIZE           (4 * 1024 * 1024)
 #define DEFAULT_NUM_CHANNELS 8
 #define DEFAULT_SAMPLERATE   0
 
 struct context {
        gboolean started;
        uint64_t samplerate;
+       uint16_t unitsize;
 };
 
 static int init(struct sr_input *in, GHashTable *options)
@@ -56,10 +57,12 @@ static int init(struct sr_input *in, GHashTable *options)
        inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
 
        for (i = 0; i < num_channels; i++) {
-               snprintf(name, 16, "%d", i);
+               snprintf(name, sizeof(name), "%d", i);
                sr_channel_new(in->sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
        }
 
+       inc->unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+
        return SR_OK;
 }
 
@@ -92,14 +95,14 @@ static int process_buffer(struct sr_input *in)
 
        packet.type = SR_DF_LOGIC;
        packet.payload = &logic;
-       logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+       logic.unitsize = inc->unitsize;
 
        /* Cut off at multiple of unitsize. */
        chunk_size = in->buf->len / logic.unitsize * logic.unitsize;
 
        for (i = 0; i < chunk_size; i += chunk) {
                logic.data = in->buf->str + i;
-               chunk = MIN(MAX_CHUNK_SIZE, chunk_size - i);
+               chunk = MIN(CHUNK_SIZE, chunk_size - i);
                logic.length = chunk;
                sr_session_send(in->sdi, &packet);
        }
@@ -153,8 +156,8 @@ static int reset(struct sr_input *in)
 }
 
 static struct sr_option options[] = {
-       { "numchannels", "Number of channels", "Number of channels", NULL, NULL },
-       { "samplerate", "Sample rate", "Sample rate", NULL, NULL },
+       { "numchannels", "Number of logic channels", "The number of (logic) channels in the data", NULL, NULL },
+       { "samplerate", "Sample rate (Hz)", "The sample rate of the (logic) data in Hz", NULL, NULL },
        ALL_ZERO
 };
 
@@ -171,7 +174,7 @@ static const struct sr_option *get_options(void)
 SR_PRIV struct sr_input_module input_binary = {
        .id = "binary",
        .name = "Binary",
-       .desc = "Raw binary",
+       .desc = "Raw binary logic data",
        .exts = NULL,
        .options = get_options,
        .init = init,
index 6c5707330934bb833305e0a4195241cb04aae505..e7b7f082015ebe3c99d9dca209ac74c3fd9b7a40 100644 (file)
 
 #define DEFAULT_NUM_CHANNELS    8
 #define DEFAULT_SAMPLERATE      SR_MHZ(100)
-#define MAX_CHUNK_SIZE          (4 * 1024)
-#define CHRONOVU_LA8_FILESIZE   ((8 * 1024 * 1024) + 5)
+#define CHUNK_SIZE              (4 * 1024 * 1024)
+
+/*
+ * File layout:
+ * - Fixed size 8MiB data part at offset 0.
+ *   - Either one byte per sample for LA8.
+ *   - Or two bytes per sample for LA16, in little endian format.
+ * - Five byte "header" at offset 8MiB.
+ *   - One "clock divider" byte. The byte value is the divider factor
+ *     minus 1. Value 0xff is invalid. Base clock is 100MHz for LA8, or
+ *     200MHz for LA16.
+ *   - Four bytes for the trigger position. This 32bit value is the
+ *     sample number in little endian format, or 0 when unused.
+ */
+#define CHRONOVU_LA8_DATASIZE   (8 * 1024 * 1024)
+#define CHRONOVU_LA8_HDRSIZE    (sizeof(uint8_t) + sizeof(uint32_t))
+#define CHRONOVU_LA8_FILESIZE   (CHRONOVU_LA8_DATASIZE + CHRONOVU_LA8_HDRSIZE)
+
+/*
+ * Implementation note:
+ *
+ * The .format_match() routine only checks the file size, but none of
+ * the header fields. Only little would be gained (only clock divider
+ * 0xff could get tested), but complexity would increase dramatically.
+ * Also the .format_match() routine is unlikely to receive large enough
+ * a buffer to include the header. Neither is the filename available to
+ * the .format_match() routine.
+ *
+ * There is no way to programmatically tell whether the file was created
+ * by LA8 or LA16 software, i.e. with 8 or 16 logic channels. If the
+ * filename was available, one might guess based on the file extension,
+ * but still would require user specs if neither of the known extensions
+ * were used or the input is fed from a pipe.
+ *
+ * The current input module implementation assumes that users specify
+ * the (channel count and) sample rate. Input data gets processed and
+ * passed along to the session bus, before the file "header" is seen.
+ * A future implementation could move channel creation from init() to
+ * receive() or end() (actually: a common routine called from those two
+ * routines), and could defer sample processing and feeding the session
+ * until the header was seen, including deferred samplerate calculation
+ * after having seen the header. But again this improvement depends on
+ * the availability of either the filename or the device type. Also note
+ * that applications then had to keep sending data to the input module's
+ * receive() routine until sufficient amounts of input data were seen
+ * including the header (see bug #1017).
+ */
 
 struct context {
        gboolean started;
        uint64_t samplerate;
+       uint64_t samples_remain;
 };
 
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
 {
-       int size;
-
-       size = GPOINTER_TO_INT(g_hash_table_lookup(metadata,
+       uint64_t size;
+
+       /*
+        * In the absence of a reliable condition like magic strings,
+        * we can only guess based on the file size. Since this is
+        * rather weak a condition, signal "little confidence" and
+        * optionally give precedence to better matches.
+        */
+       size = GPOINTER_TO_SIZE(g_hash_table_lookup(metadata,
                        GINT_TO_POINTER(SR_INPUT_META_FILESIZE)));
-       if (size == CHRONOVU_LA8_FILESIZE)
-               return SR_OK;
+       if (size != CHRONOVU_LA8_FILESIZE)
+               return SR_ERR;
+       *confidence = 100;
 
-       return SR_ERR;
+       return SR_OK;
 }
 
 static int init(struct sr_input *in, GHashTable *options)
@@ -67,7 +120,7 @@ static int init(struct sr_input *in, GHashTable *options)
        inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
 
        for (i = 0; i < num_channels; i++) {
-               snprintf(name, 16, "%d", i);
+               snprintf(name, sizeof(name), "%d", i);
                sr_channel_new(in->sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
        }
 
@@ -82,9 +135,12 @@ static int process_buffer(struct sr_input *in)
        struct sr_config *src;
        struct context *inc;
        gsize chunk_size, i;
-       int chunk;
+       gsize chunk;
+       uint16_t unitsize;
 
        inc = in->priv;
+       unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+
        if (!inc->started) {
                std_session_send_df_header(in->sdi);
 
@@ -98,21 +154,28 @@ static int process_buffer(struct sr_input *in)
                        sr_config_free(src);
                }
 
+               inc->samples_remain = CHRONOVU_LA8_DATASIZE;
+               inc->samples_remain /= unitsize;
+
                inc->started = TRUE;
        }
 
        packet.type = SR_DF_LOGIC;
        packet.payload = &logic;
-       logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+       logic.unitsize = unitsize;
 
-       /* Cut off at multiple of unitsize. */
+       /* Cut off at multiple of unitsize. Avoid sending the "header". */
        chunk_size = in->buf->len / logic.unitsize * logic.unitsize;
+       chunk_size = MIN(chunk_size, inc->samples_remain * unitsize);
 
        for (i = 0; i < chunk_size; i += chunk) {
                logic.data = in->buf->str + i;
-               chunk = MIN(MAX_CHUNK_SIZE, chunk_size - i);
-               logic.length = chunk;
-               sr_session_send(in->sdi, &packet);
+               chunk = MIN(CHUNK_SIZE, chunk_size - i);
+               if (chunk) {
+                       logic.length = chunk;
+                       sr_session_send(in->sdi, &packet);
+                       inc->samples_remain -= chunk / unitsize;
+               }
        }
        g_string_erase(in->buf, 0, chunk_size);
 
@@ -164,8 +227,8 @@ static int reset(struct sr_input *in)
 }
 
 static struct sr_option options[] = {
-       { "numchannels", "Number of channels", "Number of channels", NULL, NULL },
-       { "samplerate", "Sample rate", "Sample rate", NULL, NULL },
+       { "numchannels", "Number of logic channels", "The number of (logic) channels in the data", NULL, NULL },
+       { "samplerate", "Sample rate (Hz)", "The sample rate of the (logic) data in Hz", NULL, NULL },
        ALL_ZERO
 };
 
@@ -181,9 +244,9 @@ static const struct sr_option *get_options(void)
 
 SR_PRIV struct sr_input_module input_chronovu_la8 = {
        .id = "chronovu-la8",
-       .name = "Chronovu-LA8",
-       .desc = "ChronoVu LA8",
-       .exts = (const char*[]){"kdt", NULL},
+       .name = "ChronoVu LA8/LA16",
+       .desc = "ChronoVu LA8/LA16 native file format data",
+       .exts = (const char*[]){"kdt", "kd1", NULL},
        .metadata = { SR_INPUT_META_FILESIZE | SR_INPUT_META_REQUIRED },
        .options = get_options,
        .format_match = format_match,
index 000599a8d305bf9140fa78679aefb1b5cad72174..fdfc26419ae7e44e910549dafc4f8067cc985d40 100644 (file)
@@ -26,7 +26,7 @@
 
 #define LOG_PREFIX "input/csv"
 
-#define DATAFEED_MAX_SAMPLES   (128 * 1024)
+#define CHUNK_SIZE     (4 * 1024 * 1024)
 
 /*
  * The CSV input module has the following options:
@@ -624,7 +624,7 @@ static int initial_parse(const struct sr_input *in, GString *buf)
         * to a location within that large buffer.
         */
        inc->sample_unit_size = (inc->num_channels + 7) / 8;
-       inc->datafeed_buf_size = DATAFEED_MAX_SAMPLES;
+       inc->datafeed_buf_size = CHUNK_SIZE;
        inc->datafeed_buf_size *= inc->sample_unit_size;
        inc->datafeed_buffer = g_malloc(inc->datafeed_buf_size);
        inc->datafeed_buf_fill = 0;
@@ -911,25 +911,32 @@ static int reset(struct sr_input *in)
 }
 
 static struct sr_option options[] = {
-       { "single-column", "Single column", "Enable/specify single column", NULL, NULL },
-       { "numchannels", "Max channels", "Number of channels", NULL, NULL },
-       { "delimiter", "Delimiter", "Column delimiter", NULL, NULL },
-       { "format", "Format", "Numeric format", NULL, NULL },
-       { "comment", "Comment", "Comment prefix character", NULL, NULL },
-       { "samplerate", "Samplerate", "Samplerate used during capture", NULL, NULL },
-       { "first-channel", "First channel", "Column number of first channel", NULL, NULL },
-       { "header", "Header", "Treat first line as header with channel names", NULL, NULL },
-       { "startline", "Start line", "Line number at which to start processing samples", NULL, NULL },
+       { "single-column", "Single column", "Enable single-column mode, using the specified column (>= 1); 0: multi-col. mode", NULL, NULL },
+       { "numchannels", "Number of logic channels", "The number of (logic) channels (single-col. mode: number of bits beginning at 'first channel', LSB-first)", NULL, NULL },
+       { "delimiter", "Column delimiter", "The column delimiter (>= 1 characters)", NULL, NULL },
+       { "format", "Data format (single-col. mode)", "The numeric format of the data (single-col. mode): bin, hex, oct", NULL, NULL },
+       { "comment", "Comment character(s)", "The comment prefix character(s)", NULL, NULL },
+       { "samplerate", "Samplerate (Hz)", "The sample rate (used during capture) in Hz", NULL, NULL },
+       { "first-channel", "First channel", "The column number of the first channel (multi-col. mode); bit position for the first channel (single-col. mode)", NULL, NULL },
+       { "header", "Interpret first line as header (multi-col. mode)", "Treat the first line as header with channel names (multi-col. mode)", NULL, NULL },
+       { "startline", "Start line", "The line number at which to start processing samples (>= 1)", NULL, NULL },
        ALL_ZERO
 };
 
 static const struct sr_option *get_options(void)
 {
+       GSList *l;
+
        if (!options[0].def) {
                options[0].def = g_variant_ref_sink(g_variant_new_int32(0));
                options[1].def = g_variant_ref_sink(g_variant_new_int32(0));
                options[2].def = g_variant_ref_sink(g_variant_new_string(","));
                options[3].def = g_variant_ref_sink(g_variant_new_string("bin"));
+               l = NULL;
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("bin")));
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("hex")));
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("oct")));
+               options[3].values = l;
                options[4].def = g_variant_ref_sink(g_variant_new_string(";"));
                options[5].def = g_variant_ref_sink(g_variant_new_uint64(0));
                options[6].def = g_variant_ref_sink(g_variant_new_int32(0));
index 3db29ffefdee1669795a5ebd854be4ca6eb36d74..282084cab008332593cab865a1e5ad987138a075 100644 (file)
@@ -29,6 +29,8 @@
 #define LOG_PREFIX "input"
 /** @endcond */
 
+#define CHUNK_SIZE     (4 * 1024 * 1024)
+
 /**
  * @file
  *
@@ -66,6 +68,8 @@ extern SR_PRIV struct sr_input_module input_trace32_ad;
 extern SR_PRIV struct sr_input_module input_vcd;
 extern SR_PRIV struct sr_input_module input_wav;
 extern SR_PRIV struct sr_input_module input_raw_analog;
+extern SR_PRIV struct sr_input_module input_logicport;
+extern SR_PRIV struct sr_input_module input_null;
 /* @endcond */
 
 static const struct sr_input_module *input_module_list[] = {
@@ -76,6 +80,8 @@ static const struct sr_input_module *input_module_list[] = {
        &input_vcd,
        &input_wav,
        &input_raw_analog,
+       &input_logicport,
+       &input_null,
        NULL,
 };
 
@@ -334,11 +340,15 @@ static gboolean check_required_metadata(const uint8_t *metadata, uint8_t *avail)
  * Try to find an input module that can parse the given buffer.
  *
  * The buffer must contain enough of the beginning of the file for
- * the input modules to find a match. This is format-dependent, but
- * 128 bytes is normally enough.
+ * the input modules to find a match. This is format-dependent. When
+ * magic strings get checked, 128 bytes normally could be enough. Note
+ * that some formats try to parse larger header sections, and benefit
+ * from seeing a larger scope.
  *
  * If an input module is found, an instance is created into *in.
- * Otherwise, *in contains NULL.
+ * Otherwise, *in contains NULL. When multiple input moduless claim
+ * support for the format, the one with highest confidence takes
+ * precedence. Applications will see at most one input module spec.
  *
  * If an instance is created, it has the given buffer used for scanning
  * already submitted to it, to be processed before more data is sent.
@@ -349,9 +359,10 @@ static gboolean check_required_metadata(const uint8_t *metadata, uint8_t *avail)
  */
 SR_API int sr_input_scan_buffer(GString *buf, const struct sr_input **in)
 {
-       const struct sr_input_module *imod;
+       const struct sr_input_module *imod, *best_imod;
        GHashTable *meta;
        unsigned int m, i;
+       unsigned int conf, best_conf;
        int ret;
        uint8_t mitem, avail_metadata[8];
 
@@ -360,7 +371,8 @@ SR_API int sr_input_scan_buffer(GString *buf, const struct sr_input **in)
        avail_metadata[1] = 0;
 
        *in = NULL;
-       ret = SR_ERR;
+       best_imod = NULL;
+       best_conf = ~0;
        for (i = 0; input_module_list[i]; i++) {
                imod = input_module_list[i];
                if (!imod->metadata[0]) {
@@ -384,45 +396,55 @@ SR_API int sr_input_scan_buffer(GString *buf, const struct sr_input **in)
                        continue;
                }
                sr_spew("Trying module %s.", imod->id);
-               ret = imod->format_match(meta);
+               ret = imod->format_match(meta, &conf);
                g_hash_table_destroy(meta);
                if (ret == SR_ERR_DATA) {
                        /* Module recognized this buffer, but cannot handle it. */
-                       break;
+                       continue;
                } else if (ret == SR_ERR) {
                        /* Module didn't recognize this buffer. */
                        continue;
                } else if (ret != SR_OK) {
                        /* Can be SR_ERR_NA. */
-                       return ret;
+                       continue;
                }
 
                /* Found a matching module. */
-               sr_spew("Module %s matched.", imod->id);
-               *in = sr_input_new(imod, NULL);
+               sr_spew("Module %s matched, confidence %u.", imod->id, conf);
+               if (conf >= best_conf)
+                       continue;
+               best_imod = imod;
+               best_conf = conf;
+       }
+
+       if (best_imod) {
+               *in = sr_input_new(best_imod, NULL);
                g_string_insert_len((*in)->buf, 0, buf->str, buf->len);
-               break;
+               return SR_OK;
        }
 
-       return ret;
+       return SR_ERR;
 }
 
 /**
  * Try to find an input module that can parse the given file.
  *
  * If an input module is found, an instance is created into *in.
- * Otherwise, *in contains NULL.
+ * Otherwise, *in contains NULL. When multiple input moduless claim
+ * support for the format, the one with highest confidence takes
+ * precedence. Applications will see at most one input module spec.
  *
  */
 SR_API int sr_input_scan_file(const char *filename, const struct sr_input **in)
 {
        int64_t filesize;
        FILE *stream;
-       const struct sr_input_module *imod;
+       const struct sr_input_module *imod, *best_imod;
        GHashTable *meta;
        GString *header;
        size_t count;
        unsigned int midx, i;
+       unsigned int conf, best_conf;
        int ret;
        uint8_t avail_metadata[8];
 
@@ -444,11 +466,9 @@ SR_API int sr_input_scan_file(const char *filename, const struct sr_input **in)
                fclose(stream);
                return SR_ERR;
        }
-       /* This actually allocates 256 bytes to allow for NUL termination. */
-       header = g_string_sized_new(255);
+       header = g_string_sized_new(CHUNK_SIZE);
        count = fread(header->str, 1, header->allocated_len - 1, stream);
-
-       if (count != header->allocated_len - 1 && ferror(stream)) {
+       if (count < 1 || ferror(stream)) {
                sr_err("Failed to read %s: %s", filename, g_strerror(errno));
                fclose(stream);
                g_string_free(header, TRUE);
@@ -471,8 +491,8 @@ SR_API int sr_input_scan_file(const char *filename, const struct sr_input **in)
        avail_metadata[midx] = 0;
        /* TODO: MIME type */
 
-       ret = SR_ERR;
-
+       best_imod = NULL;
+       best_conf = ~0;
        for (i = 0; input_module_list[i]; i++) {
                imod = input_module_list[i];
                if (!imod->metadata[0]) {
@@ -486,24 +506,46 @@ SR_API int sr_input_scan_file(const char *filename, const struct sr_input **in)
 
                sr_dbg("Trying module %s.", imod->id);
 
-               ret = imod->format_match(meta);
+               ret = imod->format_match(meta, &conf);
                if (ret == SR_ERR) {
                        /* Module didn't recognize this buffer. */
                        continue;
                } else if (ret != SR_OK) {
                        /* Module recognized this buffer, but cannot handle it. */
-                       break;
+                       continue;
                }
                /* Found a matching module. */
-               sr_dbg("Module %s matched.", imod->id);
-
-               *in = sr_input_new(imod, NULL);
-               break;
+               sr_dbg("Module %s matched, confidence %u.", imod->id, conf);
+               if (conf >= best_conf)
+                       continue;
+               best_imod = imod;
+               best_conf = conf;
        }
        g_hash_table_destroy(meta);
        g_string_free(header, TRUE);
 
-       return ret;
+       if (best_imod) {
+               *in = sr_input_new(best_imod, NULL);
+               return SR_OK;
+       }
+
+       return SR_ERR;
+}
+
+/**
+ * Return the input instance's module "class". This can be used to find out
+ * which input module handles a specific input file. This is especially
+ * useful when an application did not create the input stream by specifying
+ * an input module, but instead some shortcut or convenience wrapper did.
+ *
+ * @since 0.6.0
+ */
+SR_API const struct sr_input_module *sr_input_module_get(const struct sr_input *in)
+{
+       if (!in)
+               return NULL;
+
+       return in->module;
 }
 
 /**
@@ -540,8 +582,10 @@ SR_API struct sr_dev_inst *sr_input_dev_inst_get(const struct sr_input *in)
  */
 SR_API int sr_input_send(const struct sr_input *in, GString *buf)
 {
-       sr_spew("Sending %" G_GSIZE_FORMAT " bytes to %s module.",
-               buf->len, in->module->id);
+       size_t len;
+
+       len = buf ? buf->len : 0;
+       sr_spew("Sending %zu bytes to %s module.", len, in->module->id);
        return in->module->receive((struct sr_input *)in, buf);
 }
 
@@ -568,16 +612,49 @@ SR_API int sr_input_end(const struct sr_input *in)
  *
  * @since 0.5.0
  */
-SR_API int sr_input_reset(const struct sr_input *in)
+SR_API int sr_input_reset(const struct sr_input *in_ro)
 {
-       if (!in->module->reset) {
+       struct sr_input *in;
+       int rc;
+
+       in = (struct sr_input *)in_ro;  /* "un-const" */
+       if (!in || !in->module)
+               return SR_ERR_ARG;
+
+       /*
+        * Run the optional input module's .reset() method. This shall
+        * take care of the context (kept in the 'inc' variable).
+        */
+       if (in->module->reset) {
+               sr_spew("Resetting %s module.", in->module->id);
+               rc = in->module->reset(in);
+       } else {
                sr_spew("Tried to reset %s module but no reset handler found.",
                        in->module->id);
-               return SR_OK;
+               rc = SR_OK;
        }
 
-       sr_spew("Resetting %s module.", in->module->id);
-       return in->module->reset((struct sr_input *)in);
+       /*
+        * Handle input module status (kept in the 'in' variable) here
+        * in common logic. This agrees with how input module's receive()
+        * and end() routines "amend but never seed" the 'in' information.
+        *
+        * Void potentially accumulated receive() buffer content, and
+        * clear the sdi_ready flag. This makes sure that subsequent
+        * processing will scan the header again before sample data gets
+        * interpreted, and stale content from previous calls won't affect
+        * the result.
+        *
+        * This common logic does not harm when the input module implements
+        * .reset() and contains identical assignments. In the absence of
+        * an individual .reset() method, simple input modules can completely
+        * rely on common code and keep working across resets.
+        */
+       if (in->buf)
+               g_string_truncate(in->buf, 0);
+       in->sdi_ready = FALSE;
+
+       return rc;
 }
 
 /**
@@ -590,8 +667,19 @@ SR_API void sr_input_free(const struct sr_input *in)
        if (!in)
                return;
 
+       /*
+        * Run the input module's optional .cleanup() routine. This
+        * takes care of the context (kept in the 'inc' variable).
+        */
        if (in->module->cleanup)
                in->module->cleanup((struct sr_input *)in);
+
+       /*
+        * Common code releases the input module's state (kept in the
+        * 'in' variable). Release the device instance, the receive()
+        * buffer, the shallow 'in->priv' block which is 'inc' (after
+        * .cleanup() released potentially nested resources under 'inc').
+        */
        sr_dev_inst_free(in->sdi);
        if (in->buf->len > 64) {
                /* That seems more than just some sub-unitsize leftover... */
diff --git a/src/input/logicport.c b/src/input/logicport.c
new file mode 100644 (file)
index 0000000..e594b2c
--- /dev/null
@@ -0,0 +1,1209 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/>.
+ */
+
+/*
+ * See the LA1034 vendor's http://www.pctestinstruments.com/ website.
+ *
+ * The hardware comes with (Windows only) software which uses the .lpf
+ * ("LogicPort File") filename extension for project files, which hold
+ * both the configuration as well as sample data (up to 2K samples). In
+ * the absence of an attached logic analyzer, the software provides a
+ * demo mode which generates random input signals. The software installs
+ * example project files (with samples), too.
+ *
+ * The file format is "mostly text", is line oriented, though it uses
+ * funny DC1 separator characters as well as line continuation by means
+ * of a combination of DC1 and slashes. Fortunately the last text line
+ * is terminated by means of CRLF.
+ *
+ * The software is rather complex and has features which don't easily
+ * translate to sigrok semantics (like one signal being a member of
+ * multiple groups, display format specs for groups' values).
+ *
+ * This input module implementation supports the following features:
+ * - input format auto detection
+ * - sample period to sample rate conversion
+ * - wire names, acquisition filters ("enabled") and inversion flags
+ * - decompression (repetition counters for sample data)
+ * - strict '0' and '1' levels (as well as ignoring 'U' values)
+ * - signal names (user assigned names, "aliases" for "wires")
+ * - signal groups (no support for multiple assignments, no support for
+ *   display format specs)
+ * - "logic" channels (mere bits, no support for analog channels, also
+ *   nothing analog "gets derived from" any signal groups) -- libsigrok
+ *   using applications might provide such a feature if they want to
+ */
+
+#include <config.h>
+#include <ctype.h>
+#include <glib.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/* TODO: Move these helpers to some library API routine group. */
+struct sr_channel_group *sr_channel_group_new(const char *name, void *priv);
+void sr_channel_group_free(struct sr_channel_group *cg);
+
+#define LOG_PREFIX     "input/logicport"
+
+#define MAX_CHANNELS   34
+#define CHUNK_SIZE     (4 * 1024 * 1024)
+
+#define CRLF           "\r\n"
+#define DC1_CHR                '\x11'
+#define DC1_STR                "\x11"
+#define CONT_OPEN      "/" DC1_STR
+#define CONT_CLOSE     DC1_STR "/"
+
+/*
+ * This is some heuristics (read: a HACK). The current implementation
+ * neither processes nor displays the user's notes, but takes their
+ * presence as a hint that all relevant input was seen, and sample data
+ * can get forwarded to the session bus.
+ */
+#define LAST_KEYWORD   "NotesString"
+
+/*
+ * The vendor software supports signal groups, and a single signal can
+ * be a member in multiple groups at the same time. The sigrok project
+ * does not support that configuration. Let's ignore the "All Signals"
+ * group by default, thus reducing the probability of a conflict.
+ */
+#define SKIP_SIGNAL_GROUP      "All Signals"
+
+struct signal_group_desc {
+       char *name;
+       uint64_t mask;
+};
+
+struct context {
+       gboolean got_header;
+       gboolean ch_feed_prep;
+       gboolean header_sent;
+       gboolean rate_sent;
+       char *sw_version;
+       size_t sw_build;
+       GString *cont_buff;
+       size_t channel_count;
+       size_t sample_lines_total;
+       size_t sample_lines_read;
+       size_t sample_lines_fed;
+       uint64_t samples_got_uncomp;
+       enum {
+               SAMPLEDATA_NONE,
+               SAMPLEDATA_OPEN_BRACE,
+               SAMPLEDATA_WIRES_COUNT,
+               SAMPLEDATA_DATA_LINES,
+               SAMPLEDATA_CLOSE_BRACE,
+       } in_sample_data;
+       struct sample_data_entry {
+               uint64_t bits;
+               size_t repeat;
+       } *sample_data_queue;
+       uint64_t sample_rate;
+       uint64_t wires_all_mask;
+       uint64_t wires_enabled;
+       uint64_t wires_inverted;
+       uint64_t wires_undefined;
+       char *wire_names[MAX_CHANNELS];
+       char *signal_names[MAX_CHANNELS];
+       uint64_t wires_grouped;
+       GSList *signal_groups;
+       GSList *channels;
+       size_t unitsize;
+       size_t samples_per_chunk;
+       size_t samples_in_buffer;
+       uint8_t *feed_buffer;
+};
+
+static struct signal_group_desc *alloc_signal_group(const char *name)
+{
+       struct signal_group_desc *desc;
+
+       desc = g_malloc0(sizeof(*desc));
+       if (name)
+               desc->name = g_strdup(name);
+
+       return desc;
+}
+
+static void free_signal_group(struct signal_group_desc *desc)
+{
+       if (!desc)
+               return;
+       g_free(desc->name);
+       g_free(desc);
+}
+
+struct sr_channel_group *sr_channel_group_new(const char *name, void *priv)
+{
+       struct sr_channel_group *cg;
+
+       cg = g_malloc0(sizeof(*cg));
+       if (name && *name)
+               cg->name = g_strdup(name);
+       cg->priv = priv;
+
+       return cg;
+}
+
+void sr_channel_group_free(struct sr_channel_group *cg)
+{
+       if (!cg)
+               return;
+       g_free(cg->name);
+       g_slist_free(cg->channels);
+}
+
+/* Wrapper for GDestroyNotify compatibility. */
+static void sg_free(void *p)
+{
+       return free_signal_group(p);
+}
+
+static int check_vers_line(char *line, int need_key,
+       gchar **version, gchar **build)
+{
+       static const char *keyword = "Version";
+       static const char *caution = " CAUTION: Do not change the contents of this file.";
+       char *read_ptr;
+       const char *prev_ptr;
+
+       read_ptr = line;
+       if (version)
+               *version = NULL;
+       if (build)
+               *build = NULL;
+
+       /* Expect the 'Version' literal, followed by a DC1 separator. */
+       if (need_key) {
+               if (strncmp(read_ptr, keyword, strlen(keyword)) != 0)
+                       return SR_ERR_DATA;
+               read_ptr += strlen(keyword);
+               if (*read_ptr != DC1_CHR)
+                       return SR_ERR_DATA;
+               read_ptr++;
+       }
+
+       /* Expect some "\d+\.\d+" style version string and DC1. */
+       if (!*read_ptr)
+               return SR_ERR_DATA;
+       if (version)
+               *version = read_ptr;
+       prev_ptr = read_ptr;
+       read_ptr += strspn(read_ptr, "0123456789.");
+       if (read_ptr == prev_ptr)
+               return SR_ERR_DATA;
+       if (*read_ptr != DC1_CHR)
+               return SR_ERR_DATA;
+       *read_ptr++ = '\0';
+
+       /* Expect some "\d+" style build number and DC1. */
+       if (!*read_ptr)
+               return SR_ERR_DATA;
+       if (build)
+               *build = read_ptr;
+       prev_ptr = read_ptr;
+       read_ptr += strspn(read_ptr, "0123456789");
+       if (read_ptr == prev_ptr)
+               return SR_ERR_DATA;
+       if (*read_ptr != DC1_CHR)
+               return SR_ERR_DATA;
+       *read_ptr++ = '\0';
+
+       /* Expect the 'CAUTION...' text (weak test, only part of the text). */
+       if (strncmp(read_ptr, caution, strlen(caution)) != 0)
+               return SR_ERR_DATA;
+       read_ptr += strlen(caution);
+
+       /* No check for CRLF, due to the weak CAUTION test. */
+       return SR_OK;
+}
+
+static int process_wire_names(struct context *inc, char **names)
+{
+       size_t count, idx;
+
+       /*
+        * The 'names' array contains the *wire* names, plus a 'Count'
+        * label for the last column.
+        */
+       count = g_strv_length(names);
+       if (count != inc->channel_count + 1)
+               return SR_ERR_DATA;
+       if (strcmp(names[inc->channel_count], "Count") != 0)
+               return SR_ERR_DATA;
+
+       for (idx = 0; idx < inc->channel_count; idx++)
+               inc->wire_names[idx] = g_strdup(names[idx]);
+
+       return SR_OK;
+}
+
+static int process_signal_names(struct context *inc, char **names)
+{
+       size_t count, idx;
+
+       /*
+        * The 'names' array contains the *signal* names (and no other
+        * entries, unlike the *wire* names).
+        */
+       count = g_strv_length(names);
+       if (count != inc->channel_count)
+               return SR_ERR_DATA;
+
+       for (idx = 0; idx < inc->channel_count; idx++)
+               inc->signal_names[idx] = g_strdup(names[idx]);
+
+       return SR_OK;
+}
+
+static int process_signal_group(struct context *inc, char **args)
+{
+       char *name, *wires;
+       struct signal_group_desc *desc;
+       uint64_t bit_mask;
+       char *p, *endp;
+       size_t idx;
+
+       /*
+        * List of arguments that we receive:
+        * - [0] group name
+        * - [1] - [5] uncertain meaning, four integers and one boolean
+        * - [6] comma separated list of wire indices (zero based)
+        * - [7] - [9] uncertain meaning, a boolean, two integers
+        * - [10] - [35] uncertain meaning, 26 empty columns
+        */
+
+       /* Check for the minimum amount of input data. */
+       if (!args)
+               return SR_ERR_DATA;
+       if (g_strv_length(args) < 7)
+               return SR_ERR_DATA;
+       name = args[0];
+       wires = args[6];
+
+       /* Accept empty names and empty signal lists. Silently ignore. */
+       if (!name || !*name)
+               return SR_OK;
+       if (!wires || !*wires)
+               return SR_OK;
+       /*
+        * TODO: Introduce a user configurable "ignore" option? Skip the
+        * "All Signals" group by default, and in addition whatever
+        * the user specified?
+        */
+       if (strcmp(name, SKIP_SIGNAL_GROUP) == 0) {
+               sr_info("Skipping signal group '%s'", name);
+               return SR_OK;
+       }
+
+       /*
+        * Create the descriptor here to store the member list to. We
+        * cannot access signal names and sigrok channels yet, they
+        * only become avilable at a later point in time.
+        */
+       desc = alloc_signal_group(name);
+       if (!desc)
+               return SR_ERR_MALLOC;
+       inc->signal_groups = g_slist_append(inc->signal_groups, desc);
+
+       /* Determine the bit mask of the group's signals' indices. */
+       bit_mask = 0;
+       p = wires;
+       while (p && *p) {
+               endp = NULL;
+               idx = strtoul(p, &endp, 0);
+               if (!endp || endp == p)
+                       return SR_ERR_DATA;
+               if (*endp && *endp != ',')
+                       return SR_ERR_DATA;
+               p = endp;
+               if (*p == ',')
+                       p++;
+               if (idx >= MAX_CHANNELS)
+                       return SR_ERR_DATA;
+               bit_mask = UINT64_C(1) << idx;
+               if (inc->wires_grouped & bit_mask) {
+                       sr_warn("Not adding signal at index %zu to group %s (multiple assignments)",
+                               idx, name);
+               } else {
+                       desc->mask |= bit_mask;
+                       inc->wires_grouped |= bit_mask;
+               }
+       }
+       sr_dbg("'Group' done, name '%s', mask 0x%" PRIx64 ".",
+               desc->name, desc->mask);
+
+       return SR_OK;
+}
+
+static int process_ungrouped_signals(struct context *inc)
+{
+       uint64_t bit_mask;
+       struct signal_group_desc *desc;
+
+       /*
+        * Only create the "ungrouped" channel group if there are any
+        * groups of other signals already.
+        */
+       if (!inc->signal_groups)
+               return SR_OK;
+
+       /*
+        * Determine the bit mask of signals that are part of the
+        * acquisition and are not a member of any other group.
+        */
+       bit_mask = inc->wires_all_mask;
+       bit_mask &= inc->wires_enabled;
+       bit_mask &= ~inc->wires_grouped;
+       sr_dbg("'ungrouped' check: all 0x%" PRIx64 ", en 0x%" PRIx64 ", grp 0x%" PRIx64 " -> un 0x%" PRIx64 ".",
+               inc->wires_all_mask, inc->wires_enabled,
+               inc->wires_grouped, bit_mask);
+       if (!bit_mask)
+               return SR_OK;
+
+       /* Create a sigrok channel group without a name. */
+       desc = alloc_signal_group(NULL);
+       if (!desc)
+               return SR_ERR_MALLOC;
+       inc->signal_groups = g_slist_append(inc->signal_groups, desc);
+       desc->mask = bit_mask;
+
+       return SR_OK;
+}
+
+static int process_enabled_channels(struct context *inc, char **flags)
+{
+       size_t count, idx;
+       uint64_t bits, mask;
+
+       /*
+        * The 'flags' array contains (the textual representation of)
+        * the "enabled" state of the acquisition device's channels.
+        */
+       count = g_strv_length(flags);
+       if (count != inc->channel_count)
+               return SR_ERR_DATA;
+       bits = 0;
+       mask = UINT64_C(1);
+       for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+               if (strcmp(flags[idx], "True") == 0)
+                       bits |= mask;
+       }
+       inc->wires_enabled = bits;
+
+       return SR_OK;
+}
+
+static int process_inverted_channels(struct context *inc, char **flags)
+{
+       size_t count, idx;
+       uint64_t bits, mask;
+
+       /*
+        * The 'flags' array contains (the textual representation of)
+        * the "inverted" state of the acquisition device's channels.
+        */
+       count = g_strv_length(flags);
+       if (count != inc->channel_count)
+               return SR_ERR_DATA;
+       bits = 0;
+       mask = UINT64_C(1);
+       for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+               if (strcmp(flags[idx], "True") == 0)
+                       bits |= mask;
+       }
+       inc->wires_inverted = bits;
+
+       return SR_OK;
+}
+
+static int process_sample_line(struct context *inc, char **values)
+{
+       size_t count, idx;
+       struct sample_data_entry *entry;
+       uint64_t mask;
+       long conv_ret;
+       int rc;
+
+       /*
+        * The 'values' array contains '0'/'1' text representation of
+        * wire's values, as well as a (a textual representation of a)
+        * repeat counter for that set of samples.
+        */
+       count = g_strv_length(values);
+       if (count != inc->channel_count + 1)
+               return SR_ERR_DATA;
+       entry = &inc->sample_data_queue[inc->sample_lines_read];
+       entry->bits = 0;
+       mask = UINT64_C(1);
+       for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+               if (strcmp(values[idx], "1") == 0)
+                       entry->bits |= mask;
+               if (strcmp(values[idx], "U") == 0)
+                       inc->wires_undefined |= mask;
+       }
+       rc = sr_atol(values[inc->channel_count], &conv_ret);
+       if (rc != SR_OK)
+               return rc;
+       entry->repeat = conv_ret;
+       inc->samples_got_uncomp += entry->repeat;
+
+       return SR_OK;
+}
+
+static int process_keyvalue_line(struct context *inc, char *line)
+{
+       char *sep, *key, *arg;
+       char **args;
+       int rc;
+       char *version, *build;
+       long build_num;
+       int wires, samples;
+       size_t alloc_size;
+       double period, dbl_rate;
+       uint64_t int_rate;
+
+       /*
+        * Process lines of the 'SampleData' block. Inspection of the
+        * block got started below in the "regular keyword line" section.
+        * The code here handles the remaining number of lines: Opening
+        * and closing braces, wire names, and sample data sets. Note
+        * that the wire names and sample values are separated by comma,
+        * not by DC1 like other key/value pairs and argument lists.
+        */
+       switch (inc->in_sample_data) {
+       case SAMPLEDATA_OPEN_BRACE:
+               if (strcmp(line, "{") != 0)
+                       return SR_ERR_DATA;
+               inc->in_sample_data++;
+               return SR_OK;
+       case SAMPLEDATA_WIRES_COUNT:
+               while (isspace(*line))
+                       line++;
+               args = g_strsplit(line, ",", 0);
+               rc = process_wire_names(inc, args);
+               g_strfreev(args);
+               if (rc)
+                       return rc;
+               inc->in_sample_data++;
+               inc->sample_lines_read = 0;
+               return SR_OK;
+       case SAMPLEDATA_DATA_LINES:
+               while (isspace(*line))
+                       line++;
+               args = g_strsplit(line, ",", 0);
+               rc = process_sample_line(inc, args);
+               g_strfreev(args);
+               if (rc)
+                       return rc;
+               inc->sample_lines_read++;
+               if (inc->sample_lines_read == inc->sample_lines_total)
+                       inc->in_sample_data++;
+               return SR_OK;
+       case SAMPLEDATA_CLOSE_BRACE:
+               if (strcmp(line, "}") != 0)
+                       return SR_ERR_DATA;
+               sr_dbg("'SampleData' done: samples count %" PRIu64 ".",
+                       inc->samples_got_uncomp);
+               inc->sample_lines_fed = 0;
+               inc->in_sample_data = SAMPLEDATA_NONE;
+               return SR_OK;
+       case SAMPLEDATA_NONE:
+               /* EMPTY */ /* Fall through to regular keyword-line logic. */
+               break;
+       }
+
+       /* Process regular key/value lines separated by DC1. */
+       key = line;
+       sep = strchr(line, DC1_CHR);
+       if (!sep)
+               return SR_ERR_DATA;
+       *sep++ = '\0';
+       arg = sep;
+       if (strcmp(key, "Version") == 0) {
+               rc = check_vers_line(arg, 0, &version, &build);
+               if (rc == SR_OK) {
+                       inc->sw_version = g_strdup(version ? version : "?");
+                       rc = sr_atol(build, &build_num);
+                       inc->sw_build = build_num;
+               }
+               sr_dbg("'Version' line: version %s, build %zu.",
+                       inc->sw_version, inc->sw_build);
+               return rc;
+       }
+       if (strcmp(key, "AcquiredSamplePeriod") == 0) {
+               rc = sr_atod(arg, &period);
+               if (rc != SR_OK)
+                       return rc;
+               /*
+                * Implementation detail: The vendor's software provides
+                * 1/2/5 choices in the 1kHz - 500MHz range. Unfortunately
+                * the choice of saving the sample _period_ as a floating
+                * point number in the text file yields inaccurate results
+                * for naive implementations of the conversion (0.1 is an
+                * "odd number" in the computer's internal representation).
+                * The below logic of rounding to integer and then rounding
+                * to full kHz works for the samplerate value's range.
+                * "Simplifying" the implementation will introduce errors.
+                */
+               dbl_rate = 1.0 / period;
+               int_rate = (uint64_t)(dbl_rate + 0.5);
+               int_rate += 500;
+               int_rate /= 1000;
+               int_rate *= 1000;
+               inc->sample_rate = int_rate;
+               if (!inc->sample_rate)
+                       return SR_ERR_DATA;
+               sr_dbg("Sample rate: %" PRIu64 ".", inc->sample_rate);
+               return SR_OK;
+       }
+       if (strcmp(key, "AcquiredChannelList") == 0) {
+               args = g_strsplit(arg, DC1_STR, 0);
+               rc = process_enabled_channels(inc, args);
+               g_strfreev(args);
+               if (rc)
+                       return rc;
+               sr_dbg("Enabled channels: 0x%" PRIx64 ".",
+                       inc->wires_enabled);
+               return SR_OK;
+       }
+       if (strcmp(key, "InvertedChannelList") == 0) {
+               args = g_strsplit(arg, DC1_STR, 0);
+               rc = process_inverted_channels(inc, args);
+               g_strfreev(args);
+               sr_dbg("Inverted channels: 0x%" PRIx64 ".",
+                       inc->wires_inverted);
+               return SR_OK;
+       }
+       if (strcmp(key, "Signals") == 0) {
+               args = g_strsplit(arg, DC1_STR, 0);
+               rc = process_signal_names(inc, args);
+               g_strfreev(args);
+               if (rc)
+                       return rc;
+               sr_dbg("Got signal names.");
+               return SR_OK;
+       }
+       if (strcmp(key, "SampleData") == 0) {
+               args = g_strsplit(arg, DC1_STR, 3);
+               if (!args || !args[0] || !args[1]) {
+                       g_strfreev(args);
+                       return SR_ERR_DATA;
+               }
+               rc = sr_atoi(args[0], &wires);
+               if (rc) {
+                       g_strfreev(args);
+                       return SR_ERR_DATA;
+               }
+               rc = sr_atoi(args[1], &samples);
+               if (rc) {
+                       g_strfreev(args);
+                       return SR_ERR_DATA;
+               }
+               g_strfreev(args);
+               if (!wires || !samples)
+                       return SR_ERR_DATA;
+               inc->channel_count = wires;
+               inc->sample_lines_total = samples;
+               sr_dbg("'SampleData' start: wires %zu, sample lines %zu.",
+                       inc->channel_count, inc->sample_lines_total);
+               if (inc->channel_count > MAX_CHANNELS)
+                       return SR_ERR_DATA;
+               inc->in_sample_data = SAMPLEDATA_OPEN_BRACE;
+               alloc_size = sizeof(inc->sample_data_queue[0]);
+               alloc_size *= inc->sample_lines_total;
+               inc->sample_data_queue = g_malloc0(alloc_size);
+               if (!inc->sample_data_queue)
+                       return SR_ERR_DATA;
+               inc->sample_lines_fed = 0;
+               return SR_OK;
+       }
+       if (strcmp(key, "Group") == 0) {
+               args = g_strsplit(arg, DC1_STR, 0);
+               rc = process_signal_group(inc, args);
+               g_strfreev(args);
+               if (rc)
+                       return rc;
+               return SR_OK;
+       }
+       if (strcmp(key, LAST_KEYWORD) == 0) {
+               sr_dbg("'" LAST_KEYWORD "' seen, assuming \"header done\".");
+               inc->got_header = TRUE;
+               return SR_OK;
+       }
+
+       /* Unsupported keyword, silently ignore the line. */
+       return SR_OK;
+}
+
+/* Check for, and isolate another line of text input. */
+static int have_text_line(struct sr_input *in, char **line, char **next)
+{
+       char *sol_ptr, *eol_ptr;
+
+       if (!in || !in->buf || !in->buf->str)
+               return 0;
+       sol_ptr = in->buf->str;
+       eol_ptr = strstr(sol_ptr, CRLF);
+       if (!eol_ptr)
+               return 0;
+       if (line)
+               *line = sol_ptr;
+       *eol_ptr = '\0';
+       eol_ptr += strlen(CRLF);
+       if (next)
+               *next = eol_ptr;
+
+       return 1;
+}
+
+/* Handle line continuation. Have logical lines processed. */
+static int process_text_line(struct context *inc, char *line)
+{
+       char *p;
+       int is_cont_end;
+       int rc;
+
+       /*
+        * Handle line continuation in the input stream. Notice that
+        * continued lines can start and end on the same input line.
+        * The text between the markers can be empty, too.
+        *
+        * Make the result look like a regular line. Put a DC1 delimiter
+        * between the keyword and the right hand side. Strip the /<DC1>
+        * and <DC1>/ "braces". Put CRLF between all continued parts,
+        * this makes the data appear "most intuitive and natural"
+        * should we e.g. pass on user's notes in a future version.
+        */
+       is_cont_end = 0;
+       if (!inc->cont_buff) {
+               p = strstr(line, CONT_OPEN);
+               if (p) {
+                       /* Start of continuation. */
+                       inc->cont_buff = g_string_new_len(line, p - line + 1);
+                       inc->cont_buff->str[inc->cont_buff->len - 1] = DC1_CHR;
+                       line = p + strlen(CONT_OPEN);
+               }
+               /* Regular line, fall through to below regular logic. */
+       }
+       if (inc->cont_buff) {
+               p = strstr(line, CONT_CLOSE);
+               is_cont_end = p != NULL;
+               if (is_cont_end)
+                       *p = '\0';
+               g_string_append_len(inc->cont_buff, line, strlen(line));
+               if (!is_cont_end) {
+                       /* Keep accumulating. */
+                       g_string_append_len(inc->cont_buff, CRLF, strlen(CRLF));
+                       return SR_OK;
+               }
+               /* End of continuation. */
+               line = inc->cont_buff->str;
+       }
+
+       /*
+        * Process a logical line of input. It either was received from
+        * the caller, or is the result of accumulating continued lines.
+        */
+       rc = process_keyvalue_line(inc, line);
+
+       /* Release the accumulation buffer when a continuation ended. */
+       if (is_cont_end) {
+               g_string_free(inc->cont_buff, TRUE);
+               inc->cont_buff = NULL;
+       }
+
+       return rc;
+}
+
+/* Tell whether received data is sufficient for session feed preparation. */
+static int have_header(GString *buf)
+{
+       const char *assumed_last_key = CRLF LAST_KEYWORD CONT_OPEN;
+
+       if (strstr(buf->str, assumed_last_key))
+               return TRUE;
+
+       return FALSE;
+}
+
+/* Process/inspect previously received input data. Get header parameters. */
+static int parse_header(struct sr_input *in)
+{
+       struct context *inc;
+       char *line, *next;
+       int rc;
+
+       inc = in->priv;
+       while (have_text_line(in, &line, &next)) {
+               rc = process_text_line(inc, line);
+               g_string_erase(in->buf, 0, next - line);
+               if (rc)
+                       return rc;
+       }
+
+       return SR_OK;
+}
+
+/* Create sigrok channels and groups. */
+static int create_channels_groups(struct sr_input *in)
+{
+       struct context *inc;
+       uint64_t mask;
+       size_t idx;
+       const char *name;
+       gboolean enabled;
+       struct sr_channel *ch;
+       struct sr_dev_inst *sdi;
+       GSList *l;
+       struct signal_group_desc *desc;
+       struct sr_channel_group *cg;
+
+       inc = in->priv;
+
+       if (inc->channels)
+               return SR_OK;
+
+       mask = UINT64_C(1);
+       for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+               name = inc->signal_names[idx];
+               if (!name || !*name)
+                       name = inc->wire_names[idx];
+               enabled = (inc->wires_enabled & mask) ? TRUE : FALSE;
+               ch = sr_channel_new(in->sdi, idx,
+                       SR_CHANNEL_LOGIC, enabled, name);
+               if (!ch)
+                       return SR_ERR_MALLOC;
+               inc->channels = g_slist_append(inc->channels, ch);
+       }
+
+       sdi = in->sdi;
+       for (l = inc->signal_groups; l; l = l->next) {
+               desc = l->data;
+               cg = sr_channel_group_new(desc->name, NULL);
+               if (!cg)
+                       return SR_ERR_MALLOC;
+               sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+               mask = UINT64_C(1);
+               for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+                       if (!(desc->mask & mask))
+                               continue;
+                       ch = g_slist_nth_data(inc->channels, idx);
+                       if (!ch)
+                               return SR_ERR_DATA;
+                       cg->channels = g_slist_append(cg->channels, ch);
+               }
+       }
+
+       return SR_OK;
+}
+
+/* Allocate the session feed buffer. */
+static int create_feed_buffer(struct sr_input *in)
+{
+       struct context *inc;
+
+       inc = in->priv;
+
+       inc->unitsize = (inc->channel_count + 7) / 8;
+       inc->samples_per_chunk = CHUNK_SIZE / inc->unitsize;
+       inc->samples_in_buffer = 0;
+       inc->feed_buffer = g_malloc0(inc->samples_per_chunk * inc->unitsize);
+       if (!inc->feed_buffer)
+               return SR_ERR_MALLOC;
+
+       return SR_OK;
+}
+
+/* Send all accumulated sample data values to the session. */
+static int send_buffer(struct sr_input *in)
+{
+       struct context *inc;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_meta meta;
+       struct sr_config *src;
+       struct sr_datafeed_logic logic;
+       int rc;
+
+       inc = in->priv;
+       if (!inc->samples_in_buffer)
+               return SR_OK;
+
+       if (!inc->header_sent) {
+               rc = std_session_send_df_header(in->sdi);
+               if (rc)
+                       return rc;
+               inc->header_sent = TRUE;
+       }
+
+       if (inc->sample_rate && !inc->rate_sent) {
+               packet.type = SR_DF_META;
+               packet.payload = &meta;
+               src = sr_config_new(SR_CONF_SAMPLERATE,
+                       g_variant_new_uint64(inc->sample_rate));
+               meta.config = g_slist_append(NULL, src);
+               rc = sr_session_send(in->sdi, &packet);
+               g_slist_free(meta.config);
+               sr_config_free(src);
+               if (rc)
+                       return rc;
+               inc->rate_sent = TRUE;
+       }
+
+       packet.type = SR_DF_LOGIC;
+       packet.payload = &logic;
+       logic.unitsize = inc->unitsize;
+       logic.data = inc->feed_buffer;
+       logic.length = inc->unitsize * inc->samples_in_buffer;
+       rc = sr_session_send(in->sdi, &packet);
+
+       inc->samples_in_buffer = 0;
+
+       if (rc)
+               return rc;
+
+       return SR_OK;
+}
+
+/*
+ * Add N copies of the current sample to the buffer. Send the buffer to
+ * the session feed when a maximum amount of data was collected.
+ */
+static int add_samples(struct sr_input *in, uint64_t samples, size_t count)
+{
+       struct context *inc;
+       uint8_t sample_buffer[sizeof(uint64_t)];
+       size_t idx;
+       size_t copy_count;
+       uint8_t *p;
+       int rc;
+
+       inc = in->priv;
+       for (idx = 0; idx < inc->unitsize; idx++) {
+               sample_buffer[idx] = samples & 0xff;
+               samples >>= 8;
+       }
+       while (count) {
+               copy_count = inc->samples_per_chunk - inc->samples_in_buffer;
+               if (copy_count > count)
+                       copy_count = count;
+               count -= copy_count;
+
+               p = inc->feed_buffer + inc->samples_in_buffer * inc->unitsize;
+               while (copy_count-- > 0) {
+                       memcpy(p, sample_buffer, inc->unitsize);
+                       p += inc->unitsize;
+                       inc->samples_in_buffer++;
+               }
+
+               if (inc->samples_in_buffer == inc->samples_per_chunk) {
+                       rc = send_buffer(in);
+                       if (rc)
+                               return rc;
+               }
+       }
+
+       return SR_OK;
+}
+
+/* Pass on previously received samples to the session. */
+static int process_queued_samples(struct sr_input *in)
+{
+       struct context *inc;
+       struct sample_data_entry *entry;
+       uint64_t sample_bits;
+       int rc;
+
+       inc = in->priv;
+       while (inc->sample_lines_fed < inc->sample_lines_total) {
+               entry = &inc->sample_data_queue[inc->sample_lines_fed++];
+               sample_bits = entry->bits;
+               sample_bits ^= inc->wires_inverted;
+               sample_bits &= inc->wires_enabled;
+               rc = add_samples(in, sample_bits, entry->repeat);
+               if (rc)
+                       return rc;
+       }
+
+       return SR_OK;
+}
+
+/*
+ * Create required resources between having read the input file and
+ * sending sample data to the session. Send initial packets before
+ * sample data follows.
+ */
+static int prepare_session_feed(struct sr_input *in)
+{
+       struct context *inc;
+       int rc;
+
+       inc = in->priv;
+       if (inc->ch_feed_prep)
+               return SR_OK;
+
+       /* Got channel names? At least fallbacks? */
+       if (!inc->wire_names[0] || !inc->wire_names[0][0])
+               return SR_ERR_DATA;
+       /* Samples seen? Seen them all? */
+       if (!inc->channel_count)
+               return SR_ERR_DATA;
+       if (!inc->sample_lines_total)
+               return SR_ERR_DATA;
+       if (inc->in_sample_data)
+               return SR_ERR_DATA;
+       if (!inc->sample_data_queue)
+               return SR_ERR_DATA;
+       inc->sample_lines_fed = 0;
+
+       /*
+        * Normalize some variants of input data.
+        * - Let's create a mask for the maximum possible
+        *   bit positions, it will be useful to avoid garbage
+        *   in other code paths, too.
+        * - Input files _might_ specify which channels were
+        *   enabled during acquisition. _Or_ not specify the
+        *   enabled channels, but provide 'U' values in some
+        *   columns. When neither was seen, assume that all
+        *   channels are enabled.
+        * - If there are any signal groups, put all signals into
+        *   an anonymous group that are not part of another group.
+        */
+       inc->wires_all_mask = UINT64_C(1);
+       inc->wires_all_mask <<= inc->channel_count;
+       inc->wires_all_mask--;
+       sr_dbg("all wires mask: 0x%" PRIx64 ".", inc->wires_all_mask);
+       if (!inc->wires_enabled) {
+               inc->wires_enabled = ~inc->wires_undefined;
+               inc->wires_enabled &= ~inc->wires_all_mask;
+               sr_dbg("enabled from undefined: 0x%" PRIx64 ".",
+                       inc->wires_enabled);
+       }
+       if (!inc->wires_enabled) {
+               inc->wires_enabled = inc->wires_all_mask;
+               sr_dbg("enabled from total mask: 0x%" PRIx64 ".",
+                       inc->wires_enabled);
+       }
+       sr_dbg("enabled mask: 0x%" PRIx64 ".",
+               inc->wires_enabled);
+       rc = process_ungrouped_signals(inc);
+       if (rc)
+               return rc;
+
+       /*
+        * "Start" the session: Create channels, send the DF
+        * header to the session. Optionally send the sample
+        * rate before sample data will be sent.
+        */
+       rc = create_channels_groups(in);
+       if (rc)
+               return rc;
+       rc = create_feed_buffer(in);
+       if (rc)
+               return rc;
+
+       inc->ch_feed_prep = TRUE;
+
+       return SR_OK;
+}
+
+static int format_match(GHashTable *metadata, unsigned int *confidence)
+{
+       GString *buf, *tmpbuf;
+       int rc;
+       gchar *version, *build;
+
+       /* Get a copy of the start of the file's content. */
+       buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+       if (!buf || !buf->str)
+               return SR_ERR_ARG;
+       tmpbuf = g_string_new_len(buf->str, buf->len);
+       if (!tmpbuf || !tmpbuf->str)
+               return SR_ERR_MALLOC;
+
+       /* See if we can spot a typical first LPF line. */
+       rc = check_vers_line(tmpbuf->str, 1, &version, &build);
+       if (rc == SR_OK && version && build) {
+               sr_dbg("Looks like a LogicProbe project, version %s, build %s.",
+                       version, build);
+               *confidence = 1;
+       }
+       g_string_free(tmpbuf, TRUE);
+
+       return rc;
+}
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+       struct context *inc;
+
+       (void)options;
+
+       in->sdi = g_malloc0(sizeof(*in->sdi));
+       inc = g_malloc0(sizeof(*inc));
+       in->priv = inc;
+
+       return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+       struct context *inc;
+       int rc;
+
+       /* Accumulate another chunk of input data. */
+       g_string_append_len(in->buf, buf->str, buf->len);
+
+       /*
+        * Wait for the full header's availability, then process it in a
+        * single call, and set the "ready" flag. Make sure sample data
+        * and the header get processed in disjoint calls to receive(),
+        * the backend requires those separate phases.
+        */
+       inc = in->priv;
+       if (!inc->got_header) {
+               if (!have_header(in->buf))
+                       return SR_OK;
+               rc = parse_header(in);
+               if (rc)
+                       return rc;
+               rc = prepare_session_feed(in);
+               if (rc)
+                       return rc;
+               in->sdi_ready = TRUE;
+               return SR_OK;
+       }
+
+       /* Process sample data, after the header got processed. */
+       rc = process_queued_samples(in);
+
+       return rc;
+}
+
+static int end(struct sr_input *in)
+{
+       struct context *inc;
+       int rc;
+
+       /* Nothing to do here if we never started feeding the session. */
+       if (!in->sdi_ready)
+               return SR_OK;
+
+       /*
+        * Process sample data that may not have been forwarded before.
+        * Flush any potentially queued samples.
+        */
+       rc = process_queued_samples(in);
+       if (rc)
+               return rc;
+       rc = send_buffer(in);
+       if (rc)
+               return rc;
+
+       /* End the session feed if one was started. */
+       inc = in->priv;
+       if (inc->header_sent) {
+               rc = std_session_send_df_end(in->sdi);
+               inc->header_sent = FALSE;
+       }
+
+       return rc;
+}
+
+static void cleanup(struct sr_input *in)
+{
+       struct context *inc;
+       size_t idx;
+
+       if (!in)
+               return;
+
+       inc = in->priv;
+       if (!inc)
+               return;
+
+       /*
+        * Release potentially allocated resources. Void all references
+        * and scalars, so that re-runs start out fresh again.
+        */
+       g_free(inc->sw_version);
+       if (inc->cont_buff)
+               g_string_free(inc->cont_buff, TRUE);
+       g_free(inc->sample_data_queue);
+       for (idx = 0; idx < inc->channel_count; idx++)
+               g_free(inc->wire_names[idx]);
+       for (idx = 0; idx < inc->channel_count; idx++)
+               g_free(inc->signal_names[idx]);
+       g_slist_free_full(inc->signal_groups, sg_free);
+       g_slist_free_full(inc->channels, g_free);
+       g_free(inc->feed_buffer);
+       memset(inc, 0, sizeof(*inc));
+}
+
+static int reset(struct sr_input *in)
+{
+       struct context *inc;
+       GSList *channels;
+
+       inc = in->priv;
+
+       /*
+        * The input module's .reset() routine clears the 'inc' context,
+        * but 'in' is kept which contains channel groups which reference
+        * channels. Since we cannot re-create the channels (applications
+        * don't expect us to, see bug #1215), make sure to keep the
+        * channels across the reset operation.
+        */
+       channels = inc->channels;
+       inc->channels = NULL;
+       cleanup(in);
+       inc->channels = channels;
+
+       return SR_OK;
+}
+
+static struct sr_option options[] = {
+       ALL_ZERO,
+};
+
+static const struct sr_option *get_options(void)
+{
+       return options;
+}
+
+SR_PRIV struct sr_input_module input_logicport = {
+       .id = "logicport",
+       .name = "LogicPort File",
+       .desc = "Intronix LA1034 LogicPort project",
+       .exts = (const char *[]){ "lpf", NULL },
+       .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
+       .options = get_options,
+       .format_match = format_match,
+       .init = init,
+       .receive = receive,
+       .end = end,
+       .cleanup = cleanup,
+       .reset = reset,
+};
diff --git a/src/input/null.c b/src/input/null.c
new file mode 100644 (file)
index 0000000..031b553
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/null"
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+       (void)in;
+       (void)options;
+
+       return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+       (void)in;
+       (void)buf;
+
+       return SR_OK;
+}
+
+static int end(struct sr_input *in)
+{
+       (void)in;
+
+       return SR_OK;
+}
+
+SR_PRIV struct sr_input_module input_null = {
+       .id = "null",
+       .name = "Null",
+       .desc = "Null input (discards all input)",
+       .exts = NULL,
+       .options = NULL,
+       .init = init,
+       .receive = receive,
+       .end = end,
+       .reset = NULL,
+};
index 95b1d669ee0d5b51a2278ec4e8b14c625bc6fdaf..4d181b420c1a5dbc2c4ae7d6047e64540b9dffec 100644 (file)
@@ -31,7 +31,7 @@
 #define LOG_PREFIX "input/raw_analog"
 
 /* How many bytes at a time to process and send to the session bus. */
-#define CHUNK_SIZE             4096
+#define CHUNK_SIZE             (4 * 1024 * 1024)
 #define DEFAULT_NUM_CHANNELS   1
 #define DEFAULT_SAMPLERATE     0
 
@@ -54,20 +54,21 @@ struct sample_format {
 
 static const struct sample_format sample_formats[] =
 {
-       { "S8",         { 1, TRUE,  FALSE, FALSE, 0, TRUE, { 1,                     128}, { 0, 1}}},
-       { "U8",         { 1, FALSE, FALSE, FALSE, 0, TRUE, { 1,                     255}, {-1, 2}}},
-       { "S16_LE",     { 2, TRUE,  FALSE, FALSE, 0, TRUE, { 1,           INT16_MAX + 1}, { 0, 1}}},
-       { "U16_LE",     { 2, FALSE, FALSE, FALSE, 0, TRUE, { 1,              UINT16_MAX}, {-1, 2}}},
-       { "S16_BE",     { 2, TRUE,  FALSE, TRUE,  0, TRUE, { 1,           INT16_MAX + 1}, { 0, 1}}},
-       { "U16_BE",     { 2, FALSE, FALSE, TRUE,  0, TRUE, { 1,              UINT16_MAX}, {-1, 2}}},
-       { "S32_LE",     { 4, TRUE,  FALSE, FALSE, 0, TRUE, { 1, (uint64_t)INT32_MAX + 1}, { 0, 1}}},
-       { "U32_LE",     { 4, FALSE, FALSE, FALSE, 0, TRUE, { 1,              UINT32_MAX}, {-1, 2}}},
-       { "S32_BE",     { 4, TRUE,  FALSE, TRUE,  0, TRUE, { 1, (uint64_t)INT32_MAX + 1}, { 0, 1}}},
-       { "U32_BE",     { 4, FALSE, FALSE, TRUE,  0, TRUE, { 1,              UINT32_MAX}, {-1, 2}}},
-       { "FLOAT_LE",   { 4, TRUE,  TRUE,  FALSE, 0, TRUE, { 1,                       1}, { 0, 1}}},
-       { "FLOAT_BE",   { 4, TRUE,  TRUE,  TRUE,  0, TRUE, { 1,                       1}, { 0, 1}}},
-       { "FLOAT64_LE", { 8, TRUE,  TRUE,  FALSE, 0, TRUE, { 1,                       1}, { 0, 1}}},
-       { "FLOAT64_BE", { 8, TRUE,  TRUE,  TRUE,  0, TRUE, { 1,                       1}, { 0, 1}}},
+                       // bytes, signed, floating, bigendian, digits, digits decimal, scale, offset
+       { "S8",         { 1, TRUE,  FALSE, FALSE,  7, FALSE, { 1,                     128}, { 0, 1}}},
+       { "U8",         { 1, FALSE, FALSE, FALSE,  8, FALSE, { 1,                     255}, {-1, 2}}},
+       { "S16_LE",     { 2, TRUE,  FALSE, FALSE, 15, FALSE, { 1,           INT16_MAX + 1}, { 0, 1}}},
+       { "U16_LE",     { 2, FALSE, FALSE, FALSE, 16, FALSE, { 1,              UINT16_MAX}, {-1, 2}}},
+       { "S16_BE",     { 2, TRUE,  FALSE, TRUE,  15, FALSE, { 1,           INT16_MAX + 1}, { 0, 1}}},
+       { "U16_BE",     { 2, FALSE, FALSE, TRUE,  16, FALSE, { 1,              UINT16_MAX}, {-1, 2}}},
+       { "S32_LE",     { 4, TRUE,  FALSE, FALSE, 31, FALSE, { 1, (uint64_t)INT32_MAX + 1}, { 0, 1}}},
+       { "U32_LE",     { 4, FALSE, FALSE, FALSE, 32, FALSE, { 1,              UINT32_MAX}, {-1, 2}}},
+       { "S32_BE",     { 4, TRUE,  FALSE, TRUE,  31, FALSE, { 1, (uint64_t)INT32_MAX + 1}, { 0, 1}}},
+       { "U32_BE",     { 4, FALSE, FALSE, TRUE,  32, FALSE, { 1,              UINT32_MAX}, {-1, 2}}},
+       { "FLOAT_LE",   { 4, TRUE,  TRUE,  FALSE,   6, TRUE,  { 1,                       1}, { 0, 1}}},
+       { "FLOAT_BE",   { 4, TRUE,  TRUE,  TRUE,    6, TRUE,  { 1,                       1}, { 0, 1}}},
+       { "FLOAT64_LE", { 8, TRUE,  TRUE,  FALSE,  15, TRUE,  { 1,                       1}, { 0, 1}}},
+       { "FLOAT64_BE", { 8, TRUE,  TRUE,  TRUE,   15, TRUE,  { 1,                       1}, { 0, 1}}},
 };
 
 static int parse_format_string(const char *format)
@@ -105,7 +106,7 @@ static int init(struct sr_input *in, GHashTable *options)
 {
        struct context *inc;
        int num_channels;
-       char channelname[8];
+       char channelname[16];
        const char *format;
        int fmt_index;
 
@@ -130,7 +131,7 @@ static int init(struct sr_input *in, GHashTable *options)
        in->priv = inc = g_malloc0(sizeof(struct context));
 
        for (int i = 0; i < num_channels; i++) {
-               snprintf(channelname, 8, "CH%d", i + 1);
+               snprintf(channelname, sizeof(channelname) - 1, "CH%d", i + 1);
                sr_channel_new(in->sdi, i, SR_CHANNEL_ANALOG, TRUE, channelname);
        }
 
@@ -233,9 +234,9 @@ static int end(struct sr_input *in)
 }
 
 static struct sr_option options[] = {
-       { "numchannels", "Number of channels", "Number of channels", NULL, NULL },
-       { "samplerate", "Sample rate", "Sample rate", NULL, NULL },
-       { "format", "Format", "Numeric format", NULL, NULL },
+       { "numchannels", "Number of analog channels", "The number of (analog) channels in the data", NULL, NULL },
+       { "samplerate", "Sample rate (Hz)", "The sample rate of the (analog) data in Hz", NULL, NULL },
+       { "format", "Data format", "The format of the data (data type, signedness, endianness)", NULL, NULL },
        ALL_ZERO
 };
 
@@ -256,23 +257,21 @@ static const struct sr_option *get_options(void)
 
 static void cleanup(struct sr_input *in)
 {
-       struct context *inc;
+       g_free(in->priv);
+       in->priv = NULL;
 
-       inc = in->priv;
        g_variant_unref(options[0].def);
        g_variant_unref(options[1].def);
        g_variant_unref(options[2].def);
        g_slist_free_full(options[2].values, (GDestroyNotify)g_variant_unref);
-       g_free(inc);
-       in->priv = NULL;
 }
 
 static int reset(struct sr_input *in)
 {
        struct context *inc = in->priv;
 
-       cleanup(in);
        inc->started = FALSE;
+
        g_string_truncate(in->buf, 0);
 
        return SR_OK;
@@ -281,7 +280,7 @@ static int reset(struct sr_input *in)
 SR_PRIV struct sr_input_module input_raw_analog = {
        .id = "raw_analog",
        .name = "RAW analog",
-       .desc = "analog signals without header",
+       .desc = "Raw analog data without header",
        .exts = (const char*[]){"raw", "bin", NULL},
        .options = get_options,
        .init = init,
index 8cbd4d0721d9da1c52ff5d1b099340feed1111be..b1d801e8a85b9d7f93c4361d602f5addace8cb42 100644 (file)
 
 #define LOG_PREFIX "input/trace32_ad"
 
-#define MAX_CHUNK_SIZE    4096
-#define OUTBUF_FLUSH_SIZE 10240
+#define CHUNK_SIZE        (4 * 1024 * 1024)
 #define MAX_POD_COUNT     12
 #define HEADER_SIZE       80
 
+#define SPACE             ' '
+#define CTRLZ             '\x1a'
+#define TRACE32           "trace32"
+
 #define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
 
 /*
  */
 #define DEFAULT_SAMPLERATE 200
 
-enum {
-       AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
-       AD_FORMAT_TXTHDR      /* Textual header, binary data */
+enum ad_format {
+       AD_FORMAT_UNKNOWN,
+       AD_FORMAT_BINHDR,       /* Binary header, binary data, textual setup info */
+       AD_FORMAT_TXTHDR,       /* Textual header, binary data */
 };
 
-enum {
+enum ad_device {
        AD_DEVICE_PI = 1, /* Data recorded by LA-7940 PowerIntegrator or */
                           /* LA-394x PowerIntegrator II. */
        AD_DEVICE_IPROBE  /* Data recorded by LA-769x PowerTrace II IProbe. */
@@ -72,12 +76,12 @@ enum {
        /* Missing file format info for LA-4530 uTrace analog probe */
 };
 
-enum {
+enum ad_mode {
        AD_MODE_250MHZ = 0,
        AD_MODE_500MHZ = 1
 };
 
-enum {
+enum ad_compr {
        AD_COMPR_NONE  = 0, /* File created with /NOCOMPRESS */
        AD_COMPR_QCOMP = 6, /* File created with /COMPRESS or /QUICKCOMPRESS */
 };
@@ -85,7 +89,10 @@ enum {
 struct context {
        gboolean meta_sent;
        gboolean header_read, records_read, trigger_sent;
-       char format, device, record_mode, compression;
+       enum ad_format format;
+       enum ad_device device;
+       enum ad_mode record_mode;
+       enum ad_compr compression;
        char pod_status[MAX_POD_COUNT];
        struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
        uint64_t trigger_timestamp;
@@ -99,6 +106,29 @@ struct context {
 static int process_header(GString *buf, struct context *inc);
 static void create_channels(struct sr_input *in);
 
+/* Transform non-printable chars to '\xNN' presentation. */
+static char *printable_name(const char *name)
+{
+       size_t l, i;
+       char *s, *p;
+
+       if (!name)
+               return NULL;
+       l = strlen(name);
+       s = g_malloc0(l * strlen("\\x00") + 1);
+       for (p = s, i = 0; i < l; i++) {
+               if (g_ascii_isprint(name[i])) {
+                       *p++ = name[i];
+               } else {
+                       snprintf(p, 5, "\\x%02x", name[i]);
+                       p += strlen("\\x00");
+               }
+       }
+       *p = '\0';
+
+       return s;
+}
+
 static char get_pod_name_from_id(int id)
 {
        switch (id) {
@@ -152,24 +182,34 @@ static int init(struct sr_input *in, GHashTable *options)
                return SR_ERR;
        }
 
-       inc->out_buf = g_string_sized_new(OUTBUF_FLUSH_SIZE);
+       inc->out_buf = g_string_sized_new(CHUNK_SIZE);
 
        return SR_OK;
 }
 
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
 {
        GString *buf;
+       int rc;
 
        buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+       rc = process_header(buf, NULL);
+
+       if (rc != SR_OK)
+               return rc;
+       *confidence = 10;
 
-       return process_header(buf, NULL);
+       return SR_OK;
 }
 
 static int process_header(GString *buf, struct context *inc)
 {
        char *format_name, *format_name_sig;
-       int i, record_size, device_id;
+       char *p;
+       int has_trace32;
+       size_t record_size;
+       enum ad_device device_id;
+       enum ad_format format;
 
        /*
         * 00-31 (0x00-1F) file format name
@@ -190,40 +230,58 @@ static int process_header(GString *buf, struct context *inc)
         * 78-79 (0x4E-4F) ??
         */
 
-       /* Note: inc is off-limits until we check whether it's a valid pointer. */
+       /*
+        * Note: The routine is called from different contexts. Either
+        * to auto-detect the file format (format_match(), 'inc' is NULL),
+        * or to process the data during acquisition (receive(), 'inc'
+        * is a valid pointer). This header parse routine shall gracefully
+        * deal with unexpected or incorrect input data.
+        */
 
+       /*
+        * Get up to the first 32 bytes of the file content. File format
+        * names end on SPACE or CTRL-Z (or NUL). Trim trailing SPACE
+        * before further processing.
+        */
        format_name = g_strndup(buf->str, 32);
+       p = strchr(format_name, CTRLZ);
+       if (p)
+               *p = '\0';
+       g_strchomp(format_name);
 
-       /* File format name ends on 0x20/0x1A, let's remove both. */
-       for (i = 1; i < 31; i++) {
-               if (format_name[i] == 0x1A) {
-                       format_name[i - 1] = 0;
-                       format_name[i] = 0;
-               }
-       }
-       g_strchomp(format_name); /* This is for additional padding spaces. */
-
-       format_name_sig = g_strndup(format_name, 5);
+       /*
+        * File format names either start with the "trace32" literal,
+        * or with a digit and SPACE.
+        */
+       format_name_sig = g_strndup(format_name, strlen(TRACE32));
+       has_trace32 = g_strcmp0(format_name_sig, TRACE32) == 0;
+       g_free(format_name_sig);
 
-       /* Desired file formats either start with digit+space or "trace32". */
-       if (g_strcmp0(format_name_sig, "trace32")) {
-               if (inc)
-                       inc->format = AD_FORMAT_BINHDR;
-       } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == 0x20)) {
-               if (inc)
-                       inc->format = AD_FORMAT_TXTHDR;
-               g_free(format_name_sig);
+       format = AD_FORMAT_UNKNOWN;
+       if (has_trace32) {
+               /* Literal "trace32" leader, binary header follows. */
+               format = AD_FORMAT_BINHDR;
+       } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == SPACE)) {
+               /* Digit and SPACE leader, currently unsupported text header. */
+               format = AD_FORMAT_TXTHDR;
                g_free(format_name);
-               sr_err("This format isn't implemented yet, aborting.");
+               if (inc)
+                       sr_err("This format isn't implemented yet, aborting.");
                return SR_ERR;
        } else {
-               g_free(format_name_sig);
+               /* Unknown kind of format name. Unsupported. */
                g_free(format_name);
-               sr_err("Don't know this file format, aborting.");
+               if (inc)
+                       sr_err("Don't know this file format, aborting.");
                return SR_ERR;
        }
+       if (!format)
+               return SR_ERR;
 
-       sr_dbg("File says it's \"%s\"", format_name);
+       p = printable_name(format_name);
+       if (inc)
+               sr_dbg("File says it's \"%s\" -> format type %u.", p, format);
+       g_free(p);
 
        record_size = R8(buf->str + 56);
        device_id = 0;
@@ -237,20 +295,20 @@ static int process_header(GString *buf, struct context *inc)
        }
 
        if (!device_id) {
-               g_free(format_name_sig);
                g_free(format_name);
-               sr_err("Don't know how to handle this file with record size %d.",
-                       record_size);
+               if (inc)
+                       sr_err("Cannot handle file with record size %zu.",
+                               record_size);
                return SR_ERR;
        }
 
-       g_free(format_name_sig);
        g_free(format_name);
 
        /* Stop processing the header if we just want to identify the file. */
        if (!inc)
                return SR_OK;
 
+       inc->format       = format;
        inc->device       = device_id;
        inc->trigger_timestamp = RL64(buf->str + 32);
        inc->compression  = R8(buf->str + 48); /* Maps to the enum. */
@@ -266,7 +324,7 @@ static int process_header(GString *buf, struct context *inc)
                inc->last_record);
 
        /* Check if we can work with this compression. */
-       if (inc->compression != AD_COMPR_NONE) {
+       if (inc->compression) {
                sr_err("File uses unsupported compression (0x%02X), can't continue.",
                        inc->compression);
                return SR_ERR;
@@ -291,13 +349,13 @@ static void create_channels(struct sr_input *in)
                        continue;
 
                for (channel = 0; channel < 16; channel++) {
-                       snprintf(name, 8, "%c%d", get_pod_name_from_id(pod), channel);
+                       snprintf(name, sizeof(name), "%c%d", get_pod_name_from_id(pod), channel);
                        inc->channels[pod][channel] =
                                sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
                        chan_id++;
                }
 
-               snprintf(name, 8, "CLK%c", get_pod_name_from_id(pod));
+               snprintf(name, sizeof(name), "CLK%c", get_pod_name_from_id(pod));
                inc->channels[pod][16] =
                        sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
                chan_id++;
@@ -502,7 +560,7 @@ static void process_record_pi(struct sr_input *in, gsize start)
                        g_string_append_len(inc->out_buf, single_payload, payload_len);
        }
 
-       if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+       if (inc->out_buf->len >= CHUNK_SIZE)
                flush_output_buffer(in);
 }
 
@@ -555,7 +613,7 @@ static void process_record_iprobe(struct sr_input *in, gsize start)
                        g_string_append_len(inc->out_buf, single_payload, payload_len);
        }
 
-       if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+       if (inc->out_buf->len >= CHUNK_SIZE)
                flush_output_buffer(in);
 }
 
@@ -787,7 +845,7 @@ static struct sr_option options[] = {
        { "podN", "Import pod N", "Create channels and data for pod N", NULL, NULL },
        { "podO", "Import pod O", "Create channels and data for pod O", NULL, NULL },
 
-       { "samplerate", "Reduced sample rate in MHz", "Reduced sample rate in MHz", NULL, NULL },
+       { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
 
        ALL_ZERO
 };
index 8d6edd1dcc77594ea31f6cab8c489c1c974a48aa..e3c2347ac0875ff6f44f99f000077a691ce3a455 100644 (file)
 
 #define LOG_PREFIX "input/vcd"
 
-#define CHUNKSIZE (1024 * 1024)
+#define CHUNK_SIZE (4 * 1024 * 1024)
 
 struct context {
        gboolean started;
        gboolean got_header;
+       uint64_t prev_timestamp;
        uint64_t samplerate;
        unsigned int maxchannels;
        unsigned int channelcount;
@@ -84,6 +85,7 @@ struct context {
        size_t samples_in_buffer;
        uint8_t *buffer;
        uint8_t *current_levels;
+       GSList *prev_sr_channels;
 };
 
 struct vcd_channel {
@@ -150,7 +152,11 @@ static gboolean parse_section(GString *buf, gchar **name, gchar **contents)
 
 static void free_channel(void *data)
 {
-       struct vcd_channel *vcd_ch = data;
+       struct vcd_channel *vcd_ch;
+
+       vcd_ch = data;
+       if (!vcd_ch)
+               return;
        g_free(vcd_ch->name);
        g_free(vcd_ch->identifier);
        g_free(vcd_ch);
@@ -170,6 +176,56 @@ static void remove_empty_parts(gchar **parts)
        *dest = NULL;
 }
 
+/*
+ * Keep track of a previously created channel list, in preparation of
+ * re-reading the input file. Gets called from reset()/cleanup() paths.
+ */
+static void keep_header_for_reread(const struct sr_input *in)
+{
+       struct context *inc;
+
+       inc = in->priv;
+       g_slist_free_full(inc->prev_sr_channels, sr_channel_free_cb);
+       inc->prev_sr_channels = in->sdi->channels;
+       in->sdi->channels = NULL;
+}
+
+/*
+ * Check whether the input file is being re-read, and refuse operation
+ * when essential parameters of the acquisition have changed in ways
+ * that are unexpected to calling applications. Gets called after the
+ * file header got parsed (again).
+ *
+ * Changing the channel list across re-imports of the same file is not
+ * supported, by design and for valid reasons, see bug #1215 for details.
+ * Users are expected to start new sessions when they change these
+ * essential parameters in the acquisition's setup. When we accept the
+ * re-read file, then make sure to keep using the previous channel list,
+ * applications may still reference them.
+ */
+static int check_header_in_reread(const struct sr_input *in)
+{
+       struct context *inc;
+
+       if (!in)
+               return FALSE;
+       inc = in->priv;
+       if (!inc)
+               return FALSE;
+       if (!inc->prev_sr_channels)
+               return TRUE;
+
+       if (sr_channel_lists_differ(inc->prev_sr_channels, in->sdi->channels)) {
+               sr_err("Channel list change not supported for file re-read.");
+               return FALSE;
+       }
+       g_slist_free_full(in->sdi->channels, sr_channel_free_cb);
+       in->sdi->channels = inc->prev_sr_channels;
+       inc->prev_sr_channels = NULL;
+
+       return TRUE;
+}
+
 /*
  * Parse VCD header to get values for context structure.
  * The context structure should be zeroed before calling this.
@@ -260,11 +316,13 @@ static gboolean parse_header(const struct sr_input *in, GString *buf)
        inc->current_levels = g_malloc0(inc->bytes_per_sample);
 
        inc->got_header = status;
+       if (status)
+               status = check_header_in_reread(in);
 
        return status;
 }
 
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
 {
        GString *buf, *tmpbuf;
        gboolean status;
@@ -282,7 +340,11 @@ static int format_match(GHashTable *metadata)
        g_free(name);
        g_free(contents);
 
-       return status ? SR_OK : SR_ERR;
+       if (!status)
+               return SR_ERR;
+       *confidence = 1;
+
+       return SR_OK;
 }
 
 /* Send all accumulated bytes from inc->buffer. */
@@ -318,7 +380,7 @@ static void add_samples(const struct sr_input *in, size_t count)
        uint8_t *p;
 
        inc = in->priv;
-       samples_per_chunk = CHUNKSIZE / inc->bytes_per_sample;
+       samples_per_chunk = CHUNK_SIZE / inc->bytes_per_sample;
 
        while (count) {
                space_left = samples_per_chunk - inc->samples_in_buffer;
@@ -367,12 +429,11 @@ static void process_bit(struct context *inc, char *identifier, unsigned int bit)
 static void parse_contents(const struct sr_input *in, char *data)
 {
        struct context *inc;
-       uint64_t timestamp, prev_timestamp;
+       uint64_t timestamp;
        unsigned int bit, i;
        char **tokens;
 
        inc = in->priv;
-       prev_timestamp = 0;
 
        /* Read one space-delimited token at a time. */
        tokens = g_strsplit_set(data, " \t\r\n", 0);
@@ -399,22 +460,26 @@ static void parse_contents(const struct sr_input *in, char *data)
                         */
                        if (inc->skip < 0) {
                                inc->skip = timestamp;
-                               prev_timestamp = timestamp;
+                               inc->prev_timestamp = timestamp;
                        } else if (inc->skip > 0 && timestamp < (uint64_t)inc->skip) {
-                               prev_timestamp = inc->skip;
-                       } else if (timestamp == prev_timestamp) {
+                               inc->prev_timestamp = inc->skip;
+                       } else if (timestamp == inc->prev_timestamp) {
                                /* Ignore repeated timestamps (e.g. sigrok outputs these) */
+                       } else if (timestamp < inc->prev_timestamp) {
+                               sr_err("Invalid timestamp: %" PRIu64 " (smaller than previous timestamp).", timestamp);
+                               inc->skip_until_end = TRUE;
+                               break;
                        } else {
-                               if (inc->compress != 0 && timestamp - prev_timestamp > inc->compress) {
+                               if (inc->compress != 0 && timestamp - inc->prev_timestamp > inc->compress) {
                                        /* Compress long idle periods */
-                                       prev_timestamp = timestamp - inc->compress;
+                                       inc->prev_timestamp = timestamp - inc->compress;
                                }
 
                                sr_dbg("New timestamp: %" PRIu64, timestamp);
 
                                /* Generate samples from prev_timestamp up to timestamp - 1. */
-                               add_samples(in, timestamp - prev_timestamp);
-                               prev_timestamp = timestamp;
+                               add_samples(in, timestamp - inc->prev_timestamp);
+                               inc->prev_timestamp = timestamp;
                        }
                } else if (tokens[i][0] == '$' && tokens[i][1] != '\0') {
                        /*
@@ -498,7 +563,7 @@ static int init(struct sr_input *in, GHashTable *options)
        in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
        in->priv = inc;
 
-       inc->buffer = g_malloc(CHUNKSIZE);
+       inc->buffer = g_malloc(CHUNK_SIZE);
 
        return SR_OK;
 }
@@ -606,7 +671,10 @@ static void cleanup(struct sr_input *in)
        struct context *inc;
 
        inc = in->priv;
+       keep_header_for_reread(in);
        g_slist_free_full(inc->channels, free_channel);
+       inc->channels = NULL;
+
        g_free(inc->buffer);
        inc->buffer = NULL;
        g_free(inc->current_levels);
@@ -618,17 +686,25 @@ static int reset(struct sr_input *in)
        struct context *inc = in->priv;
 
        cleanup(in);
-       inc->started = FALSE;
        g_string_truncate(in->buf, 0);
 
+       inc->started = FALSE;
+       inc->got_header = FALSE;
+       inc->prev_timestamp = 0;
+       inc->skip_until_end = FALSE;
+       inc->channelcount = 0;
+       /* The inc->channels list was released in cleanup() above. */
+       inc->buffer = g_malloc(CHUNK_SIZE);
+
        return SR_OK;
 }
 
 static struct sr_option options[] = {
-       { "numchannels", "Number of channels", "Number of channels", NULL, NULL },
-       { "skip", "Skip", "Skip until timestamp", NULL, NULL },
-       { "downsample", "Downsample", "Divide samplerate by factor", NULL, NULL },
-       { "compress", "Compress", "Compress idle periods longer than this value", NULL, NULL },
+       { "numchannels", "Number of logic channels", "The number of (logic) channels in the data", NULL, NULL },
+       { "skip", "Skip samples until timestamp", "Skip samples until the specified timestamp; "
+               "< 0: Skip until first timestamp listed; 0: Don't skip", NULL, NULL },
+       { "downsample", "Downsampling factor", "Downsample, i.e. divide the samplerate by the specified factor", NULL, NULL },
+       { "compress", "Compress idle periods", "Compress idle periods longer than the specified value", NULL, NULL },
        ALL_ZERO
 };
 
@@ -647,7 +723,7 @@ static const struct sr_option *get_options(void)
 SR_PRIV struct sr_input_module input_vcd = {
        .id = "vcd",
        .name = "VCD",
-       .desc = "Value Change Dump",
+       .desc = "Value Change Dump data",
        .exts = (const char*[]){"vcd", NULL},
        .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
        .options = get_options,
index 243fd4ca2d984451a8cf68e889e187a03a607c31..b6d55888bf54d3499f4d694ae061f80235ab94a6 100644 (file)
@@ -31,7 +31,7 @@
 #define LOG_PREFIX "input/wav"
 
 /* How many bytes at a time to process and send to the session bus. */
-#define CHUNK_SIZE               4096
+#define CHUNK_SIZE               (1 * 1024 * 1024 * sizeof(float))
 
 /* Minimum size of header + 1 8-bit mono PCM sample. */
 #define MIN_DATA_CHUNK_OFFSET    45
@@ -51,6 +51,7 @@ struct context {
        int num_channels;
        int unitsize;
        gboolean found_data;
+       gboolean create_channels;
 };
 
 static int parse_wav_header(GString *buf, struct context *inc)
@@ -124,7 +125,7 @@ static int parse_wav_header(GString *buf, struct context *inc)
        return SR_OK;
 }
 
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
 {
        GString *buf;
        int ret;
@@ -143,15 +144,22 @@ static int format_match(GHashTable *metadata)
        if ((ret = parse_wav_header(buf, NULL)) != SR_OK)
                return ret;
 
+       *confidence = 1;
+
        return SR_OK;
 }
 
 static int init(struct sr_input *in, GHashTable *options)
 {
+       struct context *inc;
+
        (void)options;
 
        in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
        in->priv = g_malloc0(sizeof(struct context));
+       inc = in->priv;
+
+       inc->create_channels = TRUE;
 
        return SR_OK;
 }
@@ -189,16 +197,17 @@ static void send_chunk(const struct sr_input *in, int offset, int num_samples)
        struct sr_analog_meaning meaning;
        struct sr_analog_spec spec;
        struct context *inc;
-       float fdata[CHUNK_SIZE];
+       float *fdata;
        int total_samples, samplenum;
        char *s, *d;
 
        inc = in->priv;
 
+       total_samples = num_samples * inc->num_channels;
+       fdata = g_malloc0(total_samples * sizeof(float));
        s = in->buf->str + offset;
        d = (char *)fdata;
-       memset(fdata, 0, CHUNK_SIZE);
-       total_samples = num_samples * inc->num_channels;
+
        for (samplenum = 0; samplenum < total_samples; samplenum++) {
                if (inc->fmt_code == WAVE_FORMAT_PCM_) {
                        switch (inc->unitsize) {
@@ -238,6 +247,7 @@ static void send_chunk(const struct sr_input *in, int offset, int num_samples)
        analog.meaning->mqflags = 0;
        analog.meaning->unit = 0;
        sr_session_send(in->sdi, &packet);
+       g_free(fdata);
 }
 
 static int process_buffer(struct sr_input *in)
@@ -310,7 +320,7 @@ static int receive(struct sr_input *in, GString *buf)
 {
        struct context *inc;
        int ret;
-       char channelname[8];
+       char channelname[16];
 
        g_string_append_len(in->buf, buf->str, buf->len);
 
@@ -330,11 +340,15 @@ static int receive(struct sr_input *in, GString *buf)
                else if (ret != SR_OK)
                        return ret;
 
-               for (int i = 0; i < inc->num_channels; i++) {
-                       snprintf(channelname, 8, "CH%d", i + 1);
-                       sr_channel_new(in->sdi, i, SR_CHANNEL_ANALOG, TRUE, channelname);
+               if (inc->create_channels) {
+                       for (int i = 0; i < inc->num_channels; i++) {
+                               snprintf(channelname, sizeof(channelname), "CH%d", i + 1);
+                               sr_channel_new(in->sdi, i, SR_CHANNEL_ANALOG, TRUE, channelname);
+                       }
                }
 
+               inc->create_channels = FALSE;
+
                /* sdi is ready, notify frontend. */
                in->sdi_ready = TRUE;
                return SR_OK;
@@ -364,9 +378,13 @@ static int end(struct sr_input *in)
 
 static int reset(struct sr_input *in)
 {
-       struct context *inc = in->priv;
+       memset(in->priv, 0, sizeof(struct context));
+
+       /*
+        * We only want to create the sigrok channels once, so
+        * inc->create_channels won't be set to TRUE this time around.
+        */
 
-       inc->started = FALSE;
        g_string_truncate(in->buf, 0);
 
        return SR_OK;
@@ -375,7 +393,7 @@ static int reset(struct sr_input *in)
 SR_PRIV struct sr_input_module input_wav = {
        .id = "wav",
        .name = "WAV",
-       .desc = "WAV file",
+       .desc = "Microsoft WAV file format data",
        .exts = (const char*[]){"wav", NULL},
        .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
        .format_match = format_match,
index 3a987c626928c1a62b1953077713383fa18b5bf0..2dc5e648159aeddeb6fe73370da6e789a77f72e8 100644 (file)
@@ -251,32 +251,6 @@ static int serial_stream_check(struct sr_serial_dev_inst *serial,
                                       is_valid, timeout_ms, baudrate);
 }
 
-struct std_opt_desc {
-       const uint32_t *scanopts;
-       const int num_scanopts;
-       const uint32_t *devopts;
-       const int num_devopts;
-};
-
-static int std_config_list(uint32_t key, GVariant **data,
-                          const struct std_opt_desc *d)
-{
-       switch (key) {
-       case SR_CONF_SCAN_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       d->scanopts, d->num_scanopts, sizeof(uint32_t));
-               break;
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       d->devopts, d->num_devopts, sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
-}
-
 static int send_config_update(struct sr_dev_inst *sdi, struct sr_config *cfg)
 {
        struct sr_datafeed_packet packet;
@@ -413,15 +387,11 @@ static const char *const models[] = {
        "NONE", "PARALLEL", "SERIES", "AUTO",
 };
 
-/** Private, per-device-instance driver context. */
 struct dev_context {
-       /** The number of frames. */
        struct dev_limit_counter frame_count;
 
-       /** The time limit counter. */
        struct dev_time_counter time_count;
 
-       /** Data buffer. */
        struct dev_buffer *buf;
 
        /** The frequency of the test signal (index to frequencies[]). */
@@ -623,6 +593,7 @@ static void handle_packet(struct sr_dev_inst *sdi, const uint8_t *pkt)
        unsigned int val;
        float floatval;
        gboolean frame;
+       struct sr_channel *channel;
 
        devc = sdi->priv;
 
@@ -650,10 +621,11 @@ static void handle_packet(struct sr_dev_inst *sdi, const uint8_t *pkt)
        analog.num_samples = 1;
        analog.data = &floatval;
 
-       analog.meaning->channels = g_slist_append(NULL, sdi->channels->data);
+       channel = sdi->channels->data;
+       analog.meaning->channels = g_slist_append(NULL, channel);
 
        parse_measurement(pkt, &floatval, &analog, 0);
-       if (analog.meaning->mq != 0) {
+       if (analog.meaning->mq != 0 && channel->enabled) {
                if (!frame) {
                        packet.type = SR_DF_FRAME_BEGIN;
                        sr_session_send(sdi, &packet);
@@ -667,10 +639,12 @@ static void handle_packet(struct sr_dev_inst *sdi, const uint8_t *pkt)
        }
 
        g_slist_free(analog.meaning->channels);
-       analog.meaning->channels = g_slist_append(NULL, sdi->channels->next->data);
+
+       channel = sdi->channels->next->data;
+       analog.meaning->channels = g_slist_append(NULL, channel);
 
        parse_measurement(pkt, &floatval, &analog, 1);
-       if (analog.meaning->mq != 0) {
+       if (analog.meaning->mq != 0 && channel->enabled) {
                if (!frame) {
                        packet.type = SR_DF_FRAME_BEGIN;
                        sr_session_send(sdi, &packet);
@@ -731,7 +705,7 @@ static int receive_data(int fd, int revents, void *cb_data)
 
        if (dev_limit_counter_limit_reached(&devc->frame_count) ||
            dev_time_limit_reached(&devc->time_count))
-               sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
 
        return TRUE;
 }
@@ -756,7 +730,6 @@ SR_PRIV void es51919_serial_clean(void *priv)
                return;
 
        dev_buffer_destroy(devc->buf);
-       g_free(devc);
 }
 
 SR_PRIV struct sr_dev_inst *es51919_serial_scan(GSList *options,
@@ -865,8 +838,11 @@ static const uint32_t scanopts[] = {
        SR_CONF_SERIALCOMM,
 };
 
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
        SR_CONF_LCRMETER,
+};
+
+static const uint32_t devopts[] = {
        SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
        SR_CONF_LIMIT_MSEC | SR_CONF_SET,
@@ -874,28 +850,20 @@ static const uint32_t devopts[] = {
        SR_CONF_EQUIV_CIRCUIT_MODEL | SR_CONF_GET | SR_CONF_LIST,
 };
 
-static const struct std_opt_desc opts = {
-       scanopts, ARRAY_SIZE(scanopts),
-       devopts, ARRAY_SIZE(devopts),
-};
-
 SR_PRIV int es51919_serial_config_list(uint32_t key, GVariant **data,
                                       const struct sr_dev_inst *sdi,
                                       const struct sr_channel_group *cg)
 {
-       (void)sdi;
-       (void)cg;
-
-       if (std_config_list(key, data, &opts) == SR_OK)
-               return SR_OK;
-
        switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
        case SR_CONF_OUTPUT_FREQUENCY:
                *data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE,
-                       frequencies, ARRAY_SIZE(frequencies), sizeof(double));
+                       ARRAY_AND_SIZE(frequencies), sizeof(double));
                break;
        case SR_CONF_EQUIV_CIRCUIT_MODEL:
-               *data = g_variant_new_strv(models, ARRAY_SIZE(models));
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(models));
                break;
        default:
                return SR_ERR_NA;
@@ -909,9 +877,6 @@ SR_PRIV int es51919_serial_acquisition_start(const struct sr_dev_inst *sdi)
        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))
                return SR_ERR_BUG;
 
@@ -920,7 +885,6 @@ SR_PRIV int es51919_serial_acquisition_start(const struct sr_dev_inst *sdi)
 
        std_session_send_df_header(sdi);
 
-       /* Poll every 50ms, or whenever some data comes in. */
        serial = sdi->conn;
        serial_source_add(sdi->session, serial, G_IO_IN, 50,
                          receive_data, (void *)sdi);
index 2e041a26aef37edac32f4823ae3e864685085bc4..b50cd57b90df8669e2cc1de1f909269987aca41e 100644 (file)
@@ -426,6 +426,8 @@ struct sr_input_module {
         * Check if this input module can load and parse the specified stream.
         *
         * @param[in] metadata Metadata the module can use to identify the stream.
+        * @param[out] confidence "Strength" of the detection.
+        *   Specialized handlers can take precedence over generic/basic support.
         *
         * @retval SR_OK This module knows the format.
         * @retval SR_ERR_NA There wasn't enough data for this module to
@@ -434,8 +436,15 @@ struct sr_input_module {
         *   it. This means the stream is either corrupt, or indicates a
         *   feature that the module does not support.
         * @retval SR_ERR This module does not know the format.
+        *
+        * Lower numeric values of 'confidence' mean that the input module
+        * stronger believes in its capability to handle this specific format.
+        * This way, multiple input modules can claim support for a format,
+        * and the application can pick the best match, or try fallbacks
+        * in case of errors. This approach also copes with formats that
+        * are unreliable to detect in the absence of magic signatures.
         */
-       int (*format_match) (GHashTable *metadata);
+       int (*format_match) (GHashTable *metadata, unsigned int *confidence);
 
        /**
         * Initialize the input module.
@@ -736,7 +745,7 @@ struct drv_context {
 
 /*--- log.c -----------------------------------------------------------------*/
 
-#if defined(G_OS_WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
+#if defined(_WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
 /*
  * On MinGW, we need to specify the gnu_printf format flavor or GCC
  * will assume non-standard Microsoft printf syntax.
@@ -773,8 +782,12 @@ enum {
 
 SR_PRIV struct sr_channel *sr_channel_new(struct sr_dev_inst *sdi,
                int index, int type, gboolean enabled, const char *name);
+SR_PRIV void sr_channel_free(struct sr_channel *ch);
+SR_PRIV void sr_channel_free_cb(void *p);
 SR_PRIV struct sr_channel *sr_next_enabled_channel(const struct sr_dev_inst *sdi,
                struct sr_channel *cur_channel);
+SR_PRIV gboolean sr_channels_differ(struct sr_channel *ch1, struct sr_channel *ch2);
+SR_PRIV gboolean sr_channel_lists_differ(GSList *l1, GSList *l2);
 
 /** Device instance data */
 struct sr_dev_inst {
@@ -834,6 +847,8 @@ SR_PRIV int sr_variant_type_check(uint32_t key, GVariant *data);
 SR_PRIV void sr_hw_cleanup_all(const struct sr_context *ctx);
 SR_PRIV struct sr_config *sr_config_new(uint32_t key, GVariant *data);
 SR_PRIV void sr_config_free(struct sr_config *src);
+SR_PRIV int sr_dev_acquisition_start(struct sr_dev_inst *sdi);
+SR_PRIV int sr_dev_acquisition_stop(struct sr_dev_inst *sdi);
 
 /*--- session.c -------------------------------------------------------------*/
 
@@ -898,9 +913,6 @@ SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
 SR_PRIV int sr_sessionfile_check(const char *filename);
 SR_PRIV struct sr_dev_inst *sr_session_prepare_sdi(const char *filename,
                struct sr_session **session);
-SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
-               struct sr_datafeed_packet **copy);
-SR_PRIV void sr_packet_free(struct sr_datafeed_packet *packet);
 
 /*--- session_file.c --------------------------------------------------------*/
 
@@ -928,18 +940,67 @@ typedef void (*std_dev_clear_callback)(void *priv);
 
 SR_PRIV int std_init(struct sr_dev_driver *di, struct sr_context *sr_ctx);
 SR_PRIV int std_cleanup(const struct sr_dev_driver *di);
+SR_PRIV int std_dummy_dev_open(struct sr_dev_inst *sdi);
+SR_PRIV int std_dummy_dev_close(struct sr_dev_inst *sdi);
+SR_PRIV int std_dummy_dev_acquisition_start(const struct sr_dev_inst *sdi);
+SR_PRIV int std_dummy_dev_acquisition_stop(struct sr_dev_inst *sdi);
 #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);
 #endif
 SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi);
 SR_PRIV int std_session_send_df_end(const struct sr_dev_inst *sdi);
-SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
+SR_PRIV int std_session_send_frame_begin(const struct sr_dev_inst *sdi);
+SR_PRIV int std_session_send_frame_end(const struct sr_dev_inst *sdi);
+SR_PRIV int std_dev_clear_with_callback(const struct sr_dev_driver *driver,
                std_dev_clear_callback clear_private);
+SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver);
 SR_PRIV GSList *std_dev_list(const struct sr_dev_driver *di);
 SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi);
 SR_PRIV GSList *std_scan_complete(struct sr_dev_driver *di, GSList *devices);
 
+SR_PRIV int std_opts_config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg,
+       const uint32_t scanopts[], size_t scansize, const uint32_t drvopts[],
+       size_t drvsize, const uint32_t devopts[], size_t devsize);
+
+extern SR_PRIV const uint32_t NO_OPTS[1];
+
+#define STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts) \
+       std_opts_config_list(key, data, sdi, cg, ARRAY_AND_SIZE(scanopts), \
+               ARRAY_AND_SIZE(drvopts), ARRAY_AND_SIZE(devopts))
+
+SR_PRIV GVariant *std_gvar_tuple_array(const uint64_t a[][2], unsigned int n);
+SR_PRIV GVariant *std_gvar_tuple_rational(const struct sr_rational *r, unsigned int n);
+SR_PRIV GVariant *std_gvar_samplerates(const uint64_t samplerates[], unsigned int n);
+SR_PRIV GVariant *std_gvar_samplerates_steps(const uint64_t samplerates[], unsigned int n);
+SR_PRIV GVariant *std_gvar_min_max_step(double min, double max, double step);
+SR_PRIV GVariant *std_gvar_min_max_step_array(const double a[3]);
+SR_PRIV GVariant *std_gvar_min_max_step_thresholds(const double dmin, const double dmax, const double dstep);
+
+SR_PRIV GVariant *std_gvar_tuple_u64(uint64_t low, uint64_t high);
+SR_PRIV GVariant *std_gvar_tuple_double(double low, double high);
+
+SR_PRIV GVariant *std_gvar_array_i32(const int32_t a[], unsigned int n);
+SR_PRIV GVariant *std_gvar_array_u32(const uint32_t a[], unsigned int n);
+SR_PRIV GVariant *std_gvar_array_u64(const uint64_t a[], unsigned int n);
+SR_PRIV GVariant *std_gvar_array_str(const char *a[], unsigned int n);
+
+SR_PRIV GVariant *std_gvar_thresholds(const double a[][2], unsigned int n);
+
+SR_PRIV int std_str_idx(GVariant *data, const char *a[], unsigned int n);
+SR_PRIV int std_u64_idx(GVariant *data, const uint64_t a[], unsigned int n);
+SR_PRIV int std_u8_idx(GVariant *data, const uint8_t a[], unsigned int n);
+
+SR_PRIV int std_str_idx_s(const char *s, const char *a[], unsigned int n);
+SR_PRIV int std_u8_idx_s(uint8_t b, const uint8_t a[], unsigned int n);
+
+SR_PRIV int std_u64_tuple_idx(GVariant *data, const uint64_t a[][2], unsigned int n);
+SR_PRIV int std_double_tuple_idx(GVariant *data, const double a[][2], unsigned int n);
+SR_PRIV int std_double_tuple_idx_d0(const double d, const double a[][2], unsigned int n);
+
+SR_PRIV int std_cg_idx(const struct sr_channel_group *cg, struct sr_channel_group *a[], unsigned int n);
+
 /*--- resource.c ------------------------------------------------------------*/
 
 SR_PRIV int64_t sr_file_get_size(FILE *file);
@@ -962,8 +1023,12 @@ 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_atod_ascii(const char *str, double *ret);
 SR_PRIV int sr_atof_ascii(const char *str, float *ret);
 
+SR_PRIV GString *sr_hexdump_new(const uint8_t *data, const size_t len);
+SR_PRIV void sr_hexdump_free(GString *s);
+
 /*--- soft-trigger.c --------------------------------------------------------*/
 
 struct soft_trigger_logic {
@@ -979,6 +1044,7 @@ struct soft_trigger_logic {
        int pre_trigger_fill;
 };
 
+SR_PRIV int logic_channel_unitsize(GSList *channels);
 SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
                const struct sr_dev_inst *sdi, struct sr_trigger *trigger,
                int pre_trigger_samples);
@@ -1050,6 +1116,8 @@ 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);
 SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len);
+SR_PRIV gboolean usb_match_manuf_prod(libusb_device *dev,
+               const char *manufacturer, const char *product);
 #endif
 
 
@@ -1190,6 +1258,21 @@ 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/dmm/ms8250d.c ------------------------------------------------*/
+
+#define MS8250D_PACKET_SIZE 18
+
+struct ms8250d_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_ncv, is_min, is_max, is_sign, is_autotimer;
+};
+
+SR_PRIV gboolean sr_ms8250d_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_ms8250d_parse(const uint8_t *buf, float *floatval,
+                            struct sr_datafeed_analog *analog, void *info);
+
 /*--- hardware/dmm/dtm0660.c ------------------------------------------------*/
 
 #define DTM0660_PACKET_SIZE 15
@@ -1222,11 +1305,13 @@ SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
 #define METEX14_PACKET_SIZE 14
 
 struct metex14_info {
+       size_t ch_idx;
        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;
+       gboolean is_hertz, is_ohm, is_celsius, is_fahrenheit, is_watt;
+       gboolean is_pico, is_nano, is_micro, is_milli, is_kilo, is_mega;
+       gboolean is_gain, is_decibel, is_power, is_decibel_mw, is_power_factor;
+       gboolean is_hfe, is_unitless, is_logic, is_min, is_max, is_avg;
 };
 
 #ifdef HAVE_LIBSERIALPORT
@@ -1235,6 +1320,9 @@ SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial);
 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);
+SR_PRIV gboolean sr_metex14_4packets_valid(const uint8_t *buf);
+SR_PRIV int sr_metex14_4packets_parse(const uint8_t *buf, float *floatval,
+                            struct sr_datafeed_analog *analog, void *info);
 
 /*--- hardware/dmm/rs9lcd.c -------------------------------------------------*/
 
@@ -1294,6 +1382,21 @@ SR_PRIV gboolean sr_vc870_packet_valid(const uint8_t *buf);
 SR_PRIV int sr_vc870_parse(const uint8_t *buf, float *floatval,
                struct sr_datafeed_analog *analog, void *info);
 
+/*--- hardware/dmm/vc96.c ---------------------------------------------------*/
+
+#define VC96_PACKET_SIZE 13
+
+struct vc96_info {
+       size_t ch_idx;
+       gboolean is_ac, is_dc, is_resistance, is_diode, is_ampere, is_volt;
+       gboolean is_ohm, is_micro, is_milli, is_kilo, is_mega, is_hfe;
+       gboolean is_unitless;
+};
+
+SR_PRIV gboolean sr_vc96_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_vc96_parse(const uint8_t *buf, float *floatval,
+               struct sr_datafeed_analog *analog, void *info);
+
 /*--- hardware/lcr/es51919.c ------------------------------------------------*/
 
 SR_PRIV void es51919_serial_clean(void *priv);
@@ -1349,6 +1452,49 @@ SR_PRIV gboolean sr_asycii_packet_valid(const uint8_t *buf);
 SR_PRIV int sr_asycii_parse(const uint8_t *buf, float *floatval,
                            struct sr_datafeed_analog *analog, void *info);
 
+/*--- src/dmm/eev121gw.c ----------------------------------------------------*/
+
+#define EEV121GW_PACKET_SIZE 19
+
+enum eev121gw_display {
+       EEV121GW_DISPLAY_MAIN,
+       EEV121GW_DISPLAY_SUB,
+       EEV121GW_DISPLAY_BAR,
+       EEV121GW_DISPLAY_COUNT,
+};
+
+struct eev121gw_info {
+       /* Selected channel. */
+       size_t ch_idx;
+       /*
+        * Measured value, number and sign/overflow flags, scale factor
+        * and significant digits.
+        */
+       uint32_t uint_value;
+       gboolean is_ofl, is_neg;
+       int factor, digits;
+       /* Currently active mode (meter's function). */
+       gboolean is_ac, is_dc, is_voltage, is_current, is_power, is_gain;
+       gboolean is_resistance, is_capacitance, is_diode, is_temperature;
+       gboolean is_continuity, is_frequency, is_period, is_duty_cycle;
+       /* Quantities associated with mode/function. */
+       gboolean is_ampere, is_volt, is_volt_ampere, is_dbm;
+       gboolean is_ohm, is_farad, is_celsius, is_fahrenheit;
+       gboolean is_hertz, is_seconds, is_percent, is_loop_current;
+       gboolean is_unitless, is_logic;
+       /* Other indicators. */
+       gboolean is_min, is_max, is_avg, is_1ms_peak, is_rel, is_hold;
+       gboolean is_low_pass, is_mem, is_bt, is_auto_range, is_test;
+       gboolean is_auto_poweroff, is_low_batt;
+};
+
+extern SR_PRIV const char *eev121gw_channel_formats[];
+SR_PRIV gboolean sr_eev121gw_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_eev121gw_parse(const uint8_t *buf, float *floatval,
+                            struct sr_datafeed_analog *analog, void *info);
+SR_PRIV int sr_eev121gw_3displays_parse(const uint8_t *buf, float *floatval,
+                            struct sr_datafeed_analog *analog, void *info);
+
 /*--- hardware/scale/kern.c -------------------------------------------------*/
 
 struct kern_info {
index cdf2293bdeb32b012d43548545c44c058eb15a1b..701df645e76cf296dfe7c760368704668575a2d5 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -160,6 +160,28 @@ SR_API int sr_log_callback_set_default(void)
        return SR_OK;
 }
 
+/**
+ * Get the libsigrok log callback routine and callback data.
+ *
+ * @param[out] cb Pointer to a function pointer to receive the log callback
+ *     function. Optional, can be NULL.
+ * @param[out] cb_data Pointer to a void pointer to receive the log callback's
+ *     additional arguments. Optional, can be NULL.
+ *
+ * @return SR_OK upon success.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_log_callback_get(sr_log_callback *cb, void **cb_data)
+{
+       if (cb)
+               *cb = sr_log_cb;
+       if (cb_data)
+               *cb_data = sr_log_cb_data;
+
+       return SR_OK;
+}
+
 static int sr_logv(void *cb_data, int loglevel, const char *format, va_list args)
 {
        uint64_t elapsed_us, minutes;
@@ -170,9 +192,7 @@ static int sr_logv(void *cb_data, int loglevel, const char *format, va_list args
        /* 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;
+       (void)loglevel;
 
        if (cur_loglevel >= LOGLEVEL_TIMESTAMP) {
                elapsed_us = g_get_monotonic_time() - sr_log_start_time;
@@ -204,6 +224,7 @@ static int sr_logv(void *cb_data, int loglevel, const char *format, va_list args
        }
 
        g_fprintf(stderr, "%s\n", output);
+       fflush(stderr);
        g_free(raw_output);
        g_free(output);
 
@@ -216,6 +237,10 @@ SR_PRIV int sr_log(int loglevel, const char *format, ...)
        int ret;
        va_list args;
 
+       /* Only output messages of at least the selected loglevel(s). */
+       if (loglevel > cur_loglevel)
+               return SR_OK;
+
        va_start(args, format);
        ret = sr_log_cb(sr_log_cb_data, loglevel, format, args);
        va_end(args);
index 21205d941163d6544602e66b44a9d90d5f73fabc..065c25f67a355f23316bb5a186aad1300ec2be42 100644 (file)
@@ -109,20 +109,20 @@ static int modbus_serial_rtu_send(void *priv,
        uint8_t slave_addr = modbus->slave_addr;
        uint16_t crc;
 
-       result = serial_write_nonblocking(serial, &slave_addr, sizeof(slave_addr));
+       result = serial_write_blocking(serial, &slave_addr, sizeof(slave_addr), 0);
        if (result < 0)
-               return result;
+               return SR_ERR;
 
-       result = serial_write_nonblocking(serial, buffer, buffer_size);
+       result = serial_write_blocking(serial, buffer, buffer_size, 0);
        if (result < 0)
-               return result;
+               return SR_ERR;
 
        crc = modbus_serial_rtu_crc(0xFFFF, &slave_addr, sizeof(slave_addr));
        crc = modbus_serial_rtu_crc(crc, buffer, buffer_size);
 
-       result = serial_write_nonblocking(serial, &crc, sizeof(crc));
+       result = serial_write_blocking(serial, &crc, sizeof(crc), 0);
        if (result < 0)
-               return result;
+               return SR_ERR;
 
        return SR_OK;
 }
@@ -133,13 +133,13 @@ static int modbus_serial_rtu_read_begin(void *priv, uint8_t *function_code)
        uint8_t slave_addr;
        int ret;
 
-       ret = serial_read_blocking(modbus->serial, &slave_addr, 1, 100);
+       ret = serial_read_blocking(modbus->serial, &slave_addr, 1, 500);
        if (ret != 1 || slave_addr != modbus->slave_addr)
-               return ret;
+               return SR_ERR;
 
        ret = serial_read_blocking(modbus->serial, function_code, 1, 100);
        if (ret != 1)
-               return ret;
+               return SR_ERR;
 
        modbus->crc = modbus_serial_rtu_crc(0xFFFF, &slave_addr, sizeof(slave_addr));
        modbus->crc = modbus_serial_rtu_crc(modbus->crc, function_code, 1);
@@ -167,7 +167,7 @@ static int modbus_serial_rtu_read_end(void *priv)
 
        ret = serial_read_blocking(modbus->serial, &crc, sizeof(crc), 100);
        if (ret != 2)
-               return ret;
+               return SR_ERR;
 
        if (crc != modbus->crc) {
                sr_err("CRC error (0x%04X vs 0x%04X).", crc, modbus->crc);
index cd1764b842fd7c23d0e8fd2a91e3aaf762a3160c..6632cda68844fc631e8ac1143af6dd2b7119e5ab 100644 (file)
@@ -27,6 +27,8 @@
 
 #define LOG_PREFIX "output/analog"
 
+#define BIN_TO_DEC_DIGITS (log(2) / log(10))
+
 struct context {
        int num_enabled_channels;
        GPtrArray *channellist;
@@ -75,6 +77,9 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
 {
        struct context *ctx;
        const struct sr_datafeed_analog *analog;
+       const struct sr_datafeed_meta *meta;
+       const struct sr_config *src;
+       const struct sr_key_info *srci;
        struct sr_channel *ch;
        GSList *l;
        float *fdata;
@@ -94,6 +99,29 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
        case SR_DF_FRAME_END:
                *out = g_string_new("FRAME-END\n");
                break;
+       case SR_DF_META:
+               meta = packet->payload;
+               for (l = meta->config; l; l = l->next) {
+                       src = l->data;
+                       if (!(srci = sr_key_info_get(SR_KEY_CONFIG, src->key)))
+                               return SR_ERR;
+                       *out = g_string_sized_new(512);
+                       g_string_append(*out, "META ");
+                       g_string_append_printf(*out, "%s: ", srci->id);
+                       if (srci->datatype == SR_T_BOOL) {
+                               g_string_append_printf(*out, "%u",
+                                       g_variant_get_boolean(src->data));
+                       } else if (srci->datatype == SR_T_FLOAT) {
+                               g_string_append_printf(*out, "%f",
+                                       g_variant_get_double(src->data));
+                       } else if (srci->datatype == SR_T_UINT64) {
+                               g_string_append_printf(*out, "%"
+                                       G_GUINT64_FORMAT,
+                                       g_variant_get_uint64(src->data));
+                       }
+                       g_string_append(*out, "\n");
+               }
+               break;
        case SR_DF_ANALOG:
                analog = packet->payload;
                num_channels = g_slist_length(analog->meaning->channels);
@@ -104,15 +132,12 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                if ((ret = sr_analog_to_float(analog, fdata)) != SR_OK)
                        return ret;
                *out = g_string_sized_new(512);
-               if (analog->encoding->is_digits_decimal) {
-                       if (ctx->digits == DIGITS_ALL)
-                               digits = analog->encoding->digits;
-                       else
-                               digits = analog->spec->spec_digits;
-               } else {
-                       /* TODO we don't know how to print by number of bits yet. */
-                       digits = 6;
-               }
+               if (ctx->digits == DIGITS_ALL)
+                       digits = analog->encoding->digits;
+               else
+                       digits = analog->spec->spec_digits;
+               if (!analog->encoding->is_digits_decimal)
+                       digits = copysign(ceil(abs(digits) * BIN_TO_DEC_DIGITS), digits);
                gboolean si_friendly = sr_analog_si_prefix_friendly(analog->meaning->unit);
                sr_analog_unit_to_string(analog, &suffix);
                for (i = 0; i < analog->num_samples; i++) {
@@ -179,7 +204,7 @@ static int cleanup(struct sr_output *o)
 SR_PRIV struct sr_output_module output_analog = {
        .id = "analog",
        .name = "Analog",
-       .desc = "Analog data and types",
+       .desc = "ASCII analog data values and units",
        .exts = NULL,
        .flags = 0,
        .options = get_options,
index a2faa044ddcb72ca57df5bfda986107f01957ff6..9e04e768502a3e817048b8a7a34780e065c5b3ab 100644 (file)
@@ -199,7 +199,12 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                                        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;
+                                               /*
+                                                * Sample data lines have one character per bit and
+                                                * no separator between bytes. Align trigger marker
+                                                * to this layout.
+                                                */
+                                               offset = ctx->trigger;
                                                g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
                                                ctx->trigger = -1;
                                        }
@@ -272,7 +277,7 @@ static const struct sr_option *get_options(void)
 SR_PRIV struct sr_output_module output_ascii = {
        .id = "ascii",
        .name = "ASCII",
-       .desc = "ASCII art",
+       .desc = "ASCII art logic data",
        .exts = (const char*[]){"txt", NULL},
        .flags = 0,
        .options = get_options,
index f4d190107c1b747ba78b4b8e4827f01ee365e50b..7c8fa105e80eb7819d58dae990b785017eac304b 100644 (file)
@@ -45,7 +45,7 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
 SR_PRIV struct sr_output_module output_binary = {
        .id = "binary",
        .name = "Binary",
-       .desc = "Raw binary",
+       .desc = "Raw binary logic data",
        .exts = NULL,
        .flags = 0,
        .options = NULL,
index f486803305ea58b4bddc29f0f1975d73cf06a1fb..c3f2ef32dc26c53abf916c86b81d4c00eae07bcd 100644 (file)
@@ -168,6 +168,11 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                                        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) {
+                                               /*
+                                                * Sample data lines have one character per bit,
+                                                * plus one separator per byte. Align trigger marker
+                                                * to this layout.
+                                                */
                                                offset = ctx->trigger + ctx->trigger / 8;
                                                g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
                                                ctx->trigger = -1;
@@ -238,7 +243,7 @@ static const struct sr_option *get_options(void)
 SR_PRIV struct sr_output_module output_bits = {
        .id = "bits",
        .name = "Bits",
-       .desc = "0/1 digits",
+       .desc = "0/1 digits logic data",
        .exts = (const char*[]){"txt", NULL},
        .flags = 0,
        .options = get_options,
index 2793f97ac7aa00599ce441b2173f9b4907963470..adc6ba0d67d88df1188aa5cba9c4cb7e8b70bfd9 100644 (file)
@@ -187,7 +187,7 @@ static int cleanup(struct sr_output *o)
 SR_PRIV struct sr_output_module output_chronovu_la8 = {
        .id = "chronovu-la8",
        .name = "ChronoVu LA8",
-       .desc = "ChronoVu LA8 native file format",
+       .desc = "ChronoVu LA8 native file format data",
        .exts = (const char*[]){"kdt", NULL},
        .flags = 0,
        .options = NULL,
index 2be4db372f67101ae3241ede4d7d4c2e9ba3b27c..4752658716d1e827731c543411f40fa18893a9ff 100644 (file)
  * trigger: Whether or not to add a "trigger" column as the last column.
  *          Defaults to FALSE.
  *
- * dedup:   Don't output duplicate rows. Defaults to TRUE. If time is off, then
+ * dedup:   Don't output duplicate rows. Defaults to FALSE. If time is off, then
  *          this is forced to be off.
  */
 
-#include <math.h>
 #include <config.h>
+#include <math.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
@@ -216,7 +216,7 @@ static GString *gen_header(const struct sr_output *o,
        struct sr_channel *ch;
        GVariant *gvar;
        GString *header;
-       GSList *l;
+       GSList *channels, *l;
        unsigned int num_channels, i;
        uint64_t samplerate = 0, sr;
        char *samplerate_s;
@@ -256,18 +256,20 @@ static GString *gen_header(const struct sr_output *o,
                        ctx->title, ctime(&hdr->starttime.tv_sec));
 
                /* Columns / channels */
-               num_channels = g_slist_length(o->sdi->channels);
+               channels = o->sdi ? o->sdi->channels : NULL;
+               num_channels = g_slist_length(channels);
                g_string_append_printf(header, "%s Channels (%d/%d):",
                        ctx->comment, ctx->num_analog_channels +
                        ctx->num_logic_channels, num_channels);
-               for (l = o->sdi->channels; l; l = l->next) {
+               for (l = channels; l; l = l->next) {
                        ch = l->data;
                        if (ch->enabled)
                                g_string_append_printf(header, " %s,", ch->name);
                }
-               if (o->sdi->channels)
+               if (channels) {
                        /* Drop last separator. */
                        g_string_truncate(header, header->len - 1);
+               }
                g_string_append_printf(header, "\n");
                if (samplerate != 0) {
                        samplerate_s = sr_samplerate_string(samplerate);
@@ -308,10 +310,13 @@ static void process_analog(struct context *ctx,
                           const struct sr_datafeed_analog *analog)
 {
        int ret;
-       unsigned int i, j, c, num_channels;
+       size_t num_rcvd_ch, num_have_ch;
+       size_t idx_have, idx_smpl, idx_rcvd;
+       size_t idx_send;
        struct sr_analog_meaning *meaning;
        GSList *l;
        float *fdata = NULL;
+       struct sr_channel *ch;
 
        if (!ctx->analog_samples) {
                ctx->analog_samples = g_malloc(analog->num_samples
@@ -324,31 +329,34 @@ static void process_analog(struct context *ctx,
                        ctx->num_samples, analog->num_samples);
 
        meaning = analog->meaning;
-       num_channels = g_slist_length(meaning->channels);
-       ctx->channels_seen += num_channels;
-       sr_dbg("Processing packet of %u analog channels", num_channels);
-       fdata = g_malloc(analog->num_samples * num_channels * sizeof(float));
+       num_rcvd_ch = g_slist_length(meaning->channels);
+       ctx->channels_seen += num_rcvd_ch;
+       sr_dbg("Processing packet of %zu analog channels", num_rcvd_ch);
+       fdata = g_malloc(analog->num_samples * num_rcvd_ch * sizeof(float));
        if ((ret = sr_analog_to_float(analog, fdata)) != SR_OK)
                sr_warn("Problems converting data to floating point values.");
 
-       for (i = 0; i < ctx->num_analog_channels + ctx->num_logic_channels; i++) {
-               if (ctx->channels[i].ch->type == SR_CHANNEL_ANALOG) {
-                       sr_dbg("Looking for channel %s",
-                              ctx->channels[i].ch->name);
-                       for (l = meaning->channels, c = 0; l; l = l->next, c++) {
-                               struct sr_channel *ch = l->data;
-                               sr_dbg("Checking %s", ch->name);
-                               if (ctx->channels[i].ch == l->data) {
-                                       if (ctx->label_do && !ctx->label_names) {
-                                               sr_analog_unit_to_string(analog,
-                                                       &ctx->channels[i].label);
-                                       }
-                                       for (j = 0; j < analog->num_samples; j++)
-                                               ctx->analog_samples[j * ctx->num_analog_channels + i] = fdata[j * num_channels + c];
-                                       break;
-                               }
+       num_have_ch = ctx->num_analog_channels + ctx->num_logic_channels;
+       idx_send = 0;
+       for (idx_have = 0; idx_have < num_have_ch; idx_have++) {
+               if (ctx->channels[idx_have].ch->type != SR_CHANNEL_ANALOG)
+                       continue;
+               sr_dbg("Looking for channel %s",
+                      ctx->channels[idx_have].ch->name);
+               for (l = meaning->channels, idx_rcvd = 0; l; l = l->next, idx_rcvd++) {
+                       ch = l->data;
+                       sr_dbg("Checking %s", ch->name);
+                       if (ctx->channels[idx_have].ch != ch)
+                               continue;
+                       if (ctx->label_do && !ctx->label_names) {
+                               sr_analog_unit_to_string(analog,
+                                       &ctx->channels[idx_have].label);
                        }
+                       for (idx_smpl = 0; idx_smpl < analog->num_samples; idx_smpl++)
+                               ctx->analog_samples[idx_smpl * ctx->num_analog_channels + idx_send] = fdata[idx_smpl * num_rcvd_ch + idx_rcvd];
+                       break;
                }
+               idx_send++;
        }
        g_free(fdata);
 }
@@ -578,7 +586,7 @@ static int receive(const struct sr_output *o,
                break;
        case SR_DF_FRAME_BEGIN:
                *out = g_string_new(ctx->frame);
-               /* And then fall through to... */
+               /* Fallthrough */
        case SR_DF_END:
                /* Got to end of frame/session with part of the data. */
                if (ctx->channels_seen)
@@ -623,7 +631,7 @@ static struct sr_option options[] = {
        {"scale", "scale", "Scale gnuplot graphs", NULL, NULL},
        {"value", "Value separator", "Character to print between values", NULL, NULL},
        {"record", "Record separator", "String to print between records", NULL, NULL},
-       {"frame", "Frame seperator", "String to print between frames", NULL, NULL},
+       {"frame", "Frame separator", "String to print between frames", NULL, NULL},
        {"comment", "Comment start string", "String used at start of comment lines", NULL, NULL},
        {"header", "Output header", "Output header comment with capture metdata", NULL, NULL},
        {"label", "Label values", "Type of column labels", NULL, NULL},
@@ -635,6 +643,8 @@ static struct sr_option options[] = {
 
 static const struct sr_option *get_options(void)
 {
+       GSList *l = NULL;
+
        if (!options[0].def) {
                options[0].def = g_variant_ref_sink(g_variant_new_string(""));
                options[1].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
@@ -644,9 +654,13 @@ static const struct sr_option *get_options(void)
                options[5].def = g_variant_ref_sink(g_variant_new_string(";"));
                options[6].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
                options[7].def = g_variant_ref_sink(g_variant_new_string("units"));
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("units")));
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("channel")));
+               l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("off")));
+               options[7].values = l;
                options[8].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
                options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
-               options[10].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
+               options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
        }
 
        return options;
index 5d9e0f556eebe8e3a52401d3a47af148b466dd93..a1c09f81b4c4e4abe31f1ed4a13ddc8d877d49ef 100644 (file)
@@ -180,8 +180,13 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                                        /* 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;
+                                       if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
+                                               /*
+                                                * Sample data lines have one character per nibble,
+                                                * plus one separator per byte. Align trigger marker
+                                                * to this layout.
+                                                */
+                                               offset = ctx->trigger / 4 + ctx->trigger / 8;
                                                g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
                                                ctx->trigger = -1;
                                        }
@@ -252,7 +257,7 @@ static const struct sr_option *get_options(void)
 SR_PRIV struct sr_output_module output_hex = {
        .id = "hex",
        .name = "Hexadecimal",
-       .desc = "Hexadecimal digits",
+       .desc = "Hexadecimal digits logic data",
        .exts = (const char*[]){"txt", NULL},
        .flags = 0,
        .options = get_options,
diff --git a/src/output/null.c b/src/output/null.c
new file mode 100644 (file)
index 0000000..94faf4b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/null"
+
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
+               GString **out)
+{
+       (void)o;
+       (void)packet;
+
+       *out = NULL;
+
+       return SR_OK;
+}
+
+SR_PRIV struct sr_output_module output_null = {
+       .id = "null",
+       .name = "Null output",
+       .desc = "Null output (discards all data)",
+       .exts = NULL,
+       .flags = 0,
+       .options = NULL,
+       .receive = receive,
+};
index 719434432ea19edab774a824263c1ddcbf6693b9..2129658a346deaa1a48abcd6aab20d1c4e103619 100644 (file)
@@ -149,7 +149,7 @@ static int cleanup(struct sr_output *o)
 SR_PRIV struct sr_output_module output_ols = {
        .id = "ols",
        .name = "OLS",
-       .desc = "OpenBench Logic Sniffer",
+       .desc = "OpenBench Logic Sniffer data",
        .exts = (const char*[]){"ols", NULL},
        .flags = 0,
        .options = NULL,
index efb205c68192d8d020f590c86b04841cc9e3c886..c6c2d7206cbb2fce828177546659a13284127258 100644 (file)
@@ -64,6 +64,7 @@ extern SR_PRIV struct sr_output_module output_csv;
 extern SR_PRIV struct sr_output_module output_analog;
 extern SR_PRIV struct sr_output_module output_srzip;
 extern SR_PRIV struct sr_output_module output_wav;
+extern SR_PRIV struct sr_output_module output_null;
 /* @endcond */
 
 static const struct sr_output_module *output_module_list[] = {
@@ -78,6 +79,7 @@ static const struct sr_output_module *output_module_list[] = {
        &output_analog,
        &output_srzip,
        &output_wav,
+       &output_null,
        NULL,
 };
 
index 249fe057994aeeaf80b9106ae3b041a8c1fb7b5e..93c43d32eb87bd02e16de1ad21a2673684dc7150 100644 (file)
@@ -150,6 +150,7 @@ static int zip_create(const struct sr_output *o)
                if (!ch->enabled)
                        continue;
 
+               s = NULL;
                switch (ch->type) {
                case SR_CHANNEL_LOGIC:
                        s = g_strdup_printf("probe%d", ch->index + 1);
@@ -160,8 +161,10 @@ static int zip_create(const struct sr_output *o)
                        index++;
                        break;
                }
-               g_key_file_set_string(meta, devgroup, s, ch->name);
-               g_free(s);
+               if (s) {
+                       g_key_file_set_string(meta, devgroup, s, ch->name);
+                       g_free(s);
+               }
        }
 
        metabuf = g_key_file_to_data(meta, &metalen, NULL);
@@ -379,12 +382,13 @@ static int zip_append_analog(const struct sr_output *o,
        analogsrc = zip_source_buffer(archive, chunkbuf, chunksize, FALSE);
        chunkname = g_strdup_printf("%s-%u", basename, next_chunk_num);
        i = zip_add(archive, chunkname, analogsrc);
-       g_free(chunkname);
        if (i < 0) {
                sr_err("Failed to add chunk '%s': %s", chunkname, zip_strerror(archive));
+               g_free(chunkname);
                zip_source_free(analogsrc);
                goto err_free_chunkbuf;
        }
+       g_free(chunkname);
        if (zip_close(archive) < 0) {
                sr_err("Error saving session file: %s", zip_strerror(archive));
                goto err_free_chunkbuf;
@@ -464,9 +468,6 @@ static struct sr_option options[] = {
 
 static const struct sr_option *get_options(void)
 {
-       if (!options[0].def)
-               options[0].def = g_variant_ref_sink(g_variant_new_string(""));
-
        return options;
 }
 
@@ -475,7 +476,6 @@ static int cleanup(struct sr_output *o)
        struct out_context *outc;
 
        outc = o->priv;
-       g_variant_unref(options[0].def);
        g_free(outc->analog_index_map);
        g_free(outc->filename);
        g_free(outc);
@@ -487,7 +487,7 @@ static int cleanup(struct sr_output *o)
 SR_PRIV struct sr_output_module output_srzip = {
        .id = "srzip",
        .name = "srzip",
-       .desc = "srzip session file",
+       .desc = "srzip session file format data",
        .exts = (const char*[]){"sr", NULL},
        .flags = SR_OUTPUT_INTERNAL_IO_HANDLING,
        .options = get_options,
index 43020029ba7ab511d4e0c36437afa85e7c061b19..f90db498e5710d1d595bfab3c5b27818c7b2d202 100644 (file)
@@ -96,7 +96,7 @@ static GString *gen_header(const struct sr_output *o)
        /* timestamp */
        t = time(NULL);
        timestamp = g_strdup(ctime(&t));
-       timestamp[strlen(timestamp)-1] = 0;
+       timestamp[strlen(timestamp) - 1] = 0;
        g_string_printf(header, "$date %s $end\n", timestamp);
        g_free(timestamp);
 
@@ -136,7 +136,7 @@ static GString *gen_header(const struct sr_output *o)
        g_string_append_printf(header, "$scope module %s $end\n", PACKAGE_NAME);
 
        /* Wires / channels */
-       for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
+       for (i = 0, l = o->sdi->channels; l; l = l->next) {
                ch = l->data;
                if (ch->type != SR_CHANNEL_LOGIC)
                        continue;
@@ -144,6 +144,7 @@ static GString *gen_header(const struct sr_output *o)
                        continue;
                g_string_append_printf(header, "$var wire 1 %c %s $end\n",
                                (char)('!' + i), ch->name);
+               i++;
        }
 
        g_string_append(header, "$upscope $end\n$enddefinitions $end\n");
@@ -199,7 +200,16 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                        timestamp_written = FALSE;
 
                        for (p = 0; p < ctx->num_enabled_channels; p++) {
-                               index = ctx->channel_index[p];
+                               /*
+                                * TODO Check whether the mapping from
+                                * data image positions to channel numbers
+                                * is required. Experiments suggest that
+                                * the data image "is dense", and packs
+                                * bits of enabled channels, and leaves no
+                                * room for positions of disabled channels.
+                                */
+                               /* index = ctx->channel_index[p]; */
+                               index = p;
 
                                curbit = ((unsigned)sample[index / 8]
                                                >> (index % 8)) & 1;
@@ -260,7 +270,7 @@ static int cleanup(struct sr_output *o)
 struct sr_output_module output_vcd = {
        .id = "vcd",
        .name = "VCD",
-       .desc = "Value Change Dump",
+       .desc = "Value Change Dump data",
        .exts = (const char*[]){"vcd", NULL},
        .flags = 0,
        .options = NULL,
index 6984c03d29b5185c61c41cc34325772d964249c2..732fe976f0df420c2cae8b8e4b9a935ca0201b45 100644 (file)
@@ -186,9 +186,9 @@ static GString *gen_header(const struct sr_output *o)
  */
 static void float_to_le(uint8_t *buf, float value)
 {
-       char *old;
+       uint8_t *old;
 
-       old = (char *)&value;
+       old = (uint8_t *)&value;
 #ifdef WORDS_BIGENDIAN
        buf[0] = old[3];
        buf[1] = old[2];
@@ -219,9 +219,10 @@ static int check_chanbuf_size(const struct sr_output *o)
                                /* Nothing in all the buffers yet. */
                                size = -1;
                                break;
-                       } else
+                       } else {
                                /* New high water mark. */
                                size = outc->chanbuf_used[i];
+                       }
                } else if (outc->chanbuf_used[i] != size) {
                        /* All channel buffers are not equally full yet. */
                        size = -1;
@@ -265,8 +266,9 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                if (!outc->header_done) {
                        *out = gen_header(o);
                        outc->header_done = TRUE;
-               } else
+               } else {
                        *out = g_string_sized_new(512);
+               }
 
                analog = packet->payload;
                num_samples = analog->num_samples;
@@ -305,7 +307,7 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                                idx = chan_idx[j];
                                buf = outc->chanbuf[idx] + outc->chanbuf_used[idx]++ * 4;
                                f = data[i * num_channels + j];
-                               if (outc->scale != 0.0)
+                               if (outc->scale != 1.0)
                                        f /= outc->scale;
                                float_to_le(buf, f);
                        }
@@ -338,7 +340,7 @@ static struct sr_option options[] = {
 static const struct sr_option *get_options(void)
 {
        if (!options[0].def)
-               options[0].def = g_variant_ref_sink(g_variant_new_double(0.0));
+               options[0].def = g_variant_ref_sink(g_variant_new_double(1.0));
 
        return options;
 }
@@ -365,7 +367,7 @@ static int cleanup(struct sr_output *o)
 SR_PRIV struct sr_output_module output_wav = {
        .id = "wav",
        .name = "WAV",
-       .desc = "Microsoft WAV file format",
+       .desc = "Microsoft WAV file format data",
        .exts = (const char*[]){"wav", NULL},
        .flags = 0,
        .options = get_options,
index 634159308ef5c057a51447e4327cc8d93c005065..f2ef8c1587bfb5e0c69a368154fd9bb5d834d805 100644 (file)
  * Access to resource files.
  */
 
-/** Retrieve the size of the open stream @a file.
+/**
+ * Get a list of paths where we look for resource (e.g. firmware) files.
+ *
+ * @param res_type The type of resource to get the search paths for.
+ *
+ * @return List of strings that must be freed after use, including the strings.
+ *
+ * @since 0.6.0
+ */
+SR_API GSList *sr_resourcepaths_get(int res_type)
+{
+       const char *subdir = NULL;
+       GSList *l = NULL;
+       const char *env;
+       const char *const *datadirs;
+
+       if (res_type == SR_RESOURCE_FIRMWARE) {
+               subdir = "sigrok-firmware";
+
+               env = g_getenv("SIGROK_FIRMWARE_DIR");
+               if (env)
+                       l = g_slist_append(l, g_strdup(env));
+       }
+
+       l = g_slist_append(l, g_build_filename(g_get_user_data_dir(), subdir, NULL));
+
+#ifdef FIRMWARE_DIR
+       if (res_type == SR_RESOURCE_FIRMWARE) {
+               /*
+                * Scan the hard-coded directory before the system directories to
+                * avoid picking up possibly outdated files from a system install.
+                */
+               l = g_slist_append(l, g_strdup(FIRMWARE_DIR));
+       }
+#endif
+
+       datadirs = g_get_system_data_dirs();
+       while (*datadirs)
+               l = g_slist_append(l, g_build_filename(*datadirs++, subdir, NULL));
+
+       return l;
+}
+
+/**
+ * Retrieve the size of the open stream @a file.
  *
  * This function only works on seekable streams. However, the set of seekable
  * streams is generally congruent with the set of streams that have a size.
@@ -82,7 +126,11 @@ static FILE *try_open_file(const char *datadir, const char *subdir,
        char *filename;
        FILE *file;
 
-       filename = g_build_filename(datadir, subdir, name, NULL);
+       if (subdir)
+               filename = g_build_filename(datadir, subdir, name, NULL);
+       else
+               filename = g_build_filename(datadir, name, NULL);
+
        file = g_fopen(filename, "rb");
 
        if (file)
@@ -98,42 +146,27 @@ static FILE *try_open_file(const char *datadir, const char *subdir,
 static int resource_open_default(struct sr_resource *res,
                const char *name, void *cb_data)
 {
+       GSList *paths, *p = NULL;
        int64_t filesize;
-#ifdef FIRMWARE_DIR
-       const char *builtindir;
-#endif
-       const char *subdir;
-       const char *const *datadirs;
-       FILE *file;
+       FILE *file = NULL;
 
        (void)cb_data;
 
-       switch (res->type) {
-       case SR_RESOURCE_FIRMWARE:
-#ifdef FIRMWARE_DIR
-               builtindir = FIRMWARE_DIR;
-#endif
-               subdir = "sigrok-firmware";
-               break;
-       default:
+       paths = sr_resourcepaths_get(res->type);
+
+       /* Currently, the enum only defines SR_RESOURCE_FIRMWARE. */
+       if (res->type != SR_RESOURCE_FIRMWARE) {
                sr_err("%s: unknown type %d.", __func__, res->type);
                return SR_ERR_ARG;
        }
 
-       file = try_open_file(g_get_user_data_dir(), subdir, name);
-       /*
-        * Scan the hard-coded directory before the system directories to
-        * avoid picking up possibly outdated files from a system install.
-        */
-#ifdef FIRMWARE_DIR
-       if (!file)
-               file = try_open_file(builtindir, "", name);
-#endif
-       if (!file) {
-               datadirs = g_get_system_data_dirs();
-               while (*datadirs && !file)
-                       file = try_open_file(*datadirs++, subdir, name);
+       p = paths;
+       while (p && !file) {
+               file = try_open_file((const char *)(p->data), NULL, name);
+               p = p->next;
        }
+       g_slist_free_full(paths, g_free);
+
        if (!file) {
                sr_dbg("Failed to locate '%s'.", name);
                return SR_ERR;
index 932300624e437ddaa063f3880da0b2af4c68181c..e55d36f1513880d84a8d6119a92c59abc507a0d1 100644 (file)
@@ -99,6 +99,8 @@ struct sr_scpi_dev_inst {
        void *priv;
        /* Only used for quirk workarounds, notably the Rigol DS1000 series. */
        uint64_t firmware_version;
+       GMutex scpi_mutex;
+       char *actual_channel_name;
 };
 
 SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
@@ -148,11 +150,15 @@ SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
 SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info);
 
 SR_PRIV const char *sr_vendor_alias(const char *raw_vendor);
-SR_PRIV const char *scpi_cmd_get(const struct scpi_command *cmdtable, int command);
-SR_PRIV int scpi_cmd(const struct sr_dev_inst *sdi,
-               const struct scpi_command *cmdtable, int command, ...);
-SR_PRIV int scpi_cmd_resp(const struct sr_dev_inst *sdi,
+SR_PRIV const char *sr_scpi_cmd_get(const struct scpi_command *cmdtable,
+               int command);
+SR_PRIV int sr_scpi_cmd(const struct sr_dev_inst *sdi,
                const struct scpi_command *cmdtable,
+               int channel_command, const char *channel_name,
+               int command, ...);
+SR_PRIV int sr_scpi_cmd_resp(const struct sr_dev_inst *sdi,
+               const struct scpi_command *cmdtable,
+               int channel_command, const char *channel_name,
                GVariant **gvar, const GVariantType *gvtype, int command, ...);
 
 #endif
diff --git a/src/scpi/helpers.c b/src/scpi/helpers.c
deleted file mode 100644 (file)
index dc19c3a..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2015 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 <config.h>
-#include <strings.h>
-#include <libsigrok/libsigrok.h>
-#include "libsigrok-internal.h"
-#include "scpi.h"
-
-#define LOG_PREFIX "scpi/helpers"
-
-static const char *scpi_vendors[][2] = {
-       { "HEWLETT-PACKARD", "HP" },
-       { "Agilent Technologies", "Agilent" },
-       { "RIGOL TECHNOLOGIES", "Rigol" },
-       { "PHILIPS", "Philips" },
-       { "CHROMA", "Chroma" },
-       { "Chroma ATE", "Chroma" },
-};
-
-SR_PRIV const char *sr_vendor_alias(const char *raw_vendor)
-{
-       unsigned int i;
-
-       for (i = 0; i < ARRAY_SIZE(scpi_vendors); i++) {
-               if (!g_ascii_strcasecmp(raw_vendor, scpi_vendors[i][0]))
-                       return scpi_vendors[i][1];
-       }
-
-       return raw_vendor;
-}
-
-SR_PRIV const char *scpi_cmd_get(const struct scpi_command *cmdtable, int command)
-{
-       unsigned int i;
-       const char *cmd;
-
-       if (!cmdtable)
-               return NULL;
-
-       cmd = NULL;
-       for (i = 0; cmdtable[i].string; i++) {
-               if (cmdtable[i].command == command) {
-                       cmd = cmdtable[i].string;
-                       break;
-               }
-       }
-
-       return cmd;
-}
-
-SR_PRIV int scpi_cmd(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable,
-               int command, ...)
-{
-       struct sr_scpi_dev_inst *scpi;
-       va_list args;
-       int ret;
-       const char *cmd;
-
-       if (!(cmd = scpi_cmd_get(cmdtable, command))) {
-               /* Device does not implement this command, that's OK. */
-               return SR_OK;
-       }
-
-       scpi = sdi->conn;
-       va_start(args, command);
-       ret = sr_scpi_send_variadic(scpi, cmd, args);
-       va_end(args);
-
-       return ret;
-}
-
-SR_PRIV int scpi_cmd_resp(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable,
-               GVariant **gvar, const GVariantType *gvtype, int command, ...)
-{
-       struct sr_scpi_dev_inst *scpi;
-       va_list args;
-       double d;
-       int ret;
-       char *s;
-       const char *cmd;
-
-       if (!(cmd = scpi_cmd_get(cmdtable, command))) {
-               /* Device does not implement this command. */
-               return SR_ERR_NA;
-       }
-
-       scpi = sdi->conn;
-       va_start(args, command);
-       ret = sr_scpi_send_variadic(scpi, cmd, args);
-       va_end(args);
-       if (ret != SR_OK)
-               return ret;
-
-       /* Straight SCPI getters to GVariant types. */
-       if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_BOOLEAN)) {
-               if ((ret = sr_scpi_get_string(scpi, NULL, &s)) != SR_OK)
-                       return ret;
-               if (!g_ascii_strcasecmp(s, "ON") || !g_ascii_strcasecmp(s, "1")
-                               || !g_ascii_strcasecmp(s, "YES"))
-                       *gvar = g_variant_new_boolean(TRUE);
-               else if (!g_ascii_strcasecmp(s, "OFF") || !g_ascii_strcasecmp(s, "0")
-                               || !g_ascii_strcasecmp(s, "NO"))
-                       *gvar = g_variant_new_boolean(FALSE);
-               else
-                       ret = SR_ERR;
-               g_free(s);
-       } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_DOUBLE)) {
-               if ((ret = sr_scpi_get_double(scpi, NULL, &d)) == SR_OK)
-                       *gvar = g_variant_new_double(d);
-       } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_STRING)) {
-               if ((ret = sr_scpi_get_string(scpi, NULL, &s)) == SR_OK)
-                       *gvar = g_variant_new_string(s);
-       } else {
-               sr_err("Unable to convert to desired GVariant type.");
-               ret = SR_ERR_NA;
-       }
-
-       return ret;
-}
index 48e6a48495c9bfaba591147a12f715b67fe0b493..3700478692229e7a78813c4058fe608beac93ab7 100644 (file)
@@ -2,6 +2,7 @@
  * This file is part of the libsigrok project.
  *
  * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
+ * Copyright (C) 2015 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
 #define SCPI_READ_RETRIES 100
 #define SCPI_READ_RETRY_TIMEOUT_US (10 * 1000)
 
+static const char *scpi_vendors[][2] = {
+       { "HEWLETT-PACKARD", "HP" },
+       { "Agilent Technologies", "Agilent" },
+       { "RIGOL TECHNOLOGIES", "Rigol" },
+       { "PHILIPS", "Philips" },
+       { "CHROMA", "Chroma" },
+       { "Chroma ATE", "Chroma" },
+};
+
 /**
  * Parse a string representation of a boolean-like value into a gboolean.
  * Similar to sr_parse_boolstring but rejects strings which do not represent
@@ -122,6 +132,183 @@ static struct sr_dev_inst *sr_scpi_scan_resource(struct drv_context *drvc,
        return sdi;
 }
 
+/**
+ * Send a SCPI command with a variadic argument list without mutex.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ * @param format Format string.
+ * @param args Argument list.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+static int 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 = sr_vsnprintf_ascii(NULL, 0, format, args_copy);
+       va_end(args_copy);
+
+       /* Allocate buffer and write out command. */
+       buf = g_malloc0(len + 2);
+       sr_vsprintf_ascii(buf, format, args);
+       if (buf[len - 1] != '\n')
+               buf[len] = '\n';
+
+       /* Send command. */
+       ret = scpi->send(scpi->priv, buf);
+
+       /* Free command buffer. */
+       g_free(buf);
+
+       return ret;
+}
+
+/**
+ * Send a SCPI command without mutex.
+ *
+ * @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.
+ */
+static int scpi_send(struct sr_scpi_dev_inst *scpi, const char *format, ...)
+{
+       va_list args;
+       int ret;
+
+       va_start(args, format);
+       ret = scpi_send_variadic(scpi, format, args);
+       va_end(args);
+
+       return ret;
+}
+
+/**
+ * Send data to SCPI device without mutex.
+ *
+ * TODO: This is only implemented in TcpRaw, but never used.
+ * TODO: Use Mutex at all?
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param buf Buffer with data to send.
+ * @param len Number of bytes to send.
+ *
+ * @return Number of bytes read, or SR_ERR upon failure.
+ */
+static int scpi_write_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen)
+{
+       return scpi->write_data(scpi->priv, buf, maxlen);
+}
+
+/**
+ * Read part of a response from SCPI device without mutex.
+ *
+ * @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.
+ */
+static int scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen)
+{
+       return scpi->read_data(scpi->priv, buf, maxlen);
+}
+
+/**
+ * Do a non-blocking read of up to the allocated length, and
+ * check if a timeout has occured, without mutex.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param response Buffer to which the response is appended.
+ * @param abs_timeout_us Absolute timeout in microseconds
+ *
+ * @return read length on success, SR_ERR* on failure.
+ */
+static int scpi_read_response(struct sr_scpi_dev_inst *scpi,
+                               GString *response, gint64 abs_timeout_us)
+{
+       int len, space;
+
+       space = response->allocated_len - response->len;
+       len = scpi->read_data(scpi->priv, &response->str[response->len], space);
+
+       if (len < 0) {
+               sr_err("Incompletely read SCPI response.");
+               return SR_ERR;
+       }
+
+       if (len > 0) {
+               g_string_set_size(response, response->len + len);
+               return len;
+       }
+
+       if (g_get_monotonic_time() > abs_timeout_us) {
+               sr_err("Timed out waiting for SCPI response.");
+               return SR_ERR_TIMEOUT;
+       }
+
+       return 0;
+}
+
+/**
+ * Send a SCPI command, receive the reply and store the reply in
+ * scpi_response, without mutex.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device.
+ * @param scpi_response Pointer where to store the SCPI response.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+static int scpi_get_data(struct sr_scpi_dev_inst *scpi,
+                               const char *command, GString **scpi_response)
+{
+       int ret;
+       GString *response;
+       int space;
+       gint64 timeout;
+
+       /* Optionally send caller provided command. */
+       if (command) {
+               if (scpi_send(scpi, command) != SR_OK)
+                       return SR_ERR;
+       }
+
+       /* Initiate SCPI read operation. */
+       if (sr_scpi_read_begin(scpi) != SR_OK)
+               return SR_ERR;
+
+       /* Keep reading until completion or until timeout. */
+       timeout = g_get_monotonic_time() + scpi->read_timeout_us;
+
+       response = *scpi_response;
+
+       while (!sr_scpi_read_complete(scpi)) {
+               /* Resize the buffer when free space drops below a threshold. */
+               space = response->allocated_len - response->len;
+               if (space < 128) {
+                       int oldlen = response->len;
+                       g_string_set_size(response, oldlen + 1024);
+                       g_string_set_size(response, oldlen);
+               }
+
+               /* Read another chunk of the response. */
+               ret = scpi_read_response(scpi, response, timeout);
+
+               if (ret < 0)
+                       return ret;
+               if (ret > 0)
+                       timeout = g_get_monotonic_time() + scpi->read_timeout_us;
+       }
+
+       return SR_OK;
+}
+
 SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
                struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi))
 {
@@ -214,6 +401,8 @@ SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
  */
 SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi)
 {
+       g_mutex_init(&scpi->scpi_mutex);
+
        return scpi->open(scpi);
 }
 
@@ -268,7 +457,9 @@ SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi,
        int ret;
 
        va_start(args, format);
-       ret = sr_scpi_send_variadic(scpi, format, args);
+       g_mutex_lock(&scpi->scpi_mutex);
+       ret = scpi_send_variadic(scpi, format, args);
+       g_mutex_unlock(&scpi->scpi_mutex);
        va_end(args);
 
        return ret;
@@ -286,26 +477,11 @@ SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi,
 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_malloc0(len + 2);
-       vsprintf(buf, format, args);
-       if (buf[len - 1] != '\n')
-               buf[len] = '\n';
-
-       /* Send command. */
-       ret = scpi->send(scpi->priv, buf);
+       int ret;
 
-       /* Free command buffer. */
-       g_free(buf);
+       g_mutex_lock(&scpi->scpi_mutex);
+       ret = scpi_send_variadic(scpi, format, args);
+       g_mutex_unlock(&scpi->scpi_mutex);
 
        return ret;
 }
@@ -334,12 +510,21 @@ 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)
 {
-       return scpi->read_data(scpi->priv, buf, maxlen);
+       int ret;
+
+       g_mutex_lock(&scpi->scpi_mutex);
+       ret = scpi_read_data(scpi, buf, maxlen);
+       g_mutex_unlock(&scpi->scpi_mutex);
+
+       return ret;
 }
 
 /**
  * Send data to SCPI device.
  *
+ * TODO: This is only implemented in TcpRaw, but never used.
+ * TODO: Use Mutex at all?
+ *
  * @param scpi Previously initialised SCPI device structure.
  * @param buf Buffer with data to send.
  * @param len Number of bytes to send.
@@ -349,7 +534,13 @@ SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi,
 SR_PRIV int sr_scpi_write_data(struct sr_scpi_dev_inst *scpi,
                        char *buf, int maxlen)
 {
-       return scpi->write_data(scpi->priv, buf, maxlen);
+       int ret;
+
+       g_mutex_lock(&scpi->scpi_mutex);
+       ret = scpi_write_data(scpi, buf, maxlen);
+       g_mutex_unlock(&scpi->scpi_mutex);
+
+       return ret;
 }
 
 /**
@@ -373,7 +564,14 @@ 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)
 {
-       return scpi->close(scpi);
+       int ret;
+
+       g_mutex_lock(&scpi->scpi_mutex);
+       ret = scpi->close(scpi);
+       g_mutex_unlock(&scpi->scpi_mutex);
+       g_mutex_clear(&scpi->scpi_mutex);
+
+       return ret;
 }
 
 /**
@@ -389,6 +587,7 @@ SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi)
 
        scpi->free(scpi->priv);
        g_free(scpi->priv);
+       g_free(scpi->actual_channel_name);
        g_free(scpi);
 }
 
@@ -442,71 +641,25 @@ SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
 SR_PRIV int sr_scpi_read_response(struct sr_scpi_dev_inst *scpi,
                                  GString *response, gint64 abs_timeout_us)
 {
-       int len, space;
-
-       space = response->allocated_len - response->len;
-       len = sr_scpi_read_data(scpi, &response->str[response->len], space);
-
-       if (len < 0) {
-               sr_err("Incompletely read SCPI response.");
-               return SR_ERR;
-       }
-
-       if (len > 0) {
-               g_string_set_size(response, response->len + len);
-               return len;
-       }
+       int ret;
 
-       if (g_get_monotonic_time() > abs_timeout_us) {
-               sr_err("Timed out waiting for SCPI response.");
-               return SR_ERR_TIMEOUT;
-       }
+       g_mutex_lock(&scpi->scpi_mutex);
+       ret = scpi_read_response(scpi, response, abs_timeout_us);
+       g_mutex_unlock(&scpi->scpi_mutex);
 
-       return 0;
+       return ret;
 }
 
 SR_PRIV int sr_scpi_get_data(struct sr_scpi_dev_inst *scpi,
                             const char *command, GString **scpi_response)
 {
        int ret;
-       GString *response;
-       int space;
-       gint64 timeout;
 
-       /* Optionally send caller provided command. */
-       if (command) {
-               if (sr_scpi_send(scpi, command) != SR_OK)
-                       return SR_ERR;
-       }
-
-       /* Initiate SCPI read operation. */
-       if (sr_scpi_read_begin(scpi) != SR_OK)
-               return SR_ERR;
-
-       /* Keep reading until completion or until timeout. */
-       timeout = g_get_monotonic_time() + scpi->read_timeout_us;
-
-       response = *scpi_response;
+       g_mutex_lock(&scpi->scpi_mutex);
+       ret = scpi_get_data(scpi, command, scpi_response);
+       g_mutex_unlock(&scpi->scpi_mutex);
 
-       while (!sr_scpi_read_complete(scpi)) {
-               /* Resize the buffer when free space drops below a threshold. */
-               space = response->allocated_len - response->len;
-               if (space < 128) {
-                       int oldlen = response->len;
-                       g_string_set_size(response, oldlen + 1024);
-                       g_string_set_size(response, oldlen);
-               }
-
-               /* Read another chunk of the response. */
-               ret = sr_scpi_read_response(scpi, response, timeout);
-
-               if (ret < 0)
-                       return ret;
-               if (ret > 0)
-                       timeout = g_get_monotonic_time() + scpi->read_timeout_us;
-       }
-
-       return SR_OK;
+       return ret;
 }
 
 /**
@@ -627,7 +780,7 @@ SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
        if (ret != SR_OK && !response)
                return ret;
 
-       if (sr_atod(response, scpi_response) == SR_OK)
+       if (sr_atod_ascii(response, scpi_response) == SR_OK)
                ret = SR_OK;
        else
                ret = SR_ERR_DATA;
@@ -651,6 +804,7 @@ SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi)
        gboolean opc;
 
        for (i = 0; i < SCPI_READ_RETRIES; i++) {
+               opc = FALSE;
                sr_scpi_get_bool(scpi, SCPI_CMD_OPC, &opc);
                if (opc)
                        return SR_OK;
@@ -796,12 +950,18 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
        long datalen;
        gint64 timeout;
 
+       g_mutex_lock(&scpi->scpi_mutex);
+
        if (command)
-               if (sr_scpi_send(scpi, command) != SR_OK)
+               if (scpi_send(scpi, command) != SR_OK) {
+                       g_mutex_unlock(&scpi->scpi_mutex);
                        return SR_ERR;
+               }
 
-       if (sr_scpi_read_begin(scpi) != SR_OK)
+       if (sr_scpi_read_begin(scpi) != SR_OK) {
+               g_mutex_unlock(&scpi->scpi_mutex);
                return SR_ERR;
+       }
 
        /*
         * Assume an initial maximum length, optionally gets adjusted below.
@@ -815,8 +975,9 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
 
        /* Get (the first chunk of) the response. */
        while (response->len < 2) {
-               ret = sr_scpi_read_response(scpi, response, timeout);
+               ret = scpi_read_response(scpi, response, timeout);
                if (ret < 0) {
+                       g_mutex_unlock(&scpi->scpi_mutex);
                        g_string_free(response, TRUE);
                        return ret;
                }
@@ -834,6 +995,7 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
         * the input buffer, leaving just the data bytes.
         */
        if (response->str[0] != '#') {
+               g_mutex_unlock(&scpi->scpi_mutex);
                g_string_free(response, TRUE);
                return SR_ERR_DATA;
        }
@@ -841,13 +1003,15 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
        buf[1] = '\0';
        ret = sr_atol(buf, &llen);
        if ((ret != SR_OK) || (llen == 0)) {
+               g_mutex_unlock(&scpi->scpi_mutex);
                g_string_free(response, TRUE);
                return ret;
        }
 
        while (response->len < (unsigned long)(2 + llen)) {
-               ret = sr_scpi_read_response(scpi, response, timeout);
+               ret = scpi_read_response(scpi, response, timeout);
                if (ret < 0) {
+                       g_mutex_unlock(&scpi->scpi_mutex);
                        g_string_free(response, TRUE);
                        return ret;
                }
@@ -857,6 +1021,7 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
        buf[llen] = '\0';
        ret = sr_atol(buf, &datalen);
        if ((ret != SR_OK) || (datalen == 0)) {
+               g_mutex_unlock(&scpi->scpi_mutex);
                g_string_free(response, TRUE);
                return ret;
        }
@@ -874,8 +1039,9 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
        }
 
        while (response->len < (unsigned long)(datalen)) {
-               ret = sr_scpi_read_response(scpi, response, timeout);
+               ret = scpi_read_response(scpi, response, timeout);
                if (ret < 0) {
+                       g_mutex_unlock(&scpi->scpi_mutex);
                        g_string_free(response, TRUE);
                        return ret;
                }
@@ -883,6 +1049,8 @@ SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi,
                        timeout = g_get_monotonic_time() + scpi->read_timeout_us;
        }
 
+       g_mutex_unlock(&scpi->scpi_mutex);
+
        /* Convert received data to byte array. */
        *scpi_response = g_byte_array_new_take(
                (guint8*)g_string_free(response, FALSE), datalen);
@@ -908,6 +1076,7 @@ SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
        char *response;
        gchar **tokens;
        struct sr_scpi_hw_info *hw_info;
+       gchar *idn_substr;
 
        response = NULL;
        tokens = NULL;
@@ -916,8 +1085,6 @@ SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
        if (ret != SR_OK && !response)
                return ret;
 
-       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
@@ -936,7 +1103,13 @@ SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
        g_free(response);
 
        hw_info = g_malloc0(sizeof(struct sr_scpi_hw_info));
-       hw_info->manufacturer = g_strstrip(g_strdup(tokens[0]));
+
+       idn_substr = g_strstr_len(tokens[0], -1, "IDN ");
+       if (idn_substr == NULL)
+               hw_info->manufacturer = g_strstrip(g_strdup(tokens[0]));
+       else
+               hw_info->manufacturer = g_strstrip(g_strdup(idn_substr + 4));
+
        hw_info->model = g_strstrip(g_strdup(tokens[1]));
        hw_info->serial_number = g_strstrip(g_strdup(tokens[2]));
        hw_info->firmware_version = g_strstrip(g_strdup(tokens[3]));
@@ -965,3 +1138,160 @@ SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info)
        g_free(hw_info->firmware_version);
        g_free(hw_info);
 }
+
+SR_PRIV const char *sr_vendor_alias(const char *raw_vendor)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(scpi_vendors); i++) {
+               if (!g_ascii_strcasecmp(raw_vendor, scpi_vendors[i][0]))
+                       return scpi_vendors[i][1];
+       }
+
+       return raw_vendor;
+}
+
+SR_PRIV const char *sr_scpi_cmd_get(const struct scpi_command *cmdtable,
+               int command)
+{
+       unsigned int i;
+       const char *cmd;
+
+       if (!cmdtable)
+               return NULL;
+
+       cmd = NULL;
+       for (i = 0; cmdtable[i].string; i++) {
+               if (cmdtable[i].command == command) {
+                       cmd = cmdtable[i].string;
+                       break;
+               }
+       }
+
+       return cmd;
+}
+
+SR_PRIV int sr_scpi_cmd(const struct sr_dev_inst *sdi,
+               const struct scpi_command *cmdtable,
+               int channel_command, const char *channel_name,
+               int command, ...)
+{
+       struct sr_scpi_dev_inst *scpi;
+       va_list args;
+       int ret;
+       const char *channel_cmd;
+       const char *cmd;
+
+       scpi = sdi->conn;
+
+       if (!(cmd = sr_scpi_cmd_get(cmdtable, command))) {
+               /* Device does not implement this command, that's OK. */
+               return SR_OK;
+       }
+
+       g_mutex_lock(&scpi->scpi_mutex);
+
+       /* Select channel. */
+       channel_cmd = sr_scpi_cmd_get(cmdtable, channel_command);
+       if (channel_cmd && channel_name &&
+                       g_strcmp0(channel_name, scpi->actual_channel_name)) {
+               sr_spew("sr_scpi_cmd(): new channel = %s", channel_name);
+               g_free(scpi->actual_channel_name);
+               scpi->actual_channel_name = g_strdup(channel_name);
+               ret = scpi_send(scpi, channel_cmd, channel_name);
+               if (ret != SR_OK)
+                       return ret;
+       }
+
+       va_start(args, command);
+       ret = scpi_send_variadic(scpi, cmd, args);
+       va_end(args);
+
+       g_mutex_unlock(&scpi->scpi_mutex);
+
+       return ret;
+}
+
+SR_PRIV int sr_scpi_cmd_resp(const struct sr_dev_inst *sdi,
+               const struct scpi_command *cmdtable,
+               int channel_command, const char *channel_name,
+               GVariant **gvar, const GVariantType *gvtype, int command, ...)
+{
+       struct sr_scpi_dev_inst *scpi;
+       va_list args;
+       const char *channel_cmd;
+       const char *cmd;
+       GString *response;
+       char *s;
+       gboolean b;
+       double d;
+       int ret;
+
+       scpi = sdi->conn;
+
+       if (!(cmd = sr_scpi_cmd_get(cmdtable, command))) {
+               /* Device does not implement this command. */
+               return SR_ERR_NA;
+       }
+
+       g_mutex_lock(&scpi->scpi_mutex);
+
+       /* Select channel. */
+       channel_cmd = sr_scpi_cmd_get(cmdtable, channel_command);
+       if (channel_cmd && channel_name &&
+                       g_strcmp0(channel_name, scpi->actual_channel_name)) {
+               sr_spew("sr_scpi_cmd_get(): new channel = %s", channel_name);
+               g_free(scpi->actual_channel_name);
+               scpi->actual_channel_name = g_strdup(channel_name);
+               ret = scpi_send(scpi, channel_cmd, channel_name);
+               if (ret != SR_OK)
+                       return ret;
+       }
+
+       va_start(args, command);
+       ret = scpi_send_variadic(scpi, cmd, args);
+       va_end(args);
+       if (ret != SR_OK) {
+               g_mutex_unlock(&scpi->scpi_mutex);
+               return ret;
+       }
+
+       response = g_string_sized_new(1024);
+       ret = scpi_get_data(scpi, NULL, &response);
+       if (ret != SR_OK) {
+               g_mutex_unlock(&scpi->scpi_mutex);
+               if (response)
+                       g_string_free(response, TRUE);
+               return ret;
+       }
+
+       g_mutex_unlock(&scpi->scpi_mutex);
+
+       /* Get rid of trailing linefeed if present */
+       if (response->len >= 1 && response->str[response->len - 1] == '\n')
+               g_string_truncate(response, response->len - 1);
+
+       /* Get rid of trailing carriage return if present */
+       if (response->len >= 1 && response->str[response->len - 1] == '\r')
+               g_string_truncate(response, response->len - 1);
+
+       s = g_string_free(response, FALSE);
+
+       ret = SR_OK;
+       if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_BOOLEAN)) {
+               if ((ret = parse_strict_bool(s, &b)) == SR_OK)
+                       *gvar = g_variant_new_boolean(b);
+       } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_DOUBLE)) {
+               if ((ret = sr_atod_ascii(s, &d)) == SR_OK)
+                       *gvar = g_variant_new_double(d);
+       } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_STRING)) {
+               *gvar = g_variant_new_string(s);
+       } else {
+               sr_err("Unable to convert to desired GVariant type.");
+               ret = SR_ERR_NA;
+       }
+
+       g_free(s);
+
+       return ret;
+}
index cab72df720f426bfcfc9f7f19909bb9862da7e03..2f3256be948e2eae404d02cda0e38b4391bab465 100644 (file)
@@ -118,8 +118,9 @@ static int scpi_gpib_read_data(void *priv, char *buf, int maxlen)
 
        if (ibsta & ERR)
        {
-               sr_err("Error while reading SCPI response: iberr = %s.",
-                       gpib_error_string(iberr));
+               sr_err("Error while reading SCPI response: "
+                       "iberr = %s, ibsta = %d.",
+                       gpib_error_string(iberr), ibsta);
                return SR_ERR;
        }
 
index 6c4e63c7b9122756c7bfaaf2983d9e80203f4489..23a5e3f02cb31acf784242d418dd1821f36ae57b 100644 (file)
@@ -117,20 +117,15 @@ static int scpi_serial_source_remove(struct sr_session *session, void *priv)
 
 static int scpi_serial_send(void *priv, const char *command)
 {
-       int len, result, written;
+       int result;
        struct scpi_serial *sscpi = priv;
        struct sr_serial_dev_inst *serial = sscpi->serial;
 
-       len = strlen(command);
-       written = 0;
-       while (written < len) {
-               result = serial_write_nonblocking(serial,
-                               command + written, len - written);
-               if (result < 0) {
-                       sr_err("Error while sending SCPI command: '%s'.", command);
-                       return SR_ERR;
-               }
-               written += result;
+       result = serial_write_blocking(serial, command, strlen(command), 0);
+       if (result < 0) {
+               sr_err("Error while sending SCPI command '%s': %d.",
+                       command, result);
+               return SR_ERR;
        }
 
        sr_spew("Successfully sent SCPI command: '%s'.", command);
@@ -158,8 +153,6 @@ static int scpi_serial_read_data(void *priv, char *buf, int maxlen)
                return ret;
 
        if (ret > 0) {
-               sr_spew("Read %d bytes into buffer.", ret);
-
                if (buf[ret - 1] == '\n') {
                        sscpi->got_newline = TRUE;
                        sr_spew("Received terminator");
index 5e8b267665fe5ac705d77afce9e9e5ea9028537f..c1d4029ca1e89fedaec057fb7a28d8e7639a3805 100644 (file)
@@ -105,8 +105,16 @@ struct usbtmc_blacklist {
 static struct usbtmc_blacklist blacklist_remote[] = {
        { 0x1ab1, 0x0588 }, /* Rigol DS1000 series */
        { 0x1ab1, 0x04b0 }, /* Rigol DS2000 series */
+       { 0x1ab1, 0x04b1 }, /* Rigol DS4000 series */
        { 0x0957, 0x0588 }, /* Agilent DSO1000 series (rebadged Rigol DS1000) */
        { 0x0b21, 0xffff }, /* All Yokogawa devices */
+       { 0xf4ec, 0xffff }, /* All Siglent SDS devices */
+       ALL_ZERO
+};
+
+/* Devices that shall get reset during open(). */
+static struct usbtmc_blacklist whitelist_usb_reset[] = {
+       { 0xf4ec, 0xffff }, /* All Siglent SDS devices */
        ALL_ZERO
 };
 
@@ -131,8 +139,9 @@ static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
 
                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));
+                               if (ret != LIBUSB_ERROR_NOT_FOUND)
+                                       sr_dbg("Failed to get configuration descriptor: %s, "
+                                              "ignoring device.", libusb_error_name(ret));
                                break;
                        }
                        for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
@@ -155,7 +164,7 @@ static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
        }
        libusb_free_device_list(devlist, 1);
 
-       sr_dbg("Found %d device(s).", g_slist_length(resources));
+       /* No log message for #devices found (caller will log that). */
 
        return resources;
 }
@@ -290,6 +299,7 @@ static int scpi_usbtmc_libusb_open(struct sr_scpi_dev_inst *scpi)
        int confidx, intfidx, epidx, config = 0, current_config;
        uint8_t capabilities[24];
        int ret, found = 0;
+       int do_reset;
 
        if (usb->devhdl)
                return SR_OK;
@@ -302,8 +312,9 @@ static int scpi_usbtmc_libusb_open(struct sr_scpi_dev_inst *scpi)
 
        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));
+                       if (ret != LIBUSB_ERROR_NOT_FOUND)
+                               sr_dbg("Failed to get configuration descriptor: %s, "
+                                      "ignoring device.", libusb_error_name(ret));
                        continue;
                }
                for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
@@ -370,6 +381,12 @@ static int scpi_usbtmc_libusb_open(struct sr_scpi_dev_inst *scpi)
                return SR_ERR;
        }
 
+       /* Optionally reset the USB device. */
+       do_reset = check_usbtmc_blacklist(whitelist_usb_reset,
+               des.idVendor, des.idProduct);
+       if (do_reset)
+               libusb_reset_device(usb->devhdl);
+
        /* Get capabilities. */
        ret = libusb_control_transfer(usb->devhdl, LIBUSB_ENDPOINT_IN |
                LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
index aa8d4ddedf3b383fadbdf06bedbe062ba67f24c4..2d67a8000c2b46ec812d863f295ccaebd14dd2ea 100644 (file)
@@ -28,7 +28,7 @@
 #include <libserialport.h>
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
-#ifdef G_OS_WIN32
+#ifdef _WIN32
 #include <windows.h> /* for HANDLE */
 #endif
 
@@ -715,7 +715,11 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
                time /= 1000;
 
                if ((ibuf - i) >= packet_size) {
+                       GString *text;
                        /* We have at least a packet's worth of data. */
+                       text = sr_hexdump_new(&buf[i], packet_size);
+                       sr_spew("Trying packet: %s", text->str);
+                       sr_hexdump_free(text);
                        if (is_valid(&buf[i])) {
                                sr_spew("Found valid %zu-byte packet after "
                                        "%" PRIu64 "ms.", (ibuf - i), time);
@@ -788,7 +792,7 @@ SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_devic
 }
 
 /** @cond PRIVATE */
-#ifdef G_OS_WIN32
+#ifdef _WIN32
 typedef HANDLE event_handle;
 #else
 typedef int event_handle;
index c88e48c74e49c159c85fdc0e946d7774e4e3f1a2..b3fdfb54a2451b0ac506b0f82b452fda30ad6807 100644 (file)
@@ -365,7 +365,7 @@ SR_API int sr_session_dev_add(struct sr_session *session,
                               sr_strerror(ret));
                        return ret;
                }
-               if ((ret = sdi->driver->dev_acquisition_start(sdi)) != SR_OK) {
+               if ((ret = sr_dev_acquisition_start(sdi)) != SR_OK) {
                        sr_err("Failed to start acquisition of device in "
                               "running session (%s)", sr_strerror(ret));
                        return ret;
@@ -816,7 +816,7 @@ SR_API int sr_session_start(struct sr_session *session)
                        ret = SR_ERR;
                        break;
                }
-               ret = sdi->driver->dev_acquisition_start(sdi);
+               ret = sr_dev_acquisition_start(sdi);
                if (ret != SR_OK) {
                        sr_err("Could not start %s device %s acquisition.",
                                sdi->driver->name, sdi->connection_id);
@@ -830,7 +830,7 @@ SR_API int sr_session_start(struct sr_session *session)
                lend = l->next;
                for (l = session->devs; l != lend; l = l->next) {
                        sdi = l->data;
-                       sdi->driver->dev_acquisition_stop(sdi);
+                       sr_dev_acquisition_stop(sdi);
                }
                /* TODO: Handle delayed stops. Need to iterate the event
                 * sources... */
@@ -913,8 +913,7 @@ static gboolean session_stop_sync(void *user_data)
 
        for (node = session->devs; node; node = node->next) {
                sdi = node->data;
-               if (sdi->driver && sdi->driver->dev_acquisition_stop)
-                       sdi->driver->dev_acquisition_stop(sdi);
+               sr_dev_acquisition_stop(sdi);
        }
 
        return G_SOURCE_REMOVE;
@@ -1295,7 +1294,7 @@ SR_PRIV int sr_session_source_add_channel(struct sr_session *session,
        /* We should be using g_io_create_watch(), but can't without
         * changing the driver API, as the callback signature is different.
         */
-#ifdef G_OS_WIN32
+#ifdef _WIN32
        g_io_channel_win32_make_pollfd(channel, events, &pollfd);
 #else
        pollfd.fd = g_io_channel_unix_get_fd(channel);
@@ -1451,8 +1450,7 @@ static void copy_src(struct sr_config *src, struct sr_datafeed_meta *meta_copy)
                                           g_memdup(src, sizeof(struct sr_config)));
 }
 
-/** @private */
-SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
+SR_API int sr_packet_copy(const struct sr_datafeed_packet *packet,
                struct sr_datafeed_packet **copy)
 {
        const struct sr_datafeed_meta *meta;
@@ -1485,8 +1483,15 @@ SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
        case SR_DF_LOGIC:
                logic = packet->payload;
                logic_copy = g_malloc(sizeof(*logic_copy));
+               if (!logic_copy)
+                       return SR_ERR;
                logic_copy->length = logic->length;
                logic_copy->unitsize = logic->unitsize;
+               logic_copy->data = g_malloc(logic->length * logic->unitsize);
+               if (!logic_copy->data) {
+                       g_free(logic_copy);
+                       return SR_ERR;
+               }
                memcpy(logic_copy->data, logic->data, logic->length * logic->unitsize);
                (*copy)->payload = logic_copy;
                break;
@@ -1516,7 +1521,7 @@ SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
        return SR_OK;
 }
 
-void sr_packet_free(struct sr_datafeed_packet *packet)
+SR_API void sr_packet_free(struct sr_datafeed_packet *packet)
 {
        const struct sr_datafeed_meta *meta;
        const struct sr_datafeed_logic *logic;
@@ -1561,7 +1566,6 @@ void sr_packet_free(struct sr_datafeed_packet *packet)
                sr_err("Unknown packet type %d", packet->type);
        }
        g_free(packet);
-
 }
 
 /** @} */
index 684a7e539122418695be981fcc4fa21a9692172e..79383cc719fb011aab5758b1ad471d64324abb0b 100644 (file)
@@ -72,7 +72,7 @@ static gboolean stream_session_data(struct sr_dev_inst *sdi)
        struct sr_analog_spec spec;
        struct zip_stat zs;
        int ret, got_data;
-       char capturefile[16];
+       char capturefile[128];
        void *buf;
 
        got_data = FALSE;
@@ -92,7 +92,7 @@ static gboolean stream_session_data(struct sr_dev_inst *sdi)
                                sr_dbg("Opened %s.", vdev->capturefile);
                        } else {
                                /* Try as first chunk filename. */
-                               snprintf(capturefile, 15, "%s-1", vdev->capturefile);
+                               snprintf(capturefile, sizeof(capturefile) - 1, "%s-1", vdev->capturefile);
                                if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
                                        vdev->cur_chunk = 1;
                                        if (!(vdev->capfile = zip_fopen(vdev->archive,
@@ -108,7 +108,7 @@ static gboolean stream_session_data(struct sr_dev_inst *sdi)
                } else {
                        /* Capture data is chunked, advance to the next chunk. */
                        vdev->cur_chunk++;
-                       snprintf(capturefile, 15, "%s-%d", vdev->capturefile,
+                       snprintf(capturefile, sizeof(capturefile) - 1, "%s-%d", vdev->capturefile,
                                        vdev->cur_chunk);
                        if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
                                if (!(vdev->capfile = zip_fopen(vdev->archive,
@@ -150,8 +150,8 @@ static gboolean stream_session_data(struct sr_dev_inst *sdi)
                ret = zip_fread(vdev->capfile, buf, CHUNKSIZE);
 
        if (ret > 0) {
-               got_data = TRUE;
                if (vdev->cur_analog_channel != 0) {
+                       got_data = TRUE;
                        packet.type = SR_DF_ANALOG;
                        packet.payload = &analog;
                        /* TODO: Use proper 'digits' value for this device (and its modes). */
@@ -164,7 +164,8 @@ static gboolean stream_session_data(struct sr_dev_inst *sdi)
                        analog.meaning->unit = SR_UNIT_VOLT;
                        analog.meaning->mqflags = SR_MQFLAG_DC;
                        analog.data = (float *) buf;
-               } else {
+               } else if (vdev->unitsize) {
+                       got_data = TRUE;
                        if (ret % vdev->unitsize != 0)
                                sr_warn("Read size %d not a multiple of the"
                                        " unit size %d.", ret, vdev->unitsize);
@@ -173,9 +174,17 @@ static gboolean stream_session_data(struct sr_dev_inst *sdi)
                        logic.length = ret;
                        logic.unitsize = vdev->unitsize;
                        logic.data = buf;
+               } else {
+                       /*
+                        * Neither analog data, nor logic which has
+                        * unitsize, must be an unexpected API use.
+                        */
+                       sr_warn("Neither analog nor logic data. Ignoring.");
+               }
+               if (got_data) {
+                       vdev->bytes_read += ret;
+                       sr_session_send(sdi, &packet);
                }
-               vdev->bytes_read += ret;
-               sr_session_send(sdi, &packet);
        } else {
                /* done with this capture file */
                zip_fclose(vdev->capfile);
@@ -223,20 +232,6 @@ static int receive_data(int fd, int revents, void *cb_data)
 
 /* driver callbacks */
 
-static int dev_clear(const struct sr_dev_driver *di)
-{
-       struct drv_context *drvc;
-       GSList *l;
-
-       drvc = di->context;
-       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 sr_dev_driver *di;
@@ -264,8 +259,8 @@ static int dev_close(struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct session_vdev *vdev;
 
@@ -290,8 +285,8 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
        return SR_OK;
 }
 
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
        struct session_vdev *vdev;
 
@@ -330,22 +325,10 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        return SR_OK;
 }
 
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
+static int config_list(uint32_t 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_UINT32,
-                               devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
+       return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, NO_OPTS, devopts);
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
@@ -402,10 +385,10 @@ SR_PRIV struct sr_dev_driver session_driver = {
        .longname = "Session-emulating driver",
        .api_version = 1,
        .init = std_init,
-       .cleanup = dev_clear,
+       .cleanup = std_cleanup,
        .scan = NULL,
        .dev_list = NULL,
-       .dev_clear = dev_clear,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 663fea722f9db161eafee3dc5f316809560c785b..7a2010e485ef7eca90f992b87076310783523a47 100644 (file)
@@ -167,7 +167,7 @@ SR_PRIV int sr_sessionfile_check(const char *filename)
 
        return SR_OK;
 }
+
 /** @private */
 SR_PRIV struct sr_dev_inst *sr_session_prepare_sdi(const char *filename, struct sr_session **session)
 {
@@ -175,7 +175,7 @@ SR_PRIV struct sr_dev_inst *sr_session_prepare_sdi(const char *filename, struct
 
        sdi = g_malloc0(sizeof(struct sr_dev_inst));
        sdi->driver = &session_driver;
-       sdi->status = SR_ST_ACTIVE;
+       sdi->status = SR_ST_INACTIVE;
        if (!session_driver_initialized) {
                /* first device, init the driver */
                session_driver_initialized = 1;
index 50fdd41cec094999764311fc66475dcfbb1f91dc..245eb4fff8c94ed652233de2676a341961eeb691 100644 (file)
 #define LOG_PREFIX "soft-trigger"
 /* @endcond */
 
+SR_PRIV int logic_channel_unitsize(GSList *channels)
+{
+       int number = 0;
+       struct sr_channel *channel;
+       GSList *l;
+
+       for (l = channels; l; l = l->next) {
+               channel = l->data;
+               if (channel->type == SR_CHANNEL_LOGIC)
+                       number++;
+       }
+
+       return (number + 7) / 8;
+}
+
 SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
                const struct sr_dev_inst *sdi, struct sr_trigger *trigger,
                int pre_trigger_samples)
@@ -35,10 +50,19 @@ SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
        stl = g_malloc0(sizeof(struct soft_trigger_logic));
        stl->sdi = sdi;
        stl->trigger = trigger;
-       stl->unitsize = (g_slist_length(sdi->channels) + 7) / 8;
+       stl->unitsize = logic_channel_unitsize(sdi->channels);
        stl->prev_sample = g_malloc0(stl->unitsize);
        stl->pre_trigger_size = stl->unitsize * pre_trigger_samples;
-       stl->pre_trigger_buffer = g_malloc(stl->pre_trigger_size);
+       stl->pre_trigger_buffer = g_try_malloc(stl->pre_trigger_size);
+       if (pre_trigger_samples > 0 && !stl->pre_trigger_buffer) {
+               /*
+                * Error out if g_try_malloc() failed (or was invoked as
+                * g_try_malloc(0)) *and* more than 0 pretrigger samples
+                * were requested.
+                */
+               soft_trigger_logic_free(stl);
+               return NULL;
+       }
        stl->pre_trigger_head = stl->pre_trigger_buffer;
 
        if (stl->pre_trigger_size > 0 && !stl->pre_trigger_buffer) {
index 87b3f1f137d5950116aeaf1b8b91cd0127715567..6a6c80adfff5df1f8b93bfec89a6267fe1ee6a9f 100644 (file)
--- a/src/std.c
+++ b/src/std.c
  * @internal
  */
 
+/* Needed for gettimeofday(), at least on FreeBSD. */
+#define _XOPEN_SOURCE 700
+
 #include <config.h>
+#include <string.h>
+#include <math.h>
+#include <sys/time.h>
 #include <glib.h>
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "std"
 
+SR_PRIV const uint32_t NO_OPTS[1] = {};
+
 /**
- * Standard sr_driver_init() API helper.
+ * Standard driver init() callback 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.
+ * Create a new 'struct drv_context' (drvc), assign sr_ctx to it, and
+ * then assign 'drvc' to the 'struct sr_dev_driver' (di) that is passed.
  *
- * @param di The driver instance to use.
- * @param sr_ctx The libsigrok context to assign.
+ * @param[in] di The driver instance to use. Must not be NULL.
+ * @param[in] sr_ctx The libsigrok context to assign. May be NULL.
  *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
  */
 SR_PRIV int std_init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
        struct drv_context *drvc;
 
+       if (!di) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
+       }
+
        drvc = g_malloc0(sizeof(struct drv_context));
        drvc->sr_ctx = sr_ctx;
        drvc->instances = NULL;
@@ -59,56 +73,124 @@ SR_PRIV int std_init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 }
 
 /**
- * Standard driver cleanup() callback API helper
+ * Standard driver cleanup() callback API helper.
  *
- * @param di The driver instance to use.
+ * This function can be used to simplify most driver's cleanup() API callback.
  *
- * Frees all device instances by calling sr_dev_clear() and then releases any
+ * Free all device instances by calling sr_dev_clear() and then release any
  * resources allocated by std_init().
  *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid driver
+ * @param[in] di The driver instance to use. Must not be NULL.
  *
-*/
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
+ */
 SR_PRIV int std_cleanup(const struct sr_dev_driver *di)
 {
        int ret;
 
+       if (!di) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
+       }
+
        ret = sr_dev_clear(di);
        g_free(di->context);
 
        return ret;
 }
 
+/**
+ * Dummmy driver dev_open() callback API helper.
+ *
+ * @param[in] sdi The device instance to use. May be NULL (unused).
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_dummy_dev_open(struct sr_dev_inst *sdi)
+{
+       (void)sdi;
+
+       return SR_OK;
+}
+
+/**
+ * Dummmy driver dev_close() callback API helper.
+ *
+ * @param[in] sdi The device instance to use. May be NULL (unused).
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_dummy_dev_close(struct sr_dev_inst *sdi)
+{
+       (void)sdi;
+
+       return SR_OK;
+}
+
+/**
+ * Dummmy driver dev_acquisition_start() callback API helper.
+ *
+ * @param[in] sdi The device instance to use. May be NULL (unused).
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_dummy_dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+       (void)sdi;
+
+       return SR_OK;
+}
+
+/**
+ * Dummmy driver dev_acquisition_stop() callback API helper.
+ *
+ * @param[in] sdi The device instance to use. May be NULL (unused).
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_dummy_dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+       (void)sdi;
+
+       return SR_OK;
+}
+
 /**
  * Standard API helper for sending an SR_DF_HEADER packet.
  *
- * This function can be used to simplify most driver's
+ * This function can be used to simplify most drivers'
  * dev_acquisition_start() API callback.
  *
- * @param sdi The device instance to use.
+ * @param[in] sdi The device instance to use. Must not be NULL.
  *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- *         SR_ERR upon other errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
  */
 SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi)
 {
-       const char *prefix = (sdi->driver) ? sdi->driver->name : "unknown";
+       const char *prefix;
        int ret;
        struct sr_datafeed_packet packet;
        struct sr_datafeed_header header;
 
-       sr_dbg("%s: Starting acquisition.", prefix);
+       if (!sdi) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
+       }
+
+       prefix = (sdi->driver) ? sdi->driver->name : "unknown";
 
        /* 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);
+               sr_err("%s: Failed to send SR_DF_HEADER packet: %d.", prefix, ret);
                return ret;
        }
 
@@ -118,21 +200,27 @@ SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi)
 /**
  * Standard API helper for sending an SR_DF_END packet.
  *
- * @param sdi The device instance to use. Must not be NULL.
+ * This function can be used to simplify most drivers'
+ * dev_acquisition_stop() API callback.
+ *
+ * @param[in] sdi The device instance to use. Must not be NULL.
  *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- *         SR_ERR upon other errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
  */
 SR_PRIV int std_session_send_df_end(const struct sr_dev_inst *sdi)
 {
-       const char *prefix = (sdi->driver) ? sdi->driver->name : "unknown";
+       const char *prefix;
        int ret;
        struct sr_datafeed_packet packet;
 
-       if (!sdi)
+       if (!sdi) {
+               sr_err("%s: Invalid argument.", __func__);
                return SR_ERR_ARG;
+       }
 
-       sr_dbg("%s: Sending SR_DF_END packet.", prefix);
+       prefix = (sdi->driver) ? sdi->driver->name : "unknown";
 
        packet.type = SR_DF_END;
        packet.payload = NULL;
@@ -145,123 +233,202 @@ SR_PRIV int std_session_send_df_end(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
+/**
+ * Standard API helper for sending an SR_DF_FRAME_BEGIN packet.
+ *
+ * This function can be used to simplify most drivers'
+ * frame handling.
+ *
+ * @param[in] sdi The device instance to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
+ */
+SR_PRIV int std_session_send_frame_begin(const struct sr_dev_inst *sdi)
+{
+       const char *prefix;
+       int ret;
+       struct sr_datafeed_packet packet;
+
+       if (!sdi) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
+       }
+
+       prefix = (sdi->driver) ? sdi->driver->name : "unknown";
+
+       packet.type = SR_DF_FRAME_BEGIN;
+       packet.payload = NULL;
+
+       if ((ret = sr_session_send(sdi, &packet)) < 0) {
+               sr_err("%s: Failed to send SR_DF_FRAME_BEGIN packet: %d.", prefix, ret);
+               return ret;
+       }
+
+       return SR_OK;
+}
+
+/**
+ * Standard API helper for sending an SR_DF_FRAME_END packet.
+ *
+ * This function can be used to simplify most drivers'
+ * frame handling.
+ *
+ * @param[in] sdi The device instance to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
+ */
+SR_PRIV int std_session_send_frame_end(const struct sr_dev_inst *sdi)
+{
+       const char *prefix;
+       int ret;
+       struct sr_datafeed_packet packet;
+
+       if (!sdi) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
+       }
+
+       prefix = (sdi->driver) ? sdi->driver->name : "unknown";
+
+       packet.type = SR_DF_FRAME_END;
+       packet.payload = NULL;
+
+       if ((ret = sr_session_send(sdi, &packet)) < 0) {
+               sr_err("%s: Failed to send SR_DF_FRAME_END packet: %d.", prefix, ret);
+               return ret;
+       }
+
+       return SR_OK;
+}
+
 #ifdef HAVE_LIBSERIALPORT
 
 /**
- * Standard serial driver dev_open() helper.
+ * Standard serial driver dev_open() callback API 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 flag.
  *
- * If the open succeeded, the status field of the given sdi is set
- * to SR_ST_ACTIVE.
+ * @param[in] sdi The device instance to use. Must not be NULL.
  *
  * @retval SR_OK Success.
- * @retval SR_ERR Serial port open failed.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other 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) != SR_OK)
-               return SR_ERR;
+       if (!sdi) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
+       }
 
-       sdi->status = SR_ST_ACTIVE;
+       serial = sdi->conn;
 
-       return SR_OK;
+       return serial_open(serial, SERIAL_RDWR);
 }
 
 /**
- * Standard serial driver dev_close() helper.
+ * Standard serial driver dev_close() callback API 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.
+ * @param[in] sdi The device instance to use. Must not be NULL.
  *
  * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Serial port close failed.
  */
 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;
+       if (!sdi) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
        }
 
-       return SR_OK;
+       serial = sdi->conn;
+
+       return serial_close(serial);
 }
 
 /**
- * Standard sr_session_stop() API helper.
+ * Standard serial driver dev_acquisition_stop() callback API helper.
  *
- * This function can be used to simplify most (serial port based) driver's
+ * This function can be used to simplify most (serial port based) drivers'
  * dev_acquisition_stop() API callback.
  *
- * @param sdi The device instance for which acquisition should stop.
- *            Must not be NULL.
+ * @param[in] sdi The device instance for which acquisition should stop.
+ *                Must not be NULL.
  *
  * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid arguments.
- * @retval SR_ERR_DEV_CLOSED Device is closed.
- * @retval SR_ERR Other errors.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
  */
 SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       struct sr_serial_dev_inst *serial = sdi->conn;
-       const char *prefix = sdi->driver->name;
+       struct sr_serial_dev_inst *serial;
+       const char *prefix;
        int ret;
 
-       if (sdi->status != SR_ST_ACTIVE) {
-               sr_err("%s: Device inactive, can't stop acquisition.", prefix);
-               return SR_ERR_DEV_CLOSED;
+       if (!sdi) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
        }
 
-       sr_dbg("%s: Stopping acquisition.", prefix);
+       serial = sdi->conn;
+       prefix = sdi->driver->name;
 
        if ((ret = serial_source_remove(sdi->session, serial)) < 0) {
                sr_err("%s: Failed to remove source: %d.", prefix, ret);
                return ret;
        }
 
-       if ((ret = sdi->driver->dev_close(sdi)) < 0) {
+       if ((ret = sr_dev_close(sdi)) < 0) {
                sr_err("%s: Failed to close device: %d.", prefix, ret);
                return ret;
        }
 
-       std_session_send_df_end(sdi);
-
-       return SR_OK;
+       return std_session_send_df_end(sdi);
 }
 
 #endif
 
 /**
- * Standard driver dev_clear() helper.
+ * Standard driver dev_clear() callback API 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).
+ * The only limitation is driver-specific device contexts (sdi->priv / devc).
  * 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.
+ * @param[in] driver The driver which will have its instances released.
+ *                   Must not be NULL.
+ * @param[in] clear_private If not NULL, this points to a function called
+ *            with sdi->priv (devc) as argument. The function can then clear
+ *            any device instance-specific resources kept there.
+ *            It must NOT clear the struct pointed to by sdi->priv (devc),
+ *            since this function will always free it after clear_private()
+ *            has run.
  *
- * @return SR_OK on success.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR_BUG Implementation bug.
+ * @retval other Other error.
  */
-SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
+SR_PRIV int std_dev_clear_with_callback(const struct sr_dev_driver *driver,
                std_dev_clear_callback clear_private)
 {
        struct drv_context *drvc;
@@ -269,13 +436,17 @@ SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
        GSList *l;
        int ret;
 
-       if (!(drvc = driver->context))
-               /* Driver was never initialized, nothing to do. */
-               return SR_OK;
+       if (!driver) {
+               sr_err("%s: Invalid argument.", __func__);
+               return SR_ERR_ARG;
+       }
+
+       drvc = driver->context; /* Caller checked for context != NULL. */
 
        ret = SR_OK;
        for (l = drvc->instances; l; l = l->next) {
                if (!(sdi = l->data)) {
+                       sr_err("%s: Invalid device instance.", __func__);
                        ret = SR_ERR_BUG;
                        continue;
                }
@@ -296,12 +467,13 @@ SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
                        if (sdi->inst_type == SR_INST_MODBUS)
                                sr_modbus_free(sdi->conn);
                }
+
+               /* Clear driver-specific stuff, if any. */
                if (clear_private)
-                       /* The helper function is responsible for freeing
-                        * its own sdi->priv! */
                        clear_private(sdi->priv);
-               else
-                       g_free(sdi->priv);
+
+               /* Clear sdi->priv (devc). */
+               g_free(sdi->priv);
 
                sr_dev_inst_free(sdi);
        }
@@ -312,27 +484,39 @@ SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
        return ret;
 }
 
+SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver)
+{
+       return std_dev_clear_with_callback(driver, NULL);
+}
+
 /**
- * Standard implementation for the driver dev_list() callback
+ * Standard driver dev_list() callback API helper.
  *
- * This function can be used as the dev_list callback by most drivers that use
- * the standard helper functions. It returns the devices contained in the driver
- * context instances list.
+ * This function can be used as the dev_list() callback by most drivers.
  *
- * @param di The driver instance to use.
+ * Return the devices contained in the driver context instances list.
  *
- * @return The list of devices/instances of this driver, or NULL upon errors
- *         or if the list is empty.
+ * @param[in] di The driver instance to use. Must not be NULL.
+ *
+ * @retval NULL Error, or the list is empty.
+ * @retval other The list of device instances of this driver.
  */
 SR_PRIV GSList *std_dev_list(const struct sr_dev_driver *di)
 {
-       struct drv_context *drvc = di->context;
+       struct drv_context *drvc;
+
+       if (!di) {
+               sr_err("%s: Invalid argument.", __func__);
+               return NULL;
+       }
+
+       drvc = di->context;
 
        return drvc->instances;
 }
 
 /**
- * Standard scan() callback API helper.
+ * Standard driver scan() callback API helper.
  *
  * This function can be used to perform common tasks required by a driver's
  * scan() callback. It will initialize the driver for each device on the list
@@ -359,8 +543,9 @@ SR_PRIV GSList *std_dev_list(const struct sr_dev_driver *di)
  * }
  * @endcode
  *
- * @param di The driver instance to use. Must not be NULL.
- * @param devices List of newly discovered devices (struct sr_dev_inst).
+ * @param[in] di The driver instance to use. Must not be NULL.
+ * @param[in] devices List of newly discovered devices (struct sr_dev_inst).
+ *                    May be NULL.
  *
  * @return The @p devices list.
  */
@@ -379,7 +564,7 @@ SR_PRIV GSList *std_scan_complete(struct sr_dev_driver *di, GSList *devices)
        for (l = devices; l; l = l->next) {
                struct sr_dev_inst *sdi = l->data;
                if (!sdi) {
-                       sr_err("Invalid driver instance, cannot complete scan.");
+                       sr_err("Invalid device instance, cannot complete scan.");
                        return NULL;
                }
                sdi->driver = di;
@@ -389,3 +574,366 @@ SR_PRIV GSList *std_scan_complete(struct sr_dev_driver *di, GSList *devices)
 
        return devices;
 }
+
+SR_PRIV int std_opts_config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg,
+       const uint32_t scanopts[], size_t scansize, const uint32_t drvopts[],
+       size_t drvsize, const uint32_t devopts[], size_t devsize)
+{
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+               /* Always return scanopts, regardless of sdi or cg. */
+               if (!scanopts || scanopts == NO_OPTS)
+                       return SR_ERR_ARG;
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                       scanopts, scansize, sizeof(uint32_t));
+               break;
+       case SR_CONF_DEVICE_OPTIONS:
+               if (!sdi) {
+                       /* sdi == NULL: return drvopts. */
+                       if (!drvopts || drvopts == NO_OPTS)
+                               return SR_ERR_ARG;
+                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                               drvopts, drvsize, sizeof(uint32_t));
+               } else if (sdi && !cg) {
+                       /* sdi != NULL, cg == NULL: return devopts. */
+                       if (!devopts || devopts == NO_OPTS)
+                               return SR_ERR_ARG;
+                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                               devopts, devsize, sizeof(uint32_t));
+               } else {
+                       /*
+                        * Note: sdi != NULL, cg != NULL is not handled by
+                        * this function since it's very driver-specific.
+                        */
+                       sr_err("%s: %s: sdi/cg != NULL: not handling.",
+                              sdi->driver->name, __func__);
+                       return SR_ERR_ARG;
+               }
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV GVariant *std_gvar_tuple_array(const uint64_t a[][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(a[i][0]);
+               rational[1] = g_variant_new_uint64(a[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);
+}
+
+SR_PRIV GVariant *std_gvar_tuple_rational(const struct sr_rational *r, 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(r[i].p);
+               rational[1] = g_variant_new_uint64(r[i].q);
+
+               /* 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 GVariant *samplerate_helper(const uint64_t samplerates[], unsigned int n, const char *str)
+{
+       GVariant *gvar;
+       GVariantBuilder gvb;
+
+       g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+       gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
+                       n, sizeof(uint64_t));
+       g_variant_builder_add(&gvb, "{sv}", str, gvar);
+
+       return g_variant_builder_end(&gvb);
+}
+
+SR_PRIV GVariant *std_gvar_samplerates(const uint64_t samplerates[], unsigned int n)
+{
+       return samplerate_helper(samplerates, n, "samplerates");
+}
+
+SR_PRIV GVariant *std_gvar_samplerates_steps(const uint64_t samplerates[], unsigned int n)
+{
+       return samplerate_helper(samplerates, n, "samplerate-steps");
+}
+
+SR_PRIV GVariant *std_gvar_min_max_step(double min, double max, double step)
+{
+       GVariantBuilder gvb;
+
+       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+       g_variant_builder_add_value(&gvb, g_variant_new_double(min));
+       g_variant_builder_add_value(&gvb, g_variant_new_double(max));
+       g_variant_builder_add_value(&gvb, g_variant_new_double(step));
+
+       return g_variant_builder_end(&gvb);
+}
+
+SR_PRIV GVariant *std_gvar_min_max_step_array(const double a[3])
+{
+       unsigned int i;
+       GVariantBuilder gvb;
+
+       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+       for (i = 0; i < 3; i++)
+               g_variant_builder_add_value(&gvb, g_variant_new_double(a[i]));
+
+       return g_variant_builder_end(&gvb);
+}
+
+SR_PRIV GVariant *std_gvar_min_max_step_thresholds(const double min, const double max, const double step)
+{
+       double d, v;
+       GVariant *gvar, *range[2];
+       GVariantBuilder gvb;
+
+       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+       for (d = min; d <= max; d += step) {
+               /*
+                * We will never see exactly 0.0 because of the error we're
+                * accumulating, so catch the "zero" value and force it to be 0.
+                */
+               v = ((d > (-step / 2)) && (d < (step / 2))) ? 0 : d;
+
+               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);
+       }
+
+       return g_variant_builder_end(&gvb);
+}
+
+SR_PRIV GVariant *std_gvar_tuple_u64(uint64_t low, uint64_t high)
+{
+       GVariant *range[2];
+
+       range[0] = g_variant_new_uint64(low);
+       range[1] = g_variant_new_uint64(high);
+
+       return g_variant_new_tuple(range, 2);
+}
+
+SR_PRIV GVariant *std_gvar_tuple_double(double low, double high)
+{
+       GVariant *range[2];
+
+       range[0] = g_variant_new_double(low);
+       range[1] = g_variant_new_double(high);
+
+       return g_variant_new_tuple(range, 2);
+}
+
+SR_PRIV GVariant *std_gvar_array_i32(const int32_t a[], unsigned int n)
+{
+       return g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+                               a, n, sizeof(int32_t));
+}
+
+SR_PRIV GVariant *std_gvar_array_u32(const uint32_t a[], unsigned int n)
+{
+       return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                               a, n, sizeof(uint32_t));
+}
+
+SR_PRIV GVariant *std_gvar_array_u64(const uint64_t a[], unsigned int n)
+{
+       return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
+                               a, n, sizeof(uint64_t));
+}
+
+SR_PRIV GVariant *std_gvar_array_str(const char *a[], unsigned int n)
+{
+       GVariant *gvar;
+       GVariantBuilder *builder;
+       unsigned int i;
+
+       builder = g_variant_builder_new(G_VARIANT_TYPE ("as"));
+
+       for (i = 0; i < n; i++)
+               g_variant_builder_add(builder, "s", a[i]);
+
+       gvar = g_variant_new("as", builder);
+       g_variant_builder_unref(builder);
+
+       return gvar;
+}
+
+SR_PRIV GVariant *std_gvar_thresholds(const double a[][2], unsigned int n)
+{
+       unsigned int i;
+       GVariant *gvar, *range[2];
+       GVariantBuilder gvb;
+
+       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+       for (i = 0; i < n; i++) {
+               range[0] = g_variant_new_double(a[i][0]);
+               range[1] = g_variant_new_double(a[i][1]);
+               gvar = g_variant_new_tuple(range, 2);
+               g_variant_builder_add_value(&gvb, gvar);
+       }
+
+       return g_variant_builder_end(&gvb);
+}
+
+/* Return the index of 'data' in the array 'arr' (or -1). */
+static int find_in_array(GVariant *data, const GVariantType *type,
+                        const void *arr, unsigned int n)
+{
+       const char * const *sarr;
+       const char *s;
+       const uint64_t *u64arr;
+       const uint8_t *u8arr;
+       uint64_t u64;
+       uint8_t u8;
+       unsigned int i;
+
+       if (!g_variant_is_of_type(data, type))
+               return -1;
+
+       switch (g_variant_classify(data)) {
+       case G_VARIANT_CLASS_STRING:
+               s = g_variant_get_string(data, NULL);
+               sarr = arr;
+
+               for (i = 0; i < n; i++)
+                       if (!strcmp(s, sarr[i]))
+                               return i;
+               break;
+       case G_VARIANT_CLASS_UINT64:
+               u64 = g_variant_get_uint64(data);
+               u64arr = arr;
+
+               for (i = 0; i < n; i++)
+                       if (u64 == u64arr[i])
+                               return i;
+               break;
+       case G_VARIANT_CLASS_BYTE:
+               u8 = g_variant_get_byte(data);
+               u8arr = arr;
+
+               for (i = 0; i < n; i++)
+                       if (u8 == u8arr[i])
+                               return i;
+       default:
+               break;
+       }
+
+       return -1;
+}
+
+SR_PRIV int std_str_idx(GVariant *data, const char *a[], unsigned int n)
+{
+       return find_in_array(data, G_VARIANT_TYPE_STRING, a, n);
+}
+
+SR_PRIV int std_u64_idx(GVariant *data, const uint64_t a[], unsigned int n)
+{
+       return find_in_array(data, G_VARIANT_TYPE_UINT64, a, n);
+}
+
+SR_PRIV int std_u8_idx(GVariant *data, const uint8_t a[], unsigned int n)
+{
+       return find_in_array(data, G_VARIANT_TYPE_BYTE, a, n);
+}
+
+SR_PRIV int std_str_idx_s(const char *s, const char *a[], unsigned int n)
+{
+       int idx;
+       GVariant *data;
+
+       data = g_variant_new_string(s);
+       idx = find_in_array(data, G_VARIANT_TYPE_STRING, a, n);
+       g_variant_unref(data);
+
+       return idx;
+}
+
+SR_PRIV int std_u8_idx_s(uint8_t b, const uint8_t a[], unsigned int n)
+{
+       int idx;
+       GVariant *data;
+
+       data = g_variant_new_byte(b);
+       idx = find_in_array(data, G_VARIANT_TYPE_BYTE, a, n);
+       g_variant_unref(data);
+
+       return idx;
+}
+
+SR_PRIV int std_u64_tuple_idx(GVariant *data, const uint64_t a[][2], unsigned int n)
+{
+       unsigned int i;
+       uint64_t low, high;
+
+       g_variant_get(data, "(tt)", &low, &high);
+
+       for (i = 0; i < n; i++)
+               if (a[i][0] == low && a[i][1] == high)
+                       return i;
+
+       return -1;
+}
+
+SR_PRIV int std_double_tuple_idx(GVariant *data, const double a[][2], unsigned int n)
+{
+       unsigned int i;
+       double low, high;
+
+       g_variant_get(data, "(dd)", &low, &high);
+
+       for (i = 0; i < n; i++)
+               if ((fabs(a[i][0] - low) < 0.1) && ((fabs(a[i][1] - high) < 0.1)))
+                       return i;
+
+       return -1;
+}
+
+SR_PRIV int std_double_tuple_idx_d0(const double d, const double a[][2], unsigned int n)
+{
+       unsigned int i;
+
+       for (i = 0; i < n; i++)
+               if (d == a[i][0])
+                       return i;
+
+       return -1;
+}
+
+SR_PRIV int std_cg_idx(const struct sr_channel_group *cg, struct sr_channel_group *a[], unsigned int n)
+{
+       unsigned int i;
+
+       for (i = 0; i < n; i++)
+               if (cg == a[i])
+                       return i;
+
+       return -1;
+}
index 72378d4a2942d5dd7f411e35d41ffb7a01b9a183..93e424ee1f047d7ba96a23c4b657450597a2c94c 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+/* Needed for POSIX.1-2008 locale functions */
+#define _XOPEN_SOURCE 700
 #include <config.h>
+#include <ctype.h>
+#include <locale.h>
+#if defined(__FreeBSD__) || defined(__APPLE__)
+#include <xlocale.h>
+#endif
+#if defined(__FreeBSD__)
+#include <sys/param.h>
+#endif
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -66,6 +76,9 @@ SR_PRIV int sr_atol(const char *str, long *ret)
        errno = 0;
        tmp = strtol(str, &endptr, 10);
 
+       while (endptr && isspace(*endptr))
+               endptr++;
+
        if (!endptr || *endptr || errno) {
                if (!errno)
                        errno = EINVAL;
@@ -128,6 +141,9 @@ SR_PRIV int sr_atod(const char *str, double *ret)
        errno = 0;
        tmp = strtof(str, &endptr);
 
+       while (endptr && isspace(*endptr))
+               endptr++;
+
        if (!endptr || *endptr || errno) {
                if (!errno)
                        errno = EINVAL;
@@ -168,6 +184,38 @@ SR_PRIV int sr_atof(const char *str, float *ret)
        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. This version ignores the locale.
+ *
+ * @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.
+ */
+SR_PRIV int sr_atod_ascii(const char *str, double *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;
+       }
+
+       *ret = tmp;
+       return SR_OK;
+}
+
 /**
  * @private
  *
@@ -210,6 +258,374 @@ SR_PRIV int sr_atof_ascii(const char *str, float *ret)
        return SR_OK;
 }
 
+/**
+ * Compose a string with a format string in the buffer pointed to by buf.
+ *
+ * It is up to the caller to ensure that the allocated buffer is large enough
+ * to hold the formatted result.
+ *
+ * A terminating NUL character is automatically appended after the content
+ * written.
+ *
+ * After the format parameter, the function expects at least as many additional
+ * arguments as needed for format.
+ *
+ * This version ignores the current locale and uses the locale "C" for Linux,
+ * FreeBSD, OSX and Android.
+ *
+ * @param buf Pointer to a buffer where the resulting C string is stored.
+ * @param format C string that contains a format string (see printf).
+ * @param ... A sequence of additional arguments, each containing a value to be
+ *        used to replace a format specifier in the format string.
+ *
+ * @return On success, the number of characters that would have been written,
+ *         not counting the terminating NUL character.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_sprintf_ascii(char *buf, const char *format, ...)
+{
+       int ret;
+       va_list args;
+
+       va_start(args, format);
+       ret = sr_vsprintf_ascii(buf, format, args);
+       va_end(args);
+
+       return ret;
+}
+
+/**
+ * Compose a string with a format string in the buffer pointed to by buf.
+ *
+ * It is up to the caller to ensure that the allocated buffer is large enough
+ * to hold the formatted result.
+ *
+ * Internally, the function retrieves arguments from the list identified by
+ * args as if va_arg was used on it, and thus the state of args is likely to
+ * be altered by the call.
+ *
+ * In any case, args should have been initialized by va_start at some point
+ * before the call, and it is expected to be released by va_end at some point
+ * after the call.
+ *
+ * This version ignores the current locale and uses the locale "C" for Linux,
+ * FreeBSD, OSX and Android.
+ *
+ * @param buf Pointer to a buffer where the resulting C string is stored.
+ * @param format C string that contains a format string (see printf).
+ * @param args A value identifying a variable arguments list initialized with
+ *        va_start.
+ *
+ * @return On success, the number of characters that would have been written,
+ *         not counting the terminating NUL character.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_vsprintf_ascii(char *buf, const char *format, va_list args)
+{
+#if defined(_WIN32)
+       int ret;
+
+#if 0
+       /*
+        * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
+        *       Doesn't start because of "Procedure entry point _create_locale
+        *       not found in msvcrt.dll".
+        *       mingw-w64 should link to msvcr100.dll not msvcrt.dll!
+        * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx
+        */
+       _locale_t locale;
+
+       locale = _create_locale(LC_NUMERIC, "C");
+       ret = _vsprintf_l(buf, format, locale, args);
+       _free_locale(locale);
+#endif
+
+       /* vsprintf() uses the current locale, may not work correctly for floats. */
+       ret = vsprintf(buf, format, args);
+
+       return ret;
+#elif defined(__APPLE__)
+       /*
+        * See:
+        * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
+        * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
+        */
+       int ret;
+       locale_t locale;
+
+       locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+       ret = vsprintf_l(buf, locale, format, args);
+       freelocale(locale);
+
+       return ret;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
+       /*
+        * See:
+        * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
+        * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
+        */
+       int ret;
+       locale_t locale;
+
+       locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+       ret = vsprintf_l(buf, locale, format, args);
+       freelocale(locale);
+
+       return ret;
+#elif defined(__ANDROID__)
+       /*
+        * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
+        * aka "en_US.UTF-8"). The decimal point is hard coded as "."
+        * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
+        */
+       int ret;
+
+       ret = vsprintf(buf, format, args);
+
+       return ret;
+#elif defined(__linux__)
+       int ret;
+       locale_t old_locale, temp_locale;
+
+       /* Switch to C locale for proper float/double conversion. */
+       temp_locale = newlocale(LC_NUMERIC, "C", NULL);
+       old_locale = uselocale(temp_locale);
+
+       ret = vsprintf(buf, format, args);
+
+       /* Switch back to original locale. */
+       uselocale(old_locale);
+       freelocale(temp_locale);
+
+       return ret;
+#elif defined(__unix__) || defined(__unix)
+       /*
+        * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
+        * using the current locale for snprintf(). This may not work correctly
+        * for floats!
+        */
+       int ret;
+
+       ret = vsprintf(buf, format, args);
+
+       return ret;
+#else
+       /* No implementation for unknown systems! */
+       return -1;
+#endif
+}
+
+/**
+ * Composes a string with a format string (like printf) in the buffer pointed
+ * by buf (taking buf_size as the maximum buffer capacity to fill).
+ * If the resulting string would be longer than n - 1 characters, the remaining
+ * characters are discarded and not stored, but counted for the value returned
+ * by the function.
+ * A terminating NUL character is automatically appended after the content
+ * written.
+ * After the format parameter, the function expects at least as many additional
+ * arguments as needed for format.
+ *
+ * This version ignores the current locale and uses the locale "C" for Linux,
+ * FreeBSD, OSX and Android.
+ *
+ * @param buf Pointer to a buffer where the resulting C string is stored.
+ * @param buf_size Maximum number of bytes to be used in the buffer. The
+ *        generated string has a length of at most buf_size - 1, leaving space
+ *        for the additional terminating NUL character.
+ * @param format C string that contains a format string (see printf).
+ * @param ... A sequence of additional arguments, each containing a value to be
+ *        used to replace a format specifier in the format string.
+ *
+ * @return On success, the number of characters that would have been written if
+ *         buf_size had been sufficiently large, not counting the terminating
+ *         NUL character. On failure, a negative number is returned.
+ *         Notice that only when this returned value is non-negative and less
+ *         than buf_size, the string has been completely written.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_snprintf_ascii(char *buf, size_t buf_size,
+       const char *format, ...)
+{
+       int ret;
+       va_list args;
+
+       va_start(args, format);
+       ret = sr_vsnprintf_ascii(buf, buf_size, format, args);
+       va_end(args);
+
+       return ret;
+}
+
+/**
+ * Composes a string with a format string (like printf) in the buffer pointed
+ * by buf (taking buf_size as the maximum buffer capacity to fill).
+ * If the resulting string would be longer than n - 1 characters, the remaining
+ * characters are discarded and not stored, but counted for the value returned
+ * by the function.
+ * A terminating NUL character is automatically appended after the content
+ * written.
+ * Internally, the function retrieves arguments from the list identified by
+ * args as if va_arg was used on it, and thus the state of args is likely to
+ * be altered by the call.
+ * In any case, arg should have been initialized by va_start at some point
+ * before the call, and it is expected to be released by va_end at some point
+ * after the call.
+ *
+ * This version ignores the current locale and uses the locale "C" for Linux,
+ * FreeBSD, OSX and Android.
+ *
+ * @param buf Pointer to a buffer where the resulting C string is stored.
+ * @param buf_size Maximum number of bytes to be used in the buffer. The
+ *        generated string has a length of at most buf_size - 1, leaving space
+ *        for the additional terminating NUL character.
+ * @param format C string that contains a format string (see printf).
+ * @param args A value identifying a variable arguments list initialized with
+ *        va_start.
+ *
+ * @return On success, the number of characters that would have been written if
+ *         buf_size had been sufficiently large, not counting the terminating
+ *         NUL character. On failure, a negative number is returned.
+ *         Notice that only when this returned value is non-negative and less
+ *         than buf_size, the string has been completely written.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size,
+       const char *format, va_list args)
+{
+#if defined(_WIN32)
+       int ret;
+
+#if 0
+       /*
+        * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
+        *       Doesn't start because of "Procedure entry point _create_locale
+        *       not found in msvcrt.dll".
+        *       mingw-w64 should link to msvcr100.dll not msvcrt.dll!.
+        * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx
+        */
+       _locale_t locale;
+
+       locale = _create_locale(LC_NUMERIC, "C");
+       ret = _vsnprintf_l(buf, buf_size, format, locale, args);
+       _free_locale(locale);
+#endif
+
+       /* vsprintf uses the current locale, may cause issues for floats. */
+       ret = vsnprintf(buf, buf_size, format, args);
+
+       return ret;
+#elif defined(__APPLE__)
+       /*
+        * See:
+        * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
+        * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
+        */
+       int ret;
+       locale_t locale;
+
+       locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+       ret = vsnprintf_l(buf, buf_size, locale, format, args);
+       freelocale(locale);
+
+       return ret;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
+       /*
+        * See:
+        * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
+        * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
+        */
+       int ret;
+       locale_t locale;
+
+       locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+       ret = vsnprintf_l(buf, buf_size, locale, format, args);
+       freelocale(locale);
+
+       return ret;
+#elif defined(__ANDROID__)
+       /*
+        * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
+        * aka "en_US.UTF-8"). The decimal point is hard coded as ".".
+        * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
+        */
+       int ret;
+
+       ret = vsnprintf(buf, buf_size, format, args);
+
+       return ret;
+#elif defined(__linux__)
+       int ret;
+       locale_t old_locale, temp_locale;
+
+       /* Switch to C locale for proper float/double conversion. */
+       temp_locale = newlocale(LC_NUMERIC, "C", NULL);
+       old_locale = uselocale(temp_locale);
+
+       ret = vsnprintf(buf, buf_size, format, args);
+
+       /* Switch back to original locale. */
+       uselocale(old_locale);
+       freelocale(temp_locale);
+
+       return ret;
+#elif defined(__unix__) || defined(__unix)
+       /*
+        * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
+        * using the current locale for snprintf(). This may not work correctly
+        * for floats!
+        */
+       int ret;
+
+       ret = vsnprintf(buf, buf_size, format, args);
+
+       return ret;
+#else
+       /* No implementation for unknown systems! */
+       return -1;
+#endif
+}
+
+/**
+ * Convert a sequence of bytes to its textual representation ("hex dump").
+ *
+ * Callers should free the allocated GString. See @ref sr_hexdump_free().
+ *
+ * @param[in] data Pointer to the byte sequence to print.
+ * @param[in] len Number of bytes to print.
+ *
+ * @return #NULL upon error, newly allocated GString pointer otherwise.
+ */
+SR_PRIV GString *sr_hexdump_new(const uint8_t *data, const size_t len)
+{
+       GString *s;
+       size_t i;
+
+       s = g_string_sized_new(3 * len);
+       for (i = 0; i < len; i++) {
+               if (i)
+                       g_string_append_c(s, ' ');
+               g_string_append_printf(s, "%02x", data[i]);
+       }
+
+       return s;
+}
+
+/**
+ * Free a hex dump text that was created by @ref sr_hexdump_new().
+ *
+ * @param[in] s Pointer to the GString to release.
+ */
+SR_PRIV void sr_hexdump_free(GString *s)
+{
+       if (s)
+               g_string_free(s, TRUE);
+}
+
 /**
  * Convert a string representation of a numeric value to a sr_rational.
  *
@@ -233,21 +649,49 @@ SR_API int sr_parse_rational(const char *str, struct sr_rational *ret)
        int64_t denominator = 1;
        int32_t fractional_len = 0;
        int32_t exponent = 0;
+       gboolean is_negative = FALSE;
+       gboolean no_integer, no_fractional;
+
+       while (isspace(*str))
+               str++;
 
        errno = 0;
        integral = g_ascii_strtoll(str, &endptr, 10);
 
-       if (errno)
+       if (str == endptr && (str[0] == '-' || str[0] == '+') && str[1] == '.') {
+               endptr += 1;
+               no_integer = TRUE;
+       } else if (str == endptr && str[0] == '.') {
+               no_integer = TRUE;
+       } else if (errno) {
                return SR_ERR;
+       } else {
+               no_integer = FALSE;
+       }
 
+       if (integral < 0 || str[0] == '-')
+               is_negative = TRUE;
+
+       errno = 0;
        if (*endptr == '.') {
-               const char* start = endptr + 1;
+               gboolean is_exp, is_eos;
+               const char *start = endptr + 1;
                fractional = g_ascii_strtoll(start, &endptr, 10);
+               is_exp = *endptr == 'E' || *endptr == 'e';
+               is_eos = *endptr == '\0';
+               if (endptr == start && (is_exp || is_eos)) {
+                       fractional = 0;
+                       errno = 0;
+               }
                if (errno)
                        return SR_ERR;
+               no_fractional = endptr == start;
+               if (no_integer && no_fractional)
+                       return SR_ERR;
                fractional_len = endptr - start;
        }
 
+       errno = 0;
        if ((*endptr == 'E') || (*endptr == 'e')) {
                exponent = g_ascii_strtoll(endptr + 1, &endptr, 10);
                if (errno)
@@ -261,7 +705,7 @@ SR_API int sr_parse_rational(const char *str, struct sr_rational *ret)
                integral *= 10;
        exponent -= fractional_len;
 
-       if (integral >= 0)
+       if (!is_negative)
                integral += fractional;
        else
                integral -= fractional;
@@ -366,42 +810,31 @@ SR_API char *sr_samplerate_string(uint64_t samplerate)
 SR_API char *sr_period_string(uint64_t v_p, uint64_t v_q)
 {
        double freq, v;
-       char *o;
-       int prec, r;
+       int prec;
 
        freq = 1 / ((double)v_p / v_q);
 
-       o = g_malloc0(30 + 1);
-
        if (freq > SR_GHZ(1)) {
                v = (double)v_p / v_q * 1000000000000.0;
                prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
-               r = snprintf(o, 30, "%.*f ps", prec, v);
+               return g_strdup_printf("%.*f ps", prec, v);
        } else if (freq > SR_MHZ(1)) {
                v = (double)v_p / v_q * 1000000000.0;
                prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
-               r = snprintf(o, 30, "%.*f ns", prec, v);
+               return g_strdup_printf("%.*f ns", prec, v);
        } else if (freq > SR_KHZ(1)) {
                v = (double)v_p / v_q * 1000000.0;
                prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
-               r = snprintf(o, 30, "%.*f us", prec, v);
+               return g_strdup_printf("%.*f us", prec, v);
        } else if (freq > 1) {
                v = (double)v_p / v_q * 1000.0;
                prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
-               r = snprintf(o, 30, "%.*f ms", prec, v);
+               return g_strdup_printf("%.*f ms", prec, v);
        } else {
                v = (double)v_p / v_q;
                prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
-               r = snprintf(o, 30, "%.*f s", prec, v);
+               return g_strdup_printf("%.*f s", prec, v);
        }
-
-       if (r < 0) {
-               /* Something went wrong... */
-               g_free(o);
-               return NULL;
-       }
-
-       return o;
 }
 
 /**
@@ -422,25 +855,12 @@ SR_API char *sr_period_string(uint64_t v_p, uint64_t v_q)
  */
 SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
 {
-       int r;
-       char *o;
-
-       o = g_malloc0(30 + 1);
-
        if (v_q == 1000)
-               r = snprintf(o, 30, "%" PRIu64 "mV", v_p);
+               return g_strdup_printf("%" PRIu64 " mV", v_p);
        else if (v_q == 1)
-               r = snprintf(o, 30, "%" PRIu64 "V", v_p);
+               return g_strdup_printf("%" 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;
+               return g_strdup_printf("%g V", (float)v_p / (float)v_q);
 }
 
 /**
@@ -462,7 +882,8 @@ SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
  */
 SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
 {
-       int multiplier, done;
+       uint64_t multiplier;
+       int done;
        double frac_part;
        char *s;
 
@@ -489,6 +910,18 @@ SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
                case 'G':
                        multiplier = SR_GHZ(1);
                        break;
+               case 't':
+               case 'T':
+                       multiplier = SR_GHZ(1000);
+                       break;
+               case 'p':
+               case 'P':
+                       multiplier = SR_GHZ(1000 * 1000);
+                       break;
+               case 'e':
+               case 'E':
+                       multiplier = SR_GHZ(1000 * 1000 * 1000);
+                       break;
                default:
                        done = TRUE;
                        s--;
@@ -498,8 +931,9 @@ SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
        if (multiplier > 0) {
                *size *= multiplier;
                *size += frac_part * multiplier;
-       } else
+       } else {
                *size += frac_part;
+       }
 
        if (s && *s && g_ascii_strcasecmp(s, "Hz"))
                return SR_ERR;
@@ -555,8 +989,13 @@ SR_API uint64_t sr_parse_timestring(const char *timestring)
 /** @since 0.1.0 */
 SR_API gboolean sr_parse_boolstring(const char *boolstr)
 {
-       if (!boolstr)
-               return FALSE;
+       /*
+        * Complete absence of an input spec is assumed to mean TRUE,
+        * as in command line option strings like this:
+        *   ...:samplerate=100k:header:numchannels=4:...
+        */
+       if (!boolstr || !*boolstr)
+               return TRUE;
 
        if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
            !g_ascii_strncasecmp(boolstr, "yes", 3) ||
@@ -581,11 +1020,11 @@ SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q)
                while (*s == ' ')
                        s++;
                if (!strcmp(s, "fs"))
-                       *q = 1000000000000000ULL;
+                       *q = UINT64_C(1000000000000000);
                else if (!strcmp(s, "ps"))
-                       *q = 1000000000000ULL;
+                       *q = UINT64_C(1000000000000);
                else if (!strcmp(s, "ns"))
-                       *q = 1000000000ULL;
+                       *q = UINT64_C(1000000000);
                else if (!strcmp(s, "us"))
                        *q = 1000000;
                else if (!strcmp(s, "ms"))
index f53119a6aab181b08c2faf338777da9db9db0712..8a8f0ca56dfff018bd6d6828cd267c0c1e5f4a98 100644 (file)
--- a/src/usb.c
+++ b/src/usb.c
@@ -184,7 +184,7 @@ static LIBUSB_CALL void usb_pollfd_added(libusb_os_handle fd,
                return;
 
        pollfd = g_slice_new(GPollFD);
-#ifdef G_OS_WIN32
+#ifdef _WIN32
        events = G_IO_IN;
 #endif
        pollfd->fd = (gintptr)fd;
@@ -329,8 +329,7 @@ SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn)
                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);
+               /* Trying to find USB device via VID:PID. */
        } else {
                g_match_info_unref(match);
                g_regex_unref(reg);
@@ -343,8 +342,7 @@ SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn)
                        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);
+                       /* Trying to find USB device via bus.address. */
                }
        }
        g_match_info_unref(match);
@@ -386,13 +384,12 @@ SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn)
                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);
+               usb = sr_usb_dev_inst_new(b, a, NULL);
                devices = g_slist_append(devices, usb);
        }
        libusb_free_device_list(devlist, 1);
 
-       sr_dbg("Found %d device(s).", g_slist_length(devices));
+       /* No log message for #devices found (caller will log that). */
 
        return devices;
 }
@@ -510,3 +507,47 @@ SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len)
 
        return SR_OK;
 }
+
+/**
+ * Check the USB configuration to determine if this device has a given
+ * manufacturer and product string.
+ *
+ * @return TRUE if the device's configuration profile strings
+ *         configuration, FALSE otherwise.
+ */
+SR_PRIV gboolean usb_match_manuf_prod(libusb_device *dev,
+               const char *manufacturer, const char *product)
+{
+       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. */
+               libusb_get_device_descriptor(dev, &des);
+
+               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, manufacturer))
+                       break;
+
+               if (libusb_get_string_descriptor_ascii(hdl,
+                               des.iProduct, strdesc, sizeof(strdesc)) < 0)
+                       break;
+               if (strcmp((const char *)strdesc, product))
+                       break;
+
+               ret = TRUE;
+       }
+       if (hdl)
+               libusb_close(hdl);
+
+       return ret;
+}
index 85c6afcd976b80577bc140d3eb97a39ec0ea213a..98d55b1cc6f3958cb6c1f039efef3186d2693a8b 100644 (file)
@@ -197,13 +197,15 @@ START_TEST(test_analog_unit_to_string)
        struct sr_analog_encoding encoding;
        struct sr_analog_meaning meaning;
        struct sr_analog_spec spec;
-       const char *r[] = {" V RMS"};
+       const int u[] = {SR_UNIT_VOLT, SR_UNIT_AMPERE, SR_UNIT_CELSIUS};
+       const int f[] = {SR_MQFLAG_RMS, 0, 0};
+       const char *r[] = {"V RMS", "A", "°C"};
 
        sr_analog_init_(&analog, &encoding, &meaning, &spec, 3);
 
-       for (i = -1; i < ARRAY_SIZE(r); i++) {
-               meaning.unit = SR_UNIT_VOLT;
-               meaning.mqflags = SR_MQFLAG_RMS;
+       for (i = 0; i < ARRAY_SIZE(r); i++) {
+               meaning.unit = u[i];
+               meaning.mqflags = f[i];
                ret = sr_analog_unit_to_string(&analog, &result);
                fail_unless(ret == SR_OK);
                fail_unless(result != NULL);
index a36b6a2d06a1fe39a133ceb8c1a1b76df9362246..fca70eaaee36ef270d6725376391add9715197a2 100644 (file)
 
 #include <config.h>
 #include <check.h>
+#include <errno.h>
+#include <locale.h>
 #include <libsigrok/libsigrok.h>
 #include "lib.h"
 
+#if 0
+static void test_vsnprintf(const char *expected, char *format, ...)
+{
+       va_list args;
+       char *s;
+       int len;
+
+       len = 16;
+       s = g_malloc0(len + 1);
+
+       va_start(args, format);
+       len = vsnprintf(s, len, format, args);
+       va_end(args);
+
+       fail_unless(s != NULL,
+                       "Invalid result for '%s': len = %i.", expected, len);
+       fail_unless(!strcmp(s, expected),
+                       "Invalid result for '%s': %s.", expected, s);
+       g_free(s);
+}
+#endif
+
+static void test_sr_vsnprintf_ascii(const char *expected, char *format, ...)
+{
+       va_list args;
+       char *s;
+       int len;
+
+       len = 16;
+       s = g_malloc0(len + 1);
+
+       va_start(args, format);
+       len = sr_vsnprintf_ascii(s, len, format, args);
+       va_end(args);
+
+       fail_unless(s != NULL,
+                       "Invalid result for '%s': len = %i.", expected, len);
+       fail_unless(!strcmp(s, expected),
+                       "Invalid result for '%s': %s.", expected, s);
+       g_free(s);
+}
+
+static void test_sr_vsprintf_ascii(const char *expected, char *format, ...)
+{
+       va_list args, args_copy;
+       char *s;
+       int len;
+
+       /* Get length of buffer required. */
+       va_start(args, format);
+       va_copy(args_copy, args);
+       len = sr_vsnprintf_ascii(NULL, 0, format, args);
+       va_end(args);
+
+       /* Allocate buffer and write out command. */
+       s = g_malloc0(len + 1);
+       len = sr_vsprintf_ascii(s, format, args_copy);
+       va_end(args_copy);
+
+       fail_unless(s != NULL,
+                       "Invalid result for '%s': len = %i.", expected, len);
+       fail_unless(!strcmp(s, expected),
+                       "Invalid result for '%s': %s.", expected, s);
+       g_free(s);
+}
+
 static void test_samplerate(uint64_t samplerate, const char *expected)
 {
        char *s;
@@ -50,12 +118,95 @@ static void test_rational(const char *input, struct sr_rational expected)
        struct sr_rational rational;
 
        ret = sr_parse_rational(input, &rational);
-       fail_unless(ret == SR_OK);
+       fail_unless(ret == SR_OK, "Unexpected rc for '%s': %d, errno %d.",
+               input, ret, errno);
        fail_unless((expected.p == rational.p) && (expected.q == rational.q),
                    "Invalid result for '%s': %ld/%ld'.",
                    input, rational.p, rational.q);
 }
 
+static void test_rational_fail(const char *input)
+{
+       int ret;
+       struct sr_rational rational;
+
+       ret = sr_parse_rational(input, &rational);
+       fail_unless(ret != SR_OK, "Unexpected success for '%s'.", input);
+}
+
+static void test_voltage(uint64_t v_p, uint64_t v_q, const char *expected)
+{
+       char *s;
+
+       s = sr_voltage_string(v_p, v_q);
+       fail_unless(s != NULL);
+       fail_unless(!strcmp(s, expected),
+                   "Invalid result for '%s': %s.", expected, s);
+       g_free(s);
+}
+
+START_TEST(test_locale)
+{
+       char *old_locale, *saved_locale;
+
+       /* Get the the current locale. */
+       old_locale = setlocale(LC_NUMERIC, NULL);
+       fprintf(stderr, "Old locale = %s\n", old_locale);
+       /* Copy the name so it won’t be clobbered by setlocale. */
+       saved_locale = g_strdup(old_locale);
+       ck_assert_msg(saved_locale != NULL);
+
+#ifdef _WIN32
+       /*
+        * See: https://msdn.microsoft.com/en-us/library/cc233982.aspx
+        * Doesn't work! Locale is not set!
+        */
+       setlocale(LC_NUMERIC, "de-DE");
+#else
+       /*
+        * For all *nix and OSX systems, change the locale for all threads to
+        * one that is known for not working correctly with printf(), e.g.
+        * "de_DE.UTF-8".
+        *
+        * Find all your available system locales with "locale -a".
+        */
+       setlocale(LC_NUMERIC, "de_DE.UTF-8");
+#endif
+       fprintf(stderr, "New locale = %s\n", setlocale(LC_NUMERIC, NULL));
+
+       test_sr_vsnprintf_ascii("0.1", "%.1f", (double)0.1);
+       test_sr_vsnprintf_ascii("0.12", "%.2f", (double)0.12);
+       test_sr_vsnprintf_ascii("0.123", "%.3f", (double)0.123);
+       test_sr_vsnprintf_ascii("0.1234", "%.4f", (double)0.1234);
+       test_sr_vsnprintf_ascii("0.12345", "%.5f", (double)0.12345);
+       test_sr_vsnprintf_ascii("0.123456", "%.6f", (double)0.123456);
+
+       test_sr_vsprintf_ascii("0.1", "%.1f", (double)0.1);
+       test_sr_vsprintf_ascii("0.12", "%.2f", (double)0.12);
+       test_sr_vsprintf_ascii("0.123", "%.3f", (double)0.123);
+       test_sr_vsprintf_ascii("0.1234", "%.4f", (double)0.1234);
+       test_sr_vsprintf_ascii("0.12345", "%.5f", (double)0.12345);
+       test_sr_vsprintf_ascii("0.123456", "%.6f", (double)0.123456);
+
+#if 0
+       /*
+        * These tests can be used to tell on which platforms the printf()
+        * functions are locale-dependent (i.e. these tests will fail).
+        */
+       test_vsnprintf("0.1", "%.1f", (double)0.1);
+       test_vsnprintf("0.12", "%.2f", (double)0.12);
+       test_vsnprintf("0.123", "%.3f", (double)0.123);
+       test_vsnprintf("0.1234", "%.4f", (double)0.1234);
+       test_vsnprintf("0.12345", "%.5f", (double)0.12345);
+       test_vsnprintf("0.123456", "%.6f", (double)0.123456);
+#endif
+
+       /* Restore the original locale. */
+       setlocale(LC_NUMERIC, saved_locale);
+       g_free(saved_locale);
+}
+END_TEST
+
 /*
  * Check various inputs for sr_samplerate_string():
  *
@@ -144,19 +295,17 @@ END_TEST
 
 START_TEST(test_ghz)
 {
-       /* Note: Numbers > 2^32 need a ULL suffix. */
-
-       test_samplerate(1000000000, "1 GHz");
-       test_samplerate(5000000000ULL, "5 GHz");
-       test_samplerate(72000000000ULL, "72 GHz");
-       test_samplerate(388000000000ULL, "388 GHz");
-       test_samplerate(4417594444ULL, "4.417594444 GHz");
-       test_samplerate(44175944444ULL, "44.175944444 GHz");
-       test_samplerate(441759444441ULL, "441.759444441 GHz");
-       test_samplerate(441759000001ULL, "441.759000001 GHz");
-       test_samplerate(441050000000ULL, "441.05 GHz");
-       test_samplerate(441000000005ULL, "441.000000005 GHz");
-       test_samplerate(441500000000ULL, "441.5 GHz");
+       test_samplerate(UINT64_C(1000000000), "1 GHz");
+       test_samplerate(UINT64_C(5000000000), "5 GHz");
+       test_samplerate(UINT64_C(72000000000), "72 GHz");
+       test_samplerate(UINT64_C(388000000000), "388 GHz");
+       test_samplerate(UINT64_C(4417594444), "4.417594444 GHz");
+       test_samplerate(UINT64_C(44175944444), "44.175944444 GHz");
+       test_samplerate(UINT64_C(441759444441), "441.759444441 GHz");
+       test_samplerate(UINT64_C(441759000001), "441.759000001 GHz");
+       test_samplerate(UINT64_C(441050000000), "441.05 GHz");
+       test_samplerate(UINT64_C(441000000005), "441.000000005 GHz");
+       test_samplerate(UINT64_C(441500000000), "441.5 GHz");
 
        /* Again, but now using SR_GHZ(). */
        test_samplerate(SR_GHZ(1), "1 GHz");
@@ -172,8 +321,8 @@ START_TEST(test_ghz)
        test_samplerate(SR_GHZ(441.500000000), "441.5 GHz");
 
        /* Now check the biggest-possible samplerate (2^64 Hz). */
-       // test_samplerate(18446744073709551615ULL, "18446744073.709551615 GHz");
-       // test_samplerate(SR_GHZ(18446744073ULL), "18446744073 GHz");
+       // test_samplerate(UINT64_C(18446744073709551615), "18446744073.709551615 GHz");
+       // test_samplerate(SR_GHZ(UINT64_C(18446744073)), "18446744073 GHz");
 }
 END_TEST
 
@@ -196,13 +345,12 @@ END_TEST
 
 START_TEST(test_ghz_period)
 {
-       /* Note: Numbers > 2^32 need a ULL suffix. */
-       test_period(1, 1000000000, "1 ns");
-       test_period(1, 5000000000ULL, "200 ps");
-       test_period(1, 72000000000ULL, "13.889 ps");
-       test_period(1, 388000000000ULL, "2.577 ps");
-       test_period(10, 1000000000000, "10 ps");
-       test_period(200, 1000000000000ULL, "200 ps");
+       test_period(1, UINT64_C(1000000000), "1 ns");
+       test_period(1, UINT64_C(5000000000), "200 ps");
+       test_period(1, UINT64_C(72000000000), "13.889 ps");
+       test_period(1, UINT64_C(388000000000), "2.577 ps");
+       test_period(10, UINT64_C(1000000000000), "10 ps");
+       test_period(200, UINT64_C(1000000000000), "200 ps");
 
        /* Again, but now using SR_GHZ(). */
        test_period(1, SR_GHZ(1), "1 ns");
@@ -214,6 +362,19 @@ START_TEST(test_ghz_period)
 }
 END_TEST
 
+START_TEST(test_volt)
+{
+       test_voltage(34, 1, "34 V");
+       test_voltage(34, 2, "17 V");
+       test_voltage(1, 1, "1 V");
+       test_voltage(1, 5, "0.2 V");
+       test_voltage(200, 1000, "200 mV");
+       test_voltage(1, 72, "0.0138889 V");
+       test_voltage(1, 388, "0.00257732 V");
+       test_voltage(10, 1000, "10 mV");
+}
+END_TEST
+
 START_TEST(test_integral)
 {
        test_rational("1", (struct sr_rational){1, 1});
@@ -231,6 +392,21 @@ START_TEST(test_fractional)
        test_rational("12.34", (struct sr_rational){1234, 100});
        test_rational("-12.34", (struct sr_rational){-1234, 100});
        test_rational("10.00", (struct sr_rational){1000, 100});
+       test_rational(".1", (struct sr_rational){1, 10});
+       test_rational("+0.1", (struct sr_rational){1, 10});
+       test_rational("+.1", (struct sr_rational){1, 10});
+       test_rational("-0.1", (struct sr_rational){-1, 10});
+       test_rational("-.1", (struct sr_rational){-1, 10});
+       test_rational(".1", (struct sr_rational){1, 10});
+       test_rational(".123", (struct sr_rational){123, 1000});
+       test_rational("1.", (struct sr_rational){1, 1});
+       test_rational("123.", (struct sr_rational){123, 1});
+       test_rational("-.1", (struct sr_rational){-1, 10});
+       test_rational(" .1", (struct sr_rational){1, 10});
+       test_rational("+.1", (struct sr_rational){1, 10});
+       test_rational_fail(".");
+       test_rational_fail(".e");
+       test_rational_fail(".e1");
 }
 END_TEST
 
@@ -246,6 +422,11 @@ START_TEST(test_exponent)
        test_rational("0.001e3", (struct sr_rational){1, 1});
        test_rational("0.001e0", (struct sr_rational){1, 1000});
        test_rational("0.001e-3", (struct sr_rational){1, 1000000});
+       test_rational("43.737E-3", (struct sr_rational){43737, 1000000});
+       test_rational("-0.1e-2", (struct sr_rational){-1, 1000});
+       test_rational("-.1e-2", (struct sr_rational){-1, 1000});
+       test_rational("-.0e-2", (struct sr_rational){0, 1000});
+       test_rational("+.0e-2", (struct sr_rational){0, 1000});
 }
 END_TEST
 
@@ -258,12 +439,14 @@ Suite *suite_strutil(void)
 
        tc = tcase_create("sr_samplerate_string");
        tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
+       tcase_add_test(tc, test_locale);
        tcase_add_test(tc, test_hz);
        tcase_add_test(tc, test_khz);
        tcase_add_test(tc, test_mhz);
        tcase_add_test(tc, test_ghz);
        tcase_add_test(tc, test_hz_period);
        tcase_add_test(tc, test_ghz_period);
+       tcase_add_test(tc, test_volt);
        tcase_add_test(tc, test_integral);
        tcase_add_test(tc, test_fractional);
        tcase_add_test(tc, test_exponent);