Compare commits

..

13 Commits

Author SHA1 Message Date
Uwe Hermann a6b07d7e28 Bump package version to 0.5.2. 2019-12-25 21:25:15 +01:00
Uwe Hermann c66fde84f6 configure.ac: Bump libtool/library version from 5:0:1 to 5:1:1.
The libtool current:revision:age numbers change from 5:0:1 to 5:1:1
since no new APIs were added and no existing ones were changed or
removed. Thus, revision is incremented, current and age stay unchanged.

Details:
http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info

This changes the library filename (e.g. on Linux) from
libsigrok.so.4.1.0 to libsigrok.so.4.1.1, the SONAME
(+symlink) remains the same, though (libsigrok.so.4).
2019-12-25 21:24:29 +01:00
Uwe Hermann c1a8e19d9a Doxyfile: Set version to 0.5.2. 2019-12-24 16:42:45 +01:00
Uwe Hermann fdb297c6de NEWS: Add list of user-visible changes so far. 2019-12-24 16:42:45 +01:00
Uwe Hermann e4204b1757 Backport recent changes from mainline.
This includes all changes from

  4c660b46c1
  Makefile.am: Add missing src/serial_hid.h.

up to

  39ea7b7d39
  dmm/eev121gw: add missing scale items for sub display in power modes

This is possible since none of the changes above change or
remove public API calls of the library.
2019-12-24 16:40:57 +01:00
Uwe Hermann 8686b747cd configure.ac: Bump package version to 0.5.1. 2018-10-14 22:41:49 +02:00
Uwe Hermann 45106f0ca5 configure.ac: Bump libtool/library version from 4:0:0 to 5:0:1.
The libtool current:revision:age numbers change from 4:0:0 to 5:0:1
since new APIs were added (but no existing ones were changed or
removed). Thus, revision is set to 0, current and age are bumped.

Details:
http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info

This changes the library filename (e.g. on Linux) from
libsigrok.so.4.0.0 to libsigrok.so.4.1.0, the SONAME
(+symlink) remains the same, though (libsigrok.so.4).
2018-10-14 22:41:49 +02:00
Uwe Hermann 6cec31aeac Doxygen: Update relevant @since tags to 0.5.1. 2018-10-14 22:41:49 +02:00
Uwe Hermann d0667da66f Doxyfile: Set version to 0.5.1. 2018-10-14 22:41:49 +02:00
Uwe Hermann 2d5ccd2601 NEWS: Add list of user-visible changes so far. 2018-10-14 22:41:49 +02:00
Uwe Hermann d2af13d03f Revert API/ABI change for Context::create_analog_packet().
This is fine in libsigrokcxx mainline, but for the 0.5.x series we need
to remain API/ABI-compatible.
2018-10-14 22:40:09 +02:00
Uwe Hermann 8cd15dd4ce Backport recent changes from mainline.
This includes all changes from

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

up to

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

This is possible since (almost) none of the changes above change or
remove public API calls of the library.
2018-10-14 22:39:48 +02:00
Uwe Hermann 1aba657270 Doxyfile: Set version to 0.5.0. 2017-06-12 03:08:36 +02:00
208 changed files with 6018 additions and 30050 deletions

2
.gitignore vendored
View File

@ -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

704
Doxyfile

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);
} }

View File

@ -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))

View File

@ -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

View File

@ -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"

View File

@ -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 -------------------------------------------------*/

View File

@ -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
*/ */

View File

@ -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
* *

View File

@ -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;
}

View File

@ -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));

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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 */ };

View File

@ -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 */ };

View File

@ -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)
{ {

View File

@ -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;

View File

@ -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},

View File

@ -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,

View File

@ -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);

View File

@ -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.

View File

@ -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);
} }

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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));

View File

@ -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. */

View File

@ -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;

View File

@ -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)) {

View File

@ -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));
} }

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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));

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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;
} }

View File

@ -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;

View File

@ -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,

View File

@ -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) {

View File

@ -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. */

View File

@ -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;
} }

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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)

View File

@ -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);
} }

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;
} }

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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