Compare commits
13 Commits
hw/sq50
...
libsigrok-
| Author | SHA1 | Date |
|---|---|---|
|
|
a6b07d7e28 | |
|
|
c66fde84f6 | |
|
|
c1a8e19d9a | |
|
|
fdb297c6de | |
|
|
e4204b1757 | |
|
|
8686b747cd | |
|
|
45106f0ca5 | |
|
|
6cec31aeac | |
|
|
d0667da66f | |
|
|
2d5ccd2601 | |
|
|
d2af13d03f | |
|
|
8cd15dd4ce | |
|
|
1aba657270 |
|
|
@ -8,8 +8,6 @@
|
||||||
/configure.lineno
|
/configure.lineno
|
||||||
/m4/libtool.m4
|
/m4/libtool.m4
|
||||||
/m4/lt*.m4
|
/m4/lt*.m4
|
||||||
/build
|
|
||||||
/inst
|
|
||||||
|
|
||||||
# Editor/IDE cruft
|
# Editor/IDE cruft
|
||||||
*.kate-swp
|
*.kate-swp
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
4
HACKING
4
HACKING
|
|
@ -164,9 +164,7 @@ Doxygen
|
||||||
|
|
||||||
- Mark private functions (SR_PRIV) with /** @private */, so that Doxygen
|
- Mark private functions (SR_PRIV) with /** @private */, so that Doxygen
|
||||||
doesn't include them in the output. Functions that are "static" anyway
|
doesn't include them in the output. Functions that are "static" anyway
|
||||||
don't need to be marked like this. Functions in non-public files that
|
don't need to be marked like this.
|
||||||
are explicitly excluded in Doxyfile don't need to be marked either.
|
|
||||||
Don't use @internal, always use @private instead.
|
|
||||||
|
|
||||||
- Mark private variables/#defines with /** @cond PRIVATE */ and
|
- Mark private variables/#defines with /** @cond PRIVATE */ and
|
||||||
/** @endcond */, so that Doxygen doesn't include them in the output.
|
/** @endcond */, so that Doxygen doesn't include them in the output.
|
||||||
|
|
|
||||||
73
Makefile.am
73
Makefile.am
|
|
@ -27,7 +27,7 @@ DISTCHECK_CONFIGURE_FLAGS = --disable-python
|
||||||
|
|
||||||
FIRMWARE_DIR = $(datadir)/sigrok-firmware
|
FIRMWARE_DIR = $(datadir)/sigrok-firmware
|
||||||
|
|
||||||
local_includes = -Iinclude -I$(srcdir)/include -I$(srcdir)/src -I. @RPC_CFLAGS@
|
local_includes = -Iinclude -I$(srcdir)/include -I$(srcdir)/src -I.
|
||||||
if BINDINGS_CXX
|
if BINDINGS_CXX
|
||||||
local_includes += -Ibindings/cxx/include -I$(srcdir)/bindings/cxx/include -Ibindings/cxx
|
local_includes += -Ibindings/cxx/include -I$(srcdir)/bindings/cxx/include -Ibindings/cxx
|
||||||
endif
|
endif
|
||||||
|
|
@ -50,9 +50,7 @@ lib_LTLIBRARIES = libsigrok.la
|
||||||
# Backend files
|
# Backend files
|
||||||
libsigrok_la_SOURCES = \
|
libsigrok_la_SOURCES = \
|
||||||
src/backend.c \
|
src/backend.c \
|
||||||
src/binary_helpers.c \
|
|
||||||
src/conversion.c \
|
src/conversion.c \
|
||||||
src/crc.c \
|
|
||||||
src/device.c \
|
src/device.c \
|
||||||
src/session.c \
|
src/session.c \
|
||||||
src/session_file.c \
|
src/session_file.c \
|
||||||
|
|
@ -73,13 +71,11 @@ libsigrok_la_SOURCES = \
|
||||||
# Input modules
|
# Input modules
|
||||||
libsigrok_la_SOURCES += \
|
libsigrok_la_SOURCES += \
|
||||||
src/input/input.c \
|
src/input/input.c \
|
||||||
src/input/feed_queue.c \
|
|
||||||
src/input/binary.c \
|
src/input/binary.c \
|
||||||
src/input/chronovu_la8.c \
|
src/input/chronovu_la8.c \
|
||||||
src/input/csv.c \
|
src/input/csv.c \
|
||||||
src/input/logicport.c \
|
src/input/logicport.c \
|
||||||
src/input/raw_analog.c \
|
src/input/raw_analog.c \
|
||||||
src/input/saleae.c \
|
|
||||||
src/input/trace32_ad.c \
|
src/input/trace32_ad.c \
|
||||||
src/input/vcd.c \
|
src/input/vcd.c \
|
||||||
src/input/wav.c \
|
src/input/wav.c \
|
||||||
|
|
@ -165,8 +161,6 @@ endif
|
||||||
libsigrok_la_SOURCES += \
|
libsigrok_la_SOURCES += \
|
||||||
src/dmm/asycii.c \
|
src/dmm/asycii.c \
|
||||||
src/dmm/bm25x.c \
|
src/dmm/bm25x.c \
|
||||||
src/dmm/bm52x.c \
|
|
||||||
src/dmm/bm85x.c \
|
|
||||||
src/dmm/bm86x.c \
|
src/dmm/bm86x.c \
|
||||||
src/dmm/dtm0660.c \
|
src/dmm/dtm0660.c \
|
||||||
src/dmm/eev121gw.c \
|
src/dmm/eev121gw.c \
|
||||||
|
|
@ -175,7 +169,6 @@ libsigrok_la_SOURCES += \
|
||||||
src/dmm/fs9922.c \
|
src/dmm/fs9922.c \
|
||||||
src/dmm/m2110.c \
|
src/dmm/m2110.c \
|
||||||
src/dmm/metex14.c \
|
src/dmm/metex14.c \
|
||||||
src/dmm/mm38xr.c \
|
|
||||||
src/dmm/ms2115b.c \
|
src/dmm/ms2115b.c \
|
||||||
src/dmm/ms8250d.c \
|
src/dmm/ms8250d.c \
|
||||||
src/dmm/rs9lcd.c \
|
src/dmm/rs9lcd.c \
|
||||||
|
|
@ -261,6 +254,13 @@ src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/beaglelogic/beaglelogic_native.c \
|
src/hardware/beaglelogic/beaglelogic_native.c \
|
||||||
src/hardware/beaglelogic/beaglelogic_tcp.c
|
src/hardware/beaglelogic/beaglelogic_tcp.c
|
||||||
endif
|
endif
|
||||||
|
if HW_BRYMEN_DMM
|
||||||
|
src_libdrivers_la_SOURCES += \
|
||||||
|
src/hardware/brymen-dmm/parser.c \
|
||||||
|
src/hardware/brymen-dmm/protocol.h \
|
||||||
|
src/hardware/brymen-dmm/protocol.c \
|
||||||
|
src/hardware/brymen-dmm/api.c
|
||||||
|
endif
|
||||||
if HW_CEM_DT_885X
|
if HW_CEM_DT_885X
|
||||||
src_libdrivers_la_SOURCES += \
|
src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/cem-dt-885x/protocol.h \
|
src/hardware/cem-dt-885x/protocol.h \
|
||||||
|
|
@ -291,12 +291,6 @@ src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/conrad-digi-35-cpu/protocol.c \
|
src/hardware/conrad-digi-35-cpu/protocol.c \
|
||||||
src/hardware/conrad-digi-35-cpu/api.c
|
src/hardware/conrad-digi-35-cpu/api.c
|
||||||
endif
|
endif
|
||||||
if HW_DCTTECH_USBRELAY
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/dcttech-usbrelay/protocol.h \
|
|
||||||
src/hardware/dcttech-usbrelay/protocol.c \
|
|
||||||
src/hardware/dcttech-usbrelay/api.c
|
|
||||||
endif
|
|
||||||
if HW_DEMO
|
if HW_DEMO
|
||||||
src_libdrivers_la_SOURCES += \
|
src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/demo/protocol.h \
|
src/hardware/demo/protocol.h \
|
||||||
|
|
@ -387,12 +381,6 @@ src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/hp-3478a/protocol.c \
|
src/hardware/hp-3478a/protocol.c \
|
||||||
src/hardware/hp-3478a/api.c
|
src/hardware/hp-3478a/api.c
|
||||||
endif
|
endif
|
||||||
if HW_HP_59306A
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/hp-59306a/protocol.h \
|
|
||||||
src/hardware/hp-59306a/protocol.c \
|
|
||||||
src/hardware/hp-59306a/api.c
|
|
||||||
endif
|
|
||||||
if HW_HUNG_CHANG_DSO_2100
|
if HW_HUNG_CHANG_DSO_2100
|
||||||
src_libdrivers_la_SOURCES += \
|
src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/hung-chang-dso-2100/protocol.h \
|
src/hardware/hung-chang-dso-2100/protocol.h \
|
||||||
|
|
@ -411,24 +399,12 @@ src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/ikalogic-scanaplus/protocol.c \
|
src/hardware/ikalogic-scanaplus/protocol.c \
|
||||||
src/hardware/ikalogic-scanaplus/api.c
|
src/hardware/ikalogic-scanaplus/api.c
|
||||||
endif
|
endif
|
||||||
if HW_IKALOGIC_SCANAQUAD
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/ikalogic-scanaquad/protocol.h \
|
|
||||||
src/hardware/ikalogic-scanaquad/protocol.c \
|
|
||||||
src/hardware/ikalogic-scanaquad/api.c
|
|
||||||
endif
|
|
||||||
if HW_IPDBG_LA
|
if HW_IPDBG_LA
|
||||||
src_libdrivers_la_SOURCES += \
|
src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/ipdbg-la/protocol.h \
|
src/hardware/ipdbg-la/protocol.h \
|
||||||
src/hardware/ipdbg-la/protocol.c \
|
src/hardware/ipdbg-la/protocol.c \
|
||||||
src/hardware/ipdbg-la/api.c
|
src/hardware/ipdbg-la/api.c
|
||||||
endif
|
endif
|
||||||
if HW_ITECH_IT8500
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/itech-it8500/protocol.h \
|
|
||||||
src/hardware/itech-it8500/protocol.c \
|
|
||||||
src/hardware/itech-it8500/api.c
|
|
||||||
endif
|
|
||||||
if HW_KECHENG_KC_330B
|
if HW_KECHENG_KC_330B
|
||||||
src_libdrivers_la_SOURCES += \
|
src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/kecheng-kc-330b/protocol.h \
|
src/hardware/kecheng-kc-330b/protocol.h \
|
||||||
|
|
@ -441,12 +417,6 @@ src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/kern-scale/protocol.c \
|
src/hardware/kern-scale/protocol.c \
|
||||||
src/hardware/kern-scale/api.c
|
src/hardware/kern-scale/api.c
|
||||||
endif
|
endif
|
||||||
if HW_KINGST_LA2016
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/kingst-la2016/protocol.h \
|
|
||||||
src/hardware/kingst-la2016/protocol.c \
|
|
||||||
src/hardware/kingst-la2016/api.c
|
|
||||||
endif
|
|
||||||
if HW_KORAD_KAXXXXP
|
if HW_KORAD_KAXXXXP
|
||||||
src_libdrivers_la_SOURCES += \
|
src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/korad-kaxxxxp/protocol.h \
|
src/hardware/korad-kaxxxxp/protocol.h \
|
||||||
|
|
@ -543,24 +513,6 @@ src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/rdtech-dps/protocol.c \
|
src/hardware/rdtech-dps/protocol.c \
|
||||||
src/hardware/rdtech-dps/api.c
|
src/hardware/rdtech-dps/api.c
|
||||||
endif
|
endif
|
||||||
if HW_RDTECH_UM
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/rdtech-um/protocol.h \
|
|
||||||
src/hardware/rdtech-um/protocol.c \
|
|
||||||
src/hardware/rdtech-um/api.c
|
|
||||||
endif
|
|
||||||
if HW_RDTECH_TC
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/rdtech-tc/protocol.h \
|
|
||||||
src/hardware/rdtech-tc/protocol.c \
|
|
||||||
src/hardware/rdtech-tc/api.c
|
|
||||||
endif
|
|
||||||
if HW_RIGOL_DG
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/rigol-dg/protocol.h \
|
|
||||||
src/hardware/rigol-dg/protocol.c \
|
|
||||||
src/hardware/rigol-dg/api.c
|
|
||||||
endif
|
|
||||||
if HW_RIGOL_DS
|
if HW_RIGOL_DS
|
||||||
src_libdrivers_la_SOURCES += \
|
src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/rigol-ds/protocol.h \
|
src/hardware/rigol-ds/protocol.h \
|
||||||
|
|
@ -656,12 +608,6 @@ src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/uni-t-dmm/protocol.c \
|
src/hardware/uni-t-dmm/protocol.c \
|
||||||
src/hardware/uni-t-dmm/api.c
|
src/hardware/uni-t-dmm/api.c
|
||||||
endif
|
endif
|
||||||
if HW_UNI_T_UT181A
|
|
||||||
src_libdrivers_la_SOURCES += \
|
|
||||||
src/hardware/uni-t-ut181a/protocol.h \
|
|
||||||
src/hardware/uni-t-ut181a/protocol.c \
|
|
||||||
src/hardware/uni-t-ut181a/api.c
|
|
||||||
endif
|
|
||||||
if HW_UNI_T_UT32X
|
if HW_UNI_T_UT32X
|
||||||
src_libdrivers_la_SOURCES += \
|
src_libdrivers_la_SOURCES += \
|
||||||
src/hardware/uni-t-ut32x/protocol.h \
|
src/hardware/uni-t-ut32x/protocol.h \
|
||||||
|
|
@ -769,8 +715,7 @@ tests_main_SOURCES = \
|
||||||
tests/driver_all.c \
|
tests/driver_all.c \
|
||||||
tests/device.c \
|
tests/device.c \
|
||||||
tests/trigger.c \
|
tests/trigger.c \
|
||||||
tests/analog.c \
|
tests/analog.c
|
||||||
tests/conv.c
|
|
||||||
|
|
||||||
tests_main_LDADD = libsigrok.la $(SR_EXTRA_LIBS) $(TESTS_LIBS)
|
tests_main_LDADD = libsigrok.la $(SR_EXTRA_LIBS) $(TESTS_LIBS)
|
||||||
|
|
||||||
|
|
|
||||||
477
NEWS
477
NEWS
|
|
@ -1,3 +1,480 @@
|
||||||
|
0.5.2 (2019-12-23)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Note: This release does NOT change the libsigrok public C API in incompatible
|
||||||
|
ways. While new config keys have been added (additional enum
|
||||||
|
entries / numbers), no existing interfaces were changed or removed.
|
||||||
|
Frontends should continue to work fine without recompiling or relinking.
|
||||||
|
|
||||||
|
* New supported hardware:
|
||||||
|
- Logic analyzers:
|
||||||
|
- Microchip PICkit2
|
||||||
|
- Sysclk SLA5032
|
||||||
|
- Multimeters:
|
||||||
|
- Agilent 34405A
|
||||||
|
- Agilent U1271
|
||||||
|
- Agilent U1272
|
||||||
|
- Agilent U1273
|
||||||
|
- HP 34401A
|
||||||
|
- Keysight 34465A
|
||||||
|
- MASTECH MS2115B
|
||||||
|
- Mooshimeter
|
||||||
|
- Power supplies:
|
||||||
|
- BK Precision 9130
|
||||||
|
- HP 6611C
|
||||||
|
- HP 6612C
|
||||||
|
- HP 6613C
|
||||||
|
- HP 6614C
|
||||||
|
- HP 66312A
|
||||||
|
- HP 6632A
|
||||||
|
- HP 6634A
|
||||||
|
- Korad KA3005P (with 0xBC as extra byte in the model ID).
|
||||||
|
- Korad KD6005P
|
||||||
|
- Manson HCS-3300
|
||||||
|
- Manson HCS-3302
|
||||||
|
- RND 320-KD3005P
|
||||||
|
- Stamos S-LS-31
|
||||||
|
- TENMA 72-2535 V2.1
|
||||||
|
- Voltcraft DPPS-32-15
|
||||||
|
- Oscilloscopes:
|
||||||
|
- Agilent DSO1000B series
|
||||||
|
- Hameg HMO3522
|
||||||
|
- Rigol MSO5000 series
|
||||||
|
- Rohde&Schwarz HMO1000 series (e.g. bug #1286)
|
||||||
|
- Rohde&Schwarz RTA4000 series
|
||||||
|
- Rohde&Schwarz RTB2000 series
|
||||||
|
- Rohde&Schwarz RTC1000 series
|
||||||
|
- Rohde&Schwarz RTM3000 series
|
||||||
|
- YiXingDianZi MDSO
|
||||||
|
- LCR meters:
|
||||||
|
- MASTECH MS5308
|
||||||
|
- PeakTech 2165
|
||||||
|
- UNI-T UT612
|
||||||
|
- Voltcraft 4080
|
||||||
|
- Thermometers:
|
||||||
|
- MASTECH MS6514
|
||||||
|
* New config keys:
|
||||||
|
- SR_CONF_OFFSET
|
||||||
|
- SR_CONF_TRIGGER_PATTERN
|
||||||
|
- SR_CONF_HIGH_RESOLUTION
|
||||||
|
- SR_CONF_PEAK_DETECTION
|
||||||
|
- SR_CONF_LOGIC_THRESHOLD
|
||||||
|
- SR_CONF_LOGIC_THRESHOLD_CUSTOM
|
||||||
|
- SR_CONF_RANGE
|
||||||
|
- SR_CONF_DIGITS
|
||||||
|
* New output modules:
|
||||||
|
- wavedrom: WaveDrom JSON-based digital timing diagrams.
|
||||||
|
* Build dependencies:
|
||||||
|
- hidapi >= 0.8.0 (optional, used for some HID based "serial cables")
|
||||||
|
- bluez/libbluetooth >= 4.0 (optional, used for Bluetooth/BLE; Linux-only)
|
||||||
|
- libgio >= 2.32.0 (optional, used by some drivers)
|
||||||
|
* C++ bindings:
|
||||||
|
- Add Output::format().
|
||||||
|
- Avoid "using namespace" in headers (bug #1354).
|
||||||
|
* Python bindings:
|
||||||
|
- Add data array for logic packet payload.
|
||||||
|
- Add override for Context.create_logic_packet().
|
||||||
|
- Add new Context::create_end_packet() method.
|
||||||
|
* Backend:
|
||||||
|
- Add support for "serial over USB HID" transport, e.g. in DMMs (bug #555).
|
||||||
|
Supported chips/protocols: WCH CH9325, SiLabs CP2110, Brymen BU-86X IR
|
||||||
|
adapter, Victor DMM chip.
|
||||||
|
- Add support for Bluetooth and BLE (currently Linux-only).
|
||||||
|
- Add support for "serial over Bluetooth/BLE" transport (used e.g. in DMMs).
|
||||||
|
- serial: Add support for optional "RX chunk" callback.
|
||||||
|
- Remove sr_dev_close call from std_serial_dev_acquisition_stop (bug #1271).
|
||||||
|
* input/trace32_ad:
|
||||||
|
- Add support for new file format (BINHDR2).
|
||||||
|
* input/csv:
|
||||||
|
- Unbreak re-import of CSV files.
|
||||||
|
- Various parsing robustness improvements.
|
||||||
|
- Introduce generic "column processing" support.
|
||||||
|
- Various module option changes and renames.
|
||||||
|
- Add support for analog input data.
|
||||||
|
- Add support for timestamp columns, auto detect samplerate.
|
||||||
|
- Add automatic format match support.
|
||||||
|
- Set default "header" option value to true.
|
||||||
|
- Fix incorrect/incomplete startline/newline handling (bug #968).
|
||||||
|
* output/analog:
|
||||||
|
- Also print meta strings (not just numbers).
|
||||||
|
* output/vcd:
|
||||||
|
- Use larger data type to internally store frequency.
|
||||||
|
- Support smaller timescales with higher resolution.
|
||||||
|
* output/csv:
|
||||||
|
- Set default "time" option value to false.
|
||||||
|
* scpi:
|
||||||
|
- Add connenction_id() function to all SCPI drivers.
|
||||||
|
- scpi_libgpib: Add mutex to SPoll.
|
||||||
|
* dmm/bm86x:
|
||||||
|
- Unbreak temperature modes for two probes and no probes (bug #1394).
|
||||||
|
* brymen-bm86x, victor-dmm:
|
||||||
|
- Removed in favor of serial-dmm based driver support.
|
||||||
|
* scpi-pps:
|
||||||
|
- Add support for various config keys for HP 66xxB power supplies.
|
||||||
|
- Add init_acquisition() and update_status() for HP 66xxB power supplies.
|
||||||
|
- Set device and channel group feature for HP 66xxB.
|
||||||
|
- Don't use SCPI_CMD_REMOTE/_LOCAL for HP 66xxB devices when in GPIB mode.
|
||||||
|
- Add support for various config keys for HP 66xxA power supplies.
|
||||||
|
- Add configurable sr_mqflags.
|
||||||
|
- Add missing frequency channel settings for acquisition.
|
||||||
|
* hantek-4032l:
|
||||||
|
- Fix broken triggering on low signal (bug #1402).
|
||||||
|
* hameg-hmo:
|
||||||
|
- Use g_byte_array_free() instead of g_free() (bug #1324).
|
||||||
|
- Remove invalid HMO2522, add missing HMO3522 (bug #1322).
|
||||||
|
- Add support for 16 digital (logic) channels, i.e. two pods.
|
||||||
|
- Add support for various new config keys.
|
||||||
|
- Avoid getting stuck upon SCPI timeouts (bug #1323).
|
||||||
|
- Try to find a valid serialcomm if none is supplied (bug #1321).
|
||||||
|
- When setting slope, also set trigger type to edge (bug #1328).
|
||||||
|
- Use 1-based (not 0-based) POD numbers.
|
||||||
|
- Fix for an incorrect samplerate being returned.
|
||||||
|
- Fix the upper limit for the vertical scale.
|
||||||
|
* agilent-dmm:
|
||||||
|
- Fix support for Agilent/Keysight U1252A.
|
||||||
|
* asix-sigma:
|
||||||
|
- Fix a compiler warning (bug #1411).
|
||||||
|
* beaglelogic:
|
||||||
|
- Fix mismatched printf specifiers.
|
||||||
|
* saleae-logic16:
|
||||||
|
- Warn (instead of erroring out) if FPGA is unsupported.
|
||||||
|
* demo:
|
||||||
|
- Set an initial MQ, MQ flag and digits to the analog package.
|
||||||
|
- Get/set measurement quantity for the analog channels.
|
||||||
|
- Get/set amplitude while data acquisition is running.
|
||||||
|
- Get/set new config key SR_CONF_OFFSET.
|
||||||
|
- Add random analog signal generation.
|
||||||
|
* korad-kaxxxxp:
|
||||||
|
- Add SR_MQFLAG_DC flag to the current channel packet.
|
||||||
|
- Fix bug when setting values while acquisition is running.
|
||||||
|
- Send META packet when states have changed.
|
||||||
|
* lecroy-xstream:
|
||||||
|
- Remove header read, which caused issues on some devices (PR #33).
|
||||||
|
* uni-t-ut32x:
|
||||||
|
- Pre-set to default conn= spec.
|
||||||
|
- Use common code for sample/time limits.
|
||||||
|
- Improve robustness of packet parser, more diagnostics.
|
||||||
|
* chronovu-la:
|
||||||
|
- Fix broken triggering (#1369).
|
||||||
|
* openbench-logic-sniffer:
|
||||||
|
- Use 32bit for handling sample counts.
|
||||||
|
- Extend reponse delay when scanning device (helps on Pepino).
|
||||||
|
- Add feature to support >256K memory.
|
||||||
|
- Introduce metadata quirks support, unbreak Logic Shrimp.
|
||||||
|
* ftdi-la:
|
||||||
|
- Added FTDI FT232H device ID.
|
||||||
|
- Do enter the error path upon VID:PID mismatch (bug #1390).
|
||||||
|
* hp-3478a:
|
||||||
|
- Check via GPIB serial poll if new data is available.
|
||||||
|
- Add get/set/list of measurement ranges.
|
||||||
|
- Add get/set/list of digits.
|
||||||
|
- Check for measurement value overflow.
|
||||||
|
* manson-hcs-3xxx:
|
||||||
|
- Add support for some models with changes ID string (e.g. bug #1441).
|
||||||
|
* fx2lafw:
|
||||||
|
- Add support for 48MHz sampling (for very short intervals only).
|
||||||
|
* ipdbg-la:
|
||||||
|
- Check if limit samples is valid.
|
||||||
|
- Improve acquisition speed.
|
||||||
|
- Fix various issues on Windows.
|
||||||
|
- Fix an issue when capture rate is 100% (bug #1393).
|
||||||
|
* scpi-dmm:
|
||||||
|
- Accept serialcomm= scan options.
|
||||||
|
- Run OPC queries immediately before essential commands.
|
||||||
|
* brymen-bm86x:
|
||||||
|
- Fix incorrect channel indices.
|
||||||
|
* dmm/eev121gw:
|
||||||
|
- Add missing scale items for sub display in power modes.
|
||||||
|
* rigol-ds:
|
||||||
|
- Fix memory buffer readout on DS4000 series.
|
||||||
|
* fluke-45:
|
||||||
|
- Avoid NULL dereference in the probe routine.
|
||||||
|
- Disable ECHO test, it confuses other SCPI devices (bug #1272).
|
||||||
|
∗ fluke-dmm:
|
||||||
|
- Fix use-after-free bugs (bug #1423).
|
||||||
|
* rdtech-dps:
|
||||||
|
- Synchronize read and write operations.
|
||||||
|
- Retry sr_modbus_read_holding_registers() up to 3 times.
|
||||||
|
- Handle different per-model current/voltage digits (bug #1385).
|
||||||
|
- Send META package when states have changed.
|
||||||
|
* udev:
|
||||||
|
- Add an additional Rohde&Schwarz HMO VID/PID.
|
||||||
|
- Add Siglent SDS1104X-E VID/PID (bug #1357).
|
||||||
|
- Add Lecroy WaveRunner VID/PID.
|
||||||
|
* Build system:
|
||||||
|
- Fix an issue when building in directories that are symlinks (bug #547).
|
||||||
|
- Fix C++ bindings compilation with Doxygen >= 1.8.16 (bug #1422).
|
||||||
|
* Fix various memory leaks in the backend and in drivers.
|
||||||
|
* README.devices: Update/amend docs, add more examples.
|
||||||
|
|
||||||
|
0.5.1 (2018-10-14)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Note: This release does NOT change the libsigrok public C API in incompatible
|
||||||
|
ways. While new config keys have been added (additional enum
|
||||||
|
entries / numbers), no existing interfaces were changed or removed.
|
||||||
|
Frontends should continue to work fine without recompiling or relinking.
|
||||||
|
|
||||||
|
* New supported hardware:
|
||||||
|
- Logic analyzers:
|
||||||
|
- DreamSourceLab DSLogic Basic
|
||||||
|
- DreamSourceLab DSLogic Plus
|
||||||
|
- Hantek 4032L
|
||||||
|
- IPDBG (ipdbg.org FPGA IP debugger)
|
||||||
|
- Meilhaus Logian-16L
|
||||||
|
- Saleae Logic Pro 16 (experimental)
|
||||||
|
- ZEROPLUS Logic Cube LAP-16032U
|
||||||
|
- ZEROPLUS Logic Cube LAP-C (16128+) (bug #1045)
|
||||||
|
- Oscilloscopes:
|
||||||
|
- Agilent MSO7034A (experimental, digital channels not yet supported)
|
||||||
|
- Hantek DSO-2250 (experimental)
|
||||||
|
- Rigol DS4000 series (bug #1208)
|
||||||
|
- Siglent SDS1000 series
|
||||||
|
- Siglent SDS2000 series
|
||||||
|
- Multimeters:
|
||||||
|
- EEVblog 121GW (supports serial connection, requires BLE-serial bridge)
|
||||||
|
- Fluke 45
|
||||||
|
- HP 3478A
|
||||||
|
- MASTECH MS8250D
|
||||||
|
- Metex M-3860M
|
||||||
|
- PeakTech 4390A
|
||||||
|
- SparkFun 70C
|
||||||
|
- Victor DMMs with Mini-USB connector
|
||||||
|
- Voltcraft VC-96
|
||||||
|
- Power supplies:
|
||||||
|
- GW Instek GPD series
|
||||||
|
- HP 6631B
|
||||||
|
- HP 66332A
|
||||||
|
- HP 6633B
|
||||||
|
- HP 6634B
|
||||||
|
- Korad KD3005P
|
||||||
|
- RDTech DPS/DPH series
|
||||||
|
- Rigol DP711
|
||||||
|
- Rigol DP712
|
||||||
|
- RND KA3005P
|
||||||
|
- Tenma 72-2540
|
||||||
|
- Electronic loads:
|
||||||
|
- ZKETECH EBD-USB
|
||||||
|
* New config keys:
|
||||||
|
- SR_CONF_POWERMETER
|
||||||
|
- SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD
|
||||||
|
- SR_CONF_EXTERNAL_CLOCK_SOURCE
|
||||||
|
* Build dependencies:
|
||||||
|
- Drop libftdi 0.x support, require libftdi 1.x (bug #959).
|
||||||
|
* dreamsourcelab-dslogic:
|
||||||
|
- Split DSLogic devices into this new driver (were in fx2lafw previously).
|
||||||
|
- Handle SR_CONF_TRIGGER_MATCH in config_list() (bug #1032).
|
||||||
|
- Fix incorrect default/initial threshold setting.
|
||||||
|
- Fix deinterleaving when non-contiguous set of channels is enabled.
|
||||||
|
- Fix a triggering issue (bug #1188).
|
||||||
|
* demo:
|
||||||
|
- Unbreak execution with all analog channels disabled (bug #625).
|
||||||
|
- Only send average result data when averaging is active (bug #930).
|
||||||
|
- Don't generate analog output data for disabled channels (bug #923).
|
||||||
|
- Skip generating data when all channels in a group are disabled (bug #923).
|
||||||
|
- Mask out logic data for disabled channels in datafeed packets.
|
||||||
|
- Add multi-frame and frame-count (development) feature.
|
||||||
|
- Properly handle low samplerates.
|
||||||
|
- Add graycode generator mode.
|
||||||
|
- Add triggering support.
|
||||||
|
* conrad-digi-35-cpu:
|
||||||
|
- Add SR_CONF_LIST for voltage_target and current_limit.
|
||||||
|
* arachnid-labs-re-load-pro:
|
||||||
|
- Stop monitoring when open/close device.
|
||||||
|
- Add SR_DF_META packets for changed values/states.
|
||||||
|
- Change serial read in acquisition mode to prevent data loss.
|
||||||
|
- Fix a rounding issue when setting a current limit.
|
||||||
|
- Add SR_CONF_UNDER_VOLTAGE_THRESHOLD support.
|
||||||
|
- Make SR_CONF_REGULATION listable.
|
||||||
|
- Get a response when in acquision mode.
|
||||||
|
- Set an encoding "digits" value for analog packets.
|
||||||
|
* asix-sigma:
|
||||||
|
- Propagate errors from firmware upload (bug #471).
|
||||||
|
- Only change number of channels after successful firmware upload (bug #471).
|
||||||
|
- Only open the USB device once; fails with newer libftdi (bug #471).
|
||||||
|
- Download sample data upon user initiated stop, too.
|
||||||
|
* serial-lcr:
|
||||||
|
- Fix a double-free error (tested with PeakTech 2170).
|
||||||
|
- Support channel selection (enable/disable P1/P2).
|
||||||
|
* brymen-bm86x:
|
||||||
|
- Support channel selection (enable/disable channels).
|
||||||
|
- Fix a crash upon device closing.
|
||||||
|
* manson-hcs-3xxx:
|
||||||
|
- Add new name for the HCS-3304 (adjustment for new Manson firmware).
|
||||||
|
* chronovu-la:
|
||||||
|
- Silence overly verbose and confusing log messages.
|
||||||
|
* beaglelogic:
|
||||||
|
- Update scan() to return all 14 channels by default.
|
||||||
|
- Enable seamless continuous capturing.
|
||||||
|
- Use a flexible sampleunit depending on enabled channels.
|
||||||
|
- Add TCP protocol support.
|
||||||
|
- Fix a crash on device connection closing.
|
||||||
|
- Correctly advertise samplerates via SR_CONF_SAMPLERATE/SR_CONF_LIST.
|
||||||
|
- Fix a compiler warning (bug #1094).
|
||||||
|
* lecroy-xstream:
|
||||||
|
- Fix COMM_HEADER and COMM_FORMAT.
|
||||||
|
- Use best-effort strategy for models unknown to the driver.
|
||||||
|
- Fix trigger source/slope handling.
|
||||||
|
- Implement config_channel_set API callback.
|
||||||
|
- Fix sample rate handling.
|
||||||
|
- Keep acquiring infinitely if no frame limit is set.
|
||||||
|
- Properly handle a stopping acquisition.
|
||||||
|
- Wait for trigger before acquiring additional frames.
|
||||||
|
* hantek-6xxx:
|
||||||
|
- Use lower MAX_PACKET_SIZE on Windows (bug #1048).
|
||||||
|
- Use FIFO-based sampling, don't buffer all samples before send (bug #1214).
|
||||||
|
- Hantek 6022BE: Add VID/PID 04b5:6022 support (bug #918).
|
||||||
|
- Hantek 6022BL: Add VID/PID 04b5:602a support (bug #1295).
|
||||||
|
* scpi-pps:
|
||||||
|
- Add channel group device options for HP 663xx.
|
||||||
|
- Use thread safe SCPI functions, return float not double (bug #779).
|
||||||
|
- Improve switching of channel groups (PSU channels).
|
||||||
|
- Add missing functionality for the HP 6632B power supply.
|
||||||
|
- Add listable OVP/OCP threshold for HP 66xxx and prepared all others.
|
||||||
|
- Don't block waiting for a value on capture stop.
|
||||||
|
- Use software sample and time limits.
|
||||||
|
- Fix broken channel selection code (bug #1279).
|
||||||
|
* hameg-hmo:
|
||||||
|
- Forward internal channel state to sigrok channel state (bug #883).
|
||||||
|
* sysclk-lwla:
|
||||||
|
- Fix a segfault in dev_close().
|
||||||
|
- Fix a regression with (at least) the LWLA1034.
|
||||||
|
* rigol-ds:
|
||||||
|
- Fix crash when fetching logic channels (bug #1073).
|
||||||
|
- Fix a memory leak.
|
||||||
|
- Fix an issue causing only one channel to be acquired (bug #1018).
|
||||||
|
- Store trigger sources and their number for each model (bug #299).
|
||||||
|
* hantek-dso:
|
||||||
|
- Fix segfault when accessing already free()d memory (bug #458).
|
||||||
|
* korad-kaxxxxp:
|
||||||
|
- Add two channels "V" and "I", remove channel "CH1".
|
||||||
|
- Synchronize read and write operations.
|
||||||
|
* serial-dmm:
|
||||||
|
- Add support for multiple channels per DMM.
|
||||||
|
- Count analog DMM channels starting at 1.
|
||||||
|
- Print data bytes according to specific meter's packet length.
|
||||||
|
* siglent-sds:
|
||||||
|
- Fixed timebase problem where "ns" could not be selected (bug #1120).
|
||||||
|
- Add an averaging function.
|
||||||
|
- Partial fix for a USB connection problem (bug #1130).
|
||||||
|
- Fix samplerate and memory depth calculation.
|
||||||
|
- Better support for *-E series devices (more work might be needed).
|
||||||
|
- Fix SR_CONF_AVERAGING/SR_CONF_AVG_SAMPLES handling.
|
||||||
|
* hantek-4032l:
|
||||||
|
- Various data acquisition fixes (bug #1190).
|
||||||
|
- Add support for cleanly aborting an acquisition.
|
||||||
|
* fx2lafw:
|
||||||
|
- Silence message in query for channel group's device options (bug #1267).
|
||||||
|
- Add usb-c-grok IDs for upcoming device support.
|
||||||
|
* input/wav:
|
||||||
|
- Fix incorrect memset() call.
|
||||||
|
- Fix module reset issue (bug #1167).
|
||||||
|
- Don't assume CHUNK_SIZE >= total_samples.
|
||||||
|
* input/raw_analog:
|
||||||
|
- Fix module reset issue (bug #1167).
|
||||||
|
- Set appropriate precision digits for sample format (bug #950).
|
||||||
|
* input/vcd:
|
||||||
|
- Fixup VCD timestamp to sigrok samplenum mapping (bug #1075).
|
||||||
|
- Abort VCD import when timestamp counts backwards (bug #1250).
|
||||||
|
- Expand the reset() logic (bug #1306).
|
||||||
|
- Add channel list checks for file re-read (bug #1306).
|
||||||
|
* input/csv:
|
||||||
|
- Make the data format option a list.
|
||||||
|
* input/logicport:
|
||||||
|
- Add input module for LogicPort File (*.lpf) files.
|
||||||
|
* input/chronovu_la8:
|
||||||
|
- Only send data to the session, don't send the header.
|
||||||
|
- Also claim responsibility for ChronoVu LA16 files (.kdt/.kd1).
|
||||||
|
* input/null:
|
||||||
|
- Add this new input module (used for testing and benchmarking purposes).
|
||||||
|
* Input modules:
|
||||||
|
- Improve option names and descriptions.
|
||||||
|
- Increase chunk sizes to 4MB in most cases for improved performance.
|
||||||
|
- Add confidence (detection strength) to format_match() (bug #1200).
|
||||||
|
* output/null:
|
||||||
|
- Add this new output module (used for testing and benchmarking purposes).
|
||||||
|
* output/analog:
|
||||||
|
- Display SR_DF_META packets.
|
||||||
|
- Convert binary to digital digits of precision (bug #950).
|
||||||
|
* output/vcd:
|
||||||
|
- Assign adjacent names to enabled channels (bug #519).
|
||||||
|
- Assume packed input data image, unbreak 2nd+ channel (bug #519).
|
||||||
|
* output/wav:
|
||||||
|
- Change default for scale factor from 0.0 to 1.0.
|
||||||
|
* output/csv:
|
||||||
|
- Fix out-of-bounds array access in process_analog() (bug #1124).
|
||||||
|
- Make the label values option a list.
|
||||||
|
- Disable the dedup option by default.
|
||||||
|
* Output modules:
|
||||||
|
- Fixup trigger marker position in ascii/bits/hex output modules.
|
||||||
|
* Fix various compiler warnings, scan-build issues, and memory leaks.
|
||||||
|
* soft-trigger:
|
||||||
|
- Fix a memory allocation issue (bug #1000).
|
||||||
|
- Fix an issue causing triggers to not work.
|
||||||
|
* session_driver: Fix an issue causing incorrect analog data reads.
|
||||||
|
* Logging:
|
||||||
|
- Flush log lines to cope with non-terminal output (pipes).
|
||||||
|
- Move log level check so that it affects all handlers (bug #698).
|
||||||
|
* SCPI:
|
||||||
|
- Fix an issue caused by trailing whitespace handling (bug #788).
|
||||||
|
- Synchronize read, write and write+read operations.
|
||||||
|
* Various DMMs: Set DC flag for diode mode (bug #144).
|
||||||
|
* All drivers:
|
||||||
|
- Use serial_write_blocking() everywhere (bug #962).
|
||||||
|
- Fix locale dependent string to float conversion (bug #1064).
|
||||||
|
- Show firmware name when loading fails (bug #1262).
|
||||||
|
* FreeBSD:
|
||||||
|
- Ensure device is closed before usb_get_port_path() (bug #1109).
|
||||||
|
* Metex DMMs:
|
||||||
|
- Add missing modes and set correct digits values.
|
||||||
|
- Add power factor measurement mode.
|
||||||
|
- Fix incorrect measurement modes.
|
||||||
|
* DTM0660 DMMs:
|
||||||
|
- Do not apply the exponent twice on the value (bug #1236).
|
||||||
|
* libsigrok API:
|
||||||
|
- Add sr_a2l_threshold().
|
||||||
|
- Add sr_a2l_schmitt_trigger().
|
||||||
|
- Add sr_sprintf_ascii().
|
||||||
|
- Add sr_snprintf_ascii().
|
||||||
|
- Add sr_vsprintf_ascii().
|
||||||
|
- Add sr_vsnprintf_ascii().
|
||||||
|
- Add sr_resourcepaths_get() (bug #1128).
|
||||||
|
- Add sr_input_module_get().
|
||||||
|
- Add sr_log_callback_get().
|
||||||
|
- Add sr_packet_copy().
|
||||||
|
- Add sr_packet_free().
|
||||||
|
- sr_parse_boolstring(): Assume bool is true when no value is specified.
|
||||||
|
- sr_voltage_string(): Add a space before the unit.
|
||||||
|
- sr_parse_rational():
|
||||||
|
- Bugfix for integral parts between -0 and -1.
|
||||||
|
- Bugfix in an error path (bug #1093).
|
||||||
|
- Various parsing robustness improvements/fixes.
|
||||||
|
- Accept leading whitespace.
|
||||||
|
- Insist on some mantissa.
|
||||||
|
- sr_analog_si_prefix_friendly(): Fix read past end of array (bug #950).
|
||||||
|
- sr_parse_sizestring(): Support tera/peta/exa suffixes (bug #763).
|
||||||
|
* libsigrokcxx API:
|
||||||
|
- Add Analog::get_logic_via_threshold().
|
||||||
|
- Add Analog::get_logic_via_schmitt_trigger().
|
||||||
|
- Add Context::input_format_match().
|
||||||
|
- Add Option::parse_string().
|
||||||
|
- Add ConfigKey::parse_string().
|
||||||
|
* backend: Emit firmware search paths in a log message.
|
||||||
|
* Fix a driver-independent firmware loading issue (bug #1140).
|
||||||
|
* modbus: Improve error handling.
|
||||||
|
* Multiple internal refactorings to massively reduce per-driver boilerplate.
|
||||||
|
* Also check $SIGROK_FIRMWARE_DIR for firmware files.
|
||||||
|
* udev rules file:
|
||||||
|
- Add Brymen BU-86X adapter IDs.
|
||||||
|
- Add Rigol DP800 series IDs.
|
||||||
|
- Add usb-c-grok IDs.
|
||||||
|
- Rename file for correct rule ordering on systemd systems (bug #1059).
|
||||||
|
- Split device access policy from the device database. New files:
|
||||||
|
60-libsigrok.rules, 61-libsigrok-{plugdev,uaccess}.rules.
|
||||||
|
* Build system:
|
||||||
|
- Emit a warning if the C++ bindings are not being built.
|
||||||
|
- Install MIME info file in $(datadir)/mime/packages (bug #983).
|
||||||
|
|
||||||
0.5.0 (2017-06-12)
|
0.5.0 (2017-06-12)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
|
||||||
5
README
5
README
|
|
@ -38,7 +38,6 @@ Requirements for the C library:
|
||||||
- pkg-config >= 0.22
|
- pkg-config >= 0.22
|
||||||
- libglib >= 2.32.0
|
- libglib >= 2.32.0
|
||||||
- libzip >= 0.10
|
- libzip >= 0.10
|
||||||
- libtirpc (optional, used by VXI, fallback when glibc >= 2.26)
|
|
||||||
- libserialport >= 0.1.1 (optional, used by some drivers)
|
- libserialport >= 0.1.1 (optional, used by some drivers)
|
||||||
- librevisa >= 0.0.20130412 (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)
|
- libusb-1.0 >= 1.0.16 (optional, used by some drivers)
|
||||||
|
|
@ -79,7 +78,7 @@ Requirements for the Python bindings:
|
||||||
Requirements for the Ruby bindings:
|
Requirements for the Ruby bindings:
|
||||||
|
|
||||||
- libsigrokcxx >= 0.4.0 (the libsigrok C++ bindings, see above)
|
- libsigrokcxx >= 0.4.0 (the libsigrok C++ bindings, see above)
|
||||||
- Ruby >= 2.5.0 (including development files!)
|
- Ruby >= 1.9.3 (including development files!)
|
||||||
- SWIG >= 3.0.8
|
- SWIG >= 3.0.8
|
||||||
- YARD (optional, only needed for the Ruby API docs)
|
- YARD (optional, only needed for the Ruby API docs)
|
||||||
|
|
||||||
|
|
@ -152,7 +151,7 @@ Mailing list
|
||||||
IRC
|
IRC
|
||||||
---
|
---
|
||||||
|
|
||||||
You can find the sigrok developers in the #sigrok IRC channel on Libera.Chat.
|
You can find the sigrok developers in the #sigrok IRC channel on Freenode.
|
||||||
|
|
||||||
|
|
||||||
Website
|
Website
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ The following drivers/devices do not need any firmware upload:
|
||||||
- atten-pps3xxx
|
- atten-pps3xxx
|
||||||
- baylibre-acme
|
- baylibre-acme
|
||||||
- beaglelogic
|
- beaglelogic
|
||||||
|
- brymen-dmm
|
||||||
- cem-dt-885x
|
- cem-dt-885x
|
||||||
- center-3xx (including all subdrivers)
|
- center-3xx (including all subdrivers)
|
||||||
- chronovu-la
|
- chronovu-la
|
||||||
|
|
@ -135,7 +136,6 @@ The following drivers/devices do not need any firmware upload:
|
||||||
- pce-322a
|
- pce-322a
|
||||||
- pipistrello-ols
|
- pipistrello-ols
|
||||||
- rdtech-dps
|
- rdtech-dps
|
||||||
- rigol-dg
|
|
||||||
- rigol-ds
|
- rigol-ds
|
||||||
- rohde-schwarz-sme-0x
|
- rohde-schwarz-sme-0x
|
||||||
- scpi-dmm
|
- scpi-dmm
|
||||||
|
|
@ -185,7 +185,6 @@ Formal syntax for serial communication:
|
||||||
conn=hid[/<chip>]/usb=<bus>.<dev>[.<if>]
|
conn=hid[/<chip>]/usb=<bus>.<dev>[.<if>]
|
||||||
conn=hid[/<chip>]/raw=<path>
|
conn=hid[/<chip>]/raw=<path>
|
||||||
conn=hid[/<chip>]/sn=<serno>
|
conn=hid[/<chip>]/sn=<serno>
|
||||||
conn=hid[/<chip>]/iokit=<path>
|
|
||||||
chip can be: bu86x, ch9325, cp2110, victor
|
chip can be: bu86x, ch9325, cp2110, victor
|
||||||
path may contain slashes
|
path may contain slashes
|
||||||
path and serno are "greedy" (span to the end of the spec)
|
path and serno are "greedy" (span to the end of the spec)
|
||||||
|
|
@ -275,20 +274,6 @@ rules shipped by the system will be broken.
|
||||||
Please consult the udev docs for details.
|
Please consult the udev docs for details.
|
||||||
|
|
||||||
|
|
||||||
Non-default drivers for commodity chips
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
Some vendors include common USB chips in their products yet assign device
|
|
||||||
specific VID:PID pairs. Which results in the necessity for extra steps
|
|
||||||
before the serial port can be used:
|
|
||||||
|
|
||||||
- GW Instek VCP, found in GDM-8000 and probably other meters: Install the
|
|
||||||
vendors Windows driver to get access to a COM port. Or force the driver
|
|
||||||
assignment on Linux:
|
|
||||||
# modprobe cp210x
|
|
||||||
# echo 2184 0030 > /sys/bus/usb-serial/drivers/cp210x/new_id
|
|
||||||
|
|
||||||
|
|
||||||
Cypress FX2 based devices
|
Cypress FX2 based devices
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
@ -404,11 +389,7 @@ a short list for convenience:
|
||||||
'SI232 online' (28-29S) or 'SI232 store' (22-26x). The interface must
|
'SI232 online' (28-29S) or 'SI232 store' (22-26x). The interface must
|
||||||
be configured to the same baud rate as the host (default 9600).
|
be configured to the same baud rate as the host (default 9600).
|
||||||
Multimeter and interface must be configured to the same address.
|
Multimeter and interface must be configured to the same address.
|
||||||
- GW Instek GDM-397: Press the "REL/RS232C (USB)" button for roughly 1 second.
|
|
||||||
- GW Instek VCP: See the discussion on manual driver assignment to common
|
|
||||||
USB to UART chips with non-default USB identification.
|
|
||||||
- MASTECH MS6514: Press the "Setup/PC-Link" button for roughly 3 seconds.
|
- MASTECH MS6514: Press the "Setup/PC-Link" button for roughly 3 seconds.
|
||||||
- Meterman 38XR: Press the "RS232" button.
|
|
||||||
- Metrix MX56C: Press the PRINT button to have the meter send acquisition
|
- 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
|
data via IR. Hold the PRINT button to adjust the meter's transmission
|
||||||
interval.
|
interval.
|
||||||
|
|
@ -422,7 +403,6 @@ a short list for convenience:
|
||||||
- UNI-T UT61B/C/D: Press the "REL/RS232/USB" button for roughly 1 second.
|
- UNI-T UT61B/C/D: Press the "REL/RS232/USB" button for roughly 1 second.
|
||||||
- UNI-T UT71x: Press the "SEND/-/MAXMIN" button for roughly 1 second.
|
- UNI-T UT71x: Press the "SEND/-/MAXMIN" button for roughly 1 second.
|
||||||
Briefly pressing the "EXIT" button leaves this mode again.
|
Briefly pressing the "EXIT" button leaves this mode again.
|
||||||
- UNI-T UT181A: In the "SETUP" menu set "Communication" to "ON".
|
|
||||||
- UNI-T UT325: Briefly press the "SEND" button (as per manual). However, it
|
- UNI-T UT325: Briefly press the "SEND" button (as per manual). However, it
|
||||||
appears that in practice you don't have to press the button (at least on
|
appears that in practice you don't have to press the button (at least on
|
||||||
some versions of the device), simply connect the device via USB.
|
some versions of the device), simply connect the device via USB.
|
||||||
|
|
@ -472,16 +452,6 @@ Example:
|
||||||
$ sigrok-cli --driver ols:conn=/dev/ttyACM0 ...
|
$ sigrok-cli --driver ols:conn=/dev/ttyACM0 ...
|
||||||
|
|
||||||
|
|
||||||
JTAGulator
|
|
||||||
----------
|
|
||||||
|
|
||||||
The Grand Idea Studio JTAGulator also implements the SUMP protocol and
|
|
||||||
thus is covered by the OLS driver. See the vendor's wiki on details how
|
|
||||||
to enable the Logic Analyzer mode of operation.
|
|
||||||
|
|
||||||
https://github.com/grandideastudio/jtagulator/wiki/Logic-Analyzer
|
|
||||||
|
|
||||||
|
|
||||||
Mooshimeter
|
Mooshimeter
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -334,7 +334,7 @@ shared_ptr<Packet> Context::create_logic_packet(
|
||||||
|
|
||||||
shared_ptr<Packet> Context::create_analog_packet(
|
shared_ptr<Packet> Context::create_analog_packet(
|
||||||
vector<shared_ptr<Channel> > channels,
|
vector<shared_ptr<Channel> > channels,
|
||||||
const float *data_pointer, unsigned int num_samples, const Quantity *mq,
|
float *data_pointer, unsigned int num_samples, const Quantity *mq,
|
||||||
const Unit *unit, vector<const QuantityFlag *> mqflags)
|
const Unit *unit, vector<const QuantityFlag *> mqflags)
|
||||||
{
|
{
|
||||||
auto analog = g_new0(struct sr_datafeed_analog, 1);
|
auto analog = g_new0(struct sr_datafeed_analog, 1);
|
||||||
|
|
@ -372,7 +372,7 @@ shared_ptr<Packet> Context::create_analog_packet(
|
||||||
spec->spec_digits = 0;
|
spec->spec_digits = 0;
|
||||||
|
|
||||||
analog->num_samples = num_samples;
|
analog->num_samples = num_samples;
|
||||||
analog->data = (float*)data_pointer;
|
analog->data = data_pointer;
|
||||||
auto packet = g_new(struct sr_datafeed_packet, 1);
|
auto packet = g_new(struct sr_datafeed_packet, 1);
|
||||||
packet->type = SR_DF_ANALOG;
|
packet->type = SR_DF_ANALOG;
|
||||||
packet->payload = analog;
|
packet->payload = analog;
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,6 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||||
#include <glibmm.h>
|
#include <glibmm.h>
|
||||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -284,7 +283,7 @@ public:
|
||||||
/** Create an analog packet. */
|
/** Create an analog packet. */
|
||||||
std::shared_ptr<Packet> create_analog_packet(
|
std::shared_ptr<Packet> create_analog_packet(
|
||||||
std::vector<std::shared_ptr<Channel> > channels,
|
std::vector<std::shared_ptr<Channel> > channels,
|
||||||
const float *data_pointer, unsigned int num_samples, const Quantity *mq,
|
float *data_pointer, unsigned int num_samples, const Quantity *mq,
|
||||||
const Unit *unit, std::vector<const QuantityFlag *> mqflags);
|
const Unit *unit, std::vector<const QuantityFlag *> mqflags);
|
||||||
/** Create an end packet. */
|
/** Create an end packet. */
|
||||||
std::shared_ptr<Packet> create_end_packet();
|
std::shared_ptr<Packet> create_end_packet();
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -94,18 +94,10 @@ VECTOR(std::shared_ptr<sigrok::HardwareDevice>, HardwareDevice)
|
||||||
"java.util.Map<JKey, JValue>"
|
"java.util.Map<JKey, JValue>"
|
||||||
|
|
||||||
%typemap(javain,
|
%typemap(javain,
|
||||||
/* SWIG 4.0.0 changed the std::map wrappers in an incompatible way. */
|
|
||||||
#if SWIG_VERSION >= 0x040000
|
|
||||||
pre=" $javaclassname temp$javainput = new $javaclassname();
|
|
||||||
for (java.util.Map.Entry<JKey, JValue> entry : $javainput.entrySet())
|
|
||||||
temp$javainput.put(entry.getKey(), entry.getValue());",
|
|
||||||
pgcppname="temp$javainput")
|
|
||||||
#else
|
|
||||||
pre=" $javaclassname temp$javainput = new $javaclassname();
|
pre=" $javaclassname temp$javainput = new $javaclassname();
|
||||||
for (java.util.Map.Entry<JKey, JValue> entry : $javainput.entrySet())
|
for (java.util.Map.Entry<JKey, JValue> entry : $javainput.entrySet())
|
||||||
temp$javainput.set(entry.getKey(), entry.getValue());",
|
temp$javainput.set(entry.getKey(), entry.getValue());",
|
||||||
pgcppname="temp$javainput")
|
pgcppname="temp$javainput")
|
||||||
#endif
|
|
||||||
std::map< CKey, CValue > "$javaclassname.getCPtr(temp$javainput)"
|
std::map< CKey, CValue > "$javaclassname.getCPtr(temp$javainput)"
|
||||||
|
|
||||||
%typemap(javaout) std::map< CKey, CValue > {
|
%typemap(javaout) std::map< CKey, CValue > {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -112,9 +112,6 @@ typedef guint pyg_flags_type;
|
||||||
g_free(value);
|
g_free(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use the same typemap above for Glib::VariantContainerBase */
|
|
||||||
%apply Glib::VariantBase { Glib::VariantContainerBase }
|
|
||||||
|
|
||||||
/* Map from callable PyObject to LogCallbackFunction */
|
/* Map from callable PyObject to LogCallbackFunction */
|
||||||
%typecheck(SWIG_TYPECHECK_POINTER) sigrok::LogCallbackFunction {
|
%typecheck(SWIG_TYPECHECK_POINTER) sigrok::LogCallbackFunction {
|
||||||
$1 = PyCallable_Check($input);
|
$1 = PyCallable_Check($input);
|
||||||
|
|
@ -340,14 +337,7 @@ Glib::VariantBase python_to_variant_by_key(PyObject *input, const sigrok::Config
|
||||||
return Glib::Variant<double>::create(PyFloat_AsDouble(input));
|
return Glib::Variant<double>::create(PyFloat_AsDouble(input));
|
||||||
else if (type == SR_T_INT32 && PyInt_Check(input))
|
else if (type == SR_T_INT32 && PyInt_Check(input))
|
||||||
return Glib::Variant<gint32>::create(PyInt_AsLong(input));
|
return Glib::Variant<gint32>::create(PyInt_AsLong(input));
|
||||||
else if ((type == SR_T_RATIONAL_VOLT) && PyTuple_Check(input) && (PyTuple_Size(input) == 2)) {
|
else
|
||||||
PyObject *numObj = PyTuple_GetItem(input, 0);
|
|
||||||
PyObject *denomObj = PyTuple_GetItem(input, 1);
|
|
||||||
if ((PyInt_Check(numObj) || PyLong_Check(numObj)) && (PyInt_Check(denomObj) || PyLong_Check(denomObj))) {
|
|
||||||
const std::vector<guint64> v = {(guint64)PyInt_AsLong(numObj), (guint64)PyInt_AsLong(denomObj)};
|
|
||||||
return Glib::Variant< std::vector<guint64> >::create(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
throw sigrok::Error(SR_ERR_ARG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ for compound in index.findall('compound'):
|
||||||
if language == 'python':
|
if language == 'python':
|
||||||
print('%%feature("docstring") %s "%s";' % (class_name, brief))
|
print('%%feature("docstring") %s "%s";' % (class_name, brief))
|
||||||
elif language == 'ruby':
|
elif language == 'ruby':
|
||||||
print('%%feature("docstring") %s "Document-class: %s\\n%s\\n";' % (class_name, class_name.replace("sigrok", "Sigrok", 1), brief))
|
print('%%feature("docstring") %s "/* Document-class: %s\\n%s */\\n";' % (class_name, class_name.replace("sigrok", "Sigrok", 1), brief))
|
||||||
elif language == 'java':
|
elif language == 'java':
|
||||||
print('%%typemap(javaclassmodifiers) %s "/** %s */\npublic class"' % (
|
print('%%typemap(javaclassmodifiers) %s "/** %s */\npublic class"' % (
|
||||||
class_name, brief))
|
class_name, brief))
|
||||||
|
|
@ -77,10 +77,10 @@ for compound in index.findall('compound'):
|
||||||
for name, desc in parameters.items()]) + '";')
|
for name, desc in parameters.items()]) + '";')
|
||||||
if language == 'ruby' and kind == 'public-func':
|
if language == 'ruby' and kind == 'public-func':
|
||||||
print(str.join('\n', [
|
print(str.join('\n', [
|
||||||
'%%feature("docstring") %s::%s "%s' % (
|
'%%feature("docstring") %s::%s "/* %s' % (
|
||||||
class_name, member_name, brief)] + [
|
class_name, member_name, brief)] + [
|
||||||
'@param %s %s' % (name, desc)
|
'@param %s %s' % (name, desc)
|
||||||
for name, desc in parameters.items()]) + '\\n";')
|
for name, desc in parameters.items()]) + ' */\\n";')
|
||||||
elif language == 'java' and kind == 'public-func':
|
elif language == 'java' and kind == 'public-func':
|
||||||
print(str.join('\n', [
|
print(str.join('\n', [
|
||||||
'%%javamethodmodifiers %s::%s "/** %s' % (
|
'%%javamethodmodifiers %s::%s "/** %s' % (
|
||||||
|
|
@ -111,4 +111,4 @@ for compound in index.findall('compound'):
|
||||||
print('%}')
|
print('%}')
|
||||||
elif language == 'ruby' and constants:
|
elif language == 'ruby' and constants:
|
||||||
for member_name, brief in constants:
|
for member_name, brief in constants:
|
||||||
print('%%feature("docstring") %s::%s "%s\\n";' % (class_name, member_name, brief))
|
print('%%feature("docstring") %s::%s "/* %s */\\n";' % (class_name, member_name, brief))
|
||||||
|
|
|
||||||
51
configure.ac
51
configure.ac
|
|
@ -21,7 +21,7 @@
|
||||||
# We require at least autoconf 2.63 (AC_INIT format changed there).
|
# We require at least autoconf 2.63 (AC_INIT format changed there).
|
||||||
AC_PREREQ([2.63])
|
AC_PREREQ([2.63])
|
||||||
|
|
||||||
AC_INIT([libsigrok], [0.6.0],
|
AC_INIT([libsigrok], [0.5.2],
|
||||||
[sigrok-devel@lists.sourceforge.net],
|
[sigrok-devel@lists.sourceforge.net],
|
||||||
[libsigrok], [http://www.sigrok.org])
|
[libsigrok], [http://www.sigrok.org])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
|
@ -68,7 +68,7 @@ SR_PKG_VERSION_SET([SR_PACKAGE_VERSION], [AC_PACKAGE_VERSION])
|
||||||
# The algorithm for determining which number to change (and how) is nontrivial!
|
# The algorithm for determining which number to change (and how) is nontrivial!
|
||||||
# http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
|
# http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
|
||||||
# Format: current:revision:age.
|
# Format: current:revision:age.
|
||||||
SR_LIB_VERSION_SET([SR_LIB_VERSION], [4:0:0])
|
SR_LIB_VERSION_SET([SR_LIB_VERSION], [5:1:1])
|
||||||
|
|
||||||
AM_CONDITIONAL([WIN32], [test -z "${host_os##mingw*}" || test -z "${host_os##cygwin*}"])
|
AM_CONDITIONAL([WIN32], [test -z "${host_os##mingw*}" || test -z "${host_os##cygwin*}"])
|
||||||
|
|
||||||
|
|
@ -107,8 +107,6 @@ SR_ARG_OPT_PKG([libhidapi], [LIBHIDAPI], ,
|
||||||
|
|
||||||
SR_ARG_OPT_PKG([libbluez], [LIBBLUEZ], , [bluez >= 4.0])
|
SR_ARG_OPT_PKG([libbluez], [LIBBLUEZ], , [bluez >= 4.0])
|
||||||
|
|
||||||
SR_ARG_OPT_PKG([libnettle], [LIBNETTLE], , [nettle])
|
|
||||||
|
|
||||||
# FreeBSD comes with an "integrated" libusb-1.0-style USB API.
|
# 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.
|
# This means libusb-1.0 is always available; no need to check for it.
|
||||||
# On Windows, require the latest version we can get our hands on,
|
# On Windows, require the latest version we can get our hands on,
|
||||||
|
|
@ -188,31 +186,28 @@ AS_CASE([$host_os], [mingw*], [SR_PREPEND([SR_EXTRA_LIBS], [-lws2_32])])
|
||||||
SR_SEARCH_LIBS([SR_EXTRA_LIBS], [pow], [m])
|
SR_SEARCH_LIBS([SR_EXTRA_LIBS], [pow], [m])
|
||||||
|
|
||||||
# RPC is only needed for VXI support.
|
# RPC is only needed for VXI support.
|
||||||
AC_CACHE_CHECK([for SunRPC support], [sr_cv_have_sunrpc],
|
AC_CACHE_CHECK([for RPC support], [sr_cv_have_rpc],
|
||||||
[AC_LINK_IFELSE([AC_LANG_PROGRAM(
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM(
|
||||||
[[#include <rpc/rpc.h>]m4_newline[CLIENT *rpc_test(void);]],
|
[[#include <rpc/rpc.h>]m4_newline[CLIENT *rpc_test(void);]],
|
||||||
[[(void) clnt_create("", 0, 0, "");]])],
|
[[(void) clnt_create("", 0, 0, "");]])],
|
||||||
[RPC_CFLAGS=""; RPC_LIBS=""; sr_cv_have_sunrpc=yes],
|
[sr_cv_have_rpc=yes], [sr_cv_have_rpc=no])])
|
||||||
[sr_cv_have_sunrpc=no])])
|
|
||||||
PKG_CHECK_MODULES([TIRPC],
|
|
||||||
[libtirpc],
|
|
||||||
[RPC_CFLAGS=$TIRPC_CFLAGS; SR_PREPEND([SR_EXTRA_LIBS], [$TIRPC_LIBS]); sr_cv_have_tirpc=yes],
|
|
||||||
[sr_cv_have_tirpc=no])
|
|
||||||
AS_IF([test "x$sr_cv_have_sunrpc" = xyes -o "x$sr_cv_have_tirpc" = xyes],
|
|
||||||
[sr_cv_have_rpc=yes], [sr_cv_have_rpc=no])
|
|
||||||
|
|
||||||
AC_SUBST(RPC_CFLAGS)
|
|
||||||
AC_SUBST(RPC_LIBS)
|
|
||||||
|
|
||||||
AS_IF([test "x$sr_cv_have_rpc" = xyes],
|
AS_IF([test "x$sr_cv_have_rpc" = xyes],
|
||||||
[AC_DEFINE([HAVE_RPC], [1], [Specifies whether we have RPC support (either by SunRPC or libtirpc).])])
|
[AC_DEFINE([HAVE_RPC], [1], [Specifies whether we have RPC support.])])
|
||||||
|
|
||||||
# VXI support is only compiled if RPC support was found.
|
# VXI support is only compiled if RPC support was found.
|
||||||
AM_CONDITIONAL([NEED_RPC], [test "x$sr_cv_have_rpc" = xyes])
|
AM_CONDITIONAL([NEED_RPC], [test "x$sr_cv_have_rpc" = xyes])
|
||||||
|
|
||||||
# Check for compiler support of 128 bit integers
|
# Check for compiler support of 128 bit integers
|
||||||
AC_CHECK_TYPES([__int128_t, __uint128_t], [], [], [])
|
AC_CHECK_TYPES([__int128_t, __uint128_t], [], [], [])
|
||||||
|
|
||||||
|
# Availability of bt_put_le16() depends on the bluez library version.
|
||||||
|
AC_CACHE_CHECK([for bt_put_le16], [sr_cv_have_btputle16],
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[[#include <bluetooth/bluetooth.h>]],
|
||||||
|
[[bt_put_le16(0, (void *)0);]])],
|
||||||
|
[sr_cv_have_btputle16=yes], [sr_cv_have_btputle16=no])])
|
||||||
|
AS_IF([test "x$sr_cv_have_btputle16" = xyes],
|
||||||
|
[AC_DEFINE([HAVE_BT_PUT_LE16], [1], [Specifies whether we have bt_put_le16().])])
|
||||||
|
|
||||||
########################
|
########################
|
||||||
## Hardware drivers ##
|
## Hardware drivers ##
|
||||||
########################
|
########################
|
||||||
|
|
@ -264,12 +259,12 @@ SR_DRIVER([ASIX SIGMA/SIGMA2], [asix-sigma], [libftdi])
|
||||||
SR_DRIVER([Atten PPS3xxx], [atten-pps3xxx], [serial_comm])
|
SR_DRIVER([Atten PPS3xxx], [atten-pps3xxx], [serial_comm])
|
||||||
SR_DRIVER([BayLibre ACME], [baylibre-acme], [sys_timerfd_h])
|
SR_DRIVER([BayLibre ACME], [baylibre-acme], [sys_timerfd_h])
|
||||||
SR_DRIVER([BeagleLogic], [beaglelogic], [sys_mman_h sys_ioctl_h])
|
SR_DRIVER([BeagleLogic], [beaglelogic], [sys_mman_h sys_ioctl_h])
|
||||||
|
SR_DRIVER([Brymen DMM], [brymen-dmm], [serial_comm])
|
||||||
SR_DRIVER([CEM DT-885x], [cem-dt-885x], [serial_comm])
|
SR_DRIVER([CEM DT-885x], [cem-dt-885x], [serial_comm])
|
||||||
SR_DRIVER([Center 3xx], [center-3xx], [serial_comm])
|
SR_DRIVER([Center 3xx], [center-3xx], [serial_comm])
|
||||||
SR_DRIVER([ChronoVu LA], [chronovu-la], [libusb libftdi])
|
SR_DRIVER([ChronoVu LA], [chronovu-la], [libusb libftdi])
|
||||||
SR_DRIVER([Colead SLM], [colead-slm], [serial_comm])
|
SR_DRIVER([Colead SLM], [colead-slm], [serial_comm])
|
||||||
SR_DRIVER([Conrad DIGI 35 CPU], [conrad-digi-35-cpu], [serial_comm])
|
SR_DRIVER([Conrad DIGI 35 CPU], [conrad-digi-35-cpu], [serial_comm])
|
||||||
SR_DRIVER([dcttech usbrelay], [dcttech-usbrelay], [libhidapi])
|
|
||||||
SR_DRIVER([demo], [demo])
|
SR_DRIVER([demo], [demo])
|
||||||
SR_DRIVER([DreamSourceLab DSLogic], [dreamsourcelab-dslogic], [libusb])
|
SR_DRIVER([DreamSourceLab DSLogic], [dreamsourcelab-dslogic], [libusb])
|
||||||
SR_DRIVER([Fluke 45], [fluke-45])
|
SR_DRIVER([Fluke 45], [fluke-45])
|
||||||
|
|
@ -285,16 +280,12 @@ SR_DRIVER([Hantek 6xxx], [hantek-6xxx], [libusb])
|
||||||
SR_DRIVER([Hantek DSO], [hantek-dso], [libusb])
|
SR_DRIVER([Hantek DSO], [hantek-dso], [libusb])
|
||||||
SR_DRIVER([HP 3457A], [hp-3457a])
|
SR_DRIVER([HP 3457A], [hp-3457a])
|
||||||
SR_DRIVER([HP 3478A], [hp-3478a], [libgpib])
|
SR_DRIVER([HP 3478A], [hp-3478a], [libgpib])
|
||||||
SR_DRIVER([hp-59306a], [hp-59306a])
|
|
||||||
SR_DRIVER([Hung-Chang DSO-2100], [hung-chang-dso-2100], [libieee1284])
|
SR_DRIVER([Hung-Chang DSO-2100], [hung-chang-dso-2100], [libieee1284])
|
||||||
SR_DRIVER([Ikalogic Scanalogic-2], [ikalogic-scanalogic2], [libusb])
|
SR_DRIVER([Ikalogic Scanalogic-2], [ikalogic-scanalogic2], [libusb])
|
||||||
SR_DRIVER([Ikalogic Scanaplus], [ikalogic-scanaplus], [libftdi])
|
SR_DRIVER([Ikalogic Scanaplus], [ikalogic-scanaplus], [libftdi])
|
||||||
SR_DRIVER([Ikalogic ScanaQuad], [ikalogic-scanaquad], [libftdi])
|
|
||||||
SR_DRIVER([IPDBG LA], [ipdbg-la])
|
SR_DRIVER([IPDBG LA], [ipdbg-la])
|
||||||
SR_DRIVER([ITECH IT8500], [itech-it8500], [serial_comm])
|
|
||||||
SR_DRIVER([Kecheng KC-330B], [kecheng-kc-330b], [libusb])
|
SR_DRIVER([Kecheng KC-330B], [kecheng-kc-330b], [libusb])
|
||||||
SR_DRIVER([KERN scale], [kern-scale], [serial_comm])
|
SR_DRIVER([KERN scale], [kern-scale], [serial_comm])
|
||||||
SR_DRIVER([Kingst LA2016], [kingst-la2016], [libusb])
|
|
||||||
SR_DRIVER([Korad KAxxxxP], [korad-kaxxxxp], [serial_comm])
|
SR_DRIVER([Korad KAxxxxP], [korad-kaxxxxp], [serial_comm])
|
||||||
SR_DRIVER([Lascar EL-USB], [lascar-el-usb], [libusb])
|
SR_DRIVER([Lascar EL-USB], [lascar-el-usb], [libusb])
|
||||||
SR_DRIVER([LeCroy LogicStudio], [lecroy-logicstudio], [libusb])
|
SR_DRIVER([LeCroy LogicStudio], [lecroy-logicstudio], [libusb])
|
||||||
|
|
@ -311,10 +302,7 @@ SR_DRIVER([OpenBench Logic Sniffer], [openbench-logic-sniffer], [serial_comm])
|
||||||
SR_DRIVER([PCE PCE-322A], [pce-322a], [serial_comm])
|
SR_DRIVER([PCE PCE-322A], [pce-322a], [serial_comm])
|
||||||
SR_DRIVER([Pipistrello-OLS], [pipistrello-ols], [libftdi])
|
SR_DRIVER([Pipistrello-OLS], [pipistrello-ols], [libftdi])
|
||||||
SR_DRIVER([RDTech DPSxxxx/DPHxxxx], [rdtech-dps], [serial_comm])
|
SR_DRIVER([RDTech DPSxxxx/DPHxxxx], [rdtech-dps], [serial_comm])
|
||||||
SR_DRIVER([RDTech UMXX], [rdtech-um], [serial_comm])
|
|
||||||
SR_DRIVER([RDTech TCXX], [rdtech-tc], [serial_comm libnettle])
|
|
||||||
SR_DRIVER([Rigol DS], [rigol-ds])
|
SR_DRIVER([Rigol DS], [rigol-ds])
|
||||||
SR_DRIVER([Rigol DG], [rigol-dg])
|
|
||||||
SR_DRIVER([Rohde&Schwarz SME-0x], [rohde-schwarz-sme-0x], [serial_comm])
|
SR_DRIVER([Rohde&Schwarz SME-0x], [rohde-schwarz-sme-0x], [serial_comm])
|
||||||
SR_DRIVER([Saleae Logic16], [saleae-logic16], [libusb])
|
SR_DRIVER([Saleae Logic16], [saleae-logic16], [libusb])
|
||||||
SR_DRIVER([Saleae Logic Pro], [saleae-logic-pro], [libusb])
|
SR_DRIVER([Saleae Logic Pro], [saleae-logic-pro], [libusb])
|
||||||
|
|
@ -329,7 +317,6 @@ SR_DRIVER([Teleinfo], [teleinfo], [serial_comm])
|
||||||
SR_DRIVER([Testo], [testo], [libusb])
|
SR_DRIVER([Testo], [testo], [libusb])
|
||||||
SR_DRIVER([Tondaj SL-814], [tondaj-sl-814], [serial_comm])
|
SR_DRIVER([Tondaj SL-814], [tondaj-sl-814], [serial_comm])
|
||||||
SR_DRIVER([UNI-T DMM], [uni-t-dmm], [libusb])
|
SR_DRIVER([UNI-T DMM], [uni-t-dmm], [libusb])
|
||||||
SR_DRIVER([UNI-T UT181A], [uni-t-ut181a], [serial_comm])
|
|
||||||
SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [serial_comm])
|
SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [serial_comm])
|
||||||
SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm])
|
SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm])
|
||||||
SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb])
|
SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb])
|
||||||
|
|
@ -457,7 +444,7 @@ AS_IF([test "x$HAVE_PYMOD_NUMPY" != xyes],
|
||||||
[SR_APPEND([sr_python_missing], [', '], [numpy])])
|
[SR_APPEND([sr_python_missing], [', '], [numpy])])
|
||||||
|
|
||||||
# The Python bindings use SWIG to generate code.
|
# The Python bindings use SWIG to generate code.
|
||||||
AC_CHECK_PROGS([SWIG], [swig swig4.0 swig3.0 swig2.0])
|
AC_CHECK_PROGS([SWIG], [swig swig3.0 swig2.0])
|
||||||
AS_IF([test "x$SWIG" != x],
|
AS_IF([test "x$SWIG" != x],
|
||||||
AC_MSG_CHECKING([for $SWIG version])
|
AC_MSG_CHECKING([for $SWIG version])
|
||||||
[SWIG_VERSION=`$SWIG -version 2>&1 | sed -n 's/SWIG Version //p'`]
|
[SWIG_VERSION=`$SWIG -version 2>&1 | sed -n 's/SWIG Version //p'`]
|
||||||
|
|
@ -495,8 +482,8 @@ sr_rbminor=${sr_rbminor%%.*}
|
||||||
|
|
||||||
# The Ruby bindings need Ruby development files.
|
# The Ruby bindings need Ruby development files.
|
||||||
SR_PKG_CHECK([ruby_dev], [SR_PKGLIBS_RUBY],
|
SR_PKG_CHECK([ruby_dev], [SR_PKGLIBS_RUBY],
|
||||||
[ruby >= 2.5.0],
|
[ruby],
|
||||||
[ruby-$sr_rbmajor.$sr_rbminor >= 2.5.0])
|
[ruby-$sr_rbmajor.$sr_rbminor])
|
||||||
|
|
||||||
AS_IF([test "x$sr_have_ruby_dev" != xyes],
|
AS_IF([test "x$sr_have_ruby_dev" != xyes],
|
||||||
[SR_APPEND([sr_ruby_missing], [', '], [Headers])])
|
[SR_APPEND([sr_ruby_missing], [', '], [Headers])])
|
||||||
|
|
@ -657,8 +644,6 @@ Enabled serial communication transports:
|
||||||
|
|
||||||
Enabled SCPI backends:
|
Enabled SCPI backends:
|
||||||
- TCP............................. yes
|
- TCP............................. yes
|
||||||
- SunRPC ......................... $sr_cv_have_sunrpc
|
|
||||||
- TI-RPC ......................... $sr_cv_have_tirpc
|
|
||||||
- RPC............................. $sr_cv_have_rpc
|
- RPC............................. $sr_cv_have_rpc
|
||||||
- serial.......................... $sr_have_serial_comm
|
- serial.......................... $sr_have_serial_comm
|
||||||
- VISA............................ $sr_have_librevisa
|
- VISA............................ $sr_have_librevisa
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,6 @@ SUBSYSTEM!="usb|usbmisc|usb_device|hidraw", GOTO="libsigrok_rules_end"
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0618", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0618", ENV{ID_SIGROK}="1"
|
||||||
# 34410A
|
# 34410A
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0607", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0607", ENV{ID_SIGROK}="1"
|
||||||
# 34460A
|
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="1b07", ENV{ID_SIGROK}="1"
|
|
||||||
# DSO1000 series
|
# DSO1000 series
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", ENV{ID_SIGROK}="1"
|
||||||
# MSO7000A series
|
# MSO7000A series
|
||||||
|
|
@ -91,15 +89,12 @@ ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", ENV{ID_SIGROK}="1"
|
||||||
# Dangerous Prototypes Buspirate (v3)
|
# Dangerous Prototypes Buspirate (v3)
|
||||||
# ChronoVu LA8 (old VID/PID)
|
# ChronoVu LA8 (old VID/PID)
|
||||||
# ChronoVu LA16 (old VID/PID)
|
# ChronoVu LA16 (old VID/PID)
|
||||||
# ftdi-la (FT232R)
|
# ftdi-la
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# Dangerous Prototypes Buspirate (v4)
|
# Dangerous Prototypes Buspirate (v4)
|
||||||
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# dcttech.com USB relay card, and other V-USB based firmware
|
|
||||||
ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# DreamSourceLab DSLogic
|
# DreamSourceLab DSLogic
|
||||||
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0001", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0001", ENV{ID_SIGROK}="1"
|
||||||
# DreamSourceLab DSLogic Pro
|
# DreamSourceLab DSLogic Pro
|
||||||
|
|
@ -111,9 +106,6 @@ ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0020", ENV{ID_SIGROK}="1"
|
||||||
# DreamSourceLab DSLogic Basic
|
# DreamSourceLab DSLogic Basic
|
||||||
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0021", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0021", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# GW-Instek GDM-9061 (USBTMC mode)
|
|
||||||
ATTRS{idVendor}=="2184", ATTRS{idProduct}=="0059", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# HAMEG HO720
|
# HAMEG HO720
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed72", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed72", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
|
|
@ -163,22 +155,12 @@ ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="4032", ENV{ID_SIGROK}="1"
|
||||||
ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4123", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4123", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# IKALOGIC ScanaPLUS
|
# IKALOGIC ScanaPLUS
|
||||||
# ftdi-la (FT232H)
|
# ftdi-la
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# ftdi-la (TIAO USB Multi Protocol Adapter (TUMPA))
|
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8a98", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Kecheng KC-330B
|
# Kecheng KC-330B
|
||||||
ATTRS{idVendor}=="1041", ATTRS{idProduct}=="8101", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="1041", ATTRS{idProduct}=="8101", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# Keysight USBTMC-connected devices
|
|
||||||
# 34465A
|
|
||||||
ATTRS{idVendor}=="2a8d", ATTRS{idProduct}=="0101", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Kingst LA2016
|
|
||||||
ATTRS{idVendor}=="77a1", ATTRS{idProduct}=="01a2", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Lascar Electronics EL-USB-2
|
# Lascar Electronics EL-USB-2
|
||||||
# Lascar Electronics EL-USB-CO
|
# Lascar Electronics EL-USB-CO
|
||||||
# This is actually the generic SiLabs (Cygnal) F32x USBXpress VID:PID.
|
# This is actually the generic SiLabs (Cygnal) F32x USBXpress VID:PID.
|
||||||
|
|
@ -199,20 +181,14 @@ ATTRS{idVendor}=="3195", ATTRS{idProduct}=="f190", ENV{ID_SIGROK}="1"
|
||||||
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa95", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa95", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# MiniLA Mockup
|
# MiniLA Mockup
|
||||||
# ftdi-la (FT2232H)
|
# ftdi-la
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# ftdi-la (FT4232H)
|
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# MIC 98581
|
# MIC 98581
|
||||||
# MIC 98583
|
# MIC 98583
|
||||||
# Tondaj SL-814
|
# Tondaj SL-814
|
||||||
ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# Microchip PICkit2
|
|
||||||
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="0033", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Openbench Logic Sniffer
|
# Openbench Logic Sniffer
|
||||||
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
|
|
@ -231,12 +207,6 @@ ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b1", ENV{ID_SIGROK}="1"
|
||||||
# Rigol DG4000 series
|
# Rigol DG4000 series
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0641", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0641", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
# Rigol DG1000z series
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0642", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol DG800 and DG900 series
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0643", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol DP800 series
|
# Rigol DP800 series
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0e11", ENV{ID_SIGROK}="1"
|
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0e11", ENV{ID_SIGROK}="1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,13 +80,6 @@ enum sr_error_code {
|
||||||
/* Update sr_strerror()/sr_strerror_name() (error.c) upon changes! */
|
/* Update sr_strerror()/sr_strerror_name() (error.c) upon changes! */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Ternary return type for DMM/LCR/etc packet parser validity checks. */
|
|
||||||
enum sr_valid_code {
|
|
||||||
SR_PACKET_INVALID = -1, /**< Certainly invalid. */
|
|
||||||
SR_PACKET_VALID = 0, /**< Certainly valid. */
|
|
||||||
SR_PACKET_NEED_RX = +1, /**< Need more RX data. */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SR_MAX_CHANNELNAME_LEN 32
|
#define SR_MAX_CHANNELNAME_LEN 32
|
||||||
|
|
||||||
/* Handy little macros */
|
/* Handy little macros */
|
||||||
|
|
@ -240,10 +233,6 @@ enum sr_mq {
|
||||||
SR_MQ_MASS,
|
SR_MQ_MASS,
|
||||||
/** Harmonic ratio */
|
/** Harmonic ratio */
|
||||||
SR_MQ_HARMONIC_RATIO,
|
SR_MQ_HARMONIC_RATIO,
|
||||||
/** Energy. */
|
|
||||||
SR_MQ_ENERGY,
|
|
||||||
/** Electric charge. */
|
|
||||||
SR_MQ_ELECTRIC_CHARGE,
|
|
||||||
|
|
||||||
/* Update sr_key_info_mq[] (hwdriver.c) upon changes! */
|
/* Update sr_key_info_mq[] (hwdriver.c) upon changes! */
|
||||||
};
|
};
|
||||||
|
|
@ -301,7 +290,7 @@ enum sr_unit {
|
||||||
SR_UNIT_VOLT_AMPERE,
|
SR_UNIT_VOLT_AMPERE,
|
||||||
/** Real power [W]. */
|
/** Real power [W]. */
|
||||||
SR_UNIT_WATT,
|
SR_UNIT_WATT,
|
||||||
/** Energy (consumption) in watt hour [Wh]. */
|
/** Consumption [Wh]. */
|
||||||
SR_UNIT_WATT_HOUR,
|
SR_UNIT_WATT_HOUR,
|
||||||
/** Wind speed in meters per second. */
|
/** Wind speed in meters per second. */
|
||||||
SR_UNIT_METER_SECOND,
|
SR_UNIT_METER_SECOND,
|
||||||
|
|
@ -335,12 +324,6 @@ enum sr_unit {
|
||||||
SR_UNIT_TOLA,
|
SR_UNIT_TOLA,
|
||||||
/** Pieces (number of items). */
|
/** Pieces (number of items). */
|
||||||
SR_UNIT_PIECE,
|
SR_UNIT_PIECE,
|
||||||
/** Energy in joule. */
|
|
||||||
SR_UNIT_JOULE,
|
|
||||||
/** Electric charge in coulomb. */
|
|
||||||
SR_UNIT_COULOMB,
|
|
||||||
/** Electric charge in ampere hour [Ah]. */
|
|
||||||
SR_UNIT_AMPERE_HOUR,
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update unit_strings[] (analog.c) and fancyprint() (output/analog.c)
|
* Update unit_strings[] (analog.c) and fancyprint() (output/analog.c)
|
||||||
|
|
@ -723,12 +706,6 @@ enum sr_configkey {
|
||||||
/** The device can measure power. */
|
/** The device can measure power. */
|
||||||
SR_CONF_POWERMETER,
|
SR_CONF_POWERMETER,
|
||||||
|
|
||||||
/**
|
|
||||||
* The device can switch between multiple sources, e.g. a relay actuator
|
|
||||||
* or multiplexer.
|
|
||||||
*/
|
|
||||||
SR_CONF_MULTIPLEXER,
|
|
||||||
|
|
||||||
/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
|
/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
|
||||||
|
|
||||||
/*--- Driver scan options -------------------------------------------*/
|
/*--- Driver scan options -------------------------------------------*/
|
||||||
|
|
@ -774,16 +751,6 @@ enum sr_configkey {
|
||||||
*/
|
*/
|
||||||
SR_CONF_MODBUSADDR,
|
SR_CONF_MODBUSADDR,
|
||||||
|
|
||||||
/**
|
|
||||||
* User specified forced driver attachment to unknown devices.
|
|
||||||
*
|
|
||||||
* By design the interpretation of the string depends on the
|
|
||||||
* specific driver. It typically would be either a replacement
|
|
||||||
* '*IDN?' response value, or a sub-driver name. But could also
|
|
||||||
* be anything else and totally arbitrary.
|
|
||||||
*/
|
|
||||||
SR_CONF_FORCE_DETECT,
|
|
||||||
|
|
||||||
/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
|
/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
|
||||||
|
|
||||||
/*--- Device (or channel group) configuration -----------------------*/
|
/*--- Device (or channel group) configuration -----------------------*/
|
||||||
|
|
@ -1054,35 +1021,6 @@ enum sr_configkey {
|
||||||
/** The number of digits (e.g. for a DMM). */
|
/** The number of digits (e.g. for a DMM). */
|
||||||
SR_CONF_DIGITS,
|
SR_CONF_DIGITS,
|
||||||
|
|
||||||
/** Phase of a source signal. */
|
|
||||||
SR_CONF_PHASE,
|
|
||||||
|
|
||||||
/** Duty cycle of a source signal. */
|
|
||||||
SR_CONF_DUTY_CYCLE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current power.
|
|
||||||
* @arg type: double
|
|
||||||
* @arg get: get measured power
|
|
||||||
*/
|
|
||||||
SR_CONF_POWER,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Power target.
|
|
||||||
* @arg type: double
|
|
||||||
* @arg get: get power target
|
|
||||||
* @arg set: change power target
|
|
||||||
*/
|
|
||||||
SR_CONF_POWER_TARGET,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resistance target.
|
|
||||||
* @arg type: double
|
|
||||||
* @arg get: get resistance target
|
|
||||||
* @arg set: change resistance target
|
|
||||||
*/
|
|
||||||
SR_CONF_RESISTANCE_TARGET,
|
|
||||||
|
|
||||||
/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
|
/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
|
||||||
|
|
||||||
/*--- Special stuff -------------------------------------------------*/
|
/*--- Special stuff -------------------------------------------------*/
|
||||||
|
|
|
||||||
283
src/analog.c
283
src/analog.c
|
|
@ -88,9 +88,6 @@ static struct unit_mq_string unit_strings[] = {
|
||||||
{ SR_UNIT_MOMME, "momme" },
|
{ SR_UNIT_MOMME, "momme" },
|
||||||
{ SR_UNIT_TOLA, "tola" },
|
{ SR_UNIT_TOLA, "tola" },
|
||||||
{ SR_UNIT_PIECE, "pcs" },
|
{ SR_UNIT_PIECE, "pcs" },
|
||||||
{ SR_UNIT_JOULE, "J" },
|
|
||||||
{ SR_UNIT_COULOMB, "C" },
|
|
||||||
{ SR_UNIT_AMPERE_HOUR, "Ah" },
|
|
||||||
ALL_ZERO
|
ALL_ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -160,8 +157,8 @@ SR_PRIV int sr_analog_init(struct sr_datafeed_analog *analog,
|
||||||
/**
|
/**
|
||||||
* Convert an analog datafeed payload to an array of floats.
|
* Convert an analog datafeed payload to an array of floats.
|
||||||
*
|
*
|
||||||
* The caller must provide the #outbuf space for the conversion result,
|
* Sufficient memory for outbuf must have been pre-allocated by the caller,
|
||||||
* and is expected to free allocated space after use.
|
* who is also responsible for freeing it when no longer needed.
|
||||||
*
|
*
|
||||||
* @param[in] analog The analog payload to convert. Must not be NULL.
|
* @param[in] analog The analog payload to convert. Must not be NULL.
|
||||||
* analog->data, analog->meaning, and analog->encoding
|
* analog->data, analog->meaning, and analog->encoding
|
||||||
|
|
@ -177,205 +174,124 @@ SR_PRIV int sr_analog_init(struct sr_datafeed_analog *analog,
|
||||||
SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
|
SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
|
||||||
float *outbuf)
|
float *outbuf)
|
||||||
{
|
{
|
||||||
size_t count;
|
unsigned int b, count;
|
||||||
gboolean host_bigendian;
|
gboolean bigendian;
|
||||||
gboolean input_float, input_signed, input_bigendian;
|
|
||||||
size_t input_unitsize;
|
|
||||||
double scale, offset, value;
|
|
||||||
const uint8_t *data8;
|
|
||||||
gboolean input_is_native;
|
|
||||||
char type_text[10];
|
|
||||||
|
|
||||||
if (!analog || !analog->data || !analog->meaning || !analog->encoding)
|
if (!analog || !(analog->data) || !(analog->meaning)
|
||||||
return SR_ERR_ARG;
|
|| !(analog->encoding) || !outbuf)
|
||||||
if (!outbuf)
|
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
|
|
||||||
count = analog->num_samples * g_slist_length(analog->meaning->channels);
|
count = analog->num_samples * g_slist_length(analog->meaning->channels);
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine properties of the input data's and the host's
|
|
||||||
* native formats, to simplify test conditions below.
|
|
||||||
* Error messages for unsupported input property combinations
|
|
||||||
* will only be seen by developers and maintainers of input
|
|
||||||
* formats or acquisition device drivers. Terse output is
|
|
||||||
* acceptable there, users shall never see them.
|
|
||||||
*/
|
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
host_bigendian = TRUE;
|
bigendian = TRUE;
|
||||||
#else
|
#else
|
||||||
host_bigendian = FALSE;
|
bigendian = FALSE;
|
||||||
#endif
|
#endif
|
||||||
input_float = analog->encoding->is_float;
|
|
||||||
input_signed = analog->encoding->is_signed;
|
|
||||||
input_bigendian = analog->encoding->is_bigendian;
|
|
||||||
input_unitsize = analog->encoding->unitsize;
|
|
||||||
|
|
||||||
/*
|
if (!analog->encoding->is_float) {
|
||||||
* Prepare the iteration over the sample data: Get the common
|
float offset = analog->encoding->offset.p / (float)analog->encoding->offset.q;
|
||||||
* scale/offset factors which apply to all individual values.
|
float scale = analog->encoding->scale.p / (float)analog->encoding->scale.q;
|
||||||
* Position the read pointer on the first byte of input data.
|
gboolean is_signed = analog->encoding->is_signed;
|
||||||
*/
|
gboolean is_bigendian = analog->encoding->is_bigendian;
|
||||||
offset = analog->encoding->offset.p;
|
int8_t *data8 = (int8_t *)(analog->data);
|
||||||
offset /= analog->encoding->offset.q;
|
int16_t *data16 = (int16_t *)(analog->data);
|
||||||
scale = analog->encoding->scale.p;
|
int32_t *data32 = (int32_t *)(analog->data);
|
||||||
scale /= analog->encoding->scale.q;
|
|
||||||
data8 = analog->data;
|
|
||||||
|
|
||||||
/*
|
switch (analog->encoding->unitsize) {
|
||||||
* Immediately handle the special case where input data needs
|
case 1:
|
||||||
* no conversion because it already is in the application's
|
if (is_signed) {
|
||||||
* native format. Do apply scale/offset though when applicable
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
* on our way out.
|
outbuf[i] = scale * data8[i];
|
||||||
*/
|
outbuf[i] += offset;
|
||||||
input_is_native = input_float &&
|
}
|
||||||
input_unitsize == sizeof(outbuf[0]) &&
|
} else {
|
||||||
input_bigendian == host_bigendian;
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
if (input_is_native) {
|
outbuf[i] = scale * R8(data8 + i);
|
||||||
memcpy(outbuf, data8, count * sizeof(outbuf[0]));
|
outbuf[i] += offset;
|
||||||
if (scale != 1.0 || offset != 0.0) {
|
|
||||||
while (count--) {
|
|
||||||
*outbuf *= scale;
|
|
||||||
*outbuf += offset;
|
|
||||||
outbuf++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SR_OK;
|
break;
|
||||||
|
case 2:
|
||||||
|
if (is_signed && is_bigendian) {
|
||||||
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
|
outbuf[i] = scale * RB16S(&data16[i]);
|
||||||
|
outbuf[i] += offset;
|
||||||
}
|
}
|
||||||
|
} else if (is_bigendian) {
|
||||||
/*
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
* Accept sample values in different widths and data types and
|
outbuf[i] = scale * RB16(&data16[i]);
|
||||||
* endianess formats (floating point or signed or unsigned
|
outbuf[i] += offset;
|
||||||
* integer, in either endianess, for a set of supported widths).
|
|
||||||
* Common scale/offset factors apply to all sample values.
|
|
||||||
*
|
|
||||||
* Do most internal calculations on double precision values.
|
|
||||||
* Only trim the result data to single precision, since that's
|
|
||||||
* the routine's result data type in its public API which needs
|
|
||||||
* to be kept for compatibility. It remains an option for later
|
|
||||||
* to add another public routine which returns double precision
|
|
||||||
* result data, call sites could migrate at their own pace.
|
|
||||||
*/
|
|
||||||
if (input_float && input_unitsize == sizeof(float)) {
|
|
||||||
float (*reader)(const uint8_t **p);
|
|
||||||
if (input_bigendian)
|
|
||||||
reader = read_fltbe_inc;
|
|
||||||
else
|
|
||||||
reader = read_fltle_inc;
|
|
||||||
while (count--) {
|
|
||||||
value = reader(&data8);
|
|
||||||
value *= scale;
|
|
||||||
value += offset;
|
|
||||||
*outbuf++ = value;
|
|
||||||
}
|
}
|
||||||
return SR_OK;
|
} else if (is_signed) {
|
||||||
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
|
outbuf[i] = scale * RL16S(&data16[i]);
|
||||||
|
outbuf[i] += offset;
|
||||||
}
|
}
|
||||||
if (input_float && input_unitsize == sizeof(double)) {
|
} else {
|
||||||
double (*reader)(const uint8_t **p);
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
if (input_bigendian)
|
outbuf[i] = scale * RL16(&data16[i]);
|
||||||
reader = read_dblbe_inc;
|
outbuf[i] += offset;
|
||||||
else
|
|
||||||
reader = read_dblle_inc;
|
|
||||||
while (count--) {
|
|
||||||
value = reader(&data8);
|
|
||||||
value *= scale;
|
|
||||||
value += offset;
|
|
||||||
*outbuf++ = value;
|
|
||||||
}
|
}
|
||||||
return SR_OK;
|
|
||||||
}
|
}
|
||||||
if (input_float) {
|
break;
|
||||||
snprintf(type_text, sizeof(type_text), "%c%zu%s",
|
case 4:
|
||||||
'f', input_unitsize * 8, input_bigendian ? "be" : "le");
|
if (is_signed && is_bigendian) {
|
||||||
sr_err("Unsupported type for analog-to-float conversion: %s.",
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
type_text);
|
outbuf[i] = scale * RB32S(&data32[i]);
|
||||||
|
outbuf[i] += offset;
|
||||||
|
}
|
||||||
|
} else if (is_bigendian) {
|
||||||
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
|
outbuf[i] = scale * RB32(&data32[i]);
|
||||||
|
outbuf[i] += offset;
|
||||||
|
}
|
||||||
|
} else if (is_signed) {
|
||||||
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
|
outbuf[i] = scale * RL32S(&data32[i]);
|
||||||
|
outbuf[i] += offset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
|
outbuf[i] = scale * RL32(&data32[i]);
|
||||||
|
outbuf[i] += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported unit size '%d' for analog-to-float"
|
||||||
|
" conversion.", analog->encoding->unitsize);
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (input_unitsize == sizeof(uint8_t) && input_signed) {
|
if (analog->encoding->unitsize == sizeof(float)
|
||||||
int8_t (*reader)(const uint8_t **p);
|
&& analog->encoding->is_bigendian == bigendian
|
||||||
reader = read_i8_inc;
|
&& analog->encoding->scale.p == 1
|
||||||
while (count--) {
|
&& analog->encoding->scale.q == 1
|
||||||
value = reader(&data8);
|
&& analog->encoding->offset.p / (float)analog->encoding->offset.q == 0) {
|
||||||
value *= scale;
|
/* The data is already in the right format. */
|
||||||
value += offset;
|
memcpy(outbuf, analog->data, count * sizeof(float));
|
||||||
*outbuf++ = value;
|
} else {
|
||||||
}
|
for (unsigned int i = 0; i < count; i += analog->encoding->unitsize) {
|
||||||
return SR_OK;
|
for (b = 0; b < analog->encoding->unitsize; b++) {
|
||||||
}
|
if (analog->encoding->is_bigendian == bigendian)
|
||||||
if (input_unitsize == sizeof(uint8_t)) {
|
((uint8_t *)outbuf)[i + b] =
|
||||||
uint8_t (*reader)(const uint8_t **p);
|
((uint8_t *)analog->data)[i * analog->encoding->unitsize + b];
|
||||||
reader = read_u8_inc;
|
|
||||||
while (count--) {
|
|
||||||
value = reader(&data8);
|
|
||||||
value *= scale;
|
|
||||||
value += offset;
|
|
||||||
*outbuf++ = value;
|
|
||||||
}
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
if (input_unitsize == sizeof(uint16_t) && input_signed) {
|
|
||||||
int16_t (*reader)(const uint8_t **p);
|
|
||||||
if (input_bigendian)
|
|
||||||
reader = read_i16be_inc;
|
|
||||||
else
|
else
|
||||||
reader = read_i16le_inc;
|
((uint8_t *)outbuf)[i + (analog->encoding->unitsize - b)] =
|
||||||
while (count--) {
|
((uint8_t *)analog->data)[i * analog->encoding->unitsize + b];
|
||||||
value = reader(&data8);
|
|
||||||
value *= scale;
|
|
||||||
value += offset;
|
|
||||||
*outbuf++ = value;
|
|
||||||
}
|
}
|
||||||
|
if (analog->encoding->scale.p != 1
|
||||||
|
|| analog->encoding->scale.q != 1)
|
||||||
|
outbuf[i] = (outbuf[i] * analog->encoding->scale.p) / analog->encoding->scale.q;
|
||||||
|
float offset = ((float)analog->encoding->offset.p / (float)analog->encoding->offset.q);
|
||||||
|
outbuf[i] += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
}
|
|
||||||
if (input_unitsize == sizeof(uint16_t)) {
|
|
||||||
uint16_t (*reader)(const uint8_t **p);
|
|
||||||
if (input_bigendian)
|
|
||||||
reader = read_u16be_inc;
|
|
||||||
else
|
|
||||||
reader = read_u16le_inc;
|
|
||||||
while (count--) {
|
|
||||||
value = reader(&data8);
|
|
||||||
value *= scale;
|
|
||||||
value += offset;
|
|
||||||
*outbuf++ = value;
|
|
||||||
}
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
if (input_unitsize == sizeof(uint32_t) && input_signed) {
|
|
||||||
int32_t (*reader)(const uint8_t **p);
|
|
||||||
if (input_bigendian)
|
|
||||||
reader = read_i32be_inc;
|
|
||||||
else
|
|
||||||
reader = read_i32le_inc;
|
|
||||||
while (count--) {
|
|
||||||
value = reader(&data8);
|
|
||||||
value *= scale;
|
|
||||||
value += offset;
|
|
||||||
*outbuf++ = value;
|
|
||||||
}
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
if (input_unitsize == sizeof(uint32_t)) {
|
|
||||||
uint32_t (*reader)(const uint8_t **p);
|
|
||||||
if (input_bigendian)
|
|
||||||
reader = read_u32be_inc;
|
|
||||||
else
|
|
||||||
reader = read_u32le_inc;
|
|
||||||
while (count--) {
|
|
||||||
value = reader(&data8);
|
|
||||||
value *= scale;
|
|
||||||
value += offset;
|
|
||||||
*outbuf++ = value;
|
|
||||||
}
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
snprintf(type_text, sizeof(type_text), "%c%zu%s",
|
|
||||||
input_float ? 'f' : input_signed ? 'i' : 'u',
|
|
||||||
input_unitsize * 8, input_bigendian ? "be" : "le");
|
|
||||||
sr_err("Unsupported type for analog-to-float conversion: %s.",
|
|
||||||
type_text);
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -686,8 +602,9 @@ SR_API int sr_rational_mult(struct sr_rational *res, const struct sr_rational *a
|
||||||
* @param[out] res Result.
|
* @param[out] res Result.
|
||||||
*
|
*
|
||||||
* @retval SR_OK Success.
|
* @retval SR_OK Success.
|
||||||
* @retval SR_ERR_ARG Division by zero, denominator of divisor too large,
|
* @retval SR_ERR_ARG Division by zero.
|
||||||
* or resulting value too large.
|
* @retval SR_ERR_ARG Denominator of divisor too large.
|
||||||
|
* @retval SR_ERR_ARG Resulting value too large.
|
||||||
*
|
*
|
||||||
* @since 0.5.0
|
* @since 0.5.0
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,8 @@
|
||||||
* @section sec_irc IRC
|
* @section sec_irc IRC
|
||||||
*
|
*
|
||||||
* You can find the sigrok developers in the
|
* You can find the sigrok developers in the
|
||||||
* <a href="ircs://irc.libera.chat/#sigrok">\#sigrok</a>
|
* <a href="irc://chat.freenode.net/sigrok">\#sigrok</a>
|
||||||
* IRC channel on Libera.Chat.
|
* IRC channel on Freenode.
|
||||||
*
|
*
|
||||||
* @section sec_website Website
|
* @section sec_website Website
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Andreas Sandberg <andreas@sandberg.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"
|
|
||||||
|
|
||||||
SR_PRIV int bv_get_value(float *out, const struct binary_value_spec *spec, const void *data, size_t length)
|
|
||||||
{
|
|
||||||
float value;
|
|
||||||
|
|
||||||
if (!out || !spec || !data)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
#define VALUE_TYPE(T, R, L) \
|
|
||||||
case T: \
|
|
||||||
if (spec->offset + (L) > length) \
|
|
||||||
return SR_ERR_DATA; \
|
|
||||||
value = R(data + spec->offset); \
|
|
||||||
break
|
|
||||||
|
|
||||||
switch (spec->type) {
|
|
||||||
VALUE_TYPE(BVT_UINT8, R8, 1);
|
|
||||||
|
|
||||||
VALUE_TYPE(BVT_BE_UINT16, RB16, 2);
|
|
||||||
VALUE_TYPE(BVT_BE_UINT32, RB32, 4);
|
|
||||||
VALUE_TYPE(BVT_BE_UINT64, RB64, 8);
|
|
||||||
VALUE_TYPE(BVT_BE_FLOAT, RBFL, 4);
|
|
||||||
|
|
||||||
VALUE_TYPE(BVT_LE_UINT16, RL16, 2);
|
|
||||||
VALUE_TYPE(BVT_LE_UINT32, RL32, 4);
|
|
||||||
VALUE_TYPE(BVT_LE_UINT64, RL64, 8);
|
|
||||||
VALUE_TYPE(BVT_LE_FLOAT, RLFL, 4);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef VALUE_TYPE
|
|
||||||
|
|
||||||
*out = value * spec->scale;
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int bv_send_analog_channel(const struct sr_dev_inst *sdi, struct sr_channel *ch,
|
|
||||||
const struct binary_analog_channel *bac, const void *data, size_t length)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct sr_analog_encoding encoding;
|
|
||||||
struct sr_analog_meaning meaning;
|
|
||||||
struct sr_analog_spec spec;
|
|
||||||
struct sr_datafeed_analog analog;
|
|
||||||
struct sr_datafeed_packet packet = {
|
|
||||||
.type = SR_DF_ANALOG,
|
|
||||||
.payload = &analog,
|
|
||||||
};
|
|
||||||
float value;
|
|
||||||
|
|
||||||
err = bv_get_value(&value, &bac->spec, data, length);
|
|
||||||
if (err != SR_OK)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
err = sr_analog_init(&analog, &encoding, &meaning, &spec, bac->digits);
|
|
||||||
if (err != SR_OK)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
meaning.mq = bac->mq;
|
|
||||||
meaning.unit = bac->unit;
|
|
||||||
meaning.mqflags = 0;
|
|
||||||
meaning.channels = g_slist_append(NULL, ch);
|
|
||||||
|
|
||||||
spec.spec_digits = bac->digits;
|
|
||||||
|
|
||||||
analog.data = &value;
|
|
||||||
analog.num_samples = 1;
|
|
||||||
|
|
||||||
err = sr_session_send(sdi, &packet);
|
|
||||||
if (err != SR_OK)
|
|
||||||
goto err_free;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
|
|
||||||
err_free:
|
|
||||||
g_slist_free(meaning.channels);
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
@ -87,15 +87,14 @@
|
||||||
#include <libsigrok/libsigrok.h>
|
#include <libsigrok/libsigrok.h>
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/** @cond PRIVATE */
|
||||||
#define LOG_PREFIX "bt-bluez"
|
#define LOG_PREFIX "bt-bluez"
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
#define CONNECT_BLE_TIMEOUT 20 /* Connect timeout in seconds. */
|
#define CONNECT_BLE_TIMEOUT 20 /* Connect timeout in seconds. */
|
||||||
#define STORE_MAC_REVERSE 1
|
#define STORE_MAC_REVERSE 1
|
||||||
#define ACCEPT_NONSEP_MAC 1
|
#define ACCEPT_NONSEP_MAC 1
|
||||||
|
|
||||||
#define CONNECT_RFCOMM_TRIES 3
|
|
||||||
#define CONNECT_RFCOMM_RETRY_MS 100
|
|
||||||
|
|
||||||
/* Silence warning about (currently) unused routine. */
|
/* Silence warning about (currently) unused routine. */
|
||||||
#define WITH_WRITE_TYPE_HANDLE 0
|
#define WITH_WRITE_TYPE_HANDLE 0
|
||||||
|
|
||||||
|
|
@ -106,6 +105,14 @@
|
||||||
* the header doesn't.
|
* the header doesn't.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !defined HAVE_BT_PUT_LE16
|
||||||
|
static inline void bt_put_le16(uint16_t v, uint8_t *p)
|
||||||
|
{
|
||||||
|
p[0] = (v >> 0) & 0xff;
|
||||||
|
p[1] = (v >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* }}} compat decls */
|
/* }}} compat decls */
|
||||||
/* {{{ Linux socket specific decls */
|
/* {{{ Linux socket specific decls */
|
||||||
|
|
||||||
|
|
@ -790,7 +797,7 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
|
||||||
SR_PRIV int sr_bt_connect_rfcomm(struct sr_bt_desc *desc)
|
SR_PRIV int sr_bt_connect_rfcomm(struct sr_bt_desc *desc)
|
||||||
{
|
{
|
||||||
struct sockaddr_rc addr;
|
struct sockaddr_rc addr;
|
||||||
int i, fd, rc;
|
int fd, rc;
|
||||||
|
|
||||||
if (!desc)
|
if (!desc)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -802,40 +809,25 @@ SR_PRIV int sr_bt_connect_rfcomm(struct sr_bt_desc *desc)
|
||||||
if (!desc->rfcomm_channel)
|
if (!desc->rfcomm_channel)
|
||||||
desc->rfcomm_channel = 1;
|
desc->rfcomm_channel = 1;
|
||||||
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
addr.rc_family = AF_BLUETOOTH;
|
|
||||||
str2ba(desc->remote_addr, &addr.rc_bdaddr);
|
|
||||||
addr.rc_channel = desc->rfcomm_channel;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There are cases where connect returns EBUSY if we are re-connecting
|
|
||||||
* to a device. Try multiple times to work around this issue.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < CONNECT_RFCOMM_TRIES; i++) {
|
|
||||||
fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
perror("socket");
|
perror("socket");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
|
|
||||||
if (rc >= 0) {
|
|
||||||
sr_spew("connected");
|
|
||||||
desc->fd = fd;
|
desc->fd = fd;
|
||||||
return 0;
|
|
||||||
} else if (rc < 0 && errno == EBUSY) {
|
memset(&addr, 0, sizeof(addr));
|
||||||
close(fd);
|
addr.rc_family = AF_BLUETOOTH;
|
||||||
g_usleep(CONNECT_RFCOMM_RETRY_MS * 1000);
|
str2ba(desc->remote_addr, &addr.rc_bdaddr);
|
||||||
} else {
|
addr.rc_channel = desc->rfcomm_channel;
|
||||||
close(fd);
|
rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||||
|
if (rc < 0) {
|
||||||
perror("connect");
|
perror("connect");
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
sr_spew("connected");
|
||||||
|
|
||||||
sr_err("Connect failed, device busy.");
|
return 0;
|
||||||
|
|
||||||
return -2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SR_PRIV void sr_bt_disconnect(struct sr_bt_desc *desc)
|
SR_PRIV void sr_bt_disconnect(struct sr_bt_desc *desc)
|
||||||
|
|
@ -890,7 +882,7 @@ SR_PRIV int sr_bt_start_notify(struct sr_bt_desc *desc)
|
||||||
if (sr_bt_check_socket_usable(desc) < 0)
|
if (sr_bt_check_socket_usable(desc) < 0)
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
write_u16le(buf, desc->cccd_value);
|
bt_put_le16(desc->cccd_value, buf);
|
||||||
wrlen = sr_bt_char_write_req(desc, desc->cccd_handle, buf, sizeof(buf));
|
wrlen = sr_bt_char_write_req(desc, desc->cccd_handle, buf, sizeof(buf));
|
||||||
if (wrlen != sizeof(buf))
|
if (wrlen != sizeof(buf))
|
||||||
return -2;
|
return -2;
|
||||||
|
|
@ -1040,7 +1032,7 @@ static ssize_t sr_bt_write_type_handle_bytes(struct sr_bt_desc *desc,
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
header[0] = type;
|
header[0] = type;
|
||||||
write_u16le(&header[1], handle);
|
bt_put_le16(handle, &header[1]);
|
||||||
|
|
||||||
if (data && len)
|
if (data && len)
|
||||||
wrlen = writev(desc->fd, iov, ARRAY_SIZE(iov));
|
wrlen = writev(desc->fd, iov, ARRAY_SIZE(iov));
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,7 @@
|
||||||
#include <libsigrok/libsigrok.h>
|
#include <libsigrok/libsigrok.h>
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
/** @cond PRIVATE */
|
|
||||||
#define LOG_PREFIX "conv"
|
#define LOG_PREFIX "conv"
|
||||||
/** @endcond */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert analog values to logic values by using a fixed threshold.
|
* Convert analog values to logic values by using a fixed threshold.
|
||||||
|
|
@ -69,7 +67,7 @@ SR_API int sr_a2l_threshold(const struct sr_datafeed_analog *analog,
|
||||||
*
|
*
|
||||||
* @param analog The analog input values.
|
* @param analog The analog input values.
|
||||||
* @param lo_thr The low threshold - result becomes 0 below it.
|
* @param lo_thr The low threshold - result becomes 0 below it.
|
||||||
* @param hi_thr The high threshold - result becomes 1 above it.
|
* @param lo_thr The high threshold - result becomes 1 above it.
|
||||||
* @param state The internal converter state. Must contain the state of logic
|
* @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.
|
* 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
|
* @param output The converted output values; either 0 or 1. Must provide
|
||||||
|
|
|
||||||
43
src/crc.c
43
src/crc.c
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Aurelien Jacobs <aurel@gnuage.org>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
|
|
||||||
SR_PRIV uint16_t sr_crc16(uint16_t crc, const uint8_t *buffer, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!buffer || len < 0)
|
|
||||||
return crc;
|
|
||||||
|
|
||||||
while (len--) {
|
|
||||||
crc ^= *buffer++;
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
int carry = crc & 1;
|
|
||||||
crc >>= 1;
|
|
||||||
if (carry)
|
|
||||||
crc ^= 0xA001;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
13
src/device.c
13
src/device.c
|
|
@ -92,9 +92,7 @@ SR_PRIV void sr_channel_free(struct sr_channel *ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around sr_channel_free(), suitable for glib iterators.
|
* Wrapper around @ref sr_channel_free(), suitable for glib iterators.
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
SR_PRIV void sr_channel_free_cb(void *p)
|
SR_PRIV void sr_channel_free_cb(void *p)
|
||||||
{
|
{
|
||||||
|
|
@ -203,9 +201,9 @@ SR_PRIV struct sr_channel *sr_next_enabled_channel(const struct sr_dev_inst *sdi
|
||||||
* @param[in] ch1 First channel.
|
* @param[in] ch1 First channel.
|
||||||
* @param[in] ch2 Second channel.
|
* @param[in] ch2 Second channel.
|
||||||
*
|
*
|
||||||
* @return TRUE upon differences or unexpected input, FALSE otherwise.
|
* @return #TRUE upon differences or unexpected input, #FALSE otherwise.
|
||||||
*
|
*
|
||||||
* @private
|
* @internal
|
||||||
*/
|
*/
|
||||||
SR_PRIV gboolean sr_channels_differ(struct sr_channel *ch1, struct sr_channel *ch2)
|
SR_PRIV gboolean sr_channels_differ(struct sr_channel *ch1, struct sr_channel *ch2)
|
||||||
{
|
{
|
||||||
|
|
@ -229,9 +227,9 @@ SR_PRIV gboolean sr_channels_differ(struct sr_channel *ch1, struct sr_channel *c
|
||||||
* @param[in] l1 First channel list.
|
* @param[in] l1 First channel list.
|
||||||
* @param[in] l2 Second channel list.
|
* @param[in] l2 Second channel list.
|
||||||
*
|
*
|
||||||
* @return TRUE upon differences or unexpected input, FALSE otherwise.
|
* @return #TRUE upon differences or unexpected input, #FALSE otherwise.
|
||||||
*
|
*
|
||||||
* @private
|
* @internal
|
||||||
*/
|
*/
|
||||||
SR_PRIV gboolean sr_channel_lists_differ(GSList *l1, GSList *l2)
|
SR_PRIV gboolean sr_channel_lists_differ(GSList *l1, GSList *l2)
|
||||||
{
|
{
|
||||||
|
|
@ -420,7 +418,6 @@ SR_API struct sr_dev_inst *sr_dev_inst_user_new(const char *vendor,
|
||||||
/**
|
/**
|
||||||
* Add a new channel to the specified device instance.
|
* Add a new channel to the specified device instance.
|
||||||
*
|
*
|
||||||
* @param[in] sdi Device instance to use. Must not be NULL.
|
|
||||||
* @param[in] index @copydoc sr_channel::index
|
* @param[in] index @copydoc sr_channel::index
|
||||||
* @param[in] type @copydoc sr_channel::type
|
* @param[in] type @copydoc sr_channel::type
|
||||||
* @param[in] name @copydoc sr_channel::name
|
* @param[in] name @copydoc sr_channel::name
|
||||||
|
|
|
||||||
|
|
@ -455,6 +455,8 @@ static gboolean flags_valid(const struct asycii_info *info)
|
||||||
* without the PC's intervention.
|
* without the PC's intervention.
|
||||||
*
|
*
|
||||||
* @param[in] serial The serial connection.
|
* @param[in] serial The serial connection.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
SR_PRIV int sr_asycii_packet_request(struct sr_serial_dev_inst *serial)
|
SR_PRIV int sr_asycii_packet_request(struct sr_serial_dev_inst *serial)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
1577
src/dmm/bm52x.c
1577
src/dmm/bm52x.c
File diff suppressed because it is too large
Load Diff
457
src/dmm/bm85x.c
457
src/dmm/bm85x.c
|
|
@ -1,457 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
|
||||||
* Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
|
|
||||||
* Copyright (C) 2019-2020 Gerhard Sittig <gerhard.sittig@gmx.net>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
*
|
|
||||||
* Protocol parser for Brymen BM850s DMM packets. The USB protocol (for the
|
|
||||||
* cable) and the packet description (for the meter) were retrieved from:
|
|
||||||
* http://brymen.com/product-html/Download2.html
|
|
||||||
* http://brymen.com/product-html/PD02BM850s_protocolDL.html
|
|
||||||
* http://brymen.com/product-html/images/DownloadList/ProtocolList/BM850-BM850a-BM850s_List/BM850-BM850a-BM850s-500000-count-DMM-protocol-BC85X-BC85Xa.zip
|
|
||||||
*
|
|
||||||
* Implementor's notes on the protocol:
|
|
||||||
* - The BM85x devices require a low RTS pulse after COM port open and
|
|
||||||
* before communication of requests and responses. The vendor doc
|
|
||||||
* recommends 100ms pulse width including delays around it. Without
|
|
||||||
* that RTS pulse the meter won't respond to requests.
|
|
||||||
* - The request has a three byte header (DLE, STX, command code), two
|
|
||||||
* bytes command arguments, and three bytes tail (checksum, DLE, ETX).
|
|
||||||
* The checksum spans the area (including) the command code and args.
|
|
||||||
* The checksum value is the XOR across all payload bytes. Exclusively
|
|
||||||
* command 0x00 is used (initiate next measurement response) which does
|
|
||||||
* not need arguments (passes all-zero values).
|
|
||||||
* - The response has a four byte header (DLE, STX, command code, payload
|
|
||||||
* size), the respective number of payload data bytes, and a three byte
|
|
||||||
* tail (checksum, DLE, ETX). The checksum spans the range after the
|
|
||||||
* length field and before the checksum field. Command 0 response data
|
|
||||||
* payload consists of a four byte flags field and a text field for
|
|
||||||
* measurement values (floating point with exponent in ASCII).
|
|
||||||
* - Special cases of response data:
|
|
||||||
* - The text field which carries the measurement value also contains
|
|
||||||
* whitespace which may break simple text to number conversion. Like
|
|
||||||
* 10 02 00 0f 07 00 00 00 20 30 2e 30 30 33 32 20 45 2b 30 46 10 03
|
|
||||||
* which translates to: 07 00 00 00 " 0.0032 E+0". Text for overload
|
|
||||||
* conditions can be shorter which results in variable packet length.
|
|
||||||
* Some meter functions provide unexpected text for their values.
|
|
||||||
* - The reference impedance for decibel measurements looks wrong and
|
|
||||||
* requires special treatment to isolate the 4..1200R value:
|
|
||||||
* bfunc 80 20 00 00, text " 0. 800 E+1" (reference, 800R)
|
|
||||||
* The decibel measurement values use an unexpected scale.
|
|
||||||
* bfunc 00 20 00 00, text "-0.3702 E-1" (measurement, -37.02dBm)
|
|
||||||
* The reference value gets sent (sometimes) in a DMM response when
|
|
||||||
* the meter's function is entered, or the reference value changes.
|
|
||||||
* The 'bfunc' flags combination allows telling packet types apart.
|
|
||||||
* - Temperature measurements put the C/F unit between the mantissa
|
|
||||||
* and the exponent, which needs to get removed: " 0.0217CE+3"
|
|
||||||
* - Diode measurements appear to exclusively provide the 'Volt' flag
|
|
||||||
* but no 'Diode' flag. The display shows ".diod" for a moment but
|
|
||||||
* this information is no longer available when voltage measurements
|
|
||||||
* are seen.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define LOG_PREFIX "brymen-bm85x"
|
|
||||||
|
|
||||||
#define STX 0x02
|
|
||||||
#define ETX 0x03
|
|
||||||
#define DLE 0x10
|
|
||||||
|
|
||||||
#define CMD_GET_READING 0
|
|
||||||
|
|
||||||
#define PKT_HEAD_LEN 4
|
|
||||||
#define PKT_DATA_MAX 15
|
|
||||||
#define PKT_TAIL_LEN 3
|
|
||||||
#define PKT_BFUNC_LEN 4
|
|
||||||
|
|
||||||
static uint8_t bm85x_crc(const uint8_t *buf, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t crc;
|
|
||||||
|
|
||||||
crc = 0;
|
|
||||||
while (len--)
|
|
||||||
crc ^= *buf++;
|
|
||||||
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_SERIAL_COMM
|
|
||||||
/** Meter's specific activity after port open and before data exchange. */
|
|
||||||
SR_PRIV int brymen_bm85x_after_open(struct sr_serial_dev_inst *serial)
|
|
||||||
{
|
|
||||||
int rts_toggle_delay_us;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The device requires an RTS *pulse* before communication.
|
|
||||||
* The vendor's documentation recommends the following sequence:
|
|
||||||
* Open the COM port, wait for 100ms, set RTS=1, wait for 100ms,
|
|
||||||
* set RTS=0, wait for 100ms, set RTS=1, configure bitrate and
|
|
||||||
* frame format, transmit request data, receive response data.
|
|
||||||
*/
|
|
||||||
rts_toggle_delay_us = 100 * 1000; /* 100ms */
|
|
||||||
g_usleep(rts_toggle_delay_us);
|
|
||||||
serial_set_handshake(serial, 1, -1);
|
|
||||||
g_usleep(rts_toggle_delay_us);
|
|
||||||
serial_set_handshake(serial, 0, -1);
|
|
||||||
g_usleep(rts_toggle_delay_us);
|
|
||||||
serial_set_handshake(serial, 1, -1);
|
|
||||||
g_usleep(rts_toggle_delay_us);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bm85x_send_command(struct sr_serial_dev_inst *serial,
|
|
||||||
uint8_t cmd, uint8_t arg1, uint8_t arg2)
|
|
||||||
{
|
|
||||||
uint8_t buf[8];
|
|
||||||
uint8_t crc, *wrptr, *crcptr;
|
|
||||||
size_t wrlen;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
wrptr = &buf[0];
|
|
||||||
write_u8_inc(&wrptr, DLE);
|
|
||||||
write_u8_inc(&wrptr, STX);
|
|
||||||
crcptr = wrptr;
|
|
||||||
write_u8_inc(&wrptr, cmd);
|
|
||||||
write_u8_inc(&wrptr, arg1);
|
|
||||||
write_u8_inc(&wrptr, arg2);
|
|
||||||
crc = bm85x_crc(crcptr, wrptr - crcptr);
|
|
||||||
write_u8_inc(&wrptr, crc);
|
|
||||||
write_u8_inc(&wrptr, DLE);
|
|
||||||
write_u8_inc(&wrptr, ETX);
|
|
||||||
|
|
||||||
wrlen = wrptr - &buf[0];
|
|
||||||
ret = serial_write_nonblocking(serial, &buf[0], wrlen);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
if ((size_t)ret != wrlen)
|
|
||||||
return SR_ERR_IO;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initiate reception of another meter's reading. */
|
|
||||||
SR_PRIV int brymen_bm85x_packet_request(struct sr_serial_dev_inst *serial)
|
|
||||||
{
|
|
||||||
return bm85x_send_command(serial, CMD_GET_READING, 0, 0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check Brymen BM85x DMM packet for validity.
|
|
||||||
*
|
|
||||||
* @param[in] st The DMM driver's internal state.
|
|
||||||
* @param[in] buf The data bytes received so far.
|
|
||||||
* @param[in] len The received data's length (byte count).
|
|
||||||
* @param[out] pkt_len The packet's calculated total size (when valid).
|
|
||||||
*
|
|
||||||
* The BM850s protocol uses packets of variable length. A minimum amount
|
|
||||||
* of RX data provides the packet header, which communicates the payload
|
|
||||||
* size, which allows to determine the packet's total size. Callers of
|
|
||||||
* this validity checker can learn how much data will get consumed when
|
|
||||||
* a valid packet got received and processed. The packet size is not
|
|
||||||
* known in advance.
|
|
||||||
*
|
|
||||||
* @returns SR_OK when the packet is valid.
|
|
||||||
* @returns SR_ERR* (below zero) when the packet is invalid.
|
|
||||||
* @returns Greater 0 when packet is incomplete, more data is needed.
|
|
||||||
*/
|
|
||||||
SR_PRIV int brymen_bm85x_packet_valid(void *st,
|
|
||||||
const uint8_t *buf, size_t len, size_t *pkt_len)
|
|
||||||
{
|
|
||||||
size_t plen;
|
|
||||||
uint8_t cmd, crc;
|
|
||||||
|
|
||||||
(void)st;
|
|
||||||
|
|
||||||
/* Four header bytes: DLE, STX, command, payload length. */
|
|
||||||
if (len < PKT_HEAD_LEN)
|
|
||||||
return SR_PACKET_NEED_RX;
|
|
||||||
if (read_u8_inc(&buf) != DLE)
|
|
||||||
return SR_PACKET_INVALID;
|
|
||||||
if (read_u8_inc(&buf) != STX)
|
|
||||||
return SR_PACKET_INVALID;
|
|
||||||
cmd = read_u8_inc(&buf);
|
|
||||||
/* Non-fatal, happens with OL pending during connect. */
|
|
||||||
if (cmd == 0x01)
|
|
||||||
cmd = 0x00;
|
|
||||||
if (cmd != CMD_GET_READING)
|
|
||||||
return SR_PACKET_INVALID;
|
|
||||||
plen = read_u8_inc(&buf);
|
|
||||||
if (plen > PKT_DATA_MAX)
|
|
||||||
return SR_PACKET_INVALID;
|
|
||||||
len -= PKT_HEAD_LEN;
|
|
||||||
|
|
||||||
/* Checksum spans bfunc and value text. Length according to header. */
|
|
||||||
if (len < plen + PKT_TAIL_LEN)
|
|
||||||
return SR_PACKET_NEED_RX;
|
|
||||||
crc = bm85x_crc(buf, plen);
|
|
||||||
buf += plen;
|
|
||||||
len -= plen;
|
|
||||||
|
|
||||||
/* Three tail bytes: checksum, DLE, ETX. */
|
|
||||||
if (len < PKT_TAIL_LEN)
|
|
||||||
return SR_PACKET_NEED_RX;
|
|
||||||
if (read_u8_inc(&buf) != crc)
|
|
||||||
return SR_PACKET_INVALID;
|
|
||||||
if (read_u8_inc(&buf) != DLE)
|
|
||||||
return SR_PACKET_INVALID;
|
|
||||||
if (read_u8_inc(&buf) != ETX)
|
|
||||||
return SR_PACKET_INVALID;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only return the total packet length when the receive buffer
|
|
||||||
* was found to be valid. For invalid packets it's preferred to
|
|
||||||
* have the caller keep trying to sync to the packet stream.
|
|
||||||
*/
|
|
||||||
if (pkt_len)
|
|
||||||
*pkt_len = PKT_HEAD_LEN + plen + PKT_TAIL_LEN;
|
|
||||||
return SR_PACKET_VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct bm85x_flags {
|
|
||||||
gboolean is_batt, is_db, is_perc, is_hz, is_amp, is_beep;
|
|
||||||
gboolean is_ohm, is_temp_f, is_temp_c, is_diode, is_cap;
|
|
||||||
gboolean is_volt, is_dc, is_ac;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int bm85x_parse_flags(const uint8_t *bfunc, struct bm85x_flags *flags)
|
|
||||||
{
|
|
||||||
if (!bfunc || !flags)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
memset(flags, 0, sizeof(*flags));
|
|
||||||
|
|
||||||
flags->is_batt = bfunc[3] & (1u << 7);
|
|
||||||
if ((bfunc[3] & 0x7f) != 0)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
if ((bfunc[2] & 0xff) != 0)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
if ((bfunc[1] & 0xc0) != 0)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
flags->is_db = bfunc[1] & (1u << 5);
|
|
||||||
if ((bfunc[1] & 0x10) != 0)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
flags->is_perc = bfunc[1] & (1u << 3);
|
|
||||||
flags->is_hz = bfunc[1] & (1u << 2);
|
|
||||||
flags->is_amp = bfunc[1] & (1u << 1);
|
|
||||||
flags->is_beep = bfunc[1] & (1u << 0);
|
|
||||||
|
|
||||||
flags->is_ohm = bfunc[0] & (1u << 7);
|
|
||||||
flags->is_temp_f = bfunc[0] & (1u << 6);
|
|
||||||
flags->is_temp_c = bfunc[0] & (1u << 5);
|
|
||||||
flags->is_diode = bfunc[0] & (1u << 4);
|
|
||||||
flags->is_cap = bfunc[0] & (1u << 3);
|
|
||||||
flags->is_volt = bfunc[0] & (1u << 2);
|
|
||||||
flags->is_dc = bfunc[0] & (1u << 1);
|
|
||||||
flags->is_ac = bfunc[0] & (1u << 0);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bm85x_parse_value(char *txt, double *val, int *digits)
|
|
||||||
{
|
|
||||||
char *src, *dst, c;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* See above comment on whitespace in response's number text.
|
|
||||||
* The caller provides a NUL terminated writable text copy.
|
|
||||||
* Go for low hanging fruit first (OL condition). Eliminate
|
|
||||||
* whitespace then and do the number conversion.
|
|
||||||
*/
|
|
||||||
if (strstr(txt, "+OL")) {
|
|
||||||
*val = +INFINITY;
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
if (strstr(txt, "-OL")) {
|
|
||||||
*val = -INFINITY;
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
if (strstr(txt, "OL")) {
|
|
||||||
*val = INFINITY;
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
src = txt;
|
|
||||||
dst = txt;
|
|
||||||
while (*src) {
|
|
||||||
c = *src++;
|
|
||||||
if (c == ' ')
|
|
||||||
continue;
|
|
||||||
*dst++ = c;
|
|
||||||
}
|
|
||||||
*dst = '\0';
|
|
||||||
|
|
||||||
ret = sr_atod_ascii_digits(txt, val, digits);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bm85x_parse_payload(const uint8_t *p, size_t l,
|
|
||||||
double *val, struct sr_datafeed_analog *analog)
|
|
||||||
{
|
|
||||||
const uint8_t *bfunc;
|
|
||||||
char text_buf[PKT_DATA_MAX], *text;
|
|
||||||
size_t text_len;
|
|
||||||
int ret;
|
|
||||||
struct bm85x_flags flags;
|
|
||||||
int digits;
|
|
||||||
char *parse;
|
|
||||||
|
|
||||||
/* Get a bfunc bits reference, and a writable value text. */
|
|
||||||
bfunc = &p[0];
|
|
||||||
text_len = l - PKT_BFUNC_LEN;
|
|
||||||
memcpy(text_buf, &p[PKT_BFUNC_LEN], text_len);
|
|
||||||
text_buf[text_len] = '\0';
|
|
||||||
text = &text_buf[0];
|
|
||||||
sr_dbg("DMM bfunc %02x %02x %02x %02x, text \"%s\"",
|
|
||||||
bfunc[0], bfunc[1], bfunc[2], bfunc[3], text);
|
|
||||||
|
|
||||||
/* Check 'bfunc' bitfield first, text interpretation depends on it. */
|
|
||||||
ret = bm85x_parse_flags(bfunc, &flags);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Parse the text after potential normalization/transformation. */
|
|
||||||
if (flags.is_db && flags.is_ohm) {
|
|
||||||
static const char *prefix = " 0.";
|
|
||||||
static const char *suffix = " E";
|
|
||||||
/* See above comment on dBm reference value text. */
|
|
||||||
if (strncmp(text, prefix, strlen(prefix)) != 0)
|
|
||||||
return SR_ERR_DATA;
|
|
||||||
text += strlen(prefix);
|
|
||||||
text_len -= strlen(prefix);
|
|
||||||
parse = strstr(text, suffix);
|
|
||||||
if (!parse)
|
|
||||||
return SR_ERR_DATA;
|
|
||||||
*parse = '\0';
|
|
||||||
}
|
|
||||||
if (flags.is_temp_f || flags.is_temp_c) {
|
|
||||||
/* See above comment on temperature value text. */
|
|
||||||
parse = strchr(text, flags.is_temp_f ? 'F' : 'C');
|
|
||||||
if (!parse)
|
|
||||||
return SR_ERR_DATA;
|
|
||||||
*parse = ' ';
|
|
||||||
}
|
|
||||||
digits = 0;
|
|
||||||
ret = bm85x_parse_value(text, val, &digits);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Fill in MQ and flags result details. */
|
|
||||||
analog->meaning->mqflags = 0;
|
|
||||||
if (flags.is_volt) {
|
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
|
||||||
}
|
|
||||||
if (flags.is_amp) {
|
|
||||||
analog->meaning->mq = SR_MQ_CURRENT;
|
|
||||||
analog->meaning->unit = SR_UNIT_AMPERE;
|
|
||||||
}
|
|
||||||
if (flags.is_ohm) {
|
|
||||||
if (flags.is_db)
|
|
||||||
analog->meaning->mq = SR_MQ_RESISTANCE;
|
|
||||||
else if (flags.is_beep)
|
|
||||||
analog->meaning->mq = SR_MQ_CONTINUITY;
|
|
||||||
else
|
|
||||||
analog->meaning->mq = SR_MQ_RESISTANCE;
|
|
||||||
analog->meaning->unit = SR_UNIT_OHM;
|
|
||||||
}
|
|
||||||
if (flags.is_hz) {
|
|
||||||
analog->meaning->mq = SR_MQ_FREQUENCY;
|
|
||||||
analog->meaning->unit = SR_UNIT_HERTZ;
|
|
||||||
}
|
|
||||||
if (flags.is_perc) {
|
|
||||||
analog->meaning->mq = SR_MQ_DUTY_CYCLE;
|
|
||||||
analog->meaning->unit = SR_UNIT_PERCENTAGE;
|
|
||||||
}
|
|
||||||
if (flags.is_cap) {
|
|
||||||
analog->meaning->mq = SR_MQ_CAPACITANCE;
|
|
||||||
analog->meaning->unit = SR_UNIT_FARAD;
|
|
||||||
}
|
|
||||||
if (flags.is_temp_f) {
|
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
|
||||||
analog->meaning->unit = SR_UNIT_FAHRENHEIT;
|
|
||||||
}
|
|
||||||
if (flags.is_temp_c) {
|
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
|
||||||
analog->meaning->unit = SR_UNIT_CELSIUS;
|
|
||||||
}
|
|
||||||
if (flags.is_db && !flags.is_ohm) {
|
|
||||||
/* See above comment on dBm measurements scale. */
|
|
||||||
analog->meaning->mq = SR_MQ_POWER;
|
|
||||||
analog->meaning->unit = SR_UNIT_DECIBEL_MW;
|
|
||||||
*val *= 1000;
|
|
||||||
digits -= 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags.is_diode) {
|
|
||||||
/* See above comment on diode measurement responses. */
|
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DIODE;
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DC;
|
|
||||||
}
|
|
||||||
if (flags.is_ac)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AC;
|
|
||||||
if (flags.is_dc)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DC;
|
|
||||||
|
|
||||||
analog->encoding->digits = digits;
|
|
||||||
analog->spec->spec_digits = digits;
|
|
||||||
|
|
||||||
if (flags.is_batt)
|
|
||||||
sr_warn("Low battery!");
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int brymen_bm85x_parse(void *st, const uint8_t *buf, size_t len,
|
|
||||||
double *val, struct sr_datafeed_analog *analog, void *info)
|
|
||||||
{
|
|
||||||
const uint8_t *pl_ptr;
|
|
||||||
size_t pl_len;
|
|
||||||
|
|
||||||
(void)st;
|
|
||||||
(void)info;
|
|
||||||
|
|
||||||
if (!buf || !len)
|
|
||||||
return SR_ERR_DATA;
|
|
||||||
if (!val || !analog)
|
|
||||||
return SR_ERR_DATA;
|
|
||||||
|
|
||||||
if (brymen_bm85x_packet_valid(NULL, buf, len, NULL) != SR_PACKET_VALID)
|
|
||||||
return SR_ERR_DATA;
|
|
||||||
pl_ptr = &buf[PKT_HEAD_LEN];
|
|
||||||
pl_len = len - PKT_HEAD_LEN - PKT_TAIL_LEN;
|
|
||||||
|
|
||||||
return bm85x_parse_payload(pl_ptr, pl_len, val, analog);
|
|
||||||
}
|
|
||||||
|
|
@ -22,6 +22,8 @@
|
||||||
*
|
*
|
||||||
* EEVblog 121GW 19-bytes binary protocol parser.
|
* EEVblog 121GW 19-bytes binary protocol parser.
|
||||||
*
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
* Note that this protocol is different from other meters. We need not
|
* Note that this protocol is different from other meters. We need not
|
||||||
* decode the LCD presentation (segments a-g and dot of seven segment
|
* decode the LCD presentation (segments a-g and dot of seven segment
|
||||||
* displays). Neither need we decode a textual presentation consisting
|
* displays). Neither need we decode a textual presentation consisting
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
*
|
*
|
||||||
* Metex 14-bytes ASCII protocol parser.
|
* Metex 14-bytes ASCII protocol parser.
|
||||||
*
|
*
|
||||||
|
* @internal
|
||||||
* This should work for various multimeters which use this kind of protocol,
|
* This should work for various multimeters which use this kind of protocol,
|
||||||
* even though there is some variation in which modes each DMM supports.
|
* even though there is some variation in which modes each DMM supports.
|
||||||
*
|
*
|
||||||
|
|
@ -332,19 +333,10 @@ static gboolean flags_valid(const struct metex14_info *info)
|
||||||
SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial)
|
SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial)
|
||||||
{
|
{
|
||||||
const uint8_t wbuf = 'D';
|
const uint8_t wbuf = 'D';
|
||||||
size_t wrlen;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
sr_spew("Requesting DMM packet.");
|
sr_spew("Requesting DMM packet.");
|
||||||
|
|
||||||
wrlen = sizeof(wbuf);
|
return serial_write_blocking(serial, &wbuf, 1, 0);
|
||||||
ret = serial_write_blocking(serial, &wbuf, wrlen, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
if ((size_t)ret != wrlen)
|
|
||||||
return SR_ERR_IO;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
457
src/dmm/mm38xr.c
457
src/dmm/mm38xr.c
|
|
@ -1,457 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Peter Skarpetis <peters@skarpetis.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Meterman 38XR protocol parser
|
|
||||||
*
|
|
||||||
* Communication parameters: Unidirectional, 9600/8n1
|
|
||||||
*
|
|
||||||
* The user guide can be downloaded from:
|
|
||||||
* https://assets.tequipment.net/assets/1/26/Documents/38XR_Manual.pdf
|
|
||||||
*
|
|
||||||
* Protocol is described in a PDF available at:
|
|
||||||
* https://www.elfadistrelec.fi/Web/Downloads/od/es/fj38XR-Serial-Output-Codes.pdf
|
|
||||||
*
|
|
||||||
* There is also a disussion about the protocol at the NI forum:
|
|
||||||
* https://forums.ni.com/t5/Digital-Multimeters-DMMs-and/Meterman-DMM/td-p/179597?profile.language=en
|
|
||||||
*
|
|
||||||
* EEVBlog discussion thread about the meter
|
|
||||||
* https://www.eevblog.com/forum/chat/meterman-38xr/
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
*
|
|
||||||
* Meterman 38XR ASCII protocol parser.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define LOG_PREFIX "mm38xr"
|
|
||||||
|
|
||||||
#define METERMAN_DIGITS_OVERLOAD 0xb0dd
|
|
||||||
#define METERMAN_DIGITS_BAD_INPUT_JACK 0xbaab
|
|
||||||
#define METERMAN_BARGRAPH_NO_SEGMENTS = 0x2a
|
|
||||||
|
|
||||||
enum mm38xr_func_code {
|
|
||||||
FUNC_CODE_UNUSED = 0x01,
|
|
||||||
FUNC_CODE_TEMPERATURE_FARENHEIGHT = 0x02,
|
|
||||||
FUNC_CODE_CURRENT_4_20_MAMPS = 0x03, /* 4-20 mA */
|
|
||||||
FUNC_CODE_DIODE_TEST = 0x04,
|
|
||||||
FUNC_CODE_INDUCTANCE_HENRIES = 0x05,
|
|
||||||
FUNC_CODE_TEMPERATURE_CELSIUS = 0x06,
|
|
||||||
FUNC_CODE_CURRENT_UAMPS = 0x07, /* uA */
|
|
||||||
FUNC_CODE_RESISTANCE_OHMS = 0x08,
|
|
||||||
FUNC_CODE_INDUCTANCE_MHENRIES = 0x09, /* mH */
|
|
||||||
FUNC_CODE_CURRENT_10_AMPS = 0x0a,
|
|
||||||
FUNC_CODE_CAPACITANCE = 0x0b,
|
|
||||||
FUNC_CODE_VOLTS_DC = 0x0c,
|
|
||||||
FUNC_CODE_LOGIC = 0x0d,
|
|
||||||
FUNC_CODE_CURRENT_MAMPS = 0x0e, /* mA */
|
|
||||||
FUNC_CODE_FREQUENCY_HZ = 0x0f, /* and duty cycle */
|
|
||||||
FUNC_CODE_VOLTS_AC = 0x10, /* and dBm */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mm38xr_meas_mode {
|
|
||||||
/* This is used to index into the digits and exponent arrays below. */
|
|
||||||
MEAS_MODE_VOLTS,
|
|
||||||
MEAS_MODE_RESISTANCE_OHMS,
|
|
||||||
MEAS_MODE_CURRENT_UAMPS, /* uA */
|
|
||||||
MEAS_MODE_CURRENT_MAMPS, /* mA */
|
|
||||||
MEAS_MODE_CURRENT_AMPS,
|
|
||||||
MEAS_MODE_CAPACITANCE,
|
|
||||||
MEAS_MODE_DIODE_TEST,
|
|
||||||
MEAS_MODE_TEMPERATURE_C,
|
|
||||||
MEAS_MODE_TEMPERATURE_F,
|
|
||||||
MEAS_MODE_FREQUENCY_HZ,
|
|
||||||
MEAS_MODE_INDUCTANCE_H,
|
|
||||||
MEAS_MODE_INDUCTANCE_MH, /* mH */
|
|
||||||
MEAS_MODE_DBM,
|
|
||||||
MEAS_MODE_DUTY_CYCLE,
|
|
||||||
MEAS_MODE_CONTINUITY,
|
|
||||||
/* For internal purposes. */
|
|
||||||
MEAS_MODE_UNDEFINED,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mm38xr_adcd_mode {
|
|
||||||
ACDC_MODE_NONE = 1000,
|
|
||||||
ACDC_MODE_DC,
|
|
||||||
ACDC_MODE_AC,
|
|
||||||
ACDC_MODE_AC_AND_DC,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct meterman_info {
|
|
||||||
enum mm38xr_func_code functioncode; /* columns 0, 1 */
|
|
||||||
unsigned int reading; /* columns 2,3,4,5; LCD digits */
|
|
||||||
unsigned int bargraphsegments; /* columns 6, 7; max 40 segments, 0x2A = no bargraph */
|
|
||||||
size_t rangecode; /* column 8 */
|
|
||||||
unsigned int ampsfunction; /* column 9 */
|
|
||||||
unsigned int peakstatus; /* column 10 */
|
|
||||||
unsigned int rflag_h; /* column 11 */
|
|
||||||
unsigned int rflag_l; /* column 12 */
|
|
||||||
|
|
||||||
/* calculated values */
|
|
||||||
enum mm38xr_meas_mode meas_mode;
|
|
||||||
enum mm38xr_adcd_mode acdc;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int decimal_digits[][7] = {
|
|
||||||
[MEAS_MODE_VOLTS] = { 1, 3, 2, 1, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_RESISTANCE_OHMS] = { 2, 3, 4, 2, 3, 1, 0, },
|
|
||||||
[MEAS_MODE_CURRENT_UAMPS] = { 2, 1, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_CURRENT_MAMPS] = { 3, 2, 1, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_CURRENT_AMPS] = { 3, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_CAPACITANCE] = { 2, 1, 3, 2, 1, 0, 0, },
|
|
||||||
[MEAS_MODE_DIODE_TEST] = { 0, 3, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_TEMPERATURE_C] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_TEMPERATURE_F] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_FREQUENCY_HZ] = { 2, 1, 3, 2, 1, 3, 2, },
|
|
||||||
[MEAS_MODE_INDUCTANCE_H] = { 0, 0, 0, 3, 2, 0, 0, },
|
|
||||||
[MEAS_MODE_INDUCTANCE_MH] = { 3, 2, 1, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_DBM] = { 2, 2, 2, 2, 2, 2, 2, },
|
|
||||||
[MEAS_MODE_DUTY_CYCLE] = { 2, 2, 2, 2, 2, 2, 2, },
|
|
||||||
[MEAS_MODE_CONTINUITY] = { 0, 0, 0, 0, 0, 1, 0, },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int units_exponents[][7] = {
|
|
||||||
[MEAS_MODE_VOLTS] = { -3, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_RESISTANCE_OHMS] = { 6, 6, 6, 3, 3, 0, 0, },
|
|
||||||
[MEAS_MODE_CURRENT_UAMPS] = { -6, -6, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_CURRENT_MAMPS] = { -3, -3, -3, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_CURRENT_AMPS] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_CAPACITANCE] = { -9, -9, -6, -6, -6, 0, 0, },
|
|
||||||
[MEAS_MODE_DIODE_TEST] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_TEMPERATURE_C] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_TEMPERATURE_F] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_FREQUENCY_HZ] = { 0, 0, 3, 3, 3, 6, 6, },
|
|
||||||
[MEAS_MODE_INDUCTANCE_H] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_INDUCTANCE_MH] = { -3, -3, -3, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_DBM] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_DUTY_CYCLE] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
[MEAS_MODE_CONTINUITY] = { 0, 0, 0, 0, 0, 0, 0, },
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Assumes caller has already checked data fall within 0..9 and A..F */
|
|
||||||
static uint32_t meterman_38xr_hexnibble_to_uint(uint8_t v)
|
|
||||||
{
|
|
||||||
return (v <= '9') ? v - '0' : v - 'A' + 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t meterman_38xr_func_code(const uint8_t *buf)
|
|
||||||
{
|
|
||||||
uint32_t v;
|
|
||||||
|
|
||||||
v = meterman_38xr_hexnibble_to_uint(buf[0]) << 4 |
|
|
||||||
meterman_38xr_hexnibble_to_uint(buf[1]);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t meterman_38xr_barsegments(const uint8_t *buf)
|
|
||||||
{
|
|
||||||
uint32_t v;
|
|
||||||
|
|
||||||
v = meterman_38xr_hexnibble_to_uint(buf[6]) << 4 |
|
|
||||||
meterman_38xr_hexnibble_to_uint(buf[7]);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t meterman_38xr_reading(const uint8_t *buf)
|
|
||||||
{
|
|
||||||
uint32_t v;
|
|
||||||
|
|
||||||
if (buf[2] > 'A') { /* overload */
|
|
||||||
v = meterman_38xr_hexnibble_to_uint(buf[2]) << 12 |
|
|
||||||
meterman_38xr_hexnibble_to_uint(buf[3]) << 8 |
|
|
||||||
meterman_38xr_hexnibble_to_uint(buf[4]) << 4 |
|
|
||||||
meterman_38xr_hexnibble_to_uint(buf[5]) << 0;
|
|
||||||
} else {
|
|
||||||
v = meterman_38xr_hexnibble_to_uint(buf[2]) * 1000 +
|
|
||||||
meterman_38xr_hexnibble_to_uint(buf[3]) * 100 +
|
|
||||||
meterman_38xr_hexnibble_to_uint(buf[4]) * 10 +
|
|
||||||
meterman_38xr_hexnibble_to_uint(buf[5]) * 1;
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean meterman_38xr_is_negative(struct meterman_info *mi)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (mi->rflag_l == 0x01)
|
|
||||||
return TRUE;
|
|
||||||
if (mi->meas_mode == MEAS_MODE_DBM && mi->rflag_l == 0x05)
|
|
||||||
return TRUE;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int currentACDC(struct meterman_info *mi)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (mi->ampsfunction == 0x01)
|
|
||||||
return ACDC_MODE_AC;
|
|
||||||
if (mi->ampsfunction == 0x02)
|
|
||||||
return ACDC_MODE_AC_AND_DC;
|
|
||||||
return ACDC_MODE_DC;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int meterman_38xr_decode(const uint8_t *buf, struct meterman_info *mi)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!meterman_38xr_packet_valid(buf))
|
|
||||||
return SR_ERR;
|
|
||||||
|
|
||||||
mi->functioncode = meterman_38xr_func_code(buf);
|
|
||||||
if (mi->functioncode < 2 || mi->functioncode > 0x10)
|
|
||||||
return SR_ERR;
|
|
||||||
mi->reading = meterman_38xr_reading(buf);
|
|
||||||
mi->bargraphsegments = meterman_38xr_barsegments(buf);
|
|
||||||
mi->rangecode = meterman_38xr_hexnibble_to_uint(buf[8]);
|
|
||||||
if (mi->rangecode > 6)
|
|
||||||
return SR_ERR;
|
|
||||||
mi->ampsfunction = meterman_38xr_hexnibble_to_uint(buf[9]);
|
|
||||||
mi->peakstatus = meterman_38xr_hexnibble_to_uint(buf[10]);
|
|
||||||
mi->rflag_h = meterman_38xr_hexnibble_to_uint(buf[11]);
|
|
||||||
mi->rflag_l = meterman_38xr_hexnibble_to_uint(buf[12]);
|
|
||||||
|
|
||||||
mi->acdc = ACDC_MODE_NONE;
|
|
||||||
switch (mi->functioncode) {
|
|
||||||
case FUNC_CODE_TEMPERATURE_FARENHEIGHT:
|
|
||||||
mi->meas_mode = MEAS_MODE_TEMPERATURE_F;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_CURRENT_4_20_MAMPS:
|
|
||||||
mi->meas_mode = MEAS_MODE_CURRENT_MAMPS;
|
|
||||||
mi->acdc = currentACDC(mi);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_DIODE_TEST:
|
|
||||||
mi->meas_mode = MEAS_MODE_DIODE_TEST;
|
|
||||||
mi->acdc = ACDC_MODE_DC;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_INDUCTANCE_HENRIES:
|
|
||||||
mi->meas_mode = MEAS_MODE_INDUCTANCE_H;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_TEMPERATURE_CELSIUS:
|
|
||||||
mi->meas_mode = MEAS_MODE_TEMPERATURE_C;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_CURRENT_UAMPS:
|
|
||||||
mi->meas_mode = MEAS_MODE_CURRENT_UAMPS;
|
|
||||||
mi->acdc = currentACDC(mi);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_RESISTANCE_OHMS:
|
|
||||||
mi->meas_mode = (mi->rflag_l == 0x08)
|
|
||||||
? MEAS_MODE_CONTINUITY
|
|
||||||
: MEAS_MODE_RESISTANCE_OHMS;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_INDUCTANCE_MHENRIES:
|
|
||||||
mi->meas_mode = MEAS_MODE_INDUCTANCE_MH;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_CURRENT_10_AMPS:
|
|
||||||
mi->meas_mode = MEAS_MODE_CURRENT_AMPS;
|
|
||||||
mi->acdc = currentACDC(mi);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_CAPACITANCE:
|
|
||||||
mi->meas_mode = MEAS_MODE_CAPACITANCE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_VOLTS_DC:
|
|
||||||
mi->meas_mode = MEAS_MODE_VOLTS;
|
|
||||||
mi->acdc = (mi->rflag_l == 0x02)
|
|
||||||
? ACDC_MODE_AC_AND_DC : ACDC_MODE_DC;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_CURRENT_MAMPS:
|
|
||||||
mi->meas_mode = MEAS_MODE_CURRENT_MAMPS;
|
|
||||||
mi->acdc = currentACDC(mi);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_FREQUENCY_HZ:
|
|
||||||
mi->meas_mode = (mi->rflag_h == 0x0B)
|
|
||||||
? MEAS_MODE_DUTY_CYCLE
|
|
||||||
: MEAS_MODE_FREQUENCY_HZ;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNC_CODE_VOLTS_AC:
|
|
||||||
mi->meas_mode = (mi->rflag_l == 0x04 || mi->rflag_l == 0x05)
|
|
||||||
? MEAS_MODE_DBM : MEAS_MODE_VOLTS;
|
|
||||||
mi->acdc = ACDC_MODE_AC;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
mi->meas_mode = MEAS_MODE_UNDEFINED;
|
|
||||||
return SR_ERR;
|
|
||||||
|
|
||||||
}
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV gboolean meterman_38xr_packet_valid(const uint8_t *buf)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
uint32_t fcode;
|
|
||||||
|
|
||||||
if ((buf[13] != '\r') || (buf[14] != '\n'))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* Check for all hex digits */
|
|
||||||
for (i = 0; i < 13; i++) {
|
|
||||||
if (buf[i] < '0')
|
|
||||||
return FALSE;
|
|
||||||
if (buf[i] > '9' && buf[i] < 'A')
|
|
||||||
return FALSE;
|
|
||||||
if (buf[i] > 'F')
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
fcode = meterman_38xr_func_code(buf);
|
|
||||||
if (fcode < 0x01 || fcode > 0x10)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int meterman_38xr_parse(const uint8_t *buf, float *floatval,
|
|
||||||
struct sr_datafeed_analog *analog, void *info)
|
|
||||||
{
|
|
||||||
gboolean is_overload, is_bad_jack;
|
|
||||||
int exponent;
|
|
||||||
int digits;
|
|
||||||
struct meterman_info mi;
|
|
||||||
|
|
||||||
(void)info;
|
|
||||||
|
|
||||||
if (meterman_38xr_decode(buf, &mi) != SR_OK)
|
|
||||||
return SR_ERR;
|
|
||||||
|
|
||||||
if (mi.meas_mode != MEAS_MODE_CONTINUITY) {
|
|
||||||
is_overload = mi.reading == METERMAN_DIGITS_OVERLOAD;
|
|
||||||
is_bad_jack = mi.reading == METERMAN_DIGITS_BAD_INPUT_JACK;
|
|
||||||
if (is_overload || is_bad_jack) {
|
|
||||||
sr_spew("Over limit.");
|
|
||||||
*floatval = INFINITY; /* overload */
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (mi.meas_mode) {
|
|
||||||
case MEAS_MODE_VOLTS:
|
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_RESISTANCE_OHMS:
|
|
||||||
analog->meaning->mq = SR_MQ_RESISTANCE;
|
|
||||||
analog->meaning->unit = SR_UNIT_OHM;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_CURRENT_UAMPS:
|
|
||||||
case MEAS_MODE_CURRENT_MAMPS:
|
|
||||||
case MEAS_MODE_CURRENT_AMPS:
|
|
||||||
analog->meaning->mq = SR_MQ_CURRENT;
|
|
||||||
analog->meaning->unit = SR_UNIT_AMPERE;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_CAPACITANCE:
|
|
||||||
analog->meaning->mq = SR_MQ_CAPACITANCE;
|
|
||||||
analog->meaning->unit = SR_UNIT_FARAD;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_DIODE_TEST:
|
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DIODE;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_TEMPERATURE_C:
|
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
|
||||||
analog->meaning->unit = SR_UNIT_CELSIUS;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_TEMPERATURE_F:
|
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
|
||||||
analog->meaning->unit = SR_UNIT_FAHRENHEIT;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_FREQUENCY_HZ:
|
|
||||||
analog->meaning->mq = SR_MQ_FREQUENCY;
|
|
||||||
analog->meaning->unit = SR_UNIT_HERTZ;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_INDUCTANCE_H:
|
|
||||||
analog->meaning->mq = SR_MQ_SERIES_INDUCTANCE;
|
|
||||||
analog->meaning->unit = SR_UNIT_HENRY;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_INDUCTANCE_MH:
|
|
||||||
analog->meaning->mq = SR_MQ_SERIES_INDUCTANCE;
|
|
||||||
analog->meaning->unit = SR_UNIT_HENRY;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_DBM:
|
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
|
||||||
analog->meaning->unit = SR_UNIT_DECIBEL_MW;
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AC;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_DUTY_CYCLE:
|
|
||||||
analog->meaning->mq = SR_MQ_DUTY_CYCLE;
|
|
||||||
analog->meaning->unit = SR_UNIT_PERCENTAGE;
|
|
||||||
break;
|
|
||||||
case MEAS_MODE_CONTINUITY:
|
|
||||||
analog->meaning->mq = SR_MQ_CONTINUITY;
|
|
||||||
analog->meaning->unit = SR_UNIT_BOOLEAN;
|
|
||||||
*floatval = (mi.reading == METERMAN_DIGITS_OVERLOAD) ? 0.0 : 1.0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
switch (mi.acdc) {
|
|
||||||
case ACDC_MODE_DC:
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DC;
|
|
||||||
break;
|
|
||||||
case ACDC_MODE_AC:
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AC;
|
|
||||||
break;
|
|
||||||
case ACDC_MODE_AC_AND_DC:
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (mi.peakstatus == 0x02 || mi.peakstatus == 0x0a)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_MAX;
|
|
||||||
if (mi.peakstatus == 0x03 || mi.peakstatus == 0x0b)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_MIN;
|
|
||||||
if (mi.rflag_h == 0x0a || mi.peakstatus == 0x0b)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
|
|
||||||
if (mi.meas_mode != MEAS_MODE_CONTINUITY) {
|
|
||||||
digits = decimal_digits[mi.meas_mode][mi.rangecode];
|
|
||||||
exponent = units_exponents[mi.meas_mode][mi.rangecode];
|
|
||||||
|
|
||||||
*floatval = mi.reading;
|
|
||||||
if (meterman_38xr_is_negative(&mi)) {
|
|
||||||
*floatval *= -1.0f;
|
|
||||||
}
|
|
||||||
*floatval *= powf(10, -digits);
|
|
||||||
*floatval *= powf(10, exponent);
|
|
||||||
}
|
|
||||||
analog->encoding->digits = 4;
|
|
||||||
analog->spec->spec_digits = 4;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SR_PRIV const struct sr_dev_driver *sr_driver_list__start[]
|
SR_PRIV const struct sr_dev_driver *sr_driver_list__start[]
|
||||||
SR_DRIVER_LIST_NOREORDER
|
|
||||||
__attribute__((section (SR_DRIVER_LIST_SECTION),
|
__attribute__((section (SR_DRIVER_LIST_SECTION),
|
||||||
used, aligned(sizeof(struct sr_dev_driver *))))
|
used, aligned(sizeof(struct sr_dev_driver *))))
|
||||||
= { NULL /* Dummy item, as zero length arrays are not allowed by C99 */ };
|
= { NULL /* Dummy item, as zero length arrays are not allowed by C99 */ };
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SR_PRIV const struct sr_dev_driver *sr_driver_list__stop[]
|
SR_PRIV const struct sr_dev_driver *sr_driver_list__stop[]
|
||||||
SR_DRIVER_LIST_NOREORDER
|
|
||||||
__attribute__((section (SR_DRIVER_LIST_SECTION),
|
__attribute__((section (SR_DRIVER_LIST_SECTION),
|
||||||
used, aligned(sizeof(struct sr_dev_driver *))))
|
used, aligned(sizeof(struct sr_dev_driver *))))
|
||||||
= { NULL /* Dummy item, as zero length arrays are not allowed by C99 */ };
|
= { NULL /* Dummy item, as zero length arrays are not allowed by C99 */ };
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,10 @@
|
||||||
SR_PRIV extern const struct sr_dev_driver *sr_driver_list__start[];
|
SR_PRIV extern const struct sr_dev_driver *sr_driver_list__start[];
|
||||||
SR_PRIV extern const struct sr_dev_driver *sr_driver_list__stop[];
|
SR_PRIV extern const struct sr_dev_driver *sr_driver_list__stop[];
|
||||||
|
|
||||||
/**
|
/** @private
|
||||||
* Initialize the driver list in a fresh libsigrok context.
|
* Initialize the driver list in a fresh libsigrok context.
|
||||||
*
|
*
|
||||||
* @param ctx Pointer to a libsigrok context struct. Must not be NULL.
|
* @param ctx Pointer to a libsigrok context struct. Must not be NULL.
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
SR_API void sr_drivers_init(struct sr_context *ctx)
|
SR_API void sr_drivers_init(struct sr_context *ctx)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,6 @@ static const struct agdmm_profile supported_agdmm[] = {
|
||||||
{ AGILENT_U1271, "U1271A", 3, agdmm_jobs_live, NULL, agdmm_recvs_u127x },
|
{ AGILENT_U1271, "U1271A", 3, agdmm_jobs_live, NULL, agdmm_recvs_u127x },
|
||||||
{ AGILENT_U1272, "U1272A", 3, agdmm_jobs_live, NULL, agdmm_recvs_u127x },
|
{ AGILENT_U1272, "U1272A", 3, agdmm_jobs_live, NULL, agdmm_recvs_u127x },
|
||||||
{ AGILENT_U1273, "U1273A", 3, agdmm_jobs_live, NULL, agdmm_recvs_u127x },
|
{ AGILENT_U1273, "U1273A", 3, agdmm_jobs_live, NULL, agdmm_recvs_u127x },
|
||||||
{ AGILENT_U1273AX, "U1273AX", 3, agdmm_jobs_live, NULL, agdmm_recvs_u127x },
|
|
||||||
|
|
||||||
{ KEYSIGHT_U1281, "U1281A", 3, agdmm_jobs_live, agdmm_jobs_log, agdmm_recvs_u128x },
|
{ KEYSIGHT_U1281, "U1281A", 3, agdmm_jobs_live, agdmm_jobs_log, agdmm_recvs_u128x },
|
||||||
{ KEYSIGHT_U1282, "U1282A", 3, agdmm_jobs_live, agdmm_jobs_log, agdmm_recvs_u128x },
|
{ KEYSIGHT_U1282, "U1282A", 3, agdmm_jobs_live, agdmm_jobs_log, agdmm_recvs_u128x },
|
||||||
|
|
@ -131,6 +130,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
serial_flush(serial);
|
||||||
if (serial_write_blocking(serial, "*IDN?\r\n", 7, SERIAL_WRITE_TIMEOUT_MS) < 7) {
|
if (serial_write_blocking(serial, "*IDN?\r\n", 7, SERIAL_WRITE_TIMEOUT_MS) < 7) {
|
||||||
sr_err("Unable to send identification string.");
|
sr_err("Unable to send identification string.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
||||||
|
|
@ -1051,7 +1051,7 @@ SR_PRIV const struct agdmm_recv agdmm_recvs_u125x[] = {
|
||||||
|
|
||||||
SR_PRIV const struct agdmm_recv agdmm_recvs_u127x[] = {
|
SR_PRIV const struct agdmm_recv agdmm_recvs_u127x[] = {
|
||||||
{ "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u123x },
|
{ "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u123x },
|
||||||
{ "^\\*([0-9]+)$", recv_switch },
|
{ "^\\*([0-9])$", recv_switch },
|
||||||
{ "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
|
{ "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
|
||||||
{ "^\"(V|MV|A|MA|UA|FREQ),(\\d),(AC|DC|ACDC)\"$", recv_conf_u123x },
|
{ "^\"(V|MV|A|MA|UA|FREQ),(\\d),(AC|DC|ACDC)\"$", recv_conf_u123x },
|
||||||
{ "^\"(RES|CAP),(\\d)\"$", recv_conf_u123x},
|
{ "^\"(RES|CAP),(\\d)\"$", recv_conf_u123x},
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@ enum {
|
||||||
AGILENT_U1271,
|
AGILENT_U1271,
|
||||||
AGILENT_U1272,
|
AGILENT_U1272,
|
||||||
AGILENT_U1273,
|
AGILENT_U1273,
|
||||||
AGILENT_U1273AX,
|
|
||||||
|
|
||||||
KEYSIGHT_U1281,
|
KEYSIGHT_U1281,
|
||||||
KEYSIGHT_U1282,
|
KEYSIGHT_U1282,
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,11 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
|
|
||||||
sr_info("Probing serial port %s.", conn);
|
sr_info("Probing serial port %s.", conn);
|
||||||
|
|
||||||
|
serial_flush(serial);
|
||||||
|
|
||||||
/* Let's get a bit of data and see if we can find a packet. */
|
/* Let's get a bit of data and see if we can find a packet. */
|
||||||
if (serial_stream_detect(serial, buf, &len, 25,
|
if (serial_stream_detect(serial, buf, &len, 25,
|
||||||
appa_55ii_packet_valid, NULL, NULL, 500) != SR_OK)
|
appa_55ii_packet_valid, 500) != SR_OK)
|
||||||
goto scan_cleanup;
|
goto scan_cleanup;
|
||||||
|
|
||||||
sr_info("Found device on port %s.", conn);
|
sr_info("Found device on port %s.", conn);
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
serial_flush(serial);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First stop potentially running monitoring and wait for 50ms before
|
* First stop potentially running monitoring and wait for 50ms before
|
||||||
* next command can be sent.
|
* next command can be sent.
|
||||||
|
|
|
||||||
|
|
@ -336,10 +336,11 @@ static void handle_packet(const struct sr_dev_inst *sdi)
|
||||||
devc->voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
|
devc->voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
|
||||||
devc->current = g_ascii_strtod(tokens[1], NULL) / 1000;
|
devc->current = g_ascii_strtod(tokens[1], NULL) / 1000;
|
||||||
g_strfreev(tokens);
|
g_strfreev(tokens);
|
||||||
g_cond_signal(&devc->voltage_cond);
|
|
||||||
|
|
||||||
/* Begin frame. */
|
/* Begin frame. */
|
||||||
std_session_send_df_frame_begin(sdi);
|
packet.type = SR_DF_FRAME_BEGIN;
|
||||||
|
packet.payload = NULL;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
sr_analog_init(&analog, &encoding, &meaning, &spec, 4);
|
sr_analog_init(&analog, &encoding, &meaning, &spec, 4);
|
||||||
|
|
||||||
|
|
@ -372,7 +373,9 @@ static void handle_packet(const struct sr_dev_inst *sdi)
|
||||||
g_slist_free(l);
|
g_slist_free(l);
|
||||||
|
|
||||||
/* End frame. */
|
/* End frame. */
|
||||||
std_session_send_df_frame_end(sdi);
|
packet.type = SR_DF_FRAME_END;
|
||||||
|
packet.payload = NULL;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
* Copyright (C) 2010-2012 Håvard Espeland <gus@ping.uio.no>,
|
* Copyright (C) 2010-2012 Håvard Espeland <gus@ping.uio.no>,
|
||||||
* Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
|
* Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
|
||||||
* Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
|
* Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
|
||||||
* Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -24,19 +23,15 @@
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Channels are labelled 1-16, see this vendor's image of the cable:
|
* Channel numbers seem to go from 1-16, according to this image:
|
||||||
* http://tools.asix.net/img/sigma_sigmacab_pins_720.jpg (TI/TO are
|
* http://tools.asix.net/img/sigma_sigmacab_pins_720.jpg
|
||||||
* additional trigger in/out signals).
|
* (the cable has two additional GND pins, and a TI and TO pin)
|
||||||
*/
|
*/
|
||||||
static const char *channel_names[] = {
|
static const char *channel_names[] = {
|
||||||
"1", "2", "3", "4", "5", "6", "7", "8",
|
"1", "2", "3", "4", "5", "6", "7", "8",
|
||||||
"9", "10", "11", "12", "13", "14", "15", "16",
|
"9", "10", "11", "12", "13", "14", "15", "16",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t scanopts[] = {
|
|
||||||
SR_CONF_CONN,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t drvopts[] = {
|
static const uint32_t drvopts[] = {
|
||||||
SR_CONF_LOGIC_ANALYZER,
|
SR_CONF_LOGIC_ANALYZER,
|
||||||
};
|
};
|
||||||
|
|
@ -44,232 +39,112 @@ static const uint32_t drvopts[] = {
|
||||||
static const uint32_t devopts[] = {
|
static const uint32_t devopts[] = {
|
||||||
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
|
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
|
||||||
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
|
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_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||||
SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
|
#if ASIX_SIGMA_WITH_TRIGGER
|
||||||
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,
|
|
||||||
SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
|
SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
|
||||||
SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
|
SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
|
||||||
/* Consider SR_CONF_TRIGGER_PATTERN (SR_T_STRING, GET/SET) support. */
|
#endif
|
||||||
};
|
|
||||||
|
|
||||||
static const char *ext_clock_edges[] = {
|
|
||||||
[SIGMA_CLOCK_EDGE_RISING] = "rising",
|
|
||||||
[SIGMA_CLOCK_EDGE_FALLING] = "falling",
|
|
||||||
[SIGMA_CLOCK_EDGE_EITHER] = "either",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if ASIX_SIGMA_WITH_TRIGGER
|
||||||
static const int32_t trigger_matches[] = {
|
static const int32_t trigger_matches[] = {
|
||||||
SR_TRIGGER_ZERO,
|
SR_TRIGGER_ZERO,
|
||||||
SR_TRIGGER_ONE,
|
SR_TRIGGER_ONE,
|
||||||
SR_TRIGGER_RISING,
|
SR_TRIGGER_RISING,
|
||||||
SR_TRIGGER_FALLING,
|
SR_TRIGGER_FALLING,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
static void clear_helper(struct dev_context *devc)
|
static void clear_helper(struct dev_context *devc)
|
||||||
{
|
{
|
||||||
(void)sigma_force_close(devc);
|
ftdi_deinit(&devc->ftdic);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dev_clear(const struct sr_dev_driver *di)
|
static int dev_clear(const struct sr_dev_driver *di)
|
||||||
{
|
{
|
||||||
return std_dev_clear_with_callback(di,
|
return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
|
||||||
(std_dev_clear_callback)clear_helper);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean bus_addr_in_devices(int bus, int addr, GSList *devs)
|
|
||||||
{
|
|
||||||
struct sr_usb_dev_inst *usb;
|
|
||||||
|
|
||||||
for (/* EMPTY */; devs; devs = devs->next) {
|
|
||||||
usb = devs->data;
|
|
||||||
if (usb->bus == bus && usb->address == addr)
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean known_vid_pid(const struct libusb_device_descriptor *des)
|
|
||||||
{
|
|
||||||
gboolean is_sigma, is_omega;
|
|
||||||
|
|
||||||
if (des->idVendor != USB_VENDOR_ASIX)
|
|
||||||
return FALSE;
|
|
||||||
is_sigma = des->idProduct == USB_PRODUCT_SIGMA;
|
|
||||||
is_omega = des->idProduct == USB_PRODUCT_OMEGA;
|
|
||||||
if (!is_sigma && !is_omega)
|
|
||||||
return FALSE;
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
{
|
{
|
||||||
struct drv_context *drvc;
|
|
||||||
libusb_context *usbctx;
|
|
||||||
const char *conn;
|
|
||||||
GSList *l, *conn_devices;
|
|
||||||
struct sr_config *src;
|
|
||||||
GSList *devices;
|
|
||||||
libusb_device **devlist, *devitem;
|
|
||||||
int bus, addr;
|
|
||||||
struct libusb_device_descriptor des;
|
|
||||||
struct libusb_device_handle *hdl;
|
|
||||||
int ret;
|
|
||||||
char conn_id[20];
|
|
||||||
char serno_txt[16];
|
|
||||||
char *end;
|
|
||||||
unsigned long serno_num, serno_pre;
|
|
||||||
enum asix_device_type dev_type;
|
|
||||||
const char *dev_text;
|
|
||||||
struct sr_dev_inst *sdi;
|
struct sr_dev_inst *sdi;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
size_t devidx, chidx;
|
struct ftdi_device_list *devlist;
|
||||||
|
char serial_txt[10];
|
||||||
|
uint32_t serial;
|
||||||
|
int ret;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
drvc = di->context;
|
(void)options;
|
||||||
usbctx = drvc->sr_ctx->libusb_ctx;
|
|
||||||
|
|
||||||
/* Find all devices which match an (optional) conn= spec. */
|
devc = g_malloc0(sizeof(struct dev_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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn_devices = NULL;
|
|
||||||
if (conn)
|
|
||||||
conn_devices = sr_usb_find(usbctx, conn);
|
|
||||||
if (conn && !conn_devices)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Find all ASIX logic analyzers (which match the connection spec). */
|
ftdi_init(&devc->ftdic);
|
||||||
devices = NULL;
|
|
||||||
libusb_get_device_list(usbctx, &devlist);
|
|
||||||
for (devidx = 0; devlist[devidx]; devidx++) {
|
|
||||||
devitem = devlist[devidx];
|
|
||||||
|
|
||||||
/* Check for connection match if a user spec was given. */
|
if ((ret = ftdi_usb_find_all(&devc->ftdic, &devlist,
|
||||||
bus = libusb_get_bus_number(devitem);
|
USB_VENDOR, USB_PRODUCT)) <= 0) {
|
||||||
addr = libusb_get_device_address(devitem);
|
if (ret < 0)
|
||||||
if (conn && !bus_addr_in_devices(bus, addr, conn_devices))
|
sr_err("ftdi_usb_find_all(): %d", ret);
|
||||||
continue;
|
goto free;
|
||||||
snprintf(conn_id, sizeof(conn_id), "%d.%d", bus, addr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for known VID:PID pairs. Get the serial number,
|
|
||||||
* to then derive the device type from it.
|
|
||||||
*/
|
|
||||||
libusb_get_device_descriptor(devitem, &des);
|
|
||||||
if (!known_vid_pid(&des))
|
|
||||||
continue;
|
|
||||||
if (!des.iSerialNumber) {
|
|
||||||
sr_warn("Cannot get serial number (index 0).");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ret = libusb_open(devitem, &hdl);
|
|
||||||
if (ret < 0) {
|
|
||||||
sr_warn("Cannot open USB device %04x.%04x: %s.",
|
|
||||||
des.idVendor, des.idProduct,
|
|
||||||
libusb_error_name(ret));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ret = libusb_get_string_descriptor_ascii(hdl,
|
|
||||||
des.iSerialNumber,
|
|
||||||
(unsigned char *)serno_txt, sizeof(serno_txt));
|
|
||||||
if (ret < 0) {
|
|
||||||
sr_warn("Cannot get serial number (%s).",
|
|
||||||
libusb_error_name(ret));
|
|
||||||
libusb_close(hdl);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
libusb_close(hdl);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All ASIX logic analyzers have a serial number, which
|
|
||||||
* reads as a hex number, and tells the device type.
|
|
||||||
*/
|
|
||||||
ret = sr_atoul_base(serno_txt, &serno_num, &end, 16);
|
|
||||||
if (ret != SR_OK || !end || *end) {
|
|
||||||
sr_warn("Cannot interpret serial number %s.", serno_txt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
dev_type = ASIX_TYPE_NONE;
|
|
||||||
dev_text = NULL;
|
|
||||||
serno_pre = serno_num >> 16;
|
|
||||||
switch (serno_pre) {
|
|
||||||
case 0xa601:
|
|
||||||
dev_type = ASIX_TYPE_SIGMA;
|
|
||||||
dev_text = "SIGMA";
|
|
||||||
sr_info("Found SIGMA, serno %s.", serno_txt);
|
|
||||||
break;
|
|
||||||
case 0xa602:
|
|
||||||
dev_type = ASIX_TYPE_SIGMA;
|
|
||||||
dev_text = "SIGMA2";
|
|
||||||
sr_info("Found SIGMA2, serno %s.", serno_txt);
|
|
||||||
break;
|
|
||||||
case 0xa603:
|
|
||||||
dev_type = ASIX_TYPE_OMEGA;
|
|
||||||
dev_text = "OMEGA";
|
|
||||||
sr_info("Found OMEGA, serno %s.", serno_txt);
|
|
||||||
if (!ASIX_WITH_OMEGA) {
|
|
||||||
sr_warn("OMEGA support is not implemented yet.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sr_warn("Unknown serno %s, skipping.", serno_txt);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a device instance, add it to the result set. */
|
/* Make sure it's a version 1 or 2 SIGMA. */
|
||||||
|
ftdi_usb_get_strings(&devc->ftdic, devlist->dev, NULL, 0, NULL, 0,
|
||||||
|
serial_txt, sizeof(serial_txt));
|
||||||
|
sscanf(serial_txt, "%x", &serial);
|
||||||
|
|
||||||
sdi = g_malloc0(sizeof(*sdi));
|
if (serial < 0xa6010000 || serial > 0xa602ffff) {
|
||||||
devices = g_slist_append(devices, sdi);
|
sr_err("Only SIGMA and SIGMA2 are supported "
|
||||||
|
"in this version of libsigrok.");
|
||||||
|
goto free;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_info("Found ASIX SIGMA - Serial: %s", serial_txt);
|
||||||
|
|
||||||
|
devc->cur_samplerate = samplerates[0];
|
||||||
|
devc->limit_msec = 0;
|
||||||
|
devc->limit_samples = 0;
|
||||||
|
devc->cur_firmware = -1;
|
||||||
|
devc->num_channels = 0;
|
||||||
|
devc->samples_per_event = 0;
|
||||||
|
devc->capture_ratio = 50;
|
||||||
|
devc->use_triggers = 0;
|
||||||
|
|
||||||
|
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
||||||
sdi->status = SR_ST_INITIALIZING;
|
sdi->status = SR_ST_INITIALIZING;
|
||||||
sdi->vendor = g_strdup("ASIX");
|
sdi->vendor = g_strdup("ASIX");
|
||||||
sdi->model = g_strdup(dev_text);
|
sdi->model = g_strdup("SIGMA");
|
||||||
sdi->serial_num = g_strdup(serno_txt);
|
|
||||||
sdi->connection_id = g_strdup(conn_id);
|
for (i = 0; i < ARRAY_SIZE(channel_names); i++)
|
||||||
for (chidx = 0; chidx < ARRAY_SIZE(channel_names); chidx++)
|
sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_names[i]);
|
||||||
sr_channel_new(sdi, chidx, SR_CHANNEL_LOGIC,
|
|
||||||
TRUE, channel_names[chidx]);
|
|
||||||
|
|
||||||
devc = g_malloc0(sizeof(*devc));
|
|
||||||
sdi->priv = devc;
|
sdi->priv = devc;
|
||||||
devc->id.vid = des.idVendor;
|
|
||||||
devc->id.pid = des.idProduct;
|
|
||||||
devc->id.serno = serno_num;
|
|
||||||
devc->id.prefix = serno_pre;
|
|
||||||
devc->id.type = dev_type;
|
|
||||||
sr_sw_limits_init(&devc->limit.config);
|
|
||||||
devc->capture_ratio = 50;
|
|
||||||
devc->use_triggers = FALSE;
|
|
||||||
|
|
||||||
/* Get current hardware configuration (or use defaults). */
|
ftdi_list_free(&devlist);
|
||||||
(void)sigma_fetch_hw_config(sdi);
|
|
||||||
}
|
|
||||||
libusb_free_device_list(devlist, 1);
|
|
||||||
g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
|
|
||||||
|
|
||||||
return std_scan_complete(di, devices);
|
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
||||||
|
|
||||||
|
free:
|
||||||
|
ftdi_deinit(&devc->ftdic);
|
||||||
|
g_free(devc);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dev_open(struct sr_dev_inst *sdi)
|
static int dev_open(struct sr_dev_inst *sdi)
|
||||||
{
|
{
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
if (devc->id.type == ASIX_TYPE_OMEGA && !ASIX_WITH_OMEGA) {
|
if ((ret = ftdi_usb_open_desc(&devc->ftdic,
|
||||||
sr_err("OMEGA support is not implemented yet.");
|
USB_VENDOR, USB_PRODUCT, USB_DESCRIPTION, NULL)) < 0) {
|
||||||
return SR_ERR_NA;
|
sr_err("Failed to open device (%d): %s.",
|
||||||
|
ret, ftdi_get_error_string(&devc->ftdic));
|
||||||
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sigma_force_open(sdi);
|
return SR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dev_close(struct sr_dev_inst *sdi)
|
static int dev_close(struct sr_dev_inst *sdi)
|
||||||
|
|
@ -278,14 +153,13 @@ static int dev_close(struct sr_dev_inst *sdi)
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
return sigma_force_close(devc);
|
return (ftdi_usb_close(&devc->ftdic) == 0) ? SR_OK : SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int config_get(uint32_t key, GVariant **data,
|
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;
|
struct dev_context *devc;
|
||||||
const char *clock_text;
|
|
||||||
|
|
||||||
(void)cg;
|
(void)cg;
|
||||||
|
|
||||||
|
|
@ -294,29 +168,20 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_CONN:
|
|
||||||
*data = g_variant_new_string(sdi->connection_id);
|
|
||||||
break;
|
|
||||||
case SR_CONF_SAMPLERATE:
|
case SR_CONF_SAMPLERATE:
|
||||||
*data = g_variant_new_uint64(devc->clock.samplerate);
|
*data = g_variant_new_uint64(devc->cur_samplerate);
|
||||||
break;
|
|
||||||
case SR_CONF_EXTERNAL_CLOCK:
|
|
||||||
*data = g_variant_new_boolean(devc->clock.use_ext_clock);
|
|
||||||
break;
|
|
||||||
case SR_CONF_EXTERNAL_CLOCK_SOURCE:
|
|
||||||
clock_text = channel_names[devc->clock.clock_pin];
|
|
||||||
*data = g_variant_new_string(clock_text);
|
|
||||||
break;
|
|
||||||
case SR_CONF_CLOCK_EDGE:
|
|
||||||
clock_text = ext_clock_edges[devc->clock.clock_edge];
|
|
||||||
*data = g_variant_new_string(clock_text);
|
|
||||||
break;
|
break;
|
||||||
case SR_CONF_LIMIT_MSEC:
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
*data = g_variant_new_uint64(devc->limit_msec);
|
||||||
|
break;
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
return sr_sw_limits_config_get(&devc->limit.config, key, data);
|
*data = g_variant_new_uint64(devc->limit_samples);
|
||||||
|
break;
|
||||||
|
#if ASIX_SIGMA_WITH_TRIGGER
|
||||||
case SR_CONF_CAPTURE_RATIO:
|
case SR_CONF_CAPTURE_RATIO:
|
||||||
*data = g_variant_new_uint64(devc->capture_ratio);
|
*data = g_variant_new_uint64(devc->capture_ratio);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
}
|
}
|
||||||
|
|
@ -328,9 +193,6 @@ 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;
|
struct dev_context *devc;
|
||||||
int ret;
|
|
||||||
uint64_t want_rate, have_rate;
|
|
||||||
int idx;
|
|
||||||
|
|
||||||
(void)cg;
|
(void)cg;
|
||||||
|
|
||||||
|
|
@ -338,42 +200,20 @@ static int config_set(uint32_t key, GVariant *data,
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_SAMPLERATE:
|
case SR_CONF_SAMPLERATE:
|
||||||
want_rate = g_variant_get_uint64(data);
|
return sigma_set_samplerate(sdi, g_variant_get_uint64(data));
|
||||||
ret = sigma_normalize_samplerate(want_rate, &have_rate);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
if (have_rate != want_rate) {
|
|
||||||
char *text_want, *text_have;
|
|
||||||
text_want = sr_samplerate_string(want_rate);
|
|
||||||
text_have = sr_samplerate_string(have_rate);
|
|
||||||
sr_info("Adjusted samplerate %s to %s.",
|
|
||||||
text_want, text_have);
|
|
||||||
g_free(text_want);
|
|
||||||
g_free(text_have);
|
|
||||||
}
|
|
||||||
devc->clock.samplerate = have_rate;
|
|
||||||
break;
|
|
||||||
case SR_CONF_EXTERNAL_CLOCK:
|
|
||||||
devc->clock.use_ext_clock = g_variant_get_boolean(data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_EXTERNAL_CLOCK_SOURCE:
|
|
||||||
idx = std_str_idx(data, ARRAY_AND_SIZE(channel_names));
|
|
||||||
if (idx < 0)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
devc->clock.clock_pin = idx;
|
|
||||||
break;
|
|
||||||
case SR_CONF_CLOCK_EDGE:
|
|
||||||
idx = std_str_idx(data, ARRAY_AND_SIZE(ext_clock_edges));
|
|
||||||
if (idx < 0)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
devc->clock.clock_edge = idx;
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_MSEC:
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
devc->limit_msec = g_variant_get_uint64(data);
|
||||||
|
break;
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
return sr_sw_limits_config_set(&devc->limit.config, key, data);
|
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:
|
case SR_CONF_CAPTURE_RATIO:
|
||||||
devc->capture_ratio = g_variant_get_uint64(data);
|
devc->capture_ratio = g_variant_get_uint64(data);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
}
|
}
|
||||||
|
|
@ -385,24 +225,16 @@ 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)
|
||||||
{
|
{
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_SCAN_OPTIONS:
|
|
||||||
case SR_CONF_DEVICE_OPTIONS:
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
if (cg)
|
return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
|
||||||
return SR_ERR_NA;
|
|
||||||
return STD_CONFIG_LIST(key, data, sdi, cg,
|
|
||||||
scanopts, drvopts, devopts);
|
|
||||||
case SR_CONF_SAMPLERATE:
|
case SR_CONF_SAMPLERATE:
|
||||||
*data = sigma_get_samplerates_list();
|
*data = std_gvar_samplerates(samplerates, samplerates_count);
|
||||||
break;
|
|
||||||
case SR_CONF_EXTERNAL_CLOCK_SOURCE:
|
|
||||||
*data = g_variant_new_strv(ARRAY_AND_SIZE(channel_names));
|
|
||||||
break;
|
|
||||||
case SR_CONF_CLOCK_EDGE:
|
|
||||||
*data = g_variant_new_strv(ARRAY_AND_SIZE(ext_clock_edges));
|
|
||||||
break;
|
break;
|
||||||
|
#if ASIX_SIGMA_WITH_TRIGGER
|
||||||
case SR_CONF_TRIGGER_MATCH:
|
case SR_CONF_TRIGGER_MATCH:
|
||||||
*data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
|
*data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
}
|
}
|
||||||
|
|
@ -413,194 +245,115 @@ static int config_list(uint32_t key, GVariant **data,
|
||||||
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
||||||
{
|
{
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
uint16_t pindis_mask;
|
struct clockselect_50 clockselect;
|
||||||
uint8_t async, div;
|
int triggerpin, ret;
|
||||||
int ret;
|
uint8_t triggerselect;
|
||||||
size_t triggerpin;
|
|
||||||
uint8_t trigsel2;
|
|
||||||
struct triggerinout triggerinout_conf;
|
struct triggerinout triggerinout_conf;
|
||||||
struct triggerlut lut;
|
struct triggerlut lut;
|
||||||
uint8_t regval, cmd_bytes[4], *wrptr;
|
uint8_t regval;
|
||||||
|
uint8_t clock_bytes[sizeof(clockselect)];
|
||||||
|
size_t clock_idx;
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
/* Convert caller's trigger spec to driver's internal format. */
|
if (sigma_convert_trigger(sdi) != SR_OK) {
|
||||||
ret = sigma_convert_trigger(sdi);
|
sr_err("Failed to configure triggers.");
|
||||||
if (ret != SR_OK) {
|
return SR_ERR;
|
||||||
sr_err("Could not configure triggers.");
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* If the samplerate has not been set, default to 200 kHz. */
|
||||||
* Setup the device's samplerate from the value which up to now
|
if (devc->cur_firmware == -1) {
|
||||||
* just got checked and stored. As a byproduct this can pick and
|
if ((ret = sigma_set_samplerate(sdi, SR_KHZ(200))) != SR_OK)
|
||||||
* send firmware to the device, reduce the number of available
|
return ret;
|
||||||
* logic channels, etc.
|
|
||||||
*
|
|
||||||
* Determine an acquisition timeout from optionally configured
|
|
||||||
* sample count or time limits. Which depends on the samplerate.
|
|
||||||
* Force 50MHz samplerate when external clock is in use.
|
|
||||||
*/
|
|
||||||
if (devc->clock.use_ext_clock) {
|
|
||||||
if (devc->clock.samplerate != SR_MHZ(50))
|
|
||||||
sr_info("External clock, forcing 50MHz samplerate.");
|
|
||||||
devc->clock.samplerate = SR_MHZ(50);
|
|
||||||
}
|
}
|
||||||
ret = sigma_set_samplerate(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
ret = sigma_set_acquire_timeout(devc);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Enter trigger programming mode. */
|
/* Enter trigger programming mode. */
|
||||||
trigsel2 = TRGSEL2_RESET;
|
sigma_set_register(WRITE_TRIGGER_SELECT1, 0x20, devc);
|
||||||
ret = sigma_set_register(devc, WRITE_TRIGGER_SELECT2, trigsel2);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
trigsel2 = 0;
|
triggerselect = 0;
|
||||||
if (devc->clock.samplerate >= SR_MHZ(100)) {
|
if (devc->cur_samplerate >= SR_MHZ(100)) {
|
||||||
/* 100 and 200 MHz mode. */
|
/* 100 and 200 MHz mode. */
|
||||||
/* TODO Decipher the 0x81 magic number's purpose. */
|
sigma_set_register(WRITE_TRIGGER_SELECT1, 0x81, devc);
|
||||||
ret = sigma_set_register(devc, WRITE_TRIGGER_SELECT2, 0x81);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Find which pin to trigger on from mask. */
|
/* Find which pin to trigger on from mask. */
|
||||||
for (triggerpin = 0; triggerpin < 8; triggerpin++) {
|
for (triggerpin = 0; triggerpin < 8; triggerpin++)
|
||||||
if (devc->trigger.risingmask & BIT(triggerpin))
|
if ((devc->trigger.risingmask | devc->trigger.fallingmask) &
|
||||||
|
(1 << triggerpin))
|
||||||
break;
|
break;
|
||||||
if (devc->trigger.fallingmask & BIT(triggerpin))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set trigger pin and light LED on trigger. */
|
/* Set trigger pin and light LED on trigger. */
|
||||||
trigsel2 = triggerpin & TRGSEL2_PINS_MASK;
|
triggerselect = (1 << LEDSEL1) | (triggerpin & 0x7);
|
||||||
trigsel2 |= TRGSEL2_LEDSEL1;
|
|
||||||
|
|
||||||
/* Default rising edge. */
|
/* Default rising edge. */
|
||||||
/* TODO Documentation disagrees, bit set means _rising_ edge. */
|
|
||||||
if (devc->trigger.fallingmask)
|
if (devc->trigger.fallingmask)
|
||||||
trigsel2 |= TRGSEL2_PINPOL_RISE;
|
triggerselect |= 1 << 3;
|
||||||
|
|
||||||
} else if (devc->clock.samplerate <= SR_MHZ(50)) {
|
} else if (devc->cur_samplerate <= SR_MHZ(50)) {
|
||||||
/* 50MHz firmware modes. */
|
/* All other modes. */
|
||||||
|
sigma_build_basic_trigger(&lut, devc);
|
||||||
|
|
||||||
/* Translate application specs to hardware perspective. */
|
sigma_write_trigger_lut(&lut, devc);
|
||||||
ret = sigma_build_basic_trigger(devc, &lut);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Communicate resulting register values to the device. */
|
triggerselect = (1 << LEDSEL1) | (1 << LEDSEL0);
|
||||||
ret = sigma_write_trigger_lut(devc, &lut);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
trigsel2 = TRGSEL2_LEDSEL1 | TRGSEL2_LEDSEL0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup trigger in and out pins to default values. */
|
/* Setup trigger in and out pins to default values. */
|
||||||
memset(&triggerinout_conf, 0, sizeof(triggerinout_conf));
|
memset(&triggerinout_conf, 0, sizeof(struct triggerinout));
|
||||||
triggerinout_conf.trgout_bytrigger = TRUE;
|
triggerinout_conf.trgout_bytrigger = 1;
|
||||||
triggerinout_conf.trgout_enable = TRUE;
|
triggerinout_conf.trgout_enable = 1;
|
||||||
/* TODO
|
|
||||||
* Verify the correctness of this implementation. The previous
|
|
||||||
* version used to assign to a C language struct with bit fields
|
|
||||||
* which is highly non-portable and hard to guess the resulting
|
|
||||||
* raw memory layout or wire transfer content. The C struct's
|
|
||||||
* field names did not match the vendor documentation's names.
|
|
||||||
* Which means that I could not verify "on paper" either. Let's
|
|
||||||
* re-visit this code later during research for trigger support.
|
|
||||||
*/
|
|
||||||
wrptr = cmd_bytes;
|
|
||||||
regval = 0;
|
|
||||||
if (triggerinout_conf.trgout_bytrigger)
|
|
||||||
regval |= TRGOPT_TRGOOUTEN;
|
|
||||||
write_u8_inc(&wrptr, regval);
|
|
||||||
regval &= ~TRGOPT_CLEAR_MASK;
|
|
||||||
if (triggerinout_conf.trgout_enable)
|
|
||||||
regval |= TRGOPT_TRGOEN;
|
|
||||||
write_u8_inc(&wrptr, regval);
|
|
||||||
ret = sigma_write_register(devc, WRITE_TRIGGER_OPTION,
|
|
||||||
cmd_bytes, wrptr - cmd_bytes);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Leave trigger programming mode. */
|
sigma_write_register(WRITE_TRIGGER_OPTION,
|
||||||
ret = sigma_set_register(devc, WRITE_TRIGGER_SELECT2, trigsel2);
|
(uint8_t *) &triggerinout_conf,
|
||||||
if (ret != SR_OK)
|
sizeof(struct triggerinout), devc);
|
||||||
return ret;
|
|
||||||
|
|
||||||
/*
|
/* Go back to normal mode. */
|
||||||
* Samplerate dependent clock and channels configuration. Some
|
sigma_set_register(WRITE_TRIGGER_SELECT1, triggerselect, devc);
|
||||||
* channels by design are not available at higher clock rates.
|
|
||||||
* Register layout differs between firmware variants (depth 1
|
/* Set clock select register. */
|
||||||
* with LSB channel mask above 50MHz, depth 4 with more details
|
clockselect.async = 0;
|
||||||
* up to 50MHz).
|
clockselect.fraction = 1 - 1; /* Divider 1. */
|
||||||
*
|
clockselect.disabled_channels = 0x0000; /* All channels enabled. */
|
||||||
* Derive a mask where bits are set for unavailable channels.
|
if (devc->cur_samplerate == SR_MHZ(200)) {
|
||||||
* Either send the single byte, or the full byte sequence.
|
/* Enable 4 channels. */
|
||||||
*/
|
clockselect.disabled_channels = 0xf0ff;
|
||||||
pindis_mask = ~BITS_MASK(devc->interp.num_channels);
|
} else if (devc->cur_samplerate == SR_MHZ(100)) {
|
||||||
if (devc->clock.samplerate > SR_MHZ(50)) {
|
/* Enable 8 channels. */
|
||||||
ret = sigma_set_register(devc, WRITE_CLOCK_SELECT,
|
clockselect.disabled_channels = 0x00ff;
|
||||||
pindis_mask & 0xff);
|
|
||||||
} else {
|
} else {
|
||||||
wrptr = cmd_bytes;
|
/*
|
||||||
/* Select 50MHz base clock, and divider. */
|
* 50 MHz mode, or fraction thereof. The 50MHz reference
|
||||||
async = 0;
|
* can get divided by any integer in the range 1 to 256.
|
||||||
div = SR_MHZ(50) / devc->clock.samplerate - 1;
|
* Divider minus 1 gets written to the hardware.
|
||||||
if (devc->clock.use_ext_clock) {
|
* (The driver lists a discrete set of sample rates, but
|
||||||
async = CLKSEL_CLKSEL8;
|
* all of them fit the above description.)
|
||||||
div = devc->clock.clock_pin + 1;
|
*/
|
||||||
switch (devc->clock.clock_edge) {
|
clockselect.fraction = SR_MHZ(50) / devc->cur_samplerate - 1;
|
||||||
case SIGMA_CLOCK_EDGE_RISING:
|
|
||||||
div |= CLKSEL_RISING;
|
|
||||||
break;
|
|
||||||
case SIGMA_CLOCK_EDGE_FALLING:
|
|
||||||
div |= CLKSEL_FALLING;
|
|
||||||
break;
|
|
||||||
case SIGMA_CLOCK_EDGE_EITHER:
|
|
||||||
div |= CLKSEL_RISING;
|
|
||||||
div |= CLKSEL_FALLING;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
clock_idx = 0;
|
||||||
write_u8_inc(&wrptr, async);
|
clock_bytes[clock_idx++] = clockselect.async;
|
||||||
write_u8_inc(&wrptr, div);
|
clock_bytes[clock_idx++] = clockselect.fraction;
|
||||||
write_u16be_inc(&wrptr, pindis_mask);
|
clock_bytes[clock_idx++] = clockselect.disabled_channels & 0xff;
|
||||||
ret = sigma_write_register(devc, WRITE_CLOCK_SELECT,
|
clock_bytes[clock_idx++] = clockselect.disabled_channels >> 8;
|
||||||
cmd_bytes, wrptr - cmd_bytes);
|
sigma_write_register(WRITE_CLOCK_SELECT, clock_bytes, clock_idx, devc);
|
||||||
}
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Setup maximum post trigger time. */
|
/* Setup maximum post trigger time. */
|
||||||
ret = sigma_set_register(devc, WRITE_POST_TRIGGER,
|
sigma_set_register(WRITE_POST_TRIGGER,
|
||||||
(devc->capture_ratio * 255) / 100);
|
(devc->capture_ratio * 255) / 100, devc);
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Start acqusition. */
|
/* Start acqusition. */
|
||||||
|
devc->start_time = g_get_monotonic_time();
|
||||||
regval = WMR_TRGRES | WMR_SDRAMWRITEEN;
|
regval = WMR_TRGRES | WMR_SDRAMWRITEEN;
|
||||||
if (devc->use_triggers)
|
#if ASIX_SIGMA_WITH_TRIGGER
|
||||||
regval |= WMR_TRGEN;
|
regval |= WMR_TRGEN;
|
||||||
ret = sigma_set_register(devc, WRITE_MODE, regval);
|
#endif
|
||||||
if (ret != SR_OK)
|
sigma_set_register(WRITE_MODE, regval, devc);
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = std_session_send_df_header(sdi);
|
std_session_send_df_header(sdi);
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Add capture source. */
|
/* Add capture source. */
|
||||||
ret = sr_session_source_add(sdi->session, -1, 0, 10,
|
sr_session_source_add(sdi->session, -1, 0, 10, sigma_receive_data, (void *)sdi);
|
||||||
sigma_receive_data, (void *)sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
devc->state = SIGMA_CAPTURE;
|
devc->state.state = SIGMA_CAPTURE;
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -618,11 +371,11 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
|
||||||
* already. The detour is required to have sample data retrieved
|
* already. The detour is required to have sample data retrieved
|
||||||
* for forced acquisition stops.
|
* for forced acquisition stops.
|
||||||
*/
|
*/
|
||||||
if (devc->state == SIGMA_CAPTURE) {
|
if (devc->state.state == SIGMA_CAPTURE) {
|
||||||
devc->state = SIGMA_STOPPING;
|
devc->state.state = SIGMA_STOPPING;
|
||||||
} else {
|
} else {
|
||||||
devc->state = SIGMA_IDLE;
|
devc->state.state = SIGMA_IDLE;
|
||||||
(void)sr_session_source_remove(sdi->session, -1);
|
sr_session_source_remove(sdi->session, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,7 +4,6 @@
|
||||||
* Copyright (C) 2010-2012 Håvard Espeland <gus@ping.uio.no>,
|
* Copyright (C) 2010-2012 Håvard Espeland <gus@ping.uio.no>,
|
||||||
* Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
|
* Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
|
||||||
* Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
|
* Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
|
||||||
* Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -33,84 +32,29 @@
|
||||||
|
|
||||||
#define LOG_PREFIX "asix-sigma"
|
#define LOG_PREFIX "asix-sigma"
|
||||||
|
|
||||||
/* Experimental support for OMEGA (scan only, operation is ENOIMPL). */
|
|
||||||
#define ASIX_WITH_OMEGA 0
|
|
||||||
|
|
||||||
#define USB_VENDOR_ASIX 0xa600
|
|
||||||
#define USB_PRODUCT_SIGMA 0xa000
|
|
||||||
#define USB_PRODUCT_OMEGA 0xa004
|
|
||||||
|
|
||||||
enum asix_device_type {
|
|
||||||
ASIX_TYPE_NONE,
|
|
||||||
ASIX_TYPE_SIGMA,
|
|
||||||
ASIX_TYPE_OMEGA,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Mask to isolate one bit, mask to span a number of bits. */
|
|
||||||
#define BIT(pos) (1UL << (pos))
|
|
||||||
#define BITS_MASK(count) ((1UL << (count)) - 1)
|
|
||||||
|
|
||||||
#define HI4(b) (((b) >> 4) & 0x0f)
|
|
||||||
#define LO4(b) (((b) >> 0) & 0x0f)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FPGA commands are 8bits wide. The upper nibble is a command opcode,
|
* Triggers are not working in this implementation. Stop claiming
|
||||||
* the lower nibble can carry operand values. 8bit register addresses
|
* support for the feature which effectively is not available, until
|
||||||
* and 8bit data values get communicated in two steps.
|
* the implementation got fixed. Yet keep the code in place and allow
|
||||||
|
* developers to turn on this switch during development.
|
||||||
*/
|
*/
|
||||||
|
#define ASIX_SIGMA_WITH_TRIGGER 0
|
||||||
|
|
||||||
/* Register access. */
|
#define USB_VENDOR 0xa600
|
||||||
#define REG_ADDR_LOW (0x0 << 4)
|
#define USB_PRODUCT 0xa000
|
||||||
#define REG_ADDR_HIGH (0x1 << 4)
|
#define USB_DESCRIPTION "ASIX SIGMA"
|
||||||
#define REG_DATA_LOW (0x2 << 4)
|
|
||||||
#define REG_DATA_HIGH_WRITE (0x3 << 4)
|
|
||||||
#define REG_READ_ADDR (0x4 << 4)
|
|
||||||
#define REG_ADDR_ADJUST BIT(0) /* Auto adjust register address. */
|
|
||||||
#define REG_ADDR_DOWN BIT(1) /* 1 decrement, 0 increment. */
|
|
||||||
#define REG_ADDR_INC (REG_ADDR_ADJUST)
|
|
||||||
#define REG_ADDR_DEC (REG_ADDR_ADJUST | REG_ADDR_DOWN)
|
|
||||||
|
|
||||||
/* Sample memory access. */
|
|
||||||
#define REG_DRAM_WAIT_ACK (0x5 << 4) /* Wait for completion. */
|
|
||||||
#define REG_DRAM_BLOCK (0x6 << 4) /* DRAM to BRAM, plus bank select. */
|
|
||||||
#define REG_DRAM_BLOCK_BEGIN (0x8 << 4) /* Read first BRAM bytes. */
|
|
||||||
#define REG_DRAM_BLOCK_DATA (0xa << 4) /* Read full BRAM block. */
|
|
||||||
#define REG_DRAM_SEL_N (0x1 << 4) /* Bank select, added to 6/8/a. */
|
|
||||||
#define REG_DRAM_SEL_BOOL(b) ((b) ? REG_DRAM_SEL_N : 0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Registers at a specific address can have different meanings depending
|
|
||||||
* on whether data is read or written. This is why direction is part of
|
|
||||||
* the programming language identifiers.
|
|
||||||
*
|
|
||||||
* The vendor documentation suggests that in addition to the first 16
|
|
||||||
* register addresses which implement the logic analyzer's feature set,
|
|
||||||
* there are 240 more registers in the 16 to 255 address range which
|
|
||||||
* are available to applications and plugin features. Can libsigrok's
|
|
||||||
* asix-sigma driver store configuration data there, to avoid expensive
|
|
||||||
* operations (think: firmware re-load).
|
|
||||||
*
|
|
||||||
* Update: The documentation may be incorrect, or the FPGA netlist may
|
|
||||||
* be incomplete. Experiments show that registers beyond 0x0f can get
|
|
||||||
* accessed, USB communication passes, but data bytes are always 0xff.
|
|
||||||
* Are several firmware versions around, and the documentation does not
|
|
||||||
* match the one that ships with sigrok?
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum sigma_write_register {
|
enum sigma_write_register {
|
||||||
WRITE_CLOCK_SELECT = 0,
|
WRITE_CLOCK_SELECT = 0,
|
||||||
WRITE_TRIGGER_SELECT = 1,
|
WRITE_TRIGGER_SELECT0 = 1,
|
||||||
WRITE_TRIGGER_SELECT2 = 2,
|
WRITE_TRIGGER_SELECT1 = 2,
|
||||||
WRITE_MODE = 3,
|
WRITE_MODE = 3,
|
||||||
WRITE_MEMROW = 4,
|
WRITE_MEMROW = 4,
|
||||||
WRITE_POST_TRIGGER = 5,
|
WRITE_POST_TRIGGER = 5,
|
||||||
WRITE_TRIGGER_OPTION = 6,
|
WRITE_TRIGGER_OPTION = 6,
|
||||||
WRITE_PIN_VIEW = 7,
|
WRITE_PIN_VIEW = 7,
|
||||||
/* Unassigned register locations. */
|
|
||||||
WRITE_TEST = 15,
|
WRITE_TEST = 15,
|
||||||
/* Reserved for plugin features. */
|
|
||||||
REG_PLUGIN_START = 16,
|
|
||||||
REG_PLUGIN_STOP = 256,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sigma_read_register {
|
enum sigma_read_register {
|
||||||
|
|
@ -126,97 +70,58 @@ enum sigma_read_register {
|
||||||
READ_PIN_CHANGE_HIGH = 9,
|
READ_PIN_CHANGE_HIGH = 9,
|
||||||
READ_BLOCK_LAST_TS_LOW = 10,
|
READ_BLOCK_LAST_TS_LOW = 10,
|
||||||
READ_BLOCK_LAST_TS_HIGH = 11,
|
READ_BLOCK_LAST_TS_HIGH = 11,
|
||||||
READ_BLOCK_TS_OVERRUN = 12,
|
READ_PIN_VIEW = 12,
|
||||||
READ_PIN_VIEW = 13,
|
|
||||||
/* Unassigned register location. */
|
|
||||||
READ_TEST = 15,
|
READ_TEST = 15,
|
||||||
/* Reserved for plugin features. See above. */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CLKSEL_CLKSEL8 BIT(0)
|
#define REG_ADDR_LOW (0x0 << 4)
|
||||||
#define CLKSEL_PINMASK BITS_MASK(4)
|
#define REG_ADDR_HIGH (0x1 << 4)
|
||||||
#define CLKSEL_RISING BIT(4)
|
#define REG_DATA_LOW (0x2 << 4)
|
||||||
#define CLKSEL_FALLING BIT(5)
|
#define REG_DATA_HIGH_WRITE (0x3 << 4)
|
||||||
|
#define REG_READ_ADDR (0x4 << 4)
|
||||||
|
#define REG_DRAM_WAIT_ACK (0x5 << 4)
|
||||||
|
|
||||||
#define TRGSEL_SELINC_MASK BITS_MASK(2)
|
/* Bit (1 << 4) can be low or high (double buffer / cache) */
|
||||||
#define TRGSEL_SELINC_SHIFT 0
|
#define REG_DRAM_BLOCK (0x6 << 4)
|
||||||
#define TRGSEL_SELRES_MASK BITS_MASK(2)
|
#define REG_DRAM_BLOCK_BEGIN (0x8 << 4)
|
||||||
#define TRGSEL_SELRES_SHIFT 2
|
#define REG_DRAM_BLOCK_DATA (0xa << 4)
|
||||||
#define TRGSEL_SELA_MASK BITS_MASK(2)
|
|
||||||
#define TRGSEL_SELA_SHIFT 4
|
|
||||||
#define TRGSEL_SELB_MASK BITS_MASK(2)
|
|
||||||
#define TRGSEL_SELB_SHIFT 6
|
|
||||||
#define TRGSEL_SELC_MASK BITS_MASK(2)
|
|
||||||
#define TRGSEL_SELC_SHIFT 8
|
|
||||||
#define TRGSEL_SELPRESC_MASK BITS_MASK(4)
|
|
||||||
#define TRGSEL_SELPRESC_SHIFT 12
|
|
||||||
|
|
||||||
enum trgsel_selcode_t {
|
#define LEDSEL0 6
|
||||||
TRGSEL_SELCODE_LEVEL = 0,
|
#define LEDSEL1 7
|
||||||
TRGSEL_SELCODE_FALL = 1,
|
|
||||||
TRGSEL_SELCODE_RISE = 2,
|
|
||||||
TRGSEL_SELCODE_EVENT = 3,
|
|
||||||
TRGSEL_SELCODE_NEVER = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define TRGSEL2_PINS_MASK BITS_MASK(3)
|
#define NEXT_REG 1
|
||||||
#define TRGSEL2_PINPOL_RISE BIT(3)
|
|
||||||
#define TRGSEL2_LUT_ADDR_MASK BITS_MASK(4)
|
#define EVENTS_PER_CLUSTER 7
|
||||||
#define TRGSEL2_LUT_WRITE BIT(4)
|
|
||||||
#define TRGSEL2_RESET BIT(5)
|
#define CHUNK_SIZE 1024
|
||||||
#define TRGSEL2_LEDSEL0 BIT(6)
|
|
||||||
#define TRGSEL2_LEDSEL1 BIT(7)
|
|
||||||
|
|
||||||
/* WRITE_MODE register fields. */
|
/* WRITE_MODE register fields. */
|
||||||
#define WMR_SDRAMWRITEEN BIT(0)
|
#define WMR_SDRAMWRITEEN (1 << 0)
|
||||||
#define WMR_SDRAMREADEN BIT(1)
|
#define WMR_SDRAMREADEN (1 << 1)
|
||||||
#define WMR_TRGRES BIT(2)
|
#define WMR_TRGRES (1 << 2)
|
||||||
#define WMR_TRGEN BIT(3)
|
#define WMR_TRGEN (1 << 3)
|
||||||
#define WMR_FORCESTOP BIT(4)
|
#define WMR_FORCESTOP (1 << 4)
|
||||||
#define WMR_TRGSW BIT(5)
|
#define WMR_TRGSW (1 << 5)
|
||||||
/* not used: bit position 6 */
|
/* not used: bit position 6 */
|
||||||
#define WMR_SDRAMINIT BIT(7)
|
#define WMR_SDRAMINIT (1 << 7)
|
||||||
|
|
||||||
/* READ_MODE register fields. */
|
/* READ_MODE register fields. */
|
||||||
#define RMR_SDRAMWRITEEN BIT(0)
|
#define RMR_SDRAMWRITEEN (1 << 0)
|
||||||
#define RMR_SDRAMREADEN BIT(1)
|
#define RMR_SDRAMREADEN (1 << 1)
|
||||||
/* not used: bit position 2 */
|
/* not used: bit position 2 */
|
||||||
#define RMR_TRGEN BIT(3)
|
#define RMR_TRGEN (1 << 3)
|
||||||
#define RMR_ROUND BIT(4)
|
#define RMR_ROUND (1 << 4)
|
||||||
#define RMR_TRIGGERED BIT(5)
|
#define RMR_TRIGGERED (1 << 5)
|
||||||
#define RMR_POSTTRIGGERED BIT(6)
|
#define RMR_POSTTRIGGERED (1 << 6)
|
||||||
/* not used: bit position 7 */
|
/* not used: bit position 7 */
|
||||||
|
|
||||||
/*
|
|
||||||
* Trigger options. First and second write are similar, but _some_
|
|
||||||
* positions change their meaning.
|
|
||||||
*/
|
|
||||||
#define TRGOPT_TRGIEN BIT(7)
|
|
||||||
#define TRGOPT_TRGOEN BIT(6)
|
|
||||||
#define TRGOPT_TRGOINEN BIT(5) /* 1st write */
|
|
||||||
#define TRGOPT_TRGINEG TRGOPT1_TRGOINEN /* 2nd write */
|
|
||||||
#define TRGOPT_TRGOEVNTEN BIT(4) /* 1st write */
|
|
||||||
#define TRGOPT_TRGOPIN TRGOPT1_TRGOEVNTEN /* 2nd write */
|
|
||||||
#define TRGOPT_TRGOOUTEN BIT(3) /* 1st write */
|
|
||||||
#define TRGOPT_TRGOLONG TRGOPT1_TRGOOUTEN /* 2nd write */
|
|
||||||
#define TRGOPT_TRGOUTR_OUT BIT(1)
|
|
||||||
#define TRGOPT_TRGOUTR_EN BIT(0)
|
|
||||||
#define TRGOPT_CLEAR_MASK (TRGOPT_TRGOINEN | TRGOPT_TRGOEVNTEN | TRGOPT_TRGOOUTEN)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Layout of the sample data DRAM, which will be downloaded to the PC:
|
* Layout of the sample data DRAM, which will be downloaded to the PC:
|
||||||
*
|
*
|
||||||
* Sigma memory is organized in 32K rows. Each row contains 64 clusters.
|
* Sigma memory is organized in 32K rows. Each row contains 64 clusters.
|
||||||
* Each cluster contains a timestamp (16bit) and 7 events (16bits each).
|
* Each cluster contains a timestamp (16bit) and 7 samples (16bits each).
|
||||||
* Events contain 16 bits of sample data (potentially taken at multiple
|
* Total memory size is 32K x 64 x 8 x 2 bytes == 32 MB (256 Mbit).
|
||||||
* sample points, see below).
|
|
||||||
*
|
|
||||||
* Total memory size is 32K x 64 x 8 x 2 bytes == 32 MiB (256 Mbit). The
|
|
||||||
* size of a memory row is 1024 bytes. Assuming x16 organization of the
|
|
||||||
* memory array, address specs (sample count, trigger position) are kept
|
|
||||||
* in 24bit entities. The upper 15 bit address the "row", the lower 9 bit
|
|
||||||
* refer to the "event" within the row. Because there is one timestamp for
|
|
||||||
* seven events each, one memory row can hold up to 64x7 == 448 events.
|
|
||||||
*
|
*
|
||||||
* Sample data is represented in 16bit quantities. The first sample in
|
* Sample data is represented in 16bit quantities. The first sample in
|
||||||
* the cluster corresponds to the cluster's timestamp. Each next sample
|
* the cluster corresponds to the cluster's timestamp. Each next sample
|
||||||
|
|
@ -224,50 +129,80 @@ enum trgsel_selcode_t {
|
||||||
* one sample period, according to the samplerate). In the absence of
|
* one sample period, according to the samplerate). In the absence of
|
||||||
* pin level changes, no data is provided (RLE compression). A cluster
|
* pin level changes, no data is provided (RLE compression). A cluster
|
||||||
* is enforced for each 64K ticks of the timestamp, to reliably handle
|
* is enforced for each 64K ticks of the timestamp, to reliably handle
|
||||||
* rollover and determine the next timestamp of the next cluster.
|
* rollover and determination of the next timestamp of the next cluster.
|
||||||
*
|
*
|
||||||
* For samplerates up to 50MHz, an event directly translates to one set
|
|
||||||
* of sample data at a single sample point, spanning up to 16 channels.
|
|
||||||
* For samplerates of 100MHz, there is one 16 bit entity for each 20ns
|
* For samplerates of 100MHz, there is one 16 bit entity for each 20ns
|
||||||
* period (50MHz rate). The 16 bit memory contains 2 samples of up to
|
* period (50MHz rate). The 16 bit memory contains 2 samples of up to
|
||||||
* 8 channels. Bits of multiple samples are interleaved. For samplerates
|
* 8 channels. Bits of multiple samples are interleaved. For samplerates
|
||||||
* of 200MHz one 16bit entity contains 4 samples of up to 4 channels,
|
* of 200MHz one 16bit entity contains 4 samples of up to 4 channels,
|
||||||
* each 5ns apart.
|
* each 5ns apart.
|
||||||
|
*
|
||||||
|
* Memory addresses (sample count, trigger position) are kept in 24bit
|
||||||
|
* entities. The upper 15 bit refer to the "row", the lower 9 bit refer
|
||||||
|
* to the "event" within the row. Because there is one timestamp for
|
||||||
|
* seven samples each, one memory row can hold up to 64x7 == 448 samples.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define ROW_COUNT 32768
|
/* One "DRAM cluster" contains a timestamp and 7 samples, 16b total. */
|
||||||
#define ROW_LENGTH_BYTES 1024
|
struct sigma_dram_cluster {
|
||||||
#define ROW_LENGTH_U16 (ROW_LENGTH_BYTES / sizeof(uint16_t))
|
uint8_t timestamp_lo;
|
||||||
#define ROW_SHIFT 9 /* log2 of u16 count */
|
uint8_t timestamp_hi;
|
||||||
#define ROW_MASK BITS_MASK(ROW_SHIFT)
|
struct {
|
||||||
#define EVENTS_PER_CLUSTER 7
|
uint8_t sample_hi;
|
||||||
#define CLUSTERS_PER_ROW (ROW_LENGTH_U16 / (1 + EVENTS_PER_CLUSTER))
|
uint8_t sample_lo;
|
||||||
#define EVENTS_PER_ROW (CLUSTERS_PER_ROW * EVENTS_PER_CLUSTER)
|
} samples[7];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* One "DRAM line" contains 64 "DRAM clusters", 1024b total. */
|
||||||
struct sigma_dram_line {
|
struct sigma_dram_line {
|
||||||
struct sigma_dram_cluster {
|
struct sigma_dram_cluster cluster[64];
|
||||||
uint16_t timestamp;
|
};
|
||||||
uint16_t samples[EVENTS_PER_CLUSTER];
|
|
||||||
} cluster[CLUSTERS_PER_ROW];
|
struct clockselect_50 {
|
||||||
|
uint8_t async;
|
||||||
|
uint8_t fraction;
|
||||||
|
uint16_t disabled_channels;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The effect of all these are still a bit unclear. */
|
/* The effect of all these are still a bit unclear. */
|
||||||
struct triggerinout {
|
struct triggerinout {
|
||||||
gboolean trgout_resistor_enable, trgout_resistor_pullup;
|
uint8_t trgout_resistor_enable : 1;
|
||||||
gboolean trgout_resistor_enable2, trgout_resistor_pullup2;
|
uint8_t trgout_resistor_pullup : 1;
|
||||||
gboolean trgout_bytrigger, trgout_byevent, trgout_bytriggerin;
|
uint8_t reserved1 : 1;
|
||||||
gboolean trgout_long, trgout_pin; /* 1ms pulse, 1k resistor */
|
uint8_t trgout_bytrigger : 1;
|
||||||
gboolean trgin_negate, trgout_enable, trgin_enable;
|
uint8_t trgout_byevent : 1;
|
||||||
|
uint8_t trgout_bytriggerin : 1;
|
||||||
|
uint8_t reserved2 : 2;
|
||||||
|
|
||||||
|
/* Should be set same as the first two */
|
||||||
|
uint8_t trgout_resistor_enable2 : 1;
|
||||||
|
uint8_t trgout_resistor_pullup2 : 1;
|
||||||
|
|
||||||
|
uint8_t reserved3 : 1;
|
||||||
|
uint8_t trgout_long : 1;
|
||||||
|
uint8_t trgout_pin : 1; /* Use 1k resistor. Pullup? */
|
||||||
|
uint8_t trgin_negate : 1;
|
||||||
|
uint8_t trgout_enable : 1;
|
||||||
|
uint8_t trgin_enable : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct triggerlut {
|
struct triggerlut {
|
||||||
|
/* The actual LUTs. */
|
||||||
uint16_t m0d[4], m1d[4], m2d[4];
|
uint16_t m0d[4], m1d[4], m2d[4];
|
||||||
uint16_t m3q, m3s, m4;
|
uint16_t m3, m3s, m4;
|
||||||
|
|
||||||
|
/* Parameters should be sent as a single register write. */
|
||||||
struct {
|
struct {
|
||||||
uint8_t selpresc;
|
uint8_t selc : 2;
|
||||||
uint8_t sela, selb, selc;
|
uint8_t selpresc : 6;
|
||||||
uint8_t selinc, selres;
|
|
||||||
uint16_t cmpa, cmpb;
|
uint8_t selinc : 2;
|
||||||
|
uint8_t selres : 2;
|
||||||
|
uint8_t sela : 2;
|
||||||
|
uint8_t selb : 2;
|
||||||
|
|
||||||
|
uint16_t cmpb;
|
||||||
|
uint16_t cmpa;
|
||||||
} params;
|
} params;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -306,120 +241,47 @@ enum triggerfunc {
|
||||||
FUNC_NXOR,
|
FUNC_NXOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sigma_firmware_idx {
|
struct sigma_state {
|
||||||
SIGMA_FW_NONE,
|
|
||||||
SIGMA_FW_50MHZ,
|
|
||||||
SIGMA_FW_100MHZ,
|
|
||||||
SIGMA_FW_200MHZ,
|
|
||||||
SIGMA_FW_SYNC,
|
|
||||||
SIGMA_FW_FREQ,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ext_clock_edge_t {
|
|
||||||
SIGMA_CLOCK_EDGE_RISING,
|
|
||||||
SIGMA_CLOCK_EDGE_FALLING,
|
|
||||||
SIGMA_CLOCK_EDGE_EITHER,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct submit_buffer;
|
|
||||||
|
|
||||||
struct dev_context {
|
|
||||||
struct {
|
|
||||||
uint16_t vid, pid;
|
|
||||||
uint32_t serno;
|
|
||||||
uint16_t prefix;
|
|
||||||
enum asix_device_type type;
|
|
||||||
} id;
|
|
||||||
struct {
|
|
||||||
struct ftdi_context ctx;
|
|
||||||
gboolean is_open, must_close;
|
|
||||||
} ftdi;
|
|
||||||
struct {
|
|
||||||
uint64_t samplerate;
|
|
||||||
gboolean use_ext_clock;
|
|
||||||
size_t clock_pin;
|
|
||||||
enum ext_clock_edge_t clock_edge;
|
|
||||||
} clock;
|
|
||||||
struct {
|
|
||||||
/*
|
|
||||||
* User specified configuration values, in contrast to
|
|
||||||
* internal arrangement of acquisition, and submission
|
|
||||||
* to the session feed.
|
|
||||||
*/
|
|
||||||
struct sr_sw_limits config;
|
|
||||||
struct sr_sw_limits acquire;
|
|
||||||
struct sr_sw_limits submit;
|
|
||||||
} limit;
|
|
||||||
enum sigma_firmware_idx firmware_idx;
|
|
||||||
struct sigma_sample_interp {
|
|
||||||
/* Interpretation of sample memory. */
|
|
||||||
size_t num_channels;
|
|
||||||
size_t samples_per_event;
|
|
||||||
struct {
|
|
||||||
uint16_t ts;
|
|
||||||
uint16_t sample;
|
|
||||||
} last;
|
|
||||||
struct sigma_location {
|
|
||||||
size_t raw, line, cluster, event;
|
|
||||||
} start, stop, trig, iter, trig_arm;
|
|
||||||
struct {
|
|
||||||
size_t lines_total, lines_done;
|
|
||||||
size_t lines_per_read; /* USB transfer limit */
|
|
||||||
size_t lines_rcvd;
|
|
||||||
struct sigma_dram_line *rcvd_lines;
|
|
||||||
struct sigma_dram_line *curr_line;
|
|
||||||
} fetch;
|
|
||||||
struct {
|
|
||||||
gboolean armed;
|
|
||||||
gboolean matched;
|
|
||||||
size_t evt_remain;
|
|
||||||
} trig_chk;
|
|
||||||
} interp;
|
|
||||||
uint64_t capture_ratio;
|
|
||||||
struct sigma_trigger trigger;
|
|
||||||
gboolean use_triggers;
|
|
||||||
gboolean late_trigger_timeout;
|
|
||||||
enum {
|
enum {
|
||||||
SIGMA_UNINITIALIZED = 0,
|
SIGMA_UNINITIALIZED = 0,
|
||||||
SIGMA_CONFIG,
|
|
||||||
SIGMA_IDLE,
|
SIGMA_IDLE,
|
||||||
SIGMA_CAPTURE,
|
SIGMA_CAPTURE,
|
||||||
SIGMA_STOPPING,
|
SIGMA_STOPPING,
|
||||||
SIGMA_DOWNLOAD,
|
SIGMA_DOWNLOAD,
|
||||||
} state;
|
} state;
|
||||||
struct submit_buffer *buffer;
|
uint16_t lastts;
|
||||||
|
uint16_t lastsample;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* "Automatic" and forced USB connection open/close support. */
|
struct dev_context {
|
||||||
SR_PRIV int sigma_check_open(const struct sr_dev_inst *sdi);
|
struct ftdi_context ftdic;
|
||||||
SR_PRIV int sigma_check_close(struct dev_context *devc);
|
uint64_t cur_samplerate;
|
||||||
SR_PRIV int sigma_force_open(const struct sr_dev_inst *sdi);
|
uint64_t limit_msec;
|
||||||
SR_PRIV int sigma_force_close(struct dev_context *devc);
|
uint64_t limit_samples;
|
||||||
|
uint64_t sent_samples;
|
||||||
|
uint64_t start_time;
|
||||||
|
int cur_firmware;
|
||||||
|
int num_channels;
|
||||||
|
int cur_channels;
|
||||||
|
int samples_per_event;
|
||||||
|
uint64_t capture_ratio;
|
||||||
|
struct sigma_trigger trigger;
|
||||||
|
int use_triggers;
|
||||||
|
struct sigma_state state;
|
||||||
|
};
|
||||||
|
|
||||||
/* Save configuration across sessions, to reduce cost of continuation. */
|
extern SR_PRIV const uint64_t samplerates[];
|
||||||
SR_PRIV int sigma_store_hw_config(const struct sr_dev_inst *sdi);
|
extern SR_PRIV const size_t samplerates_count;
|
||||||
SR_PRIV int sigma_fetch_hw_config(const struct sr_dev_inst *sdi);
|
|
||||||
|
|
||||||
/* Send register content (simple and complex) to the hardware. */
|
SR_PRIV int sigma_write_register(uint8_t reg, uint8_t *data, size_t len,
|
||||||
SR_PRIV int sigma_write_register(struct dev_context *devc,
|
struct dev_context *devc);
|
||||||
uint8_t reg, uint8_t *data, size_t len);
|
SR_PRIV int sigma_set_register(uint8_t reg, uint8_t value, struct dev_context *devc);
|
||||||
SR_PRIV int sigma_set_register(struct dev_context *devc,
|
SR_PRIV int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *devc);
|
||||||
uint8_t reg, uint8_t value);
|
SR_PRIV uint64_t sigma_limit_samples_to_msec(const struct dev_context *devc,
|
||||||
SR_PRIV int sigma_write_trigger_lut(struct dev_context *devc,
|
uint64_t limit_samples);
|
||||||
struct triggerlut *lut);
|
SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
|
||||||
|
|
||||||
/* Samplerate constraints check, get/set/list helpers. */
|
|
||||||
SR_PRIV int sigma_normalize_samplerate(uint64_t want_rate, uint64_t *have_rate);
|
|
||||||
SR_PRIV GVariant *sigma_get_samplerates_list(void);
|
|
||||||
|
|
||||||
/* Preparation of data acquisition, spec conversion, hardware configuration. */
|
|
||||||
SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi);
|
|
||||||
SR_PRIV int sigma_set_acquire_timeout(struct dev_context *devc);
|
|
||||||
SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi);
|
SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi);
|
||||||
SR_PRIV int sigma_build_basic_trigger(struct dev_context *devc,
|
|
||||||
struct triggerlut *lut);
|
|
||||||
|
|
||||||
/* Callback to periodically drive acuisition progress. */
|
|
||||||
SR_PRIV int sigma_receive_data(int fd, int revents, void *cb_data);
|
SR_PRIV int sigma_receive_data(int fd, int revents, void *cb_data);
|
||||||
|
SR_PRIV int sigma_build_basic_trigger(struct triggerlut *lut, struct dev_context *devc);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options, int modelid)
|
||||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
serial_flush(serial);
|
||||||
|
|
||||||
/* This is how the vendor software scans for hardware. */
|
/* This is how the vendor software scans for hardware. */
|
||||||
memset(packet, 0, PACKET_SIZE);
|
memset(packet, 0, PACKET_SIZE);
|
||||||
packet[0] = 0xaa;
|
packet[0] = 0xaa;
|
||||||
|
|
|
||||||
|
|
@ -703,7 +703,7 @@ SR_PRIV void bl_acme_close_channel(struct sr_channel *ch)
|
||||||
SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
|
SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
|
||||||
{
|
{
|
||||||
uint64_t nrexpiration;
|
uint64_t nrexpiration;
|
||||||
struct sr_datafeed_packet packet;
|
struct sr_datafeed_packet packet, framep;
|
||||||
struct sr_datafeed_analog analog;
|
struct sr_datafeed_analog analog;
|
||||||
struct sr_analog_encoding encoding;
|
struct sr_analog_encoding encoding;
|
||||||
struct sr_analog_meaning meaning;
|
struct sr_analog_meaning meaning;
|
||||||
|
|
@ -759,7 +759,8 @@ SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
|
||||||
* accuracy.
|
* accuracy.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < nrexpiration; i++) {
|
for (i = 0; i < nrexpiration; i++) {
|
||||||
std_session_send_df_frame_begin(sdi);
|
framep.type = SR_DF_FRAME_BEGIN;
|
||||||
|
sr_session_send(sdi, &framep);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Due to different units used in each channel we're sending
|
* Due to different units used in each channel we're sending
|
||||||
|
|
@ -787,7 +788,8 @@ SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
|
||||||
sr_session_send(sdi, &packet);
|
sr_session_send(sdi, &packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
std_session_send_df_frame_end(sdi);
|
framep.type = SR_DF_FRAME_END;
|
||||||
|
sr_session_send(sdi, &framep);
|
||||||
}
|
}
|
||||||
|
|
||||||
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
||||||
|
|
|
||||||
|
|
@ -88,12 +88,10 @@ static int beaglelogic_get_lasterror(struct dev_context *devc)
|
||||||
if ((fd = open(BEAGLELOGIC_SYSFS_ATTR(lasterror), O_RDONLY)) == -1)
|
if ((fd = open(BEAGLELOGIC_SYSFS_ATTR(lasterror), O_RDONLY)) == -1)
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
|
|
||||||
ret = read(fd, buf, 16);
|
if ((ret = read(fd, buf, 16)) < 0)
|
||||||
close(fd);
|
|
||||||
|
|
||||||
if (ret)
|
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
devc->last_error = strtoul(buf, NULL, 10);
|
devc->last_error = strtoul(buf, NULL, 10);
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the libsigrok project.
|
* This file is part of the libsigrok project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018-2020 Andreas Sandberg <andreas@sandberg.pp.se>
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -17,28 +17,16 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <glib.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
|
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
|
||||||
#define RDTECH_UM_SERIALCOMM "115200/8n1"
|
|
||||||
|
|
||||||
static const uint32_t scanopts[] = {
|
static const uint32_t scanopts[] = {
|
||||||
SR_CONF_CONN,
|
SR_CONF_CONN,
|
||||||
SR_CONF_SERIALCOMM,
|
SR_CONF_SERIALCOMM,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t drvopts[] = {
|
static const uint32_t drvopts[] = {
|
||||||
SR_CONF_ENERGYMETER,
|
SR_CONF_MULTIMETER,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t devopts[] = {
|
static const uint32_t devopts[] = {
|
||||||
|
|
@ -47,65 +35,68 @@ static const uint32_t devopts[] = {
|
||||||
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
|
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static GSList *rdtech_um_scan(struct sr_dev_driver *di, const char *conn,
|
static GSList *brymen_scan(struct sr_dev_driver *di, const char *conn,
|
||||||
const char *serialcomm)
|
const char *serialcomm)
|
||||||
{
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct dev_context *devc;
|
||||||
struct sr_serial_dev_inst *serial;
|
struct sr_serial_dev_inst *serial;
|
||||||
const struct rdtech_um_profile *p = NULL;
|
GSList *devices;
|
||||||
GSList *devices = NULL;
|
int ret;
|
||||||
struct dev_context *devc = NULL;
|
uint8_t buf[128];
|
||||||
struct sr_dev_inst *sdi = NULL;
|
size_t len;
|
||||||
|
|
||||||
serial = sr_serial_dev_inst_new(conn, serialcomm);
|
serial = sr_serial_dev_inst_new(conn, serialcomm);
|
||||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
p = rdtech_um_probe(serial);
|
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
||||||
if (!p) {
|
return NULL;
|
||||||
sr_err("Failed to find a supported RDTech UM device.");
|
|
||||||
goto err_out_serial;
|
sr_info("Probing port %s.", conn);
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
|
||||||
|
/* Request reading */
|
||||||
|
if ((ret = brymen_packet_request(serial)) < 0) {
|
||||||
|
sr_err("Unable to send command: %d.", ret);
|
||||||
|
goto scan_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
devc = g_malloc0(sizeof(struct dev_context));
|
len = sizeof(buf);
|
||||||
|
ret = brymen_stream_detect(serial, buf, &len, brymen_packet_length,
|
||||||
|
brymen_packet_is_valid, 1000, 9600);
|
||||||
|
if (ret != SR_OK)
|
||||||
|
goto scan_cleanup;
|
||||||
|
|
||||||
|
sr_info("Found device on port %s.", conn);
|
||||||
|
|
||||||
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
||||||
|
|
||||||
sr_sw_limits_init(&devc->limits);
|
|
||||||
devc->profile = p;
|
|
||||||
|
|
||||||
sdi->status = SR_ST_INACTIVE;
|
sdi->status = SR_ST_INACTIVE;
|
||||||
sdi->vendor = g_strdup("RDTech");
|
sdi->vendor = g_strdup("Brymen");
|
||||||
sdi->model = g_strdup(p->model_name);
|
sdi->model = g_strdup("BM85x");
|
||||||
sdi->version = NULL;
|
devc = g_malloc0(sizeof(struct dev_context));
|
||||||
|
sr_sw_limits_init(&devc->sw_limits);
|
||||||
sdi->inst_type = SR_INST_SERIAL;
|
sdi->inst_type = SR_INST_SERIAL;
|
||||||
sdi->conn = serial;
|
sdi->conn = serial;
|
||||||
sdi->priv = devc;
|
sdi->priv = devc;
|
||||||
|
sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
|
||||||
for (int i = 0; p->channels[i].name; i++)
|
|
||||||
sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE,
|
|
||||||
p->channels[i].name);
|
|
||||||
|
|
||||||
devices = g_slist_append(devices, sdi);
|
devices = g_slist_append(devices, sdi);
|
||||||
|
|
||||||
|
scan_cleanup:
|
||||||
serial_close(serial);
|
serial_close(serial);
|
||||||
if (!devices)
|
|
||||||
sr_serial_dev_inst_free(serial);
|
|
||||||
|
|
||||||
return std_scan_complete(di, devices);
|
return std_scan_complete(di, devices);
|
||||||
|
|
||||||
err_out_serial:
|
|
||||||
serial_close(serial);
|
|
||||||
err_out:
|
|
||||||
sr_serial_dev_inst_free(serial);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
{
|
{
|
||||||
struct sr_config *src;
|
struct sr_config *src;
|
||||||
const char *conn = NULL;
|
GSList *devices, *l;
|
||||||
const char *serialcomm = RDTECH_UM_SERIALCOMM;
|
const char *conn, *serialcomm;
|
||||||
|
|
||||||
for (GSList *l = options; l; l = l->next) {
|
devices = NULL;
|
||||||
|
|
||||||
|
conn = serialcomm = NULL;
|
||||||
|
for (l = options; l; l = l->next) {
|
||||||
src = l->data;
|
src = l->data;
|
||||||
switch (src->key) {
|
switch (src->key) {
|
||||||
case SR_CONF_CONN:
|
case SR_CONF_CONN:
|
||||||
|
|
@ -119,7 +110,12 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return rdtech_um_scan(di, conn, serialcomm);
|
if (serialcomm)
|
||||||
|
devices = brymen_scan(di, conn, serialcomm);
|
||||||
|
else
|
||||||
|
devices = brymen_scan(di, conn, "9600/8n1/dtr=1/rts=1");
|
||||||
|
|
||||||
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int config_set(uint32_t key, GVariant *data,
|
static int config_set(uint32_t key, GVariant *data,
|
||||||
|
|
@ -131,7 +127,7 @@ static int config_set(uint32_t key, GVariant *data,
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
return sr_sw_limits_config_set(&devc->limits, key, data);
|
return sr_sw_limits_config_set(&devc->sw_limits, key, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int config_list(uint32_t key, GVariant **data,
|
static int config_list(uint32_t key, GVariant **data,
|
||||||
|
|
@ -142,21 +138,24 @@ static int config_list(uint32_t key, GVariant **data,
|
||||||
|
|
||||||
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
||||||
{
|
{
|
||||||
struct dev_context *devc = sdi->priv;
|
struct dev_context *devc;
|
||||||
struct sr_serial_dev_inst *serial = sdi->conn;
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
sr_sw_limits_acquisition_start(&devc->limits);
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
sr_sw_limits_acquisition_start(&devc->sw_limits);
|
||||||
std_session_send_df_header(sdi);
|
std_session_send_df_header(sdi);
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
serial_source_add(sdi->session, serial, G_IO_IN, 50,
|
serial_source_add(sdi->session, serial, G_IO_IN, 50,
|
||||||
rdtech_um_receive_data, (void *)sdi);
|
brymen_dmm_receive_data, (void *)sdi);
|
||||||
|
|
||||||
return rdtech_um_poll(sdi);
|
return SR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sr_dev_driver rdtech_um_driver_info = {
|
static struct sr_dev_driver brymen_bm857_driver_info = {
|
||||||
.name = "rdtech-um",
|
.name = "brymen-bm857",
|
||||||
.longname = "RDTech UMxx USB power meter",
|
.longname = "Brymen BM857",
|
||||||
.api_version = 1,
|
.api_version = 1,
|
||||||
.init = std_init,
|
.init = std_init,
|
||||||
.cleanup = std_cleanup,
|
.cleanup = std_cleanup,
|
||||||
|
|
@ -172,4 +171,4 @@ static struct sr_dev_driver rdtech_um_driver_info = {
|
||||||
.dev_acquisition_stop = std_serial_dev_acquisition_stop,
|
.dev_acquisition_stop = std_serial_dev_acquisition_stop,
|
||||||
.context = NULL,
|
.context = NULL,
|
||||||
};
|
};
|
||||||
SR_REGISTER_DEV_DRIVER(rdtech_um_driver_info);
|
SR_REGISTER_DEV_DRIVER(brymen_bm857_driver_info);
|
||||||
|
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
#define MAX_PACKET_LEN 22
|
||||||
|
|
||||||
|
/* Flags passed from the DMM. */
|
||||||
|
struct brymen_flags {
|
||||||
|
gboolean is_low_batt, is_decibel, is_duty_cycle, is_hertz, is_amp;
|
||||||
|
gboolean is_beep, is_ohm, is_fahrenheit, is_celsius, is_capacitance;
|
||||||
|
gboolean is_diode, is_volt, is_dc, is_ac;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bm850_command {
|
||||||
|
uint8_t dle;
|
||||||
|
uint8_t stx;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t arg[2];
|
||||||
|
uint8_t checksum;
|
||||||
|
uint8_t dle2;
|
||||||
|
uint8_t etx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct brymen_header {
|
||||||
|
uint8_t dle;
|
||||||
|
uint8_t stx;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct brymen_tail {
|
||||||
|
uint8_t checksum;
|
||||||
|
uint8_t dle;
|
||||||
|
uint8_t etx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only have one command because we only support the BM-857. However, the
|
||||||
|
* driver is easily extensible to support more models, as the protocols are
|
||||||
|
* very similar.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
BM_CMD_REQUEST_READING = 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int bm_send_command(uint8_t command, uint8_t arg1, uint8_t arg2,
|
||||||
|
struct sr_serial_dev_inst *serial)
|
||||||
|
{
|
||||||
|
struct bm850_command cmdout;
|
||||||
|
int written;
|
||||||
|
|
||||||
|
cmdout.dle = 0x10;
|
||||||
|
cmdout.stx = 0x02;
|
||||||
|
cmdout.cmd = command;
|
||||||
|
cmdout.arg[0] = arg1;
|
||||||
|
cmdout.arg[1] = arg2;
|
||||||
|
cmdout.checksum = arg1 ^ arg2;
|
||||||
|
cmdout.dle2 = 0x10;
|
||||||
|
cmdout.etx = 0x03;
|
||||||
|
|
||||||
|
/* TODO: How to compute the checksum? Hardware seems to ignore it. */
|
||||||
|
|
||||||
|
/* Request reading. */
|
||||||
|
written = serial_write_blocking(serial, &cmdout, sizeof(cmdout),
|
||||||
|
serial_timeout(serial, sizeof(cmdout)));
|
||||||
|
if (written != sizeof(cmdout))
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial)
|
||||||
|
{
|
||||||
|
return bm_send_command(BM_CMD_REQUEST_READING, 0, 0, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len)
|
||||||
|
{
|
||||||
|
struct brymen_header *hdr;
|
||||||
|
int packet_len;
|
||||||
|
size_t buflen;
|
||||||
|
|
||||||
|
buflen = *len;
|
||||||
|
hdr = (void *)buf;
|
||||||
|
|
||||||
|
/* Did we receive a complete header yet? */
|
||||||
|
if (buflen < sizeof(*hdr))
|
||||||
|
return PACKET_NEED_MORE_DATA;
|
||||||
|
|
||||||
|
if (hdr->dle != 0x10 || hdr->stx != 0x02)
|
||||||
|
return PACKET_INVALID_HEADER;
|
||||||
|
|
||||||
|
/* Our packet includes the header, the payload, and the tail. */
|
||||||
|
packet_len = sizeof(*hdr) + hdr->len + sizeof(struct brymen_tail);
|
||||||
|
|
||||||
|
/* In case we pick up an invalid header, limit our search. */
|
||||||
|
if (packet_len > MAX_PACKET_LEN) {
|
||||||
|
sr_spew("Header specifies an invalid payload length: %i.",
|
||||||
|
hdr->len);
|
||||||
|
return PACKET_INVALID_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
*len = packet_len;
|
||||||
|
sr_spew("Expecting a %d-byte packet.", *len);
|
||||||
|
return PACKET_HEADER_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
struct brymen_header *hdr;
|
||||||
|
struct brymen_tail *tail;
|
||||||
|
int i;
|
||||||
|
uint8_t chksum = 0;
|
||||||
|
uint8_t *payload;
|
||||||
|
|
||||||
|
payload = (uint8_t *)(buf + sizeof(struct brymen_header));
|
||||||
|
|
||||||
|
hdr = (void *)buf;
|
||||||
|
tail = (void *)(payload + hdr->len);
|
||||||
|
|
||||||
|
for (i = 0; i< hdr->len; i++)
|
||||||
|
chksum ^= payload[i];
|
||||||
|
|
||||||
|
if (tail->checksum != chksum) {
|
||||||
|
sr_dbg("Packet has invalid checksum 0x%.2x. Expected 0x%.2x.",
|
||||||
|
chksum, tail->checksum);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_value(const char *strbuf, int len, float *floatval)
|
||||||
|
{
|
||||||
|
int s, d;
|
||||||
|
char str[32];
|
||||||
|
|
||||||
|
if (strstr(strbuf, "OL")) {
|
||||||
|
sr_dbg("Overlimit.");
|
||||||
|
*floatval = INFINITY;
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(str, 0, sizeof(str));
|
||||||
|
/* Spaces may interfere with parsing the exponent. Strip them. */
|
||||||
|
for (s = 0, d = 0; s < len; s++) {
|
||||||
|
if (strbuf[s] != ' ')
|
||||||
|
str[d++] = strbuf[s];
|
||||||
|
}
|
||||||
|
if (sr_atof_ascii(str, floatval) != SR_OK)
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_flags(const uint8_t *buf, struct brymen_flags *info)
|
||||||
|
{
|
||||||
|
info->is_low_batt = (buf[4 + 3] & (1 << 7)) != 0;
|
||||||
|
|
||||||
|
info->is_decibel = (buf[4 + 1] & (1 << 5)) != 0;
|
||||||
|
info->is_duty_cycle = (buf[4 + 1] & (1 << 3)) != 0;
|
||||||
|
info->is_hertz = (buf[4 + 1] & (1 << 2)) != 0;
|
||||||
|
info->is_amp = (buf[4 + 1] & (1 << 1)) != 0;
|
||||||
|
info->is_beep = (buf[4 + 1] & (1 << 0)) != 0;
|
||||||
|
|
||||||
|
info->is_ohm = (buf[4 + 0] & (1 << 7)) != 0;
|
||||||
|
info->is_fahrenheit = (buf[4 + 0] & (1 << 6)) != 0;
|
||||||
|
info->is_celsius = (buf[4 + 0] & (1 << 5)) != 0;
|
||||||
|
info->is_diode = (buf[4 + 0] & (1 << 4)) != 0;
|
||||||
|
info->is_capacitance = (buf[4 + 0] & (1 << 3)) != 0;
|
||||||
|
info->is_volt = (buf[4 + 0] & (1 << 2)) != 0;
|
||||||
|
info->is_dc = (buf[4 + 0] & (1 << 1)) != 0;
|
||||||
|
info->is_ac = (buf[4 + 0] & (1 << 0)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
|
||||||
|
struct sr_datafeed_analog *analog, void *info)
|
||||||
|
{
|
||||||
|
struct brymen_flags flags;
|
||||||
|
struct brymen_header *hdr;
|
||||||
|
uint8_t *bfunc;
|
||||||
|
int asciilen;
|
||||||
|
|
||||||
|
(void)info;
|
||||||
|
|
||||||
|
hdr = (void *)buf;
|
||||||
|
bfunc = (uint8_t *)(buf + sizeof(struct brymen_header));
|
||||||
|
|
||||||
|
analog->meaning->mqflags = 0;
|
||||||
|
|
||||||
|
/* Give some debug info about the package. */
|
||||||
|
asciilen = hdr->len - 4;
|
||||||
|
sr_dbg("DMM flags: %.2x %.2x %.2x %.2x",
|
||||||
|
bfunc[3], bfunc[2], bfunc[1], bfunc[0]);
|
||||||
|
/* Value is an ASCII string. */
|
||||||
|
sr_dbg("DMM packet: \"%.*s\"", asciilen, bfunc + 4);
|
||||||
|
|
||||||
|
parse_flags(buf, &flags);
|
||||||
|
if (parse_value((const char *)(bfunc + 4), asciilen, floatval) != SR_OK)
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
if (flags.is_volt) {
|
||||||
|
analog->meaning->mq = SR_MQ_VOLTAGE;
|
||||||
|
analog->meaning->unit = SR_UNIT_VOLT;
|
||||||
|
}
|
||||||
|
if (flags.is_amp) {
|
||||||
|
analog->meaning->mq = SR_MQ_CURRENT;
|
||||||
|
analog->meaning->unit = SR_UNIT_AMPERE;
|
||||||
|
}
|
||||||
|
if (flags.is_ohm) {
|
||||||
|
if (flags.is_beep)
|
||||||
|
analog->meaning->mq = SR_MQ_CONTINUITY;
|
||||||
|
else
|
||||||
|
analog->meaning->mq = SR_MQ_RESISTANCE;
|
||||||
|
analog->meaning->unit = SR_UNIT_OHM;
|
||||||
|
}
|
||||||
|
if (flags.is_hertz) {
|
||||||
|
analog->meaning->mq = SR_MQ_FREQUENCY;
|
||||||
|
analog->meaning->unit = SR_UNIT_HERTZ;
|
||||||
|
}
|
||||||
|
if (flags.is_duty_cycle) {
|
||||||
|
analog->meaning->mq = SR_MQ_DUTY_CYCLE;
|
||||||
|
analog->meaning->unit = SR_UNIT_PERCENTAGE;
|
||||||
|
}
|
||||||
|
if (flags.is_capacitance) {
|
||||||
|
analog->meaning->mq = SR_MQ_CAPACITANCE;
|
||||||
|
analog->meaning->unit = SR_UNIT_FARAD;
|
||||||
|
}
|
||||||
|
if (flags.is_fahrenheit) {
|
||||||
|
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
||||||
|
analog->meaning->unit = SR_UNIT_FAHRENHEIT;
|
||||||
|
}
|
||||||
|
if (flags.is_celsius) {
|
||||||
|
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
||||||
|
analog->meaning->unit = SR_UNIT_CELSIUS;
|
||||||
|
}
|
||||||
|
if (flags.is_capacitance) {
|
||||||
|
analog->meaning->mq = SR_MQ_CAPACITANCE;
|
||||||
|
analog->meaning->unit = SR_UNIT_FARAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The high-end Brymen models have a configurable reference impedance.
|
||||||
|
* When the reference impedance is changed, the DMM sends one packet
|
||||||
|
* with the value of the new reference impedance. Both decibel and ohm
|
||||||
|
* flags are set in this case, so we must be careful to correctly
|
||||||
|
* identify the value as ohm, not dBmW.
|
||||||
|
*/
|
||||||
|
if (flags.is_decibel && !flags.is_ohm) {
|
||||||
|
analog->meaning->mq = SR_MQ_POWER;
|
||||||
|
analog->meaning->unit = SR_UNIT_DECIBEL_MW;
|
||||||
|
/*
|
||||||
|
* For some reason, dBm measurements are sent by the multimeter
|
||||||
|
* with a value three orders of magnitude smaller than the
|
||||||
|
* displayed value.
|
||||||
|
*/
|
||||||
|
*floatval *= 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags.is_diode)
|
||||||
|
analog->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;
|
||||||
|
if (flags.is_dc)
|
||||||
|
analog->meaning->mqflags |= SR_MQFLAG_DC;
|
||||||
|
|
||||||
|
if (flags.is_low_batt)
|
||||||
|
sr_info("Low battery!");
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,256 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
float floatval;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
struct sr_datafeed_analog analog;
|
||||||
|
struct sr_analog_encoding encoding;
|
||||||
|
struct sr_analog_meaning meaning;
|
||||||
|
struct sr_analog_spec spec;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
/* TODO: Use proper 'digits' value for this device (and its modes). */
|
||||||
|
sr_analog_init(&analog, &encoding, &meaning, &spec, 2);
|
||||||
|
|
||||||
|
analog.num_samples = 1;
|
||||||
|
analog.meaning->mq = 0;
|
||||||
|
|
||||||
|
if (brymen_parse(buf, &floatval, &analog, NULL) != SR_OK)
|
||||||
|
return;
|
||||||
|
analog.data = &floatval;
|
||||||
|
|
||||||
|
analog.meaning->channels = sdi->channels;
|
||||||
|
|
||||||
|
if (analog.meaning->mq != 0) {
|
||||||
|
/* Got a measurement. */
|
||||||
|
packet.type = SR_DF_ANALOG;
|
||||||
|
packet.payload = &analog;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
sr_sw_limits_update_samples_read(&devc->sw_limits, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_new_data(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
int len, status, offset = 0;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
serial = sdi->conn;
|
||||||
|
|
||||||
|
/* Try to get as much data as the buffer can hold. */
|
||||||
|
len = DMM_BUFSIZE - devc->buflen;
|
||||||
|
len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
|
||||||
|
if (len < 1) {
|
||||||
|
sr_err("Serial port read error: %d.", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
devc->buflen += len;
|
||||||
|
status = PACKET_INVALID_HEADER;
|
||||||
|
|
||||||
|
/* Now look for packets in that data. */
|
||||||
|
while (status != PACKET_NEED_MORE_DATA) {
|
||||||
|
/* We don't have a header, look for one. */
|
||||||
|
if (devc->next_packet_len == 0) {
|
||||||
|
len = devc->buflen - offset;
|
||||||
|
status = brymen_packet_length(devc->buf + offset, &len);
|
||||||
|
if (status == PACKET_HEADER_OK) {
|
||||||
|
/* We know how large the packet will be. */
|
||||||
|
devc->next_packet_len = len;
|
||||||
|
} else if (status == PACKET_NEED_MORE_DATA) {
|
||||||
|
/* We didn't yet receive the full header. */
|
||||||
|
devc->next_packet_len = 0;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* Invalid header. Move on. */
|
||||||
|
devc->next_packet_len = 0;
|
||||||
|
offset++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We know how the packet size, but did we receive all of it? */
|
||||||
|
if (devc->buflen - offset < devc->next_packet_len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* We should have a full packet here, so we can check it. */
|
||||||
|
if (brymen_packet_is_valid(devc->buf + offset)) {
|
||||||
|
handle_packet(devc->buf + offset, sdi);
|
||||||
|
offset += devc->next_packet_len;
|
||||||
|
} else {
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We are done with this packet. Look for a new one. */
|
||||||
|
devc->next_packet_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have any data left, move it to the beginning of our buffer. */
|
||||||
|
memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
|
||||||
|
devc->buflen -= offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
(void)fd;
|
||||||
|
|
||||||
|
if (!(sdi = cb_data))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
|
||||||
|
if (revents == G_IO_IN) {
|
||||||
|
/* Serial data arrived. */
|
||||||
|
handle_new_data(sdi);
|
||||||
|
} else {
|
||||||
|
/* Timeout, send another packet request. */
|
||||||
|
if ((ret = brymen_packet_request(serial)) < 0) {
|
||||||
|
sr_err("Failed to request packet: %d.", ret);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sr_sw_limits_check(&devc->sw_limits))
|
||||||
|
sr_dev_acquisition_stop(sdi);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to find a valid packet in a serial data stream.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param buf Buffer containing the bytes to write.
|
||||||
|
* @param buflen Size of the buffer.
|
||||||
|
* @param get_packet_size Callback that assesses the size of incoming packets.
|
||||||
|
* @param is_valid Callback that assesses whether the packet is valid or not.
|
||||||
|
* @param timeout_ms The timeout after which, if no packet is detected, to
|
||||||
|
* abort scanning.
|
||||||
|
* @param baudrate The baudrate of the serial port. This parameter is not
|
||||||
|
* critical, but it helps fine tune the serial port polling
|
||||||
|
* delay.
|
||||||
|
*
|
||||||
|
* @return SR_OK if a valid packet is found within the given timeout,
|
||||||
|
* SR_ERR upon failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
|
||||||
|
uint8_t *buf, size_t *buflen,
|
||||||
|
packet_length_t get_packet_size,
|
||||||
|
packet_valid_callback is_valid,
|
||||||
|
uint64_t timeout_ms, int baudrate)
|
||||||
|
{
|
||||||
|
int64_t start, time, byte_delay_us;
|
||||||
|
size_t ibuf, i, maxlen;
|
||||||
|
ssize_t len, stream_len;
|
||||||
|
int packet_len;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
maxlen = *buflen;
|
||||||
|
|
||||||
|
sr_dbg("Detecting packets on %s (timeout = %" PRIu64
|
||||||
|
"ms, baudrate = %d).", serial->port, timeout_ms, baudrate);
|
||||||
|
|
||||||
|
/* Assume 8n1 transmission. That is 10 bits for every byte. */
|
||||||
|
byte_delay_us = 10 * ((1000 * 1000) / baudrate);
|
||||||
|
start = g_get_monotonic_time();
|
||||||
|
|
||||||
|
packet_len = i = ibuf = len = 0;
|
||||||
|
while (ibuf < maxlen) {
|
||||||
|
len = serial_read_nonblocking(serial, &buf[ibuf], maxlen - ibuf);
|
||||||
|
if (len > 0) {
|
||||||
|
ibuf += len;
|
||||||
|
sr_spew("Read %zd bytes.", len);
|
||||||
|
}
|
||||||
|
|
||||||
|
time = g_get_monotonic_time() - start;
|
||||||
|
time /= 1000;
|
||||||
|
|
||||||
|
stream_len = ibuf - i;
|
||||||
|
if (stream_len > 0 && packet_len == 0) {
|
||||||
|
/* How large of a packet are we expecting? */
|
||||||
|
packet_len = stream_len;
|
||||||
|
status = get_packet_size(&buf[i], &packet_len);
|
||||||
|
switch (status) {
|
||||||
|
case PACKET_HEADER_OK:
|
||||||
|
/* We know how much data we need to wait for. */
|
||||||
|
break;
|
||||||
|
case PACKET_NEED_MORE_DATA:
|
||||||
|
/* We did not receive the full header. */
|
||||||
|
packet_len = 0;
|
||||||
|
break;
|
||||||
|
case PACKET_INVALID_HEADER:
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* We had enough data, but here was an error in
|
||||||
|
* parsing the header. Restart parsing from the
|
||||||
|
* next byte.
|
||||||
|
*/
|
||||||
|
packet_len = 0;
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((stream_len >= packet_len) && (packet_len != 0)) {
|
||||||
|
/* We have at least a packet's worth of data. */
|
||||||
|
if (is_valid(&buf[i])) {
|
||||||
|
sr_spew("Found valid %d-byte packet after "
|
||||||
|
"%" PRIu64 "ms.", packet_len, time);
|
||||||
|
*buflen = ibuf;
|
||||||
|
return SR_OK;
|
||||||
|
} else {
|
||||||
|
sr_spew("Got %d bytes, but not a valid "
|
||||||
|
"packet.", packet_len);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not a valid packet. Continue searching. */
|
||||||
|
i++;
|
||||||
|
packet_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time >= (int64_t)timeout_ms) {
|
||||||
|
/* Timeout */
|
||||||
|
sr_dbg("Detection timed out after %" PRIi64 "ms.", time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_usleep(byte_delay_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
*buflen = ibuf;
|
||||||
|
sr_err("Didn't find a valid packet (read %zu bytes).", ibuf);
|
||||||
|
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBSIGROK_HARDWARE_BRYMEN_DMM_PROTOCOL_H
|
||||||
|
#define LIBSIGROK_HARDWARE_BRYMEN_DMM_PROTOCOL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <libsigrok/libsigrok.h>
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
#define LOG_PREFIX "brymen-dmm"
|
||||||
|
|
||||||
|
#define DMM_BUFSIZE 256
|
||||||
|
|
||||||
|
enum packet_len_status {
|
||||||
|
PACKET_HEADER_OK,
|
||||||
|
PACKET_NEED_MORE_DATA,
|
||||||
|
PACKET_INVALID_HEADER,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dev_context {
|
||||||
|
struct sr_sw_limits sw_limits;
|
||||||
|
|
||||||
|
uint8_t buf[DMM_BUFSIZE];
|
||||||
|
int bufoffset;
|
||||||
|
int buflen;
|
||||||
|
int next_packet_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that assesses the size and status of the incoming packet.
|
||||||
|
*
|
||||||
|
* @return PACKET_HEADER_OK - This is a proper packet header.
|
||||||
|
* PACKET_NEED_MORE_DATA The buffer does not contain the entire header.
|
||||||
|
* PACKET_INVALID_HEADER Not a valid start of packet.
|
||||||
|
*/
|
||||||
|
typedef int (*packet_length_t)(const uint8_t *buf, int *len);
|
||||||
|
|
||||||
|
SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data);
|
||||||
|
SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial);
|
||||||
|
|
||||||
|
SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len);
|
||||||
|
SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf);
|
||||||
|
|
||||||
|
SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
|
||||||
|
struct sr_datafeed_analog *analog, void *info);
|
||||||
|
|
||||||
|
SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
|
||||||
|
uint8_t *buf, size_t *buflen,
|
||||||
|
packet_length_t get_packet_size,
|
||||||
|
packet_valid_callback is_valid,
|
||||||
|
uint64_t timeout_ms, int baudrate);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -67,6 +67,8 @@ static GSList *center_scan(const char *conn, const char *serialcomm, int idx)
|
||||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
serial_flush(serial);
|
||||||
|
|
||||||
sr_info("Found device on port %s.", conn);
|
sr_info("Found device on port %s.", conn);
|
||||||
|
|
||||||
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ static void log_packet(const uint8_t *buf, int idx)
|
||||||
static int packet_parse(const uint8_t *buf, int idx, struct center_info *info)
|
static int packet_parse(const uint8_t *buf, int idx, struct center_info *info)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int16_t temp_i16;
|
uint16_t temp_u16;
|
||||||
|
|
||||||
log_packet(buf, idx);
|
log_packet(buf, idx);
|
||||||
|
|
||||||
|
|
@ -89,8 +89,9 @@ static int packet_parse(const uint8_t *buf, int idx, struct center_info *info)
|
||||||
|
|
||||||
/* Byte 7+8/9+10/11+12/13+14: channel T1/T2/T3/T4 temperature. */
|
/* Byte 7+8/9+10/11+12/13+14: channel T1/T2/T3/T4 temperature. */
|
||||||
for (i = 0; i < NUM_CHANNELS; i++) {
|
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||||
temp_i16 = RB16S(&buf[7 + 2 * i]);
|
temp_u16 = buf[8 + (i * 2)];
|
||||||
info->temp[i] = (float)temp_i16;
|
temp_u16 |= ((uint16_t)buf[7 + (i * 2)] << 8);
|
||||||
|
info->temp[i] = (float)temp_u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Byte 43: Specifies whether we need to divide the value(s) by 10. */
|
/* Byte 43: Specifies whether we need to divide the value(s) by 10. */
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,7 @@ static int dev_open(struct sr_dev_inst *sdi)
|
||||||
goto err_ftdi_free;
|
goto err_ftdi_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = ftdi_tcioflush(devc->ftdic)) < 0) {
|
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
|
||||||
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
goto err_ftdi_free;
|
goto err_ftdi_free;
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ static int close_usb_reset_sequencer(struct dev_context *devc)
|
||||||
sr_dbg("Purging buffers, resetting+closing FTDI device.");
|
sr_dbg("Purging buffers, resetting+closing FTDI device.");
|
||||||
|
|
||||||
/* Log errors, but ignore them (i.e., don't abort). */
|
/* Log errors, but ignore them (i.e., don't abort). */
|
||||||
if ((ret = ftdi_tcioflush(devc->ftdic)) < 0)
|
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0)
|
||||||
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
|
if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
|
||||||
|
|
@ -494,7 +494,9 @@ SR_PRIV void cv_send_block_to_session_bus(const struct sr_dev_inst *sdi, int blo
|
||||||
/* Send the SR_DF_TRIGGER packet to the session bus. */
|
/* Send the SR_DF_TRIGGER packet to the session bus. */
|
||||||
sr_spew("Sending SR_DF_TRIGGER packet, sample = %d.",
|
sr_spew("Sending SR_DF_TRIGGER packet, sample = %d.",
|
||||||
(block * BS) + trigger_point);
|
(block * BS) + trigger_point);
|
||||||
std_session_send_df_trigger(sdi);
|
packet.type = SR_DF_TRIGGER;
|
||||||
|
packet.payload = NULL;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
/* If at least one sample is located after the trigger... */
|
/* If at least one sample is located after the trigger... */
|
||||||
if (trigger_point < (BS - 1)) {
|
if (trigger_point < (BS - 1)) {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
serial_flush(serial);
|
||||||
serial_close(serial);
|
serial_close(serial);
|
||||||
|
|
||||||
sr_spew("Conrad DIGI 35 CPU assumed at %s.", conn);
|
sr_spew("Conrad DIGI 35 CPU assumed at %s.", conn);
|
||||||
|
|
@ -88,6 +89,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
sdi->inst_type = SR_INST_SERIAL;
|
sdi->inst_type = SR_INST_SERIAL;
|
||||||
sdi->conn = serial;
|
sdi->conn = serial;
|
||||||
sdi->priv = devc;
|
sdi->priv = devc;
|
||||||
|
sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
|
||||||
|
|
||||||
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,454 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@gmx.net>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <hidapi.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "protocol.h"
|
|
||||||
|
|
||||||
static const uint32_t scanopts[] = {
|
|
||||||
SR_CONF_CONN,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t drvopts[] = {
|
|
||||||
SR_CONF_MULTIPLEXER,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t devopts[] = {
|
|
||||||
SR_CONF_CONN | SR_CONF_GET,
|
|
||||||
SR_CONF_ENABLED | SR_CONF_SET, /* Enable/disable all relays at once. */
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t devopts_cg[] = {
|
|
||||||
SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct sr_dev_driver dcttech_usbrelay_driver_info;
|
|
||||||
|
|
||||||
static struct sr_dev_inst *probe_device_common(const char *path,
|
|
||||||
uint16_t vid, uint16_t pid,
|
|
||||||
const wchar_t *vendor, const wchar_t *product)
|
|
||||||
{
|
|
||||||
char nonws[16], *s, *endp;
|
|
||||||
unsigned long relay_count;
|
|
||||||
hid_device *hid;
|
|
||||||
int ret;
|
|
||||||
char serno[SERNO_LENGTH + 1];
|
|
||||||
uint8_t curr_state;
|
|
||||||
uint8_t report[1 + REPORT_BYTECOUNT];
|
|
||||||
GString *txt;
|
|
||||||
size_t snr_pos;
|
|
||||||
char c;
|
|
||||||
struct sr_dev_inst *sdi;
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct channel_group_context *cgc;
|
|
||||||
size_t idx, nr;
|
|
||||||
struct sr_channel_group *cg;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get relay count from product string. Weak condition,
|
|
||||||
* accept any trailing number regardless of preceeding text.
|
|
||||||
*/
|
|
||||||
snprintf(nonws, sizeof(nonws), "%ls", product);
|
|
||||||
s = nonws;
|
|
||||||
s += strlen(s);
|
|
||||||
while (s > nonws && isdigit((int)s[-1]))
|
|
||||||
s--;
|
|
||||||
ret = sr_atoul_base(s, &relay_count, &endp, 10);
|
|
||||||
if (ret != SR_OK || !endp || *endp)
|
|
||||||
return NULL;
|
|
||||||
if (!relay_count)
|
|
||||||
return NULL;
|
|
||||||
sr_info("Relay count %lu from product string %s.", relay_count, nonws);
|
|
||||||
|
|
||||||
/* Open device, need to communicate to identify. */
|
|
||||||
if (vid && pid)
|
|
||||||
hid = hid_open(vid, pid, NULL);
|
|
||||||
else
|
|
||||||
hid = hid_open_path(path);
|
|
||||||
if (!hid) {
|
|
||||||
sr_err("Cannot open %s.", path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get an HID report. */
|
|
||||||
hid_set_nonblocking(hid, 0);
|
|
||||||
memset(&report, 0, sizeof(report));
|
|
||||||
report[0] = REPORT_NUMBER;
|
|
||||||
ret = hid_get_feature_report(hid, report, sizeof(report));
|
|
||||||
hid_close(hid);
|
|
||||||
if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
|
|
||||||
txt = sr_hexdump_new(report, sizeof(report));
|
|
||||||
sr_spew("Got report bytes: %s, rc %d.", txt->str, ret);
|
|
||||||
sr_hexdump_free(txt);
|
|
||||||
}
|
|
||||||
if (ret < 0) {
|
|
||||||
sr_err("Cannot read %s: %ls.", path, hid_error(NULL));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (ret != sizeof(report)) {
|
|
||||||
sr_err("Unexpected HID report length %d from %s.", ret, path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Serial number must be all printable characters. Relay state
|
|
||||||
* is for information only, gets re-retrieved before configure
|
|
||||||
* API calls (get/set).
|
|
||||||
*/
|
|
||||||
memset(serno, 0, sizeof(serno));
|
|
||||||
for (snr_pos = 0; snr_pos < SERNO_LENGTH; snr_pos++) {
|
|
||||||
c = report[1 + snr_pos];
|
|
||||||
serno[snr_pos] = c;
|
|
||||||
if (c < 0x20 || c > 0x7e) {
|
|
||||||
sr_warn("Skipping %s, non-printable serial.", path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
curr_state = report[1 + STATE_INDEX];
|
|
||||||
sr_info("HID report data: serial number %s, relay state 0x%02x.",
|
|
||||||
serno, curr_state);
|
|
||||||
|
|
||||||
/* Create a device instance. */
|
|
||||||
sdi = g_malloc0(sizeof(*sdi));
|
|
||||||
sdi->vendor = g_strdup_printf("%ls", vendor);
|
|
||||||
sdi->model = g_strdup_printf("%ls", product);
|
|
||||||
sdi->serial_num = g_strdup(serno);
|
|
||||||
sdi->connection_id = g_strdup(path);
|
|
||||||
sdi->driver = &dcttech_usbrelay_driver_info;
|
|
||||||
sdi->inst_type = SR_INST_USB;
|
|
||||||
|
|
||||||
/* Create channels (groups). */
|
|
||||||
devc = g_malloc0(sizeof(*devc));
|
|
||||||
sdi->priv = devc;
|
|
||||||
devc->hid_path = g_strdup(path);
|
|
||||||
devc->usb_vid = vid;
|
|
||||||
devc->usb_pid = pid;
|
|
||||||
devc->relay_count = relay_count;
|
|
||||||
devc->relay_mask = (1U << relay_count) - 1;
|
|
||||||
for (idx = 0; idx < devc->relay_count; idx++) {
|
|
||||||
nr = idx + 1;
|
|
||||||
cg = g_malloc0(sizeof(*cg));
|
|
||||||
cg->name = g_strdup_printf("R%zu", nr);
|
|
||||||
cgc = g_malloc0(sizeof(*cgc));
|
|
||||||
cg->priv = cgc;
|
|
||||||
cgc->number = nr;
|
|
||||||
sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sdi;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sr_dev_inst *probe_device_enum(struct hid_device_info *dev)
|
|
||||||
{
|
|
||||||
return probe_device_common(dev->path, 0, 0,
|
|
||||||
dev->manufacturer_string, dev->product_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sr_dev_inst *probe_device_conn(const char *path)
|
|
||||||
{
|
|
||||||
char vid_pid[12];
|
|
||||||
uint16_t vid, pid;
|
|
||||||
const char *s;
|
|
||||||
char *endp;
|
|
||||||
unsigned long num;
|
|
||||||
hid_device *dev;
|
|
||||||
gboolean ok;
|
|
||||||
int ret;
|
|
||||||
wchar_t vendor[32], product[32];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The hidapi(3) library's API strives for maximum portability,
|
|
||||||
* thus won't provide ways of getting a path from alternative
|
|
||||||
* presentations like VID:PID pairs, bus.addr specs, etc. The
|
|
||||||
* typical V-USB setup neither provides reliable serial numbers
|
|
||||||
* (that USB enumeration would cover). So this driver's support
|
|
||||||
* for conn= specs beyond Unix style path names is limited, too.
|
|
||||||
* This implementation tries "VID.PID" then assumes "path". The
|
|
||||||
* inability to even get the path for a successfully opened HID
|
|
||||||
* results in redundancy across the places which open devices.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Check for "<vid>.<pid>" specs. */
|
|
||||||
vid = pid = 0;
|
|
||||||
s = path;
|
|
||||||
ret = sr_atoul_base(s, &num, &endp, 16);
|
|
||||||
if (ret == SR_OK && endp && endp == s + 4 && *endp == '.' && num) {
|
|
||||||
vid = num;
|
|
||||||
s = ++endp;
|
|
||||||
}
|
|
||||||
ret = sr_atoul_base(s, &num, &endp, 16);
|
|
||||||
if (ret == SR_OK && endp && endp == s + 4 && *endp == '\0' && num) {
|
|
||||||
pid = num;
|
|
||||||
s = ++endp;
|
|
||||||
}
|
|
||||||
if (vid && pid) {
|
|
||||||
snprintf(vid_pid, sizeof(vid_pid), "%04x.%04x", vid, pid);
|
|
||||||
path = vid_pid;
|
|
||||||
sr_dbg("Using VID.PID %s.", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open the device, get vendor and product strings. */
|
|
||||||
if (vid && pid)
|
|
||||||
dev = hid_open(vid, pid, NULL);
|
|
||||||
else
|
|
||||||
dev = hid_open_path(path);
|
|
||||||
if (!dev) {
|
|
||||||
sr_err("Cannot open %s.", path);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ok = TRUE;
|
|
||||||
ret = hid_get_manufacturer_string(dev, vendor, ARRAY_SIZE(vendor));
|
|
||||||
if (ret != 0)
|
|
||||||
ok = FALSE;
|
|
||||||
if (!wcslen(vendor))
|
|
||||||
ok = FALSE;
|
|
||||||
ret = hid_get_product_string(dev, product, ARRAY_SIZE(product));
|
|
||||||
if (ret != 0)
|
|
||||||
ok = FALSE;
|
|
||||||
if (!wcslen(product))
|
|
||||||
ok = FALSE;
|
|
||||||
hid_close(dev);
|
|
||||||
if (!ok)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return probe_device_common(path, vid, pid, vendor, product);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|
||||||
{
|
|
||||||
const char *conn;
|
|
||||||
GSList *devices;
|
|
||||||
struct drv_context *drvc;
|
|
||||||
struct hid_device_info *devs, *curdev;
|
|
||||||
wchar_t *ws;
|
|
||||||
char nonws[32];
|
|
||||||
struct sr_dev_inst *sdi;
|
|
||||||
|
|
||||||
/* Get optional conn= spec when provided. */
|
|
||||||
conn = NULL;
|
|
||||||
(void)sr_serial_extract_options(options, &conn, NULL);
|
|
||||||
if (conn && !*conn)
|
|
||||||
conn = NULL;
|
|
||||||
|
|
||||||
devices = NULL;
|
|
||||||
drvc = di->context;
|
|
||||||
drvc->instances = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The firmware is V-USB based. The USB VID:PID identification
|
|
||||||
* is shared across several projects. Need to inspect the vendor
|
|
||||||
* and product _strings_ to actually identify the device. The
|
|
||||||
* USB serial number need not be present nor reliable. The HID
|
|
||||||
* report content will carry the board's serial number.
|
|
||||||
*
|
|
||||||
* When conn= was specified, then have HIDAPI open _this_ device
|
|
||||||
* and skip the enumeration. Which allows users to specify paths
|
|
||||||
* that need not match the enumeration's details.
|
|
||||||
*/
|
|
||||||
if (conn) {
|
|
||||||
sr_info("Checking HID path %s.", conn);
|
|
||||||
sdi = probe_device_conn(conn);
|
|
||||||
if (!sdi)
|
|
||||||
sr_warn("Failed to communicate to %s.", conn);
|
|
||||||
else
|
|
||||||
devices = g_slist_append(devices, sdi);
|
|
||||||
}
|
|
||||||
devs = hid_enumerate(VENDOR_ID, PRODUCT_ID);
|
|
||||||
for (curdev = devs; curdev; curdev = curdev->next) {
|
|
||||||
if (conn)
|
|
||||||
break;
|
|
||||||
if (!curdev->vendor_id || !curdev->product_id)
|
|
||||||
continue;
|
|
||||||
if (!curdev->manufacturer_string || !curdev->product_string)
|
|
||||||
continue;
|
|
||||||
if (!*curdev->manufacturer_string || !*curdev->product_string)
|
|
||||||
continue;
|
|
||||||
sr_dbg("Checking %04hx:%04hx, vendor %ls, product %ls.",
|
|
||||||
curdev->vendor_id, curdev->product_id,
|
|
||||||
curdev->manufacturer_string, curdev->product_string);
|
|
||||||
|
|
||||||
/* Check USB details retrieved by enumeration. */
|
|
||||||
ws = curdev->manufacturer_string;
|
|
||||||
if (!ws || !wcslen(ws))
|
|
||||||
continue;
|
|
||||||
snprintf(nonws, sizeof(nonws), "%ls", ws);
|
|
||||||
if (strcmp(nonws, VENDOR_STRING) != 0)
|
|
||||||
continue;
|
|
||||||
ws = curdev->product_string;
|
|
||||||
if (!ws || !wcslen(ws))
|
|
||||||
continue;
|
|
||||||
snprintf(nonws, sizeof(nonws), "%ls", ws);
|
|
||||||
if (!g_str_has_prefix(nonws, PRODUCT_STRING_PREFIX))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Identify device by communicating to it. */
|
|
||||||
sr_info("Checking HID path %s.", curdev->path);
|
|
||||||
sdi = probe_device_enum(curdev);
|
|
||||||
if (!sdi) {
|
|
||||||
sr_warn("Failed to communicate to %s.", curdev->path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
devices = g_slist_append(devices, sdi);
|
|
||||||
}
|
|
||||||
hid_free_enumeration(devs);
|
|
||||||
|
|
||||||
return devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_open(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
|
|
||||||
if (devc->hid_dev) {
|
|
||||||
hid_close(devc->hid_dev);
|
|
||||||
devc->hid_dev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (devc->usb_vid && devc->usb_pid)
|
|
||||||
devc->hid_dev = hid_open(devc->usb_vid, devc->usb_pid, NULL);
|
|
||||||
else
|
|
||||||
devc->hid_dev = hid_open_path(devc->hid_path);
|
|
||||||
if (!devc->hid_dev)
|
|
||||||
return SR_ERR_IO;
|
|
||||||
|
|
||||||
(void)dcttech_usbrelay_update_state(sdi);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_close(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
|
|
||||||
if (devc->hid_dev) {
|
|
||||||
hid_close(devc->hid_dev);
|
|
||||||
devc->hid_dev = 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)
|
|
||||||
{
|
|
||||||
gboolean on;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!cg) {
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_CONN:
|
|
||||||
if (!sdi->connection_id)
|
|
||||||
return SR_ERR_NA;
|
|
||||||
*data = g_variant_new_string(sdi->connection_id);
|
|
||||||
return SR_OK;
|
|
||||||
default:
|
|
||||||
return SR_ERR_NA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_ENABLED:
|
|
||||||
ret = dcttech_usbrelay_query_cg(sdi, cg, &on);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
return ret;
|
|
||||||
*data = g_variant_new_boolean(on);
|
|
||||||
return SR_OK;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
gboolean on;
|
|
||||||
|
|
||||||
if (!cg) {
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_ENABLED:
|
|
||||||
/* Enable/disable all channels at the same time. */
|
|
||||||
on = g_variant_get_boolean(data);
|
|
||||||
return dcttech_usbrelay_switch_cg(sdi, cg, on);
|
|
||||||
default:
|
|
||||||
return SR_ERR_NA;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_ENABLED:
|
|
||||||
on = g_variant_get_boolean(data);
|
|
||||||
return dcttech_usbrelay_switch_cg(sdi, cg, on);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
|
|
||||||
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);
|
|
||||||
default:
|
|
||||||
return SR_ERR_NA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_DEVICE_OPTIONS:
|
|
||||||
*data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
|
|
||||||
return SR_OK;
|
|
||||||
default:
|
|
||||||
return SR_ERR_NA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sr_dev_driver dcttech_usbrelay_driver_info = {
|
|
||||||
.name = "dcttech-usbrelay",
|
|
||||||
.longname = "dcttech usbrelay",
|
|
||||||
.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 = std_dummy_dev_acquisition_start,
|
|
||||||
.dev_acquisition_stop = std_dummy_dev_acquisition_stop,
|
|
||||||
.context = NULL,
|
|
||||||
};
|
|
||||||
SR_REGISTER_DEV_DRIVER(dcttech_usbrelay_driver_info);
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@gmx.net>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "protocol.h"
|
|
||||||
|
|
||||||
SR_PRIV int dcttech_usbrelay_update_state(const struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
uint8_t report[1 + REPORT_BYTECOUNT];
|
|
||||||
int ret;
|
|
||||||
GString *txt;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
|
|
||||||
/* Get another HID report. */
|
|
||||||
memset(report, 0, sizeof(report));
|
|
||||||
report[0] = REPORT_NUMBER;
|
|
||||||
ret = hid_get_feature_report(devc->hid_dev, report, sizeof(report));
|
|
||||||
if (ret != sizeof(report))
|
|
||||||
return SR_ERR_IO;
|
|
||||||
if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
|
|
||||||
txt = sr_hexdump_new(report, sizeof(report));
|
|
||||||
sr_spew("Got report bytes: %s.", txt->str);
|
|
||||||
sr_hexdump_free(txt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update relay state cache from HID report content. */
|
|
||||||
devc->relay_state = report[1 + STATE_INDEX];
|
|
||||||
devc->relay_state &= devc->relay_mask;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int dcttech_usbrelay_switch_cg(const struct sr_dev_inst *sdi,
|
|
||||||
const struct sr_channel_group *cg, gboolean on)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct channel_group_context *cgc;
|
|
||||||
gboolean is_all;
|
|
||||||
size_t relay_idx;
|
|
||||||
uint8_t report[1 + REPORT_BYTECOUNT];
|
|
||||||
int ret;
|
|
||||||
GString *txt;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
|
|
||||||
/* Determine if all or a single relay should be turned off or on. */
|
|
||||||
is_all = !cg ? TRUE : FALSE;
|
|
||||||
if (is_all) {
|
|
||||||
relay_idx = 0;
|
|
||||||
} else {
|
|
||||||
cgc = cg->priv;
|
|
||||||
relay_idx = cgc->number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Construct and send the HID report. Notice the weird(?) bit
|
|
||||||
* pattern. Bit 1 is low when all relays are affected at once,
|
|
||||||
* and high to control an individual relay? Bit 0 communicates
|
|
||||||
* whether the relay(s) should be on or off? And all other bits
|
|
||||||
* are always set? It's assumed that the explicit assignment of
|
|
||||||
* full byte values simplifies future maintenance.
|
|
||||||
*/
|
|
||||||
memset(report, 0, sizeof(report));
|
|
||||||
report[0] = REPORT_NUMBER;
|
|
||||||
if (is_all) {
|
|
||||||
if (on) {
|
|
||||||
report[1] = 0xfe;
|
|
||||||
} else {
|
|
||||||
report[1] = 0xfc;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (on) {
|
|
||||||
report[1] = 0xff;
|
|
||||||
report[2] = relay_idx;
|
|
||||||
} else {
|
|
||||||
report[1] = 0xfd;
|
|
||||||
report[2] = relay_idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
|
|
||||||
txt = sr_hexdump_new(report, sizeof(report));
|
|
||||||
sr_spew("Sending report bytes: %s", txt->str);
|
|
||||||
sr_hexdump_free(txt);
|
|
||||||
}
|
|
||||||
ret = hid_send_feature_report(devc->hid_dev, report, sizeof(report));
|
|
||||||
if (ret != sizeof(report))
|
|
||||||
return SR_ERR_IO;
|
|
||||||
|
|
||||||
/* Update relay state cache (non-fatal). */
|
|
||||||
(void)dcttech_usbrelay_update_state(sdi);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Answers the query from cached relay state. Beware of 1-based indexing. */
|
|
||||||
SR_PRIV int dcttech_usbrelay_query_cg(const struct sr_dev_inst *sdi,
|
|
||||||
const struct sr_channel_group *cg, gboolean *on)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct channel_group_context *cgc;
|
|
||||||
size_t relay_idx;
|
|
||||||
uint32_t relay_mask;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
if (!cg)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
cgc = cg->priv;
|
|
||||||
relay_idx = cgc->number;
|
|
||||||
if (relay_idx < 1 || relay_idx > devc->relay_count)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
relay_mask = 1U << (relay_idx - 1);
|
|
||||||
|
|
||||||
*on = devc->relay_state & relay_mask;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@gmx.net>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef LIBSIGROK_HARDWARE_DCTTECH_USBRELAY_PROTOCOL_H
|
|
||||||
#define LIBSIGROK_HARDWARE_DCTTECH_USBRELAY_PROTOCOL_H
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <hidapi.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
|
|
||||||
#define LOG_PREFIX "dcttech-usbrelay"
|
|
||||||
|
|
||||||
/* USB identification. */
|
|
||||||
#define VENDOR_ID 0x16c0
|
|
||||||
#define PRODUCT_ID 0x05df
|
|
||||||
#define VENDOR_STRING "www.dcttech.com"
|
|
||||||
#define PRODUCT_STRING_PREFIX "USBRelay"
|
|
||||||
|
|
||||||
/* HID report layout. */
|
|
||||||
#define REPORT_NUMBER 0
|
|
||||||
#define REPORT_BYTECOUNT 8
|
|
||||||
#define SERNO_LENGTH 5
|
|
||||||
#define STATE_INDEX 7
|
|
||||||
|
|
||||||
struct dev_context {
|
|
||||||
char *hid_path;
|
|
||||||
uint16_t usb_vid, usb_pid;
|
|
||||||
hid_device *hid_dev;
|
|
||||||
size_t relay_count;
|
|
||||||
uint32_t relay_mask;
|
|
||||||
uint32_t relay_state;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct channel_group_context {
|
|
||||||
size_t number;
|
|
||||||
};
|
|
||||||
|
|
||||||
SR_PRIV int dcttech_usbrelay_update_state(const struct sr_dev_inst *sdi);
|
|
||||||
SR_PRIV int dcttech_usbrelay_switch_cg(const struct sr_dev_inst *sdi,
|
|
||||||
const struct sr_channel_group *cg, gboolean on);
|
|
||||||
SR_PRIV int dcttech_usbrelay_query_cg(const struct sr_dev_inst *sdi,
|
|
||||||
const struct sr_channel_group *cg, gboolean *on);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -573,7 +573,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
||||||
std_session_send_df_header(sdi);
|
std_session_send_df_header(sdi);
|
||||||
|
|
||||||
if (devc->limit_frames > 0)
|
if (devc->limit_frames > 0)
|
||||||
std_session_send_df_frame_begin(sdi);
|
std_session_send_frame_begin(sdi);
|
||||||
|
|
||||||
/* We use this timestamp to decide how many more samples to send. */
|
/* We use this timestamp to decide how many more samples to send. */
|
||||||
devc->start_us = g_get_monotonic_time();
|
devc->start_us = g_get_monotonic_time();
|
||||||
|
|
@ -591,7 +591,7 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
if (devc->limit_frames > 0)
|
if (devc->limit_frames > 0)
|
||||||
std_session_send_df_frame_end(sdi);
|
std_session_send_frame_end(sdi);
|
||||||
|
|
||||||
std_session_send_df_end(sdi);
|
std_session_send_df_end(sdi);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -714,7 +714,7 @@ SR_PRIV int demo_prepare_data(int fd, int revents, void *cb_data)
|
||||||
devc->spent_us += todo_us;
|
devc->spent_us += todo_us;
|
||||||
|
|
||||||
if (devc->limit_frames && devc->sent_frame_samples >= SAMPLES_PER_FRAME) {
|
if (devc->limit_frames && devc->sent_frame_samples >= SAMPLES_PER_FRAME) {
|
||||||
std_session_send_df_frame_end(sdi);
|
std_session_send_frame_end(sdi);
|
||||||
devc->sent_frame_samples = 0;
|
devc->sent_frame_samples = 0;
|
||||||
devc->limit_frames--;
|
devc->limit_frames--;
|
||||||
if (!devc->limit_frames) {
|
if (!devc->limit_frames) {
|
||||||
|
|
@ -742,7 +742,7 @@ SR_PRIV int demo_prepare_data(int fd, int revents, void *cb_data)
|
||||||
sr_dev_acquisition_stop(sdi);
|
sr_dev_acquisition_stop(sdi);
|
||||||
} else if (devc->limit_frames) {
|
} else if (devc->limit_frames) {
|
||||||
if (devc->sent_frame_samples == 0)
|
if (devc->sent_frame_samples == 0)
|
||||||
std_session_send_df_frame_begin(sdi);
|
std_session_send_frame_begin(sdi);
|
||||||
}
|
}
|
||||||
|
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
|
|
|
||||||
|
|
@ -776,6 +776,7 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
||||||
(DSLOGIC_ATOMIC_BYTES * channel_count);
|
(DSLOGIC_ATOMIC_BYTES * channel_count);
|
||||||
|
|
||||||
gboolean packet_has_error = FALSE;
|
gboolean packet_has_error = FALSE;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
unsigned int num_samples;
|
unsigned int num_samples;
|
||||||
int trigger_offset;
|
int trigger_offset;
|
||||||
|
|
||||||
|
|
@ -855,7 +856,9 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
||||||
devc->sent_samples += trigger_offset;
|
devc->sent_samples += trigger_offset;
|
||||||
/* Trigger position. */
|
/* Trigger position. */
|
||||||
devc->trigger_pos = 0;
|
devc->trigger_pos = 0;
|
||||||
std_session_send_df_trigger(sdi);
|
packet.type = SR_DF_TRIGGER;
|
||||||
|
packet.payload = NULL;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
/* Post trigger samples. */
|
/* Post trigger samples. */
|
||||||
num_samples -= trigger_offset;
|
num_samples -= trigger_offset;
|
||||||
send_data(sdi, devc->deinterleave_buffer
|
send_data(sdi, devc->deinterleave_buffer
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@
|
||||||
#include "scpi.h"
|
#include "scpi.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test violates the SCPI protocol, and confuses other devices.
|
||||||
|
* Disable it for now, until a better location was found.
|
||||||
|
*/
|
||||||
|
#define ECHO_TEST 0
|
||||||
|
|
||||||
static const uint32_t scanopts[] = {
|
static const uint32_t scanopts[] = {
|
||||||
SR_CONF_CONN,
|
SR_CONF_CONN,
|
||||||
SR_CONF_SERIALCOMM,
|
SR_CONF_SERIALCOMM,
|
||||||
|
|
@ -62,15 +68,26 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
const struct fluke_scpi_dmm_model *model = NULL;
|
const struct fluke_scpi_dmm_model *model = NULL;
|
||||||
gchar *channel_name;
|
gchar *channel_name;
|
||||||
|
#if ECHO_TEST
|
||||||
|
char *response;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ECHO_TEST
|
||||||
|
/* Test for serial port ECHO enabled. */
|
||||||
|
response = NULL;
|
||||||
|
sr_scpi_get_string(scpi, "ECHO-TEST", &response);
|
||||||
|
if (response && strcmp(response, "ECHO-TEST") == 0) {
|
||||||
|
sr_err("Serial port ECHO is ON. Please turn it OFF!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Get device IDN. */
|
/* Get device IDN. */
|
||||||
if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
|
if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
|
||||||
sr_scpi_hw_info_free(hw_info);
|
|
||||||
sr_info("Couldn't get IDN response, retrying.");
|
sr_info("Couldn't get IDN response, retrying.");
|
||||||
sr_scpi_close(scpi);
|
sr_scpi_close(scpi);
|
||||||
sr_scpi_open(scpi);
|
sr_scpi_open(scpi);
|
||||||
if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
|
if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
|
||||||
sr_scpi_hw_info_free(hw_info);
|
|
||||||
sr_info("Couldn't get IDN response.");
|
sr_info("Couldn't get IDN response.");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,6 @@ SR_PRIV int fl45_scpi_get_response(const struct sr_dev_inst *sdi, char *cmd)
|
||||||
* If the response is a prompt then ignore and read the next
|
* If the response is a prompt then ignore and read the next
|
||||||
* response in the buffer.
|
* response in the buffer.
|
||||||
*/
|
*/
|
||||||
g_free(devc->response);
|
|
||||||
devc->response = NULL;
|
devc->response = NULL;
|
||||||
/* Now attempt to read again. */
|
/* Now attempt to read again. */
|
||||||
if (sr_scpi_get_string(sdi->conn, NULL, &devc->response) != SR_OK)
|
if (sr_scpi_get_string(sdi->conn, NULL, &devc->response) != SR_OK)
|
||||||
|
|
@ -340,10 +339,9 @@ SR_PRIV int fl45_scpi_get_response(const struct sr_dev_inst *sdi, char *cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NULL RS232 error prompts. */
|
/* NULL RS232 error prompts. */
|
||||||
if (strcmp(devc->response, "!>") == 0 ||
|
if (strcmp(devc->response, "!>") == 0
|
||||||
(strcmp(devc->response, "?>") == 0)) {
|
|| (strcmp(devc->response, "?>") == 0)) {
|
||||||
/* Unable to execute CMD. */
|
/* Unable to execute CMD. */
|
||||||
g_free(devc->response);
|
|
||||||
devc->response = NULL;
|
devc->response = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,28 +57,6 @@ static const struct ftdi_chip_desc ft2232h_desc = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct ftdi_chip_desc ft2232h_tumpa_desc = {
|
|
||||||
.vendor = 0x0403,
|
|
||||||
.product = 0x8a98,
|
|
||||||
.samplerate_div = 20,
|
|
||||||
/* 20 pin JTAG header */
|
|
||||||
.channel_names = {
|
|
||||||
"TCK", "TDI", "TDO", "TMS", "RST", "nTRST", "DBGRQ", "RTCK",
|
|
||||||
NULL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct ftdi_chip_desc ft4232h_desc = {
|
|
||||||
.vendor = 0x0403,
|
|
||||||
.product = 0x6011,
|
|
||||||
.samplerate_div = 20,
|
|
||||||
.channel_names = {
|
|
||||||
"ADBUS0", "ADBUS1", "ADBUS2", "ADBUS3", "ADBUS4", "ADBUS5", "ADBUS6", "ADBUS7",
|
|
||||||
/* TODO: BDBUS[0..7], CDBUS[0..7], DDBUS[0..7] channels. */
|
|
||||||
NULL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct ftdi_chip_desc ft232r_desc = {
|
static const struct ftdi_chip_desc ft232r_desc = {
|
||||||
.vendor = 0x0403,
|
.vendor = 0x0403,
|
||||||
.product = 0x6001,
|
.product = 0x6001,
|
||||||
|
|
@ -101,8 +79,6 @@ static const struct ftdi_chip_desc ft232h_desc = {
|
||||||
|
|
||||||
static const struct ftdi_chip_desc *chip_descs[] = {
|
static const struct ftdi_chip_desc *chip_descs[] = {
|
||||||
&ft2232h_desc,
|
&ft2232h_desc,
|
||||||
&ft2232h_tumpa_desc,
|
|
||||||
&ft4232h_desc,
|
|
||||||
&ft232r_desc,
|
&ft232r_desc,
|
||||||
&ft232h_desc,
|
&ft232h_desc,
|
||||||
NULL,
|
NULL,
|
||||||
|
|
@ -111,8 +87,6 @@ static const struct ftdi_chip_desc *chip_descs[] = {
|
||||||
static void scan_device(struct ftdi_context *ftdic,
|
static void scan_device(struct ftdi_context *ftdic,
|
||||||
struct libusb_device *dev, GSList **devices)
|
struct libusb_device *dev, GSList **devices)
|
||||||
{
|
{
|
||||||
static const int usb_str_maxlen = 32;
|
|
||||||
|
|
||||||
struct libusb_device_descriptor usb_desc;
|
struct libusb_device_descriptor usb_desc;
|
||||||
const struct ftdi_chip_desc *desc;
|
const struct ftdi_chip_desc *desc;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
|
|
@ -145,42 +119,14 @@ static void scan_device(struct ftdi_context *ftdic,
|
||||||
|
|
||||||
devc->desc = desc;
|
devc->desc = desc;
|
||||||
|
|
||||||
vendor = g_malloc(usb_str_maxlen);
|
vendor = g_malloc(32);
|
||||||
model = g_malloc(usb_str_maxlen);
|
model = g_malloc(32);
|
||||||
serial_num = g_malloc(usb_str_maxlen);
|
serial_num = g_malloc(32);
|
||||||
rv = ftdi_usb_get_strings(ftdic, dev, vendor, usb_str_maxlen,
|
rv = ftdi_usb_get_strings(ftdic, dev, vendor, 32,
|
||||||
model, usb_str_maxlen, serial_num, usb_str_maxlen);
|
model, 32, serial_num, 32);
|
||||||
switch (rv) {
|
switch (rv) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
/* ftdi_usb_get_strings() fails on first miss, hence fall through. */
|
|
||||||
case -7:
|
|
||||||
sr_dbg("The device lacks a manufacturer descriptor.");
|
|
||||||
g_snprintf(vendor, usb_str_maxlen, "Generic");
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case -8:
|
|
||||||
sr_dbg("The device lacks a product descriptor.");
|
|
||||||
switch (usb_desc.idProduct) {
|
|
||||||
case 0x6001:
|
|
||||||
g_snprintf(model, usb_str_maxlen, "FT232R");
|
|
||||||
break;
|
|
||||||
case 0x6010:
|
|
||||||
g_snprintf(model, usb_str_maxlen, "FT2232H");
|
|
||||||
break;
|
|
||||||
case 0x6011:
|
|
||||||
g_snprintf(model, usb_str_maxlen, "FT4232H");
|
|
||||||
break;
|
|
||||||
case 0x6014:
|
|
||||||
g_snprintf(model, usb_str_maxlen, "FT232H");
|
|
||||||
break;
|
|
||||||
case 0x8a98:
|
|
||||||
g_snprintf(model, usb_str_maxlen, "FT2232H-TUMPA");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
g_snprintf(model, usb_str_maxlen, "Unknown");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case -9:
|
case -9:
|
||||||
sr_dbg("The device lacks a serial number.");
|
sr_dbg("The device lacks a serial number.");
|
||||||
g_free(serial_num);
|
g_free(serial_num);
|
||||||
|
|
@ -325,7 +271,7 @@ static int dev_open(struct sr_dev_inst *sdi)
|
||||||
goto err_ftdi_free;
|
goto err_ftdi_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ftdi_tcioflush(devc->ftdic);
|
ret = ftdi_usb_purge_buffers(devc->ftdic);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
|
sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,6 @@ static const uint32_t drvopts[] = {
|
||||||
|
|
||||||
static const uint32_t devopts[] = {
|
static const uint32_t devopts[] = {
|
||||||
SR_CONF_CONTINUOUS,
|
SR_CONF_CONTINUOUS,
|
||||||
SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
|
|
||||||
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
|
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
|
||||||
SR_CONF_CONN | SR_CONF_GET,
|
SR_CONF_CONN | SR_CONF_GET,
|
||||||
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||||
|
|
@ -492,9 +491,6 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
*data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
|
*data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
|
||||||
break;
|
break;
|
||||||
case SR_CONF_LIMIT_FRAMES:
|
|
||||||
*data = g_variant_new_uint64(devc->limit_frames);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
*data = g_variant_new_uint64(devc->limit_samples);
|
*data = g_variant_new_uint64(devc->limit_samples);
|
||||||
break;
|
break;
|
||||||
|
|
@ -530,9 +526,6 @@ static int config_set(uint32_t key, GVariant *data,
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
devc->cur_samplerate = devc->samplerates[idx];
|
devc->cur_samplerate = devc->samplerates[idx];
|
||||||
break;
|
break;
|
||||||
case SR_CONF_LIMIT_FRAMES:
|
|
||||||
devc->limit_frames = g_variant_get_uint64(data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
devc->limit_samples = g_variant_get_uint64(data);
|
devc->limit_samples = g_variant_get_uint64(data);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -257,11 +257,9 @@ SR_PRIV struct dev_context *fx2lafw_dev_new(void)
|
||||||
devc->profile = NULL;
|
devc->profile = NULL;
|
||||||
devc->fw_updated = 0;
|
devc->fw_updated = 0;
|
||||||
devc->cur_samplerate = 0;
|
devc->cur_samplerate = 0;
|
||||||
devc->limit_frames = 1;
|
|
||||||
devc->limit_samples = 0;
|
devc->limit_samples = 0;
|
||||||
devc->capture_ratio = 0;
|
devc->capture_ratio = 0;
|
||||||
devc->sample_wide = FALSE;
|
devc->sample_wide = FALSE;
|
||||||
devc->num_frames = 0;
|
|
||||||
devc->stl = NULL;
|
devc->stl = NULL;
|
||||||
|
|
||||||
return devc;
|
return devc;
|
||||||
|
|
@ -416,7 +414,7 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
gboolean packet_has_error = FALSE;
|
gboolean packet_has_error = FALSE;
|
||||||
unsigned int num_samples;
|
unsigned int num_samples;
|
||||||
int trigger_offset, cur_sample_count, unitsize, processed_samples;
|
int trigger_offset, cur_sample_count, unitsize;
|
||||||
int pre_trigger_samples;
|
int pre_trigger_samples;
|
||||||
|
|
||||||
sdi = transfer->user_data;
|
sdi = transfer->user_data;
|
||||||
|
|
@ -437,7 +435,6 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
||||||
/* Save incoming transfer before reusing the transfer struct. */
|
/* Save incoming transfer before reusing the transfer struct. */
|
||||||
unitsize = devc->sample_wide ? 2 : 1;
|
unitsize = devc->sample_wide ? 2 : 1;
|
||||||
cur_sample_count = transfer->actual_length / unitsize;
|
cur_sample_count = transfer->actual_length / unitsize;
|
||||||
processed_samples = 0;
|
|
||||||
|
|
||||||
switch (transfer->status) {
|
switch (transfer->status) {
|
||||||
case LIBUSB_TRANSFER_NO_DEVICE:
|
case LIBUSB_TRANSFER_NO_DEVICE:
|
||||||
|
|
@ -468,67 +465,38 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
||||||
} else {
|
} else {
|
||||||
devc->empty_transfer_count = 0;
|
devc->empty_transfer_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_trigger:
|
|
||||||
if (devc->trigger_fired) {
|
if (devc->trigger_fired) {
|
||||||
if (!devc->limit_samples || devc->sent_samples < devc->limit_samples) {
|
if (!devc->limit_samples || devc->sent_samples < devc->limit_samples) {
|
||||||
/* Send the incoming transfer to the session bus. */
|
/* Send the incoming transfer to the session bus. */
|
||||||
num_samples = cur_sample_count - processed_samples;
|
if (devc->limit_samples && devc->sent_samples + cur_sample_count > devc->limit_samples)
|
||||||
if (devc->limit_samples && devc->sent_samples + num_samples > devc->limit_samples)
|
|
||||||
num_samples = devc->limit_samples - devc->sent_samples;
|
num_samples = devc->limit_samples - devc->sent_samples;
|
||||||
|
else
|
||||||
|
num_samples = cur_sample_count;
|
||||||
|
|
||||||
devc->send_data_proc(sdi, (uint8_t *)transfer->buffer + processed_samples * unitsize,
|
devc->send_data_proc(sdi, (uint8_t *)transfer->buffer,
|
||||||
num_samples * unitsize, unitsize);
|
num_samples * unitsize, unitsize);
|
||||||
devc->sent_samples += num_samples;
|
devc->sent_samples += num_samples;
|
||||||
processed_samples += num_samples;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trigger_offset = soft_trigger_logic_check(devc->stl,
|
trigger_offset = soft_trigger_logic_check(devc->stl,
|
||||||
transfer->buffer + processed_samples * unitsize,
|
transfer->buffer, transfer->actual_length, &pre_trigger_samples);
|
||||||
transfer->actual_length - processed_samples * unitsize,
|
|
||||||
&pre_trigger_samples);
|
|
||||||
if (trigger_offset > -1) {
|
if (trigger_offset > -1) {
|
||||||
std_session_send_df_frame_begin(sdi);
|
|
||||||
devc->sent_samples += pre_trigger_samples;
|
devc->sent_samples += pre_trigger_samples;
|
||||||
num_samples = cur_sample_count - processed_samples - trigger_offset;
|
num_samples = cur_sample_count - trigger_offset;
|
||||||
if (devc->limit_samples &&
|
if (devc->limit_samples &&
|
||||||
devc->sent_samples + num_samples > devc->limit_samples)
|
num_samples > devc->limit_samples - devc->sent_samples)
|
||||||
num_samples = devc->limit_samples - devc->sent_samples;
|
num_samples = devc->limit_samples - devc->sent_samples;
|
||||||
|
|
||||||
devc->send_data_proc(sdi, (uint8_t *)transfer->buffer
|
devc->send_data_proc(sdi, (uint8_t *)transfer->buffer
|
||||||
+ processed_samples * unitsize
|
|
||||||
+ trigger_offset * unitsize,
|
+ trigger_offset * unitsize,
|
||||||
num_samples * unitsize, unitsize);
|
num_samples * unitsize, unitsize);
|
||||||
devc->sent_samples += num_samples;
|
devc->sent_samples += num_samples;
|
||||||
processed_samples += trigger_offset + num_samples;
|
|
||||||
|
|
||||||
devc->trigger_fired = TRUE;
|
devc->trigger_fired = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int frame_ended = devc->limit_samples && (devc->sent_samples >= devc->limit_samples);
|
if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) {
|
||||||
const int final_frame = devc->limit_frames && (devc->num_frames >= (devc->limit_frames - 1));
|
|
||||||
|
|
||||||
if (frame_ended) {
|
|
||||||
devc->num_frames++;
|
|
||||||
devc->sent_samples = 0;
|
|
||||||
devc->trigger_fired = FALSE;
|
|
||||||
std_session_send_df_frame_end(sdi);
|
|
||||||
|
|
||||||
/* There may be another trigger in the remaining data, go back and check for it */
|
|
||||||
if (processed_samples < cur_sample_count) {
|
|
||||||
/* Reset the trigger stage */
|
|
||||||
if (devc->stl)
|
|
||||||
devc->stl->cur_stage = 0;
|
|
||||||
else {
|
|
||||||
std_session_send_df_frame_begin(sdi);
|
|
||||||
devc->trigger_fired = TRUE;
|
|
||||||
}
|
|
||||||
if (!final_frame)
|
|
||||||
goto check_trigger;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (frame_ended && final_frame) {
|
|
||||||
fx2lafw_abort_acquisition(devc);
|
fx2lafw_abort_acquisition(devc);
|
||||||
free_transfer(transfer);
|
free_transfer(transfer);
|
||||||
} else
|
} else
|
||||||
|
|
@ -653,10 +621,8 @@ static int start_transfers(const struct sr_dev_inst *sdi)
|
||||||
if (!devc->stl)
|
if (!devc->stl)
|
||||||
return SR_ERR_MALLOC;
|
return SR_ERR_MALLOC;
|
||||||
devc->trigger_fired = FALSE;
|
devc->trigger_fired = FALSE;
|
||||||
} else {
|
} else
|
||||||
std_session_send_df_frame_begin(sdi);
|
|
||||||
devc->trigger_fired = TRUE;
|
devc->trigger_fired = TRUE;
|
||||||
}
|
|
||||||
|
|
||||||
num_transfers = get_number_of_transfers(devc);
|
num_transfers = get_number_of_transfers(devc);
|
||||||
|
|
||||||
|
|
@ -721,7 +687,6 @@ SR_PRIV int fx2lafw_start_acquisition(const struct sr_dev_inst *sdi)
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
devc->ctx = drvc->sr_ctx;
|
devc->ctx = drvc->sr_ctx;
|
||||||
devc->num_frames = 0;
|
|
||||||
devc->sent_samples = 0;
|
devc->sent_samples = 0;
|
||||||
devc->empty_transfer_count = 0;
|
devc->empty_transfer_count = 0;
|
||||||
devc->acq_aborted = FALSE;
|
devc->acq_aborted = FALSE;
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ struct dev_context {
|
||||||
int num_samplerates;
|
int num_samplerates;
|
||||||
|
|
||||||
uint64_t cur_samplerate;
|
uint64_t cur_samplerate;
|
||||||
uint64_t limit_frames;
|
|
||||||
uint64_t limit_samples;
|
uint64_t limit_samples;
|
||||||
uint64_t capture_ratio;
|
uint64_t capture_ratio;
|
||||||
|
|
||||||
|
|
@ -111,8 +110,7 @@ struct dev_context {
|
||||||
gboolean sample_wide;
|
gboolean sample_wide;
|
||||||
struct soft_trigger_logic *stl;
|
struct soft_trigger_logic *stl;
|
||||||
|
|
||||||
uint64_t num_frames;
|
unsigned int sent_samples;
|
||||||
uint64_t sent_samples;
|
|
||||||
int submitted_transfers;
|
int submitted_transfers;
|
||||||
int empty_transfer_count;
|
int empty_transfer_count;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ static enum model scan_model_sm(struct sr_serial_dev_inst *serial)
|
||||||
* Try to find message consisting of device code and several
|
* Try to find message consisting of device code and several
|
||||||
* (at least 4) data bytes.
|
* (at least 4) data bytes.
|
||||||
*/
|
*/
|
||||||
serial_flush(serial);
|
|
||||||
for (bytecnt = 0; bytecnt < 100; bytecnt++) {
|
for (bytecnt = 0; bytecnt < 100; bytecnt++) {
|
||||||
byte = read_byte(serial, timeout_us);
|
byte = read_byte(serial, timeout_us);
|
||||||
if ((byte == -1) || (timeout_us < g_get_monotonic_time()))
|
if ((byte == -1) || (timeout_us < g_get_monotonic_time()))
|
||||||
|
|
@ -176,6 +175,8 @@ static GSList *scan_1x_2x_rs232(struct sr_dev_driver *di, GSList *options)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serial_flush(serial);
|
||||||
|
|
||||||
model = scan_model_sm(serial);
|
model = scan_model_sm(serial);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -186,9 +187,11 @@ static GSList *scan_1x_2x_rs232(struct sr_dev_driver *di, GSList *options)
|
||||||
serialcomm = SERIALCOMM_1X_RS232;
|
serialcomm = SERIALCOMM_1X_RS232;
|
||||||
g_free(serial->serialcomm);
|
g_free(serial->serialcomm);
|
||||||
serial->serialcomm = g_strdup(serialcomm);
|
serial->serialcomm = g_strdup(serialcomm);
|
||||||
if (serial_set_paramstr(serial, serialcomm) == SR_OK)
|
if (serial_set_paramstr(serial, serialcomm) == SR_OK) {
|
||||||
|
serial_flush(serial);
|
||||||
model = scan_model_sm(serial);
|
model = scan_model_sm(serial);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (model != METRAHIT_NONE) {
|
if (model != METRAHIT_NONE) {
|
||||||
sr_spew("%s detected!", gmc_model_str(model));
|
sr_spew("%s detected!", gmc_model_str(model));
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ static const uint32_t drvopts[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t devopts[] = {
|
static const uint32_t devopts[] = {
|
||||||
SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
|
SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
|
||||||
SR_CONF_SAMPLERATE | SR_CONF_GET,
|
SR_CONF_SAMPLERATE | SR_CONF_GET,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -132,9 +132,6 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
case SR_CONF_SAMPLERATE:
|
case SR_CONF_SAMPLERATE:
|
||||||
*data = g_variant_new_uint64(devc->sample_rate);
|
*data = g_variant_new_uint64(devc->sample_rate);
|
||||||
break;
|
break;
|
||||||
case SR_CONF_LIMIT_FRAMES:
|
|
||||||
*data = g_variant_new_uint64(devc->frame_limit);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
}
|
}
|
||||||
|
|
@ -192,13 +189,17 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi)
|
||||||
{
|
{
|
||||||
struct sr_scpi_dev_inst *scpi;
|
struct sr_scpi_dev_inst *scpi;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
|
||||||
scpi = sdi->conn;
|
scpi = sdi->conn;
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
if (devc->df_started) {
|
if (devc->df_started) {
|
||||||
std_session_send_df_frame_end(sdi);
|
packet.type = SR_DF_FRAME_END;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
std_session_send_df_end(sdi);
|
std_session_send_df_end(sdi);
|
||||||
|
|
||||||
devc->df_started = FALSE;
|
devc->df_started = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,8 +141,11 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
|
||||||
} else {
|
} else {
|
||||||
/* Start acquiring next frame. */
|
/* Start acquiring next frame. */
|
||||||
if (devc->df_started) {
|
if (devc->df_started) {
|
||||||
std_session_send_df_frame_end(sdi);
|
packet.type = SR_DF_FRAME_END;
|
||||||
std_session_send_df_frame_begin(sdi);
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
|
packet.type = SR_DF_FRAME_BEGIN;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
devc->cur_acq_frame++;
|
devc->cur_acq_frame++;
|
||||||
|
|
@ -199,7 +202,10 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
|
||||||
if (!devc->df_started) {
|
if (!devc->df_started) {
|
||||||
std_session_send_df_header(sdi);
|
std_session_send_df_header(sdi);
|
||||||
std_session_send_df_frame_begin(sdi);
|
|
||||||
|
packet.type = SR_DF_FRAME_BEGIN;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
devc->df_started = TRUE;
|
devc->df_started = TRUE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -265,8 +271,11 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
|
||||||
} else {
|
} else {
|
||||||
/* Start acquiring next frame. */
|
/* Start acquiring next frame. */
|
||||||
if (devc->df_started) {
|
if (devc->df_started) {
|
||||||
std_session_send_df_frame_end(sdi);
|
packet.type = SR_DF_FRAME_END;
|
||||||
std_session_send_df_frame_begin(sdi);
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
|
packet.type = SR_DF_FRAME_BEGIN;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
}
|
}
|
||||||
devc->cur_acq_frame++;
|
devc->cur_acq_frame++;
|
||||||
devc->state = START_ACQUISITION;
|
devc->state = START_ACQUISITION;
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
|
||||||
#define IDN_RETRIES 3 /* at least 2 */
|
|
||||||
|
|
||||||
static const uint32_t scanopts[] = {
|
static const uint32_t scanopts[] = {
|
||||||
SR_CONF_CONN,
|
SR_CONF_CONN,
|
||||||
SR_CONF_SERIALCOMM,
|
SR_CONF_SERIALCOMM,
|
||||||
|
|
@ -51,12 +49,6 @@ static const char *channel_modes[] = {
|
||||||
"Independent",
|
"Independent",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *gpd_serialcomms[] = {
|
|
||||||
"9600/8n1",
|
|
||||||
"57600/8n1",
|
|
||||||
"115200/8n1"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct gpd_model models[] = {
|
static const struct gpd_model models[] = {
|
||||||
{ GPD_2303S, "GPD-2303S",
|
{ GPD_2303S, "GPD-2303S",
|
||||||
CHANMODE_INDEPENDENT,
|
CHANMODE_INDEPENDENT,
|
||||||
|
|
@ -68,21 +60,11 @@ static const struct gpd_model models[] = {
|
||||||
{ { 0, 30, 0.001 }, { 0, 3, 0.001 } },
|
{ { 0, 30, 0.001 }, { 0, 3, 0.001 } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ GPD_3303S, "GPD-3303S",
|
|
||||||
CHANMODE_INDEPENDENT,
|
|
||||||
2,
|
|
||||||
{
|
|
||||||
/* Channel 1 */
|
|
||||||
{ { 0, 32, 0.001 }, { 0, 3.2, 0.001 } },
|
|
||||||
/* Channel 2 */
|
|
||||||
{ { 0, 32, 0.001 }, { 0, 3.2, 0.001 } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
{
|
{
|
||||||
const char *conn, *serialcomm, **serialcomms;
|
const char *conn, *serialcomm;
|
||||||
const struct gpd_model *model;
|
const struct gpd_model *model;
|
||||||
const struct sr_config *src;
|
const struct sr_config *src;
|
||||||
struct sr_channel *ch;
|
struct sr_channel *ch;
|
||||||
|
|
@ -90,8 +72,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
GSList *l;
|
GSList *l;
|
||||||
struct sr_serial_dev_inst *serial;
|
struct sr_serial_dev_inst *serial;
|
||||||
struct sr_dev_inst *sdi;
|
struct sr_dev_inst *sdi;
|
||||||
char reply[100];
|
char reply[50];
|
||||||
unsigned int i, b, serialcomms_count;
|
unsigned int i;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
char channel[10];
|
char channel[10];
|
||||||
GRegex *regex;
|
GRegex *regex;
|
||||||
|
|
@ -118,47 +100,20 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
|
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (serialcomm) {
|
if (!serialcomm)
|
||||||
serialcomms = &serialcomm;
|
serialcomm = "115200/8n1";
|
||||||
serialcomms_count = 1;
|
sr_info("Probing serial port %s.", conn);
|
||||||
} else {
|
|
||||||
serialcomms = gpd_serialcomms;
|
|
||||||
serialcomms_count = sizeof(gpd_serialcomms) / sizeof(gpd_serialcomms[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for( b = 0; b < serialcomms_count; b++) {
|
|
||||||
serialcomm = serialcomms[b];
|
|
||||||
sr_info("Probing serial port %s @ %s", conn, serialcomm);
|
|
||||||
serial = sr_serial_dev_inst_new(conn, serialcomm);
|
serial = sr_serial_dev_inst_new(conn, serialcomm);
|
||||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
||||||
continue;
|
return NULL;
|
||||||
|
|
||||||
/*
|
|
||||||
* Problem: we need to clear the GPD receive buffer before we
|
|
||||||
* can expect it to process commands correctly.
|
|
||||||
*
|
|
||||||
* Do not just send a newline, since that may cause it to
|
|
||||||
* execute a currently buffered command.
|
|
||||||
*
|
|
||||||
* Solution: Send identification request a few times.
|
|
||||||
* The first should corrupt any previous buffered command if present
|
|
||||||
* and respond with "Invalid Character." or respond directly with
|
|
||||||
* an identification string.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < IDN_RETRIES; i++) {
|
|
||||||
/* Request the GPD to identify itself */
|
|
||||||
gpd_send_cmd(serial, "*IDN?\n");
|
|
||||||
if (gpd_receive_reply(serial, reply, sizeof(reply)) == SR_OK) {
|
|
||||||
if (0 == strncmp(reply, "GW INSTEK", 9)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == IDN_RETRIES) {
|
|
||||||
sr_err("Device did not reply to identification request.");
|
|
||||||
serial_flush(serial);
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
serial_flush(serial);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returned identification string is for example:
|
* Returned identification string is for example:
|
||||||
|
|
@ -215,19 +170,11 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
if (sscanf(reply, "%1u%1u%1u%1u%1u%1u%1u%1u", &cc_cv_ch1,
|
if (sscanf(reply, "%1u%1u%1u%1u%1u%1u%1u%1u", &cc_cv_ch1,
|
||||||
&cc_cv_ch2, &track1, &track2, &beep,
|
&cc_cv_ch2, &track1, &track2, &beep,
|
||||||
&devc->output_enabled, &baud1, &baud2) != 8) {
|
&devc->output_enabled, &baud1, &baud2) != 8) {
|
||||||
/* old firmware (< 2.00?) responds with different format */
|
|
||||||
if (sscanf(reply, "%1u %1u %1u %1u %1u X %1u X", &cc_cv_ch1,
|
|
||||||
&cc_cv_ch2, &track1, &track2, &beep,
|
|
||||||
&devc->output_enabled) != 6) {
|
|
||||||
sr_err("Invalid reply to STATUS: '%s'.", reply);
|
sr_err("Invalid reply to STATUS: '%s'.", reply);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
/* ignore remaining two lines of status message */
|
|
||||||
gpd_receive_reply(serial, reply, sizeof(reply));
|
|
||||||
gpd_receive_reply(serial, reply, sizeof(reply));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < model->num_channels; i++) {
|
for (i = 0; i < model->num_channels; ++i) {
|
||||||
gpd_send_cmd(serial, "ISET%d?\n", i + 1);
|
gpd_send_cmd(serial, "ISET%d?\n", i + 1);
|
||||||
gpd_receive_reply(serial, reply, sizeof(reply));
|
gpd_receive_reply(serial, reply, sizeof(reply));
|
||||||
if (sscanf(reply, "%f", &devc->config[i].output_current_max) != 1) {
|
if (sscanf(reply, "%f", &devc->config[i].output_current_max) != 1) {
|
||||||
|
|
@ -255,16 +202,17 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serial_close(serial);
|
||||||
|
|
||||||
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (match_info)
|
if (match_info)
|
||||||
g_match_info_free(match_info);
|
g_match_info_free(match_info);
|
||||||
if (regex)
|
if (regex)
|
||||||
g_regex_unref(regex);
|
g_regex_unref(regex);
|
||||||
if (serial)
|
if (serial)
|
||||||
serial_close(serial);
|
serial_close(serial);
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +220,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
static int config_get(uint32_t key, GVariant **data,
|
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 channel;
|
int ret, channel;
|
||||||
const struct dev_context *devc;
|
const struct dev_context *devc;
|
||||||
const struct sr_channel *ch;
|
const struct sr_channel *ch;
|
||||||
|
|
||||||
|
|
@ -283,9 +231,6 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
|
|
||||||
if (!cg) {
|
if (!cg) {
|
||||||
switch (key) {
|
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_CHANNEL_CONFIG:
|
case SR_CONF_CHANNEL_CONFIG:
|
||||||
*data = g_variant_new_string(
|
*data = g_variant_new_string(
|
||||||
channel_modes[devc->channel_mode]);
|
channel_modes[devc->channel_mode]);
|
||||||
|
|
@ -299,6 +244,7 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
} else {
|
} else {
|
||||||
ch = cg->channels->data;
|
ch = cg->channels->data;
|
||||||
channel = ch->index;
|
channel = ch->index;
|
||||||
|
ret = SR_OK;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_VOLTAGE:
|
case SR_CONF_VOLTAGE:
|
||||||
*data = g_variant_new_double(
|
*data = g_variant_new_double(
|
||||||
|
|
@ -321,7 +267,7 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SR_OK;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int config_set(uint32_t key, GVariant *data,
|
static int config_set(uint32_t key, GVariant *data,
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ SR_PRIV int gpd_receive_reply(struct sr_serial_dev_inst *serial, char *buf,
|
||||||
{
|
{
|
||||||
int l_recv = 0, bufpos = 0, retc, l_startpos = 0, lines = 1;
|
int l_recv = 0, bufpos = 0, retc, l_startpos = 0, lines = 1;
|
||||||
gint64 start, remaining;
|
gint64 start, remaining;
|
||||||
const int timeout_ms = 250;
|
const int timeout_ms = 100;
|
||||||
|
|
||||||
if (!serial || !buf || (buflen <= 0))
|
if (!serial || !buf || (buflen <= 0))
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
|
|
@ -69,7 +69,7 @@ SR_PRIV int gpd_receive_reply(struct sr_serial_dev_inst *serial, char *buf,
|
||||||
if (bufpos == 0 && buf[bufpos] == '\n')
|
if (bufpos == 0 && buf[bufpos] == '\n')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (buf[bufpos] == '\n' || buf[bufpos] == '\r') {
|
if (buf[bufpos] == '\n') {
|
||||||
buf[bufpos] = '\0';
|
buf[bufpos] = '\0';
|
||||||
sr_dbg("Received line '%s'.", &buf[l_startpos]);
|
sr_dbg("Received line '%s'.", &buf[l_startpos]);
|
||||||
buf[bufpos] = '\n';
|
buf[bufpos] = '\n';
|
||||||
|
|
@ -131,8 +131,8 @@ SR_PRIV int gpd_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
|
||||||
reply[0] = '\0';
|
reply[0] = '\0';
|
||||||
gpd_receive_reply(serial, reply, sizeof(reply));
|
gpd_receive_reply(serial, reply, sizeof(reply));
|
||||||
if (sscanf(reply, "%f", &devc->config[i].output_current_last) != 1) {
|
if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
|
||||||
sr_err("Invalid reply to IOUT1?: '%s'.",
|
sr_err("Invalid reply to VOUT1?: '%s'.",
|
||||||
reply);
|
reply);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
@ -148,12 +148,12 @@ SR_PRIV int gpd_receive_data(int fd, int revents, void *cb_data)
|
||||||
analog.meaning->mqflags = 0;
|
analog.meaning->mqflags = 0;
|
||||||
analog.encoding->digits = 3;
|
analog.encoding->digits = 3;
|
||||||
analog.spec->spec_digits = 3;
|
analog.spec->spec_digits = 3;
|
||||||
analog.data = &devc->config[i].output_current_last;
|
analog.data = &devc->config[i].output_current_max;
|
||||||
sr_session_send(sdi, &packet);
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
reply[0] = '\0';
|
reply[0] = '\0';
|
||||||
gpd_receive_reply(serial, reply, sizeof(reply));
|
gpd_receive_reply(serial, reply, sizeof(reply));
|
||||||
if (sscanf(reply, "%f", &devc->config[i].output_voltage_last) != 1) {
|
if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
|
||||||
sr_err("Invalid reply to VOUT1?: '%s'.",
|
sr_err("Invalid reply to VOUT1?: '%s'.",
|
||||||
reply);
|
reply);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
@ -170,12 +170,11 @@ SR_PRIV int gpd_receive_data(int fd, int revents, void *cb_data)
|
||||||
analog.meaning->mqflags = SR_MQFLAG_DC;
|
analog.meaning->mqflags = SR_MQFLAG_DC;
|
||||||
analog.encoding->digits = 3;
|
analog.encoding->digits = 3;
|
||||||
analog.spec->spec_digits = 3;
|
analog.spec->spec_digits = 3;
|
||||||
analog.data = &devc->config[i].output_voltage_last;
|
analog.data = &devc->config[i].output_voltage_max;
|
||||||
sr_session_send(sdi, &packet);
|
sr_session_send(sdi, &packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
devc->reply_pending = FALSE;
|
devc->reply_pending = FALSE;
|
||||||
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!devc->reply_pending) {
|
if (!devc->reply_pending) {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GPD_2303S,
|
GPD_2303S,
|
||||||
GPD_3303S,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Maximum number of output channels handled by this driver. */
|
/* Maximum number of output channels handled by this driver. */
|
||||||
|
|
|
||||||
|
|
@ -259,9 +259,6 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
}
|
}
|
||||||
*data = g_variant_new_double(state->digital_pods[idx].user_threshold);
|
*data = g_variant_new_double(state->digital_pods[idx].user_threshold);
|
||||||
break;
|
break;
|
||||||
case SR_CONF_LIMIT_FRAMES:
|
|
||||||
*data = g_variant_new_uint64(devc->frame_limit);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ static const char *rohde_schwarz_log_not_pod_scpi_dialect[] = {
|
||||||
static const uint32_t devopts[] = {
|
static const uint32_t devopts[] = {
|
||||||
SR_CONF_OSCILLOSCOPE,
|
SR_CONF_OSCILLOSCOPE,
|
||||||
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
|
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
|
||||||
SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
|
SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
|
||||||
SR_CONF_SAMPLERATE | SR_CONF_GET,
|
SR_CONF_SAMPLERATE | SR_CONF_GET,
|
||||||
SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||||
SR_CONF_NUM_HDIV | SR_CONF_GET,
|
SR_CONF_NUM_HDIV | SR_CONF_GET,
|
||||||
|
|
@ -1380,6 +1380,8 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
|
||||||
(void)fd;
|
(void)fd;
|
||||||
(void)revents;
|
(void)revents;
|
||||||
|
|
||||||
|
data = NULL;
|
||||||
|
|
||||||
if (!(sdi = cb_data))
|
if (!(sdi = cb_data))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
|
@ -1400,15 +1402,16 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
|
||||||
* Send "frame begin" packet upon reception of data for the
|
* Send "frame begin" packet upon reception of data for the
|
||||||
* first enabled channel.
|
* first enabled channel.
|
||||||
*/
|
*/
|
||||||
if (devc->current_channel == devc->enabled_channels)
|
if (devc->current_channel == devc->enabled_channels) {
|
||||||
std_session_send_df_frame_begin(sdi);
|
packet.type = SR_DF_FRAME_BEGIN;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pass on the received data of the channel(s).
|
* Pass on the received data of the channel(s).
|
||||||
*/
|
*/
|
||||||
switch (ch->type) {
|
switch (ch->type) {
|
||||||
case SR_CHANNEL_ANALOG:
|
case SR_CHANNEL_ANALOG:
|
||||||
data = NULL;
|
|
||||||
if (sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK) {
|
if (sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK) {
|
||||||
if (data)
|
if (data)
|
||||||
g_byte_array_free(data, TRUE);
|
g_byte_array_free(data, TRUE);
|
||||||
|
|
@ -1422,9 +1425,25 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
|
||||||
/* Truncate acquisition if a smaller number of samples has been requested. */
|
/* Truncate acquisition if a smaller number of samples has been requested. */
|
||||||
if (devc->samples_limit > 0 && analog.num_samples > devc->samples_limit)
|
if (devc->samples_limit > 0 && analog.num_samples > devc->samples_limit)
|
||||||
analog.num_samples = devc->samples_limit;
|
analog.num_samples = devc->samples_limit;
|
||||||
/* TODO: Use proper 'digits' value for this device (and its modes). */
|
analog.encoding = &encoding;
|
||||||
sr_analog_init(&analog, &encoding, &meaning, &spec, 2);
|
analog.meaning = &meaning;
|
||||||
|
analog.spec = &spec;
|
||||||
|
|
||||||
|
encoding.unitsize = sizeof(float);
|
||||||
encoding.is_signed = TRUE;
|
encoding.is_signed = TRUE;
|
||||||
|
encoding.is_float = TRUE;
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
encoding.is_bigendian = TRUE;
|
||||||
|
#else
|
||||||
|
encoding.is_bigendian = FALSE;
|
||||||
|
#endif
|
||||||
|
/* TODO: Use proper 'digits' value for this device (and its modes). */
|
||||||
|
encoding.digits = 2;
|
||||||
|
encoding.is_digits_decimal = FALSE;
|
||||||
|
encoding.scale.p = 1;
|
||||||
|
encoding.scale.q = 1;
|
||||||
|
encoding.offset.p = 0;
|
||||||
|
encoding.offset.q = 1;
|
||||||
if (state->analog_channels[ch->index].probe_unit == 'V') {
|
if (state->analog_channels[ch->index].probe_unit == 'V') {
|
||||||
meaning.mq = SR_MQ_VOLTAGE;
|
meaning.mq = SR_MQ_VOLTAGE;
|
||||||
meaning.unit = SR_UNIT_VOLT;
|
meaning.unit = SR_UNIT_VOLT;
|
||||||
|
|
@ -1432,7 +1451,10 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
|
||||||
meaning.mq = SR_MQ_CURRENT;
|
meaning.mq = SR_MQ_CURRENT;
|
||||||
meaning.unit = SR_UNIT_AMPERE;
|
meaning.unit = SR_UNIT_AMPERE;
|
||||||
}
|
}
|
||||||
|
meaning.mqflags = 0;
|
||||||
meaning.channels = g_slist_append(NULL, ch);
|
meaning.channels = g_slist_append(NULL, ch);
|
||||||
|
/* TODO: Use proper 'digits' value for this device (and its modes). */
|
||||||
|
spec.spec_digits = 2;
|
||||||
packet.payload = &analog;
|
packet.payload = &analog;
|
||||||
sr_session_send(sdi, &packet);
|
sr_session_send(sdi, &packet);
|
||||||
devc->num_samples = data->len / sizeof(float);
|
devc->num_samples = data->len / sizeof(float);
|
||||||
|
|
@ -1441,7 +1463,6 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
|
||||||
data = NULL;
|
data = NULL;
|
||||||
break;
|
break;
|
||||||
case SR_CHANNEL_LOGIC:
|
case SR_CHANNEL_LOGIC:
|
||||||
data = NULL;
|
|
||||||
if (sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK) {
|
if (sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK) {
|
||||||
if (data)
|
if (data)
|
||||||
g_byte_array_free(data, TRUE);
|
g_byte_array_free(data, TRUE);
|
||||||
|
|
@ -1505,7 +1526,8 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
|
||||||
*/
|
*/
|
||||||
hmo_cleanup_logic_data(devc);
|
hmo_cleanup_logic_data(devc);
|
||||||
|
|
||||||
std_session_send_df_frame_end(sdi);
|
packet.type = SR_DF_FRAME_END;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* End of frame was reached. Stop acquisition after the specified
|
* End of frame was reached. Stop acquisition after the specified
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,10 @@ static void send_data(struct sr_dev_inst *sdi,
|
||||||
.type = SR_DF_LOGIC,
|
.type = SR_DF_LOGIC,
|
||||||
.payload = &logic
|
.payload = &logic
|
||||||
};
|
};
|
||||||
|
const struct sr_datafeed_packet trig = {
|
||||||
|
.type = SR_DF_TRIGGER,
|
||||||
|
.payload = NULL
|
||||||
|
};
|
||||||
size_t trigger_offset;
|
size_t trigger_offset;
|
||||||
|
|
||||||
if (devc->trigger_pos >= devc->sent_samples &&
|
if (devc->trigger_pos >= devc->sent_samples &&
|
||||||
|
|
@ -125,7 +129,7 @@ static void send_data(struct sr_dev_inst *sdi,
|
||||||
sr_session_send(sdi, &packet);
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
/* Send trigger position. */
|
/* Send trigger position. */
|
||||||
std_session_send_df_trigger(sdi);
|
sr_session_send(sdi, &trig);
|
||||||
|
|
||||||
/* Send rest of data. */
|
/* Send rest of data. */
|
||||||
logic.length = (sample_count - trigger_offset) * sizeof(uint32_t);
|
logic.length = (sample_count - trigger_offset) * sizeof(uint32_t);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
/* Max time in ms before we want to check on USB events */
|
/* Max time in ms before we want to check on USB events */
|
||||||
#define TICK 200
|
#define TICK 200
|
||||||
|
|
||||||
#define RANGE(ch) (((float)devc->vdivs[devc->voltage[ch]][0] / devc->vdivs[devc->voltage[ch]][1]) * VDIV_MULTIPLIER)
|
#define RANGE(ch) (((float)vdivs[devc->voltage[ch]][0] / vdivs[devc->voltage[ch]][1]) * VDIV_MULTIPLIER)
|
||||||
|
|
||||||
static const uint32_t scanopts[] = {
|
static const uint32_t scanopts[] = {
|
||||||
SR_CONF_CONN,
|
SR_CONF_CONN,
|
||||||
|
|
@ -43,8 +43,8 @@ static const uint32_t devopts[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t devopts_cg[] = {
|
static const uint32_t devopts_cg[] = {
|
||||||
SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
|
||||||
SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
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 *channel_names[] = {
|
static const char *channel_names[] = {
|
||||||
|
|
@ -59,69 +59,51 @@ static const char *acdc_coupling[] = {
|
||||||
"AC", "DC",
|
"AC", "DC",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint64_t vdivs[][2] = {
|
|
||||||
VDIV_VALUES
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint64_t vdivs_instrustar[][2] = {
|
|
||||||
VDIV_VALUES_INSTRUSTAR
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint64_t samplerates[] = {
|
|
||||||
SAMPLERATE_VALUES
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct hantek_6xxx_profile dev_profiles[] = {
|
static const struct hantek_6xxx_profile dev_profiles[] = {
|
||||||
{
|
{
|
||||||
/* Windows: "Hantek6022BE DRIVER 1": 04b4:6022 */
|
/* Windows: "Hantek6022BE DRIVER 1": 04b4:6022 */
|
||||||
0x04b4, 0x6022, 0x1d50, 0x608e, 0x0001,
|
0x04b4, 0x6022, 0x1d50, 0x608e, 0x0001,
|
||||||
"Hantek", "6022BE", "fx2lafw-hantek-6022be.fw",
|
"Hantek", "6022BE", "fx2lafw-hantek-6022be.fw",
|
||||||
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
||||||
ARRAY_AND_SIZE(vdivs),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
/* Windows: "Hantek6022BE DRIVER 2": 04b5:6022 */
|
/* Windows: "Hantek6022BE DRIVER 2": 04b5:6022 */
|
||||||
0x04b5, 0x6022, 0x1d50, 0x608e, 0x0001,
|
0x04b5, 0x6022, 0x1d50, 0x608e, 0x0001,
|
||||||
"Hantek", "6022BE", "fx2lafw-hantek-6022be.fw",
|
"Hantek", "6022BE", "fx2lafw-hantek-6022be.fw",
|
||||||
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
||||||
ARRAY_AND_SIZE(vdivs),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
0x8102, 0x8102, 0x1d50, 0x608e, 0x0002,
|
0x8102, 0x8102, 0x1d50, 0x608e, 0x0002,
|
||||||
"Sainsmart", "DDS120", "fx2lafw-sainsmart-dds120.fw",
|
"Sainsmart", "DDS120", "fx2lafw-sainsmart-dds120.fw",
|
||||||
ARRAY_AND_SIZE(acdc_coupling), TRUE,
|
ARRAY_AND_SIZE(acdc_coupling), TRUE,
|
||||||
ARRAY_AND_SIZE(vdivs),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
/* Windows: "Hantek6022BL DRIVER 1": 04b4:602a */
|
/* Windows: "Hantek6022BL DRIVER 1": 04b4:602a */
|
||||||
0x04b4, 0x602a, 0x1d50, 0x608e, 0x0003,
|
0x04b4, 0x602a, 0x1d50, 0x608e, 0x0003,
|
||||||
"Hantek", "6022BL", "fx2lafw-hantek-6022bl.fw",
|
"Hantek", "6022BL", "fx2lafw-hantek-6022bl.fw",
|
||||||
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
||||||
ARRAY_AND_SIZE(vdivs),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
/* Windows: "Hantek6022BL DRIVER 2": 04b5:602a */
|
/* Windows: "Hantek6022BL DRIVER 2": 04b5:602a */
|
||||||
0x04b5, 0x602a, 0x1d50, 0x608e, 0x0003,
|
0x04b5, 0x602a, 0x1d50, 0x608e, 0x0003,
|
||||||
"Hantek", "6022BL", "fx2lafw-hantek-6022bl.fw",
|
"Hantek", "6022BL", "fx2lafw-hantek-6022bl.fw",
|
||||||
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
||||||
ARRAY_AND_SIZE(vdivs),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
0xd4a2, 0x5660, 0x1d50, 0x608e, 0x0004,
|
0xd4a2, 0x5660, 0x1d50, 0x608e, 0x0004,
|
||||||
"YiXingDianZi", "MDSO", "fx2lafw-yixingdianzi-mdso.fw",
|
"YiXingDianZi", "MDSO", "fx2lafw-yixingdianzi-mdso.fw",
|
||||||
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
ARRAY_AND_SIZE(dc_coupling), FALSE,
|
||||||
ARRAY_AND_SIZE(vdivs),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*"InstrustarISDS205": d4a2:5661 */
|
|
||||||
0xd4a2, 0x5661, 0x1d50, 0x608e, 0x0005,
|
|
||||||
"Instrustar", "ISDS205B", "fx2lafw-instrustar-isds205b.fw",
|
|
||||||
ARRAY_AND_SIZE(acdc_coupling), TRUE,
|
|
||||||
ARRAY_AND_SIZE(vdivs_instrustar),
|
|
||||||
},
|
},
|
||||||
ALL_ZERO
|
ALL_ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint64_t samplerates[] = {
|
||||||
|
SAMPLERATE_VALUES
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint64_t vdivs[][2] = {
|
||||||
|
VDIV_VALUES
|
||||||
|
};
|
||||||
|
|
||||||
static int read_channel(const struct sr_dev_inst *sdi, uint32_t amount);
|
static int read_channel(const struct sr_dev_inst *sdi, uint32_t amount);
|
||||||
|
|
||||||
|
|
@ -156,8 +138,6 @@ static struct sr_dev_inst *hantek_6xxx_dev_new(const struct hantek_6xxx_profile
|
||||||
devc->coupling_vals = prof->coupling_vals;
|
devc->coupling_vals = prof->coupling_vals;
|
||||||
devc->coupling_tab_size = prof->coupling_tab_size;
|
devc->coupling_tab_size = prof->coupling_tab_size;
|
||||||
devc->has_coupling = prof->has_coupling;
|
devc->has_coupling = prof->has_coupling;
|
||||||
devc->vdivs = prof->vdivs;
|
|
||||||
devc->vdivs_size = prof->vdivs_size;
|
|
||||||
|
|
||||||
devc->profile = prof;
|
devc->profile = prof;
|
||||||
devc->dev_state = IDLE;
|
devc->dev_state = IDLE;
|
||||||
|
|
@ -367,17 +347,16 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
const uint64_t *vdiv;
|
const uint64_t *vdiv;
|
||||||
int ch_idx;
|
int ch_idx;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case SR_CONF_NUM_VDIV:
|
||||||
|
*data = g_variant_new_int32(ARRAY_SIZE(vdivs));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!sdi)
|
if (!sdi)
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_NUM_VDIV:
|
|
||||||
*data = g_variant_new_int32(devc->vdivs_size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cg) {
|
if (!cg) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_SAMPLERATE:
|
case SR_CONF_SAMPLERATE:
|
||||||
|
|
@ -411,7 +390,7 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_VDIV:
|
case SR_CONF_VDIV:
|
||||||
vdiv = devc->vdivs[devc->voltage[ch_idx]];
|
vdiv = vdivs[devc->voltage[ch_idx]];
|
||||||
*data = g_variant_new("(tt)", vdiv[0], vdiv[1]);
|
*data = g_variant_new("(tt)", vdiv[0], vdiv[1]);
|
||||||
break;
|
break;
|
||||||
case SR_CONF_COUPLING:
|
case SR_CONF_COUPLING:
|
||||||
|
|
@ -455,7 +434,7 @@ static int config_set(uint32_t key, GVariant *data,
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_VDIV:
|
case SR_CONF_VDIV:
|
||||||
if ((idx = std_u64_tuple_idx(data, devc->vdivs, devc->vdivs_size)) < 0)
|
if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
devc->voltage[ch_idx] = idx;
|
devc->voltage[ch_idx] = idx;
|
||||||
hantek_6xxx_update_vdiv(sdi);
|
hantek_6xxx_update_vdiv(sdi);
|
||||||
|
|
@ -465,7 +444,6 @@ static int config_set(uint32_t key, GVariant *data,
|
||||||
devc->coupling_tab_size)) < 0)
|
devc->coupling_tab_size)) < 0)
|
||||||
return SR_ERR_ARG;
|
return SR_ERR_ARG;
|
||||||
devc->coupling[ch_idx] = idx;
|
devc->coupling[ch_idx] = idx;
|
||||||
hantek_6xxx_update_coupling(sdi);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
|
|
@ -504,9 +482,7 @@ static int config_list(uint32_t key, GVariant **data,
|
||||||
*data = g_variant_new_strv(devc->coupling_vals, devc->coupling_tab_size);
|
*data = g_variant_new_strv(devc->coupling_vals, devc->coupling_tab_size);
|
||||||
break;
|
break;
|
||||||
case SR_CONF_VDIV:
|
case SR_CONF_VDIV:
|
||||||
if (!devc)
|
*data = std_gvar_tuple_array(ARRAY_AND_SIZE(vdivs));
|
||||||
return SR_ERR_ARG;
|
|
||||||
*data = std_gvar_tuple_array(devc->vdivs,devc->vdivs_size);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
|
|
|
||||||
|
|
@ -46,21 +46,13 @@
|
||||||
48, 30, 24, 16, 8, 4, 1, 50, 20, 10,
|
48, 30, 24, 16, 8, 4, 1, 50, 20, 10,
|
||||||
|
|
||||||
#define VDIV_VALUES \
|
#define VDIV_VALUES \
|
||||||
{ 1, 1 }, \
|
{ 100, 1000 }, \
|
||||||
{ 500, 1000 }, \
|
|
||||||
{ 250, 1000 }, \
|
{ 250, 1000 }, \
|
||||||
{ 100, 1000 },
|
{ 500, 1000 }, \
|
||||||
|
{ 1, 1 },
|
||||||
#define VDIV_VALUES_INSTRUSTAR \
|
|
||||||
{ 128, 100 }, \
|
|
||||||
{ 705, 1000 }, \
|
|
||||||
{ 288, 1000 }, \
|
|
||||||
{ 140, 1000 }, \
|
|
||||||
{ 576, 10000 }, \
|
|
||||||
{ 176, 10000 },
|
|
||||||
|
|
||||||
#define VDIV_REG \
|
#define VDIV_REG \
|
||||||
1, 2, 5, 10, 11, 12, 13,
|
10, 5, 2, 1,
|
||||||
|
|
||||||
#define VDIV_MULTIPLIER 10
|
#define VDIV_MULTIPLIER 10
|
||||||
|
|
||||||
|
|
@ -113,8 +105,6 @@ struct hantek_6xxx_profile {
|
||||||
const char **coupling_vals;
|
const char **coupling_vals;
|
||||||
uint8_t coupling_tab_size;
|
uint8_t coupling_tab_size;
|
||||||
gboolean has_coupling;
|
gboolean has_coupling;
|
||||||
const uint64_t (*vdivs)[2];
|
|
||||||
const uint32_t vdivs_size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dev_context {
|
struct dev_context {
|
||||||
|
|
@ -140,8 +130,6 @@ struct dev_context {
|
||||||
uint8_t coupling_tab_size;
|
uint8_t coupling_tab_size;
|
||||||
gboolean has_coupling;
|
gboolean has_coupling;
|
||||||
uint64_t samplerate;
|
uint64_t samplerate;
|
||||||
const uint64_t (*vdivs)[2];
|
|
||||||
uint8_t vdivs_size;
|
|
||||||
|
|
||||||
uint64_t limit_msec;
|
uint64_t limit_msec;
|
||||||
uint64_t limit_samples;
|
uint64_t limit_samples;
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ static const uint32_t drvopts[] = {
|
||||||
static const uint32_t devopts[] = {
|
static const uint32_t devopts[] = {
|
||||||
SR_CONF_CONTINUOUS,
|
SR_CONF_CONTINUOUS,
|
||||||
SR_CONF_CONN | SR_CONF_GET,
|
SR_CONF_CONN | SR_CONF_GET,
|
||||||
SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
|
SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
|
||||||
SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||||
SR_CONF_NUM_HDIV | SR_CONF_GET,
|
SR_CONF_NUM_HDIV | SR_CONF_GET,
|
||||||
SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
|
SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
|
||||||
|
|
@ -477,9 +477,6 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
case SR_CONF_CAPTURE_RATIO:
|
case SR_CONF_CAPTURE_RATIO:
|
||||||
*data = g_variant_new_uint64(devc->capture_ratio);
|
*data = g_variant_new_uint64(devc->capture_ratio);
|
||||||
break;
|
break;
|
||||||
case SR_CONF_LIMIT_FRAMES:
|
|
||||||
*data = g_variant_new_uint64(devc->limit_frames);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
}
|
}
|
||||||
|
|
@ -707,6 +704,7 @@ static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
|
||||||
*/
|
*/
|
||||||
static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
||||||
{
|
{
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
struct sr_dev_inst *sdi;
|
struct sr_dev_inst *sdi;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
int num_samples, pre;
|
int num_samples, pre;
|
||||||
|
|
@ -784,7 +782,8 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
||||||
devc->framebuf = NULL;
|
devc->framebuf = NULL;
|
||||||
|
|
||||||
/* Mark the end of this frame. */
|
/* Mark the end of this frame. */
|
||||||
std_session_send_df_frame_end(sdi);
|
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 */
|
/* Terminate session */
|
||||||
|
|
@ -798,6 +797,7 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
||||||
static int handle_event(int fd, int revents, void *cb_data)
|
static int handle_event(int fd, int revents, void *cb_data)
|
||||||
{
|
{
|
||||||
const struct sr_dev_inst *sdi;
|
const struct sr_dev_inst *sdi;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
struct sr_dev_driver *di;
|
struct sr_dev_driver *di;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
|
|
@ -889,7 +889,8 @@ static int handle_event(int fd, int revents, void *cb_data)
|
||||||
devc->dev_state = FETCH_DATA;
|
devc->dev_state = FETCH_DATA;
|
||||||
|
|
||||||
/* Tell the frontend a new frame is on the way. */
|
/* Tell the frontend a new frame is on the way. */
|
||||||
std_session_send_df_frame_begin(sdi);
|
packet.type = SR_DF_FRAME_BEGIN;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
break;
|
break;
|
||||||
case CAPTURE_READY_9BIT:
|
case CAPTURE_READY_9BIT:
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
|
|
||||||
|
|
@ -326,8 +326,7 @@ static int dso2250_set_trigger_samplerate(const struct sr_dev_inst *sdi)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp = base / devc->samplerate;
|
tmp = base / devc->samplerate;
|
||||||
/* Downsample only if really necessary */
|
if (tmp) {
|
||||||
if (tmp > 1) {
|
|
||||||
/* Downsampling on */
|
/* Downsampling on */
|
||||||
cmdstring[2] |= 2;
|
cmdstring[2] |= 2;
|
||||||
/* Downsampler = 1comp((Base / Samplerate) - 2)
|
/* Downsampler = 1comp((Base / Samplerate) - 2)
|
||||||
|
|
|
||||||
|
|
@ -127,14 +127,6 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
|
||||||
struct sr_dev_inst *sdi;
|
struct sr_dev_inst *sdi;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
|
|
||||||
/*
|
|
||||||
* The device cannot get identified by means of SCPI queries.
|
|
||||||
* Neither shall non-SCPI requests get emitted before reliable
|
|
||||||
* identification of the device. Assume that we only get here
|
|
||||||
* when user specs led us to believe it's safe to communicate
|
|
||||||
* to the expected kind of device.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This command ensures we receive an EOI after every response, so that
|
* This command ensures we receive an EOI after every response, so that
|
||||||
* we don't wait the entire timeout after the response is received.
|
* we don't wait the entire timeout after the response is received.
|
||||||
|
|
@ -146,15 +138,13 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
|
||||||
if ((ret != SR_OK) || !response)
|
if ((ret != SR_OK) || !response)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (strcmp(response, "HP3457A") != 0) {
|
if (strcmp(response, "HP3457A"))
|
||||||
g_free(response);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
g_free(response);
|
g_free(response);
|
||||||
|
|
||||||
devc = g_malloc0(sizeof(*devc));
|
devc = g_malloc0(sizeof(struct dev_context));
|
||||||
sdi = g_malloc0(sizeof(*sdi));
|
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
||||||
sdi->vendor = g_strdup("Hewlett-Packard");
|
sdi->vendor = g_strdup("Hewlett-Packard");
|
||||||
sdi->model = g_strdup("3457A");
|
sdi->model = g_strdup("3457A");
|
||||||
sdi->version = get_revision(scpi);
|
sdi->version = get_revision(scpi);
|
||||||
|
|
@ -178,14 +168,6 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
{
|
{
|
||||||
const char *conn;
|
|
||||||
|
|
||||||
/* Only scan for a device when conn= was specified. */
|
|
||||||
conn = NULL;
|
|
||||||
(void)sr_serial_extract_options(options, &conn, NULL);
|
|
||||||
if (!conn)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return sr_scpi_scan(di->context, options, probe_device);
|
return sr_scpi_scan(di->context, options, probe_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the libsigrok project.
|
* This file is part of the libsigrok project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2021 Frank Stettner <frank-stettner@gmx.net>
|
* Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -129,14 +129,6 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
|
||||||
struct sr_dev_inst *sdi;
|
struct sr_dev_inst *sdi;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
|
|
||||||
/*
|
|
||||||
* The device cannot get identified by means of SCPI queries.
|
|
||||||
* Neither shall non-SCPI requests get emitted before reliable
|
|
||||||
* identification of the device. Assume that we only get here
|
|
||||||
* when user specs led us to believe it's safe to communicate
|
|
||||||
* to the expected kind of device.
|
|
||||||
*/
|
|
||||||
|
|
||||||
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
||||||
sdi->vendor = g_strdup("Hewlett-Packard");
|
sdi->vendor = g_strdup("Hewlett-Packard");
|
||||||
sdi->model = g_strdup("3478A");
|
sdi->model = g_strdup("3478A");
|
||||||
|
|
@ -160,14 +152,6 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
{
|
{
|
||||||
const char *conn;
|
|
||||||
|
|
||||||
/* Only scan for a device when conn= was specified. */
|
|
||||||
conn = NULL;
|
|
||||||
(void)sr_serial_extract_options(options, &conn, NULL);
|
|
||||||
if (!conn)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return sr_scpi_scan(di->context, options, probe_device);
|
return sr_scpi_scan(di->context, options, probe_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +176,7 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
|
|
||||||
(void)cg;
|
(void)cg;
|
||||||
|
|
||||||
devc = sdi ? sdi->priv : NULL;
|
devc = sdi->priv;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
|
@ -203,7 +187,7 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
if (ret != SR_OK)
|
if (ret != SR_OK)
|
||||||
return ret;
|
return ret;
|
||||||
arr[0] = g_variant_new_uint32(devc->measurement_mq);
|
arr[0] = g_variant_new_uint32(devc->measurement_mq);
|
||||||
arr[1] = g_variant_new_uint64(devc->measurement_mq_flag);
|
arr[1] = g_variant_new_uint64(devc->measurement_mq_flags);
|
||||||
*data = g_variant_new_tuple(arr, 2);
|
*data = g_variant_new_tuple(arr, 2);
|
||||||
break;
|
break;
|
||||||
case SR_CONF_RANGE:
|
case SR_CONF_RANGE:
|
||||||
|
|
@ -213,7 +197,7 @@ static int config_get(uint32_t key, GVariant **data,
|
||||||
range_str = "Auto";
|
range_str = "Auto";
|
||||||
for (i = 0; i < ARRAY_SIZE(rangeopts); i++) {
|
for (i = 0; i < ARRAY_SIZE(rangeopts); i++) {
|
||||||
if (rangeopts[i].mq == devc->measurement_mq &&
|
if (rangeopts[i].mq == devc->measurement_mq &&
|
||||||
rangeopts[i].mqflag == devc->measurement_mq_flag &&
|
rangeopts[i].mqflag == devc->measurement_mq_flags &&
|
||||||
rangeopts[i].range_exp == devc->range_exp) {
|
rangeopts[i].range_exp == devc->range_exp) {
|
||||||
range_str = rangeopts[i].range_str;
|
range_str = rangeopts[i].range_str;
|
||||||
break;
|
break;
|
||||||
|
|
@ -247,7 +231,7 @@ static int config_set(uint32_t key, GVariant *data,
|
||||||
|
|
||||||
(void)cg;
|
(void)cg;
|
||||||
|
|
||||||
devc = sdi ? sdi->priv : NULL;
|
devc = sdi->priv;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
|
@ -256,7 +240,6 @@ static int config_set(uint32_t key, GVariant *data,
|
||||||
case SR_CONF_MEASURED_QUANTITY:
|
case SR_CONF_MEASURED_QUANTITY:
|
||||||
tuple_child = g_variant_get_child_value(data, 0);
|
tuple_child = g_variant_get_child_value(data, 0);
|
||||||
mq = g_variant_get_uint32(tuple_child);
|
mq = g_variant_get_uint32(tuple_child);
|
||||||
g_variant_unref(tuple_child);
|
|
||||||
tuple_child = g_variant_get_child_value(data, 1);
|
tuple_child = g_variant_get_child_value(data, 1);
|
||||||
mq_flags = g_variant_get_uint64(tuple_child);
|
mq_flags = g_variant_get_uint64(tuple_child);
|
||||||
g_variant_unref(tuple_child);
|
g_variant_unref(tuple_child);
|
||||||
|
|
@ -265,7 +248,7 @@ static int config_set(uint32_t key, GVariant *data,
|
||||||
range_str = g_variant_get_string(data, NULL);
|
range_str = g_variant_get_string(data, NULL);
|
||||||
for (i = 0; i < ARRAY_SIZE(rangeopts); i++) {
|
for (i = 0; i < ARRAY_SIZE(rangeopts); i++) {
|
||||||
if (rangeopts[i].mq == devc->measurement_mq &&
|
if (rangeopts[i].mq == devc->measurement_mq &&
|
||||||
rangeopts[i].mqflag == devc->measurement_mq_flag &&
|
rangeopts[i].mqflag == devc->measurement_mq_flags &&
|
||||||
g_strcmp0(rangeopts[i].range_str, range_str) == 0) {
|
g_strcmp0(rangeopts[i].range_str, range_str) == 0) {
|
||||||
return hp_3478a_set_range(sdi, rangeopts[i].range_exp);
|
return hp_3478a_set_range(sdi, rangeopts[i].range_exp);
|
||||||
}
|
}
|
||||||
|
|
@ -294,7 +277,7 @@ static int config_list(uint32_t key, GVariant **data,
|
||||||
GVariant *gvar, *arr[2];
|
GVariant *gvar, *arr[2];
|
||||||
GVariantBuilder gvb;
|
GVariantBuilder gvb;
|
||||||
|
|
||||||
devc = sdi ? sdi->priv : NULL;
|
devc = sdi->priv;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_SCAN_OPTIONS:
|
case SR_CONF_SCAN_OPTIONS:
|
||||||
|
|
@ -321,7 +304,7 @@ static int config_list(uint32_t key, GVariant **data,
|
||||||
g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
|
g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
|
||||||
for (i = 0; i < ARRAY_SIZE(rangeopts); i++) {
|
for (i = 0; i < ARRAY_SIZE(rangeopts); i++) {
|
||||||
if (rangeopts[i].mq == devc->measurement_mq &&
|
if (rangeopts[i].mq == devc->measurement_mq &&
|
||||||
rangeopts[i].mqflag == devc->measurement_mq_flag) {
|
rangeopts[i].mqflag == devc->measurement_mq_flags) {
|
||||||
g_variant_builder_add(&gvb, "s", rangeopts[i].range_str);
|
g_variant_builder_add(&gvb, "s", rangeopts[i].range_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the libsigrok project.
|
* This file is part of the libsigrok project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2021 Frank Stettner <frank-stettner@gmx.net>
|
* Copyright (C) 2017-2018 Frank Stettner <frank-stettner@gmx.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -69,8 +69,9 @@ SR_PRIV int hp_3478a_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq,
|
||||||
struct sr_scpi_dev_inst *scpi = sdi->conn;
|
struct sr_scpi_dev_inst *scpi = sdi->conn;
|
||||||
struct dev_context *devc = sdi->priv;
|
struct dev_context *devc = sdi->priv;
|
||||||
|
|
||||||
/* No need to send a command if we're not changing the measurement type. */
|
/* No need to send command if we're not changing measurement type. */
|
||||||
if (devc->measurement_mq == mq && devc->measurement_mq_flag == mq_flags)
|
if (devc->measurement_mq == mq &&
|
||||||
|
((devc->measurement_mq_flags & mq_flags) == mq_flags))
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(sr_mq_to_cmd_map); i++) {
|
for (i = 0; i < ARRAY_SIZE(sr_mq_to_cmd_map); i++) {
|
||||||
|
|
@ -226,17 +227,17 @@ static int parse_function_byte(struct dev_context *devc, uint8_t function_byte)
|
||||||
return SR_ERR_DATA;
|
return SR_ERR_DATA;
|
||||||
|
|
||||||
/* Function + Range */
|
/* Function + Range */
|
||||||
devc->measurement_mq_flag = 0;
|
devc->measurement_mq_flags = 0;
|
||||||
devc->acquisition_mq_flags = 0;
|
devc->acquisition_mq_flags = 0;
|
||||||
if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_VDC) {
|
if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_VDC) {
|
||||||
devc->measurement_mq = SR_MQ_VOLTAGE;
|
devc->measurement_mq = SR_MQ_VOLTAGE;
|
||||||
devc->measurement_mq_flag = SR_MQFLAG_DC;
|
devc->measurement_mq_flags |= SR_MQFLAG_DC;
|
||||||
devc->acquisition_mq_flags |= SR_MQFLAG_DC;
|
devc->acquisition_mq_flags |= SR_MQFLAG_DC;
|
||||||
devc->measurement_unit = SR_UNIT_VOLT;
|
devc->measurement_unit = SR_UNIT_VOLT;
|
||||||
parse_range_vdc(devc, function_byte);
|
parse_range_vdc(devc, function_byte);
|
||||||
} else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_VAC) {
|
} else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_VAC) {
|
||||||
devc->measurement_mq = SR_MQ_VOLTAGE;
|
devc->measurement_mq = SR_MQ_VOLTAGE;
|
||||||
devc->measurement_mq_flag = SR_MQFLAG_AC;
|
devc->measurement_mq_flags |= SR_MQFLAG_AC;
|
||||||
devc->acquisition_mq_flags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
devc->acquisition_mq_flags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
||||||
devc->measurement_unit = SR_UNIT_VOLT;
|
devc->measurement_unit = SR_UNIT_VOLT;
|
||||||
parse_range_vac(devc, function_byte);
|
parse_range_vac(devc, function_byte);
|
||||||
|
|
@ -246,19 +247,19 @@ static int parse_function_byte(struct dev_context *devc, uint8_t function_byte)
|
||||||
parse_range_ohm(devc, function_byte);
|
parse_range_ohm(devc, function_byte);
|
||||||
} else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_4WR) {
|
} else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_4WR) {
|
||||||
devc->measurement_mq = SR_MQ_RESISTANCE;
|
devc->measurement_mq = SR_MQ_RESISTANCE;
|
||||||
devc->measurement_mq_flag = SR_MQFLAG_FOUR_WIRE;
|
devc->measurement_mq_flags |= SR_MQFLAG_FOUR_WIRE;
|
||||||
devc->acquisition_mq_flags |= SR_MQFLAG_FOUR_WIRE;
|
devc->acquisition_mq_flags |= SR_MQFLAG_FOUR_WIRE;
|
||||||
devc->measurement_unit = SR_UNIT_OHM;
|
devc->measurement_unit = SR_UNIT_OHM;
|
||||||
parse_range_ohm(devc, function_byte);
|
parse_range_ohm(devc, function_byte);
|
||||||
} else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_ADC) {
|
} else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_ADC) {
|
||||||
devc->measurement_mq = SR_MQ_CURRENT;
|
devc->measurement_mq = SR_MQ_CURRENT;
|
||||||
devc->measurement_mq_flag = SR_MQFLAG_DC;
|
devc->measurement_mq_flags |= SR_MQFLAG_DC;
|
||||||
devc->acquisition_mq_flags |= SR_MQFLAG_DC;
|
devc->acquisition_mq_flags |= SR_MQFLAG_DC;
|
||||||
devc->measurement_unit = SR_UNIT_AMPERE;
|
devc->measurement_unit = SR_UNIT_AMPERE;
|
||||||
parse_range_a(devc, function_byte);
|
parse_range_a(devc, function_byte);
|
||||||
} else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_AAC) {
|
} else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_AAC) {
|
||||||
devc->measurement_mq = SR_MQ_CURRENT;
|
devc->measurement_mq = SR_MQ_CURRENT;
|
||||||
devc->measurement_mq_flag = SR_MQFLAG_AC;
|
devc->measurement_mq_flags |= SR_MQFLAG_AC;
|
||||||
devc->acquisition_mq_flags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
devc->acquisition_mq_flags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
||||||
devc->measurement_unit = SR_UNIT_AMPERE;
|
devc->measurement_unit = SR_UNIT_AMPERE;
|
||||||
parse_range_a(devc, function_byte);
|
parse_range_a(devc, function_byte);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the libsigrok project.
|
* This file is part of the libsigrok project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2021 Frank Stettner <frank-stettner@gmx.net>
|
* Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
#define SB1_RANGE_BLOCK 0b00011100
|
#define SB1_RANGE_BLOCK 0b00011100
|
||||||
#define SB1_DIGITS_BLOCK 0b00000011
|
#define SB1_DIGITS_BLOCK 0b00000011
|
||||||
|
|
||||||
/** Status Byte 1 (Function) */
|
/* Status Byte 1 (Function) */
|
||||||
enum sb1_function {
|
enum sb1_function {
|
||||||
FUNCTION_VDC = 0b00100000,
|
FUNCTION_VDC = 0b00100000,
|
||||||
FUNCTION_VAC = 0b01000000,
|
FUNCTION_VAC = 0b01000000,
|
||||||
|
|
@ -42,7 +42,7 @@ enum sb1_function {
|
||||||
FUNCTION_EXR = 0b11100000,
|
FUNCTION_EXR = 0b11100000,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Status Byte 1 (Range V DC) */
|
/* Status Byte 1 (Range V DC) */
|
||||||
enum sb1_range_vdc {
|
enum sb1_range_vdc {
|
||||||
RANGE_VDC_30MV = 0b00000100,
|
RANGE_VDC_30MV = 0b00000100,
|
||||||
RANGE_VDC_300MV = 0b00001000,
|
RANGE_VDC_300MV = 0b00001000,
|
||||||
|
|
@ -51,7 +51,7 @@ enum sb1_range_vdc {
|
||||||
RANGE_VDC_300V = 0b00010100,
|
RANGE_VDC_300V = 0b00010100,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Status Byte 1 (Range V AC) */
|
/* Status Byte 1 (Range V AC) */
|
||||||
enum sb1_range_vac {
|
enum sb1_range_vac {
|
||||||
RANGE_VAC_300MV = 0b00000100,
|
RANGE_VAC_300MV = 0b00000100,
|
||||||
RANGE_VAC_3V = 0b00001000,
|
RANGE_VAC_3V = 0b00001000,
|
||||||
|
|
@ -59,13 +59,13 @@ enum sb1_range_vac {
|
||||||
RANGE_VAC_300V = 0b00010000,
|
RANGE_VAC_300V = 0b00010000,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Status Byte 1 (Range A) */
|
/* Status Byte 1 (Range A) */
|
||||||
enum sb1_range_a {
|
enum sb1_range_a {
|
||||||
RANGE_A_300MA = 0b00000100,
|
RANGE_A_300MA = 0b00000100,
|
||||||
RANGE_A_3A = 0b00001000,
|
RANGE_A_3A = 0b00001000,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Status Byte 1 (Range Ohm) */
|
/* Status Byte 1 (Range Ohm) */
|
||||||
enum sb1_range_ohm {
|
enum sb1_range_ohm {
|
||||||
RANGE_OHM_30R = 0b00000100,
|
RANGE_OHM_30R = 0b00000100,
|
||||||
RANGE_OHM_300R = 0b00001000,
|
RANGE_OHM_300R = 0b00001000,
|
||||||
|
|
@ -76,14 +76,14 @@ enum sb1_range_ohm {
|
||||||
RANGE_OHM_30MR = 0b00011100,
|
RANGE_OHM_30MR = 0b00011100,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Status Byte 1 (Digits) */
|
/* Status Byte 1 (Digits) */
|
||||||
enum sb1_digits {
|
enum sb1_digits {
|
||||||
DIGITS_5_5 = 0b00000001,
|
DIGITS_5_5 = 0b00000001,
|
||||||
DIGITS_4_5 = 0b00000010,
|
DIGITS_4_5 = 0b00000010,
|
||||||
DIGITS_3_5 = 0b00000011,
|
DIGITS_3_5 = 0b00000011,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Status Byte 2 */
|
/* Status Byte 2 */
|
||||||
enum sb2_status {
|
enum sb2_status {
|
||||||
STATUS_INT_TRIGGER = (1 << 0),
|
STATUS_INT_TRIGGER = (1 << 0),
|
||||||
STATUS_AUTO_RANGE = (1 << 1),
|
STATUS_AUTO_RANGE = (1 << 1),
|
||||||
|
|
@ -94,7 +94,7 @@ enum sb2_status {
|
||||||
STATUS_EXT_TRIGGER = (1 << 6),
|
STATUS_EXT_TRIGGER = (1 << 6),
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Status Byte 3 (Serial Poll Mask) */
|
/* Status Byte 3 (Serial Poll Mask) */
|
||||||
enum sb3_srq {
|
enum sb3_srq {
|
||||||
SRQ_BUS_AVAIL = (1 << 0),
|
SRQ_BUS_AVAIL = (1 << 0),
|
||||||
SRQ_SYNTAX_ERR = (1 << 2),
|
SRQ_SYNTAX_ERR = (1 << 2),
|
||||||
|
|
@ -104,7 +104,7 @@ enum sb3_srq {
|
||||||
SRQ_POWER_ON = (1 << 7),
|
SRQ_POWER_ON = (1 << 7),
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Status Byte 4 (Error) */
|
/* Status Byte 4 (Error) */
|
||||||
enum sb4_error {
|
enum sb4_error {
|
||||||
ERROR_SELF_TEST = (1 << 0),
|
ERROR_SELF_TEST = (1 << 0),
|
||||||
ERROR_RAM_SELF_TEST = (1 << 1),
|
ERROR_RAM_SELF_TEST = (1 << 1),
|
||||||
|
|
@ -114,20 +114,20 @@ enum sb4_error {
|
||||||
ERROR_AD_LINK = (1 << 5),
|
ERROR_AD_LINK = (1 << 5),
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Channel connector (front terminals or rear terminals. */
|
/* Channel connector (front terminals or rear terminals. */
|
||||||
enum terminal_connector {
|
enum terminal_connector {
|
||||||
TERMINAL_FRONT,
|
TERMINAL_FRONT,
|
||||||
TERMINAL_REAR,
|
TERMINAL_REAR,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Available triggers */
|
/* Possible triggers */
|
||||||
enum trigger_state {
|
enum trigger_state {
|
||||||
TRIGGER_UNDEFINED,
|
TRIGGER_UNDEFINED,
|
||||||
TRIGGER_EXTERNAL,
|
TRIGGER_EXTERNAL,
|
||||||
TRIGGER_INTERNAL,
|
TRIGGER_INTERNAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Available line frequencies */
|
/* Possible line frequencies */
|
||||||
enum line_freq {
|
enum line_freq {
|
||||||
LINE_50HZ,
|
LINE_50HZ,
|
||||||
LINE_60HZ,
|
LINE_60HZ,
|
||||||
|
|
@ -138,15 +138,9 @@ struct dev_context {
|
||||||
|
|
||||||
double measurement;
|
double measurement;
|
||||||
enum sr_mq measurement_mq;
|
enum sr_mq measurement_mq;
|
||||||
/**
|
/** The measurement mq flags only contain flags for AC, DC and 4-wire. */
|
||||||
* The measurement mq flag can contain none or one of the
|
enum sr_mqflag measurement_mq_flags;
|
||||||
* following flags: AC, DC, or 4-wire.
|
/** The acquisition mq flags also contain flags for autoranging and RMS. */
|
||||||
*/
|
|
||||||
enum sr_mqflag measurement_mq_flag;
|
|
||||||
/**
|
|
||||||
* The acquisition mq flags can contain multiple flags,
|
|
||||||
* for example autoranging, RMS, etc.
|
|
||||||
*/
|
|
||||||
enum sr_mqflag acquisition_mq_flags;
|
enum sr_mqflag acquisition_mq_flags;
|
||||||
enum sr_unit measurement_unit;
|
enum sr_unit measurement_unit;
|
||||||
int range_exp;
|
int range_exp;
|
||||||
|
|
|
||||||
|
|
@ -1,179 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 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_MULTIPLEXER,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t devopts[] = {
|
|
||||||
/*
|
|
||||||
* TODO Enable/disable multiple channel groups at once.
|
|
||||||
* SR_CONF_ENABLED | SR_CONF_SET,
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t devopts_cg[] = {
|
|
||||||
SR_CONF_ENABLED | SR_CONF_SET,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct sr_dev_driver hp_59306a_driver_info;
|
|
||||||
|
|
||||||
static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
|
|
||||||
{
|
|
||||||
struct sr_dev_inst *sdi;
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct channel_group_context *cgc;
|
|
||||||
size_t idx, nr;
|
|
||||||
struct sr_channel_group *cg;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The device cannot get identified by means of SCPI queries.
|
|
||||||
* Neither shall non-SCPI requests get emitted before reliable
|
|
||||||
* identification of the device. Assume that we only get here
|
|
||||||
* when user specs led us to believe it's safe to communicate
|
|
||||||
* to the expected kind of device.
|
|
||||||
*/
|
|
||||||
|
|
||||||
sdi = g_malloc0(sizeof(*sdi));
|
|
||||||
sdi->vendor = g_strdup("Hewlett-Packard");
|
|
||||||
sdi->model = g_strdup("59306A");
|
|
||||||
sdi->conn = scpi;
|
|
||||||
sdi->driver = &hp_59306a_driver_info;
|
|
||||||
sdi->inst_type = SR_INST_SCPI;
|
|
||||||
|
|
||||||
devc = g_malloc0(sizeof(*devc));
|
|
||||||
sdi->priv = devc;
|
|
||||||
|
|
||||||
devc->channel_count = 6;
|
|
||||||
for (idx = 0; idx < devc->channel_count; idx++) {
|
|
||||||
nr = idx + 1;
|
|
||||||
|
|
||||||
cg = g_malloc0(sizeof(*cg));
|
|
||||||
cg->name = g_strdup_printf("CH%zu", nr);
|
|
||||||
|
|
||||||
cgc = g_malloc0(sizeof(*cgc));
|
|
||||||
cgc->number = nr;
|
|
||||||
cg->priv = cgc;
|
|
||||||
|
|
||||||
sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sdi;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|
||||||
{
|
|
||||||
const char *conn;
|
|
||||||
|
|
||||||
/* Only scan for a device when conn= was specified. */
|
|
||||||
conn = NULL;
|
|
||||||
(void)sr_serial_extract_options(options, &conn, NULL);
|
|
||||||
if (!conn)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
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_set(uint32_t key, GVariant *data,
|
|
||||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
|
||||||
{
|
|
||||||
gboolean on;
|
|
||||||
|
|
||||||
if (!cg) {
|
|
||||||
switch (key) {
|
|
||||||
/* TODO: Enable/disbale multiple channel groups at once. */
|
|
||||||
case SR_CONF_ENABLED:
|
|
||||||
default:
|
|
||||||
return SR_ERR_NA;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_ENABLED:
|
|
||||||
on = g_variant_get_boolean(data);
|
|
||||||
return hp_59306a_switch_cg(sdi, cg, on);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
default:
|
|
||||||
return SR_ERR_NA;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_DEVICE_OPTIONS:
|
|
||||||
*data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return SR_ERR_NA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sr_dev_driver hp_59306a_driver_info = {
|
|
||||||
.name = "hp-59306a",
|
|
||||||
.longname = "hp-59306a",
|
|
||||||
.api_version = 1,
|
|
||||||
.init = std_init,
|
|
||||||
.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_acquisition_start = std_dummy_dev_acquisition_start,
|
|
||||||
.dev_acquisition_stop = std_dummy_dev_acquisition_stop,
|
|
||||||
.context = NULL,
|
|
||||||
};
|
|
||||||
SR_REGISTER_DEV_DRIVER(hp_59306a_driver_info);
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 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"
|
|
||||||
|
|
||||||
SR_PRIV int hp_59306a_switch_cg(const struct sr_dev_inst *sdi,
|
|
||||||
const struct sr_channel_group *cg, gboolean enabled)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct sr_scpi_dev_inst *scpi;
|
|
||||||
struct channel_group_context *cgc;
|
|
||||||
|
|
||||||
scpi = sdi->conn;
|
|
||||||
cgc = cg->priv;
|
|
||||||
|
|
||||||
if (enabled)
|
|
||||||
ret = sr_scpi_send(scpi, "A%zu", cgc->number);
|
|
||||||
else
|
|
||||||
ret = sr_scpi_send(scpi, "B%zu", cgc->number);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 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_59306A_PROTOCOL_H
|
|
||||||
#define LIBSIGROK_HARDWARE_HP_59306A_PROTOCOL_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <glib.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
|
|
||||||
#define LOG_PREFIX "hp-59306a"
|
|
||||||
|
|
||||||
struct dev_context {
|
|
||||||
size_t channel_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct channel_group_context {
|
|
||||||
/** The number of the channel group, as labled on the device. */
|
|
||||||
size_t number;
|
|
||||||
};
|
|
||||||
|
|
||||||
SR_PRIV int hp_59306a_switch_cg(const struct sr_dev_inst *sdi,
|
|
||||||
const struct sr_channel_group *cg, gboolean enabled);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -396,8 +396,12 @@ static int read_subframe(const struct sr_dev_inst *sdi, uint8_t *buf)
|
||||||
if (interleave)
|
if (interleave)
|
||||||
num *= 2;
|
num *= 2;
|
||||||
if (!devc->step) {
|
if (!devc->step) {
|
||||||
|
struct sr_datafeed_packet packet = {
|
||||||
|
.type = SR_DF_TRIGGER
|
||||||
|
};
|
||||||
|
|
||||||
push_samples(sdi, buf, 6);
|
push_samples(sdi, buf, 6);
|
||||||
std_session_send_df_trigger(sdi);
|
sr_session_send(sdi, &packet);
|
||||||
buf += 6;
|
buf += 6;
|
||||||
num -= 6;
|
num -= 6;
|
||||||
}
|
}
|
||||||
|
|
@ -415,6 +419,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)
|
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 };
|
||||||
struct sr_dev_inst *sdi;
|
struct sr_dev_inst *sdi;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
uint8_t state, buf[1000];
|
uint8_t state, buf[1000];
|
||||||
|
|
@ -442,7 +447,7 @@ SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
std_session_send_df_frame_begin(sdi);
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
if (devc->channel) {
|
if (devc->channel) {
|
||||||
while (read_subframe(sdi, buf)) {
|
while (read_subframe(sdi, buf)) {
|
||||||
|
|
@ -455,7 +460,8 @@ SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std_session_send_df_frame_end(sdi);
|
packet.type = SR_DF_FRAME_END;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
if (++devc->frame >= devc->frame_limit)
|
if (++devc->frame >= devc->frame_limit)
|
||||||
sr_dev_acquisition_stop(sdi);
|
sr_dev_acquisition_stop(sdi);
|
||||||
|
|
|
||||||
|
|
@ -122,8 +122,10 @@ static void process_sample_data(const struct sr_dev_inst *sdi)
|
||||||
* through the capture ratio.
|
* through the capture ratio.
|
||||||
*/
|
*/
|
||||||
if (devc->trigger_type != TRIGGER_TYPE_NONE &&
|
if (devc->trigger_type != TRIGGER_TYPE_NONE &&
|
||||||
devc->pre_trigger_samples == 0)
|
devc->pre_trigger_samples == 0) {
|
||||||
std_session_send_df_trigger(sdi);
|
packet.type = SR_DF_TRIGGER;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; k >= 0; k--) {
|
for (; k >= 0; k--) {
|
||||||
|
|
@ -162,7 +164,8 @@ static void process_sample_data(const struct sr_dev_inst *sdi)
|
||||||
logic.data = buffer;
|
logic.data = buffer;
|
||||||
sr_session_send(sdi, &packet);
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
std_session_send_df_trigger(sdi);
|
packet.type = SR_DF_TRIGGER;
|
||||||
|
sr_session_send(sdi, &packet);
|
||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ static int dev_open(struct sr_dev_inst *sdi)
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = ftdi_tcioflush(devc->ftdic)) < 0) {
|
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
|
||||||
sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
|
sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
goto err_dev_open_close_ftdic;
|
goto err_dev_open_close_ftdic;
|
||||||
|
|
|
||||||
|
|
@ -1,495 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>,
|
|
||||||
* haskal <haskal@awoo.systems>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* 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_VENDOR_ID 0x0403
|
|
||||||
#define USB_PRODUCT_ID 0x7fd0
|
|
||||||
|
|
||||||
#define USB_IPRODUCT_25 "ScanaQuad SQ25"
|
|
||||||
#define USB_IPRODUCT_50 "ScanaQuad SQ50"
|
|
||||||
#define USB_IPRODUCT_100 "ScanaQuad SQ100"
|
|
||||||
#define USB_IPRODUCT_200 "ScanaQuad SQ200"
|
|
||||||
|
|
||||||
static struct sr_dev_driver ikalogic_scanaquad_driver_info;
|
|
||||||
|
|
||||||
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_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
|
|
||||||
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_LOGIC_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
|
||||||
SR_CONF_LOGIC_THRESHOLD_CUSTOM | SR_CONF_GET | SR_CONF_SET,
|
|
||||||
|
|
||||||
/*SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,*/
|
|
||||||
|
|
||||||
/* list of TODO stuff:
|
|
||||||
* * VOLTAGE_THRESHOLD vs LOGIC_THRESHOLD? LOGIC_THRESHOLD_CUSTOM?
|
|
||||||
* * VOLTAGE_THRESHOLD: 2 double values (min, max) with a slider selection GUI
|
|
||||||
* * LOGIC_THRESHOLD: selection of (max, not thresh value) voltages
|
|
||||||
* * LOGIC_THRESHOLD_CUSTOM: custom ^, so kinda boils down to VOLTAGE_THRESHOLD?
|
|
||||||
* * see kingst-la2016/api.c
|
|
||||||
* * currently implemnted as all of them, pulseview gives a simple slider
|
|
||||||
* without value indication, which is not that practical to use...
|
|
||||||
* LOGIC_THRESHOLD(_CUSTOM) seems to give no UI?
|
|
||||||
*
|
|
||||||
* * BUFFERSIZE, DATALOG, PATTERN_MODE: should this be used?
|
|
||||||
*
|
|
||||||
* * trigger stuff:
|
|
||||||
* * TRIGGER_MATCH: uncomment when TRIGGER_PATTERN is implemented
|
|
||||||
* * TRIGGER_PATTERN: freeform string
|
|
||||||
* * see asix-sigma/protocol.c:1603 , it has some proposed formats,
|
|
||||||
* however, these aren't sufficient here as the SQ series also allows
|
|
||||||
* varying pulse widths for each trigger step...
|
|
||||||
* * TRIGGER_LEVEL, TRIGGER_SOURCE: not applicable here
|
|
||||||
*
|
|
||||||
* * the SQ50 accepts 200 MHz samplerate and a buffer size longer than it
|
|
||||||
* has available physical backing for, so it's not unlikely that modding
|
|
||||||
* it to have more memory is possible. so maybe memory and frequency
|
|
||||||
* option shouldn't be strictly checked?
|
|
||||||
* see also: https://git.lain.faith/BLAHAJ/sq50-re/wiki/Hardware-info
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *channel_names[] = {
|
|
||||||
"1", "2", "3", "4"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint64_t samplerates[] = {
|
|
||||||
/* TODO: can this be an arbitrary value? at least the settings
|
|
||||||
* blob makes it look like it can */
|
|
||||||
/* also, looks like the SQ50 can totally do 100 and 200 MHz... */
|
|
||||||
SR_MHZ(200), SR_MHZ(100),
|
|
||||||
SR_MHZ( 50), SR_MHZ( 25), SR_MHZ( 10), SR_MHZ( 5), SR_MHZ( 1),
|
|
||||||
SR_KHZ(500), SR_KHZ(250), SR_KHZ(100), SR_KHZ(50), SR_KHZ(10)
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *thresholds[] = {
|
|
||||||
"1.8V", /* 0x46 0x1e | 1.8 0.8 ~44% */
|
|
||||||
"2.8V", /* 0x6e 0x2c | 2.8 1.1 ~40% */
|
|
||||||
"3.3V", /* 0x81 0x46 | 3.3 1.8 ~55% */
|
|
||||||
"3.6V", /* 0x8d 0x4f | 3.6 2.0 ~55% */
|
|
||||||
"5V" , /* 0xc4 0x72 | 5.0 2.9 ~58% */
|
|
||||||
"5VTTL",/* 0xc3 0x3b | 5.0 1.5 */
|
|
||||||
"User" /* VOLTAGE_THRESHOLD == LOGIC_THRESHOLD_CUSTOM (?) */
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int matches[] = {
|
|
||||||
SR_TRIGGER_ZERO,
|
|
||||||
SR_TRIGGER_ONE,
|
|
||||||
SR_TRIGGER_RISING,
|
|
||||||
SR_TRIGGER_FALLING,
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint64_t dc_samples_to_msec(struct dev_context *dc, uint64_t samples)
|
|
||||||
{
|
|
||||||
float msec_per_sample = 1.0f / ((float)dc->samplerate * 0.001f);
|
|
||||||
|
|
||||||
return (uint64_t)((float)samples * msec_per_sample);
|
|
||||||
}
|
|
||||||
static uint64_t dc_msec_to_samples(struct dev_context *dc, uint64_t msec)
|
|
||||||
{
|
|
||||||
float samples_per_msec = ((float)dc->samplerate * 0.001f);
|
|
||||||
|
|
||||||
return (uint64_t)((float)msec * samples_per_msec);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_clear(const struct sr_dev_driver *di)
|
|
||||||
{
|
|
||||||
return std_dev_clear_with_callback(di, (std_dev_clear_callback)sq_destroy);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|
||||||
{
|
|
||||||
struct sr_dev_inst *sdi;
|
|
||||||
struct dev_context *dc;
|
|
||||||
struct ftdi_context *ft;
|
|
||||||
char *manuf, *prod, *serial;
|
|
||||||
unsigned int i;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
(void)options;
|
|
||||||
|
|
||||||
if (!(ft = ftdi_new())) {
|
|
||||||
sr_err("Failed to initialize libftdi.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
dc = sq_new(ft);
|
|
||||||
if (dc == NULL) {
|
|
||||||
sr_err("Failed to initialize ScanaQuad device context.");
|
|
||||||
ftdi_free(ft);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* open the FTDI device for a second to 1) check it exists, and
|
|
||||||
* 2) check which device it is from the SQ series */
|
|
||||||
|
|
||||||
rv = ftdi_usb_open(ft, USB_VENDOR_ID, USB_PRODUCT_ID);
|
|
||||||
if (rv < 0) {
|
|
||||||
if (rv != -3) { /* -3: device not found */
|
|
||||||
sr_err("Failed to open device (%d): %s", rv, ftdi_get_error_string(ft));
|
|
||||||
sq_destroy(dc);
|
|
||||||
g_free(dc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = ftdi_read_eeprom(ft);
|
|
||||||
if (rv < 0) {
|
|
||||||
sr_err("Failted to read FTDI EEPROM (%d): %s", rv, ftdi_get_error_string(ft));
|
|
||||||
sq_destroy(dc);
|
|
||||||
g_free(dc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = ftdi_eeprom_decode(ft, 0);
|
|
||||||
/* retval -1 means a checksum error, which current libftdi
|
|
||||||
* is not equipped to deal with yet, as it only knows of the
|
|
||||||
* FT230X, while the SQ uses an FT240X */
|
|
||||||
if (rv < 0) {
|
|
||||||
sr_err("Failed to decode FTDI EEPROM (%d): %s", rv, ftdi_get_error_string(ft));
|
|
||||||
sq_destroy(dc);
|
|
||||||
g_free(dc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
manuf = g_malloc0(256);
|
|
||||||
prod = g_malloc0(256);
|
|
||||||
serial = g_malloc0(256);
|
|
||||||
rv = ftdi_eeprom_get_strings(ft, manuf, 256, prod, 256, serial, 256);
|
|
||||||
if (rv < 0) {
|
|
||||||
sr_err("Failed to get device info strings (%d): %s", rv, ftdi_get_error_string(ft));
|
|
||||||
g_free(serial);
|
|
||||||
g_free(prod );
|
|
||||||
g_free(manuf );
|
|
||||||
sq_destroy(dc);
|
|
||||||
g_free(dc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ftdi_usb_close(ft); /* enough for now */
|
|
||||||
|
|
||||||
if (strcmp(manuf, "IKALOGIC")) {
|
|
||||||
sr_warn("Unexpected manufacturer name %s!", manuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(prod, USB_IPRODUCT_25)) {
|
|
||||||
dc->devtype = 25;
|
|
||||||
dc->memsize_max = MAX_MEMSIZE_SQ50 / 2;
|
|
||||||
sr_info("%s", prod);
|
|
||||||
} else if (!strcmp(prod, USB_IPRODUCT_50)) {
|
|
||||||
dc->devtype = 50;
|
|
||||||
dc->memsize_max = MAX_MEMSIZE_SQ50;
|
|
||||||
sr_info("%s", prod);
|
|
||||||
} else if (!strcmp(prod, USB_IPRODUCT_100)) {
|
|
||||||
dc->devtype = 100;
|
|
||||||
dc->memsize_max = MAX_MEMSIZE_SQ50 * 2;
|
|
||||||
sr_info("%s", prod);
|
|
||||||
} else if (!strcmp(prod, USB_IPRODUCT_200)) {
|
|
||||||
dc->devtype = 200;
|
|
||||||
dc->memsize_max = MAX_MEMSIZE_SQ50 * 4;
|
|
||||||
sr_info("%s", prod);
|
|
||||||
} else {
|
|
||||||
sr_warn("Unexpected product name %s! Assuming SQ50 behavior...", prod);
|
|
||||||
dc->devtype = 50;
|
|
||||||
dc->memsize_max = MAX_MEMSIZE_SQ50;
|
|
||||||
}
|
|
||||||
dc->limit_samples = dc->memsize_max;
|
|
||||||
|
|
||||||
sr_info("%s ScanaQuad serial number: %s", manuf, serial);
|
|
||||||
|
|
||||||
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
|
||||||
sdi->status = SR_ST_INACTIVE;
|
|
||||||
sdi->vendor = g_strdup(manuf);
|
|
||||||
sdi->model = g_strdup(prod);
|
|
||||||
sdi->priv = dc;
|
|
||||||
|
|
||||||
g_free(serial);
|
|
||||||
g_free(prod );
|
|
||||||
g_free(manuf );
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(channel_names); ++i)
|
|
||||||
sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE/* enabled */, channel_names[i]);
|
|
||||||
|
|
||||||
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CHECK_FTDI_RETVAL(msg, ft, rv) \
|
|
||||||
do { \
|
|
||||||
if ((rv) < 0) { \
|
|
||||||
sr_err(msg " in %s (%d): %s.\n", __func__, (rv), ftdi_get_error_string(ft)); \
|
|
||||||
return SR_ERR; \
|
|
||||||
} \
|
|
||||||
} while (0) \
|
|
||||||
|
|
||||||
#define CHECK_SQ_RETVAL(rv, msg) \
|
|
||||||
do { \
|
|
||||||
if ((rv) < 0) { \
|
|
||||||
sr_err(msg "in %s (%d).\n", __func__, (rv)); \
|
|
||||||
return SR_ERR; \
|
|
||||||
} \
|
|
||||||
} while (0) \
|
|
||||||
|
|
||||||
static int dev_open(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *dc;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
dc = sdi->priv;
|
|
||||||
|
|
||||||
if (!dc || !dc->ft) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
rv = ftdi_set_interface(dc->ft, INTERFACE_A);
|
|
||||||
CHECK_FTDI_RETVAL("Failed to set FTDI interface A", dc->ft, rv);
|
|
||||||
|
|
||||||
rv = ftdi_usb_open(dc->ft, USB_VENDOR_ID, USB_PRODUCT_ID);
|
|
||||||
CHECK_FTDI_RETVAL("Failed to open FTDI device", dc->ft, rv);
|
|
||||||
|
|
||||||
rv = ftdi_tcioflush(dc->ft);
|
|
||||||
CHECK_FTDI_RETVAL("Failed to purge buffers", dc->ft, rv);
|
|
||||||
|
|
||||||
rv = ftdi_set_latency_timer(dc->ft, 2);
|
|
||||||
CHECK_FTDI_RETVAL("Failed to set FTDI latency timer", dc->ft, rv);
|
|
||||||
|
|
||||||
/*rv = ftdi_read_data_set_chunksize(dc->ft, 64*1024);
|
|
||||||
CHECK_FTDI_RETVAL("Failed to set FTDI read data chunk size", dc->ft, rv);*/
|
|
||||||
|
|
||||||
return scanaquad_init(dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_close(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *dc;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
dc = sdi->priv;
|
|
||||||
|
|
||||||
if (!dc || !dc->ft) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
rv = scanaquad_cancel_all(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
|
||||||
|
|
||||||
rv = ftdi_tcioflush(dc->ft);
|
|
||||||
CHECK_FTDI_RETVAL("Failed to purge buffers", dc->ft, rv);
|
|
||||||
|
|
||||||
rv = ftdi_usb_close(dc->ft);
|
|
||||||
CHECK_FTDI_RETVAL("Failed to close FTDI device", dc->ft, rv);
|
|
||||||
|
|
||||||
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 *dc;
|
|
||||||
double rnded;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
dc = sdi->priv;
|
|
||||||
|
|
||||||
(void)cg;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
rnded = (int)(dc->voltage.thresh * 10.0 / 39.2) * 0.1;
|
|
||||||
|
|
||||||
rv = SR_OK;
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_SAMPLERATE:
|
|
||||||
*data = g_variant_new_uint64(dc->samplerate);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_MSEC:
|
|
||||||
*data = g_variant_new_uint64(dc_samples_to_msec(dc, dc->limit_samples));
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
|
||||||
*data = g_variant_new_uint64(dc->limit_samples);
|
|
||||||
break;
|
|
||||||
case SR_CONF_CAPTURE_RATIO:
|
|
||||||
*data = g_variant_new_uint64(dc->capture_ratio);
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
|
||||||
*data = std_gvar_tuple_double(rnded, rnded + 0.1);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD:
|
|
||||||
*data = g_variant_new_string(thresholds[dc->voltage_idx]);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD_CUSTOM:
|
|
||||||
*data = g_variant_new_double(rnded);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sr_err("%s no conf key %u", __func__, key);
|
|
||||||
rv = SR_ERR_NA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int config_set(uint32_t key, GVariant *data,
|
|
||||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
|
||||||
{
|
|
||||||
struct dev_context *dc;
|
|
||||||
double volt, vlo, vhi;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
dc = sdi->priv;
|
|
||||||
|
|
||||||
(void)cg;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
if (key == SR_CONF_LOGIC_THRESHOLD_CUSTOM)
|
|
||||||
volt = g_variant_get_double(data);
|
|
||||||
|
|
||||||
rv = SR_OK;
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_SAMPLERATE:
|
|
||||||
dc->samplerate = g_variant_get_uint64(data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_MSEC:
|
|
||||||
dc->limit_samples = dc_msec_to_samples(dc, g_variant_get_uint64(data));
|
|
||||||
/* TODO: make this check optional */
|
|
||||||
if (dc->limit_samples > dc->memsize_max * 4)
|
|
||||||
dc->limit_samples = dc->memsize_max * 4;
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
|
||||||
dc->limit_samples = g_variant_get_uint64(data);
|
|
||||||
/* TODO: make this check optional */
|
|
||||||
if (dc->limit_samples > dc->memsize_max * 4)
|
|
||||||
dc->limit_samples = dc->memsize_max * 4;
|
|
||||||
break;
|
|
||||||
case SR_CONF_CAPTURE_RATIO:
|
|
||||||
dc->capture_ratio = g_variant_get_uint64(data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
|
||||||
g_variant_get(data, "(dd)", &vlo, &vhi);
|
|
||||||
volt = 0.5 * (vlo + vhi);
|
|
||||||
/* fallthru! */
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD_CUSTOM:
|
|
||||||
/* in the LOGIC_THRESHOLD_CUSTOM case: see 'if' stmt before switch */
|
|
||||||
dc->voltage_idx = 6; /* user */
|
|
||||||
dc->voltage.thresh = (int)(volt * 39.2);
|
|
||||||
/* emulate vendor software values in a naive way. not 100% exact but
|
|
||||||
* oh well. */
|
|
||||||
dc->voltage.level = (dc->voltage.thresh < 0x30)
|
|
||||||
? (int)(volt * 39.2 / 0.40)
|
|
||||||
: (int)(volt * 39.2 / 0.55);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sr_err("%s no conf key %u", __func__, key);
|
|
||||||
rv = SR_ERR_NA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int config_list(uint32_t key, GVariant **data,
|
|
||||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv = SR_OK;
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_DEVICE_OPTIONS:
|
|
||||||
return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
|
|
||||||
case SR_CONF_SAMPLERATE:
|
|
||||||
*data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
|
||||||
*data = std_gvar_min_max_step_thresholds(0.8, 2.9, 0.1);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD:
|
|
||||||
*data = g_variant_new_strv(ARRAY_AND_SIZE(thresholds));
|
|
||||||
break;
|
|
||||||
case SR_CONF_TRIGGER_MATCH:
|
|
||||||
*data = std_gvar_array_i32(ARRAY_AND_SIZE(matches));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sr_err("%s no conf key %u", __func__, key);
|
|
||||||
rv = SR_ERR_NA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *dc;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
dc = sdi->priv;
|
|
||||||
|
|
||||||
if (!dc || !dc->ft) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
rv = scanaquad_acquisition_start(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to start acquisition");
|
|
||||||
|
|
||||||
std_session_send_df_header(sdi);
|
|
||||||
sr_session_source_add(sdi->session, -1, 0, 0, scanaquad_receive_data, (void *)sdi);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *dc;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
dc = sdi->priv;
|
|
||||||
|
|
||||||
if (!dc || !dc->ft) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
sr_session_source_remove(sdi->session, -1);
|
|
||||||
std_session_send_df_end(sdi);
|
|
||||||
|
|
||||||
rv = scanaquad_acquisition_stop(sdi, dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to stop acquisition");
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sr_dev_driver ikalogic_scanaquad_driver_info = {
|
|
||||||
.name = "ikalogic-scanaquad",
|
|
||||||
.longname = "IKALOGIC ScanaQuad",
|
|
||||||
.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(ikalogic_scanaquad_driver_info);
|
|
||||||
|
|
@ -1,827 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>,
|
|
||||||
* haskal <haskal@awoo.systems>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* 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 struct dev_context *sq_new(struct ftdi_context *ft)
|
|
||||||
{
|
|
||||||
struct dev_context *dc;
|
|
||||||
|
|
||||||
if (!ft) return NULL;
|
|
||||||
|
|
||||||
dc = (struct dev_context *)g_malloc0(sizeof(struct dev_context));
|
|
||||||
dc->ft = ft;
|
|
||||||
|
|
||||||
/* some sensible defaults (mostly the vendor software defaults) */
|
|
||||||
dc->samplerate = SR_MHZ(25);
|
|
||||||
dc->capture_ratio = 10; /* 10% in */
|
|
||||||
dc->voltage.level = 0x81; /* 3.3v */
|
|
||||||
dc->voltage.thresh = 0x46; /* 1.8v (3.3v cmos thresh) */
|
|
||||||
dc->voltage_idx = 2; /* "3.3V" */
|
|
||||||
|
|
||||||
return dc;
|
|
||||||
}
|
|
||||||
SR_PRIV void sq_destroy(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
if (!dc) return;
|
|
||||||
|
|
||||||
if (dc->ft) {
|
|
||||||
ftdi_free(dc->ft);
|
|
||||||
dc->ft = NULL;
|
|
||||||
}
|
|
||||||
/*g_free(dc);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CHECK_FTDI_RETVAL(write, ft, rv, expect, ...) \
|
|
||||||
do { \
|
|
||||||
if ((rv) < 0) { \
|
|
||||||
sr_err("Failed to %s FTDI data in %s (%d): %s.\n", (write)?"write":"read", __func__, (rv), \
|
|
||||||
ftdi_get_error_string(ft)); \
|
|
||||||
return SR_ERR; \
|
|
||||||
} else if ((size_t)(rv) != (expect) __VA_OPT__(&& __VA_ARGS__)) { \
|
|
||||||
sr_err("FTDI %s error in %s, only %d/%zu bytes %s: %s.", (write)?"write":"read", __func__,\
|
|
||||||
(rv), expect, (write)?"written":"read", ftdi_get_error_string(ft)); \
|
|
||||||
return SR_ERR; \
|
|
||||||
} \
|
|
||||||
} while (0) \
|
|
||||||
|
|
||||||
#define CHECK_SQ_RETVAL(rv, fmt, ...) \
|
|
||||||
do { \
|
|
||||||
if ((rv) < SR_ERR) { \
|
|
||||||
if (FALSE) { \
|
|
||||||
sr_err(fmt " in %s (%d)", ##__VA_ARGS__ , __func__, (rv)); \
|
|
||||||
} else { \
|
|
||||||
sr_err(fmt "in %s", ##__VA_ARGS__, __func__); \
|
|
||||||
} \
|
|
||||||
return rv; \
|
|
||||||
} \
|
|
||||||
} while (0) \
|
|
||||||
|
|
||||||
/* low-level commands */
|
|
||||||
|
|
||||||
/* this is a macro instead of an inline static fn, because CHECK_FTDI_RETVAL
|
|
||||||
* uses the __func__ pseudomacro for diagnostic purposes, which would be
|
|
||||||
* rendered useless if this were a real function */
|
|
||||||
#define read_ftdi_blocking(ft, data, s) ({\
|
|
||||||
size_t __i = 0; \
|
|
||||||
int __rv = 0; \
|
|
||||||
do { \
|
|
||||||
__rv = ftdi_read_data(ft, (uint8_t *)(data)+__i, (s)-__i); \
|
|
||||||
CHECK_FTDI_RETVAL(FALSE, ft, __rv, (s)-__i, FALSE); \
|
|
||||||
/*sr_spew("read_blocking: read %d\n", __rv);*/ \
|
|
||||||
__i += __rv; \
|
|
||||||
} while (__i < s); \
|
|
||||||
__rv;}) \
|
|
||||||
|
|
||||||
SR_PRIV int sq_get_status(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t get_status_cmd[] = {0xfd,0x00,0x01,0x02,0xfe};
|
|
||||||
uint8_t stat[4];
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, get_status_cmd, sizeof get_status_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof get_status_cmd);
|
|
||||||
|
|
||||||
rv = read_ftdi_blocking(dc->ft, stat, sizeof stat);
|
|
||||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, sizeof stat);
|
|
||||||
|
|
||||||
if (stat[0] != stat[1] || stat[0] != stat[2] || stat[0] != stat[3])
|
|
||||||
sr_warn("status: incoherent: %02x %02x %02x %02x", stat[0], stat[1], stat[2], stat[3]);
|
|
||||||
else
|
|
||||||
sr_spew("status: %02x", stat[0]);
|
|
||||||
|
|
||||||
return stat[0]; /* array contains the same value 4 times */
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_reset_app(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t go_app_cmd[] = {0x93};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("reset to app");
|
|
||||||
rv = ftdi_write_data(dc->ft, go_app_cmd, sizeof go_app_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof go_app_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_reset_bl(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t go_bl_cmd[] = {0x94};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("reset to bl");
|
|
||||||
rv = ftdi_write_data(dc->ft, go_bl_cmd, sizeof go_bl_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof go_bl_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_get_devid_from_eeprom(struct dev_context *dc, uint8_t *devid)
|
|
||||||
{
|
|
||||||
uint16_t val1, val2;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
if ((rv = ftdi_read_eeprom_location(dc->ft, 0x12, &val1)) < 0) {
|
|
||||||
sr_err("Failed to read EEPROM index 0x12 (%d): %s.",
|
|
||||||
rv, ftdi_get_error_string(dc->ft));
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
if ((rv = ftdi_read_eeprom_location(dc->ft, 0x13, &val2)) < 0) {
|
|
||||||
sr_err("Failed to read EEPROM index 0x13 (%d): %s.",
|
|
||||||
rv, ftdi_get_error_string(dc->ft));
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
devid[0] = (val1 >> 0) & 0xff;
|
|
||||||
devid[1] = (val1 >> 8) & 0xff;
|
|
||||||
devid[2] = (val2 >> 0) & 0xff;
|
|
||||||
|
|
||||||
sr_dbg("got devid %02x %02x %02x", devid[0], devid[1], devid[2]);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int sq_bl_send_devid(struct dev_context *dc, const uint8_t *devid)
|
|
||||||
{
|
|
||||||
uint8_t send_devid_cmd[27] = {0xf1, 0,0,0, 0};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc || !devid) return SR_ERR;
|
|
||||||
|
|
||||||
send_devid_cmd[1] = devid[0];
|
|
||||||
send_devid_cmd[2] = devid[1];
|
|
||||||
send_devid_cmd[3] = devid[2];
|
|
||||||
|
|
||||||
sr_spew("auth with devid %02x %02x %02x", devid[0], devid[1], devid[2]);
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_devid_cmd, sizeof send_devid_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_devid_cmd);
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_bl_spi_chipsel(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t send_spi_chipsel_cmd[2] = {0x90,0x00};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("SPI chip select");
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_spi_chipsel_cmd, sizeof send_spi_chipsel_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_spi_chipsel_cmd);
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_bl_spi_chipdesel(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t send_spi_chipdesel_cmd[2] = {0x91,0x00};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("SPI chip deselect");
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_spi_chipdesel_cmd, sizeof send_spi_chipdesel_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_spi_chipdesel_cmd);
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_bl_spi_xfer(struct dev_context *dc, uint8_t val)
|
|
||||||
{
|
|
||||||
uint8_t send_spi_xfer_cmd[2] = {0x92, 0};
|
|
||||||
int rv;
|
|
||||||
uint8_t read = 0;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_spi_xfer_cmd, sizeof send_spi_xfer_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_spi_xfer_cmd);
|
|
||||||
|
|
||||||
rv = read_ftdi_blocking(dc->ft, &read, 1);
|
|
||||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, (size_t)1);
|
|
||||||
|
|
||||||
sr_spew("SPI xfer: %02x -> %02x", val, read);
|
|
||||||
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int sq_app_cancel_capture(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t send_capture_cancel_cmd[] = {0xf0,0x00};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("cancel sc/wft");
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_capture_cancel_cmd, sizeof send_capture_cancel_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_capture_cancel_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_app_start_capture(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t send_start_capture_cmd[] = {0xf0,0x01};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
sr_spew("sc/wft");
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_start_capture_cmd, sizeof send_start_capture_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_start_capture_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_app_try_get_capture_result(struct dev_context *dc, uint32_t *trig_instant)
|
|
||||||
{
|
|
||||||
uint32_t ti;
|
|
||||||
uint8_t result[4];
|
|
||||||
int rv, todo, done;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
todo = 4;
|
|
||||||
done = 0;
|
|
||||||
|
|
||||||
rv = ftdi_read_data(dc->ft, &result[done], todo - done);
|
|
||||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, (size_t)0, FALSE);
|
|
||||||
|
|
||||||
if (rv == 0) return SR_ERR_ARG;
|
|
||||||
|
|
||||||
done += rv;
|
|
||||||
|
|
||||||
while (done < todo) {
|
|
||||||
rv = ftdi_read_data(dc->ft, &result[done], todo - done);
|
|
||||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, (size_t)0, FALSE);
|
|
||||||
done += rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
ti = (uint32_t)result[0] | ((uint32_t)result[1] << 8) | ((uint32_t)result[2] << 16);
|
|
||||||
if (trig_instant) *trig_instant = ti;
|
|
||||||
|
|
||||||
sr_spew("sc/wft -> %06x %02x", ti, result[3]);
|
|
||||||
|
|
||||||
return result[3];
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_app_start_generate_inf(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t send_start_geninf_cmd[] = {0xf0,0x02};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("start generate inf");
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_start_geninf_cmd, sizeof send_start_geninf_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_start_geninf_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_app_start_capgenmix(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t send_start_capgenmix_cmd[] = {0xf0,0x03};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
sr_spew("start mixed sc/g/wft");
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_start_capgenmix_cmd, sizeof send_start_capgenmix_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_start_capgenmix_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_app_upload_genpattern(struct dev_context *dc, const void *data, size_t len)
|
|
||||||
{
|
|
||||||
static const uint8_t send_upl_genpat_cmd[] = {0xf0,0x05,0xf3};
|
|
||||||
int rv;
|
|
||||||
size_t pos = 0;
|
|
||||||
|
|
||||||
if (!dc || !data || !len) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("upload genpatt %zx", len);
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_upl_genpat_cmd, sizeof send_upl_genpat_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_upl_genpat_cmd);
|
|
||||||
|
|
||||||
/* TODO: use async libftdi api? */
|
|
||||||
do {
|
|
||||||
rv = ftdi_write_data(dc->ft, (const uint8_t *)data + pos, len - pos);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, len, FALSE);
|
|
||||||
|
|
||||||
pos += rv;
|
|
||||||
} while (pos < len);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_app_download_capture(struct dev_context *dc, void *data, size_t len)
|
|
||||||
{
|
|
||||||
static const uint8_t send_dl_capt_cmd[] = {0xf0,0x06};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc || !data || !len) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("download capture %zx", len);
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_dl_capt_cmd, sizeof send_dl_capt_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_dl_capt_cmd);
|
|
||||||
|
|
||||||
rv = read_ftdi_blocking(dc->ft, data, len);
|
|
||||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, len);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_app_start_generate_once(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
static const uint8_t send_start_genonce_cmd[] = {0xf0,0x07};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
sr_spew("start generate once");
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_start_genonce_cmd, sizeof send_start_genonce_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_start_genonce_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int sq_app_apply_settings(struct dev_context *dc, const struct sq_app_settings *sett)
|
|
||||||
{
|
|
||||||
uint8_t send_sett_cmd[25] = {0xf1, 0};
|
|
||||||
uint8_t *blob;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc || !sett) return SR_ERR;
|
|
||||||
|
|
||||||
blob = &send_sett_cmd[1]; /* so i wont mess up offsets while writing this code */
|
|
||||||
|
|
||||||
blob[0x00] = sett->trigscale_us;
|
|
||||||
blob[0x01] = sett->clockfreq & 0xff;
|
|
||||||
blob[0x02] = (sett->clockfreq>>8) & 0xff;
|
|
||||||
blob[0x03] = sett->trigger_pw_scale & 0xff;
|
|
||||||
blob[0x04] = (sett->trigger_pw_scale>>8) & 0xff;
|
|
||||||
blob[0x05] = sett->memsetting1[0];
|
|
||||||
blob[0x06] = sett->memsetting1[1];
|
|
||||||
blob[0x07] = sett->memsetting1[2];
|
|
||||||
blob[0x08] = sett->memsetting2[0];
|
|
||||||
blob[0x09] = sett->memsetting2[1];
|
|
||||||
blob[0x0a] = sett->memsetting2[2];
|
|
||||||
blob[0x0b] = sett->memsetting3[0];
|
|
||||||
blob[0x0c] = sett->memsetting3[1];
|
|
||||||
blob[0x0d] = sett->memsetting3[2];
|
|
||||||
blob[0x0e] = 0;
|
|
||||||
blob[0x0f] = sett->ntrigsteps;
|
|
||||||
blob[0x10] = 0xf0;
|
|
||||||
blob[0x11] = 0x0f;
|
|
||||||
blob[0x12] = sett->chanoutmap;
|
|
||||||
blob[0x13] = sett->voltage[0];
|
|
||||||
blob[0x14] = sett->voltage[1];
|
|
||||||
blob[0x15] = 0x32;
|
|
||||||
blob[0x16] = sett->capture ? 0x01 : 0x00;
|
|
||||||
blob[0x17] = sett->generate ? 0x01 : 0x00;
|
|
||||||
|
|
||||||
sr_spew("apply settings"); /* TODO: print info about applied settings */
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_sett_cmd, sizeof send_sett_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_sett_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int sq_app_apply_triggers(struct dev_context *dc, const struct sq_app_trigstep *steps, size_t nsteps)
|
|
||||||
{
|
|
||||||
uint8_t *send_trig_cmd;
|
|
||||||
size_t i, blobsize;
|
|
||||||
uint32_t bitfield;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc || !steps || !nsteps) return SR_ERR;
|
|
||||||
|
|
||||||
/* nsteps shouldn't exceed 8 or so */
|
|
||||||
blobsize = 1 + nsteps * 4;
|
|
||||||
send_trig_cmd = (uint8_t *)g_malloc0(blobsize);
|
|
||||||
send_trig_cmd[0] = 0xf4;
|
|
||||||
|
|
||||||
for (i = 0; i < nsteps; ++i) {
|
|
||||||
bitfield = 0;
|
|
||||||
|
|
||||||
bitfield |= (steps[i].level ? 1 : 0) << 31;
|
|
||||||
bitfield |= (steps[i].pw_max & 0x1ff) << 21;
|
|
||||||
bitfield |= (steps[i].lvloverride ? 1 : 0) << 20;
|
|
||||||
bitfield |= (steps[i].pw_min & 0x1ff) << 10;
|
|
||||||
bitfield |= (steps[i].ch_ignore & 0xf) << 6;
|
|
||||||
bitfield |= (steps[i].nomax ? 1 : 0) << 5;
|
|
||||||
bitfield |= (steps[i].nomin ? 1 : 0) << 4;
|
|
||||||
bitfield |= (steps[i].ch_hi_rise_lo_fall & 0xf) << 0;
|
|
||||||
|
|
||||||
send_trig_cmd[1 + i*4 + 0] = (bitfield>> 0)&0xff;
|
|
||||||
send_trig_cmd[1 + i*4 + 1] = (bitfield>> 8)&0xff;
|
|
||||||
send_trig_cmd[1 + i*4 + 2] = (bitfield>>16)&0xff;
|
|
||||||
send_trig_cmd[1 + i*4 + 3] = (bitfield>>14)&0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
sr_spew("apply triggers"); /* TODO: print info about applied triggers */
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_trig_cmd, blobsize);
|
|
||||||
g_free(send_trig_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_trig_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int sq_app_apply_default_settings(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
/* buffer sizes meant for SQ25, but won't hurt on others I guess */
|
|
||||||
static const uint8_t send_sdef_cmd[25] = {
|
|
||||||
0xf1,
|
|
||||||
0x01, 0x04, 0x00, 0x00, 0x00, 0x48, 0xe8, 0x01,
|
|
||||||
0x48, 0xe8, 0x01, 0x08, 0x28, 0xf1, 0x00, 0x00,
|
|
||||||
0xf0, 0x0f, 0x0f, 0x81, 0x4b, 0x32, 0x01, 0x00
|
|
||||||
};
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sr_spew("apply default settings");
|
|
||||||
|
|
||||||
rv = ftdi_write_data(dc->ft, send_sdef_cmd, sizeof send_sdef_cmd);
|
|
||||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_sdef_cmd);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* higher-level routines (finally) */
|
|
||||||
SR_PRIV int scanaquad_init(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sr_info("scanaquad: init!");
|
|
||||||
|
|
||||||
rv = sq_get_status(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't get status");
|
|
||||||
|
|
||||||
if (rv == sq_status_app) {
|
|
||||||
rv = sq_app_cancel_capture(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't cancel capture/wait-for-trigger");
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = sq_reset_bl(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't reset to bootloader");
|
|
||||||
|
|
||||||
/* wait until we are in bootloader mode */
|
|
||||||
for (i = 0; i < 0x1000; ++i) {
|
|
||||||
rv = sq_get_status(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't get status");
|
|
||||||
if (rv != sq_status_app) break;
|
|
||||||
}
|
|
||||||
if (rv == sq_status_app) {
|
|
||||||
sr_err("Failed to enter bootloader mode (timed out)");
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dc->has_devid) {
|
|
||||||
rv = sq_get_devid_from_eeprom(dc, &dc->devid[0]);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't get magic device ID");
|
|
||||||
dc->has_devid = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = sq_bl_send_devid(dc, dc->devid);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't send magic device ID");
|
|
||||||
|
|
||||||
for (i = 0; i < 0x1000 /* should be enough */; ++i) {
|
|
||||||
rv = sq_get_status(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't get status");
|
|
||||||
if (rv == sq_status_bl_auth) break;
|
|
||||||
}
|
|
||||||
if (rv != sq_status_bl_auth) {
|
|
||||||
sr_err("Failed to authenticate ScanaQuad device (timed out)... devid=%02x %02x %02x",
|
|
||||||
dc->devid[0], dc->devid[1], dc->devid[2]);
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = sq_reset_app(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't reset to application");
|
|
||||||
|
|
||||||
for (i = 0; i < 0x1000 /* should be enough */; ++i) {
|
|
||||||
rv = sq_get_status(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "couldn't get status");
|
|
||||||
if (rv == sq_status_app) break;
|
|
||||||
}
|
|
||||||
if (rv != sq_status_app) {
|
|
||||||
sr_err("Failed to enter application mode (timed out)");
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sq_app_apply_default_settings(dc);
|
|
||||||
}
|
|
||||||
SR_PRIV int scanaquad_cancel_all(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sr_spew("cancel everything");
|
|
||||||
|
|
||||||
dc->acq_started = FALSE;
|
|
||||||
|
|
||||||
rv = sq_get_status(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to get ScanaQuad status");
|
|
||||||
|
|
||||||
if (rv == sq_status_app) {
|
|
||||||
/* cancel ongoing capture/wait-for-trigger */
|
|
||||||
rv = sq_app_cancel_capture(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing SC/WFT");
|
|
||||||
|
|
||||||
/* cancel ongoing pattern generation */
|
|
||||||
rv = scanaquad_apply_current_settings(dc, TRUE);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to apply default settings");
|
|
||||||
|
|
||||||
/* cancel ongoing capture/wait-for-trigger */
|
|
||||||
rv = sq_app_cancel_capture(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing SC/WFT");
|
|
||||||
|
|
||||||
/* cancel ongoing pattern generation */
|
|
||||||
rv = scanaquad_apply_current_settings(dc, TRUE);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to apply default settings");
|
|
||||||
}
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int scanaquad_apply_current_settings(struct dev_context *dc, gboolean passive)
|
|
||||||
{
|
|
||||||
/* if passive:
|
|
||||||
* * no triggers
|
|
||||||
* * set to capture mode(?) */
|
|
||||||
|
|
||||||
uint64_t samplim;
|
|
||||||
uint32_t ms1, ms2, ms3;
|
|
||||||
struct sq_app_settings sett;
|
|
||||||
|
|
||||||
/* clockfreq == 100000 / samplerate_kHz */
|
|
||||||
sett.clockfreq = (uint16_t)((100000uLL*1000) / dc->samplerate);
|
|
||||||
sett.voltage[0] = dc->voltage.level;
|
|
||||||
sett.voltage[1] = passive ? 0x4b : dc->voltage.thresh;
|
|
||||||
sett.chanoutmap = 0x0f; /* TODO: hardcoded: all inputs */
|
|
||||||
|
|
||||||
/* TODO: more than just capture mode */
|
|
||||||
samplim = dc->limit_samples / 4;
|
|
||||||
/* sample limit vs LA physical memory size check is done in api.c */
|
|
||||||
ms1 = samplim;
|
|
||||||
ms2 = samplim;
|
|
||||||
ms3 = (uint32_t)(dc->memsize_max * (1 - dc->capture_ratio * 0.01));
|
|
||||||
|
|
||||||
sett.memsetting1[0] = (ms1>> 0) & 0xff;
|
|
||||||
sett.memsetting1[1] = (ms1>> 8) & 0xff;
|
|
||||||
sett.memsetting1[2] = (ms1>>16) & 0xff;
|
|
||||||
sett.memsetting2[0] = (ms2>> 0) & 0xff;
|
|
||||||
sett.memsetting2[1] = (ms2>> 8) & 0xff;
|
|
||||||
sett.memsetting2[2] = (ms2>>16) & 0xff;
|
|
||||||
sett.memsetting3[0] = (ms3>> 0) & 0xff;
|
|
||||||
sett.memsetting3[1] = (ms3>> 8) & 0xff;
|
|
||||||
sett.memsetting3[2] = (ms3>>16) & 0xff;
|
|
||||||
|
|
||||||
/* TODO */
|
|
||||||
sett.trigscale_us = 0x01;
|
|
||||||
sett.ntrigsteps = 0;
|
|
||||||
sett.trigger_pw_scale = 0;
|
|
||||||
sett.capture = TRUE;
|
|
||||||
sett.generate = FALSE;
|
|
||||||
|
|
||||||
dc->MS_capture = ms1;
|
|
||||||
dc->MS_generate = ms2 - ms1;
|
|
||||||
|
|
||||||
return sq_app_apply_settings(dc, &sett);
|
|
||||||
}
|
|
||||||
SR_PRIV int scanaquad_apply_current_triggers(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
(void)dc;
|
|
||||||
return SR_OK; /* HUGE TODO */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* finally, we can implement the higher-level functions. */
|
|
||||||
|
|
||||||
|
|
||||||
SR_PRIV int scanaquad_acquisition_start(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc) return SR_ERR;
|
|
||||||
|
|
||||||
if (dc->acq_started) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
sr_spew("data acquisition start");
|
|
||||||
|
|
||||||
rv = scanaquad_cancel_all(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
|
||||||
|
|
||||||
rv = sq_get_status(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to get current device state");
|
|
||||||
if (rv != sq_status_app) {
|
|
||||||
sr_err("Device not in application mode.");
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = scanaquad_apply_current_settings(dc, FALSE);
|
|
||||||
if (rv == SR_OK) rv = scanaquad_apply_current_triggers(dc);
|
|
||||||
if (rv < 0) {
|
|
||||||
/* rollback */
|
|
||||||
scanaquad_apply_current_settings(dc, TRUE);
|
|
||||||
sq_app_cancel_capture(dc);
|
|
||||||
}
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to apply device settings");
|
|
||||||
|
|
||||||
rv = sq_get_status(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to get current device state");
|
|
||||||
if (rv != sq_status_app) {
|
|
||||||
sr_err("Device not in application mode.");
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = sq_app_cancel_capture(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
|
||||||
|
|
||||||
rv = sq_app_start_capture(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to start capture");
|
|
||||||
|
|
||||||
dc->acq_started = TRUE;
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scanaquad_unpack_samples(uint8_t *rdata, size_t totalunpackbytes)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = totalunpackbytes / 2; i > 0; --i) {
|
|
||||||
rdata[i*2-1] = (rdata[i-1] & 0xf0) >> 4;
|
|
||||||
rdata[i*2-2] = (rdata[i-1] & 0x0f) >> 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int scanaquad_acquisition_finish(struct sr_dev_inst *sdi, struct dev_context *dc)
|
|
||||||
{
|
|
||||||
struct sr_datafeed_packet packet1, packet2, packet3;
|
|
||||||
struct sr_datafeed_logic logic1, logic3;
|
|
||||||
size_t nbytes;
|
|
||||||
uint8_t *rdata;
|
|
||||||
uint32_t trig_instant;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (!dc || !dc->acq_started) return SR_ERR_BUG;
|
|
||||||
|
|
||||||
sr_spew("gracefully finish acquisition");
|
|
||||||
|
|
||||||
trig_instant = dc->trig_instant;
|
|
||||||
dc->acq_started = FALSE;
|
|
||||||
|
|
||||||
sr_spew("trig_instant = %u", trig_instant);
|
|
||||||
|
|
||||||
nbytes = dc->MS_capture * 2;
|
|
||||||
/* we need 2x the amount of bytes we'll read from the device, as these are
|
|
||||||
* currently packed per nybbles, while sigrok wants the samples in separate
|
|
||||||
* bytes */
|
|
||||||
rdata = g_try_malloc(nbytes * 2);
|
|
||||||
if (!rdata) {
|
|
||||||
sr_err("Out of memory: cannot download captured samples");
|
|
||||||
|
|
||||||
/* rollback */
|
|
||||||
scanaquad_apply_current_settings(dc, TRUE);
|
|
||||||
sq_app_cancel_capture(dc);
|
|
||||||
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
/* convert from trig_instant units (MS1*16) to bytes */
|
|
||||||
trig_instant = (trig_instant * 2) / 8;
|
|
||||||
|
|
||||||
rv = sq_app_cancel_capture(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
|
||||||
|
|
||||||
rv = sq_app_download_capture(dc, rdata, nbytes);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to download captured samples");
|
|
||||||
|
|
||||||
rv = sq_app_cancel_capture(dc);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
|
||||||
|
|
||||||
rv = scanaquad_apply_current_settings(dc, TRUE);
|
|
||||||
CHECK_SQ_RETVAL(rv, "Failed to reset ScanaQuad settings");
|
|
||||||
|
|
||||||
scanaquad_unpack_samples(rdata, nbytes * 2);
|
|
||||||
|
|
||||||
/* 3 packets, for trigger indicator */
|
|
||||||
logic1.length = trig_instant;
|
|
||||||
logic1.unitsize = 1;
|
|
||||||
logic1.data = rdata;
|
|
||||||
packet1.type = SR_DF_LOGIC;
|
|
||||||
packet1.payload = &logic1;
|
|
||||||
|
|
||||||
packet2.type = SR_DF_TRIGGER;
|
|
||||||
packet2.payload = NULL;
|
|
||||||
|
|
||||||
logic3.length = (nbytes * 2) - trig_instant;
|
|
||||||
logic3.unitsize = 1;
|
|
||||||
logic3.data = rdata + trig_instant;
|
|
||||||
packet3.type = SR_DF_LOGIC;
|
|
||||||
packet3.payload = &logic3;
|
|
||||||
|
|
||||||
sr_spew("data sendoff! 2nb=%zu l1=%zu l3=%zu", nbytes*2, logic1.length, logic3.length);
|
|
||||||
|
|
||||||
sr_session_send(sdi, &packet1);
|
|
||||||
sr_session_send(sdi, &packet2);
|
|
||||||
sr_session_send(sdi, &packet3);
|
|
||||||
|
|
||||||
/* i hope this is fine to do? seems like it does work just fine with pulseview */
|
|
||||||
g_free(rdata);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
SR_PRIV int scanaquad_acquisition_stop(struct sr_dev_inst *sdi, struct dev_context *dc)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
sr_spew("force-stop acquisition");
|
|
||||||
|
|
||||||
if (!dc->acq_started) {
|
|
||||||
sr_spew("already stopped");
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = sq_app_try_get_capture_result(dc, &dc->trig_instant);
|
|
||||||
if (rv == SR_ERR_ARG) { /* still busy */
|
|
||||||
sr_info("Canceling capture");
|
|
||||||
return scanaquad_cancel_all(dc);
|
|
||||||
} else if (rv < 0) {
|
|
||||||
sr_err("Cannot get device state / capture result");
|
|
||||||
return SR_ERR;
|
|
||||||
} else if (rv == SQ_APP_START_CAPTURE_SUCCESS) {
|
|
||||||
return scanaquad_acquisition_finish(sdi, dc);
|
|
||||||
} else {
|
|
||||||
sr_err("Error during capture: %02x", rv);
|
|
||||||
return SR_ERR_BUG;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int scanaquad_receive_data(int fd, int revents, void *cb_data)
|
|
||||||
{
|
|
||||||
struct sr_dev_inst *sdi;
|
|
||||||
struct dev_context *dc;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
(void)fd;
|
|
||||||
(void)revents;
|
|
||||||
|
|
||||||
if (!(sdi = cb_data)) return TRUE;
|
|
||||||
if (!(dc = sdi->priv)) return TRUE;
|
|
||||||
if (!dc->ft) return TRUE;
|
|
||||||
|
|
||||||
/*if (revents != G_IO_IN) return TRUE;*/
|
|
||||||
|
|
||||||
rv = sq_app_try_get_capture_result(dc, &dc->trig_instant);
|
|
||||||
if (rv == SR_ERR_ARG) { /* still busy */
|
|
||||||
sr_spew("Acquisition still busy...");
|
|
||||||
return TRUE;
|
|
||||||
} else if (rv < 0) {
|
|
||||||
sr_err("Cannot get device state / capture result");
|
|
||||||
sr_dev_acquisition_stop(sdi);
|
|
||||||
return FALSE;
|
|
||||||
} else if (rv == SQ_APP_START_CAPTURE_SUCCESS) {
|
|
||||||
rv = scanaquad_acquisition_finish(sdi, dc);
|
|
||||||
if (rv == SR_ERR) {
|
|
||||||
sr_err("acquisition_finish failed");
|
|
||||||
sr_dev_acquisition_stop(sdi);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
sr_spew("acquisition finished!");
|
|
||||||
sr_dev_acquisition_stop(sdi);
|
|
||||||
return TRUE;
|
|
||||||
} else {
|
|
||||||
sr_err("Error during acquisition: %02x", rv);
|
|
||||||
sr_dev_acquisition_stop(sdi);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>,
|
|
||||||
* haskal <haskal@awoo.systems>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef LIBSIGROK_HARDWARE_IKALOGIC_SCANAQUAD_PROTOCOL_H
|
|
||||||
#define LIBSIGROK_HARDWARE_IKALOGIC_SCANAQUAD_PROTOCOL_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <glib.h>
|
|
||||||
#include <ftdi.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
|
|
||||||
#define LOG_PREFIX "ikalogic-scanaquad"
|
|
||||||
|
|
||||||
#define MAX_MEMSIZE_SQ50 0x03d090
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For info on how the ScanaQuad works, see our docs here:
|
|
||||||
* https://git.lain.faith/BLAHAJ/sq50-re/wiki
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct voltage_setting {
|
|
||||||
uint8_t level;
|
|
||||||
uint8_t thresh;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dev_context {
|
|
||||||
struct ftdi_context *ft;
|
|
||||||
|
|
||||||
/* settings for next capture */
|
|
||||||
uint64_t samplerate;
|
|
||||||
uint64_t limit_samples; /* samples */
|
|
||||||
uint64_t capture_ratio; /* 0..100 */
|
|
||||||
struct voltage_setting voltage;
|
|
||||||
int16_t voltage_idx;
|
|
||||||
|
|
||||||
uint32_t trig_instant;
|
|
||||||
|
|
||||||
uint32_t MS_capture;
|
|
||||||
uint32_t MS_generate;
|
|
||||||
|
|
||||||
uint32_t memsize_max;
|
|
||||||
int devtype; /* 25, 50, 100 or 200 for SQ25, SQ50, and so on */
|
|
||||||
uint8_t devid[3];
|
|
||||||
gboolean has_devid;
|
|
||||||
|
|
||||||
gboolean acq_started;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum sq_status {
|
|
||||||
sq_status_bl_auth = 0x01,
|
|
||||||
sq_status_bl_boot = 0x09,
|
|
||||||
sq_status_app = 0x22
|
|
||||||
};
|
|
||||||
struct sq_app_settings {
|
|
||||||
uint16_t clockfreq;
|
|
||||||
uint16_t trigger_pw_scale;
|
|
||||||
uint8_t memsetting1[3];
|
|
||||||
uint8_t memsetting2[3];
|
|
||||||
uint8_t memsetting3[3];
|
|
||||||
uint8_t voltage[2];
|
|
||||||
uint8_t trigscale_us;
|
|
||||||
uint8_t ntrigsteps;
|
|
||||||
uint8_t chanoutmap;
|
|
||||||
|
|
||||||
gboolean capture;
|
|
||||||
gboolean generate;
|
|
||||||
};
|
|
||||||
struct sq_app_trigstep {
|
|
||||||
uint16_t pw_min;
|
|
||||||
uint16_t pw_max;
|
|
||||||
|
|
||||||
uint8_t ch_ignore; /* set bit in this mask to ignore a channel */
|
|
||||||
uint8_t ch_hi_rise_lo_fall; /* 1 in mask => match high/rise for chan, else low/fall */
|
|
||||||
|
|
||||||
gboolean level; /* false: edge */
|
|
||||||
gboolean lvloverride;
|
|
||||||
gboolean nomax;
|
|
||||||
gboolean nomin;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SQ_APP_START_CAPTURE_SUCCESS 0xdd
|
|
||||||
|
|
||||||
SR_PRIV struct dev_context *sq_new(struct ftdi_context *ft);
|
|
||||||
SR_PRIV void sq_destroy(struct dev_context *dc);
|
|
||||||
|
|
||||||
/* low-level wire protocol functions. see
|
|
||||||
* https://git.lain.faith/BLAHAJ/sq50-re/wiki/USB-protocol for docs on how
|
|
||||||
* these work. */
|
|
||||||
|
|
||||||
/* general commands */
|
|
||||||
SR_PRIV int sq_get_status(struct dev_context *dc);
|
|
||||||
SR_PRIV int sq_reset_app(struct dev_context *dc);
|
|
||||||
SR_PRIV int sq_reset_bl (struct dev_context *dc);
|
|
||||||
SR_PRIV int sq_get_devid_from_eeprom(struct dev_context *dc, uint8_t *devid);
|
|
||||||
|
|
||||||
/* bootloader mode commands */
|
|
||||||
SR_PRIV int sq_bl_send_devid(struct dev_context *dc, const uint8_t *devid);
|
|
||||||
SR_PRIV int sq_bl_spi_chipsel(struct dev_context *dc);
|
|
||||||
SR_PRIV int sq_bl_spi_chipdesel(struct dev_context *dc);
|
|
||||||
SR_PRIV int sq_bl_spi_xfer(struct dev_context *dc, uint8_t val);
|
|
||||||
/* two aliases for clarity */
|
|
||||||
static inline int sq_bl_spi_xfer_write(struct dev_context *dc, uint8_t val)
|
|
||||||
{
|
|
||||||
return sq_bl_spi_xfer(dc, val);
|
|
||||||
}
|
|
||||||
static inline int sq_bl_spi_xfer_read(struct dev_context *dc)
|
|
||||||
{
|
|
||||||
return sq_bl_spi_xfer(dc, 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* application mode commands */
|
|
||||||
/* 0xf0 0x0X commands */
|
|
||||||
SR_PRIV int sq_app_cancel_capture(struct dev_context *dc);
|
|
||||||
SR_PRIV int sq_app_start_capture(struct dev_context *dc);
|
|
||||||
SR_PRIV int sq_app_try_get_capture_result(struct dev_context *dc, uint32_t *trig_instant);
|
|
||||||
SR_PRIV int sq_app_start_generate_inf(struct dev_context *dc);
|
|
||||||
SR_PRIV int sq_app_start_capgenmix(struct dev_context *dc);
|
|
||||||
static inline int sq_app_try_get_capgenmix_result(struct dev_context *dc, uint32_t *trig_instant) {
|
|
||||||
return sq_app_try_get_capture_result(dc, trig_instant);
|
|
||||||
}
|
|
||||||
/* NOTE: at this point, no checks are done whether the length of the data is
|
|
||||||
* consistent with the current capture/genpattern memory size in the
|
|
||||||
* device's settings */
|
|
||||||
SR_PRIV int sq_app_upload_genpattern(struct dev_context *dc, const void *data, size_t len);
|
|
||||||
SR_PRIV int sq_app_download_capture(struct dev_context *dc, void *data, size_t len);
|
|
||||||
SR_PRIV int sq_app_start_generate_once(struct dev_context *dc);
|
|
||||||
|
|
||||||
/* 0xf1, 0xf4 */
|
|
||||||
SR_PRIV int sq_app_apply_settings(struct dev_context *dc, const struct sq_app_settings *sett);
|
|
||||||
SR_PRIV int sq_app_apply_triggers(struct dev_context *dc, const struct sq_app_trigstep *steps, size_t nsteps);
|
|
||||||
|
|
||||||
/* useful at device init & reset, as it cancels ongoing signal generator stuff */
|
|
||||||
SR_PRIV int sq_app_apply_default_settings(struct dev_context *dc);
|
|
||||||
|
|
||||||
/* higher-level stuff used by api.c */
|
|
||||||
|
|
||||||
SR_PRIV int scanaquad_init(struct dev_context *dc);
|
|
||||||
SR_PRIV int scanaquad_cancel_all(struct dev_context *dc);
|
|
||||||
SR_PRIV int scanaquad_apply_current_settings(struct dev_context *dc, gboolean passive);
|
|
||||||
SR_PRIV int scanaquad_apply_current_triggers(struct dev_context *dc);
|
|
||||||
|
|
||||||
SR_PRIV int scanaquad_acquisition_start(struct dev_context *dc);
|
|
||||||
/* stuff to do when an acquisition ended successfully */
|
|
||||||
SR_PRIV int scanaquad_acquisition_finish(struct sr_dev_inst *sdi, struct dev_context *dc);
|
|
||||||
/* either finish when possible right now, or abort */
|
|
||||||
SR_PRIV int scanaquad_acquisition_stop(struct sr_dev_inst *sdi, struct dev_context *dc);
|
|
||||||
SR_PRIV int scanaquad_receive_data(int fd, int revents, void *cb_data);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -338,7 +338,8 @@ SR_PRIV int ipdbg_la_receive_data(int fd, int revents, void *cb_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send the trigger. */
|
/* Send the trigger. */
|
||||||
std_session_send_df_trigger(cb_data);
|
packet.type = SR_DF_TRIGGER;
|
||||||
|
sr_session_send(cb_data, &packet);
|
||||||
|
|
||||||
/* Send post-trigger samples. */
|
/* Send post-trigger samples. */
|
||||||
packet.type = SR_DF_LOGIC;
|
packet.type = SR_DF_LOGIC;
|
||||||
|
|
|
||||||
|
|
@ -1,749 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Timo Kokkonen <tjko@iki.fi>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "protocol.h"
|
|
||||||
|
|
||||||
#define MIN_SAMPLE_RATE SR_HZ(1)
|
|
||||||
#define MAX_SAMPLE_RATE SR_HZ(60)
|
|
||||||
#define DEFAULT_SAMPLE_RATE SR_HZ(10)
|
|
||||||
|
|
||||||
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_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
|
|
||||||
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
|
|
||||||
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t devopts_cg[] = {
|
|
||||||
SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
|
|
||||||
SR_CONF_REGULATION | 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_POWER | SR_CONF_GET,
|
|
||||||
SR_CONF_POWER_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
|
||||||
SR_CONF_RESISTANCE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
|
||||||
SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | 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_ENABLED | SR_CONF_GET,
|
|
||||||
SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
|
|
||||||
SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
|
|
||||||
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_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET,
|
|
||||||
SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint64_t samplerates[] = {
|
|
||||||
SR_HZ(1),
|
|
||||||
SR_HZ(2),
|
|
||||||
SR_HZ(5),
|
|
||||||
SR_HZ(10),
|
|
||||||
SR_HZ(15),
|
|
||||||
SR_HZ(20),
|
|
||||||
SR_HZ(30),
|
|
||||||
SR_HZ(40),
|
|
||||||
SR_HZ(50),
|
|
||||||
SR_HZ(60),
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *default_serial_parameters[] = {
|
|
||||||
"9600/8n1", /* Factory default. */
|
|
||||||
"38400/8n1",
|
|
||||||
"19200/8n1",
|
|
||||||
"4800/8n1",
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct sr_dev_driver itech_it8500_driver_info;
|
|
||||||
|
|
||||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|
||||||
{
|
|
||||||
struct sr_dev_inst *sdi;
|
|
||||||
struct sr_config *conf;
|
|
||||||
struct sr_serial_dev_inst *serial;
|
|
||||||
struct sr_channel_group *cg;
|
|
||||||
struct sr_channel *ch;
|
|
||||||
struct dev_context *devc;
|
|
||||||
const char *custom_serial_parameters[2];
|
|
||||||
const char **serial_parameters;
|
|
||||||
const char *conn, *serialcomm;
|
|
||||||
GSList *l;
|
|
||||||
struct itech_it8500_cmd_packet *cmd, *response;
|
|
||||||
uint8_t fw_major, fw_minor;
|
|
||||||
const uint8_t *p;
|
|
||||||
char *unit_model, *unit_serial, *unit_barcode;
|
|
||||||
double max_i, max_v, min_v, max_p, max_r, min_r;
|
|
||||||
uint64_t max_samplerate;
|
|
||||||
|
|
||||||
size_t u, i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
cmd = g_malloc0(sizeof(*cmd));
|
|
||||||
devc = g_malloc0(sizeof(*devc));
|
|
||||||
sdi = g_malloc0(sizeof(*sdi));
|
|
||||||
if (!cmd || !devc || !sdi)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
serial = NULL;
|
|
||||||
response = NULL;
|
|
||||||
unit_model = NULL;
|
|
||||||
unit_serial = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Use a list of typical parameters for serial communication by
|
|
||||||
* default. Prefer user specified parameters when available.
|
|
||||||
* Lack of a user specified serial port is fatal.
|
|
||||||
*/
|
|
||||||
conn = NULL;
|
|
||||||
serialcomm = NULL;
|
|
||||||
serial_parameters = default_serial_parameters;
|
|
||||||
for (l = options; l; l = l->next) {
|
|
||||||
conf = l->data;
|
|
||||||
switch (conf->key) {
|
|
||||||
case SR_CONF_CONN:
|
|
||||||
conn = g_variant_get_string(conf->data, NULL);
|
|
||||||
break;
|
|
||||||
case SR_CONF_SERIALCOMM:
|
|
||||||
serialcomm = g_variant_get_string(conf->data, NULL);
|
|
||||||
custom_serial_parameters[0] = serialcomm;
|
|
||||||
custom_serial_parameters[1] = NULL;
|
|
||||||
serial_parameters = custom_serial_parameters;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!conn)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try different serial parameters in the list
|
|
||||||
* until we get a response (or none at all).
|
|
||||||
*/
|
|
||||||
sr_info("Probing serial port: %s", conn);
|
|
||||||
for (i = 0; (serialcomm = serial_parameters[i]); i++) {
|
|
||||||
serial = sr_serial_dev_inst_new(conn, serialcomm);
|
|
||||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
|
||||||
goto error;
|
|
||||||
serial_flush(serial);
|
|
||||||
|
|
||||||
cmd->address = 0xff; /* Use "broadcast" address. */
|
|
||||||
cmd->command = CMD_GET_MODEL_INFO;
|
|
||||||
if (itech_it8500_send_cmd(serial, cmd, &response) == SR_OK)
|
|
||||||
break;
|
|
||||||
|
|
||||||
serial_close(serial);
|
|
||||||
sr_serial_dev_inst_free(serial);
|
|
||||||
serial = NULL;
|
|
||||||
}
|
|
||||||
if (!serialcomm)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The "dense" response string consists of several fields. Grab
|
|
||||||
* integer data before putting terminators in their place to
|
|
||||||
* grab text strings afterwards. Order is important here.
|
|
||||||
*/
|
|
||||||
devc->address = response->address;
|
|
||||||
fw_major = response->data[6];
|
|
||||||
fw_minor = response->data[5];
|
|
||||||
response->data[5] = 0;
|
|
||||||
unit_model = g_strdup((const char *)&response->data[0]);
|
|
||||||
response->data[17] = 0;
|
|
||||||
unit_serial = g_strdup((const char *)&response->data[7]);
|
|
||||||
sr_info("Model name: %s (v%x.%02x)", unit_model, fw_major, fw_minor);
|
|
||||||
sr_info("Address: %d", devc->address);
|
|
||||||
sr_info("Serial number: %s", unit_serial);
|
|
||||||
|
|
||||||
sdi->status = SR_ST_INACTIVE;
|
|
||||||
sdi->conn = serial;
|
|
||||||
sdi->inst_type = SR_INST_SERIAL;
|
|
||||||
sdi->driver = &itech_it8500_driver_info;
|
|
||||||
sdi->priv = devc;
|
|
||||||
g_mutex_init(&devc->mutex);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate maxium "safe" sample rate based on serial connection
|
|
||||||
* speed / bitrate.
|
|
||||||
*/
|
|
||||||
max_samplerate = serial->comm_params.bit_rate * 15 / 9600;
|
|
||||||
if (max_samplerate < 15)
|
|
||||||
max_samplerate = 10;
|
|
||||||
if (max_samplerate > MAX_SAMPLE_RATE)
|
|
||||||
max_samplerate = MAX_SAMPLE_RATE;
|
|
||||||
devc->max_sample_rate_idx = 0;
|
|
||||||
for (u = 0; u < ARRAY_SIZE(samplerates); u++) {
|
|
||||||
if (samplerates[u] > max_samplerate)
|
|
||||||
break;
|
|
||||||
devc->max_sample_rate_idx = u;
|
|
||||||
}
|
|
||||||
devc->sample_rate = DEFAULT_SAMPLE_RATE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get full serial number (barcode).
|
|
||||||
*/
|
|
||||||
cmd->address = devc->address;
|
|
||||||
cmd->command = CMD_GET_BARCODE_INFO;
|
|
||||||
if (itech_it8500_send_cmd(serial, cmd, &response) == SR_OK) {
|
|
||||||
unit_barcode = g_malloc0(IT8500_DATA_LEN + 1);
|
|
||||||
memcpy(unit_barcode, response->data, IT8500_DATA_LEN);
|
|
||||||
sr_info("Barcode: %s", response->data);
|
|
||||||
g_free(unit_barcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Query unit capabilities.
|
|
||||||
*/
|
|
||||||
cmd->command = CMD_GET_LOAD_LIMITS;
|
|
||||||
if (itech_it8500_send_cmd(serial, cmd, &response) != SR_OK)
|
|
||||||
goto error;
|
|
||||||
p = response->data;
|
|
||||||
max_i = read_u32le_inc(&p) / 10000.0;
|
|
||||||
max_v = read_u32le_inc(&p) / 1000.0;
|
|
||||||
min_v = read_u32le_inc(&p) / 1000.0;
|
|
||||||
max_p = read_u32le_inc(&p) / 1000.0;
|
|
||||||
max_r = read_u32le_inc(&p) / 1000.0;
|
|
||||||
min_r = read_u16le_inc(&p) / 1000.0;
|
|
||||||
sr_info("Max current: %.0f A", max_i);
|
|
||||||
sr_info("Max power: %.0f W", max_p);
|
|
||||||
sr_info("Voltage range: %.1f - %.1f V", min_v, max_v);
|
|
||||||
sr_info("Resistance range: %.2f - %.2f Ohm", min_r, max_r);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get current status of the unit.
|
|
||||||
*/
|
|
||||||
if ((ret = itech_it8500_get_status(sdi)) != SR_OK) {
|
|
||||||
sr_err("Failed to get unit status: %d", ret);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
sr_info("Mode: %s", itech_it8500_mode_to_string(devc->mode));
|
|
||||||
sr_info("State: %s", devc->load_on ? "ON" : "OFF");
|
|
||||||
sr_info("Default sample rate: %" PRIu64 " Hz", devc->sample_rate);
|
|
||||||
sr_info("Maximum sample rate: %" PRIu64 " Hz",
|
|
||||||
samplerates[devc->max_sample_rate_idx]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Populate data structures.
|
|
||||||
*/
|
|
||||||
|
|
||||||
devc->fw_ver_major = fw_major;
|
|
||||||
devc->fw_ver_minor = fw_minor;
|
|
||||||
snprintf(devc->model, sizeof(devc->model), "%s", unit_model);
|
|
||||||
devc->max_current = max_i;
|
|
||||||
devc->min_voltage = min_v;
|
|
||||||
devc->max_voltage = max_v;
|
|
||||||
devc->max_power = max_p;
|
|
||||||
devc->min_resistance = min_r;
|
|
||||||
devc->max_resistance = max_r;
|
|
||||||
|
|
||||||
sdi->vendor = g_strdup("ITECH");
|
|
||||||
sdi->model = unit_model;
|
|
||||||
sdi->version = g_strdup_printf("%x.%02x", fw_major, fw_minor);
|
|
||||||
sdi->serial_num = unit_serial;
|
|
||||||
|
|
||||||
cg = g_malloc0(sizeof(*cg));
|
|
||||||
cg->name = g_strdup("1");
|
|
||||||
sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
|
|
||||||
ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V1");
|
|
||||||
cg->channels = g_slist_append(cg->channels, ch);
|
|
||||||
ch = sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I1");
|
|
||||||
cg->channels = g_slist_append(cg->channels, ch);
|
|
||||||
ch = sr_channel_new(sdi, 2, SR_CHANNEL_ANALOG, TRUE, "P1");
|
|
||||||
cg->channels = g_slist_append(cg->channels, ch);
|
|
||||||
|
|
||||||
g_free(cmd);
|
|
||||||
g_free(response);
|
|
||||||
serial_close(serial);
|
|
||||||
|
|
||||||
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
|
||||||
|
|
||||||
error:
|
|
||||||
g_free(cmd);
|
|
||||||
g_free(devc);
|
|
||||||
g_free(sdi);
|
|
||||||
g_free(response);
|
|
||||||
g_free(unit_model);
|
|
||||||
g_free(unit_serial);
|
|
||||||
if (serial) {
|
|
||||||
serial_close(serial);
|
|
||||||
sr_serial_dev_inst_free(serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 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;
|
|
||||||
const struct sr_key_info *kinfo;
|
|
||||||
const char *mode;
|
|
||||||
int ret, ival;
|
|
||||||
gboolean bval;
|
|
||||||
|
|
||||||
(void)cg;
|
|
||||||
|
|
||||||
if (!data || !sdi)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
kinfo = sr_key_info_get(SR_KEY_CONFIG, key);
|
|
||||||
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_SAMPLERATE:
|
|
||||||
*data = g_variant_new_uint64(devc->sample_rate);
|
|
||||||
break;
|
|
||||||
case SR_CONF_ENABLED:
|
|
||||||
ret = itech_it8500_get_status(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_boolean(devc->load_on);
|
|
||||||
break;
|
|
||||||
case SR_CONF_REGULATION:
|
|
||||||
ret = itech_it8500_get_status(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
mode = itech_it8500_mode_to_string(devc->mode);
|
|
||||||
*data = g_variant_new_string(mode);
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE:
|
|
||||||
ret = itech_it8500_get_status(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double(devc->voltage);
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_TARGET:
|
|
||||||
ret = itech_it8500_get_int(sdi, CMD_GET_CV_VOLTAGE, &ival);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double((double)ival / 1000.0);
|
|
||||||
break;
|
|
||||||
case SR_CONF_CURRENT:
|
|
||||||
ret = itech_it8500_get_status(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double(devc->current);
|
|
||||||
break;
|
|
||||||
case SR_CONF_CURRENT_LIMIT:
|
|
||||||
ret = itech_it8500_get_int(sdi, CMD_GET_CC_CURRENT, &ival);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double((double)ival / 10000.0);
|
|
||||||
break;
|
|
||||||
case SR_CONF_POWER:
|
|
||||||
ret = itech_it8500_get_status(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double(devc->power);
|
|
||||||
break;
|
|
||||||
case SR_CONF_POWER_TARGET:
|
|
||||||
ret = itech_it8500_get_int(sdi, CMD_GET_CW_POWER, &ival);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double((double)ival / 1000.0);
|
|
||||||
break;
|
|
||||||
case SR_CONF_RESISTANCE_TARGET:
|
|
||||||
ret = itech_it8500_get_int(sdi, CMD_GET_CR_RESISTANCE, &ival);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double((double)ival / 1000.0);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
|
|
||||||
*data = g_variant_new_boolean(TRUE);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE:
|
|
||||||
ret = itech_it8500_get_status(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
bval = devc->demand_state & DS_OV_FLAG;
|
|
||||||
*data = g_variant_new_boolean(bval);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
|
|
||||||
ret = itech_it8500_get_int(sdi, CMD_GET_MAX_VOLTAGE, &ival);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double((double)ival / 1000.0);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
|
|
||||||
*data = g_variant_new_boolean(TRUE);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE:
|
|
||||||
ret = itech_it8500_get_status(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
bval = devc->demand_state & DS_OC_FLAG;
|
|
||||||
*data = g_variant_new_boolean(bval);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
|
|
||||||
ret = itech_it8500_get_int(sdi, CMD_GET_MAX_CURRENT, &ival);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
*data = g_variant_new_double((double)ival / 10000.0);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_TEMPERATURE_PROTECTION:
|
|
||||||
*data = g_variant_new_boolean(TRUE);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE:
|
|
||||||
ret = itech_it8500_get_status(sdi);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
break;
|
|
||||||
bval = devc->demand_state & DS_OT_FLAG;
|
|
||||||
*data = g_variant_new_boolean(bval);
|
|
||||||
break;
|
|
||||||
/* Hardware doesn't support under voltage reporting. */
|
|
||||||
case SR_CONF_UNDER_VOLTAGE_CONDITION:
|
|
||||||
case SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE:
|
|
||||||
*data = g_variant_new_boolean(FALSE);
|
|
||||||
break;
|
|
||||||
case SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD:
|
|
||||||
*data = g_variant_new_double(0.0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sr_dbg("%s: Unsupported key: %u (%s)", __func__, key,
|
|
||||||
kinfo ? kinfo->name : "unknown");
|
|
||||||
ret = SR_ERR_NA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 itech_it8500_cmd_packet *cmd, *response;
|
|
||||||
const struct sr_key_info *kinfo;
|
|
||||||
enum itech_it8500_modes mode;
|
|
||||||
int ret, ivalue;
|
|
||||||
uint64_t new_sr;
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
(void)cg;
|
|
||||||
|
|
||||||
if (!data || !sdi)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
cmd = g_malloc0(sizeof(*cmd));
|
|
||||||
if (!cmd)
|
|
||||||
return SR_ERR_MALLOC;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
response = NULL;
|
|
||||||
ret = SR_OK;
|
|
||||||
|
|
||||||
kinfo = sr_key_info_get(SR_KEY_CONFIG, key);
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_LIMIT_MSEC:
|
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
|
||||||
ret = sr_sw_limits_config_set(&devc->limits, key, data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_SAMPLERATE:
|
|
||||||
new_sr = g_variant_get_uint64(data);
|
|
||||||
if (new_sr < MIN_SAMPLE_RATE) {
|
|
||||||
ret = SR_ERR_SAMPLERATE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (new_sr > samplerates[devc->max_sample_rate_idx]) {
|
|
||||||
ret = SR_ERR_SAMPLERATE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
devc->sample_rate = new_sr;
|
|
||||||
break;
|
|
||||||
case SR_CONF_ENABLED:
|
|
||||||
cmd->command = CMD_LOAD_ON_OFF;
|
|
||||||
cmd->data[0] = g_variant_get_boolean(data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_REGULATION:
|
|
||||||
s = g_variant_get_string(data, NULL);
|
|
||||||
if (itech_it8500_string_to_mode(s, &mode) != SR_OK) {
|
|
||||||
ret = SR_ERR_ARG;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cmd->command = CMD_SET_MODE;
|
|
||||||
cmd->data[0] = mode;
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_TARGET:
|
|
||||||
cmd->command = CMD_SET_CV_VOLTAGE;
|
|
||||||
ivalue = g_variant_get_double(data) * 1000.0;
|
|
||||||
WL32(&cmd->data[0], ivalue);
|
|
||||||
break;
|
|
||||||
case SR_CONF_CURRENT_LIMIT:
|
|
||||||
cmd->command = CMD_SET_CC_CURRENT;
|
|
||||||
ivalue = g_variant_get_double(data) * 10000.0;
|
|
||||||
WL32(&cmd->data[0], ivalue);
|
|
||||||
break;
|
|
||||||
case SR_CONF_POWER_TARGET:
|
|
||||||
cmd->command = CMD_SET_CW_POWER;
|
|
||||||
ivalue = g_variant_get_double(data) * 1000.0;
|
|
||||||
WL32(&cmd->data[0], ivalue);
|
|
||||||
break;
|
|
||||||
case SR_CONF_RESISTANCE_TARGET:
|
|
||||||
cmd->command = CMD_SET_CR_RESISTANCE;
|
|
||||||
ivalue = g_variant_get_double(data) * 1000.0;
|
|
||||||
WL32(&cmd->data[0], ivalue);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
|
|
||||||
cmd->command = CMD_SET_MAX_VOLTAGE;
|
|
||||||
ivalue = g_variant_get_double(data) * 1000.0;
|
|
||||||
WL32(&cmd->data[0], ivalue);
|
|
||||||
break;
|
|
||||||
case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
|
|
||||||
cmd->command = CMD_SET_MAX_CURRENT;
|
|
||||||
ivalue = g_variant_get_double(data) * 10000.0;
|
|
||||||
WL32(&cmd->data[0], ivalue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sr_dbg("%s: Unsupported key: %u (%s)", __func__, key,
|
|
||||||
kinfo ? kinfo->name : "unknown");
|
|
||||||
ret = SR_ERR_NA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == SR_OK && cmd->command) {
|
|
||||||
cmd->address = devc->address;
|
|
||||||
ret = itech_it8500_cmd(sdi, cmd, &response);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(cmd);
|
|
||||||
g_free(response);
|
|
||||||
|
|
||||||
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_key_info *kinfo;
|
|
||||||
GVariantBuilder *b;
|
|
||||||
|
|
||||||
devc = sdi ? sdi->priv : NULL;
|
|
||||||
if (!data)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
if (!cg)
|
|
||||||
return STD_CONFIG_LIST(key, data, sdi, cg,
|
|
||||||
scanopts, drvopts, devopts);
|
|
||||||
|
|
||||||
kinfo = sr_key_info_get(SR_KEY_CONFIG, key);
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_DEVICE_OPTIONS:
|
|
||||||
*data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
|
|
||||||
break;
|
|
||||||
case SR_CONF_SAMPLERATE:
|
|
||||||
*data = std_gvar_samplerates_steps(samplerates,
|
|
||||||
1 + devc->max_sample_rate_idx);
|
|
||||||
break;
|
|
||||||
case SR_CONF_REGULATION:
|
|
||||||
b = g_variant_builder_new(G_VARIANT_TYPE("as"));
|
|
||||||
g_variant_builder_add(b, "s", itech_it8500_mode_to_string(CC));
|
|
||||||
g_variant_builder_add(b, "s", itech_it8500_mode_to_string(CV));
|
|
||||||
g_variant_builder_add(b, "s", itech_it8500_mode_to_string(CW));
|
|
||||||
g_variant_builder_add(b, "s", itech_it8500_mode_to_string(CR));
|
|
||||||
*data = g_variant_new("as", b);
|
|
||||||
g_variant_builder_unref(b);
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_TARGET:
|
|
||||||
if (!devc)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
*data = std_gvar_min_max_step(devc->min_voltage,
|
|
||||||
devc->max_voltage, 0.01);
|
|
||||||
break;
|
|
||||||
case SR_CONF_CURRENT_LIMIT:
|
|
||||||
if (!devc)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
*data = std_gvar_min_max_step(0.0, devc->max_current, 0.001);
|
|
||||||
break;
|
|
||||||
case SR_CONF_POWER_TARGET:
|
|
||||||
if (!devc)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
*data = std_gvar_min_max_step(0.0, devc->max_power, 0.01);
|
|
||||||
break;
|
|
||||||
case SR_CONF_RESISTANCE_TARGET:
|
|
||||||
if (!devc)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
*data = std_gvar_min_max_step(devc->min_resistance,
|
|
||||||
devc->max_resistance, 0.01);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
sr_dbg("%s: Unsupported key: %u (%s)", __func__, key,
|
|
||||||
kinfo ? kinfo->name : "unknown");
|
|
||||||
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;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!sdi)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
serial = sdi->conn;
|
|
||||||
|
|
||||||
ret = serial_source_add(sdi->session, serial,
|
|
||||||
G_IO_IN, (1000.0 / devc->sample_rate),
|
|
||||||
itech_it8500_receive_data, (void *)sdi);
|
|
||||||
if (ret == SR_OK) {
|
|
||||||
sr_sw_limits_acquisition_start(&devc->limits);
|
|
||||||
std_session_send_df_header(sdi);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct sr_serial_dev_inst *serial;
|
|
||||||
|
|
||||||
if (!sdi)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
serial = sdi->conn;
|
|
||||||
|
|
||||||
std_session_send_df_end(sdi);
|
|
||||||
serial_source_remove(sdi->session, serial);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_open(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct itech_it8500_cmd_packet *cmd, *response;
|
|
||||||
int ret, res;
|
|
||||||
|
|
||||||
if (!sdi)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
ret = std_serial_dev_open(sdi);
|
|
||||||
if (ret == SR_OK) {
|
|
||||||
/* Request the unit to enter remote control mode. */
|
|
||||||
response = NULL;
|
|
||||||
cmd = g_malloc0(sizeof(*cmd));
|
|
||||||
if (cmd) {
|
|
||||||
cmd->address = devc->address;
|
|
||||||
cmd->command = CMD_SET_REMOTE_MODE;
|
|
||||||
cmd->data[0] = 1;
|
|
||||||
res = itech_it8500_cmd(sdi, cmd, &response);
|
|
||||||
if (res != SR_OK)
|
|
||||||
sr_dbg("Failed to set unit to remote mode");
|
|
||||||
g_free(cmd);
|
|
||||||
g_free(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_close(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct itech_it8500_cmd_packet *cmd, *response;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!sdi)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
response = NULL;
|
|
||||||
cmd = g_malloc0(sizeof(*cmd));
|
|
||||||
if (cmd) {
|
|
||||||
/* Request the unit to enter local control mode. */
|
|
||||||
cmd->address = devc->address;
|
|
||||||
cmd->command = CMD_SET_REMOTE_MODE;
|
|
||||||
cmd->data[0] = 0;
|
|
||||||
ret = itech_it8500_cmd(sdi, cmd, &response);
|
|
||||||
if (ret != SR_OK)
|
|
||||||
sr_dbg("Failed to set unit back to local mode: %d",
|
|
||||||
ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(cmd);
|
|
||||||
g_free(response);
|
|
||||||
|
|
||||||
return std_serial_dev_close(sdi);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dev_clear_callback(void *priv)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
|
|
||||||
if (!priv)
|
|
||||||
return;
|
|
||||||
|
|
||||||
devc = priv;
|
|
||||||
g_mutex_clear(&devc->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_clear(const struct sr_dev_driver *di)
|
|
||||||
{
|
|
||||||
return std_dev_clear_with_callback(di, dev_clear_callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sr_dev_driver itech_it8500_driver_info = {
|
|
||||||
.name = "itech-it8500",
|
|
||||||
.longname = "ITECH IT8500 series",
|
|
||||||
.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(itech_it8500_driver_info);
|
|
||||||
|
|
@ -1,409 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Timo Kokkonen <tjko@iki.fi>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "protocol.h"
|
|
||||||
|
|
||||||
SR_PRIV uint8_t itech_it8500_checksum(const uint8_t *packet)
|
|
||||||
{
|
|
||||||
const uint8_t *p;
|
|
||||||
uint8_t checksum;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (!packet)
|
|
||||||
return 0xff;
|
|
||||||
|
|
||||||
checksum = 0;
|
|
||||||
p = packet;
|
|
||||||
for (i = 0; i < IT8500_PACKET_LEN - 1; i++)
|
|
||||||
checksum += *p++;
|
|
||||||
|
|
||||||
return checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV const char *itech_it8500_mode_to_string(enum itech_it8500_modes mode)
|
|
||||||
{
|
|
||||||
switch (mode) {
|
|
||||||
case CC:
|
|
||||||
return "CC";
|
|
||||||
case CV:
|
|
||||||
return "CV";
|
|
||||||
case CW:
|
|
||||||
return "CW";
|
|
||||||
case CR:
|
|
||||||
return "CR";
|
|
||||||
default:
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int itech_it8500_string_to_mode(const char *modename,
|
|
||||||
enum itech_it8500_modes *mode)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
for (i = 0; i < IT8500_MODES; i++) {
|
|
||||||
s = itech_it8500_mode_to_string(i);
|
|
||||||
if (strncmp(modename, s, strlen(s)) == 0) {
|
|
||||||
*mode = i;
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int itech_it8500_send_cmd(struct sr_serial_dev_inst *serial,
|
|
||||||
struct itech_it8500_cmd_packet *cmd,
|
|
||||||
struct itech_it8500_cmd_packet **response)
|
|
||||||
{
|
|
||||||
struct itech_it8500_cmd_packet *resp;
|
|
||||||
uint8_t *cmd_buf, *resp_buf, checksum;
|
|
||||||
int ret, read_len;
|
|
||||||
|
|
||||||
if (!serial || !cmd || !response)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
cmd_buf = g_malloc0(IT8500_PACKET_LEN);
|
|
||||||
resp_buf = g_malloc0(IT8500_PACKET_LEN);
|
|
||||||
resp = g_malloc0(sizeof(*resp));
|
|
||||||
if (!cmd_buf || !resp_buf || !resp)
|
|
||||||
return SR_ERR_MALLOC;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Construct request from: preamble, address, command, data,
|
|
||||||
* and checksum.
|
|
||||||
*/
|
|
||||||
cmd_buf[0] = IT8500_PREAMBLE;
|
|
||||||
cmd_buf[1] = cmd->address;
|
|
||||||
cmd_buf[2] = cmd->command;
|
|
||||||
memcpy(&cmd_buf[3], cmd->data, IT8500_DATA_LEN);
|
|
||||||
cmd_buf[IT8500_PACKET_LEN - 1] = itech_it8500_checksum(cmd_buf);
|
|
||||||
|
|
||||||
sr_spew("%s: Sending command: %02x", __func__, cmd->command);
|
|
||||||
ret = serial_write_blocking(serial, cmd_buf, IT8500_PACKET_LEN,
|
|
||||||
serial_timeout(serial, IT8500_PACKET_LEN));
|
|
||||||
if (ret < IT8500_PACKET_LEN) {
|
|
||||||
sr_dbg("%s: Error sending command 0x%02x: %d", __func__,
|
|
||||||
cmd->command, ret);
|
|
||||||
ret = SR_ERR;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = SR_ERR;
|
|
||||||
read_len = serial_read_blocking(serial, resp_buf, IT8500_PACKET_LEN,
|
|
||||||
100);
|
|
||||||
if (read_len < IT8500_PACKET_LEN) {
|
|
||||||
sr_dbg("%s: Timeout waiting response to command: %d",
|
|
||||||
__func__, read_len);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resp_buf[0] != IT8500_PREAMBLE) {
|
|
||||||
sr_dbg("%s: Invalid packet received (first byte: %02x)",
|
|
||||||
__func__, resp_buf[0]);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
checksum = itech_it8500_checksum(resp_buf);
|
|
||||||
if (resp_buf[IT8500_PACKET_LEN - 1] != checksum) {
|
|
||||||
sr_dbg("%s: Invalid packet received: checksum mismatch",
|
|
||||||
__func__);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
resp->address = resp_buf[1];
|
|
||||||
resp->command = resp_buf[2];
|
|
||||||
memcpy(resp->data, &resp_buf[3], IT8500_DATA_LEN);
|
|
||||||
sr_spew("%s: Response packet received: cmd=%02x", __func__,
|
|
||||||
resp->command);
|
|
||||||
|
|
||||||
if (resp->command == CMD_RESPONSE) {
|
|
||||||
if (resp->data[0] != STS_COMMAND_SUCCESSFUL) {
|
|
||||||
sr_dbg("%s: Command (%02x) failed: status=%02x",
|
|
||||||
__func__, cmd->command, resp->data[0]);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (resp->command != cmd->command) {
|
|
||||||
sr_dbg("%s: Invalid response received: %02x"
|
|
||||||
" (expected: %02x)",
|
|
||||||
__func__, resp->command, cmd->command);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*response)
|
|
||||||
g_free(*response);
|
|
||||||
*response = resp;
|
|
||||||
resp = NULL;
|
|
||||||
ret = SR_OK;
|
|
||||||
|
|
||||||
error:
|
|
||||||
g_free(cmd_buf);
|
|
||||||
g_free(resp_buf);
|
|
||||||
g_free(resp);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int itech_it8500_cmd(const struct sr_dev_inst *sdi,
|
|
||||||
struct itech_it8500_cmd_packet *cmd,
|
|
||||||
struct itech_it8500_cmd_packet **response)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct sr_serial_dev_inst *serial;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!sdi)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
devc = sdi->priv;
|
|
||||||
serial = sdi->conn;
|
|
||||||
if (!devc || !serial)
|
|
||||||
return SR_ERR_NA;
|
|
||||||
|
|
||||||
g_mutex_lock(&devc->mutex);
|
|
||||||
ret = itech_it8500_send_cmd(serial, cmd, response);
|
|
||||||
g_mutex_unlock(&devc->mutex);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV void itech_it8500_status_change(const struct sr_dev_inst *sdi,
|
|
||||||
uint8_t old_os, uint8_t new_os,
|
|
||||||
uint16_t old_ds, uint16_t new_ds,
|
|
||||||
enum itech_it8500_modes old_m, enum itech_it8500_modes new_m)
|
|
||||||
{
|
|
||||||
gboolean old_bit, new_bit;
|
|
||||||
const char *mode;
|
|
||||||
|
|
||||||
/* Check it output status has changed. */
|
|
||||||
old_bit = old_os & OS_OUT_FLAG;
|
|
||||||
new_bit = new_os & OS_OUT_FLAG;
|
|
||||||
if (old_bit != new_bit)
|
|
||||||
sr_session_send_meta(sdi,
|
|
||||||
SR_CONF_ENABLED,
|
|
||||||
g_variant_new_boolean(new_bit));
|
|
||||||
|
|
||||||
/* Check if OVP status has changed. */
|
|
||||||
old_bit = old_ds & DS_OV_FLAG;
|
|
||||||
new_bit = new_ds & DS_OV_FLAG;
|
|
||||||
if (old_bit != new_bit)
|
|
||||||
sr_session_send_meta(sdi,
|
|
||||||
SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE,
|
|
||||||
g_variant_new_boolean(new_bit));
|
|
||||||
|
|
||||||
/* Check if OCP status has changed. */
|
|
||||||
old_bit = old_ds & DS_OC_FLAG;
|
|
||||||
new_bit = new_ds & DS_OC_FLAG;
|
|
||||||
if (old_bit != new_bit)
|
|
||||||
sr_session_send_meta(sdi,
|
|
||||||
SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE,
|
|
||||||
g_variant_new_boolean(new_bit));
|
|
||||||
|
|
||||||
/* Check if OTP status has changed. */
|
|
||||||
old_bit = old_ds & DS_OT_FLAG;
|
|
||||||
new_bit = new_ds & DS_OT_FLAG;
|
|
||||||
if (old_bit != new_bit)
|
|
||||||
sr_session_send_meta(sdi,
|
|
||||||
SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE,
|
|
||||||
g_variant_new_boolean(new_bit));
|
|
||||||
|
|
||||||
/* Check if operating mode has changed. */
|
|
||||||
if (old_m != new_m) {
|
|
||||||
mode = itech_it8500_mode_to_string(new_m);
|
|
||||||
sr_session_send_meta(sdi, SR_CONF_REGULATION,
|
|
||||||
g_variant_new_string(mode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int itech_it8500_get_status(const struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct itech_it8500_cmd_packet *cmd;
|
|
||||||
struct itech_it8500_cmd_packet *resp;
|
|
||||||
double voltage, current, power;
|
|
||||||
uint8_t operation_state;
|
|
||||||
uint16_t demand_state;
|
|
||||||
enum itech_it8500_modes mode;
|
|
||||||
gboolean load_on;
|
|
||||||
const uint8_t *p;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!sdi)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
if (!devc)
|
|
||||||
return SR_ERR_NA;
|
|
||||||
|
|
||||||
cmd = g_malloc0(sizeof(*cmd));
|
|
||||||
if (!cmd)
|
|
||||||
return SR_ERR_MALLOC;
|
|
||||||
cmd->address = devc->address;
|
|
||||||
cmd->command = CMD_GET_STATE;
|
|
||||||
resp = NULL;
|
|
||||||
|
|
||||||
ret = itech_it8500_cmd(sdi, cmd, &resp);
|
|
||||||
if (ret == SR_OK) {
|
|
||||||
p = resp->data;
|
|
||||||
voltage = read_u32le_inc(&p) / 1000.0;
|
|
||||||
current = read_u32le_inc(&p) / 10000.0;
|
|
||||||
power = read_u32le_inc(&p) / 1000.0;
|
|
||||||
operation_state = read_u8_inc(&p);
|
|
||||||
demand_state = read_u16le_inc(&p);
|
|
||||||
|
|
||||||
if (demand_state & DS_CC_MODE_FLAG)
|
|
||||||
mode = CC;
|
|
||||||
else if (demand_state & DS_CV_MODE_FLAG)
|
|
||||||
mode = CV;
|
|
||||||
else if (demand_state & DS_CW_MODE_FLAG)
|
|
||||||
mode = CW;
|
|
||||||
else if (demand_state & DS_CR_MODE_FLAG)
|
|
||||||
mode = CR;
|
|
||||||
else
|
|
||||||
mode = CC;
|
|
||||||
load_on = operation_state & OS_OUT_FLAG;
|
|
||||||
|
|
||||||
sr_dbg("Load status: V=%.4f, I=%.4f, P=%.3f, State=%s, "
|
|
||||||
"Mode=%s (op=0x%02x, demand=0x%04x)",
|
|
||||||
voltage, current, power, (load_on ? "ON": "OFF"),
|
|
||||||
itech_it8500_mode_to_string(mode),
|
|
||||||
operation_state, demand_state);
|
|
||||||
|
|
||||||
/* Check for status change only after scan() has completed. */
|
|
||||||
if (sdi->model) {
|
|
||||||
itech_it8500_status_change(sdi, devc->operation_state,
|
|
||||||
operation_state, devc->demand_state,
|
|
||||||
demand_state, devc->mode, mode);
|
|
||||||
}
|
|
||||||
devc->voltage = voltage;
|
|
||||||
devc->current = current;
|
|
||||||
devc->power = power;
|
|
||||||
devc->operation_state = operation_state;
|
|
||||||
devc->demand_state = demand_state;
|
|
||||||
devc->mode = mode;
|
|
||||||
devc->load_on = load_on;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(cmd);
|
|
||||||
g_free(resp);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV int itech_it8500_get_int(const struct sr_dev_inst *sdi,
|
|
||||||
enum itech_it8500_command command, int *result)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct itech_it8500_cmd_packet *cmd;
|
|
||||||
struct itech_it8500_cmd_packet *resp;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!sdi || !result)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
cmd = g_malloc0(sizeof(*cmd));
|
|
||||||
if (!cmd)
|
|
||||||
return SR_ERR_MALLOC;
|
|
||||||
cmd->address = devc->address;
|
|
||||||
cmd->command = command;
|
|
||||||
resp = NULL;
|
|
||||||
|
|
||||||
ret = itech_it8500_cmd(sdi, cmd, &resp);
|
|
||||||
if (ret == SR_OK)
|
|
||||||
*result = RL32(&resp->data[0]);
|
|
||||||
|
|
||||||
g_free(cmd);
|
|
||||||
g_free(resp);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
SR_PRIV void itech_it8500_channel_send_value(const struct sr_dev_inst *sdi,
|
|
||||||
struct sr_channel *ch, double 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;
|
|
||||||
double val;
|
|
||||||
|
|
||||||
val = value;
|
|
||||||
sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
|
|
||||||
analog.meaning->channels = g_slist_append(NULL, ch);
|
|
||||||
analog.num_samples = 1;
|
|
||||||
analog.data = &val;
|
|
||||||
analog.encoding->unitsize = sizeof(val);
|
|
||||||
analog.encoding->is_float = TRUE;
|
|
||||||
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 itech_it8500_receive_data(int fd, int revents, void *cb_data)
|
|
||||||
{
|
|
||||||
struct sr_dev_inst *sdi;
|
|
||||||
struct dev_context *devc;
|
|
||||||
GSList *l;
|
|
||||||
|
|
||||||
(void)fd;
|
|
||||||
(void)revents;
|
|
||||||
|
|
||||||
if (!(sdi = cb_data))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (!(devc = sdi->priv))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (itech_it8500_get_status(sdi) != SR_OK)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
std_session_send_df_frame_begin(sdi);
|
|
||||||
|
|
||||||
l = g_slist_nth(sdi->channels, 0);
|
|
||||||
itech_it8500_channel_send_value(sdi, l->data, devc->voltage,
|
|
||||||
SR_MQ_VOLTAGE, SR_UNIT_VOLT, 5);
|
|
||||||
|
|
||||||
l = g_slist_nth(sdi->channels, 1);
|
|
||||||
itech_it8500_channel_send_value(sdi, l->data, devc->current,
|
|
||||||
SR_MQ_CURRENT, SR_UNIT_AMPERE, 5);
|
|
||||||
|
|
||||||
l = g_slist_nth(sdi->channels, 2);
|
|
||||||
itech_it8500_channel_send_value(sdi, l->data, devc->power,
|
|
||||||
SR_MQ_POWER, SR_UNIT_WATT, 5);
|
|
||||||
|
|
||||||
std_session_send_df_frame_end(sdi);
|
|
||||||
|
|
||||||
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
|
||||||
if (sr_sw_limits_check(&devc->limits))
|
|
||||||
sr_dev_acquisition_stop(sdi);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Timo Kokkonen <tjko@iki.fi>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef LIBSIGROK_HARDWARE_ITECH_IT8500_PROTOCOL_H
|
|
||||||
#define LIBSIGROK_HARDWARE_ITECH_IT8500_PROTOCOL_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <glib.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
|
|
||||||
#define LOG_PREFIX "itech-it8500"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Unit uses 26 byte binary packets for communications.
|
|
||||||
* Packets have fixed format:
|
|
||||||
*
|
|
||||||
* Offset|Length|Description
|
|
||||||
* ------|------|-------------------------------------
|
|
||||||
* 0 | 1 | Preable (always set to 0xAA).
|
|
||||||
* 1 | 1 | Unit Address (0-254, 255=broadcast).
|
|
||||||
* 2 | 1 | Command number.
|
|
||||||
* 3 | 22 | Variable data.
|
|
||||||
* 25 | 1 | Parity code (checksum).
|
|
||||||
*/
|
|
||||||
#define IT8500_HEADER_LEN 3
|
|
||||||
#define IT8500_DATA_LEN 22
|
|
||||||
#define IT8500_PACKET_LEN (IT8500_HEADER_LEN + IT8500_DATA_LEN + 1)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Data structure to track commands and reponses.
|
|
||||||
*/
|
|
||||||
struct itech_it8500_cmd_packet {
|
|
||||||
uint8_t command; /* Command number. */
|
|
||||||
uint8_t address; /* Unit address: 0..254 (255 = broadcast). */
|
|
||||||
uint8_t data[IT8500_DATA_LEN]; /* Command/Response data. */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define IT8500_PREAMBLE 0xaa
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Operating modes.
|
|
||||||
* Note! These map directly to mode numbers used in CMD_SET_MODE
|
|
||||||
* and CMD_GET_MODE commands, so values are manually defined below.
|
|
||||||
*/
|
|
||||||
enum itech_it8500_modes {
|
|
||||||
CC = 0,
|
|
||||||
CV = 1,
|
|
||||||
CW = 2,
|
|
||||||
CR = 3,
|
|
||||||
IT8500_MODES, /* Total count, for internal use. */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum itech_it8500_command {
|
|
||||||
CMD_GET_LOAD_LIMITS = 0x01,
|
|
||||||
CMD_SET_HW_OPP_VALUE = 0x02,
|
|
||||||
CMD_GET_HW_OPP_VALUE = 0x03,
|
|
||||||
CMD_SET_VON_MODE = 0x0e,
|
|
||||||
CMD_GET_VON_MODE = 0x0f,
|
|
||||||
CMD_SET_VON_VALUE = 0x10,
|
|
||||||
CMD_GET_VON_VALUE = 0x11,
|
|
||||||
CMD_RESPONSE = 0x12, /* Response to commands not returning any data. */
|
|
||||||
CMD_SET_REMOTE_MODE = 0x20,
|
|
||||||
CMD_LOAD_ON_OFF = 0x21,
|
|
||||||
CMD_SET_MAX_VOLTAGE = 0x22,
|
|
||||||
CMD_GET_MAX_VOLTAGE = 0x23,
|
|
||||||
CMD_SET_MAX_CURRENT = 0x24,
|
|
||||||
CMD_GET_MAX_CURRENT = 0x25,
|
|
||||||
CMD_SET_MAX_POWER = 0x26,
|
|
||||||
CMD_GET_MAX_POWER = 0x27,
|
|
||||||
CMD_SET_MODE = 0x28,
|
|
||||||
CMD_GET_MODE = 0x29,
|
|
||||||
CMD_SET_CC_CURRENT = 0x2a,
|
|
||||||
CMD_GET_CC_CURRENT = 0x2b,
|
|
||||||
CMD_SET_CV_VOLTAGE = 0x2c,
|
|
||||||
CMD_GET_CV_VOLTAGE = 0x2d,
|
|
||||||
CMD_SET_CW_POWER = 0x2e,
|
|
||||||
CMD_GET_CW_POWER = 0x2f,
|
|
||||||
CMD_SET_CR_RESISTANCE = 0x30,
|
|
||||||
CMD_GET_CR_RESISTANCE = 0x31,
|
|
||||||
CMD_SET_BATTERY_MIN_VOLTAGE = 0x4e,
|
|
||||||
CMD_GET_BATTERY_MIN_VOLTAGE = 0x4f,
|
|
||||||
CMD_SET_LOAD_ON_TIMER = 0x50,
|
|
||||||
CMD_GET_LOAD_ON_TIMER = 0x51,
|
|
||||||
CMD_LOAD_ON_TIMER = 0x52,
|
|
||||||
CMD_LOAD_ON_TIME_STATUS = 0x53,
|
|
||||||
CMD_SET_ADDRESS = 0x54,
|
|
||||||
CMD_LOCAL_CONTROL = 0x55,
|
|
||||||
CMD_REMOTE_SENSING = 0x56,
|
|
||||||
CMD_REMOTE_SENSING_STATUS = 0x57,
|
|
||||||
CMD_SET_TRIGGER_SOURCE = 0x58,
|
|
||||||
CMD_GET_TRIGGER_SOURCE = 0x59,
|
|
||||||
CMD_TRIGGER = 0x5a,
|
|
||||||
CMD_SAVE_SETTINGS = 0x5b,
|
|
||||||
CMD_LOAD_SETTINGS = 0x5c,
|
|
||||||
CMD_SET_FUNCTION = 0x5d,
|
|
||||||
CMD_GET_FUNCTION = 0x5e,
|
|
||||||
CMD_GET_STATE = 0x5f,
|
|
||||||
CMD_GET_MODEL_INFO = 0x6a,
|
|
||||||
CMD_GET_BARCODE_INFO = 0x6b,
|
|
||||||
CMD_SET_OCP_VALUE = 0x80,
|
|
||||||
CMD_GET_OCP_VALUE = 0x81,
|
|
||||||
CMD_SET_OCP_DELAY = 0x82,
|
|
||||||
CMD_GET_OCP_DELAY = 0x83,
|
|
||||||
CMD_ENABLE_OCP = 0x84,
|
|
||||||
CMD_DISABLE_OCP = 0x85,
|
|
||||||
CMD_SET_OPP_VALUE = 0x86,
|
|
||||||
CMD_GET_OPP_VALUE = 0x87,
|
|
||||||
CMD_SET_OPP_DELAY = 0x88,
|
|
||||||
CMD_GET_OPP_DELAY = 0x89,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Status packet status byte values. */
|
|
||||||
enum itech_it8500_status_code {
|
|
||||||
STS_COMMAND_SUCCESSFUL = 0x80,
|
|
||||||
STS_INVALID_CHECKSUM = 0x90,
|
|
||||||
STS_INVALID_PARAMETER = 0xa0,
|
|
||||||
STS_UNKNOWN_COMMAND = 0xb0,
|
|
||||||
STS_INVALID_COMMAND = 0xc0,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* "Operation state" register flags.
|
|
||||||
*/
|
|
||||||
#define OS_CAL_FLAG (1UL << 0)
|
|
||||||
#define OS_WTG_FLAG (1UL << 1)
|
|
||||||
#define OS_REM_FLAG (1UL << 2)
|
|
||||||
#define OS_OUT_FLAG (1UL << 3)
|
|
||||||
#define OS_LOCAL_FLAG (1UL << 4)
|
|
||||||
#define OS_SENSE_FLAG (1UL << 5)
|
|
||||||
#define OS_LOT_FLAG (1UL << 6)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* "Demand state" register flags.
|
|
||||||
*/
|
|
||||||
#define DS_RV_FLAG (1UL << 0)
|
|
||||||
#define DS_OV_FLAG (1UL << 1)
|
|
||||||
#define DS_OC_FLAG (1UL << 2)
|
|
||||||
#define DS_OP_FLAG (1UL << 3)
|
|
||||||
#define DS_OT_FLAG (1UL << 4)
|
|
||||||
#define DS_SV_FLAG (1UL << 5)
|
|
||||||
#define DS_CC_MODE_FLAG (1UL << 6)
|
|
||||||
#define DS_CV_MODE_FLAG (1UL << 7)
|
|
||||||
#define DS_CW_MODE_FLAG (1UL << 8)
|
|
||||||
#define DS_CR_MODE_FLAG (1UL << 9)
|
|
||||||
|
|
||||||
#define IT8500_MAX_MODEL_NAME_LEN 5
|
|
||||||
|
|
||||||
struct dev_context {
|
|
||||||
char model[IT8500_MAX_MODEL_NAME_LEN + 1];
|
|
||||||
uint8_t fw_ver_major;
|
|
||||||
uint8_t fw_ver_minor;
|
|
||||||
uint8_t address;
|
|
||||||
double max_current;
|
|
||||||
double min_voltage;
|
|
||||||
double max_voltage;
|
|
||||||
double max_power;
|
|
||||||
double min_resistance;
|
|
||||||
double max_resistance;
|
|
||||||
size_t max_sample_rate_idx;
|
|
||||||
|
|
||||||
double voltage;
|
|
||||||
double current;
|
|
||||||
double power;
|
|
||||||
uint8_t operation_state;
|
|
||||||
uint16_t demand_state;
|
|
||||||
enum itech_it8500_modes mode;
|
|
||||||
gboolean load_on;
|
|
||||||
|
|
||||||
uint64_t sample_rate;
|
|
||||||
struct sr_sw_limits limits;
|
|
||||||
|
|
||||||
GMutex mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
SR_PRIV uint8_t itech_it8500_checksum(const uint8_t *packet);
|
|
||||||
SR_PRIV const char *itech_it8500_mode_to_string(enum itech_it8500_modes mode);
|
|
||||||
SR_PRIV int itech_it8500_string_to_mode(const char *modename,
|
|
||||||
enum itech_it8500_modes *mode);
|
|
||||||
SR_PRIV int itech_it8500_send_cmd(struct sr_serial_dev_inst *serial,
|
|
||||||
struct itech_it8500_cmd_packet *cmd,
|
|
||||||
struct itech_it8500_cmd_packet **response);
|
|
||||||
SR_PRIV int itech_it8500_cmd(const struct sr_dev_inst *sdi,
|
|
||||||
struct itech_it8500_cmd_packet *cmd,
|
|
||||||
struct itech_it8500_cmd_packet **response);
|
|
||||||
SR_PRIV void itech_it8500_status_change(const struct sr_dev_inst *sdi,
|
|
||||||
uint8_t old_op, uint8_t new_op,
|
|
||||||
uint16_t old_de, uint16_t new_de,
|
|
||||||
enum itech_it8500_modes old_m, enum itech_it8500_modes new_m);
|
|
||||||
SR_PRIV int itech_it8500_get_status(const struct sr_dev_inst *sdi);
|
|
||||||
SR_PRIV int itech_it8500_get_int(const struct sr_dev_inst *sdi,
|
|
||||||
enum itech_it8500_command command, int *result);
|
|
||||||
SR_PRIV void itech_it8500_channel_send_value(const struct sr_dev_inst *sdi,
|
|
||||||
struct sr_channel *ch, double value, enum sr_mq mq,
|
|
||||||
enum sr_unit unit, int digits);
|
|
||||||
SR_PRIV int itech_it8500_receive_data(int fd, int revents, void *cb_data);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -79,6 +79,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
sr_info("Probing serial port %s.", conn);
|
sr_info("Probing serial port %s.", conn);
|
||||||
|
|
||||||
devices = NULL;
|
devices = NULL;
|
||||||
|
serial_flush(serial);
|
||||||
|
|
||||||
sr_spew("Set O1 mode (continuous values, stable and unstable ones).");
|
sr_spew("Set O1 mode (continuous values, stable and unstable ones).");
|
||||||
if (serial_write_blocking(serial, "O1\r\n", 4, 0) < 0)
|
if (serial_write_blocking(serial, "O1\r\n", 4, 0) < 0)
|
||||||
|
|
@ -88,7 +89,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||||
/* Let's get a bit of data and see if we can find a packet. */
|
/* Let's get a bit of data and see if we can find a packet. */
|
||||||
len = sizeof(buf);
|
len = sizeof(buf);
|
||||||
ret = serial_stream_detect(serial, buf, &len, scale->packet_size,
|
ret = serial_stream_detect(serial, buf, &len, scale->packet_size,
|
||||||
scale->packet_valid, NULL, NULL, 3000);
|
scale->packet_valid, 3000);
|
||||||
if (ret != SR_OK)
|
if (ret != SR_OK)
|
||||||
goto scan_cleanup;
|
goto scan_cleanup;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,786 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Florian Schmidt <schmidt_florian@gmx.de>
|
|
||||||
* Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
|
|
||||||
* Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
|
|
||||||
* Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* mostly stolen from src/hardware/saleae-logic16/ */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <glib.h>
|
|
||||||
#include <libusb.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
#include "protocol.h"
|
|
||||||
|
|
||||||
static const uint32_t scanopts[] = {
|
|
||||||
SR_CONF_CONN,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t drvopts[] = {
|
|
||||||
SR_CONF_LOGIC_ANALYZER,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t devopts[] = {
|
|
||||||
/* TODO: SR_CONF_CONTINUOUS, */
|
|
||||||
SR_CONF_CONN | SR_CONF_GET,
|
|
||||||
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
|
||||||
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_GET | SR_CONF_LIST,
|
|
||||||
SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
|
||||||
SR_CONF_LOGIC_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
|
||||||
SR_CONF_LOGIC_THRESHOLD_CUSTOM | 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 int32_t trigger_matches[] = {
|
|
||||||
SR_TRIGGER_ZERO,
|
|
||||||
SR_TRIGGER_ONE,
|
|
||||||
SR_TRIGGER_RISING,
|
|
||||||
SR_TRIGGER_FALLING,
|
|
||||||
};
|
|
||||||
|
|
||||||
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_KHZ(20),
|
|
||||||
SR_KHZ(50),
|
|
||||||
SR_KHZ(100),
|
|
||||||
SR_KHZ(200),
|
|
||||||
SR_KHZ(500),
|
|
||||||
SR_MHZ(1),
|
|
||||||
SR_MHZ(2),
|
|
||||||
SR_MHZ(4),
|
|
||||||
SR_MHZ(5),
|
|
||||||
SR_MHZ(8),
|
|
||||||
SR_MHZ(10),
|
|
||||||
SR_MHZ(20),
|
|
||||||
SR_MHZ(50),
|
|
||||||
SR_MHZ(100),
|
|
||||||
SR_MHZ(200),
|
|
||||||
};
|
|
||||||
|
|
||||||
static const float logic_threshold_value[] = {
|
|
||||||
1.58,
|
|
||||||
2.5,
|
|
||||||
1.165,
|
|
||||||
1.5,
|
|
||||||
1.25,
|
|
||||||
0.9,
|
|
||||||
0.75,
|
|
||||||
0.60,
|
|
||||||
0.45,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *logic_threshold[] = {
|
|
||||||
"TTL 5V",
|
|
||||||
"CMOS 5V",
|
|
||||||
"CMOS 3.3V",
|
|
||||||
"CMOS 3.0V",
|
|
||||||
"CMOS 2.5V",
|
|
||||||
"CMOS 1.8V",
|
|
||||||
"CMOS 1.5V",
|
|
||||||
"CMOS 1.2V",
|
|
||||||
"CMOS 0.9V",
|
|
||||||
"USER",
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAX_NUM_LOGIC_THRESHOLD_ENTRIES ARRAY_SIZE(logic_threshold)
|
|
||||||
|
|
||||||
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_config *src;
|
|
||||||
GSList *l;
|
|
||||||
GSList *devices;
|
|
||||||
GSList *conn_devices;
|
|
||||||
struct libusb_device_descriptor des;
|
|
||||||
libusb_device **devlist;
|
|
||||||
unsigned int i, j;
|
|
||||||
const char *conn;
|
|
||||||
char connection_id[64];
|
|
||||||
int64_t fw_updated;
|
|
||||||
unsigned int dev_addr;
|
|
||||||
|
|
||||||
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 LA2016 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 (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (des.idVendor != LA2016_VID || des.idProduct != LA2016_PID)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Already has the firmware */
|
|
||||||
sr_dbg("Found a LA2016 device.");
|
|
||||||
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
|
||||||
sdi->status = SR_ST_INITIALIZING;
|
|
||||||
sdi->connection_id = g_strdup(connection_id);
|
|
||||||
|
|
||||||
fw_updated = 0;
|
|
||||||
dev_addr = libusb_get_device_address(devlist[i]);
|
|
||||||
if (des.iProduct != 2) {
|
|
||||||
sr_info("device at '%s' has no firmware loaded!", connection_id);
|
|
||||||
|
|
||||||
if (la2016_upload_firmware(drvc->sr_ctx, devlist[i], des.idProduct) != SR_OK) {
|
|
||||||
sr_err("uC firmware upload failed!");
|
|
||||||
g_free(sdi->connection_id);
|
|
||||||
g_free(sdi);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
fw_updated = g_get_monotonic_time();
|
|
||||||
dev_addr = 0xff; /* to mark that we don't know address yet... ugly */
|
|
||||||
}
|
|
||||||
|
|
||||||
sdi->vendor = g_strdup("Kingst");
|
|
||||||
sdi->model = g_strdup("LA2016");
|
|
||||||
|
|
||||||
for (j = 0; j < ARRAY_SIZE(channel_names); j++)
|
|
||||||
sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE, channel_names[j]);
|
|
||||||
|
|
||||||
devices = g_slist_append(devices, sdi);
|
|
||||||
|
|
||||||
devc = g_malloc0(sizeof(struct dev_context));
|
|
||||||
sdi->priv = devc;
|
|
||||||
devc->fw_updated = fw_updated;
|
|
||||||
devc->threshold_voltage_idx = 0;
|
|
||||||
devc->threshold_voltage = logic_threshold_value[devc->threshold_voltage_idx];
|
|
||||||
|
|
||||||
sdi->status = SR_ST_INACTIVE;
|
|
||||||
sdi->inst_type = SR_INST_USB;
|
|
||||||
|
|
||||||
sdi->conn = sr_usb_dev_inst_new(
|
|
||||||
libusb_get_bus_number(devlist[i]),
|
|
||||||
dev_addr, 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 la2016_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 drv_context *drvc;
|
|
||||||
int ret, i, device_count;
|
|
||||||
char connection_id[64];
|
|
||||||
|
|
||||||
di = sdi->driver;
|
|
||||||
drvc = di->context;
|
|
||||||
usb = sdi->conn;
|
|
||||||
ret = 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.", 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 != LA2016_VID || des.idProduct != LA2016_PID || des.iProduct != 2)
|
|
||||||
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 = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
|
|
||||||
if (ret == LIBUSB_ERROR_BUSY) {
|
|
||||||
sr_err("Unable to claim USB interface. Another "
|
|
||||||
"program or driver has already claimed it.");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ret = la2016_init_device(sdi)) != SR_OK) {
|
|
||||||
sr_err("Failed to init device.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (ret != SR_OK) {
|
|
||||||
if (usb->devhdl) {
|
|
||||||
libusb_release_interface(usb->devhdl, USB_INTERFACE);
|
|
||||||
libusb_close(usb->devhdl);
|
|
||||||
usb->devhdl = NULL;
|
|
||||||
}
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_open(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
int64_t timediff_us, timediff_ms;
|
|
||||||
uint64_t reset_done;
|
|
||||||
uint64_t now;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
|
|
||||||
* milliseconds for the FX2 to renumerate.
|
|
||||||
*/
|
|
||||||
ret = SR_ERR;
|
|
||||||
if (devc->fw_updated > 0) {
|
|
||||||
sr_info("Waiting for device to reset after firmware upload.");
|
|
||||||
/* Takes >= 2000ms for the uC to be gone from the USB bus. */
|
|
||||||
reset_done = devc->fw_updated + 18 * (uint64_t)1e5; /* 1.8 seconds */
|
|
||||||
now = g_get_monotonic_time();
|
|
||||||
if (reset_done > now)
|
|
||||||
g_usleep(reset_done - now);
|
|
||||||
timediff_ms = 0;
|
|
||||||
while (timediff_ms < MAX_RENUM_DELAY_MS) {
|
|
||||||
g_usleep(200 * 1000);
|
|
||||||
|
|
||||||
timediff_us = g_get_monotonic_time() - devc->fw_updated;
|
|
||||||
timediff_ms = timediff_us / 1000;
|
|
||||||
|
|
||||||
if ((ret = la2016_dev_open(sdi)) == SR_OK)
|
|
||||||
break;
|
|
||||||
sr_spew("Waited %" PRIi64 "ms.", timediff_ms);
|
|
||||||
}
|
|
||||||
if (ret != SR_OK) {
|
|
||||||
sr_err("Device failed to re-enumerate.");
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
sr_info("Device came back after %" PRIi64 "ms.", timediff_ms);
|
|
||||||
} else {
|
|
||||||
ret = la2016_dev_open(sdi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret != SR_OK) {
|
|
||||||
sr_err("Unable to open device.");
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
la2016_deinit_device(sdi);
|
|
||||||
|
|
||||||
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;
|
|
||||||
double rounded;
|
|
||||||
|
|
||||||
(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_SAMPLERATE:
|
|
||||||
*data = g_variant_new_uint64(devc->cur_samplerate);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
|
||||||
*data = g_variant_new_uint64(devc->limit_samples);
|
|
||||||
break;
|
|
||||||
case SR_CONF_CAPTURE_RATIO:
|
|
||||||
*data = g_variant_new_uint64(devc->capture_ratio);
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
|
||||||
rounded = (int)(devc->threshold_voltage / 0.1) * 0.1;
|
|
||||||
*data = std_gvar_tuple_double(rounded, rounded + 0.1);
|
|
||||||
return SR_OK;
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD:
|
|
||||||
*data = g_variant_new_string(logic_threshold[devc->threshold_voltage_idx]);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD_CUSTOM:
|
|
||||||
*data = g_variant_new_double(devc->threshold_voltage);
|
|
||||||
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;
|
|
||||||
double low, high;
|
|
||||||
int idx;
|
|
||||||
|
|
||||||
(void)cg;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case SR_CONF_SAMPLERATE:
|
|
||||||
devc->cur_samplerate = g_variant_get_uint64(data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
|
||||||
devc->limit_samples = g_variant_get_uint64(data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_CAPTURE_RATIO:
|
|
||||||
devc->capture_ratio = g_variant_get_uint64(data);
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
|
||||||
g_variant_get(data, "(dd)", &low, &high);
|
|
||||||
devc->threshold_voltage = (low + high) / 2.0;
|
|
||||||
devc->threshold_voltage_idx = MAX_NUM_LOGIC_THRESHOLD_ENTRIES - 1; /* USER */
|
|
||||||
break;
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD: {
|
|
||||||
if ((idx = std_str_idx(data, logic_threshold, MAX_NUM_LOGIC_THRESHOLD_ENTRIES)) < 0)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
if (idx == MAX_NUM_LOGIC_THRESHOLD_ENTRIES - 1) {
|
|
||||||
/* user threshold */
|
|
||||||
} else {
|
|
||||||
devc->threshold_voltage = logic_threshold_value[idx];
|
|
||||||
}
|
|
||||||
devc->threshold_voltage_idx = idx;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD_CUSTOM:
|
|
||||||
devc->threshold_voltage = g_variant_get_double(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;
|
|
||||||
case SR_CONF_LIMIT_SAMPLES:
|
|
||||||
*data = std_gvar_tuple_u64(LA2016_NUM_SAMPLES_MIN, LA2016_NUM_SAMPLES_MAX);
|
|
||||||
break;
|
|
||||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
|
||||||
*data = std_gvar_min_max_step_thresholds(
|
|
||||||
LA2016_THR_VOLTAGE_MIN,
|
|
||||||
LA2016_THR_VOLTAGE_MAX, 0.1);
|
|
||||||
break;
|
|
||||||
case SR_CONF_TRIGGER_MATCH:
|
|
||||||
*data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
|
|
||||||
break;
|
|
||||||
case SR_CONF_LOGIC_THRESHOLD:
|
|
||||||
*data = g_variant_new_strv(logic_threshold, MAX_NUM_LOGIC_THRESHOLD_ENTRIES);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return SR_ERR_NA;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_chunk(struct sr_dev_inst *sdi,
|
|
||||||
const uint8_t *packets, unsigned int num_tfers)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct sr_datafeed_logic logic;
|
|
||||||
struct sr_datafeed_packet sr_packet;
|
|
||||||
unsigned int max_samples, n_samples, total_samples, free_n_samples;
|
|
||||||
unsigned int i, j, k;
|
|
||||||
int do_signal_trigger;
|
|
||||||
uint16_t *wp;
|
|
||||||
const uint8_t *rp;
|
|
||||||
uint16_t state;
|
|
||||||
uint8_t repetitions;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
|
|
||||||
logic.unitsize = 2;
|
|
||||||
logic.data = devc->convbuffer;
|
|
||||||
|
|
||||||
sr_packet.type = SR_DF_LOGIC;
|
|
||||||
sr_packet.payload = &logic;
|
|
||||||
|
|
||||||
max_samples = devc->convbuffer_size / 2;
|
|
||||||
n_samples = 0;
|
|
||||||
wp = (uint16_t *)devc->convbuffer;
|
|
||||||
total_samples = 0;
|
|
||||||
do_signal_trigger = 0;
|
|
||||||
|
|
||||||
if (devc->had_triggers_configured && devc->reading_behind_trigger == 0 && devc->info.n_rep_packets_before_trigger == 0) {
|
|
||||||
std_session_send_df_trigger(sdi);
|
|
||||||
devc->reading_behind_trigger = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rp = packets;
|
|
||||||
for (i = 0; i < num_tfers; i++) {
|
|
||||||
for (k = 0; k < NUM_PACKETS_IN_CHUNK; k++) {
|
|
||||||
free_n_samples = max_samples - n_samples;
|
|
||||||
if (free_n_samples < 256 || do_signal_trigger) {
|
|
||||||
logic.length = n_samples * 2;
|
|
||||||
sr_session_send(sdi, &sr_packet);
|
|
||||||
n_samples = 0;
|
|
||||||
wp = (uint16_t *)devc->convbuffer;
|
|
||||||
if (do_signal_trigger) {
|
|
||||||
std_session_send_df_trigger(sdi);
|
|
||||||
do_signal_trigger = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state = read_u16le_inc(&rp);
|
|
||||||
repetitions = read_u8_inc(&rp);
|
|
||||||
for (j = 0; j < repetitions; j++)
|
|
||||||
*wp++ = state;
|
|
||||||
|
|
||||||
n_samples += repetitions;
|
|
||||||
total_samples += repetitions;
|
|
||||||
devc->total_samples += repetitions;
|
|
||||||
if (!devc->reading_behind_trigger) {
|
|
||||||
devc->n_reps_until_trigger--;
|
|
||||||
if (devc->n_reps_until_trigger == 0) {
|
|
||||||
devc->reading_behind_trigger = 1;
|
|
||||||
do_signal_trigger = 1;
|
|
||||||
sr_dbg(" here is trigger position after %" PRIu64 " samples, %.6fms",
|
|
||||||
devc->total_samples,
|
|
||||||
(double)devc->total_samples / devc->cur_samplerate * 1e3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(void)read_u8_inc(&rp); /* Skip sequence number. */
|
|
||||||
}
|
|
||||||
if (n_samples) {
|
|
||||||
logic.length = n_samples * 2;
|
|
||||||
sr_session_send(sdi, &sr_packet);
|
|
||||||
if (do_signal_trigger) {
|
|
||||||
std_session_send_df_trigger(sdi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sr_dbg("send_chunk done after %d samples", total_samples);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
|
|
||||||
{
|
|
||||||
struct sr_dev_inst *sdi;
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct sr_usb_dev_inst *usb;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
sdi = transfer->user_data;
|
|
||||||
devc = sdi->priv;
|
|
||||||
usb = sdi->conn;
|
|
||||||
|
|
||||||
sr_dbg("receive_transfer(): status %s received %d bytes.",
|
|
||||||
libusb_error_name(transfer->status), transfer->actual_length);
|
|
||||||
|
|
||||||
if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
|
|
||||||
sr_err("bulk transfer timeout!");
|
|
||||||
devc->transfer_finished = 1;
|
|
||||||
}
|
|
||||||
send_chunk(sdi, transfer->buffer, transfer->actual_length / TRANSFER_PACKET_LENGTH);
|
|
||||||
|
|
||||||
devc->n_bytes_to_read -= transfer->actual_length;
|
|
||||||
if (devc->n_bytes_to_read) {
|
|
||||||
uint32_t to_read = devc->n_bytes_to_read;
|
|
||||||
if (to_read > LA2016_BULK_MAX)
|
|
||||||
to_read = LA2016_BULK_MAX;
|
|
||||||
libusb_fill_bulk_transfer(
|
|
||||||
transfer, usb->devhdl,
|
|
||||||
0x86, transfer->buffer, to_read,
|
|
||||||
receive_transfer, (void *)sdi, DEFAULT_TIMEOUT_MS);
|
|
||||||
|
|
||||||
if ((ret = libusb_submit_transfer(transfer)) == 0)
|
|
||||||
return;
|
|
||||||
sr_err("Failed to submit further transfer: %s.", libusb_error_name(ret));
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(transfer->buffer);
|
|
||||||
libusb_free_transfer(transfer);
|
|
||||||
devc->transfer_finished = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_event(int fd, int revents, void *cb_data)
|
|
||||||
{
|
|
||||||
const struct sr_dev_inst *sdi;
|
|
||||||
struct dev_context *devc;
|
|
||||||
struct drv_context *drvc;
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
(void)fd;
|
|
||||||
(void)revents;
|
|
||||||
|
|
||||||
sdi = cb_data;
|
|
||||||
devc = sdi->priv;
|
|
||||||
drvc = sdi->driver->context;
|
|
||||||
|
|
||||||
if (devc->have_trigger == 0) {
|
|
||||||
if (la2016_has_triggered(sdi) == 0) {
|
|
||||||
sr_dbg("not yet ready for download...");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
devc->have_trigger = 1;
|
|
||||||
devc->transfer_finished = 0;
|
|
||||||
devc->reading_behind_trigger = 0;
|
|
||||||
devc->total_samples = 0;
|
|
||||||
/* we can start retrieving data! */
|
|
||||||
if (la2016_start_retrieval(sdi, receive_transfer) != SR_OK) {
|
|
||||||
sr_err("failed to start retrieval!");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
sr_dbg("retrieval is started...");
|
|
||||||
std_session_send_df_frame_begin(sdi);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
tv.tv_sec = tv.tv_usec = 0;
|
|
||||||
libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
|
|
||||||
|
|
||||||
if (devc->transfer_finished) {
|
|
||||||
sr_dbg("transfer is finished!");
|
|
||||||
std_session_send_df_frame_end(sdi);
|
|
||||||
|
|
||||||
usb_source_remove(sdi->session, drvc->sr_ctx);
|
|
||||||
std_session_send_df_end(sdi);
|
|
||||||
|
|
||||||
la2016_stop_acquisition(sdi);
|
|
||||||
|
|
||||||
g_free(devc->convbuffer);
|
|
||||||
devc->convbuffer = NULL;
|
|
||||||
|
|
||||||
sr_dbg("transfer is now finished");
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void abort_acquisition(struct dev_context *devc)
|
|
||||||
{
|
|
||||||
if (devc->transfer)
|
|
||||||
libusb_cancel_transfer(devc->transfer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int configure_channels(const struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
struct dev_context *devc;
|
|
||||||
|
|
||||||
devc = sdi->priv;
|
|
||||||
devc->cur_channels = 0;
|
|
||||||
devc->num_channels = 0;
|
|
||||||
|
|
||||||
for (GSList *l = sdi->channels; l; l = l->next) {
|
|
||||||
struct sr_channel *ch = (struct sr_channel*)l->data;
|
|
||||||
if (ch->enabled == FALSE)
|
|
||||||
continue;
|
|
||||||
devc->cur_channels |= 1 << ch->index;
|
|
||||||
devc->num_channels++;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ret;
|
|
||||||
|
|
||||||
di = sdi->driver;
|
|
||||||
drvc = di->context;
|
|
||||||
devc = sdi->priv;
|
|
||||||
|
|
||||||
if (configure_channels(sdi) != SR_OK) {
|
|
||||||
sr_err("Failed to configure channels.");
|
|
||||||
return SR_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
devc->convbuffer_size = 4 * 1024 * 1024;
|
|
||||||
if (!(devc->convbuffer = g_try_malloc(devc->convbuffer_size))) {
|
|
||||||
sr_err("Conversion buffer malloc failed.");
|
|
||||||
return SR_ERR_MALLOC;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ret = la2016_setup_acquisition(sdi)) != SR_OK) {
|
|
||||||
g_free(devc->convbuffer);
|
|
||||||
devc->convbuffer = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
devc->ctx = drvc->sr_ctx;
|
|
||||||
|
|
||||||
if ((ret = la2016_start_acquisition(sdi)) != SR_OK) {
|
|
||||||
abort_acquisition(devc);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
devc->have_trigger = 0;
|
|
||||||
usb_source_add(sdi->session, drvc->sr_ctx, 50, handle_event, (void *)sdi);
|
|
||||||
|
|
||||||
std_session_send_df_header(sdi);
|
|
||||||
|
|
||||||
return SR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = la2016_abort_acquisition(sdi);
|
|
||||||
abort_acquisition(sdi->priv);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sr_dev_driver kingst_la2016_driver_info = {
|
|
||||||
.name = "kingst-la2016",
|
|
||||||
.longname = "Kingst LA2016",
|
|
||||||
.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(kingst_la2016_driver_info);
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue