Compare commits
No commits in common. "hw/sq50" and "dsupstream" have entirely different histories.
hw/sq50
...
dsupstream
|
|
@ -1,79 +1,54 @@
|
||||||
# autotools cruft
|
# autotools cruft
|
||||||
/INSTALL
|
aclocal.m4
|
||||||
/Makefile.in
|
autom4te.cache/
|
||||||
/aclocal.m4
|
compile
|
||||||
/autom4te.cache/
|
config.guess
|
||||||
/autostuff/
|
config.h
|
||||||
/configure
|
config.h.in
|
||||||
/configure.lineno
|
config.log
|
||||||
/m4/libtool.m4
|
config.status
|
||||||
/m4/lt*.m4
|
config.sub
|
||||||
/build
|
configure
|
||||||
/inst
|
depcomp
|
||||||
|
libtool
|
||||||
|
ltmain.sh
|
||||||
|
install-sh
|
||||||
|
map
|
||||||
|
missing
|
||||||
|
stamp-h1
|
||||||
|
autostuff/
|
||||||
|
version.h
|
||||||
|
ChangeLog
|
||||||
|
INSTALL
|
||||||
|
tests/check_main
|
||||||
|
|
||||||
# Editor/IDE cruft
|
# recursive autoconf leftovers
|
||||||
*.kate-swp
|
.deps
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
|
||||||
|
# build leftovers
|
||||||
*~
|
*~
|
||||||
.*.sw*
|
*.o
|
||||||
/*.kdev4
|
*.a
|
||||||
/Makefile.am.user
|
*.lo
|
||||||
|
*.la
|
||||||
# Configure/build cruft
|
|
||||||
*.[ao]
|
|
||||||
*.l[ao]
|
|
||||||
.deps/
|
|
||||||
.dirstamp
|
|
||||||
.libs/
|
.libs/
|
||||||
/Makefile
|
*.pc
|
||||||
/config.*
|
|
||||||
/doxy/
|
|
||||||
/include/libsigrok/version.h
|
|
||||||
/libsigrok-*.tar.*
|
|
||||||
/libsigrok.pc
|
|
||||||
/libtool
|
|
||||||
stamp-h?
|
|
||||||
|
|
||||||
# Files generated by building C++ bindings
|
# KDE backup files
|
||||||
/bindings/cxx/doxy/
|
*.kate-swp
|
||||||
/bindings/cxx/enums.cpp
|
|
||||||
/bindings/cxx/enums.timestamp
|
# KDevelop project files
|
||||||
/bindings/cxx/include/libsigrokcxx/enums.hpp
|
*.kdev4
|
||||||
/bindings/cxx/libsigrokcxx.pc
|
|
||||||
/bindings/swig/enums.i
|
|
||||||
|
|
||||||
# Files generated by building Python bindings
|
# Files generated by building Python bindings
|
||||||
*.pyc
|
bindings/python/dist/
|
||||||
/bindings/python/build/
|
bindings/python/build
|
||||||
/bindings/python/dist/
|
bindings/python/libsigrok.egg-info/
|
||||||
/bindings/python/doxy/
|
bindings/python/sigrok/__pycache__/
|
||||||
/bindings/python/libsigrok.egg-info/
|
bindings/python/libsigrok.py
|
||||||
/bindings/python/libsigrok.py
|
bindings/python/libsigrok_python_wrap.c
|
||||||
/bindings/python/libsigrok_python_wrap.c
|
bindings/python/sigrok/core/lowlevel.py
|
||||||
/bindings/python/sigrok/__pycache__/
|
bindings/python/sigrok/core/lowlevel_wrap.c
|
||||||
/bindings/python/sigrok/core/classes.py
|
|
||||||
/bindings/python/sigrok/core/classes_wrap.cpp
|
|
||||||
/bindings/python/sigrok/core/doc_start.i
|
|
||||||
/bindings/python/sigrok/core/doc_end.i
|
|
||||||
/bindings/python/sigrok/core/lowlevel.py
|
|
||||||
/bindings/python/sigrok/core/lowlevel_wrap.c
|
|
||||||
/bindings/python/timestamp
|
|
||||||
|
|
||||||
# Files generated by building Ruby bindings
|
|
||||||
/bindings/ruby/classes_wrap.cpp
|
|
||||||
/bindings/ruby/doc.i
|
|
||||||
/bindings/ruby/sigrok.so
|
|
||||||
|
|
||||||
# Files generated by building Java bindings
|
|
||||||
*.class
|
|
||||||
/bindings/java/libsigrok_java_core_classes.so
|
|
||||||
/bindings/java/org/sigrok/core/classes/*.java
|
|
||||||
/bindings/java/org/sigrok/core/classes/classes_wrap.cxx
|
|
||||||
/bindings/java/org/sigrok/core/classes/doc.i
|
|
||||||
/bindings/java/sigrok-core.jar
|
|
||||||
/bindings/java/doxy/
|
|
||||||
|
|
||||||
# Files generated by the testsuite
|
|
||||||
/test-suite.log
|
|
||||||
/tests/*.log
|
|
||||||
/tests/*.trs
|
|
||||||
/tests/main
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
The ChangeLog is auto-generated when releasing. If you
|
|
||||||
are seeing this, use 'git log' for a detailed list of changes.
|
|
||||||
2517
Doxyfile_internal
2517
Doxyfile_internal
File diff suppressed because it is too large
Load Diff
140
HACKING
140
HACKING
|
|
@ -5,9 +5,8 @@ HACKING
|
||||||
Coding style
|
Coding style
|
||||||
------------
|
------------
|
||||||
|
|
||||||
This project is programmed using the Linux kernel coding style:
|
This project is programmed using the Linux kernel coding style, see
|
||||||
|
http://lxr.linux.no/linux/Documentation/CodingStyle for details.
|
||||||
https://www.kernel.org/doc/html/latest/process/coding-style.html
|
|
||||||
|
|
||||||
Please use the same style for any code contributions, thanks!
|
Please use the same style for any code contributions, thanks!
|
||||||
|
|
||||||
|
|
@ -15,16 +14,15 @@ Please use the same style for any code contributions, thanks!
|
||||||
Contributions
|
Contributions
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
- In order to contribute you should ideally clone the git repository and
|
- Patches should be sent to the development mailinglist at
|
||||||
let us know (preferably via IRC, or via the mailing list) from where to
|
|
||||||
pull/review your changes. You can use github.com, or any other public git
|
|
||||||
hosting site.
|
|
||||||
|
|
||||||
- Alternatively, patches can be sent to the development mailinglist at
|
|
||||||
sigrok-devel@lists.sourceforge.net (please subscribe to the list first).
|
sigrok-devel@lists.sourceforge.net (please subscribe to the list first).
|
||||||
|
|
||||||
https://lists.sourceforge.net/lists/listinfo/sigrok-devel
|
https://lists.sourceforge.net/lists/listinfo/sigrok-devel
|
||||||
|
|
||||||
|
- Alternatively, you can also clone the git repository and let us know
|
||||||
|
from where to pull/review your changes. You can use gitorious.org,
|
||||||
|
github.com, or any other public git hosting site.
|
||||||
|
|
||||||
|
|
||||||
Adding a new hardware driver
|
Adding a new hardware driver
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
@ -47,11 +45,7 @@ You can apply it like this:
|
||||||
$ cd libsigrok
|
$ cd libsigrok
|
||||||
$ git am 0001-tondaj-sl-814-Initial-driver-skeleton.patch
|
$ git am 0001-tondaj-sl-814-Initial-driver-skeleton.patch
|
||||||
|
|
||||||
You can now edit the files in src/hardware/tondaj-sl-814 as needed
|
You can now edit the files in the hardware/tondaj-sl-814 directory as needed.
|
||||||
and implement your driver based on the skeleton files there. That means your
|
|
||||||
patch submission later will consist of at least two patches: the initial one
|
|
||||||
adding the skeleton driver, and one or more additional patches that actually
|
|
||||||
implement the respective driver code.
|
|
||||||
|
|
||||||
|
|
||||||
The manual way:
|
The manual way:
|
||||||
|
|
@ -61,11 +55,15 @@ This is a rough overview of what you need to do in order to add a new driver
|
||||||
(using the Tondaj SL-814 device as example). It's basically what the
|
(using the Tondaj SL-814 device as example). It's basically what the
|
||||||
'new-driver' script (see above) does for you:
|
'new-driver' script (see above) does for you:
|
||||||
|
|
||||||
- Makefile.am: Add to src_libdrivers_la_SOURCES under the HW_TONDAJ_SL_814
|
- configure.ac:
|
||||||
condition.
|
- Add an --enable-tondaj-sl-814 option.
|
||||||
- configure.ac: Add an SR_DRIVER() call.
|
- Add "hardware/tondaj-sl-814/Makefile" to the AC_CONFIG_FILES list.
|
||||||
- src/drivers.c: Add a tondaj_sl_814_driver_info entry in two places.
|
- Add and entry for the device in the "Enabled hardware drivers" list
|
||||||
- src/hardware/tondaj-sl-814/ directory: Add api.c, protocol.c, protocol.h.
|
at the bottom of the file.
|
||||||
|
- hardware/Makefile.am: Add "tondaj-sl-814" to the SUBDIRS variable.
|
||||||
|
- hwdriver.c: Add a tondaj_sl_814_driver_info entry in two places.
|
||||||
|
- hardware/tondaj-sl-814/ directory: Add the following files:
|
||||||
|
Makefile.am, api.c, protocol.c, protocol.h
|
||||||
|
|
||||||
See existing drivers or the 'new-driver' output for the details.
|
See existing drivers or the 'new-driver' output for the details.
|
||||||
|
|
||||||
|
|
@ -73,27 +71,18 @@ See existing drivers or the 'new-driver' output for the details.
|
||||||
Random notes
|
Random notes
|
||||||
------------
|
------------
|
||||||
|
|
||||||
- Don't do variable declarations in compound statements, only at the
|
- Consistently use g_try_malloc() / g_try_malloc0(). Do not use standard
|
||||||
beginning of a function.
|
|
||||||
|
|
||||||
- Generally avoid assigning values to variables at declaration time,
|
|
||||||
especially so for complex and/or run-time dependent values.
|
|
||||||
|
|
||||||
- Consistently use g_*malloc() / g_*malloc0(). Do not use standard
|
|
||||||
malloc()/calloc() if it can be avoided (sometimes other libs such
|
malloc()/calloc() if it can be avoided (sometimes other libs such
|
||||||
as libftdi can return malloc()'d memory, for example).
|
as libftdi can return malloc()'d memory, for example).
|
||||||
|
|
||||||
- Always properly match allocations with the proper *free() functions. If
|
- Always properly match allocations with the proper *free() functions. If
|
||||||
glib's g_*malloc()/g_*malloc0() was used, use g_free() to free the
|
glib's g_try_malloc()/g_try_malloc0() was used, use g_free() to free the
|
||||||
memory. Otherwise use standard free(). Never use the wrong function!
|
memory. Otherwise use standard free(). Never use the wrong function!
|
||||||
|
|
||||||
- We assume that "small" memory allocations (< 1MB) will always succeed.
|
- Never use g_malloc() or g_malloc0(). These functions do not return NULL
|
||||||
Thus, it's fine to use g_malloc() or g_malloc0() for allocations of
|
if not enough memory is available but rather lead to an exit() or segfault
|
||||||
simple/small structs and such (instead of using g_try_malloc()), and
|
instead. This behaviour is not acceptable for libraries.
|
||||||
there's no need to check the return value.
|
Use g_try_malloc()/g_try_malloc0() instead and check the return value.
|
||||||
|
|
||||||
Do use g_try_malloc() or g_try_malloc0() for large (>= 1MB) allocations
|
|
||||||
and check the return value.
|
|
||||||
|
|
||||||
- You should never print any messages (neither to stdout nor stderr nor
|
- You should never print any messages (neither to stdout nor stderr nor
|
||||||
elsewhere) "manually" via e.g. printf() or g_log() or similar functions.
|
elsewhere) "manually" via e.g. printf() or g_log() or similar functions.
|
||||||
|
|
@ -106,7 +95,7 @@ Random notes
|
||||||
- Consistently use the same naming convention for #include guards in headers:
|
- Consistently use the same naming convention for #include guards in headers:
|
||||||
<PROJECTNAME>_<PATH_TO_FILE>_<FILE>
|
<PROJECTNAME>_<PATH_TO_FILE>_<FILE>
|
||||||
This ensures that all #include guards are always unique and consistent.
|
This ensures that all #include guards are always unique and consistent.
|
||||||
Example: LIBSIGROK_HARDWARE_MIC_985XX_PROTOCOL_H
|
Examples: LIBSIGROK_LIBSIGROK_H, LIBSIGROK_HARDWARE_ASIX_SIGMA_ASIX_SIGMA_H
|
||||||
|
|
||||||
- Consistently use the same naming convention for API functions:
|
- Consistently use the same naming convention for API functions:
|
||||||
<libprefix>_<groupname>_<action>().
|
<libprefix>_<groupname>_<action>().
|
||||||
|
|
@ -142,44 +131,21 @@ Random notes
|
||||||
Doxygen
|
Doxygen
|
||||||
-------
|
-------
|
||||||
|
|
||||||
- Use the @ notation for all Doxygen comments (e.g. @param, not \param).
|
|
||||||
|
|
||||||
- Do not use the @brief tag, it's unnecessary as we use JAVADOC_AUTOBRIEF.
|
|
||||||
|
|
||||||
- Generally use the following item order in Doxygen comments:
|
|
||||||
- Brief function description (1 line), followed by an empty line.
|
|
||||||
- Optionally, a longer function description (and another empty line).
|
|
||||||
- The list of parameter descriptions (@param).
|
|
||||||
- The return value description (@return or @retval).
|
|
||||||
- An optional @since tag (only for public SR_API functions).
|
|
||||||
- An optional @private tag (for private SR_PRIV functions).
|
|
||||||
|
|
||||||
- In @param lines, the name of the parameter is followed by a space and
|
|
||||||
then a sentence describing the parameter (starts with a capital letter,
|
|
||||||
ends with a full stop).
|
|
||||||
|
|
||||||
- In Doxygen comments, put an empty line between the block of @param lines
|
- In Doxygen comments, put an empty line between the block of @param lines
|
||||||
and the final @return line. The @param lines themselves (if there is more
|
and the final @return line. The @param lines themselves (if there is more
|
||||||
than one) are not separated by empty lines.
|
than one) are not separated by empty lines.
|
||||||
|
|
||||||
- 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.
|
||||||
Variables that are "static" don't need to be marked like this.
|
Variables that are "static" don't need to be marked like this.
|
||||||
|
|
||||||
- Mark all public API functions (SR_API) with a @since tag which indicates
|
- Mark all public API functions (SR_API) with a @since tag which indicates
|
||||||
in which release the respective function was added (e.g. "@since 0.1.0").
|
in which release the respective function was added. If the function has
|
||||||
|
existed before, but its API changed later, document this as well.
|
||||||
If the function has existed before, but its API changed later, the @since
|
|
||||||
tag should mention only the release when the API last changed.
|
|
||||||
|
|
||||||
Example: The sr_foo() call was added in 0.1.0, but the API changed in
|
|
||||||
the later 0.2.0 release. The docs should read "@since 0.2.0" in that case.
|
|
||||||
|
|
||||||
Non-public functions (static ones, and those marked SR_PRIV) don't need
|
Non-public functions (static ones, and those marked SR_PRIV) don't need
|
||||||
to have @since markers.
|
to have @since markers.
|
||||||
|
|
@ -187,57 +153,11 @@ Doxygen
|
||||||
The @since tag should be the last one, i.e. it should come after @param,
|
The @since tag should be the last one, i.e. it should come after @param,
|
||||||
@return, @see, and so on.
|
@return, @see, and so on.
|
||||||
|
|
||||||
- Examples:
|
Examples:
|
||||||
|
|
||||||
/**
|
@since 0.1.0
|
||||||
* Tell a hardware driver to scan for devices.
|
|
||||||
*
|
|
||||||
* In addition to the detection, the devices that are found are also
|
|
||||||
* initialized automatically. On some devices, this involves a firmware upload,
|
|
||||||
* or other such measures.
|
|
||||||
*
|
|
||||||
* The order in which the system is scanned for devices is not specified. The
|
|
||||||
* caller should not assume or rely on any specific order.
|
|
||||||
*
|
|
||||||
* Before calling sr_driver_scan(), the user must have previously initialized
|
|
||||||
* the driver by calling sr_driver_init().
|
|
||||||
*
|
|
||||||
* @param[in] driver The driver that should scan. Must be a pointer to one of
|
|
||||||
* the entries returned by sr_driver_list(). Must not be NULL.
|
|
||||||
* @param[in] options List of 'struct sr_hwopt' options to pass to the driver's
|
|
||||||
* scanner. Can be NULL/empty.
|
|
||||||
*
|
|
||||||
* @return A GSList * of 'struct sr_dev_inst', or NULL if no devices were
|
|
||||||
* found (or errors were encountered). This list must be freed by the
|
|
||||||
* caller using g_slist_free(), but without freeing the data pointed
|
|
||||||
* to in the list.
|
|
||||||
*
|
|
||||||
* @since 0.2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
@since 0.1.1 (but the API changed in 0.2.0)
|
||||||
* Query value of a configuration key at the given driver or device instance.
|
|
||||||
*
|
|
||||||
* @param[in] driver The sr_dev_driver struct to query. Must not be NULL.
|
|
||||||
* @param[in] sdi (optional) If the key is specific to a device, this must
|
|
||||||
* contain a pointer to the struct sr_dev_inst to be checked.
|
|
||||||
* Otherwise it must be NULL. If sdi is != NULL, sdi->priv must
|
|
||||||
* also be != NULL.
|
|
||||||
* @param[in,out] data Pointer to a GVariant where the value will be stored.
|
|
||||||
* Must not be NULL. The caller is given ownership of the GVariant
|
|
||||||
* and must thus decrease the refcount after use. However if
|
|
||||||
* this function returns an error code, the field should be
|
|
||||||
* considered unused, and should not be unreferenced.
|
|
||||||
*
|
|
||||||
* @retval SR_OK Success.
|
|
||||||
* @retval SR_ERR Error.
|
|
||||||
* @retval SR_ERR_ARG The driver doesn't know that key, but this is not to be
|
|
||||||
* interpreted as an error by the caller; merely as an indication
|
|
||||||
* that it's not applicable.
|
|
||||||
*
|
|
||||||
* @since 0.3.0
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
Testsuite
|
Testsuite
|
||||||
|
|
|
||||||
1022
Makefile.am
1022
Makefile.am
File diff suppressed because it is too large
Load Diff
837
NEWS
837
NEWS
|
|
@ -1,840 +1,3 @@
|
||||||
0.5.0 (2017-06-12)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Note: This release DOES change the libsigrok API. That means it is NOT
|
|
||||||
backwards-compatible and frontends will need updates.
|
|
||||||
|
|
||||||
* New supported hardware:
|
|
||||||
- Logic analyzers:
|
|
||||||
- CWAV USBee ZX (bug #764)
|
|
||||||
- DreamSourceLab DSLogic
|
|
||||||
- DreamSourceLab DSLogic Pro
|
|
||||||
- FTDI LA (simple, limited logic analyzer using FTDI chips directly)
|
|
||||||
- Oscilloscopes:
|
|
||||||
- Hameg HMO3524
|
|
||||||
- Hantek 6022BE
|
|
||||||
- LeCroy X-Stream series
|
|
||||||
- Rigol DS1074Z Plus
|
|
||||||
- Rigol DS1104Z Plus
|
|
||||||
- Rigol MSO2000A series
|
|
||||||
- Rocktech BM102
|
|
||||||
- Rohde&Schwarz HMO 1002
|
|
||||||
- Sainsmart DDS120
|
|
||||||
- Logic analyzers / oscilloscopes (but not MSOs):
|
|
||||||
- Hantek 6022BL
|
|
||||||
- Programmable power supplies:
|
|
||||||
- Agilent N5763A
|
|
||||||
- HP 6633A
|
|
||||||
- Rohde&Schwarz HMC8043
|
|
||||||
- Electronic loads:
|
|
||||||
- Arachnid Labs Re:load Pro
|
|
||||||
- Multimeters:
|
|
||||||
- Agilent U1241C
|
|
||||||
- Agilent U1242C
|
|
||||||
- Fluke 289
|
|
||||||
- HP 3457A
|
|
||||||
- Keysight U1281
|
|
||||||
- Keysight U1282
|
|
||||||
- Metrix MX56C
|
|
||||||
- PeakTech 3330
|
|
||||||
- Sound level meters:
|
|
||||||
- PCE PCE-322A
|
|
||||||
- LCR meters:
|
|
||||||
- PeakTech 2170
|
|
||||||
- Signal generators:
|
|
||||||
- Rohde&Schwarz SME0x series
|
|
||||||
* New build dependencies (libsigrokcxx C++ library):
|
|
||||||
- libsigrok >= 0.5.0 (the libsigrok C library)
|
|
||||||
- A C++ compiler with full C++11 support (g++ >= 4.8.1 or clang++ >= 3.3)
|
|
||||||
* New config keys:
|
|
||||||
- SR_CONF_ADC_POWERLINE_CYCLES
|
|
||||||
- SR_CONF_PROBE_FACTOR
|
|
||||||
- SR_CONF_SIGNAL_GENERATOR
|
|
||||||
- SR_CONF_TRIGGER_LEVEL
|
|
||||||
- SR_CONF_UNDER_VOLTAGE_CONDITION
|
|
||||||
- SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE
|
|
||||||
* New measurement quantity keys:
|
|
||||||
- SR_MQ_HARMONIC_RATIO
|
|
||||||
* New measurement quantity flags:
|
|
||||||
- SR_MQFLAG_FOUR_WIRE
|
|
||||||
* agilent-dmm:
|
|
||||||
- Fix handling of AC/DC flags in volts mode.
|
|
||||||
- Add support for AC/DC flags in current mode.
|
|
||||||
- Add support for Keysight branded meters.
|
|
||||||
- Add support for reading secondary display and temperature.
|
|
||||||
- Add support for dBm/dBV modes.
|
|
||||||
- Rework job management to allow for faster and configurable samplerate.
|
|
||||||
- Add support for Log-Hand/-Trig/-Auto/-Export data sources.
|
|
||||||
- Fix handling of the second channel of 2 channels models.
|
|
||||||
- Add support for Vsense (Non-Contact Voltage).
|
|
||||||
* asix-sigma:
|
|
||||||
- Fix RLE decoder.
|
|
||||||
- Disable support for triggers, they don't work right now (bug #359).
|
|
||||||
- Properly decode data gathered at 100 and 200 MHz (bug #840).
|
|
||||||
- Only download firmware when necessary.
|
|
||||||
- Enforce optionally specified sample count (bug #838).
|
|
||||||
- Fixup the download of the last data acquisition chunk (bug #838).
|
|
||||||
- Various other bugfixes and internal code improvements.
|
|
||||||
* demo:
|
|
||||||
- Add "cable squid" logic waveform (works-with logo, many channels).
|
|
||||||
- Add walking one/walking zero pattern.
|
|
||||||
* deree-de5000:
|
|
||||||
- Rename the driver to serial-lcr, allow support for multiple devices.
|
|
||||||
* fx2lafw:
|
|
||||||
- CWAV USBee SX: Add support for one analog channel.
|
|
||||||
- Fix analog scaling.
|
|
||||||
- Fix a -Wself-assign compiler warning (bug #793).
|
|
||||||
- Warn on fail to open plausible devices (bug #867).
|
|
||||||
- Use wide_sampling only if necessary (depends on channel config).
|
|
||||||
* hameg-hmo:
|
|
||||||
- Support triggering on either falling or rising edge (bug #740).
|
|
||||||
- Terminate all commands with a linefeed for all transports (bug #784).
|
|
||||||
- Add PATTern and BUS1/BUS2 trigger sources.
|
|
||||||
- Fix index access for models with 2 pods / 16 digital channels.
|
|
||||||
- Support BE format for SCPI sample downloads.
|
|
||||||
* hantek-6xxx:
|
|
||||||
- Fix some issues by using power-of-two data sizes (bug #821).
|
|
||||||
- Fix AC/DC coupling selection (bug #836).
|
|
||||||
- Only list DC coupling once (bug #822).
|
|
||||||
* lecroy-xstream:
|
|
||||||
- Fix config_list() capabilities listing (bug #913).
|
|
||||||
* openbench-logic-sniffer:
|
|
||||||
- Fix acquisition restart with trigger enabled (bug #809).
|
|
||||||
* rigol-ds:
|
|
||||||
- Add support for getting/setting the trigger level.
|
|
||||||
- Properly report which channel is enabled.
|
|
||||||
- Add probe factor support.
|
|
||||||
- Send *OPC? after commands that don't return a value (bug #933).
|
|
||||||
* saleae-logic16:
|
|
||||||
- Add 20MHz and 50MHz to samplerate preset list (bug #799).
|
|
||||||
* uni-t-ut32x:
|
|
||||||
- Accept SR_CONF_CONN to fix the device scan.
|
|
||||||
* ut71x:
|
|
||||||
- Fix float printing issue in a debug message (bug #700).
|
|
||||||
- Fix incorrect resistance values on some DMMs.
|
|
||||||
* session:
|
|
||||||
- Fix memory allocation issue in sr_packet_copy().
|
|
||||||
- Increase chunk size for slightly better performance.
|
|
||||||
- Fix a segfault when input files were read multiple times.
|
|
||||||
- Fix missing data when input files were read multiple times (bug #944).
|
|
||||||
* analog:
|
|
||||||
- Improve output readability by using SI prefix.
|
|
||||||
* scpi:
|
|
||||||
- Fix remote locking according to USBTMC spec (bug #783).
|
|
||||||
- Various internal improvements to better handle corner cases.
|
|
||||||
* scpi/vxi:
|
|
||||||
- Fix incomplete reads (bug #790).
|
|
||||||
* input/vcd:
|
|
||||||
- Skip BOM at beginning of file (bug #755).
|
|
||||||
* input/trace32_ad:
|
|
||||||
- Make the sample rate an option.
|
|
||||||
* output/srzip:
|
|
||||||
- Add support for storing analog channels.
|
|
||||||
* output/analog:
|
|
||||||
- Drop obsolete and duplicate functions (bug #636).
|
|
||||||
- Fix incorrect displaying of the values for certain digits settings.
|
|
||||||
* output/csv:
|
|
||||||
- Fix a segfault when using non-hardware input.
|
|
||||||
- Fix a segfault due to a g_malloc() allocating too few bytes.
|
|
||||||
- Add an option to output units for column labels.
|
|
||||||
- Fix segfaults related to incorrect indices and unitsizes (bug #844).
|
|
||||||
- Fix a false negative after successful import causing frontend issues.
|
|
||||||
- Skip leading UTF-8 BOM in the input stream (bug #756).
|
|
||||||
- Correctly skip over last processed end-of-line sequence and accept
|
|
||||||
absence of last end-of-line termination sequence (bug #635).
|
|
||||||
- Send larger datafeed chunks, to speed up import.
|
|
||||||
* output/ascii:
|
|
||||||
- Add support for user configurable character set.
|
|
||||||
* output/gnuplot:
|
|
||||||
- Remove, obsoleted by the improved CSV module.
|
|
||||||
* bindings:
|
|
||||||
- Link C++ code with gnustl_shared if it exists (Android).
|
|
||||||
- Flesh out the analog payload bindings.
|
|
||||||
* bindings/cxx:
|
|
||||||
- Fixup memory leak in input module receive() calls (bug #976).
|
|
||||||
- Fix various -Wundefined-var-template clang warnings (bug #915).
|
|
||||||
* bindings/ruby:
|
|
||||||
- Fix out-of-tree build (bug #797).
|
|
||||||
- Fix distribution of Ruby bindings (bug #741).
|
|
||||||
- Fix Ruby bindings build on Mac OS X and FreeBSD (bug #800).
|
|
||||||
- Fix the build with BSD Make (bug #801).
|
|
||||||
- Make the Ruby bindings build with Ruby 2.0 again.
|
|
||||||
* bindings/java:
|
|
||||||
- Use correct JNI function when calling Vector.add.
|
|
||||||
* Build system:
|
|
||||||
- Use latest AX_CXX_COMPILE_STDCXX (bug #795).
|
|
||||||
- Fix linker issues related to --whole-archive (bug #802).
|
|
||||||
- Don't access the sr_driver_list with no driver compiled (bug #820).
|
|
||||||
- Fix a build issue related to C++ compiler flags (bug #865).
|
|
||||||
- configure summary: Show whether shared/static build is enabled.
|
|
||||||
- configure summary: Show linker flags.
|
|
||||||
- uninstall: Remove empty include directories (bug #861).
|
|
||||||
* udev rules file:
|
|
||||||
- Add TAG+="uaccess" for systemd, keep plugdev group as well (bug #665).
|
|
||||||
- Use 660 permissions (bug #665).
|
|
||||||
- Add various new USB VID/PID pairs for newly-supported hardware.
|
|
||||||
* Add a MIME info file (and icons) for sigrok session files (bug #857).
|
|
||||||
* Various internal refactorings and improvements.
|
|
||||||
- Add sr_sw_limits_*() helper functions for software limits.
|
|
||||||
- Add and use the standard cleanup helper std_cleanup().
|
|
||||||
- Add std_scan_complete() helper function for scan completion.
|
|
||||||
* All drivers were converted to use the new SR_DF_ANALOG format, support
|
|
||||||
for SR_DF_ANALOG_OLD has been dropped (bug #728).
|
|
||||||
* Many drivers now report the correct number of digits in analog packets.
|
|
||||||
* resource: Improve logging output (bug #806).
|
|
||||||
* Fix an issue with analog packets with digits=0 (bug #815).
|
|
||||||
* Fix a crash when scanning for devices with no options provided (bug #786).
|
|
||||||
* Fix a segfault with input/ouput modules (bug #813).
|
|
||||||
* Fix various compiler warnings.
|
|
||||||
* Fix various memory leaks.
|
|
||||||
* Remove FSF postal address from boiler plate license text.
|
|
||||||
* Various Doxygen fixes and improvements.
|
|
||||||
* README.devices: Update firmware related (and other) information.
|
|
||||||
* HACKING: Update URL to Linux kernel coding style.
|
|
||||||
|
|
||||||
0.4.0 (2016-01-29)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Note: This release DOES change the libsigrok API. That means it is NOT
|
|
||||||
backwards-compatible and frontends will need updates.
|
|
||||||
|
|
||||||
* New supported hardware:
|
|
||||||
- Logic analyzers:
|
|
||||||
- AKIP-9101
|
|
||||||
- BeagleLogic
|
|
||||||
- LeCroy LogicStudio
|
|
||||||
- mcupro Logic16 clone
|
|
||||||
- Pipistrello OLS
|
|
||||||
- Sysclk LWLA1016
|
|
||||||
- Oscilloscopes:
|
|
||||||
- Rigol/Agilent DS1000Z series
|
|
||||||
- Yokogawa DLM2000 series
|
|
||||||
- Yokogawa DL9000 series
|
|
||||||
- Hung-Chang DSO-2100
|
|
||||||
- GW Instek GDS-800
|
|
||||||
- Multimeters:
|
|
||||||
- Agilent U1241A/B
|
|
||||||
- Agilent U1242A/B
|
|
||||||
- Brymen BM25x series
|
|
||||||
- MASTECH MS8250B
|
|
||||||
- Metrahit 16T/16U/KMM2002
|
|
||||||
- PeakTech 3415
|
|
||||||
- Tenma 72-7730
|
|
||||||
- Tenma 72-7732
|
|
||||||
- Tenma 72-9380A
|
|
||||||
- Testo 435-4
|
|
||||||
- UNI-T UT372
|
|
||||||
- UNI-T UT71x series (UT71A/B/C/D/E)
|
|
||||||
- Velleman DVM4100
|
|
||||||
- Voltcraft VC-870
|
|
||||||
- Voltcraft VC-920
|
|
||||||
- Voltcraft VC-940
|
|
||||||
- Voltcraft VC-960
|
|
||||||
- Programmable power supplies:
|
|
||||||
- Fluke/Philips PM2800 series
|
|
||||||
- HP 663xx series
|
|
||||||
- Manson HCS-3xxx series
|
|
||||||
- Motech LPS-30x series
|
|
||||||
- Rigol DP800 series
|
|
||||||
- Korad KAxxxxP series (a.k.a Velleman LABPS3005D and others)
|
|
||||||
- AC/DC sources:
|
|
||||||
- Agilent N5700A series (DC sources)
|
|
||||||
- Chroma 61600 series (AC sources)
|
|
||||||
- Chroma 62000 series (DC sources)
|
|
||||||
- Electronic loads:
|
|
||||||
- Maynuo M97 (and compatibles)
|
|
||||||
- LCR meters:
|
|
||||||
- DER EE DE-5000
|
|
||||||
- Scales:
|
|
||||||
- KERN EW 6200-2NM
|
|
||||||
- BeagleBone Black capes:
|
|
||||||
- BayLibre ACME (revA and revB)
|
|
||||||
* New input modules:
|
|
||||||
- raw_analog: Raw analog signals in various formats
|
|
||||||
- trace32_ad: Lauterbach Trace32 logic analyzer data
|
|
||||||
* New output modules:
|
|
||||||
- wav: Waveform audio file format
|
|
||||||
- srzip: Native ZIP-based sigrok file format
|
|
||||||
* Add libsigrok language bindings based on SWIG + doxygen:
|
|
||||||
- C++ language bindings
|
|
||||||
- Python language bindings
|
|
||||||
- Ruby language bindings
|
|
||||||
- Java language bindings
|
|
||||||
* Add a Modbus framework in order to be able to support Modbus based devices.
|
|
||||||
- Add a Modbus RTU backend.
|
|
||||||
* Add a new, more flexible trigger framework.
|
|
||||||
* Add a generic software-trigger framework usable by any driver, currently
|
|
||||||
used by fx2lafw and saleae-logic16.
|
|
||||||
* Add a (Linux-only) GPIB SCPI backend using linux-gpib and libgpib.
|
|
||||||
* Add a generic scpi-pps driver which supports various power supplies.
|
|
||||||
* Add an experimental framework for "transforms" which can perform operations
|
|
||||||
on libsigrok session packets. This will be changed and improved upon in
|
|
||||||
later releases. Currently implemented tranforms:
|
|
||||||
- nop: Do nothing, pass on packets unmodified.
|
|
||||||
- scale: Scale all analog values by a specified factor.
|
|
||||||
- invert: Invert all the data values.
|
|
||||||
- An analog value of x becomes 1/x.
|
|
||||||
- A digital value of 0 becomes 1 (and vice versa).
|
|
||||||
* input:
|
|
||||||
- Introduce a new input module API.
|
|
||||||
- Rename "input format" to "input module" everywhere.
|
|
||||||
- Add a preferred file extension field (bug #541).
|
|
||||||
* output:
|
|
||||||
- Fix output option enumeration.
|
|
||||||
- Fix a double-free issue.
|
|
||||||
- Add a preferred file extension field (bug #541).
|
|
||||||
* input/csv:
|
|
||||||
- Avoid a segfault related to the obsolete mimetype format match (bug #681).
|
|
||||||
* input/vcd:
|
|
||||||
- Chunk up samples in 1MB blocks for better performance (bug #551).
|
|
||||||
- Allow optional index items (bug #322).
|
|
||||||
- Add support for 1 bit vectors (bug #723).
|
|
||||||
* input/wav:
|
|
||||||
- Fix an offset calculation error.
|
|
||||||
- Properly initialize the channel list early enough (bug #387).
|
|
||||||
* output/analog:
|
|
||||||
- Fix channel deinterleaving.
|
|
||||||
* output/csv:
|
|
||||||
- Match format based on .csv extention in the filename.
|
|
||||||
- Add support for analog data/packets.
|
|
||||||
* New or updated build dependencies:
|
|
||||||
- New build dependencies (libsigrok C library):
|
|
||||||
- libgpib (optional)
|
|
||||||
- libieee1284 (optional)
|
|
||||||
- Updated build dependencies (libsigrok C library):
|
|
||||||
- libserialport >= 0.1.1 (optional)
|
|
||||||
- librevisa >= 0.0.20130412 (optional)
|
|
||||||
- libftdi >= 0.16 or libftdi1 >= 1.0 (optional)
|
|
||||||
- New build dependencies (libsigrokcxx C++ library):
|
|
||||||
- libsigrok >= 0.4.0 (the libsigrok C library, see above)
|
|
||||||
- A C++ compiler with C++11 support (g++ >= 4.7 or clang++ >= 3.1)
|
|
||||||
- doxygen (required for building the C++ library!)
|
|
||||||
- graphviz (optional, only needed for C++ API docs)
|
|
||||||
- Python (2 or 3) executable (development files are not needed)
|
|
||||||
- glibmm-2.4 (>= 2.32.0)
|
|
||||||
- New build dependencies (libsigrok Python bindings):
|
|
||||||
- libsigrokcxx >= 0.4.0 (the libsigrok C++ bindings, see above)
|
|
||||||
- Python >= 2.7 or Python >= 3 (including development files!)
|
|
||||||
- Python setuptools (for Python 2 or 3)
|
|
||||||
- pygobject >= 3.0.0 (for Python 2 or 3), a.k.a python-gi
|
|
||||||
- numpy (for Python 2 or 3)
|
|
||||||
- SWIG >= 2.0.0
|
|
||||||
- doxygen (optional, only needed for the Python API docs)
|
|
||||||
- graphviz (optional, only needed for the Python API docs)
|
|
||||||
- doxypy (optional, only needed for the Python API docs)
|
|
||||||
- New build dependencies (libsigrok Ruby bindings):
|
|
||||||
- libsigrokcxx >= 0.4.0 (the libsigrok C++ bindings, see above)
|
|
||||||
- Ruby >= 1.9.3 (including development files!)
|
|
||||||
- SWIG >= 3.0.8
|
|
||||||
- YARD (optional, only needed for the Ruby API docs)
|
|
||||||
- New build dependencies (libsigrok Java bindings):
|
|
||||||
- libsigrokcxx >= 0.4.0 (the libsigrok C++ bindings, see above)
|
|
||||||
- SWIG >= 2.0.0
|
|
||||||
- Java JDK (for JNI includes and the javac/jar binaries)
|
|
||||||
- doxygen (optional, only needed for the Java API docs)
|
|
||||||
- graphviz (optional, only needed for the Java API docs)
|
|
||||||
* Build system:
|
|
||||||
- Modernize the whole autotools setup.
|
|
||||||
- Add --with-libserialport, --with-libftdi, --with-libusb,
|
|
||||||
--with-librevisa, --with-libgpib, --with-libieee1284.
|
|
||||||
- Add --enable-bindings, --enable-cxx, --enable-python, --enable-ruby,
|
|
||||||
and --enable-java.
|
|
||||||
- Support both libftdi >= 0.16 and libftdi1 >= 1.0.
|
|
||||||
- configure: Show SCPI backends that'll be compiled.
|
|
||||||
- Unconditionally build src/lcr/es51919.c (bug #545).
|
|
||||||
- Compile with -std=c99 and _POSIX_C_SOURCE=200112L by default.
|
|
||||||
- Only link the 'check' library against the unit tests.
|
|
||||||
- Fix various out-of-tree build issues (e.g. bug #473).
|
|
||||||
- Don't set CFLAGS, LDFLAGS, etc. in configure.ac or Makefile.am (bug #578).
|
|
||||||
- Check for the numpy Python module (bug #533).
|
|
||||||
- Check for zip_discard(), provide alternative if not available (bug #674).
|
|
||||||
* Portability:
|
|
||||||
- Android: Add fallbacks for missing stoi()/stod().
|
|
||||||
- FreeBSD: Fix a libusb related compiler error.
|
|
||||||
- FreeBSD: Fix an issue with libusb_get_port_numbers().
|
|
||||||
- FreeBSD: Fix an issue with BSD Make (bug #556).
|
|
||||||
- FreeBSD: Fix an issue with SWIG detection (bug #557).
|
|
||||||
- FreeBSD: Fix a build issue related to isascii() (bug #649).
|
|
||||||
- Mac OS X: Fix 'sed' invocation in autogen.sh (bug #516).
|
|
||||||
- Mac OS X: Fix a usb_get_port_path() related issue (bug #673).
|
|
||||||
- Windows: Fix some thread-related issues causing hangs (bugs #343, #328).
|
|
||||||
- Windows: Fix a USB/thread related issue (bug #343).
|
|
||||||
- Windows: Fix shared (non-static) build.
|
|
||||||
- Windows: Fix various warnings related to a missing LIBUSB_CALL item.
|
|
||||||
- Windows: Add a missing WSAStartup() call to fix scpi/tcp (bug #692).
|
|
||||||
- Fix an issue with non-GNU Make (bug #628).
|
|
||||||
- Avoid std::map::emplace() for GCC 4.7 compatibility (bug #720).
|
|
||||||
- Avoid g_close() to not unnecessarily require glib >= 2.36 (bug #724).
|
|
||||||
* Language bindings:
|
|
||||||
- Support new output API.
|
|
||||||
- Add Doxygen docs for all language bindings.
|
|
||||||
- C++: Fix a C++ bindings linking issue (bug #534).
|
|
||||||
- Python: Fix mapping of vector & map attributes to Python types (bug #382).
|
|
||||||
- Python: Implement equality checks for EnumValue derived classes (bug #443).
|
|
||||||
- Python: Handle import failures without crashing.
|
|
||||||
- Python: Fix an installation issue (bug #644).
|
|
||||||
- Python: Prevent a numpy deprecation warning (bug #417).
|
|
||||||
- Python: Fix a ConfigKey.parse_string() crash (bug #483).
|
|
||||||
- Python: Fix the build for Python 3 (bug #645).
|
|
||||||
- Python: Fix some string conversion issues for Python 3 (bug #478).
|
|
||||||
- Python: Fix a SWIG related memory leak (bug #479).
|
|
||||||
- Python: Make device.config_keys() act like a Python dict (bug #480).
|
|
||||||
- Python: Provide sensible __str__() and __repr__() for enums (bug #688).
|
|
||||||
- Java: Install files into DESTDIR (bug #537).
|
|
||||||
- Java: Fix some SWIG warnings due to %extend redefinitions (bug #417).
|
|
||||||
- Java: Fix an issue related to C/C++ style casts (bug #688).
|
|
||||||
- Java: Fix a reference leak (bug #690).
|
|
||||||
- Session::set_trigger(): Fix segfault conditions (bugs #491, #496).
|
|
||||||
* Various API changes, additions and removals (see API docs for details).
|
|
||||||
* Add various new config keys, config info types, measurement quantity keys,
|
|
||||||
measurement quantity flags, units, device instance types, and error codes.
|
|
||||||
* udev rules file: Add entries for newly supported hardware.
|
|
||||||
* Add/use a new resource access API, defaults to XDG data dirs.
|
|
||||||
* Switch to a new SR_DF_ANALOG format (bug #640).
|
|
||||||
* All drivers:
|
|
||||||
- Publish config key capabilities.
|
|
||||||
- Gather connection info and serial number, if any.
|
|
||||||
- Cleanups of serial port based drivers wrt (non-)blocking reads/writes.
|
|
||||||
* Various drivers:
|
|
||||||
- Use physical USB connection instead of sdi->index.
|
|
||||||
- Fix blocking serial write timeout (bugs #436, #437, #433, #428, #427,
|
|
||||||
#430, #432, #434, #438).
|
|
||||||
* agilent-dmm:
|
|
||||||
- Fix value parser to consider 0.0 to be a valid result.
|
|
||||||
- Correctly parse negative overload.
|
|
||||||
- Add RMS flag to AC voltage modes.
|
|
||||||
- Add provisional support for the U124xx.
|
|
||||||
- U124xx/U125xx: Support 5 more modes (resistance, capacitance, frequency,
|
|
||||||
continuity, and temperature).
|
|
||||||
- Add current loop sensor support.
|
|
||||||
- Fix frequency support.
|
|
||||||
* beaglelogic:
|
|
||||||
- Add SR_CONF_CAPTURE_RATIO support.
|
|
||||||
* brymen-bm86x:
|
|
||||||
- Add current loop sensor support.
|
|
||||||
* chronovu-la:
|
|
||||||
- Properly handle multiple ChronoVu devices being attached (bug #504).
|
|
||||||
* colead-slm:
|
|
||||||
- Properly check acquisition sample limit.
|
|
||||||
* demo:
|
|
||||||
- Add support for continuous acquisition.
|
|
||||||
- Fix a memory leak related to channel groups.
|
|
||||||
- Support changing the amplitude of analog channels.
|
|
||||||
- Adds a new channel group "Analog", which has all analog channels in it.
|
|
||||||
- Attach analog generator to channel, not channel group.
|
|
||||||
- Provide a separate property list for the analog group (bug #505).
|
|
||||||
- Fix an issue by always honoring sample limit changes (bug #314).
|
|
||||||
- Fix square pattern output being shorter than other patterns.
|
|
||||||
- Fix analog output at low samplerates.
|
|
||||||
- Fix SR_CONF_DEVICE_OPTIONS variant type.
|
|
||||||
- Fix an infinite loop when 0 channels of one type were used.
|
|
||||||
* fx2lafw:
|
|
||||||
- Fix continuous mode usage with output modules (bug #380).
|
|
||||||
- Check for a valid samplerate before trying to set it (bug #386).
|
|
||||||
- Fix wide (16bit) sampling case (bug #373).
|
|
||||||
- Add SR_CONF_CAPTURE_RATIO support.
|
|
||||||
- Set up the transfer first, then start the acquisition (bug #574).
|
|
||||||
- Avoid the need to run "rmmod usbtest" on Linux for devices
|
|
||||||
with the standard Cypress FX2 USB VID/PID of 04b4:8613 (bug #445).
|
|
||||||
- Add support for the official fx2lafw sigrok VID/PID pairs and firmware
|
|
||||||
(this requires sigrok-firmware-fx2lafw >= 0.1.3):
|
|
||||||
- 1D50:608C: fx2lafw-sigrok-fx2-8ch.fw
|
|
||||||
- 1D50:608D: fx2lafw-sigrok-fx2-16ch.fw
|
|
||||||
* gmc-mh-1x-2x:
|
|
||||||
- Add support for the Metrahit Metrahit 16T, 16U, and KMM2002.
|
|
||||||
- Complete energy measurement ranges (V, A, W) for Metrahit 29S.
|
|
||||||
* hameg-hmo:
|
|
||||||
- Implement SR_CONF_SCAN_OPTIONS.
|
|
||||||
- Make sure the enabled_channels list is empty before populating it.
|
|
||||||
- Fix reading of analog data from an HMO1024 (Firmware 04.527).
|
|
||||||
- Fix a double-free issue.
|
|
||||||
- Fix a floating point comparison issue (bug #731).
|
|
||||||
* hantek-dso:
|
|
||||||
- Properly zero out MQ flags.
|
|
||||||
- Fix driver/global/channel group config keys.
|
|
||||||
* ikalogic-scanalogic2:
|
|
||||||
- Fix a segfault condition (bug #440).
|
|
||||||
* ikalogic-scanaplus:
|
|
||||||
- Fix a memory leak and a memory allocation issue.
|
|
||||||
* kecheng-kc-330b:
|
|
||||||
- Fix missing time/frequency weighting.
|
|
||||||
* lascar-el-usb:
|
|
||||||
- Fix issues caused by copy-paste errors.
|
|
||||||
* ols:
|
|
||||||
- Fix a serial port related issue on FreeBSD (bug #414).
|
|
||||||
- Fix detection and acquisition on Windows (bug #562).
|
|
||||||
- Fix an event source related acquisition issue (bug #678).
|
|
||||||
- Fix a portability issue due to direct use of FDs (bug #205).
|
|
||||||
* rigol-ds:
|
|
||||||
- Fix duplicated vendor string for Agilent devices.
|
|
||||||
- Replace magic numbers by appropriate constant or variable (bug #406).
|
|
||||||
- Handle POSITIVE/NEGATIVE (instead of POS/NEG) correctly (bug #558).
|
|
||||||
- Add missing 20/50/100V vdiv entries.
|
|
||||||
- Fix an issue related to the search for the closest vdiv.
|
|
||||||
- Return the actual hardware num_vdiv and vdiv list.
|
|
||||||
- Fix the smallest supported vdiv for the DS2000 series.
|
|
||||||
- Fix a double-free issue.
|
|
||||||
- SR_CONF_DATA_SOURCE is a device option, not per channel group.
|
|
||||||
* saleae-logic16:
|
|
||||||
- Recognize the FPGA FIFO overflow status.
|
|
||||||
- Downgrade error during capture to a message (bug #466).
|
|
||||||
- Add SR_CONF_CAPTURE_RATIO support.
|
|
||||||
- Support new bitstream version 1.3 with renumbered registers.
|
|
||||||
- Publish samplerates according to selected channels (bug #646).
|
|
||||||
* serial-dmm:
|
|
||||||
- Fix RadioShack 22-812 DMM incorrect readings (bug #657).
|
|
||||||
- Implement request timeout feature (bug #345).
|
|
||||||
* sysclk-lwla:
|
|
||||||
- Do not create channels in reverse order.
|
|
||||||
- Fix an issue related to sdi->connection_id (bug #441).
|
|
||||||
- Various robustness improvements.
|
|
||||||
- Fix a compile issue (bug #714).
|
|
||||||
- Work around some vendor FX2 firmware issues.
|
|
||||||
* zeroplus-logic-cube:
|
|
||||||
- Fix an issue when trying to trigger on a channel being 0/low.
|
|
||||||
* README: Drop obsolete sigrok-commits mailing list.
|
|
||||||
* Fix a Doxyfile issue which caused build failures e.g. on buildroot.
|
|
||||||
* Fix a USB timeout related sr_session_iteration() issue (bug #571).
|
|
||||||
* Fix various gcc/clang compiler errors/warnings (e.g. bugs #637, #721).
|
|
||||||
* Fix an issue related to multiple sr_init()/sr_exit() calls (bug #565).
|
|
||||||
* Fix an issue with transform packet passing (bug #631).
|
|
||||||
* Rename sr_dev_driver.priv to .context (bug #442).
|
|
||||||
* serial: Re-implement sr_serial_find_usb() using new libserialport APIs.
|
|
||||||
* Unit tests: Add some checks for session handling.
|
|
||||||
* scpi and scpi/usbtmc:
|
|
||||||
- Accept *IDN responses with more than 4 tokens (some devices need this).
|
|
||||||
- Fix incomplete data issue for e.g. Hameg HMO1024.
|
|
||||||
- Support the RL1 feature.
|
|
||||||
- Implement Rigol DS1000 workaround on any firmware version (bug #354).
|
|
||||||
* Various session related changes and improvements.
|
|
||||||
* The code now uses the Glib main loop as backend.
|
|
||||||
* Logging: Add a timestamp (at DBG/SPEW loglevel) to all log messages.
|
|
||||||
* Fix a trigger related segfault (bug #550).
|
|
||||||
* Don't check the libusb_get_device_descriptor() return code (bug #658).
|
|
||||||
* Fix various memory leaks in the code (e.g. bugs #629, #630, #632).
|
|
||||||
* session_file: Enable only probes listed in metadata (bugs #410, #495).
|
|
||||||
|
|
||||||
0.3.0 (2014-05-06)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Note: This release DOES change the libsigrok API. That means it is NOT
|
|
||||||
backwards-compatible and frontends will need updates.
|
|
||||||
|
|
||||||
* New supported hardware:
|
|
||||||
- Logic analyzers:
|
|
||||||
- ChronoVu LA16
|
|
||||||
- Sysclk LWLA1034
|
|
||||||
- Oscilloscopes:
|
|
||||||
- Agilent DSO1000 series (various models)
|
|
||||||
- Hameg HMO compact series (various models)
|
|
||||||
- Rigol DS2000 series (various models)
|
|
||||||
- Rigol VS5000 series (various models)
|
|
||||||
- Multimeters:
|
|
||||||
- BBC Goerz Metrawatt M2110
|
|
||||||
- Brymen BM869
|
|
||||||
- Fluke 189
|
|
||||||
- Gossen Metrawatt MetraHIT 1x/2x series (various models)
|
|
||||||
- Tenma 72-7745 (rebadged UNI-T UT60E)
|
|
||||||
- Tenma 72-7750 (rebadged UNI-T UT60G)
|
|
||||||
- UNI-T UT60G
|
|
||||||
- UNI-T UT61B
|
|
||||||
- UNI-T UT61C
|
|
||||||
- V&A VA40B
|
|
||||||
- Voltcraft M-3650CR
|
|
||||||
- Voltcraft ME-42
|
|
||||||
- Thermometers:
|
|
||||||
- APPA 55II
|
|
||||||
- Programmable power supplies:
|
|
||||||
- Atten PPS3000 series (various models, tested on PPS3203T-3S).
|
|
||||||
- Conrad DIGI 35 CPU
|
|
||||||
* Add support for channel groups.
|
|
||||||
* Add generic SCPI backend code which can be used via various transports:
|
|
||||||
- Serial ports
|
|
||||||
- USBTMC
|
|
||||||
- TCP/RAW
|
|
||||||
- TCP/Rigol (Rigol-VS5000 series specific)
|
|
||||||
- VXI
|
|
||||||
- librevisa
|
|
||||||
* udev rules file: Add entries for newly supported hardware.
|
|
||||||
* New config keys:
|
|
||||||
- SR_CONF_DEMODULATOR
|
|
||||||
- SR_CONF_CENTER_FREQUENCY
|
|
||||||
- SR_CONF_NUM_LOGIC_CHANNELS
|
|
||||||
- SR_CONF_NUM_ANALOG_CHANNELS
|
|
||||||
- SR_CONF_CLOCK_EDGE
|
|
||||||
- SR_CONF_POWER_SUPPLY
|
|
||||||
- SR_CONF_OUTPUT_VOLTAGE
|
|
||||||
- SR_CONF_OUTPUT_VOLTAGE_MAX
|
|
||||||
- SR_CONF_OUTPUT_CURRENT
|
|
||||||
- SR_CONF_OUTPUT_CURRENT_MAX
|
|
||||||
- SR_CONF_OUTPUT_ENABLED
|
|
||||||
- SR_CONF_OUTPUT_CHANNEL
|
|
||||||
- SR_CONF_OVER_VOLTAGE_PROTECTION
|
|
||||||
- SR_CONF_OVER_CURRENT_PROTECTION
|
|
||||||
- SR_CONF_DEVICE_MODE
|
|
||||||
- SR_CONF_TEST_MODE
|
|
||||||
* New config info types:
|
|
||||||
- SR_T_INT32
|
|
||||||
* New measurement quantity keys:
|
|
||||||
- SR_MQ_TIME
|
|
||||||
* New measurement quantity flags:
|
|
||||||
- SR_MQFLAG_DURATION
|
|
||||||
- SR_MQFLAG_AVG
|
|
||||||
* New device instance types:
|
|
||||||
- SR_INST_SCPI
|
|
||||||
* New error codes:
|
|
||||||
- SR_ERR_CHANNEL_GROUP
|
|
||||||
∗ The SR_T_CHAR config type has been renamed to SR_T_STRING.
|
|
||||||
* New build dependencies:
|
|
||||||
- libserialport >= 0.1.0 (optional).
|
|
||||||
- All drivers that talk to serial ports now require libserialport.
|
|
||||||
- If libserialport is not found, those drivers will not be built.
|
|
||||||
- librevisa >= 0.0.20130812 (optional, only used by some drivers).
|
|
||||||
* Dropped build dependencies:
|
|
||||||
- libasound2 is no longer required (only the removed alsa driver used it).
|
|
||||||
- libudev is no longer required (only the removed link-mso19 driver used it).
|
|
||||||
* Serial port handling code:
|
|
||||||
- Add support for 5/6 data bits and non-standard baud rates.
|
|
||||||
- Fix an issue related to nonblocking reads (bug #188).
|
|
||||||
- Fix an 'invalid arguments' condition causing problems (bug #192).
|
|
||||||
* alsa: This driver was removed (also fixes bugs #28, #61, #96, #114).
|
|
||||||
* asix-sigma:
|
|
||||||
- Publish SR_CONF_CAPTURE_RATIO correctly (bugs #287, #291).
|
|
||||||
- Don't set invalid config options (bug #86).
|
|
||||||
- Various other bugfixes.
|
|
||||||
* cem-dt-885x: Fix a code portability issue (bug #267).
|
|
||||||
* chronovu-la:
|
|
||||||
- Rename the 'chronovu-la8' driver to 'chronovu-la'.
|
|
||||||
- Add support for the ChronoVu LA16.
|
|
||||||
- Fix a segfault when doing multiple acquisitions on an LA8 (bug #247).
|
|
||||||
- Document that streaming is not possible for LA8/LA16 (bug #261).
|
|
||||||
* demo:
|
|
||||||
- Add support for analog channels (bug #11).
|
|
||||||
- Make the number of channels user-configurable.
|
|
||||||
- Add per-channel-group options.
|
|
||||||
- Implement analog sample patterns: sine, triangle, sawtooth.
|
|
||||||
- Fix a samplerate related issue with rates >= 50kHz (bugs #294, #295).
|
|
||||||
- Fix an issue causing data glitches every 40ms (bug #297).
|
|
||||||
- Fix an issue related to channel group handling (bug #262).
|
|
||||||
* digitek-dt4000zc: Fix driver on NetBSD due to missing DTR=1 (bug #189).
|
|
||||||
* fx2lafw:
|
|
||||||
- Fix incorrect unitsize when a trigger fires (bug #182).
|
|
||||||
- Implement config_get() for SR_CONF_LIMIT_SAMPLES.
|
|
||||||
- Don't send more samples than requested to the session bus.
|
|
||||||
* gmc-mh-1x-2x:
|
|
||||||
- Add new (sub)driver 'gmc-mh-2x-bd232'.
|
|
||||||
- Add support for the SI232-II interface.
|
|
||||||
* hameg-hmo:
|
|
||||||
- Fix a build issue on Mac OS X (bug #216).
|
|
||||||
- Various fixes related to frame limit, samplerate, options, etc.
|
|
||||||
* link-mso19: Drop unfinished driver for now (until it starts working).
|
|
||||||
* openbench-logic-sniffer:
|
|
||||||
- Fix a serial port related issue/hang, seen on (e.g.) NetBSD.
|
|
||||||
- Fix an SR_CONF_PATTERN_MODE related problem (bugs #184, #190).
|
|
||||||
- Fix a serial (non)blocking mode issue (bug #231).
|
|
||||||
- Temporarily disable the driver on Windows (needs portability fixes).
|
|
||||||
- Fix an endianness issue in the protocol handling (bug #135).
|
|
||||||
- Fix a sampling issue when (samples % 4) != 0 (bug #200).
|
|
||||||
- Fix an issue occurring when all channels were disabled (bugs #316, #347).
|
|
||||||
- Add an option to turn test patterns off again (bug #293).
|
|
||||||
* rigol-ds:
|
|
||||||
- Rename the 'rigol-ds1xx2' driver to 'rigol-ds'.
|
|
||||||
- Add support for more models and unify the driver code (bug #212).
|
|
||||||
- Add support for RS232 connectivity (previously only USBTMC worked).
|
|
||||||
- Enable the driver on non-Linux platforms since all transports the driver
|
|
||||||
uses are provided by cross-platform code/libs now (e.g. serial, USBTMC).
|
|
||||||
- Add support for 4 channels (required by some models).
|
|
||||||
- Add support for channel groups.
|
|
||||||
- Advertise SR_CONF_LIMIT_FRAMES support.
|
|
||||||
- Fix an issue with SR_DF_END sending.
|
|
||||||
- Enable/disable LA pod when (de)selecting digital channels.
|
|
||||||
- Disable key lock when closing device.
|
|
||||||
- Work around issues due to DS1000 specific protocol changes (bug #283).
|
|
||||||
- Fix incorrect digital channel numbers on some models (bug #269).
|
|
||||||
- Fix an issue with partial data reads (bugs #220, #209, #207).
|
|
||||||
* saleae-logic16:
|
|
||||||
- Fix an issue related to USB device claiming (bug #159).
|
|
||||||
- Don't send more samples than requested to the session bus (bug #350).
|
|
||||||
- Use unitsize 1 (not 2) if none of channels 8-15 are used.
|
|
||||||
* serial-dmm: Fix some parse issues by increasing a timeout.
|
|
||||||
* sysclk-lwla: Fix a sampling issue related to a glib API call (bug #270).
|
|
||||||
* uni-t-ut32x: Fix typo which prevented usage with multiple devices.
|
|
||||||
* victor-dmm: Fix MIN/MAX always being reported (bug #228).
|
|
||||||
* zeroplus-logic-cube:
|
|
||||||
- Add support for 32-channel models and additional memory sizes.
|
|
||||||
- Fix a frontend issue due to missing SR_CONF_CAPTURE_RATIO.
|
|
||||||
- Fix an issue causing pre-trigger garbage data to be sent (bug #222).
|
|
||||||
- Add initial voltage threshold support.
|
|
||||||
* Various drivers:
|
|
||||||
- Expose missing SR_CONF_TRIGGER_TYPE.
|
|
||||||
- Report max. possible number of samples (bugs #197, #258, #263).
|
|
||||||
* Output modules:
|
|
||||||
- Skip analog channels in logic-only output formats.
|
|
||||||
- Remove the obsolete output module API, add wrapper calls for the new one.
|
|
||||||
- Stop using the obsolete output API (bugs #288, #47, #48).
|
|
||||||
- Properly receive and handle samplerate metadata (bug #46).
|
|
||||||
* input/vcd: Abort with an error upon > 64 channels (bug #194).
|
|
||||||
* output/vcd:
|
|
||||||
- Fix output for more than 8 channels.
|
|
||||||
- Output timestamp only once per change.
|
|
||||||
- Minor whitespace changes of output files to make them more readable.
|
|
||||||
- Remove bogus $dumpvars and $dumpoff commands.
|
|
||||||
- Various bugfixes and portability fixes.
|
|
||||||
* output/csv:
|
|
||||||
- Remove a 64-channel limit (bug #193).
|
|
||||||
- Fix an issue resulting in incorrect trailing commas (bug #230).
|
|
||||||
- Fix an issue where all timestamps would be zero (bug #35).
|
|
||||||
* Rename 'probe' to 'channel' in all places, since libsigrok supports a lot
|
|
||||||
of gear where 'channel' fits better (bug #259).
|
|
||||||
* Change TRIGGER_SLOPE from SR_T_UINT64 to SR_T_STRING.
|
|
||||||
* Windows support improvements:
|
|
||||||
- Use libserialport (+other backend code) to fix serial devices (bug #91).
|
|
||||||
- Add serial source addition/removal backend code (bug #206).
|
|
||||||
- Add backend code for properly supporting USB based devices.
|
|
||||||
* Fix various memory leaks in the backend code.
|
|
||||||
* Fix some incorrect parsing of floating point numbers in the strutil code.
|
|
||||||
* Fix various endianness issues in backend code and drivers (bug #266).
|
|
||||||
* Fix a few issues related to incorrect parsing with non-ANSI-C locales
|
|
||||||
(bugs #271, #272, #273, #274).
|
|
||||||
* Fix an issue related to data being sent to the libsigrok session which
|
|
||||||
was not a multiple of the unit/sample size (bugs, #289, #290).
|
|
||||||
* Drop the es51922 DMM parser (replaced by the generic es519xx parser).
|
|
||||||
* libsigrok session files:
|
|
||||||
- The libsigrok session file format (for *.sr files) has changed, and the
|
|
||||||
file format 'version' field is bumped to 2. Older libsigrok versions will
|
|
||||||
not be able to handle version 2 files, but new libsigrok versions can
|
|
||||||
handle both version 1 and version 2 files. New libsigrok versions will
|
|
||||||
always write/output version 2 files.
|
|
||||||
- Fix an issue resulting in left-over temporary files (bug #276).
|
|
||||||
- Fix an issue with analog probes (unsupported) in *.sr files (bug #277).
|
|
||||||
- Fix an issue with missing samplerate from session files (bug #275).
|
|
||||||
* Improved API documentation.
|
|
||||||
* Major API changes (overview):
|
|
||||||
- Change various function/macro names related to the probe->channel rename.
|
|
||||||
- Change various functions due to the new channel group feature.
|
|
||||||
- All enums in the public API now have names (e.g. 'enum sr_mqflag').
|
|
||||||
- The lib no longer defines names with _t suffix (POSIX reserved).
|
|
||||||
- New API calls:
|
|
||||||
- sr_session_dev_list()
|
|
||||||
- sr_session_save_init()
|
|
||||||
- sr_session_append()
|
|
||||||
- sr_config_commit()
|
|
||||||
- sr_output_new()
|
|
||||||
- sr_output_send()
|
|
||||||
- sr_output_free()
|
|
||||||
- Obsoleted and removed API calls:
|
|
||||||
- sr_filter_channels()
|
|
||||||
- 'struct sr_session' is now opaque (contents shouldn't be used directly).
|
|
||||||
- Please see the Doxygen API documentation for further details.
|
|
||||||
* Build system:
|
|
||||||
- Switch to a non-recursive automake setup (fewer files, faster builds).
|
|
||||||
- configure: Clearly mark required and optional libs.
|
|
||||||
|
|
||||||
0.2.2 (2013-11-04)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Note: This release does NOT change the libsigrok API. While new config keys,
|
|
||||||
config info types, and unit codes have been added (additional enum
|
|
||||||
entries / numbers), no existing interfaces were added/changed/removed.
|
|
||||||
Frontends should continue to work fine without recompiling or relinking.
|
|
||||||
|
|
||||||
* New supported hardware:
|
|
||||||
- Logic analyzers:
|
|
||||||
- Saleae Logic16
|
|
||||||
- Thermometers:
|
|
||||||
- Center 309
|
|
||||||
- UNI-T UT325
|
|
||||||
- Voltcraft K204
|
|
||||||
- Multimeters:
|
|
||||||
- ISOTECH IDM103N
|
|
||||||
- Metex M-4650CR
|
|
||||||
- Norma DM950
|
|
||||||
- Voltcraft M-4650CR
|
|
||||||
- Energy meters:
|
|
||||||
- EDF Teleinfo
|
|
||||||
* New config keys:
|
|
||||||
- SR_CONF_VOLTAGE_THRESHOLD
|
|
||||||
- SR_CONF_EXTERNAL_CLOCK
|
|
||||||
- SR_CONF_SWAP
|
|
||||||
- SR_CONF_ENERGYMETER
|
|
||||||
* New config info types:
|
|
||||||
- SR_T_DOUBLE_RANGE
|
|
||||||
* New units:
|
|
||||||
- SR_UNIT_REVOLUTIONS_PER_MINUTE
|
|
||||||
- SR_UNIT_VOLT_AMPERE
|
|
||||||
- SR_UNIT_WATT
|
|
||||||
- SR_UNIT_WATT_HOUR
|
|
||||||
* New input modules:
|
|
||||||
- csv (comma-separated values)
|
|
||||||
* Bump required libzip version to >= 0.10.
|
|
||||||
* uni-t-dmm: This driver now requires the specification of the USB VID/PID
|
|
||||||
of the cable that is used. Example for sigrok-cli:
|
|
||||||
- Old: sigrok-cli --driver voltcraft-vc820 ...
|
|
||||||
- New: sigrok-cli --driver voltcraft-vc820:conn=1a86.e008 ...
|
|
||||||
* openbench-logic-sniffer:
|
|
||||||
- Initial test pattern support (SR_CONF_PATTERN_MODE).
|
|
||||||
- Initial external clock support (SR_CONF_EXTERNAL_CLOCK).
|
|
||||||
- Initial channel swap support (SR_CONF_SWAP).
|
|
||||||
- Various minor fixes and improvements.
|
|
||||||
* When a frontend adds a device instance to a running session, start
|
|
||||||
acquisition on it. This helps with the collectd use-case where devices
|
|
||||||
can be removed and added dynamically while a session is running.
|
|
||||||
* rigol-ds1xx2: Support newer Linux kernels with USBTMC in /sys/class/usbmisc.
|
|
||||||
* rigol-ds1xx2: Also detect the Rigol DS1152E/DS1152D.
|
|
||||||
* agilent-dmm: Fix a segfault happening in certain cases.
|
|
||||||
* output/analog: Support all currently known MQFLAGs.
|
|
||||||
* Fix a minor compile issue due to an incorrect #include.
|
|
||||||
* Fix two compile issues on FreeBSD (bug #185).
|
|
||||||
* es519xx: New generic parser for various Cyrustek DMM ICs.
|
|
||||||
* es51922/fs9721/fs9922/metex14: Use diode MQFLAG (bug #141).
|
|
||||||
* voltcraft-vc830: Fix diode mode handling (bug #142).
|
|
||||||
* Add the missing HACKING file to the tarball.
|
|
||||||
* README.devices: Updates/notes for newly added devices.
|
|
||||||
|
|
||||||
0.2.1 (2013-08-07)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Note: This release does NOT change the libsigrok API. While new config keys,
|
|
||||||
config info types, and error codes have been added (additional enum
|
|
||||||
entries / numbers), no existing interfaces were added/changed/removed.
|
|
||||||
Frontends should continue to work fine without recompiling or relinking.
|
|
||||||
|
|
||||||
* New supported hardware:
|
|
||||||
- Logic analyzers:
|
|
||||||
- IKALOGIC Scanalogic-2
|
|
||||||
- IKALOGIC ScanaPLUS
|
|
||||||
- Sound level meters:
|
|
||||||
- CEM DT-8852
|
|
||||||
- Kecheng KC-330B
|
|
||||||
- Multimeters:
|
|
||||||
- UNI-T UT60A
|
|
||||||
- UNI-T UT60E
|
|
||||||
- Voltcraft M-3650D
|
|
||||||
- Voltcraft VC-830
|
|
||||||
* Drop the Tecpel DMM-8060 (doesn't have PC connectivity).
|
|
||||||
* New config keys:
|
|
||||||
- SR_CONF_SPL_WEIGHT_FREQ
|
|
||||||
- SR_CONF_SPL_WEIGHT_TIME
|
|
||||||
- SR_CONF_SPL_MEASUREMENT_RANGE
|
|
||||||
- SR_CONF_HOLD_MIN
|
|
||||||
- SR_CONF_HOLD_MAX
|
|
||||||
- SR_CONF_POWER_OFF
|
|
||||||
- SR_CONF_DATA_SOURCE
|
|
||||||
* New config info types:
|
|
||||||
- SR_T_UINT64_RANGE
|
|
||||||
* New error codes:
|
|
||||||
- SR_ERR_TIMEOUT
|
|
||||||
* Always link against libm, the math library (bug #115).
|
|
||||||
* Fix a bug in sr_si_string_u64() at al (bug #73).
|
|
||||||
* output/csv: Fix incorrect probe order.
|
|
||||||
* alsa: Fix a double-free issue (bug #129).
|
|
||||||
* zeroplus-logic-cube: Fix a bug in the driver cleanup code.
|
|
||||||
* ikalogic-scanalogic2: Use GET_REPORT (bug #130).
|
|
||||||
* uni-t-dmm: Fix a bug breaking the UNI-T UT61E (bug #136).
|
|
||||||
* Various internal consistency fixes and code cleanups.
|
|
||||||
* Improved Doxygen documentation.
|
|
||||||
* Fixed various memory leaks.
|
|
||||||
|
|
||||||
0.2.0 (2013-05-04)
|
0.2.0 (2013-05-04)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
|
||||||
81
README
81
README
|
|
@ -27,69 +27,19 @@ Distro packagers should only use released tarballs (no git snapshots).
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Requirements for the C library:
|
- git
|
||||||
|
- gcc (>= 4.0)
|
||||||
- git (only needed when building from git)
|
|
||||||
- gcc (>= 4.0) or clang
|
|
||||||
- make
|
- make
|
||||||
- autoconf >= 2.63 (only needed when building from git)
|
- autoconf >= 2.63
|
||||||
- automake >= 1.11 (only needed when building from git)
|
- automake >= 1.11
|
||||||
- libtool (only needed when building from git)
|
- libtool
|
||||||
- pkg-config >= 0.22
|
- pkg-config >= 0.22
|
||||||
- libglib >= 2.32.0
|
- libglib >= 2.32.0
|
||||||
- libzip >= 0.10
|
- libzip >= 0.8
|
||||||
- libtirpc (optional, used by VXI, fallback when glibc >= 2.26)
|
- libusb-1.0 >= 1.0.9 (optional, used by most drivers)
|
||||||
- libserialport >= 0.1.1 (optional, used by some drivers)
|
- libftdi >= 0.16 (optional, used by some drivers)
|
||||||
- librevisa >= 0.0.20130412 (optional, used by some drivers)
|
- libasound / alsa-lib >= 1.0 (optional, only used by the alsa driver)
|
||||||
- libusb-1.0 >= 1.0.16 (optional, used by some drivers)
|
|
||||||
- hidapi >= 0.8.0 (optional, used for some HID based "serial cables")
|
|
||||||
- bluez/libbluetooth >= 4.0 (optional, used for Bluetooth/BLE communication)
|
|
||||||
- libftdi1 >= 1.0 (optional, used by some drivers)
|
|
||||||
- libgpib (optional, used by some drivers)
|
|
||||||
- libieee1284 (optional, used by some drivers)
|
|
||||||
- libgio >= 2.32.0 (optional, used by some drivers)
|
|
||||||
- check >= 0.9.4 (optional, only needed to run unit tests)
|
- check >= 0.9.4 (optional, only needed to run unit tests)
|
||||||
- doxygen (optional, only needed for the C API docs)
|
|
||||||
- graphviz (optional, only needed for the C API docs)
|
|
||||||
|
|
||||||
Requirements for the C++ bindings:
|
|
||||||
|
|
||||||
- libsigrok >= 0.4.0 (the libsigrok C library, see above)
|
|
||||||
- A C++ compiler with C++11 support (-std=c++11 option), e.g.
|
|
||||||
- g++ (>= 4.8.1)
|
|
||||||
- clang++ (>= 3.3)
|
|
||||||
- autoconf-archive (only needed when building from git)
|
|
||||||
- doxygen (required for building the bindings, not only for C++ API docs!)
|
|
||||||
- graphviz (optional, only needed for the C++ API docs)
|
|
||||||
- Python (2 or 3) executable (development files are not needed)
|
|
||||||
- glibmm-2.4 (>= 2.32.0)
|
|
||||||
|
|
||||||
Requirements for the Python bindings:
|
|
||||||
|
|
||||||
- libsigrokcxx >= 0.4.0 (the libsigrok C++ bindings, see above)
|
|
||||||
- Python >= 2.7 or Python >= 3 (including development files!)
|
|
||||||
- Python setuptools (for Python 2 or 3)
|
|
||||||
- pygobject >= 3.0.0 (for Python 2 or 3), a.k.a python-gi
|
|
||||||
- numpy (for Python 2 or 3)
|
|
||||||
- SWIG >= 2.0.0
|
|
||||||
- doxygen (optional, only needed for the Python API docs)
|
|
||||||
- graphviz (optional, only needed for the Python API docs)
|
|
||||||
- doxypy (optional, only needed for the Python API docs)
|
|
||||||
|
|
||||||
Requirements for the Ruby bindings:
|
|
||||||
|
|
||||||
- libsigrokcxx >= 0.4.0 (the libsigrok C++ bindings, see above)
|
|
||||||
- Ruby >= 2.5.0 (including development files!)
|
|
||||||
- SWIG >= 3.0.8
|
|
||||||
- YARD (optional, only needed for the Ruby API docs)
|
|
||||||
|
|
||||||
Requirements for the Java bindings:
|
|
||||||
|
|
||||||
- libsigrokcxx >= 0.4.0 (the libsigrok C++ bindings, see above)
|
|
||||||
- SWIG >= 2.0.0
|
|
||||||
- Java JDK (for JNI includes and the javac/jar binaries)
|
|
||||||
- doxygen (optional, only needed for the Java API docs)
|
|
||||||
- graphviz (optional, only needed for the Java API docs)
|
|
||||||
|
|
||||||
|
|
||||||
Building and installing
|
Building and installing
|
||||||
|
|
@ -111,10 +61,6 @@ See INSTALL or the following wiki page for more (OS-specific) instructions:
|
||||||
|
|
||||||
http://sigrok.org/wiki/Building
|
http://sigrok.org/wiki/Building
|
||||||
|
|
||||||
Please also check the following wiki page in case you encounter any issues:
|
|
||||||
|
|
||||||
http://sigrok.org/wiki/Building#FAQ
|
|
||||||
|
|
||||||
|
|
||||||
Device-specific issues
|
Device-specific issues
|
||||||
----------------------
|
----------------------
|
||||||
|
|
@ -143,16 +89,19 @@ the library as a whole is licensed under the terms of the GPLv3+.
|
||||||
Please see the individual source files for the full list of copyright holders.
|
Please see the individual source files for the full list of copyright holders.
|
||||||
|
|
||||||
|
|
||||||
Mailing list
|
Mailing lists
|
||||||
------------
|
-------------
|
||||||
|
|
||||||
|
There are two mailing lists for sigrok/libsigrok:
|
||||||
|
|
||||||
https://lists.sourceforge.net/lists/listinfo/sigrok-devel
|
https://lists.sourceforge.net/lists/listinfo/sigrok-devel
|
||||||
|
https://lists.sourceforge.net/lists/listinfo/sigrok-commits
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|
|
||||||
427
README.devices
427
README.devices
|
|
@ -10,15 +10,11 @@ Firmware
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Some devices supported by libsigrok need a firmware to be uploaded every time
|
Some devices supported by libsigrok need a firmware to be uploaded every time
|
||||||
the device is connected to the PC (usually via USB), before it can be used.
|
the device is connected to the PC (usually via USB), before it can be used.
|
||||||
|
|
||||||
The default locations where libsigrok expects the firmware files are:
|
The default location where libsigrok expects the firmware files is:
|
||||||
|
|
||||||
$SIGROK_FIRMWARE_DIR (environment variable)
|
|
||||||
$HOME/.local/share/sigrok-firmware
|
|
||||||
$prefix/share/sigrok-firmware
|
$prefix/share/sigrok-firmware
|
||||||
/usr/local/share/sigrok-firmware
|
|
||||||
/usr/share/sigrok-firmware
|
|
||||||
|
|
||||||
($prefix is usually /usr/local or /usr, depending on your ./configure options)
|
($prefix is usually /usr/local or /usr, depending on your ./configure options)
|
||||||
|
|
||||||
|
|
@ -37,134 +33,40 @@ The following drivers/devices require a firmware upload upon connection:
|
||||||
'sigrok-firmware' repository/project under a license which allows us
|
'sigrok-firmware' repository/project under a license which allows us
|
||||||
to redistribute them.
|
to redistribute them.
|
||||||
|
|
||||||
- dreamsourcelab-dslogic: The DreamSourceLab DSLogic/DSCope device series
|
|
||||||
requires various firmware files and FPGA bitstream files.
|
|
||||||
These can be extracted/downloaded from the vendor's GitHub repo using a
|
|
||||||
tool from our 'sigrok-util' repository/project.
|
|
||||||
|
|
||||||
- fx2lafw: Logic analyzers based on the Cypress FX2(LP) chip need the
|
- fx2lafw: Logic analyzers based on the Cypress FX2(LP) chip need the
|
||||||
firmware files from the 'sigrok-firmware-fx2lafw' repository/project.
|
firmware files from the 'sigrok-firmware-fx2lafw' repository/project.
|
||||||
The firmware is written from scratch and licensed under the GNU GPLv2+.
|
The firmware is written from scratch and licensed under the GPLv2+.
|
||||||
|
|
||||||
- hantek-6xxx: Certain oscilloscopes based on the Cypress FX2(LP) chip, such
|
|
||||||
as the Hantek 6022BE/6022BL, SainSmart DDS120, and Rocktech BM102, need the
|
|
||||||
firmware files from the 'sigrok-firmware-fx2lafw' repository/project.
|
|
||||||
The firmware is written from scratch and licensed under the GNU GPLv2+.
|
|
||||||
|
|
||||||
- hantek-dso: The Hantek DSO-2090 (and other supported models of the same
|
- hantek-dso: The Hantek DSO-2090 (and other supported models of the same
|
||||||
series of Hantek PC oscilloscopes) need firmware files.
|
series of Hantek PC oscilloscopes) need firmware files.
|
||||||
These can be extracted from the vendor's Windows drivers using a tool
|
These can be extracted from the vendor's Windows drivers using a tool
|
||||||
from our 'sigrok-util' repository/project.
|
from our 'sigrok-util' repository/project.
|
||||||
|
|
||||||
- lecroy-logicstudio: The LeCroy LogicStudio requires FPGA bitstream files.
|
|
||||||
These can be extracted from the vendor's Windows software using a tool
|
|
||||||
from our 'sigrok-util' repository/project.
|
|
||||||
Additionally, it requires a Cypress FX2 firmware. This can be extracted
|
|
||||||
from the vendor's Windows software using another tool. Details:
|
|
||||||
|
|
||||||
http://sigrok.org/wiki/LeCroy_LogicStudio#Firmware
|
|
||||||
|
|
||||||
- saleae-logic16: The Saleae Logic16 needs a firmware file for the
|
|
||||||
Cypress FX2 chip in the device, as well as two FPGA bitstream files.
|
|
||||||
These can be extracted from the vendor's Linux application using a tool
|
|
||||||
from our 'sigrok-util' repository/project.
|
|
||||||
|
|
||||||
- saleae-logic-pro: The Saleae Logic Pro 16 needs a firmware file for the
|
|
||||||
Cypress FX3 chip in the device, as well as an FPGA bitstream file.
|
|
||||||
These can be extracted from the vendor's Linux application using a tool
|
|
||||||
from our 'sigrok-util' repository/project.
|
|
||||||
|
|
||||||
- sysclk-lwla:
|
|
||||||
|
|
||||||
- The Sysclk LWLA1034 requires various bitstream files.
|
|
||||||
These files are available from our 'sigrok-firmware' repository/project
|
|
||||||
under a license which allows us to redistribute them.
|
|
||||||
|
|
||||||
- The Sysclk LWLA1016 requires various bitstream files.
|
|
||||||
These can be extracted from the vendor's Windows drivers using a tool
|
|
||||||
from our 'sigrok-util' repository/project.
|
|
||||||
|
|
||||||
- sysclk-sla5032: The Sysclk SLA5032 needs an FPGA bitstream file.
|
|
||||||
This file can be copied (and renamed) from the Windows vendor software
|
|
||||||
installation directory. Details:
|
|
||||||
|
|
||||||
https://sigrok.org/wiki/Sysclk_SLA5032#Firmware
|
|
||||||
|
|
||||||
The following drivers/devices do not need any firmware upload:
|
The following drivers/devices do not need any firmware upload:
|
||||||
|
|
||||||
- agilent-dmm
|
- agilent-dmm
|
||||||
- appa-55ii
|
- alsa
|
||||||
- arachnid-labs-re-load-pro
|
- brymen-dmm
|
||||||
- atten-pps3xxx
|
- chronovu-la8
|
||||||
- baylibre-acme
|
|
||||||
- beaglelogic
|
|
||||||
- cem-dt-885x
|
|
||||||
- center-3xx (including all subdrivers)
|
|
||||||
- chronovu-la
|
|
||||||
- colead-slm
|
- colead-slm
|
||||||
- conrad-digi-35-cpu
|
|
||||||
- demo
|
- demo
|
||||||
- fluke-45
|
|
||||||
- fluke-dmm
|
- fluke-dmm
|
||||||
- ftdi-la
|
|
||||||
- gmc-mh-1x-2x (including all subdrivers)
|
|
||||||
- gwinstek-gds-800
|
|
||||||
- gwinstek-gpd
|
|
||||||
- hameg-hmo
|
|
||||||
- hantek-4032l
|
|
||||||
- hp-3457a
|
|
||||||
- hp-3478a
|
|
||||||
- hung-chang-dso-2100
|
|
||||||
- ikalogic-scanalogic2
|
|
||||||
- ikalogic-scanaplus
|
|
||||||
- ipdbg-la
|
|
||||||
- kecheng-kc-330b
|
|
||||||
- kern-scale
|
|
||||||
- korad-kaxxxxp
|
|
||||||
- lascar-el-usb
|
- lascar-el-usb
|
||||||
- lecroy-xstream
|
- mic-985xx
|
||||||
- link-mso19
|
|
||||||
- manson-hcs-3xxx
|
|
||||||
- maynuo-m97
|
|
||||||
- mic-985xx (including all subdrivers)
|
|
||||||
- microchip-pickit2
|
|
||||||
- mooshimeter-dmm
|
|
||||||
- motech-lps-30x
|
|
||||||
- norma-dmm
|
|
||||||
- openbench-logic-sniffer
|
- openbench-logic-sniffer
|
||||||
- pce-322a
|
- rigol-ds1xx2
|
||||||
- pipistrello-ols
|
- serial-dmm
|
||||||
- rdtech-dps
|
|
||||||
- rigol-dg
|
|
||||||
- rigol-ds
|
|
||||||
- rohde-schwarz-sme-0x
|
|
||||||
- scpi-dmm
|
|
||||||
- scpi-pps
|
|
||||||
- serial-dmm (including all subdrivers)
|
|
||||||
- serial-lcr (including all subdrivers)
|
|
||||||
- siglent-sds
|
|
||||||
- teleinfo
|
|
||||||
- testo
|
|
||||||
- tondaj-sl-814
|
- tondaj-sl-814
|
||||||
- uni-t-dmm (including all subdrivers)
|
- uni-t-dmm
|
||||||
- uni-t-ut32x
|
- victor-dmm
|
||||||
- yokogawa-dlm
|
|
||||||
- zeroplus-logic-cube
|
- zeroplus-logic-cube
|
||||||
- zketech-ebd-usb
|
|
||||||
|
|
||||||
|
|
||||||
Specifying serial ports
|
Specifying serial ports
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Many devices supported by libsigrok use serial port based cables (real RS232
|
Many devices supported by libsigrok use serial port based cables (real RS232
|
||||||
or USB-to-serial ones, CDC class) to connect to a PC. These serial cables are
|
or USB-to-serial ones) to connect to a PC.
|
||||||
supported by the libserialport library. Some vendors prefer to use HID chips
|
|
||||||
instead of CDC chips in their serial cables. These cables can get supported
|
|
||||||
by means of the hidapi library. Note that each chip type requires specific
|
|
||||||
support in the libsigrok library. Bluetooth connected devices may be supported
|
|
||||||
as well when they communicate by means of RFCOMM channels, or one of the
|
|
||||||
implemented BLE notification/indication approaches, and one of the Bluetooth
|
|
||||||
supporting platforms is used.
|
|
||||||
|
|
||||||
For all these devices, you need to specify the serial port they are connected
|
For all these devices, you need to specify the serial port they are connected
|
||||||
to (e.g. using the 'conn' option in sigrok-cli). It is not possible to scan
|
to (e.g. using the 'conn' option in sigrok-cli). It is not possible to scan
|
||||||
|
|
@ -173,56 +75,31 @@ for such devices without specifying a serial port.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
$ sigrok-cli --driver <somedriver>:conn=/dev/ttyUSB0 ...
|
$ sigrok-cli --driver <somedriver>:conn=/dev/ttyUSB0 ...
|
||||||
$ sigrok-cli --driver <somedriver>:conn=hid/cp2110 ...
|
|
||||||
$ sigrok-cli --driver <somedriver>:conn=bt/rfcomm/01-23-45-67-89-ab ...
|
|
||||||
|
|
||||||
Formal syntax for serial communication:
|
The following drivers/devices require a serial port specification:
|
||||||
|
|
||||||
- COM ports (RS232, USB CDC):
|
- agilent-dmm
|
||||||
conn=<com-port>
|
- brymen-dmm
|
||||||
- USB HID cables:
|
- colead-slm
|
||||||
conn=hid[/<chip>]
|
- fluke-dmm
|
||||||
conn=hid[/<chip>]/usb=<bus>.<dev>[.<if>]
|
- mic-985xx
|
||||||
conn=hid[/<chip>]/raw=<path>
|
- openbench-logic-sniffer
|
||||||
conn=hid[/<chip>]/sn=<serno>
|
- serial-dmm
|
||||||
conn=hid[/<chip>]/iokit=<path>
|
- tondaj-sl-814
|
||||||
chip can be: bu86x, ch9325, cp2110, victor
|
|
||||||
path may contain slashes
|
|
||||||
path and serno are "greedy" (span to the end of the spec)
|
|
||||||
- Bluetooth Classic and Bluetooth Low Energy (BLE):
|
|
||||||
conn=bt/<conn>/<addr>
|
|
||||||
conn can be: rfcomm, ble122, nrf51, cc254x
|
|
||||||
addr can be "dense" or separated, bt/cc254x/0123456789ab or
|
|
||||||
bt/rfcomm/11-22-33-44-55-66 or bt/ble122/88:6b:12:34:56:78
|
|
||||||
(note that colons may not be available when the conn= spec is taken
|
|
||||||
from a string that separates fields by colon, e.g. in the "--driver
|
|
||||||
<name>:conn=<spec>" example, that is why the dense form and the use
|
|
||||||
of dashes for separation are supported)
|
|
||||||
|
|
||||||
Some of the drivers implement a default for the connection. Some of the
|
The following drivers/devices do not require a serial port specification:
|
||||||
drivers can auto-detect USB connected devices.
|
|
||||||
|
|
||||||
Beyond strict serial communication over COM ports (discussed above), the
|
- alsa
|
||||||
conn= property can also address specific USB devices, as well as specify TCP
|
- asix-sigma
|
||||||
or VXI communication parameters. See these examples:
|
- chronovu-la8
|
||||||
|
- demo
|
||||||
$ sigrok-cli --driver <somedriver>:conn=<vid>.<pid> ...
|
- fx2lafw
|
||||||
$ sigrok-cli --driver <somedriver>:conn=tcp-raw/<ipaddr>/<port> ...
|
- hantek-dso
|
||||||
$ sigrok-cli --driver <somedriver>:conn=vxi/<ipaddr> ...
|
- lascar-el-usb
|
||||||
$ sigrok-cli --driver <somedriver>:conn=usbtmc/<bus>.<addr> ...
|
- rigol-ds1xx2
|
||||||
|
- uni-t-dmm
|
||||||
|
- victor-dmm
|
||||||
Specifying serial port parameters
|
- zeroplus-logic-cube
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Every serial device's driver has default serial port parameters like baud
|
|
||||||
rate, number of data bits, stop bits and handshake status. If a device requires
|
|
||||||
different parameters, pass them as option "serialcomm" with the driver name.
|
|
||||||
See libsigrok docs for the function serial_set_paramstr() for complete specs.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
$ sigrok-cli --driver <somedriver>:conn=<someconn>:serialcomm=9600/7n1/dtr=1
|
|
||||||
|
|
||||||
|
|
||||||
Permissions of serial port based devices
|
Permissions of serial port based devices
|
||||||
|
|
@ -240,53 +117,33 @@ For USB-to-serial based devices, we recommended using our udev rules file
|
||||||
(see below for details).
|
(see below for details).
|
||||||
|
|
||||||
|
|
||||||
Permissions for USB devices (udev rules files)
|
Permissions for USB devices (udev rules file)
|
||||||
----------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
When using USB-based devices supported by libsigrok, the user running the
|
When using USB-based devices supported by libsigrok, the user running the
|
||||||
libsigrok frontend (e.g. sigrok-cli) has to have (read/write) permissions
|
libsigrok frontend (e.g. sigrok-cli) has to have (read/write) permissions
|
||||||
for the respective USB device.
|
for the respective USB device.
|
||||||
|
|
||||||
On Linux, this is accomplished using udev rules. libsigrok ships a rules
|
On Linux, this is accomplished using either 'chmod' (not recommended) or
|
||||||
file containing all supported devices which can be detected reliably
|
using the udev rules file shipped with libsigrok (recommended).
|
||||||
(generic USB-to-serial converters are omitted, as these are used for a wide
|
|
||||||
range of devices, e.g. GPS receivers, which are not handled by libsigrok).
|
|
||||||
|
|
||||||
The file is available in contrib/60-libsigrok.rules. This file just contains
|
The file is available in contrib/z60_libsigrok.rules. It contains entries
|
||||||
the list of devices and flags these devices with ID_SIGROK="1". Access is
|
for all libsigrok-supported (USB-based) devices and changes their group
|
||||||
granted by the 61-libsigrok-plugdev.rules or 61-libsigrok-uaccess.rules files,
|
to 'plugdev' and the permissions to '664'.
|
||||||
allowing access to members of the plugdev group or to currently logged in
|
|
||||||
users, respectively.
|
|
||||||
|
|
||||||
When using a libsigrok package from your favorite Linux distribution, the
|
When using a libsigrok package from your favorite Linux distribution, the
|
||||||
files should already be installed in /usr/lib/udev/rules.d/, i.e.
|
packager will have already taken care of properly installing the udev file
|
||||||
60-libsigrok.rules and one of the access granting rules files. Use of
|
in the correct (distro-specific) place, and you don't have to do anything.
|
||||||
61-libsigrok-uaccess.rules is encouraged on systemd distributions.
|
The packager might also have adapted 'plugdev' and '664' as needed.
|
||||||
|
|
||||||
The access policy can be locally overridden by placing appropriate rules in
|
|
||||||
/etc/udev/rules.d/, disabling or ammending the default policy. See the
|
|
||||||
udev documentation, e.g. man 7 udev, for details.
|
|
||||||
|
|
||||||
If you're building from source, you need to copy the file to the place
|
If you're building from source, you need to copy the file to the place
|
||||||
where udev will read these rules. Local rules should go to /etc/udev/rules.d.
|
where your distro expects such files. This is beyond the scope of this README,
|
||||||
Keep the file naming, otherwise interaction between the libsigrok rules and
|
but generally the location could be e.g. /etc/udev/rules.d, or maybe
|
||||||
rules shipped by the system will be broken.
|
/lib/udev/rules.d, or something else. Afterwards you might have to restart
|
||||||
|
udev, e.g. via '/etc/init.d/udev restart' or similar, and you'll have to
|
||||||
|
re-attach your device via USB.
|
||||||
|
|
||||||
Please consult the udev docs for details.
|
Please consult the udev docs of your distro 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
|
||||||
|
|
@ -308,11 +165,9 @@ UNI-T DMM (and rebranded models) cables
|
||||||
UNI-T multimeters (and rebranded devices, e.g. some Voltcraft models) can
|
UNI-T multimeters (and rebranded devices, e.g. some Voltcraft models) can
|
||||||
ship with different PC connectivity cables:
|
ship with different PC connectivity cables:
|
||||||
|
|
||||||
- UT-D02 (RS232 cable)
|
|
||||||
- UT-D04 (USB/HID cable with Hoitek HE2325U chip, USB VID/PID 04fa:2490)
|
- UT-D04 (USB/HID cable with Hoitek HE2325U chip, USB VID/PID 04fa:2490)
|
||||||
- UT-D04 (USB/HID cable with WCH CH9325 chip, USB VID/PID 1a86:e008)
|
- UT-D04 (USB/HID cable with WCH CH9325 chip, USB VID/PID 1a86:e008)
|
||||||
- UT-D07 (Bluetooth adapter, ISSC BL79 BLETR chip)
|
- UT-D02 (RS232 cable)
|
||||||
- UT-D09 (USB/HID cable with SiL CP2110 chip, USB VID/PID 10c4:ea80)
|
|
||||||
|
|
||||||
The above cables are all physically compatible (same IR connector shape)
|
The above cables are all physically compatible (same IR connector shape)
|
||||||
with all/most currently known UNI-T multimeters. For example, you can
|
with all/most currently known UNI-T multimeters. For example, you can
|
||||||
|
|
@ -339,10 +194,6 @@ When using any of the UT-D04 USB/HID cables you have to use the respective
|
||||||
driver _without_ the '-ser' drivername suffix (internally all of these models
|
driver _without_ the '-ser' drivername suffix (internally all of these models
|
||||||
are handled by the 'uni-t-dmm' driver).
|
are handled by the 'uni-t-dmm' driver).
|
||||||
|
|
||||||
You also need to specify the USB vendor/device IDs of the cable.
|
|
||||||
Autodetection is not possible here, since various other products use the
|
|
||||||
USB VID/PID of those cables too, and there is no way to distinguish them.
|
|
||||||
|
|
||||||
Since the UT-D04 cables are USB based (but don't use a USB-to-serial chip)
|
Since the UT-D04 cables are USB based (but don't use a USB-to-serial chip)
|
||||||
there is no need to specify a serial port via 'conn', of course.
|
there is no need to specify a serial port via 'conn', of course.
|
||||||
However, the user running the frontend does also need to have permissions
|
However, the user running the frontend does also need to have permissions
|
||||||
|
|
@ -350,8 +201,8 @@ to access the respective USB device (see above).
|
||||||
|
|
||||||
Examples (sigrok-cli):
|
Examples (sigrok-cli):
|
||||||
|
|
||||||
$ sigrok-cli --driver uni-t-ut61e:conn=1a86.e008 ...
|
$ sigrok-cli --driver uni-t-ut61e ...
|
||||||
$ sigrok-cli --driver voltcraft-vc820:conn=04fa.2490 ...
|
$ sigrok-cli --driver voltcraft-vc820 ...
|
||||||
|
|
||||||
|
|
||||||
UNI-T UT-D04 cable issue on Linux
|
UNI-T UT-D04 cable issue on Linux
|
||||||
|
|
@ -385,75 +236,54 @@ unless a certain action has been performed by the user beforehand. This is
|
||||||
usually mentioned in the vendor manual of the respective device, but here's
|
usually mentioned in the vendor manual of the respective device, but here's
|
||||||
a short list for convenience:
|
a short list for convenience:
|
||||||
|
|
||||||
- BBC Goertz Metrawatt M2110: Briefly press the "Start/Reset" button on the
|
|
||||||
interface panel on top.
|
|
||||||
- Brymen BM257s: Press HOLD during power-on.
|
|
||||||
- Digitek DT4000ZC: Briefly press the "RS232" button.
|
- Digitek DT4000ZC: Briefly press the "RS232" button.
|
||||||
- EEVBlog 121GW: Hold "1ms PEAK" until the "BT" indicator is shown.
|
|
||||||
- ES51919 based LCR meters (DER EE DE-5000, PeakTech 2170, UNI-T UT612):
|
|
||||||
Press the button with the "RS232" or "USB" or "PC link" label (usually
|
|
||||||
the "up" cursor button).
|
|
||||||
- Gossen Metrawatt Metrahit 1x/2x devices, driver gmc-mh-1x-2x-rs232:
|
|
||||||
- Power on the device with the "DATA" button pressed.
|
|
||||||
- Metrahit 2x devices must be configured for the respective interface type.
|
|
||||||
- Gossen Metrawatt Metrahit 2x devices, driver gmc-mh-2x-bd232:
|
|
||||||
- 'BD232' interface:
|
|
||||||
The multimeter must be configured for the respective interface type.
|
|
||||||
- 'SI232-II' interface ("PC Mode"):
|
|
||||||
The multimeter must be configured for interface type 'BD232' (all),
|
|
||||||
'SI232 online' (28-29S) or 'SI232 store' (22-26x). The interface must
|
|
||||||
be configured to the same baud rate as the host (default 9600).
|
|
||||||
Multimeter and interface must be configured to the same address.
|
|
||||||
- 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.
|
|
||||||
- Meterman 38XR: Press the "RS232" button.
|
|
||||||
- Metrix MX56C: Press the PRINT button to have the meter send acquisition
|
|
||||||
data via IR. Hold the PRINT button to adjust the meter's transmission
|
|
||||||
interval.
|
|
||||||
- Norma DM950: If the interface doesn't work (e.g. USB-RS232 converter), power
|
|
||||||
on the device with "FUNC" pressed (to power the interface from the DMM).
|
|
||||||
- PCE PCE-DM32: Briefly press the "RS232" button.
|
- PCE PCE-DM32: Briefly press the "RS232" button.
|
||||||
- RadioShack 22-812: Press and hold "SELECT" and "RANGE" together.
|
- RadioShack 22-812: Press and hold "SELECT" and "RANGE" together.
|
||||||
- TekPower TP4000ZC: Briefly press the "RS232" button.
|
- TekPower TP4000ZC: Briefly press the "RS232" button.
|
||||||
- Tenma 72-7750: Briefly press the "RS232C" button.
|
- UNI-T UT61D: Press the "REL/RS232/USB" button for roughly 1 second.
|
||||||
- UNI-T UT60G: Briefly press the "RS232C" button.
|
- V&A VA18B: Keep the "Hz/DUTY" key pressed while powering on the device.
|
||||||
- UNI-T UT61B/C/D: Press the "REL/RS232/USB" button for roughly 1 second.
|
- Victor 70C: Press the "REL/RS232" button for roughly 1 second.
|
||||||
- UNI-T UT71x: Press the "SEND/-/MAXMIN" button for roughly 1 second.
|
- Victor 86C: Press the "REL/RS232" button for roughly 1 second.
|
||||||
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
|
|
||||||
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.
|
|
||||||
- V&A VA18B/VA40B: Keep the "Hz/DUTY" key pressed while powering on the DMM.
|
|
||||||
- Victor 70C/86C: Press the "REL/RS232" button for roughly 1 second.
|
|
||||||
- Voltcraft VC-830: Press the "REL/PC" button for roughly 2 seconds.
|
|
||||||
- Voltcraft VC-870: Press the "REL/PC" button for roughly 1 second.
|
|
||||||
|
|
||||||
|
|
||||||
ChronoVu LA8/LA16 USB VID/PIDs
|
ALSA driver
|
||||||
------------------------------
|
-----------
|
||||||
|
|
||||||
The ChronoVu LA8/LA16 logic analyzer is available in two revisions. Previously,
|
The 'alsa' driver can be used to sample analog data using a PC's soundcard.
|
||||||
the device shipped with a USB VID/PID of 0403:6001, which is the standard ID
|
I.e. the sound card can act as a simple oscilloscope (with some limitations)
|
||||||
|
using commercial or DIY "sound card scope probe" cables.
|
||||||
|
|
||||||
|
Since ALSA is a Linux-specific sound system, this driver will inherently
|
||||||
|
only compile and work on Linux.
|
||||||
|
|
||||||
|
We might write additional drivers to make a similar functionality available
|
||||||
|
on other OSes at some point.
|
||||||
|
|
||||||
|
|
||||||
|
ChronoVu LA8 USB VID/PIDs
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The ChronoVu LA8 logic analyzer is available in two revisions. Previously,
|
||||||
|
the LA8 shipped with a USB VID/PID of 0403:6001, which is the standard ID
|
||||||
for FTDI FT232 USB chips.
|
for FTDI FT232 USB chips.
|
||||||
|
|
||||||
Since this made it hard to distinguish the LA8/LA16 from any other device
|
Since this made it hard to distinguish the LA8 from any other device
|
||||||
with this FTDI chip connected to the PC, the vendor later shipped the
|
with this FTDI chip connected to the PC, the vendor later shipped the
|
||||||
device with a USB VID/PID of 0403:8867.
|
LA8 with a USB VID/PID of 0403:8867.
|
||||||
|
|
||||||
The 'chronovu-la' driver in libsigrok supports both VID/PID pairs and
|
The 'chronovu-la8' driver in libsigrok supports both VID/PID pairs and
|
||||||
automatically finds devices with either VID/PID pair.
|
automatically finds devices with either VID/PID pair. However, currently
|
||||||
|
the driver will assume any device with VID/PID 0403:6001 is a ChronoVu LA8.
|
||||||
|
|
||||||
|
|
||||||
OLS
|
OLS
|
||||||
---
|
---
|
||||||
|
|
||||||
The Dangerous Prototypes Openbench Logic Sniffer (OLS) logic analyzer
|
The Dangerous Prototypes Openbench Logic Sniffer (OLS) logic analyzer is
|
||||||
driver in libsigrok assumes a somewhat recent firmware has been flashed onto
|
supported by the 'ols' driver in libsigrok. This driver assumes a somewhat
|
||||||
the OLS (it doesn't need a firmware upload every time it's attached via USB,
|
recent firmware has been flashed onto the OLS (it doesn't need a firmware
|
||||||
since the firmware is stored in the device permanently).
|
upload every time it's attached via USB, since the firmware is stored in the
|
||||||
|
device permanently).
|
||||||
|
|
||||||
The most recent firmware version that is tested is 3.07.
|
The most recent firmware version that is tested is 3.07.
|
||||||
|
|
||||||
|
|
@ -472,83 +302,20 @@ Example:
|
||||||
$ sigrok-cli --driver ols:conn=/dev/ttyACM0 ...
|
$ sigrok-cli --driver ols:conn=/dev/ttyACM0 ...
|
||||||
|
|
||||||
|
|
||||||
JTAGulator
|
Rigol DS1xx2 oscilloscopes
|
||||||
----------
|
--------------------------
|
||||||
|
|
||||||
The Grand Idea Studio JTAGulator also implements the SUMP protocol and
|
The 'rigol-ds1xx2' driver (for the Rigol DS1052E and some other, similar DSOs)
|
||||||
thus is covered by the OLS driver. See the vendor's wiki on details how
|
currently uses the Linux usbtmc kernel driver. This means it can currently
|
||||||
to enable the Logic Analyzer mode of operation.
|
only be built and used on Linux (i.e., it's non-portable).
|
||||||
|
|
||||||
https://github.com/grandideastudio/jtagulator/wiki/Logic-Analyzer
|
The use of a kernel module also means it is dependent on the kernel version
|
||||||
|
used, as well as on whether this specific module is available in the kernel.
|
||||||
|
Additionally, the usbtmc kernel module has been known to have various bugs
|
||||||
|
in some versions. These are some (but not all) drawbacks of using a kernel
|
||||||
|
module as opposed to a libusb-based driver that works in user-space.
|
||||||
|
|
||||||
|
We plan to change the driver to use the 'librevisa' user-space shared
|
||||||
|
library (which uses libusb) soon, which will fix all these issues and make
|
||||||
|
the driver portable at the same time.
|
||||||
|
|
||||||
Mooshimeter
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The Mooshim Engineering Mooshimeter is controlled via Bluetooth Low Energy
|
|
||||||
(sometimes called Bluetooth 4.0), as such it requires a supported Bluetooth
|
|
||||||
interface available. The 'conn' option is required and must contain the
|
|
||||||
Bluetooth MAC address of the meter.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
$ sigrok-cli --driver mooshimeter-dmm:conn=12-34-56-78-9A-BC ...
|
|
||||||
|
|
||||||
Since the Mooshimeter has no physical interface on the meter itself, the
|
|
||||||
channel configuration is set with the 'channel_config' option. The format
|
|
||||||
of this option is 'CH1,CH2' where each channel configuration has the form
|
|
||||||
'MODE:RANGE:ANALYSIS', with later parts being optional. In addition for
|
|
||||||
CLI compatibility, the ',' in the channels can also be a '/' and the ':' in
|
|
||||||
the individual configuration can be a ';'.
|
|
||||||
|
|
||||||
Available channel 1 modes:
|
|
||||||
|
|
||||||
- Current, A: Current in amps
|
|
||||||
- Temperature, T, K: Internal meter temperature in Kelvin
|
|
||||||
- Resistance, Ohm, W: Resistance in ohms
|
|
||||||
- Diode, D: Diode voltage
|
|
||||||
- Aux, LV: Auxiliary (W input) low voltage sensor (1.2V max)
|
|
||||||
|
|
||||||
Available channel 2 modes:
|
|
||||||
|
|
||||||
- Voltage, V: Voltage
|
|
||||||
- Temperature, T, K: Internal meter temperature in Kelvin
|
|
||||||
- Resistance, Ohm, W: Resistance in ohms
|
|
||||||
- Diode, D: Diode voltage
|
|
||||||
- Aux, LV: Auxiliary (W input) low voltage sensor (1.2V max)
|
|
||||||
|
|
||||||
Only one channel can use the shared inputs at a time (e.g. if CH1 is measuring
|
|
||||||
resistance, CH2 cannot measure low voltage). Temperature is excepted from
|
|
||||||
this, so the meter can measure internal temperature and low voltage at the
|
|
||||||
same time.
|
|
||||||
|
|
||||||
Additionally, the meter can calculate the real power of both channels. This
|
|
||||||
generally only makes sense when CH1 is set to current and CH2 is set to a
|
|
||||||
voltage and so it is disabled by default. It must be enabled by enabling the
|
|
||||||
'P' channel (the third channel).
|
|
||||||
|
|
||||||
The range of the channel specification sets the maximum input for that channel
|
|
||||||
and is rounded up to the next value the meter itself supports. For example,
|
|
||||||
specifying 50 for the voltage will result in the actual maximum of 60.
|
|
||||||
Specifying 61 would result in 600. If omitted, sigrok will perform
|
|
||||||
auto-ranging of the channel by selecting the next greater value than the
|
|
||||||
latest maximum.
|
|
||||||
|
|
||||||
The analysis option sets how the meter reports its internal sampling buffer
|
|
||||||
to sigrok:
|
|
||||||
|
|
||||||
- Mean, DC: The default is a simple arithmetic mean of the sample buffer
|
|
||||||
- RMS, AC: The root mean square of the sample buffer
|
|
||||||
- Buf, Buffer, Samples: Report the entire sample buffer to sigrok. This
|
|
||||||
results in packets that contain all the samples in the buffer instead
|
|
||||||
of a single output value.
|
|
||||||
|
|
||||||
The size of the sample buffer is set with the 'avg_samples' option, while
|
|
||||||
the sampling rate is set with the 'samplerate' option. So the update rate
|
|
||||||
is avg_samples/samplerate. Both are rounded up to the next supported value
|
|
||||||
by the meter.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
$ sigrok-cli -c channel_config="Aux;0.1/T" --driver mooshimeter-dmm...
|
|
||||||
$ sigrok-cli -c channel_config="A;;AC/V;;AC" --driver mooshimeter-dmm...
|
|
||||||
|
|
|
||||||
44
autogen.sh
44
autogen.sh
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh
|
||||||
##
|
##
|
||||||
## This file is part of the libsigrok project.
|
## This file is part of the libsigrok project.
|
||||||
##
|
##
|
||||||
|
|
@ -18,7 +18,43 @@
|
||||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
##
|
##
|
||||||
|
|
||||||
test -n "$srcdir" || srcdir=`dirname "$0"`
|
OS=`uname`
|
||||||
test -n "$srcdir" || srcdir=.
|
|
||||||
|
LIBTOOLIZE=libtoolize
|
||||||
|
ACLOCAL_DIR=
|
||||||
|
|
||||||
|
if [ "x$OS" = "xDarwin" ]; then
|
||||||
|
LIBTOOLIZE=glibtoolize
|
||||||
|
|
||||||
|
if [ -d /sw/share/aclocal ]; then
|
||||||
|
# fink installs aclocal macros here
|
||||||
|
ACLOCAL_DIR="-I /sw/share/aclocal"
|
||||||
|
elif [ -d /opt/local/share/aclocal ]; then
|
||||||
|
# Macports installs aclocal macros here
|
||||||
|
ACLOCAL_DIR="-I /opt/local/share/aclocal"
|
||||||
|
elif [ -d /usr/local/share/aclocal ]; then
|
||||||
|
# Homebrew installs aclocal macros here
|
||||||
|
ACLOCAL_DIR="-I /usr/local/share/aclocal"
|
||||||
|
elif [ -d /usr/share/aclocal ]; then
|
||||||
|
# Xcode installs aclocal macros here
|
||||||
|
ACLOCAL_DIR="-I /usr/share/aclocal"
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [ "x$OS" = "xMINGW32_NT-5.1" ]; then
|
||||||
|
# Windows XP
|
||||||
|
ACLOCAL_DIR="-I /usr/local/share/aclocal"
|
||||||
|
elif [ "x$OS" = "xMINGW32_NT-6.0" ]; then
|
||||||
|
# Windows Vista
|
||||||
|
ACLOCAL_DIR="-I /usr/local/share/aclocal"
|
||||||
|
elif [ "x$OS" = "xMINGW32_NT-6.1" ]; then
|
||||||
|
# Windows 7
|
||||||
|
ACLOCAL_DIR="-I /usr/local/share/aclocal"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Generating build system..."
|
||||||
|
${LIBTOOLIZE} --install --copy --quiet || exit 1
|
||||||
|
aclocal ${ACLOCAL_DIR} || exit 1
|
||||||
|
autoheader || exit 1
|
||||||
|
automake --add-missing --copy || exit 1
|
||||||
|
autoconf || exit 1
|
||||||
|
|
||||||
autoreconf --force --install --verbose "$srcdir"
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,404 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
* Copyright (C) 2012 Peter Stuge <peter@stuge.se>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mainpage libsigrok API
|
||||||
|
*
|
||||||
|
* @section sec_intro Introduction
|
||||||
|
*
|
||||||
|
* The <a href="http://sigrok.org">sigrok</a> project aims at creating a
|
||||||
|
* portable, cross-platform, Free/Libre/Open-Source signal analysis software
|
||||||
|
* suite that supports various device types (such as logic analyzers,
|
||||||
|
* oscilloscopes, multimeters, and more).
|
||||||
|
*
|
||||||
|
* <a href="http://sigrok.org/wiki/Libsigrok">libsigrok</a> is a shared
|
||||||
|
* library written in C which provides the basic API for talking to
|
||||||
|
* <a href="http://sigrok.org/wiki/Supported_hardware">supported hardware</a>
|
||||||
|
* and reading/writing the acquired data into various
|
||||||
|
* <a href="http://sigrok.org/wiki/Input_output_formats">input/output
|
||||||
|
* file formats</a>.
|
||||||
|
*
|
||||||
|
* @section sec_api API reference
|
||||||
|
*
|
||||||
|
* See the "Modules" page for an introduction to various libsigrok
|
||||||
|
* related topics and the detailed API documentation of the respective
|
||||||
|
* functions.
|
||||||
|
*
|
||||||
|
* You can also browse the API documentation by file, or review all
|
||||||
|
* data structures.
|
||||||
|
*
|
||||||
|
* @section sec_mailinglists Mailing lists
|
||||||
|
*
|
||||||
|
* There are two mailing lists for sigrok/libsigrok: <a href="https://lists.sourceforge.net/lists/listinfo/sigrok-devel">sigrok-devel</a> and <a href="https://lists.sourceforge.net/lists/listinfo/sigrok-commits">sigrok-commits</a>.
|
||||||
|
*
|
||||||
|
* @section sec_irc IRC
|
||||||
|
*
|
||||||
|
* You can find the sigrok developers in the
|
||||||
|
* <a href="irc://chat.freenode.net/sigrok">\#sigrok</a>
|
||||||
|
* IRC channel on Freenode.
|
||||||
|
*
|
||||||
|
* @section sec_website Website
|
||||||
|
*
|
||||||
|
* <a href="http://sigrok.org/wiki/Libsigrok">sigrok.org/wiki/Libsigrok</a>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* Initializing and shutting down libsigrok.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup grp_init Initialization
|
||||||
|
*
|
||||||
|
* Initializing and shutting down libsigrok.
|
||||||
|
*
|
||||||
|
* Before using any of the libsigrok functionality, sr_init() must
|
||||||
|
* be called to initialize the library, which will return a struct sr_context
|
||||||
|
* when the initialization was successful.
|
||||||
|
*
|
||||||
|
* When libsigrok functionality is no longer needed, sr_exit() should be
|
||||||
|
* called, which will (among other things) free the struct sr_context.
|
||||||
|
*
|
||||||
|
* Example for a minimal program using libsigrok:
|
||||||
|
*
|
||||||
|
* @code{.c}
|
||||||
|
* #include <stdio.h>
|
||||||
|
* #include <libsigrok/libsigrok.h>
|
||||||
|
*
|
||||||
|
* int main(int argc, char **argv)
|
||||||
|
* {
|
||||||
|
* int ret;
|
||||||
|
* struct sr_context *sr_ctx;
|
||||||
|
*
|
||||||
|
* if ((ret = sr_init(&sr_ctx)) != SR_OK) {
|
||||||
|
* printf("Error initializing libsigrok (%s): %s.",
|
||||||
|
* sr_strerror_name(ret), sr_strerror(ret));
|
||||||
|
* return 1;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Use libsigrok functions here...
|
||||||
|
*
|
||||||
|
* if ((ret = sr_exit(sr_ctx)) != SR_OK) {
|
||||||
|
* printf("Error shutting down libsigrok (%s): %s.",
|
||||||
|
* sr_strerror_name(ret), sr_strerror(ret));
|
||||||
|
* return 1;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanity-check all libsigrok drivers.
|
||||||
|
*
|
||||||
|
* @return SR_OK if all drivers are OK, SR_ERR if one or more have issues.
|
||||||
|
*/
|
||||||
|
static int sanity_check_all_drivers(void)
|
||||||
|
{
|
||||||
|
int i, errors, ret = SR_OK;
|
||||||
|
struct sr_dev_driver **drivers;
|
||||||
|
const char *d;
|
||||||
|
|
||||||
|
sr_spew("Sanity-checking all drivers.");
|
||||||
|
|
||||||
|
drivers = sr_driver_list();
|
||||||
|
for (i = 0; drivers[i]; i++) {
|
||||||
|
errors = 0;
|
||||||
|
|
||||||
|
d = (drivers[i]->name) ? drivers[i]->name : "NULL";
|
||||||
|
|
||||||
|
if (!drivers[i]->name) {
|
||||||
|
sr_err("No name in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->longname) {
|
||||||
|
sr_err("No longname in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (drivers[i]->api_version < 1) {
|
||||||
|
sr_err("API version in driver %d ('%s') < 1.", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->init) {
|
||||||
|
sr_err("No init in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->cleanup) {
|
||||||
|
sr_err("No cleanup in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->scan) {
|
||||||
|
sr_err("No scan in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->dev_list) {
|
||||||
|
sr_err("No dev_list in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->dev_clear) {
|
||||||
|
sr_err("No dev_clear in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
/* Note: config_get() is optional. */
|
||||||
|
if (!drivers[i]->config_set) {
|
||||||
|
sr_err("No config_set in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->config_list) {
|
||||||
|
sr_err("No config_list in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->dev_open) {
|
||||||
|
sr_err("No dev_open in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->dev_close) {
|
||||||
|
sr_err("No dev_close in driver %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->dev_acquisition_start) {
|
||||||
|
sr_err("No dev_acquisition_start in driver %d ('%s').",
|
||||||
|
i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!drivers[i]->dev_acquisition_stop) {
|
||||||
|
sr_err("No dev_acquisition_stop in driver %d ('%s').",
|
||||||
|
i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: 'priv' is allowed to be NULL. */
|
||||||
|
|
||||||
|
if (errors == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanity-check all libsigrok input modules.
|
||||||
|
*
|
||||||
|
* @return SR_OK if all modules are OK, SR_ERR if one or more have issues.
|
||||||
|
*/
|
||||||
|
static int sanity_check_all_input_modules(void)
|
||||||
|
{
|
||||||
|
int i, errors, ret = SR_OK;
|
||||||
|
struct sr_input_format **inputs;
|
||||||
|
const char *d;
|
||||||
|
|
||||||
|
sr_spew("Sanity-checking all input modules.");
|
||||||
|
|
||||||
|
inputs = sr_input_list();
|
||||||
|
for (i = 0; inputs[i]; i++) {
|
||||||
|
errors = 0;
|
||||||
|
|
||||||
|
d = (inputs[i]->id) ? inputs[i]->id : "NULL";
|
||||||
|
|
||||||
|
if (!inputs[i]->id) {
|
||||||
|
sr_err("No ID in module %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!inputs[i]->description) {
|
||||||
|
sr_err("No description in module %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!inputs[i]->format_match) {
|
||||||
|
sr_err("No format_match in module %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!inputs[i]->init) {
|
||||||
|
sr_err("No init in module %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!inputs[i]->loadfile) {
|
||||||
|
sr_err("No loadfile in module %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanity-check all libsigrok output modules.
|
||||||
|
*
|
||||||
|
* @return SR_OK if all modules are OK, SR_ERR if one or more have issues.
|
||||||
|
*/
|
||||||
|
static int sanity_check_all_output_modules(void)
|
||||||
|
{
|
||||||
|
int i, errors, ret = SR_OK;
|
||||||
|
struct sr_output_format **outputs;
|
||||||
|
const char *d;
|
||||||
|
|
||||||
|
sr_spew("Sanity-checking all output modules.");
|
||||||
|
|
||||||
|
outputs = sr_output_list();
|
||||||
|
for (i = 0; outputs[i]; i++) {
|
||||||
|
errors = 0;
|
||||||
|
|
||||||
|
d = (outputs[i]->id) ? outputs[i]->id : "NULL";
|
||||||
|
|
||||||
|
if (!outputs[i]->id) {
|
||||||
|
sr_err("No ID in module %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (!outputs[i]->description) {
|
||||||
|
sr_err("No description in module %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
if (outputs[i]->df_type < 10000 || outputs[i]->df_type > 10007) {
|
||||||
|
sr_err("Invalid df_type %d in module %d ('%s').",
|
||||||
|
outputs[i]->df_type, i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All modules must provide a data or recv API callback. */
|
||||||
|
if (!outputs[i]->data && !outputs[i]->receive) {
|
||||||
|
sr_err("No data/receive in module %d ('%s').", i, d);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Currently most API calls are optional (their function
|
||||||
|
* pointers can thus be NULL) in theory: init, event, cleanup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (errors == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize libsigrok.
|
||||||
|
*
|
||||||
|
* This function must be called before any other libsigrok function.
|
||||||
|
*
|
||||||
|
* @param ctx Pointer to a libsigrok context struct pointer. Must not be NULL.
|
||||||
|
* This will be a pointer to a newly allocated libsigrok context
|
||||||
|
* object upon success, and is undefined upon errors.
|
||||||
|
*
|
||||||
|
* @return SR_OK upon success, a (negative) error code otherwise. Upon errors
|
||||||
|
* the 'ctx' pointer is undefined and should not be used. Upon success,
|
||||||
|
* the context will be free'd by sr_exit() as part of the libsigrok
|
||||||
|
* shutdown.
|
||||||
|
*
|
||||||
|
* @since 0.1.0 (but the API changed in 0.2.0)
|
||||||
|
*/
|
||||||
|
SR_API int sr_init(struct sr_context **ctx)
|
||||||
|
{
|
||||||
|
int ret = SR_ERR;
|
||||||
|
struct sr_context *context;
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
sr_err("%s(): libsigrok context was NULL.", __func__);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sanity_check_all_drivers() < 0) {
|
||||||
|
sr_err("Internal driver error(s), aborting.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sanity_check_all_input_modules() < 0) {
|
||||||
|
sr_err("Internal input module error(s), aborting.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sanity_check_all_output_modules() < 0) {
|
||||||
|
sr_err("Internal output module error(s), aborting.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* + 1 to handle when struct sr_context has no members. */
|
||||||
|
context = g_try_malloc0(sizeof(struct sr_context) + 1);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
ret = SR_ERR_MALLOC;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBUSB_1_0
|
||||||
|
ret = libusb_init(&context->libusb_ctx);
|
||||||
|
if (LIBUSB_SUCCESS != ret) {
|
||||||
|
sr_err("libusb_init() returned %s.\n", libusb_error_name(ret));
|
||||||
|
ret = SR_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*ctx = context;
|
||||||
|
context = NULL;
|
||||||
|
ret = SR_OK;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (context)
|
||||||
|
g_free(context);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown libsigrok.
|
||||||
|
*
|
||||||
|
* @param ctx Pointer to a libsigrok context struct. Must not be NULL.
|
||||||
|
*
|
||||||
|
* @return SR_OK upon success, a (negative) error code otherwise.
|
||||||
|
*
|
||||||
|
* @since 0.1.0 (but the API changed in 0.2.0)
|
||||||
|
*/
|
||||||
|
SR_API int sr_exit(struct sr_context *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx) {
|
||||||
|
sr_err("%s(): libsigrok context was NULL.", __func__);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_hw_cleanup_all();
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBUSB_1_0
|
||||||
|
libusb_exit(ctx->libusb_ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_free(ctx);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
const DataType *ConfigKey::data_type() const
|
|
||||||
{
|
|
||||||
const struct sr_key_info *info = sr_key_info_get(SR_KEY_CONFIG, id());
|
|
||||||
if (!info)
|
|
||||||
throw Error(SR_ERR_NA);
|
|
||||||
return DataType::get(info->datatype);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ConfigKey::identifier() const
|
|
||||||
{
|
|
||||||
const struct sr_key_info *info = sr_key_info_get(SR_KEY_CONFIG, id());
|
|
||||||
if (!info)
|
|
||||||
throw Error(SR_ERR_NA);
|
|
||||||
return valid_string(info->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ConfigKey::description() const
|
|
||||||
{
|
|
||||||
const struct sr_key_info *info = sr_key_info_get(SR_KEY_CONFIG, id());
|
|
||||||
if (!info)
|
|
||||||
throw Error(SR_ERR_NA);
|
|
||||||
return valid_string(info->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConfigKey *ConfigKey::get_by_identifier(std::string identifier)
|
|
||||||
{
|
|
||||||
const struct sr_key_info *info = sr_key_info_name_get(SR_KEY_CONFIG, identifier.c_str());
|
|
||||||
if (!info)
|
|
||||||
throw Error(SR_ERR_ARG);
|
|
||||||
return get(info->key);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef HAVE_STOI_STOD
|
|
||||||
|
|
||||||
/* Fallback implementation of stoi and stod */
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
static inline int stoi( const std::string& str )
|
|
||||||
{
|
|
||||||
char *endptr;
|
|
||||||
errno = 0;
|
|
||||||
const long ret = std::strtol(str.c_str(), &endptr, 10);
|
|
||||||
if (endptr == str.c_str())
|
|
||||||
throw std::invalid_argument("stoi");
|
|
||||||
else if (errno == ERANGE ||
|
|
||||||
ret < std::numeric_limits<int>::min() ||
|
|
||||||
ret > std::numeric_limits<int>::max())
|
|
||||||
throw std::out_of_range("stoi");
|
|
||||||
else
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline double stod( const std::string& str )
|
|
||||||
{
|
|
||||||
char *endptr;
|
|
||||||
errno = 0;
|
|
||||||
const double ret = std::strtod(str.c_str(), &endptr);
|
|
||||||
if (endptr == str.c_str())
|
|
||||||
throw std::invalid_argument("stod");
|
|
||||||
else if (errno == ERANGE)
|
|
||||||
throw std::out_of_range("stod");
|
|
||||||
else
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Glib::VariantBase ConfigKey::parse_string(std::string value, enum sr_datatype dt)
|
|
||||||
{
|
|
||||||
GVariant *variant;
|
|
||||||
uint64_t p, q;
|
|
||||||
|
|
||||||
switch (dt)
|
|
||||||
{
|
|
||||||
case SR_T_UINT64:
|
|
||||||
check(sr_parse_sizestring(value.c_str(), &p));
|
|
||||||
variant = g_variant_new_uint64(p);
|
|
||||||
break;
|
|
||||||
case SR_T_STRING:
|
|
||||||
variant = g_variant_new_string(value.c_str());
|
|
||||||
break;
|
|
||||||
case SR_T_BOOL:
|
|
||||||
variant = g_variant_new_boolean(sr_parse_boolstring(value.c_str()));
|
|
||||||
break;
|
|
||||||
case SR_T_FLOAT:
|
|
||||||
try {
|
|
||||||
variant = g_variant_new_double(stod(value));
|
|
||||||
} catch (invalid_argument&) {
|
|
||||||
throw Error(SR_ERR_ARG);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SR_T_RATIONAL_PERIOD:
|
|
||||||
check(sr_parse_period(value.c_str(), &p, &q));
|
|
||||||
variant = g_variant_new("(tt)", p, q);
|
|
||||||
break;
|
|
||||||
case SR_T_RATIONAL_VOLT:
|
|
||||||
check(sr_parse_voltage(value.c_str(), &p, &q));
|
|
||||||
variant = g_variant_new("(tt)", p, q);
|
|
||||||
break;
|
|
||||||
case SR_T_INT32:
|
|
||||||
try {
|
|
||||||
variant = g_variant_new_int32(stoi(value));
|
|
||||||
} catch (invalid_argument&) {
|
|
||||||
throw Error(SR_ERR_ARG);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw Error(SR_ERR_BUG);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Glib::VariantBase(variant, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Glib::VariantBase ConfigKey::parse_string(std::string value) const
|
|
||||||
{
|
|
||||||
enum sr_datatype dt = (enum sr_datatype)(data_type()->id());
|
|
||||||
return parse_string(value, dt);
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
/** Data type used for this configuration key. */
|
|
||||||
const DataType *data_type() const;
|
|
||||||
/** String identifier for this configuration key, suitable for CLI use. */
|
|
||||||
std::string identifier() const;
|
|
||||||
/** Description of this configuration key. */
|
|
||||||
std::string description() const;
|
|
||||||
/** Get configuration key by string identifier. */
|
|
||||||
static const ConfigKey *get_by_identifier(std::string identifier);
|
|
||||||
/** Parse a string argument into the appropriate type for this key. */
|
|
||||||
static Glib::VariantBase parse_string(std::string value, enum sr_datatype dt);
|
|
||||||
Glib::VariantBase parse_string(std::string value) const;
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
%attributestring(sigrok::ConfigKey, std::string, identifier, identifier);
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,21 +0,0 @@
|
||||||
std::vector<const QuantityFlag *>
|
|
||||||
QuantityFlag::flags_from_mask(unsigned int mask)
|
|
||||||
{
|
|
||||||
auto result = std::vector<const QuantityFlag *>();
|
|
||||||
while (mask)
|
|
||||||
{
|
|
||||||
unsigned int new_mask = mask & (mask - 1);
|
|
||||||
result.push_back(QuantityFlag::get(
|
|
||||||
static_cast<enum sr_mqflag>(mask ^ new_mask)));
|
|
||||||
mask = new_mask;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int QuantityFlag::mask_from_flags(std::vector<const QuantityFlag *> flags)
|
|
||||||
{
|
|
||||||
unsigned int result = 0;
|
|
||||||
for (auto flag : flags)
|
|
||||||
result |= flag->id();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
/** Get flags corresponding to a bitmask. */
|
|
||||||
static std::vector<const QuantityFlag *>
|
|
||||||
flags_from_mask(unsigned int mask);
|
|
||||||
|
|
||||||
/** Get bitmask corresponding to a set of flags. */
|
|
||||||
static unsigned int mask_from_flags(
|
|
||||||
std::vector<const QuantityFlag *> flags);
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,177 +0,0 @@
|
||||||
##
|
|
||||||
## This file is part of the libsigrok project.
|
|
||||||
##
|
|
||||||
## Copyright (C) 2014 Martin Ling <martin-sigrok@earth.li>
|
|
||||||
##
|
|
||||||
## This program is free software: you can redistribute it and/or modify
|
|
||||||
## it under the terms of the GNU General Public License as published by
|
|
||||||
## the Free Software Foundation, either version 3 of the License, or
|
|
||||||
## (at your option) any later version.
|
|
||||||
##
|
|
||||||
## This program is distributed in the hope that it will be useful,
|
|
||||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
## GNU General Public License for more details.
|
|
||||||
##
|
|
||||||
## You should have received a copy of the GNU General Public License
|
|
||||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
##
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
from xml.etree import ElementTree
|
|
||||||
from collections import OrderedDict
|
|
||||||
import sys, os, re
|
|
||||||
|
|
||||||
index_file = sys.argv[1]
|
|
||||||
|
|
||||||
# Get directory this script is in.
|
|
||||||
dirname = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
|
|
||||||
outdirname = "bindings"
|
|
||||||
if not os.path.exists(os.path.join(outdirname, 'cxx/include/libsigrokcxx')):
|
|
||||||
os.makedirs(os.path.join(outdirname, 'cxx/include/libsigrokcxx'))
|
|
||||||
if not os.path.exists(os.path.join(outdirname, 'swig')):
|
|
||||||
os.makedirs(os.path.join(outdirname, 'swig'))
|
|
||||||
|
|
||||||
mapping = dict([
|
|
||||||
('sr_loglevel', ('LogLevel', 'Log verbosity level')),
|
|
||||||
('sr_packettype', ('PacketType', 'Type of datafeed packet')),
|
|
||||||
('sr_mq', ('Quantity', 'Measured quantity')),
|
|
||||||
('sr_unit', ('Unit', 'Unit of measurement')),
|
|
||||||
('sr_mqflag', ('QuantityFlag', 'Flag applied to measured quantity')),
|
|
||||||
('sr_configkey', ('ConfigKey', 'Configuration key')),
|
|
||||||
('sr_configcap', ('Capability', 'Configuration capability')),
|
|
||||||
('sr_datatype', ('DataType', 'Configuration data type')),
|
|
||||||
('sr_channeltype', ('ChannelType', 'Channel type')),
|
|
||||||
('sr_trigger_matches', ('TriggerMatchType', 'Trigger match type')),
|
|
||||||
('sr_output_flag', ('OutputFlag', 'Flag applied to output modules'))])
|
|
||||||
|
|
||||||
index = ElementTree.parse(index_file)
|
|
||||||
|
|
||||||
# Build mapping between class names and enumerations.
|
|
||||||
|
|
||||||
classes = OrderedDict()
|
|
||||||
|
|
||||||
for compound in index.findall('compound'):
|
|
||||||
if compound.attrib['kind'] != 'file':
|
|
||||||
continue
|
|
||||||
filename = os.path.join(
|
|
||||||
os.path.dirname(index_file),
|
|
||||||
'%s.xml' % compound.attrib['refid'])
|
|
||||||
doc = ElementTree.parse(filename)
|
|
||||||
for section in doc.find('compounddef').findall('sectiondef'):
|
|
||||||
if section.attrib["kind"] != 'enum':
|
|
||||||
continue
|
|
||||||
for member in section.findall('memberdef'):
|
|
||||||
if member.attrib["kind"] != 'enum':
|
|
||||||
continue
|
|
||||||
name = member.find('name').text
|
|
||||||
if name in mapping:
|
|
||||||
classes[member] = mapping[name]
|
|
||||||
|
|
||||||
header = open(os.path.join(outdirname, 'cxx/include/libsigrokcxx/enums.hpp'), 'w')
|
|
||||||
code = open(os.path.join(outdirname, 'cxx/enums.cpp'), 'w')
|
|
||||||
swig = open(os.path.join(outdirname, 'swig/enums.i'), 'w')
|
|
||||||
|
|
||||||
for file in (header, code):
|
|
||||||
print("/* Generated file - edit enums.py instead! */", file=file)
|
|
||||||
|
|
||||||
print("namespace sigrok {", file=header)
|
|
||||||
|
|
||||||
# Template for beginning of class declaration and public members.
|
|
||||||
header_public_template = """
|
|
||||||
template<> const SR_API std::map<const enum {enumname}, const {classname} * const> EnumValue<{classname}, enum {enumname}>::_values;
|
|
||||||
|
|
||||||
/** {brief} */
|
|
||||||
class SR_API {classname} : public EnumValue<{classname}, enum {enumname}>
|
|
||||||
{{
|
|
||||||
public:
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Template for beginning of private members.
|
|
||||||
header_private_template = """
|
|
||||||
protected:
|
|
||||||
{classname}(enum {enumname} id, const char name[]) : EnumValue(id, name) {{}}
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_text(node):
|
|
||||||
return str.join('\n\n',
|
|
||||||
[p.text.rstrip() for p in node.findall('para')])
|
|
||||||
|
|
||||||
for enum, (classname, classbrief) in classes.items():
|
|
||||||
|
|
||||||
enum_name = enum.find('name').text
|
|
||||||
members = enum.findall('enumvalue')
|
|
||||||
member_names = [m.find('name').text for m in members]
|
|
||||||
trimmed_names = [re.sub("^SR_[A-Z]+_", "", n) for n in member_names]
|
|
||||||
briefs = [get_text(m.find('briefdescription')) for m in members]
|
|
||||||
|
|
||||||
# Begin class and public declarations
|
|
||||||
print(header_public_template.format(
|
|
||||||
brief=classbrief, classname=classname, enumname=enum_name), file=header)
|
|
||||||
|
|
||||||
# Declare public pointers for each enum value
|
|
||||||
for trimmed_name, brief in zip(trimmed_names, briefs):
|
|
||||||
if brief:
|
|
||||||
print('\t/** %s */' % brief, file=header)
|
|
||||||
print('\tstatic const %s * const %s;' % (
|
|
||||||
classname, trimmed_name), file=header)
|
|
||||||
|
|
||||||
# Declare additional methods if present
|
|
||||||
filename = os.path.join(dirname, "%s_methods.hpp" % classname)
|
|
||||||
if os.path.exists(filename):
|
|
||||||
print(str.join('', open(filename).readlines()), file=header)
|
|
||||||
|
|
||||||
# Begin private declarations
|
|
||||||
print(header_private_template.format(
|
|
||||||
classname=classname, enumname=enum_name), file=header)
|
|
||||||
|
|
||||||
# Declare private constants for each enum value
|
|
||||||
for trimmed_name in trimmed_names:
|
|
||||||
print('\tstatic const %s _%s;' % (classname, trimmed_name), file=header)
|
|
||||||
|
|
||||||
# End class declaration
|
|
||||||
print('};', file=header)
|
|
||||||
|
|
||||||
# Define private constants for each enum value
|
|
||||||
for name, trimmed_name in zip(member_names, trimmed_names):
|
|
||||||
print('const %s %s::_%s = %s(%s, "%s");' % (
|
|
||||||
classname, classname, trimmed_name, classname, name, trimmed_name),
|
|
||||||
file=code)
|
|
||||||
|
|
||||||
# Define public pointers for each enum value
|
|
||||||
for trimmed_name in trimmed_names:
|
|
||||||
print('const %s * const %s::%s = &%s::_%s;' % (
|
|
||||||
classname, classname, trimmed_name, classname, trimmed_name),
|
|
||||||
file=code)
|
|
||||||
|
|
||||||
# Define map of enum values to constants
|
|
||||||
print('template<> const SR_API std::map<const enum %s, const %s * const> EnumValue<%s, enum %s>::_values = {' % (
|
|
||||||
enum_name, classname, classname, enum_name), file=code)
|
|
||||||
for name, trimmed_name in zip(member_names, trimmed_names):
|
|
||||||
print('\t{%s, %s::%s},' % (name, classname, trimmed_name), file=code)
|
|
||||||
print('};', file=code)
|
|
||||||
|
|
||||||
# Define additional methods if present
|
|
||||||
filename = os.path.join(dirname, "%s_methods.cpp" % classname)
|
|
||||||
if os.path.exists(filename):
|
|
||||||
print(str.join('', open(filename).readlines()), file=code)
|
|
||||||
|
|
||||||
# Map EnumValue::id() and EnumValue::name() as SWIG attributes.
|
|
||||||
print('%%attribute(sigrok::%s, int, id, id);' % classname, file=swig)
|
|
||||||
print('%%attributestring(sigrok::%s, std::string, name, name);' % classname,
|
|
||||||
file=swig)
|
|
||||||
|
|
||||||
# Instantiate EnumValue template for SWIG
|
|
||||||
print('%%template(EnumValue%s) sigrok::EnumValue<sigrok::%s, enum %s>;' % (
|
|
||||||
classname, classname, enum_name), file=swig)
|
|
||||||
|
|
||||||
# Apply any language-specific extras.
|
|
||||||
print('%%enumextras(%s);' % classname, file=swig)
|
|
||||||
|
|
||||||
# Declare additional attributes if present
|
|
||||||
filename = os.path.join(dirname, "%s_methods.i" % classname)
|
|
||||||
if os.path.exists(filename):
|
|
||||||
print(str.join('', open(filename).readlines()), file=swig)
|
|
||||||
|
|
||||||
print("}", file=header)
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,13 +0,0 @@
|
||||||
prefix=@prefix@
|
|
||||||
exec_prefix=@exec_prefix@
|
|
||||||
libdir=@libdir@
|
|
||||||
includedir=@includedir@
|
|
||||||
|
|
||||||
Name: libsigrokcxx
|
|
||||||
Description: C++ bindings for libsigrok
|
|
||||||
URL: http://www.sigrok.org
|
|
||||||
Requires: libsigrok glibmm-2.4
|
|
||||||
Version: @SR_PACKAGE_VERSION@
|
|
||||||
Libs: -L${libdir} -lsigrokcxx
|
|
||||||
Libs.private: -lm
|
|
||||||
Cflags: -I${includedir}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,386 +0,0 @@
|
||||||
%module classes
|
|
||||||
|
|
||||||
/* Automatically load JNI library. */
|
|
||||||
%pragma(java) jniclasscode=%{
|
|
||||||
static {
|
|
||||||
System.loadLibrary("sigrok_java_core_classes");
|
|
||||||
}
|
|
||||||
%}
|
|
||||||
|
|
||||||
/* Documentation & importing interfaces. */
|
|
||||||
%pragma(java) jniclassimports=%{
|
|
||||||
/**
|
|
||||||
* @mainpage API Reference
|
|
||||||
*
|
|
||||||
* Introduction
|
|
||||||
* ------------
|
|
||||||
*
|
|
||||||
* The sigrok-java API provides an object-oriented Java interface to the
|
|
||||||
* functionality in libsigrok. It is built on top of the libsigrokcxx C++ API.
|
|
||||||
*
|
|
||||||
* Getting started
|
|
||||||
* ---------------
|
|
||||||
*
|
|
||||||
* Usage of the sigrok-java API needs to begin with a call to Context.create().
|
|
||||||
* This will create the global libsigrok context and returns a Context object.
|
|
||||||
* Methods on this object provide access to the hardware drivers, input and
|
|
||||||
* output formats supported by the library, as well as means of creating other
|
|
||||||
* objects such as sessions and triggers.
|
|
||||||
*
|
|
||||||
* Error handling
|
|
||||||
* --------------
|
|
||||||
*
|
|
||||||
* When any libsigrok C API call returns an error, an Error exception is raised,
|
|
||||||
* which provides access to the error code and description.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import org.sigrok.core.interfaces.LogCallback;
|
|
||||||
import org.sigrok.core.interfaces.DatafeedCallback;
|
|
||||||
%}
|
|
||||||
|
|
||||||
/* Map Glib::VariantBase to a Variant class in Java */
|
|
||||||
%rename(Variant) VariantBase;
|
|
||||||
namespace Glib {
|
|
||||||
class VariantBase {};
|
|
||||||
}
|
|
||||||
|
|
||||||
%include "bindings/swig/templates.i"
|
|
||||||
|
|
||||||
/* Map between std::vector and java.util.Vector */
|
|
||||||
%define VECTOR(CValue, JValue)
|
|
||||||
|
|
||||||
%typemap(jni) std::vector< CValue > "jobject"
|
|
||||||
%typemap(jtype) std::vector< CValue > "java.util.Vector<JValue>"
|
|
||||||
%typemap(jstype) std::vector< CValue > "java.util.Vector<JValue>"
|
|
||||||
|
|
||||||
%typemap(javain,
|
|
||||||
pre=" $javaclassname temp$javainput = new $javaclassname();
|
|
||||||
for (JValue value : $javainput)
|
|
||||||
temp$javainput.add(value);",
|
|
||||||
pgcppname="temp$javainput")
|
|
||||||
std::vector< CValue > "$javaclassname.getCPtr(temp$javainput)"
|
|
||||||
|
|
||||||
%typemap(javaout) std::vector< CValue > {
|
|
||||||
return (java.util.Vector<JValue>)$jnicall;
|
|
||||||
}
|
|
||||||
|
|
||||||
%typemap(out) std::vector< CValue > {
|
|
||||||
jclass Vector = jenv->FindClass("java/util/Vector");
|
|
||||||
jmethodID Vector_init = jenv->GetMethodID(Vector, "<init>", "()V");
|
|
||||||
jmethodID Vector_add = jenv->GetMethodID(Vector, "add",
|
|
||||||
"(Ljava/lang/Object;)Z");
|
|
||||||
jclass Value = jenv->FindClass("org/sigrok/core/classes/" #JValue);
|
|
||||||
jmethodID Value_init = jenv->GetMethodID(Value, "<init>", "(JZ)V");
|
|
||||||
$result = jenv->NewObject(Vector, Vector_init);
|
|
||||||
jlong value = 0;
|
|
||||||
for (auto entry : $1)
|
|
||||||
{
|
|
||||||
*(CValue **) &value = new CValue(entry);
|
|
||||||
jenv->CallBooleanMethod($result, Vector_add,
|
|
||||||
jenv->NewObject(Value, Value_init, value, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
VECTOR(std::shared_ptr<sigrok::Channel>, Channel)
|
|
||||||
VECTOR(std::shared_ptr<sigrok::HardwareDevice>, HardwareDevice)
|
|
||||||
|
|
||||||
/* Common macro for mapping between std::map and java.util.Map */
|
|
||||||
|
|
||||||
%define MAP_COMMON(CKey, CValue, JKey, JValue)
|
|
||||||
|
|
||||||
%typemap(jstype) std::map< CKey, CValue >
|
|
||||||
"java.util.Map<JKey, JValue>"
|
|
||||||
|
|
||||||
%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();
|
|
||||||
for (java.util.Map.Entry<JKey, JValue> entry : $javainput.entrySet())
|
|
||||||
temp$javainput.set(entry.getKey(), entry.getValue());",
|
|
||||||
pgcppname="temp$javainput")
|
|
||||||
#endif
|
|
||||||
std::map< CKey, CValue > "$javaclassname.getCPtr(temp$javainput)"
|
|
||||||
|
|
||||||
%typemap(javaout) std::map< CKey, CValue > {
|
|
||||||
return (java.util.Map<JKey, JValue>)$jnicall;
|
|
||||||
}
|
|
||||||
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
/* Specialisation for string->string maps. */
|
|
||||||
|
|
||||||
MAP_COMMON(std::string, std::string, String, String)
|
|
||||||
|
|
||||||
%typemap(jni) std::map<std::string, std::string>
|
|
||||||
"jobject"
|
|
||||||
%typemap(jtype) std::map<std::string, std::string>
|
|
||||||
"java.util.Map<String,String>"
|
|
||||||
|
|
||||||
%typemap(out) std::map<std::string, std::string> {
|
|
||||||
jclass HashMap = jenv->FindClass("java/util/HashMap");
|
|
||||||
jmethodID init = jenv->GetMethodID(HashMap, "<init>", "()V");
|
|
||||||
jmethodID put = jenv->GetMethodID(HashMap, "put",
|
|
||||||
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
|
||||||
$result = jenv->NewObject(HashMap, init);
|
|
||||||
for (auto entry : $1)
|
|
||||||
jenv->CallObjectMethod($result, put,
|
|
||||||
jenv->NewStringUTF(entry.first.c_str()),
|
|
||||||
jenv->NewStringUTF(entry.second.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Specialisation macro for string->shared_ptr maps. */
|
|
||||||
|
|
||||||
%define STRING_TO_SHARED_PTR_MAP(ClassName)
|
|
||||||
|
|
||||||
%typemap(jni) std::map<std::string, std::shared_ptr<sigrok::ClassName> >
|
|
||||||
"jobject"
|
|
||||||
%typemap(jtype) std::map<std::string, std::shared_ptr<sigrok::ClassName> >
|
|
||||||
"java.util.Map<String,ClassName>"
|
|
||||||
|
|
||||||
MAP_COMMON(std::string, std::shared_ptr<sigrok::ClassName>, String, ClassName)
|
|
||||||
|
|
||||||
%typemap(out) std::map<std::string, std::shared_ptr<sigrok::ClassName> > {
|
|
||||||
jclass HashMap = jenv->FindClass("java/util/HashMap");
|
|
||||||
jmethodID HashMap_init = jenv->GetMethodID(HashMap, "<init>", "()V");
|
|
||||||
jmethodID HashMap_put = jenv->GetMethodID(HashMap, "put",
|
|
||||||
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
|
||||||
jclass Value = jenv->FindClass("org/sigrok/core/classes/" #ClassName);
|
|
||||||
jmethodID Value_init = jenv->GetMethodID(Value, "<init>", "(JZ)V");
|
|
||||||
$result = jenv->NewObject(HashMap, HashMap_init);
|
|
||||||
jlong value = 0;
|
|
||||||
for (auto entry : $1)
|
|
||||||
{
|
|
||||||
*(std::shared_ptr< sigrok::ClassName > **)&value =
|
|
||||||
new std::shared_ptr< sigrok::ClassName>(entry.second);
|
|
||||||
jenv->CallObjectMethod($result, HashMap_put,
|
|
||||||
jenv->NewStringUTF(entry.first.c_str()),
|
|
||||||
jenv->NewObject(Value, Value_init, value, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
STRING_TO_SHARED_PTR_MAP(Driver)
|
|
||||||
STRING_TO_SHARED_PTR_MAP(InputFormat)
|
|
||||||
STRING_TO_SHARED_PTR_MAP(OutputFormat)
|
|
||||||
|
|
||||||
/* Specialisation for ConfigKey->Variant maps */
|
|
||||||
|
|
||||||
MAP_COMMON(const sigrok::ConfigKey *, Glib::VariantBase, ConfigKey, Variant)
|
|
||||||
|
|
||||||
%typemap(jni) std::map<const sigrok::ConfigKey, Glib::VariantBase> "jobject"
|
|
||||||
%typemap(jtype) std::map<const sigrok::ConfigKey, Glib::VariantBase>
|
|
||||||
"java.util.Map<ConfigKey,Variant>"
|
|
||||||
|
|
||||||
%typemap(out) std::map<const sigrok::ConfigKey *, Glib::VariantBase> {
|
|
||||||
jclass HashMap = jenv->FindClass("java/util/HashMap");
|
|
||||||
jmethodID HashMap_init = jenv->GetMethodID(HashMap, "<init>", "()V");
|
|
||||||
jmethodID HashMap_put = jenv->GetMethodID(HashMap, "put",
|
|
||||||
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
|
||||||
jclass ConfigKey = jenv->FindClass("org/sigrok/core/classes/ConfigKey");
|
|
||||||
jmethodID ConfigKey_init = jenv->GetMethodID(ConfigKey, "<init>", "(JZ)V");
|
|
||||||
jclass Variant = jenv->FindClass("org/sigrok/core/classes/Variant");
|
|
||||||
jmethodID Variant_init = jenv->GetMethodID(Variant, "<init>", "(JZ)V");
|
|
||||||
$result = jenv->NewObject(HashMap, HashMap_init);
|
|
||||||
jlong key = 0;
|
|
||||||
jlong value = 0;
|
|
||||||
for (auto entry : $1)
|
|
||||||
{
|
|
||||||
*(const sigrok::ConfigKey **) &key = entry.first;
|
|
||||||
*(Glib::VariantBase **) &value = new Glib::VariantBase(entry.second);
|
|
||||||
jenv->CallObjectMethod($result, HashMap_put,
|
|
||||||
jenv->NewObject(ConfigKey, ConfigKey_init, key, false));
|
|
||||||
jenv->NewObject(Variant, Variant_init, value, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pass JNIEnv parameter to C++ extension methods requiring it. */
|
|
||||||
|
|
||||||
%typemap(in, numinputs=0) JNIEnv * %{
|
|
||||||
$1 = jenv;
|
|
||||||
%}
|
|
||||||
|
|
||||||
/* Thread safe JNIEnv handling */
|
|
||||||
|
|
||||||
%inline {
|
|
||||||
namespace {
|
|
||||||
class ScopedEnv {
|
|
||||||
public:
|
|
||||||
ScopedEnv(JavaVM *jvm);
|
|
||||||
~ScopedEnv();
|
|
||||||
JNIEnv* operator-> () { return env; }
|
|
||||||
operator bool () const { return (bool)env; }
|
|
||||||
private:
|
|
||||||
JavaVM *jvm;
|
|
||||||
JNIEnv *env;
|
|
||||||
int env_status;
|
|
||||||
};
|
|
||||||
ScopedEnv::ScopedEnv(JavaVM *jvm) : jvm(jvm), env(NULL) {
|
|
||||||
env_status = jvm->GetEnv((void **)&env, JNI_VERSION_1_2);
|
|
||||||
if (env_status == JNI_EDETACHED) {
|
|
||||||
%#if defined(__ANDROID__)
|
|
||||||
jvm->AttachCurrentThread(&env, NULL);
|
|
||||||
%#else
|
|
||||||
jvm->AttachCurrentThread((void **)&env, NULL);
|
|
||||||
%#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ScopedEnv::~ScopedEnv() {
|
|
||||||
if (env_status == JNI_EDETACHED) {
|
|
||||||
jvm->DetachCurrentThread();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* "Smartpointer" for Java references. */
|
|
||||||
|
|
||||||
%inline {
|
|
||||||
namespace {
|
|
||||||
class GlobalRefBase
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
GlobalRefBase (JavaVM *jvm, jobject ref);
|
|
||||||
~GlobalRefBase ();
|
|
||||||
JavaVM *jvm;
|
|
||||||
jobject jref;
|
|
||||||
};
|
|
||||||
GlobalRefBase::GlobalRefBase (JavaVM *jvm, jobject ref) : jvm(jvm), jref(0) {
|
|
||||||
ScopedEnv env(jvm);
|
|
||||||
if (env && ref)
|
|
||||||
jref = env->NewGlobalRef(ref);
|
|
||||||
}
|
|
||||||
GlobalRefBase::~GlobalRefBase () {
|
|
||||||
ScopedEnv env(jvm);
|
|
||||||
if(env && jref)
|
|
||||||
env->DeleteGlobalRef(jref);
|
|
||||||
}
|
|
||||||
template <class Jtype>
|
|
||||||
class GlobalRef : private GlobalRefBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GlobalRef (JavaVM *jvm, Jtype ref) : GlobalRefBase(jvm, ref) {}
|
|
||||||
GlobalRef (const GlobalRef &ref) : GlobalRefBase(ref.jvm, ref.jref) {}
|
|
||||||
operator Jtype () const { return static_cast<Jtype>(jref); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support Java log callbacks. */
|
|
||||||
|
|
||||||
%typemap(javaimports) sigrok::Context
|
|
||||||
"import org.sigrok.core.interfaces.LogCallback;"
|
|
||||||
|
|
||||||
%inline {
|
|
||||||
typedef jobject jlogcallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
%typemap(jni) jlogcallback "jlogcallback"
|
|
||||||
%typemap(jtype) jlogcallback "LogCallback"
|
|
||||||
%typemap(jstype) jlogcallback "LogCallback"
|
|
||||||
%typemap(javain) jlogcallback "$javainput"
|
|
||||||
|
|
||||||
%extend sigrok::Context
|
|
||||||
{
|
|
||||||
void add_log_callback(JNIEnv *env, jlogcallback obj)
|
|
||||||
{
|
|
||||||
JavaVM *jvm = NULL;
|
|
||||||
env->GetJavaVM(&jvm);
|
|
||||||
jclass obj_class = env->GetObjectClass(obj);
|
|
||||||
jmethodID method = env->GetMethodID(obj_class, "run",
|
|
||||||
"(Lorg/sigrok/core/classes/LogLevel;Ljava/lang/String;)V");
|
|
||||||
GlobalRef<jclass> LogLevel(jvm, env->FindClass("org/sigrok/core/classes/LogLevel"));
|
|
||||||
jmethodID LogLevel_init = env->GetMethodID(LogLevel, "<init>", "(JZ)V");
|
|
||||||
GlobalRef<jobject> obj_ref(jvm, obj);
|
|
||||||
|
|
||||||
$self->set_log_callback([=] (
|
|
||||||
const sigrok::LogLevel *loglevel,
|
|
||||||
std::string message)
|
|
||||||
{
|
|
||||||
ScopedEnv env(jvm);
|
|
||||||
if (!env)
|
|
||||||
throw sigrok::Error(SR_ERR);
|
|
||||||
jlong loglevel_addr = 0;
|
|
||||||
*(const sigrok::LogLevel **) &loglevel_addr = loglevel;
|
|
||||||
jobject loglevel_obj = env->NewObject(
|
|
||||||
LogLevel, LogLevel_init, loglevel_addr, false);
|
|
||||||
jobject message_obj = env->NewStringUTF(message.c_str());
|
|
||||||
env->CallVoidMethod(obj_ref, method, loglevel_obj, message_obj);
|
|
||||||
if (env->ExceptionCheck())
|
|
||||||
throw sigrok::Error(SR_ERR);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support Java datafeed callbacks. */
|
|
||||||
|
|
||||||
%typemap(javaimports) sigrok::Session
|
|
||||||
"import org.sigrok.core.interfaces.DatafeedCallback;"
|
|
||||||
|
|
||||||
%inline {
|
|
||||||
typedef jobject jdatafeedcallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
%typemap(jni) jdatafeedcallback "jdatafeedcallback"
|
|
||||||
%typemap(jtype) jdatafeedcallback "DatafeedCallback"
|
|
||||||
%typemap(jstype) jdatafeedcallback "DatafeedCallback"
|
|
||||||
%typemap(javain) jdatafeedcallback "$javainput"
|
|
||||||
|
|
||||||
%extend sigrok::Session
|
|
||||||
{
|
|
||||||
void add_datafeed_callback(JNIEnv *env, jdatafeedcallback obj)
|
|
||||||
{
|
|
||||||
JavaVM *jvm = NULL;
|
|
||||||
env->GetJavaVM(&jvm);
|
|
||||||
jclass obj_class = env->GetObjectClass(obj);
|
|
||||||
jmethodID method = env->GetMethodID(obj_class, "run",
|
|
||||||
"(Lorg/sigrok/core/classes/Device;Lorg/sigrok/core/classes/Packet;)V");
|
|
||||||
GlobalRef<jclass> Device(jvm, env->FindClass("org/sigrok/core/classes/Device"));
|
|
||||||
jmethodID Device_init = env->GetMethodID(Device, "<init>", "(JZ)V");
|
|
||||||
GlobalRef<jclass> Packet(jvm, env->FindClass("org/sigrok/core/classes/Packet"));
|
|
||||||
jmethodID Packet_init = env->GetMethodID(Packet, "<init>", "(JZ)V");
|
|
||||||
GlobalRef<jobject> obj_ref(jvm, obj);
|
|
||||||
|
|
||||||
$self->add_datafeed_callback([=] (
|
|
||||||
std::shared_ptr<sigrok::Device> device,
|
|
||||||
std::shared_ptr<sigrok::Packet> packet)
|
|
||||||
{
|
|
||||||
ScopedEnv env(jvm);
|
|
||||||
if (!env)
|
|
||||||
throw sigrok::Error(SR_ERR);
|
|
||||||
jlong device_addr = 0;
|
|
||||||
jlong packet_addr = 0;
|
|
||||||
*(std::shared_ptr<sigrok::Device> **) &device_addr =
|
|
||||||
new std::shared_ptr<sigrok::Device>(device);
|
|
||||||
*(std::shared_ptr<sigrok::Packet> **) &packet_addr =
|
|
||||||
new std::shared_ptr<sigrok::Packet>(packet);
|
|
||||||
jobject device_obj = env->NewObject(
|
|
||||||
Device, Device_init, device_addr, true);
|
|
||||||
jobject packet_obj = env->NewObject(
|
|
||||||
Packet, Packet_init, packet_addr, true);
|
|
||||||
env->CallVoidMethod(obj_ref, method, device_obj, packet_obj);
|
|
||||||
if (env->ExceptionCheck())
|
|
||||||
throw sigrok::Error(SR_ERR);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%include "doc.i"
|
|
||||||
|
|
||||||
%define %enumextras(Class)
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
/* Ignore these for now, need fixes. */
|
|
||||||
%ignore sigrok::Context::create_analog_packet;
|
|
||||||
%ignore sigrok::Context::create_meta_packet;
|
|
||||||
%ignore sigrok::Meta::config;
|
|
||||||
|
|
||||||
%include "bindings/swig/classes.i"
|
|
||||||
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package org.sigrok.core.interfaces;
|
|
||||||
|
|
||||||
import org.sigrok.core.classes.Device;
|
|
||||||
import org.sigrok.core.classes.Packet;
|
|
||||||
|
|
||||||
public interface DatafeedCallback
|
|
||||||
{
|
|
||||||
public void run(Device device, Packet packet);
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package org.sigrok.core.interfaces;
|
|
||||||
|
|
||||||
import org.sigrok.core.classes.LogLevel;
|
|
||||||
|
|
||||||
public interface LogCallback
|
|
||||||
{
|
|
||||||
public void run(LogLevel loglevel, String message);
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,83 +18,27 @@
|
||||||
##
|
##
|
||||||
|
|
||||||
from setuptools import setup, find_packages, Extension
|
from setuptools import setup, find_packages, Extension
|
||||||
from distutils.command.build_py import build_py as _build_py
|
import subprocess
|
||||||
from distutils.command.build_ext import build_ext as _build_ext
|
|
||||||
import numpy as np
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import shlex
|
|
||||||
|
|
||||||
srcdir = os.path.dirname(os.path.abspath(__file__))
|
sr_includes, sr_lib_dirs, sr_libs, (sr_version,) = [
|
||||||
os.chdir('bindings/python')
|
subprocess.check_output(
|
||||||
srcdir = os.path.relpath(srcdir)
|
["pkg-config", option, "libsigrok"]).decode().rstrip().split(' ')
|
||||||
srcdir_parent = os.path.normpath(os.path.join(srcdir, '..'))
|
for option in
|
||||||
|
("--cflags-only-I", "--libs-only-L", "--libs-only-l", "--modversion")]
|
||||||
# Override the default compile flags used by distutils.
|
|
||||||
os.environ['OPT'] = ''
|
|
||||||
|
|
||||||
# Parse the command line arguments for VAR=value assignments,
|
|
||||||
# and apply them as environment variables.
|
|
||||||
while len(sys.argv) > 1:
|
|
||||||
match = re.match(r'([A-Z]+)=(.*)', sys.argv[1])
|
|
||||||
if match is None:
|
|
||||||
break
|
|
||||||
os.environ[match.group(1)] = match.group(2)
|
|
||||||
del sys.argv[1]
|
|
||||||
|
|
||||||
includes = ['../../include', '../cxx/include']
|
|
||||||
includes += [os.path.normpath(os.path.join(srcdir, path)) for path in includes]
|
|
||||||
includes += ['../..', np.get_include()]
|
|
||||||
|
|
||||||
ldadd = shlex.split(os.environ.get('LDADD', ''))
|
|
||||||
libdirs = ['../../.libs', '../cxx/.libs'] + \
|
|
||||||
[l[2:] for l in ldadd if l.startswith('-L')]
|
|
||||||
libs = [l[2:] for l in ldadd if l.startswith('-l')] + ['sigrokcxx']
|
|
||||||
|
|
||||||
def vpath(file):
|
|
||||||
vfile = os.path.join(srcdir, file)
|
|
||||||
return vfile if os.path.exists(vfile) else file
|
|
||||||
|
|
||||||
def unvpath(file):
|
|
||||||
return os.path.relpath(file, srcdir) if file.startswith(srcdir) else file
|
|
||||||
|
|
||||||
class build_py(_build_py):
|
|
||||||
def find_package_modules(self, package, pkg_dir):
|
|
||||||
mods = _build_py.find_package_modules(self, package, pkg_dir)
|
|
||||||
vmods = _build_py.find_package_modules(self, package, vpath(pkg_dir))
|
|
||||||
mods.extend([mod for mod in vmods if mod not in mods])
|
|
||||||
return mods
|
|
||||||
def check_package(self, package, package_dir):
|
|
||||||
return _build_py.check_package(self, package, vpath(package_dir))
|
|
||||||
|
|
||||||
class build_ext(_build_ext):
|
|
||||||
def finalize_options(self):
|
|
||||||
_build_ext.finalize_options(self)
|
|
||||||
self.swig_opts = ['-c++', '-threads', '-Isigrok/core', '-I..',
|
|
||||||
'-I' + srcdir_parent] + ['-I%s' % i for i in includes] + self.swig_opts
|
|
||||||
def spawn (self, cmd):
|
|
||||||
cmd[1:-1] = [arg if arg.startswith('-') else unvpath(arg) for arg in
|
|
||||||
cmd[1:-1]]
|
|
||||||
_build_ext.spawn(self, cmd)
|
|
||||||
def swig_sources (self, sources, extension):
|
|
||||||
return [unvpath(src) for src in
|
|
||||||
_build_ext.swig_sources(self, sources, extension)]
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = 'libsigrok',
|
name = 'libsigrok',
|
||||||
namespace_packages = ['sigrok'],
|
namespace_packages = ['sigrok'],
|
||||||
packages = find_packages(srcdir),
|
packages = find_packages(),
|
||||||
version = os.environ.get('VERSION'),
|
version = sr_version,
|
||||||
description = "libsigrok API wrapper",
|
description = "libsigrok API wrapper",
|
||||||
zip_safe = False,
|
|
||||||
ext_modules = [
|
ext_modules = [
|
||||||
Extension('sigrok.core._classes',
|
Extension('sigrok.core._lowlevel',
|
||||||
sources = [vpath('sigrok/core/classes.i')],
|
sources = ['sigrok/core/lowlevel.i'],
|
||||||
extra_compile_args = ['-Wno-uninitialized'],
|
swig_opts = ['-threads'] + sr_includes,
|
||||||
include_dirs = includes,
|
include_dirs = [i[2:] for i in sr_includes],
|
||||||
library_dirs = libdirs,
|
library_dirs = [l[2:] for l in sr_lib_dirs],
|
||||||
libraries = libs)
|
libraries = [l[2:] for l in sr_libs]
|
||||||
|
)
|
||||||
],
|
],
|
||||||
cmdclass = {'build_py': build_py, 'build_ext': build_ext},
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,5 +17,6 @@
|
||||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
##
|
##
|
||||||
|
|
||||||
|
from . import lowlevel
|
||||||
from . import classes
|
from . import classes
|
||||||
from .classes import *
|
from .classes import *
|
||||||
|
|
|
||||||
|
|
@ -1,600 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Martin Ling <martin-sigrok@earth.li>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
%define DOCSTRING
|
|
||||||
"@mainpage API Reference
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
|
|
||||||
The pysigrok API provides an object-oriented Python interface to the
|
|
||||||
functionality in libsigrok. It is built on top of the libsigrokcxx C++ API.
|
|
||||||
|
|
||||||
Getting started
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Usage of the pysigrok API needs to begin with a call to Context.create().
|
|
||||||
This will create the global libsigrok context and returns a Context object.
|
|
||||||
Methods on this object provide access to the hardware drivers, input and output
|
|
||||||
formats supported by the library, as well as means of creating other objects
|
|
||||||
such as sessions and triggers.
|
|
||||||
|
|
||||||
Error handling
|
|
||||||
--------------
|
|
||||||
|
|
||||||
When any libsigrok C API call returns an error, an Error exception is raised,
|
|
||||||
which provides access to the error code and description."
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
%module(docstring=DOCSTRING) classes
|
|
||||||
|
|
||||||
%{
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <pygobject.h>
|
|
||||||
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
|
||||||
#include <numpy/arrayobject.h>
|
|
||||||
|
|
||||||
PyObject *PyGObject_lib;
|
|
||||||
PyObject *GLib;
|
|
||||||
|
|
||||||
#if PYGOBJECT_FLAGS_SIGNED
|
|
||||||
typedef gint pyg_flags_type;
|
|
||||||
#else
|
|
||||||
typedef guint pyg_flags_type;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if PY_VERSION_HEX >= 0x03000000
|
|
||||||
#define string_check PyUnicode_Check
|
|
||||||
#define string_from_python PyUnicode_AsUTF8
|
|
||||||
#define string_to_python PyUnicode_FromString
|
|
||||||
#else
|
|
||||||
#define string_check PyString_Check
|
|
||||||
#define string_from_python PyString_AsString
|
|
||||||
#define string_to_python PyString_FromString
|
|
||||||
#endif
|
|
||||||
|
|
||||||
%}
|
|
||||||
|
|
||||||
%init %{
|
|
||||||
PyGObject_lib = pygobject_init(-1, -1, -1);
|
|
||||||
if (!PyGObject_lib)
|
|
||||||
fprintf(stderr, "pygobject initialization failed.\n");
|
|
||||||
GLib = PyImport_ImportModule("gi.repository.GLib");
|
|
||||||
/*
|
|
||||||
* This check can't save us if the import fails, but at least it gives us
|
|
||||||
* a starting point to trace the issue versus straight out crashing.
|
|
||||||
*/
|
|
||||||
if (!GLib) {
|
|
||||||
fprintf(stderr, "Import of gi.repository.GLib failed.\n");
|
|
||||||
#if PY_VERSION_HEX >= 0x03000000
|
|
||||||
return nullptr;
|
|
||||||
#else
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
import_array();
|
|
||||||
%}
|
|
||||||
|
|
||||||
%include "../../../swig/templates.i"
|
|
||||||
|
|
||||||
/* Map file objects to file descriptors. */
|
|
||||||
%typecheck(SWIG_TYPECHECK_POINTER) int fd {
|
|
||||||
$1 = (PyObject_AsFileDescriptor($input) != -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map from Glib::Variant to native Python types. */
|
|
||||||
%typemap(out) Glib::VariantBase {
|
|
||||||
GValue *value = g_new0(GValue, 1);
|
|
||||||
g_value_init(value, G_TYPE_VARIANT);
|
|
||||||
g_value_set_variant(value, $1.gobj());
|
|
||||||
PyObject *variant = pyg_value_as_pyobject(value, true);
|
|
||||||
$result = PyObject_CallMethod(variant,
|
|
||||||
const_cast<char *>("unpack"), nullptr);
|
|
||||||
Py_XDECREF(variant);
|
|
||||||
g_free(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use the same typemap above for Glib::VariantContainerBase */
|
|
||||||
%apply Glib::VariantBase { Glib::VariantContainerBase }
|
|
||||||
|
|
||||||
/* Map from callable PyObject to LogCallbackFunction */
|
|
||||||
%typecheck(SWIG_TYPECHECK_POINTER) sigrok::LogCallbackFunction {
|
|
||||||
$1 = PyCallable_Check($input);
|
|
||||||
}
|
|
||||||
|
|
||||||
%typemap(in) sigrok::LogCallbackFunction {
|
|
||||||
if (!PyCallable_Check($input))
|
|
||||||
SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
|
|
||||||
|
|
||||||
$1 = [=] (const sigrok::LogLevel *loglevel, std::string message) {
|
|
||||||
auto gstate = PyGILState_Ensure();
|
|
||||||
|
|
||||||
auto log_obj = SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0);
|
|
||||||
|
|
||||||
auto string_obj = string_to_python(message.c_str());
|
|
||||||
|
|
||||||
auto arglist = Py_BuildValue("(OO)", log_obj, string_obj);
|
|
||||||
|
|
||||||
auto result = PyEval_CallObject($input, arglist);
|
|
||||||
|
|
||||||
Py_XDECREF(arglist);
|
|
||||||
Py_XDECREF(log_obj);
|
|
||||||
Py_XDECREF(string_obj);
|
|
||||||
|
|
||||||
bool completed = !PyErr_Occurred();
|
|
||||||
|
|
||||||
if (!completed)
|
|
||||||
PyErr_Print();
|
|
||||||
|
|
||||||
bool valid_result = (completed && result == Py_None);
|
|
||||||
|
|
||||||
Py_XDECREF(result);
|
|
||||||
|
|
||||||
if (completed && !valid_result)
|
|
||||||
{
|
|
||||||
PyErr_SetString(PyExc_TypeError,
|
|
||||||
"Log callback did not return None");
|
|
||||||
PyErr_Print();
|
|
||||||
}
|
|
||||||
|
|
||||||
PyGILState_Release(gstate);
|
|
||||||
|
|
||||||
if (!valid_result)
|
|
||||||
throw sigrok::Error(SR_ERR);
|
|
||||||
};
|
|
||||||
|
|
||||||
Py_XINCREF($input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map from callable PyObject to SessionStoppedCallback */
|
|
||||||
%typecheck(SWIG_TYPECHECK_POINTER) sigrok::SessionStoppedCallback {
|
|
||||||
$1 = PyCallable_Check($input);
|
|
||||||
}
|
|
||||||
|
|
||||||
%typemap(in) sigrok::SessionStoppedCallback {
|
|
||||||
if (!PyCallable_Check($input))
|
|
||||||
SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
|
|
||||||
|
|
||||||
$1 = [=] () {
|
|
||||||
const auto gstate = PyGILState_Ensure();
|
|
||||||
|
|
||||||
const auto result = PyEval_CallObject($input, nullptr);
|
|
||||||
const bool completed = !PyErr_Occurred();
|
|
||||||
const bool valid_result = (completed && result == Py_None);
|
|
||||||
|
|
||||||
if (completed && !valid_result) {
|
|
||||||
PyErr_SetString(PyExc_TypeError,
|
|
||||||
"Session stop callback did not return None");
|
|
||||||
}
|
|
||||||
if (!valid_result)
|
|
||||||
PyErr_Print();
|
|
||||||
|
|
||||||
Py_XDECREF(result);
|
|
||||||
PyGILState_Release(gstate);
|
|
||||||
|
|
||||||
if (!valid_result)
|
|
||||||
throw sigrok::Error(SR_ERR);
|
|
||||||
};
|
|
||||||
|
|
||||||
Py_XINCREF($input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map from callable PyObject to DatafeedCallbackFunction */
|
|
||||||
%typecheck(SWIG_TYPECHECK_POINTER) sigrok::DatafeedCallbackFunction {
|
|
||||||
$1 = PyCallable_Check($input);
|
|
||||||
}
|
|
||||||
|
|
||||||
%typemap(in) sigrok::DatafeedCallbackFunction {
|
|
||||||
if (!PyCallable_Check($input))
|
|
||||||
SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
|
|
||||||
|
|
||||||
$1 = [=] (std::shared_ptr<sigrok::Device> device,
|
|
||||||
std::shared_ptr<sigrok::Packet> packet) {
|
|
||||||
auto gstate = PyGILState_Ensure();
|
|
||||||
|
|
||||||
auto device_obj = SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(new std::shared_ptr<sigrok::Device>(device)),
|
|
||||||
SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN);
|
|
||||||
|
|
||||||
auto packet_obj = SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(new std::shared_ptr<sigrok::Packet>(packet)),
|
|
||||||
SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN);
|
|
||||||
|
|
||||||
auto arglist = Py_BuildValue("(OO)", device_obj, packet_obj);
|
|
||||||
|
|
||||||
auto result = PyEval_CallObject($input, arglist);
|
|
||||||
|
|
||||||
Py_XDECREF(arglist);
|
|
||||||
Py_XDECREF(device_obj);
|
|
||||||
Py_XDECREF(packet_obj);
|
|
||||||
|
|
||||||
bool completed = !PyErr_Occurred();
|
|
||||||
|
|
||||||
if (!completed)
|
|
||||||
PyErr_Print();
|
|
||||||
|
|
||||||
bool valid_result = (completed && result == Py_None);
|
|
||||||
|
|
||||||
Py_XDECREF(result);
|
|
||||||
|
|
||||||
if (completed && !valid_result)
|
|
||||||
{
|
|
||||||
PyErr_SetString(PyExc_TypeError,
|
|
||||||
"Datafeed callback did not return None");
|
|
||||||
PyErr_Print();
|
|
||||||
}
|
|
||||||
|
|
||||||
PyGILState_Release(gstate);
|
|
||||||
|
|
||||||
if (!valid_result)
|
|
||||||
throw sigrok::Error(SR_ERR);
|
|
||||||
};
|
|
||||||
|
|
||||||
Py_XINCREF($input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cast PacketPayload pointers to correct subclass type. */
|
|
||||||
%ignore sigrok::Packet::payload;
|
|
||||||
|
|
||||||
%extend sigrok::Packet
|
|
||||||
{
|
|
||||||
std::shared_ptr<sigrok::Header> _payload_header()
|
|
||||||
{
|
|
||||||
return dynamic_pointer_cast<sigrok::Header>($self->payload());
|
|
||||||
}
|
|
||||||
std::shared_ptr<sigrok::Meta> _payload_meta()
|
|
||||||
{
|
|
||||||
return dynamic_pointer_cast<sigrok::Meta>($self->payload());
|
|
||||||
}
|
|
||||||
std::shared_ptr<sigrok::Analog> _payload_analog()
|
|
||||||
{
|
|
||||||
return dynamic_pointer_cast<sigrok::Analog>($self->payload());
|
|
||||||
}
|
|
||||||
std::shared_ptr<sigrok::Logic> _payload_logic()
|
|
||||||
{
|
|
||||||
return dynamic_pointer_cast<sigrok::Logic>($self->payload());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%extend sigrok::Packet
|
|
||||||
{
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
def _payload(self):
|
|
||||||
if self.type == PacketType.HEADER:
|
|
||||||
return self._payload_header()
|
|
||||||
elif self.type == PacketType.META:
|
|
||||||
return self._payload_meta()
|
|
||||||
elif self.type == PacketType.LOGIC:
|
|
||||||
return self._payload_logic()
|
|
||||||
elif self.type == PacketType.ANALOG:
|
|
||||||
return self._payload_analog()
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
payload = property(_payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%{
|
|
||||||
|
|
||||||
#include "libsigrokcxx/libsigrokcxx.hpp"
|
|
||||||
|
|
||||||
/* Convert from a Python dict to a std::map<std::string, std::string> */
|
|
||||||
std::map<std::string, std::string> dict_to_map_string(PyObject *dict)
|
|
||||||
{
|
|
||||||
if (!PyDict_Check(dict))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
|
|
||||||
std::map<std::string, std::string> output;
|
|
||||||
|
|
||||||
PyObject *py_key, *py_value;
|
|
||||||
Py_ssize_t pos = 0;
|
|
||||||
|
|
||||||
while (PyDict_Next(dict, &pos, &py_key, &py_value)) {
|
|
||||||
if (!string_check(py_key))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
if (!string_check(py_value))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
auto key = string_from_python(py_key);
|
|
||||||
auto value = string_from_python(py_value);
|
|
||||||
output[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert from a Python type to Glib::Variant, according to config key data type. */
|
|
||||||
Glib::VariantBase python_to_variant_by_key(PyObject *input, const sigrok::ConfigKey *key)
|
|
||||||
{
|
|
||||||
enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
|
|
||||||
|
|
||||||
if (type == SR_T_UINT64 && PyInt_Check(input))
|
|
||||||
return Glib::Variant<guint64>::create(PyInt_AsLong(input));
|
|
||||||
if (type == SR_T_UINT64 && PyLong_Check(input))
|
|
||||||
return Glib::Variant<guint64>::create(PyLong_AsLong(input));
|
|
||||||
else if (type == SR_T_STRING && string_check(input))
|
|
||||||
return Glib::Variant<Glib::ustring>::create(string_from_python(input));
|
|
||||||
else if (type == SR_T_BOOL && PyBool_Check(input))
|
|
||||||
return Glib::Variant<bool>::create(input == Py_True);
|
|
||||||
else if (type == SR_T_FLOAT && PyFloat_Check(input))
|
|
||||||
return Glib::Variant<double>::create(PyFloat_AsDouble(input));
|
|
||||||
else if (type == SR_T_INT32 && PyInt_Check(input))
|
|
||||||
return Glib::Variant<gint32>::create(PyInt_AsLong(input));
|
|
||||||
else if ((type == SR_T_RATIONAL_VOLT) && PyTuple_Check(input) && (PyTuple_Size(input) == 2)) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert from a Python type to Glib::Variant, according to Option data type. */
|
|
||||||
Glib::VariantBase python_to_variant_by_option(PyObject *input,
|
|
||||||
std::shared_ptr<sigrok::Option> option)
|
|
||||||
{
|
|
||||||
GVariantType *type = option->default_value().get_type().gobj();
|
|
||||||
|
|
||||||
if (type == G_VARIANT_TYPE_UINT64 && PyInt_Check(input))
|
|
||||||
return Glib::Variant<guint64>::create(PyInt_AsLong(input));
|
|
||||||
if (type == G_VARIANT_TYPE_UINT64 && PyLong_Check(input))
|
|
||||||
return Glib::Variant<guint64>::create(PyLong_AsLong(input));
|
|
||||||
else if (type == G_VARIANT_TYPE_STRING && string_check(input))
|
|
||||||
return Glib::Variant<Glib::ustring>::create(string_from_python(input));
|
|
||||||
else if (type == G_VARIANT_TYPE_BOOLEAN && PyBool_Check(input))
|
|
||||||
return Glib::Variant<bool>::create(input == Py_True);
|
|
||||||
else if (type == G_VARIANT_TYPE_DOUBLE && PyFloat_Check(input))
|
|
||||||
return Glib::Variant<double>::create(PyFloat_AsDouble(input));
|
|
||||||
else if (type == G_VARIANT_TYPE_INT32 && PyInt_Check(input))
|
|
||||||
return Glib::Variant<gint32>::create(PyInt_AsLong(input));
|
|
||||||
else
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert from a Python dict to a std::map<std::string, std::string> */
|
|
||||||
std::map<std::string, Glib::VariantBase> dict_to_map_options(PyObject *dict,
|
|
||||||
std::map<std::string, std::shared_ptr<sigrok::Option> > options)
|
|
||||||
{
|
|
||||||
if (!PyDict_Check(dict))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
|
|
||||||
std::map<std::string, Glib::VariantBase> output;
|
|
||||||
|
|
||||||
PyObject *py_key, *py_value;
|
|
||||||
Py_ssize_t pos = 0;
|
|
||||||
|
|
||||||
while (PyDict_Next(dict, &pos, &py_key, &py_value)) {
|
|
||||||
if (!string_check(py_key))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
auto key = string_from_python(py_key);
|
|
||||||
auto value = python_to_variant_by_option(py_value, options[key]);
|
|
||||||
output[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
%}
|
|
||||||
|
|
||||||
/* Ignore these methods, we will override them below. */
|
|
||||||
%ignore sigrok::Analog::data;
|
|
||||||
%ignore sigrok::Logic::data;
|
|
||||||
%ignore sigrok::Driver::scan;
|
|
||||||
%ignore sigrok::InputFormat::create_input;
|
|
||||||
%ignore sigrok::OutputFormat::create_output;
|
|
||||||
|
|
||||||
%include "doc_start.i"
|
|
||||||
|
|
||||||
%define %attributevector(Class, Type, Name, Get)
|
|
||||||
%rename(_ ## Get) sigrok::Class::Get;
|
|
||||||
%extend sigrok::Class
|
|
||||||
{
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
Name = property(_ ## Get)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
%define %attributemap(Class, Type, Name, Get)
|
|
||||||
%rename(_ ## Get) sigrok::Class::Get;
|
|
||||||
%extend sigrok::Class
|
|
||||||
{
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
Name = property(fget = lambda x: x._ ## Get().asdict(), doc=_ ## Get.__doc__)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
%define %enumextras(Class)
|
|
||||||
%extend sigrok::Class
|
|
||||||
{
|
|
||||||
long __hash__()
|
|
||||||
{
|
|
||||||
return (long) $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string __str__()
|
|
||||||
{
|
|
||||||
return $self->name();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string __repr__()
|
|
||||||
{
|
|
||||||
return "Class." + $self->name();
|
|
||||||
}
|
|
||||||
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
def __eq__(self, other):
|
|
||||||
return (type(self) is type(other) and hash(self) == hash(other))
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
return (type(self) is not type(other) or hash(self) != hash(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
%include "../../../swig/classes.i"
|
|
||||||
|
|
||||||
/* Support Driver.scan() with keyword arguments. */
|
|
||||||
%extend sigrok::Driver
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan_kwargs(PyObject *dict)
|
|
||||||
{
|
|
||||||
if (!PyDict_Check(dict))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
|
|
||||||
PyObject *py_key, *py_value;
|
|
||||||
Py_ssize_t pos = 0;
|
|
||||||
std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
|
|
||||||
|
|
||||||
while (PyDict_Next(dict, &pos, &py_key, &py_value))
|
|
||||||
{
|
|
||||||
if (!string_check(py_key))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
auto key = sigrok::ConfigKey::get_by_identifier(string_from_python(py_key));
|
|
||||||
auto value = python_to_variant_by_key(py_value, key);
|
|
||||||
options[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $self->scan(options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
def _Driver_scan(self, **kwargs):
|
|
||||||
return self._scan_kwargs(kwargs)
|
|
||||||
|
|
||||||
Driver.scan = _Driver_scan
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support InputFormat.create_input() with keyword arguments. */
|
|
||||||
%extend sigrok::InputFormat
|
|
||||||
{
|
|
||||||
std::shared_ptr<sigrok::Input> _create_input_kwargs(PyObject *dict)
|
|
||||||
{
|
|
||||||
return $self->create_input(
|
|
||||||
dict_to_map_options(dict, $self->options()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
def _InputFormat_create_input(self, **kwargs):
|
|
||||||
return self._create_input(kwargs)
|
|
||||||
|
|
||||||
InputFormat.create_input = _InputFormat_create_input
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support OutputFormat.create_output() with keyword arguments. */
|
|
||||||
%extend sigrok::OutputFormat
|
|
||||||
{
|
|
||||||
std::shared_ptr<sigrok::Output> _create_output_kwargs(
|
|
||||||
std::shared_ptr<sigrok::Device> device, PyObject *dict)
|
|
||||||
{
|
|
||||||
return $self->create_output(device,
|
|
||||||
dict_to_map_options(dict, $self->options()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
def _OutputFormat_create_output(self, device, **kwargs):
|
|
||||||
return self._create_output_kwargs(device, kwargs)
|
|
||||||
|
|
||||||
OutputFormat.create_output = _OutputFormat_create_output
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support config_set() with Python input types. */
|
|
||||||
%extend sigrok::Configurable
|
|
||||||
{
|
|
||||||
void config_set(const ConfigKey *key, PyObject *input)
|
|
||||||
{
|
|
||||||
$self->config_set(key, python_to_variant_by_key(input, key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return NumPy array from Analog::data(). */
|
|
||||||
%extend sigrok::Analog
|
|
||||||
{
|
|
||||||
PyObject * _data()
|
|
||||||
{
|
|
||||||
int nd = 2;
|
|
||||||
npy_intp dims[2];
|
|
||||||
dims[0] = $self->channels().size();
|
|
||||||
dims[1] = $self->num_samples();
|
|
||||||
int typenum = NPY_FLOAT;
|
|
||||||
void *data = $self->data_pointer();
|
|
||||||
return PyArray_SimpleNewFromData(nd, dims, typenum, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
data = property(_data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return NumPy array from Logic::data(). */
|
|
||||||
%extend sigrok::Logic
|
|
||||||
{
|
|
||||||
PyObject * _data()
|
|
||||||
{
|
|
||||||
npy_intp dims[2];
|
|
||||||
dims[0] = $self->data_length() / $self->unit_size();
|
|
||||||
dims[1] = $self->unit_size();
|
|
||||||
int typenum = NPY_UINT8;
|
|
||||||
void *data = $self->data_pointer();
|
|
||||||
return PyArray_SimpleNewFromData(2, dims, typenum, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
data = property(_data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create logic packet from Python buffer. */
|
|
||||||
%extend sigrok::Context
|
|
||||||
{
|
|
||||||
std::shared_ptr<Packet> _create_logic_packet_buf(PyObject *buf, unsigned int unit_size)
|
|
||||||
{
|
|
||||||
Py_buffer view;
|
|
||||||
PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE);
|
|
||||||
return $self->create_logic_packet(view.buf, view.len, unit_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%pythoncode
|
|
||||||
{
|
|
||||||
def _Context_create_logic_packet(self, buf, unit_size):
|
|
||||||
return self._create_logic_packet_buf(buf, unit_size)
|
|
||||||
|
|
||||||
Context.create_logic_packet = _Context_create_logic_packet
|
|
||||||
}
|
|
||||||
|
|
||||||
%include "doc_end.i"
|
|
||||||
|
|
@ -0,0 +1,352 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
from fractions import Fraction
|
||||||
|
from .lowlevel import *
|
||||||
|
from . import lowlevel
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
__all__ = ['Error', 'Context', 'Driver', 'Device', 'Session', 'Packet', 'Log',
|
||||||
|
'LogLevel', 'PacketType', 'Quantity', 'Unit', 'QuantityFlag', 'ConfigKey']
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return sr_strerror(self.args[0])
|
||||||
|
|
||||||
|
def check(result):
|
||||||
|
if result != SR_OK:
|
||||||
|
raise Error(result)
|
||||||
|
|
||||||
|
def gvariant_to_python(value):
|
||||||
|
type_string = g_variant_get_type_string(value)
|
||||||
|
if type_string == 't':
|
||||||
|
return g_variant_get_uint64(value)
|
||||||
|
if type_string == 'b':
|
||||||
|
return g_variant_get_bool(value)
|
||||||
|
if type_string == 'd':
|
||||||
|
return g_variant_get_double(value)
|
||||||
|
if type_string == 's':
|
||||||
|
return g_variant_get_string(value, None)
|
||||||
|
if type_string == '(tt)':
|
||||||
|
return Fraction(
|
||||||
|
g_variant_get_uint64(g_variant_get_child_value(value, 0)),
|
||||||
|
g_variant_get_uint64(g_variant_get_child_value(value, 1)))
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Can't convert GVariant type '%s' to a Python type." % type_string)
|
||||||
|
|
||||||
|
def python_to_gvariant(value):
|
||||||
|
if isinstance(value, int):
|
||||||
|
return g_variant_new_uint64(value)
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return g_variant_new_boolean(value)
|
||||||
|
if isinstance(value, float):
|
||||||
|
return g_variant_new_double(value)
|
||||||
|
if isinstance(value, str):
|
||||||
|
return g_variant_new_string(value)
|
||||||
|
if isinstance(value, Fraction):
|
||||||
|
array = new_gvariant_ptr_array(2)
|
||||||
|
gvariant_ptr_array_setitem(array, 0,
|
||||||
|
g_variant_new_uint64(value.numerator))
|
||||||
|
gvariant_ptr_array_setitem(array, 1,
|
||||||
|
g_variant_new_uint64(value.denominator))
|
||||||
|
result = g_variant_new_tuple(array, 2)
|
||||||
|
delete_gvariant_ptr_array(array)
|
||||||
|
return result
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Can't convert Python '%s' to a GVariant." % type(value))
|
||||||
|
|
||||||
|
def callback_wrapper(session, callback, device_ptr, packet_ptr):
|
||||||
|
device = session.context._devices[int(device_ptr.this)]
|
||||||
|
packet = Packet(session, packet_ptr)
|
||||||
|
callback(device, packet)
|
||||||
|
|
||||||
|
class Context(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
context_ptr_ptr = new_sr_context_ptr_ptr()
|
||||||
|
check(sr_init(context_ptr_ptr))
|
||||||
|
self.struct = sr_context_ptr_ptr_value(context_ptr_ptr)
|
||||||
|
self._drivers = None
|
||||||
|
self._devices = {}
|
||||||
|
self.session = None
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
sr_exit(self.struct)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def drivers(self):
|
||||||
|
if not self._drivers:
|
||||||
|
self._drivers = {}
|
||||||
|
driver_list = sr_driver_list()
|
||||||
|
for i in itertools.count():
|
||||||
|
driver_ptr = sr_dev_driver_ptr_array_getitem(driver_list, i)
|
||||||
|
if driver_ptr:
|
||||||
|
self._drivers[driver_ptr.name] = Driver(self, driver_ptr)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return self._drivers
|
||||||
|
|
||||||
|
class Driver(object):
|
||||||
|
|
||||||
|
def __init__(self, context, struct):
|
||||||
|
self.context = context
|
||||||
|
self.struct = struct
|
||||||
|
self._initialized = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.struct.name
|
||||||
|
|
||||||
|
def scan(self, **kwargs):
|
||||||
|
if not self._initialized:
|
||||||
|
check(sr_driver_init(self.context.struct, self.struct))
|
||||||
|
self._initialized = True
|
||||||
|
options = []
|
||||||
|
for name, value in kwargs.items():
|
||||||
|
key = getattr(ConfigKey, name.upper())
|
||||||
|
src = sr_config()
|
||||||
|
src.key = key.id
|
||||||
|
src.data = python_to_gvariant(value)
|
||||||
|
options.append(src.this)
|
||||||
|
option_list = python_to_gslist(options)
|
||||||
|
device_list = sr_driver_scan(self.struct, option_list)
|
||||||
|
g_slist_free(option_list)
|
||||||
|
devices = [Device(self, gpointer_to_sr_dev_inst_ptr(ptr))
|
||||||
|
for ptr in gslist_to_python(device_list)]
|
||||||
|
g_slist_free(device_list)
|
||||||
|
return devices
|
||||||
|
|
||||||
|
class Device(object):
|
||||||
|
|
||||||
|
def __new__(cls, driver, struct):
|
||||||
|
address = int(struct.this)
|
||||||
|
if address not in driver.context._devices:
|
||||||
|
device = super(Device, cls).__new__(cls, driver, struct)
|
||||||
|
driver.context._devices[address] = device
|
||||||
|
return driver.context._devices[address]
|
||||||
|
|
||||||
|
def __init__(self, driver, struct):
|
||||||
|
self.driver = driver
|
||||||
|
self.struct = struct
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
key = getattr(ConfigKey, name.upper())
|
||||||
|
data = new_gvariant_ptr_ptr()
|
||||||
|
try:
|
||||||
|
check(sr_config_get(self.driver.struct,
|
||||||
|
key.id, data, self.struct))
|
||||||
|
except Error as error:
|
||||||
|
if error.errno == SR_ERR_NA:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Device does not implement %s" % name)
|
||||||
|
else:
|
||||||
|
raise AttributeError
|
||||||
|
value = gvariant_ptr_ptr_value(data)
|
||||||
|
return gvariant_to_python(value)
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
try:
|
||||||
|
key = getattr(ConfigKey, name.upper())
|
||||||
|
except AttributeError:
|
||||||
|
super(Device, self).__setattr__(name, value)
|
||||||
|
return
|
||||||
|
check(sr_config_set(self.struct,
|
||||||
|
key.id, python_to_gvariant(value)))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vendor(self):
|
||||||
|
return self.struct.vendor
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model(self):
|
||||||
|
return self.struct.model
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version(self):
|
||||||
|
return self.struct.version
|
||||||
|
|
||||||
|
class Session(object):
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
assert context.session is None
|
||||||
|
self.context = context
|
||||||
|
self.struct = sr_session_new()
|
||||||
|
context.session = self
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
check(sr_session_destroy())
|
||||||
|
|
||||||
|
def add_device(self, device):
|
||||||
|
check(sr_session_dev_add(device.struct))
|
||||||
|
|
||||||
|
def open_device(self, device):
|
||||||
|
check(sr_dev_open(device.struct))
|
||||||
|
|
||||||
|
def add_callback(self, callback):
|
||||||
|
wrapper = partial(callback_wrapper, self, callback)
|
||||||
|
check(sr_session_datafeed_python_callback_add(wrapper))
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
check(sr_session_start())
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
check(sr_session_run())
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
check(sr_session_stop())
|
||||||
|
|
||||||
|
class Packet(object):
|
||||||
|
|
||||||
|
def __init__(self, session, struct):
|
||||||
|
self.session = session
|
||||||
|
self.struct = struct
|
||||||
|
self._payload = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return PacketType(self.struct.type)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def payload(self):
|
||||||
|
if self._payload is None:
|
||||||
|
pointer = self.struct.payload
|
||||||
|
if self.type == PacketType.LOGIC:
|
||||||
|
self._payload = Logic(self,
|
||||||
|
void_ptr_to_sr_datafeed_logic_ptr(pointer))
|
||||||
|
elif self.type == PacketType.ANALOG:
|
||||||
|
self._payload = Analog(self,
|
||||||
|
void_ptr_to_sr_datafeed_analog_ptr(pointer))
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"No Python mapping for packet type %ѕ" % self.struct.type)
|
||||||
|
return self._payload
|
||||||
|
|
||||||
|
class Logic(object):
|
||||||
|
|
||||||
|
def __init__(self, packet, struct):
|
||||||
|
self.packet = packet
|
||||||
|
self.struct = struct
|
||||||
|
self._data = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
if self._data is None:
|
||||||
|
self._data = cdata(self.struct.data, self.struct.length)
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
class Analog(object):
|
||||||
|
|
||||||
|
def __init__(self, packet, struct):
|
||||||
|
self.packet = packet
|
||||||
|
self.struct = struct
|
||||||
|
self._data = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def num_samples(self):
|
||||||
|
return self.struct.num_samples
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mq(self):
|
||||||
|
return Quantity(self.struct.mq)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit(self):
|
||||||
|
return Unit(self.struct.unit)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mqflags(self):
|
||||||
|
return QuantityFlag.set_from_mask(self.struct.mqflags)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
if self._data is None:
|
||||||
|
self._data = float_array.frompointer(self.struct.data)
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
class Log(object):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def level(self):
|
||||||
|
return LogLevel(sr_log_loglevel_get())
|
||||||
|
|
||||||
|
@level.setter
|
||||||
|
def level(self, l):
|
||||||
|
check(sr_log_loglevel_set(l.id))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def domain(self):
|
||||||
|
return sr_log_logdomain_get()
|
||||||
|
|
||||||
|
@domain.setter
|
||||||
|
def domain(self, d):
|
||||||
|
check(sr_log_logdomain_set(d))
|
||||||
|
|
||||||
|
class EnumValue(object):
|
||||||
|
|
||||||
|
_enum_values = {}
|
||||||
|
|
||||||
|
def __new__(cls, id):
|
||||||
|
if cls not in cls._enum_values:
|
||||||
|
cls._enum_values[cls] = {}
|
||||||
|
if id not in cls._enum_values[cls]:
|
||||||
|
value = super(EnumValue, cls).__new__(cls)
|
||||||
|
value.id = id
|
||||||
|
cls._enum_values[cls][id] = value
|
||||||
|
return cls._enum_values[cls][id]
|
||||||
|
|
||||||
|
class LogLevel(EnumValue):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PacketType(EnumValue):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Quantity(EnumValue):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Unit(EnumValue):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class QuantityFlag(EnumValue):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_from_mask(cls, mask):
|
||||||
|
result = set()
|
||||||
|
while mask:
|
||||||
|
new_mask = mask & (mask - 1)
|
||||||
|
result.add(cls(mask ^ new_mask))
|
||||||
|
mask = new_mask
|
||||||
|
return result
|
||||||
|
|
||||||
|
class ConfigKey(EnumValue):
|
||||||
|
pass
|
||||||
|
|
||||||
|
for symbol_name in dir(lowlevel):
|
||||||
|
for prefix, cls in [
|
||||||
|
('SR_LOG_', LogLevel),
|
||||||
|
('SR_DF_', PacketType),
|
||||||
|
('SR_MQ_', Quantity),
|
||||||
|
('SR_UNIT_', Unit),
|
||||||
|
('SR_MQFLAG_', QuantityFlag),
|
||||||
|
('SR_CONF_', ConfigKey)]:
|
||||||
|
if symbol_name.startswith(prefix):
|
||||||
|
name = symbol_name[len(prefix):]
|
||||||
|
value = getattr(lowlevel, symbol_name)
|
||||||
|
setattr(cls, name, cls(value))
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
%module lowlevel
|
||||||
|
|
||||||
|
%include "../../../swig/libsigrok.i"
|
||||||
|
|
||||||
|
%{
|
||||||
|
|
||||||
|
void sr_datafeed_python_callback(const struct sr_dev_inst *sdi,
|
||||||
|
const struct sr_datafeed_packet *packet, void *cb_data)
|
||||||
|
{
|
||||||
|
PyObject *sdi_obj;
|
||||||
|
PyObject *packet_obj;
|
||||||
|
PyObject *arglist;
|
||||||
|
PyObject *result;
|
||||||
|
PyGILState_STATE gstate;
|
||||||
|
PyObject *python_callback;
|
||||||
|
|
||||||
|
python_callback = (PyObject *) cb_data;
|
||||||
|
gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
|
sdi_obj = SWIG_NewPointerObj(SWIG_as_voidptr(sdi),
|
||||||
|
SWIGTYPE_p_sr_dev_inst, 0);
|
||||||
|
|
||||||
|
packet_obj = SWIG_NewPointerObj(SWIG_as_voidptr(packet),
|
||||||
|
SWIGTYPE_p_sr_datafeed_packet, 0);
|
||||||
|
|
||||||
|
arglist = Py_BuildValue("(OO)", sdi_obj, packet_obj);
|
||||||
|
|
||||||
|
result = PyEval_CallObject(python_callback, arglist);
|
||||||
|
|
||||||
|
Py_XDECREF(arglist);
|
||||||
|
Py_XDECREF(sdi_obj);
|
||||||
|
Py_XDECREF(packet_obj);
|
||||||
|
Py_XDECREF(result);
|
||||||
|
|
||||||
|
PyGILState_Release(gstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sr_session_datafeed_python_callback_add(PyObject *cb)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!PyCallable_Check(cb))
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
else {
|
||||||
|
ret = sr_session_datafeed_callback_add(
|
||||||
|
sr_datafeed_python_callback, cb);
|
||||||
|
if (ret == SR_OK)
|
||||||
|
Py_XINCREF(cb);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *cdata(const void *data, unsigned long size)
|
||||||
|
{
|
||||||
|
#if PY_MAJOR_VERSION < 3
|
||||||
|
return PyString_FromStringAndSize(data, size);
|
||||||
|
#else
|
||||||
|
return PyBytes_FromStringAndSize(data, size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
GSList *python_to_gslist(PyObject *pylist)
|
||||||
|
{
|
||||||
|
if (PyList_Check(pylist)) {
|
||||||
|
GSList *gslist = NULL;
|
||||||
|
int size = PyList_Size(pylist);
|
||||||
|
int i;
|
||||||
|
for (i = size - 1; i >= 0; i--) {
|
||||||
|
SwigPyObject *o = (SwigPyObject *)PyList_GetItem(pylist, i);
|
||||||
|
void *data = o->ptr;
|
||||||
|
gslist = g_slist_prepend(gslist, data);
|
||||||
|
}
|
||||||
|
return gslist;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *gslist_to_python(GSList *gslist)
|
||||||
|
{
|
||||||
|
PyObject *pylist = PyList_New(0);
|
||||||
|
GSList *l;
|
||||||
|
for (l = gslist; l; l = l->next)
|
||||||
|
PyList_Append(pylist, SWIG_NewPointerObj(l->data, SWIGTYPE_p_void, 0));
|
||||||
|
return pylist;
|
||||||
|
}
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
int sr_session_datafeed_python_callback_add(PyObject *cb);
|
||||||
|
|
||||||
|
PyObject *cdata(const void *data, unsigned long size);
|
||||||
|
|
||||||
|
GSList *python_to_gslist(PyObject *pylist);
|
||||||
|
PyObject *gslist_to_python(GSList *gslist);
|
||||||
|
|
@ -1,429 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2016 Aurelien Jacobs <aurel@gnuage.org>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
%define DOCSTRING
|
|
||||||
"
|
|
||||||
= Introduction
|
|
||||||
|
|
||||||
The sigrok Ruby API provides an object-oriented Ruby interface to the
|
|
||||||
functionality in libsigrok. It is built on top of the libsigrokcxx C++ API.
|
|
||||||
|
|
||||||
= Getting started
|
|
||||||
|
|
||||||
Usage of the sigrok Ruby API needs to begin with a call to Context.create().
|
|
||||||
This will create the global libsigrok context and returns a Context object.
|
|
||||||
Methods on this object provide access to the hardware drivers, input and output
|
|
||||||
formats supported by the library, as well as means of creating other objects
|
|
||||||
such as sessions and triggers.
|
|
||||||
|
|
||||||
= Error handling
|
|
||||||
|
|
||||||
When any libsigrok C API call returns an error, an Error exception is raised,
|
|
||||||
which provides access to the error code and description."
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
%module(docstring=DOCSTRING) sigrok
|
|
||||||
|
|
||||||
%{
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <glibmm.h>
|
|
||||||
%}
|
|
||||||
|
|
||||||
%include "../swig/templates.i"
|
|
||||||
|
|
||||||
%{
|
|
||||||
static const char *string_from_ruby(VALUE obj)
|
|
||||||
{
|
|
||||||
switch (TYPE(obj)) {
|
|
||||||
case T_STRING:
|
|
||||||
return StringValueCStr(obj);
|
|
||||||
case T_SYMBOL:
|
|
||||||
return rb_id2name(SYM2ID(obj));
|
|
||||||
default:
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert from Glib::Variant to native Ruby types. */
|
|
||||||
static VALUE variant_to_ruby(Glib::VariantBase variant)
|
|
||||||
{
|
|
||||||
if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL)) {
|
|
||||||
return Glib::VariantBase::cast_dynamic< Glib::Variant<bool> >(variant).get() ? Qtrue : Qfalse;
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_BYTE)) {
|
|
||||||
return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<unsigned char> >(variant).get());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_INT16)) {
|
|
||||||
return INT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint16> >(variant).get());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT16)) {
|
|
||||||
return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint16> >(variant).get());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32)) {
|
|
||||||
return INT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint32> >(variant).get());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) {
|
|
||||||
return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint32> >(variant).get());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_INT64)) {
|
|
||||||
return LL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint64> >(variant).get());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64)) {
|
|
||||||
return ULL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint64> >(variant).get());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE)) {
|
|
||||||
return DBL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<double> >(variant).get());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING)) {
|
|
||||||
auto str = Glib::VariantBase::cast_dynamic< Glib::Variant<std::string> >(variant).get();
|
|
||||||
return rb_str_new(str.c_str(), str.length());
|
|
||||||
} else if (variant.is_of_type(Glib::VARIANT_TYPE_VARIANT)) {
|
|
||||||
auto var = Glib::VariantBase::cast_dynamic< Glib::Variant<Glib::VariantBase> >(variant).get();
|
|
||||||
return variant_to_ruby(var);
|
|
||||||
} else if (variant.is_container()) {
|
|
||||||
Glib::VariantContainerBase container = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase > (variant);
|
|
||||||
gsize count = container.get_n_children();
|
|
||||||
if (container.is_of_type(Glib::VARIANT_TYPE_DICTIONARY)) {
|
|
||||||
VALUE hash = rb_hash_new();
|
|
||||||
for (gsize i = 0; i < count; i++) {
|
|
||||||
Glib::VariantContainerBase entry = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase > (container.get_child(i));
|
|
||||||
VALUE key = variant_to_ruby(entry.get_child(0));
|
|
||||||
VALUE val = variant_to_ruby(entry.get_child(1));
|
|
||||||
rb_hash_aset(hash, key, val);
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
} else if (container.is_of_type(Glib::VARIANT_TYPE_ARRAY) ||
|
|
||||||
container.is_of_type(Glib::VARIANT_TYPE_TUPLE)) {
|
|
||||||
VALUE array = rb_ary_new2(count);
|
|
||||||
for (gsize i = 0; i < count; i++) {
|
|
||||||
VALUE val = variant_to_ruby(container.get_child(i));
|
|
||||||
rb_ary_store(array, i, val);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SWIG_exception(SWIG_TypeError, ("TODO: GVariant(" + variant.get_type().get_string() + ") -> Ruby").c_str());
|
|
||||||
}
|
|
||||||
return 0; /* Dummy, to avoid a compiler warning. */
|
|
||||||
}
|
|
||||||
%}
|
|
||||||
|
|
||||||
/* Map from Glib::Variant to native Ruby types. */
|
|
||||||
%typemap(out) Glib::VariantBase {
|
|
||||||
$result = variant_to_ruby($1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map from Glib::VariantContainer to native Ruby types. */
|
|
||||||
%typemap(out) Glib::VariantContainerBase {
|
|
||||||
$result = variant_to_ruby($1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map from callable Ruby Proc to LogCallbackFunction */
|
|
||||||
%typemap(in) sigrok::LogCallbackFunction {
|
|
||||||
if (!rb_obj_is_proc($input))
|
|
||||||
SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
|
|
||||||
|
|
||||||
std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
|
|
||||||
rb_gc_register_address(proc.get());
|
|
||||||
|
|
||||||
$1 = [=] (const sigrok::LogLevel *loglevel, std::string message) {
|
|
||||||
VALUE log_obj = SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0);
|
|
||||||
|
|
||||||
VALUE string_obj = rb_external_str_new_with_enc(message.c_str(), message.length(), rb_utf8_encoding());
|
|
||||||
|
|
||||||
VALUE args = rb_ary_new3(2, log_obj, string_obj);
|
|
||||||
rb_proc_call(*proc.get(), args);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map from callable Ruby Proc to SessionStoppedCallback */
|
|
||||||
%typemap(in) sigrok::SessionStoppedCallback {
|
|
||||||
if (!rb_obj_is_proc($input))
|
|
||||||
SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
|
|
||||||
|
|
||||||
std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
|
|
||||||
rb_gc_register_address(proc.get());
|
|
||||||
|
|
||||||
$1 = [=] () {
|
|
||||||
rb_proc_call(*proc.get(), rb_ary_new());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map from callable Ruby Proc to DatafeedCallbackFunction */
|
|
||||||
%typemap(in) sigrok::DatafeedCallbackFunction {
|
|
||||||
if (!rb_obj_is_proc($input))
|
|
||||||
SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
|
|
||||||
|
|
||||||
std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
|
|
||||||
rb_gc_register_address(proc.get());
|
|
||||||
|
|
||||||
$1 = [=] (std::shared_ptr<sigrok::Device> device,
|
|
||||||
std::shared_ptr<sigrok::Packet> packet) {
|
|
||||||
VALUE device_obj = SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(new std::shared_ptr<sigrok::Device>(device)),
|
|
||||||
SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN);
|
|
||||||
|
|
||||||
VALUE packet_obj = SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(new std::shared_ptr<sigrok::Packet>(packet)),
|
|
||||||
SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN);
|
|
||||||
|
|
||||||
VALUE args = rb_ary_new3(2, device_obj, packet_obj);
|
|
||||||
rb_proc_call(*proc.get(), args);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cast PacketPayload pointers to correct subclass type. */
|
|
||||||
%ignore sigrok::Packet::payload;
|
|
||||||
%rename sigrok::Packet::_payload payload;
|
|
||||||
%extend sigrok::Packet
|
|
||||||
{
|
|
||||||
VALUE _payload()
|
|
||||||
{
|
|
||||||
if ($self->type() == sigrok::PacketType::HEADER) {
|
|
||||||
return SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(new std::shared_ptr<sigrok::Header>(dynamic_pointer_cast<sigrok::Header>($self->payload()))),
|
|
||||||
SWIGTYPE_p_std__shared_ptrT_sigrok__Header_t, SWIG_POINTER_OWN);
|
|
||||||
} else if ($self->type() == sigrok::PacketType::META) {
|
|
||||||
return SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(new std::shared_ptr<sigrok::Meta>(dynamic_pointer_cast<sigrok::Meta>($self->payload()))),
|
|
||||||
SWIGTYPE_p_std__shared_ptrT_sigrok__Meta_t, SWIG_POINTER_OWN);
|
|
||||||
} else if ($self->type() == sigrok::PacketType::ANALOG) {
|
|
||||||
return SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(new std::shared_ptr<sigrok::Analog>(dynamic_pointer_cast<sigrok::Analog>($self->payload()))),
|
|
||||||
SWIGTYPE_p_std__shared_ptrT_sigrok__Analog_t, SWIG_POINTER_OWN);
|
|
||||||
} else if ($self->type() == sigrok::PacketType::LOGIC) {
|
|
||||||
return SWIG_NewPointerObj(
|
|
||||||
SWIG_as_voidptr(new std::shared_ptr<sigrok::Logic>(dynamic_pointer_cast<sigrok::Logic>($self->payload()))),
|
|
||||||
SWIGTYPE_p_std__shared_ptrT_sigrok__Logic_t, SWIG_POINTER_OWN);
|
|
||||||
} else {
|
|
||||||
return Qnil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%{
|
|
||||||
|
|
||||||
#include "libsigrokcxx/libsigrokcxx.hpp"
|
|
||||||
|
|
||||||
/* Convert from a Ruby type to Glib::Variant, according to config key data type. */
|
|
||||||
Glib::VariantBase ruby_to_variant_by_key(VALUE input, const sigrok::ConfigKey *key)
|
|
||||||
{
|
|
||||||
enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
|
|
||||||
|
|
||||||
if (type == SR_T_UINT64 && RB_TYPE_P(input, T_FIXNUM))
|
|
||||||
return Glib::Variant<guint64>::create(NUM2ULL(input));
|
|
||||||
if (type == SR_T_UINT64 && RB_TYPE_P(input, T_BIGNUM))
|
|
||||||
return Glib::Variant<guint64>::create(NUM2ULL(input));
|
|
||||||
else if (type == SR_T_STRING && RB_TYPE_P(input, T_STRING))
|
|
||||||
return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
|
|
||||||
else if (type == SR_T_STRING && RB_TYPE_P(input, T_SYMBOL))
|
|
||||||
return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
|
|
||||||
else if (type == SR_T_BOOL && RB_TYPE_P(input, T_TRUE))
|
|
||||||
return Glib::Variant<bool>::create(true);
|
|
||||||
else if (type == SR_T_BOOL && RB_TYPE_P(input, T_FALSE))
|
|
||||||
return Glib::Variant<bool>::create(false);
|
|
||||||
else if (type == SR_T_FLOAT && RB_TYPE_P(input, T_FLOAT))
|
|
||||||
return Glib::Variant<double>::create(RFLOAT_VALUE(input));
|
|
||||||
else if (type == SR_T_INT32 && RB_TYPE_P(input, T_FIXNUM))
|
|
||||||
return Glib::Variant<gint32>::create(NUM2INT(input));
|
|
||||||
else
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert from a Ruby type to Glib::Variant, according to Option data type. */
|
|
||||||
Glib::VariantBase ruby_to_variant_by_option(VALUE input, std::shared_ptr<sigrok::Option> option)
|
|
||||||
{
|
|
||||||
Glib::VariantBase variant = option->default_value();
|
|
||||||
|
|
||||||
if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_FIXNUM))
|
|
||||||
return Glib::Variant<guint64>::create(NUM2ULL(input));
|
|
||||||
else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_BIGNUM))
|
|
||||||
return Glib::Variant<guint64>::create(NUM2ULL(input));
|
|
||||||
else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_STRING))
|
|
||||||
return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
|
|
||||||
else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_SYMBOL))
|
|
||||||
return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
|
|
||||||
else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_TRUE))
|
|
||||||
return Glib::Variant<bool>::create(true);
|
|
||||||
else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_FALSE))
|
|
||||||
return Glib::Variant<bool>::create(false);
|
|
||||||
else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE) && RB_TYPE_P(input, T_FLOAT))
|
|
||||||
return Glib::Variant<double>::create(RFLOAT_VALUE(input));
|
|
||||||
else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32) && RB_TYPE_P(input, T_FIXNUM))
|
|
||||||
return Glib::Variant<gint32>::create(NUM2INT(input));
|
|
||||||
else
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct hash_to_map_options_params {
|
|
||||||
std::map<std::string, std::shared_ptr<sigrok::Option> > options;
|
|
||||||
std::map<std::string, Glib::VariantBase> output;
|
|
||||||
};
|
|
||||||
|
|
||||||
int convert_option(VALUE key, VALUE val, VALUE in) {
|
|
||||||
struct hash_to_map_options_params *params;
|
|
||||||
params = (struct hash_to_map_options_params *)in;
|
|
||||||
|
|
||||||
auto k = string_from_ruby(key);
|
|
||||||
auto v = ruby_to_variant_by_option(val, params->options[k]);
|
|
||||||
params->output[k] = v;
|
|
||||||
|
|
||||||
return ST_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert from a Ruby hash to a std::map<std::string, Glib::VariantBase> */
|
|
||||||
std::map<std::string, Glib::VariantBase> hash_to_map_options(VALUE hash,
|
|
||||||
std::map<std::string, std::shared_ptr<sigrok::Option> > options)
|
|
||||||
{
|
|
||||||
if (!RB_TYPE_P(hash, T_HASH))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
|
|
||||||
struct hash_to_map_options_params params = { options };
|
|
||||||
rb_hash_foreach(hash, (int (*)(ANYARGS))convert_option, (VALUE)¶ms);
|
|
||||||
|
|
||||||
return params.output;
|
|
||||||
}
|
|
||||||
|
|
||||||
int convert_option_by_key(VALUE key, VALUE val, VALUE in) {
|
|
||||||
std::map<const sigrok::ConfigKey *, Glib::VariantBase> *options;
|
|
||||||
options = (std::map<const sigrok::ConfigKey *, Glib::VariantBase> *)in;
|
|
||||||
|
|
||||||
auto k = sigrok::ConfigKey::get_by_identifier(string_from_ruby(key));
|
|
||||||
auto v = ruby_to_variant_by_key(val, k);
|
|
||||||
(*options)[k] = v;
|
|
||||||
|
|
||||||
return ST_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
%}
|
|
||||||
|
|
||||||
/* Ignore these methods, we will override them below. */
|
|
||||||
%ignore sigrok::Analog::data;
|
|
||||||
%ignore sigrok::Driver::scan;
|
|
||||||
%ignore sigrok::Input::send;
|
|
||||||
%ignore sigrok::InputFormat::create_input;
|
|
||||||
%ignore sigrok::OutputFormat::create_output;
|
|
||||||
|
|
||||||
%include "doc.i"
|
|
||||||
|
|
||||||
%define %attributevector(Class, Type, Name, Get)
|
|
||||||
%alias sigrok::Class::_ ## Get #Name;
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
%define %attributemap(Class, Type, Name, Get)
|
|
||||||
%alias sigrok::Class::_ ## Get #Name;
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
%define %enumextras(Class)
|
|
||||||
%extend sigrok::Class
|
|
||||||
{
|
|
||||||
VALUE to_s()
|
|
||||||
{
|
|
||||||
std::string str = $self->name();
|
|
||||||
return rb_external_str_new_with_enc(str.c_str(), str.length(), rb_utf8_encoding());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(void *other)
|
|
||||||
{
|
|
||||||
return (long) $self == (long) other;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
%enddef
|
|
||||||
|
|
||||||
%include "../swig/classes.i"
|
|
||||||
|
|
||||||
/* Replace the original Driver.scan with a keyword arguments version. */
|
|
||||||
%rename sigrok::Driver::_scan scan;
|
|
||||||
%extend sigrok::Driver
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan(VALUE kwargs = rb_hash_new())
|
|
||||||
{
|
|
||||||
if (!RB_TYPE_P(kwargs, T_HASH))
|
|
||||||
throw sigrok::Error(SR_ERR_ARG);
|
|
||||||
|
|
||||||
std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
|
|
||||||
rb_hash_foreach(kwargs, (int (*)(ANYARGS))convert_option_by_key, (VALUE)&options);
|
|
||||||
|
|
||||||
return $self->scan(options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support Input.send() with string argument. */
|
|
||||||
%rename sigrok::Input::_send send;
|
|
||||||
%extend sigrok::Input
|
|
||||||
{
|
|
||||||
void _send(VALUE data)
|
|
||||||
{
|
|
||||||
data = StringValue(data);
|
|
||||||
return $self->send(RSTRING_PTR(data), RSTRING_LEN(data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support InputFormat.create_input() with keyword arguments. */
|
|
||||||
%rename sigrok::InputFormat::_create_input create_input;
|
|
||||||
%extend sigrok::InputFormat
|
|
||||||
{
|
|
||||||
std::shared_ptr<sigrok::Input> _create_input(VALUE hash = rb_hash_new())
|
|
||||||
{
|
|
||||||
return $self->create_input(hash_to_map_options(hash, $self->options()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support OutputFormat.create_output() with keyword arguments. */
|
|
||||||
%rename sigrok::OutputFormat::_create_output create_output;
|
|
||||||
%extend sigrok::OutputFormat
|
|
||||||
{
|
|
||||||
std::shared_ptr<sigrok::Output> _create_output(
|
|
||||||
std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
|
|
||||||
{
|
|
||||||
return $self->create_output(device,
|
|
||||||
hash_to_map_options(hash, $self->options()));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<sigrok::Output> _create_output(string filename,
|
|
||||||
std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
|
|
||||||
{
|
|
||||||
return $self->create_output(filename, device,
|
|
||||||
hash_to_map_options(hash, $self->options()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support config_set() with Ruby input types. */
|
|
||||||
%extend sigrok::Configurable
|
|
||||||
{
|
|
||||||
void config_set(const ConfigKey *key, VALUE input)
|
|
||||||
{
|
|
||||||
$self->config_set(key, ruby_to_variant_by_key(input, key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return Ruby array from Analog::data(). */
|
|
||||||
%rename sigrok::Analog::_data data;
|
|
||||||
%extend sigrok::Analog
|
|
||||||
{
|
|
||||||
VALUE _data()
|
|
||||||
{
|
|
||||||
int num_channels = $self->channels().size();
|
|
||||||
int num_samples = $self->num_samples();
|
|
||||||
float *data = (float *) $self->data_pointer();
|
|
||||||
VALUE channels = rb_ary_new2(num_channels);
|
|
||||||
for(int i = 0; i < num_channels; i++) {
|
|
||||||
VALUE samples = rb_ary_new2(num_samples);
|
|
||||||
for (int j = 0; j < num_samples; j++) {
|
|
||||||
rb_ary_store(samples, j, DBL2NUM(data[i*num_samples+j]));
|
|
||||||
}
|
|
||||||
rb_ary_store(channels, i, samples);
|
|
||||||
}
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
##
|
##
|
||||||
## This file is part of the libsigrok project.
|
## This file is part of the libsigrok project.
|
||||||
##
|
##
|
||||||
## Copyright (C) 2017 Stefan Bruens <stefan.bruens@rwth-aachen.de>
|
## Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
##
|
##
|
||||||
## 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
|
||||||
|
|
@ -14,18 +14,17 @@
|
||||||
## GNU General Public License for more details.
|
## GNU General Public License for more details.
|
||||||
##
|
##
|
||||||
## You should have received a copy of the GNU General Public License
|
## You should have received a copy of the GNU General Public License
|
||||||
## along with this program; if not, see <http://www.gnu.org/licenses/>.
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
##
|
##
|
||||||
|
|
||||||
# Grant access permissions to users who are in the "plugdev" group.
|
require 'mkmf'
|
||||||
# "plugdev" is typically used on Debian based distributions and may not
|
|
||||||
# exist elsewhere.
|
|
||||||
#
|
|
||||||
# This file, when installed, must be installed with a name lexicographically
|
|
||||||
# sorted later than the accompanied 60-libsigrok.rules
|
|
||||||
|
|
||||||
ACTION!="add|change", GOTO="libsigrok_rules_plugdev_end"
|
$CFLAGS += " " + `pkg-config --cflags libsigrok`.strip
|
||||||
|
$LDFLAGS += " " + `pkg-config --libs libsigrok`.strip
|
||||||
|
|
||||||
ENV{ID_SIGROK}=="1", MODE="660", GROUP="plugdev"
|
have_library("sigrok", "sr_init") or exit(false)
|
||||||
|
find_header("libsigrok/libsigrok.h") or exit(false)
|
||||||
|
|
||||||
|
create_makefile('sigrok_lowlevel')
|
||||||
|
|
||||||
LABEL="libsigrok_rules_plugdev_end"
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the libsigrok project.
|
* This file is part of the libsigrok project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
|
* Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
*
|
*
|
||||||
* 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
|
||||||
|
|
@ -14,26 +14,11 @@
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LIBSIGROK_HARDWARE_TONDAJ_SL_814_PROTOCOL_H
|
%module sigrok_lowlevel
|
||||||
#define LIBSIGROK_HARDWARE_TONDAJ_SL_814_PROTOCOL_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
%include "../swig/libsigrok.i"
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
|
|
||||||
#define LOG_PREFIX "tondaj-sl-814"
|
|
||||||
|
|
||||||
struct dev_context {
|
|
||||||
struct sr_sw_limits limits;
|
|
||||||
int state;
|
|
||||||
|
|
||||||
uint8_t buf[4];
|
|
||||||
uint8_t buflen;
|
|
||||||
};
|
|
||||||
|
|
||||||
SR_PRIV int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,220 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Martin Ling <martin-sigrok@earth.li>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma SWIG nowarn=325,401
|
|
||||||
|
|
||||||
%include "typemaps.i"
|
|
||||||
%include "exception.i"
|
|
||||||
|
|
||||||
%{
|
|
||||||
|
|
||||||
static int swig_exception_code(int sigrok_exception_code) {
|
|
||||||
switch (sigrok_exception_code) {
|
|
||||||
case SR_ERR_MALLOC:
|
|
||||||
return SWIG_MemoryError;
|
|
||||||
case SR_ERR_ARG:
|
|
||||||
return SWIG_ValueError;
|
|
||||||
default:
|
|
||||||
return SWIG_RuntimeError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%}
|
|
||||||
|
|
||||||
%exception {
|
|
||||||
try {
|
|
||||||
$action
|
|
||||||
} catch (sigrok::Error &e) {
|
|
||||||
SWIG_exception(swig_exception_code(e.result),
|
|
||||||
const_cast<char*>(e.what()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template< class T > class enable_shared_from_this;
|
|
||||||
|
|
||||||
%template(ContextShared) enable_shared_from_this<Context>;
|
|
||||||
|
|
||||||
%shared_ptr(sigrok::Context);
|
|
||||||
%shared_ptr(sigrok::Driver);
|
|
||||||
%shared_ptr(sigrok::Device);
|
|
||||||
%shared_ptr(sigrok::Configurable);
|
|
||||||
%shared_ptr(sigrok::HardwareDevice);
|
|
||||||
%shared_ptr(sigrok::Channel);
|
|
||||||
%shared_ptr(sigrok::ChannelGroup);
|
|
||||||
%shared_ptr(sigrok::Session);
|
|
||||||
%shared_ptr(sigrok::SessionDevice);
|
|
||||||
%shared_ptr(sigrok::Packet);
|
|
||||||
%shared_ptr(sigrok::PacketPayload);
|
|
||||||
%shared_ptr(sigrok::Header);
|
|
||||||
%shared_ptr(sigrok::Meta);
|
|
||||||
%shared_ptr(sigrok::Analog);
|
|
||||||
%shared_ptr(sigrok::Logic);
|
|
||||||
%shared_ptr(sigrok::InputFormat);
|
|
||||||
%shared_ptr(sigrok::Input);
|
|
||||||
%shared_ptr(sigrok::InputDevice);
|
|
||||||
%shared_ptr(sigrok::Option);
|
|
||||||
%shared_ptr(sigrok::OutputFormat);
|
|
||||||
%shared_ptr(sigrok::Output);
|
|
||||||
%shared_ptr(sigrok::Trigger);
|
|
||||||
%shared_ptr(sigrok::TriggerStage);
|
|
||||||
%shared_ptr(sigrok::TriggerMatch);
|
|
||||||
%shared_ptr(sigrok::UserDevice);
|
|
||||||
|
|
||||||
#define SR_API
|
|
||||||
#define SR_PRIV
|
|
||||||
|
|
||||||
%ignore sigrok::DatafeedCallbackData;
|
|
||||||
|
|
||||||
#ifndef SWIGJAVA
|
|
||||||
|
|
||||||
#define SWIG_ATTRIBUTE_TEMPLATE
|
|
||||||
|
|
||||||
%include "attribute.i"
|
|
||||||
|
|
||||||
%inline {
|
|
||||||
typedef std::map<std::string, std::shared_ptr<sigrok::Driver> >
|
|
||||||
map_string_Driver;
|
|
||||||
typedef std::map<std::string, std::shared_ptr<sigrok::InputFormat> >
|
|
||||||
map_string_InputFormat;
|
|
||||||
typedef std::map<std::string, std::shared_ptr<sigrok::OutputFormat> >
|
|
||||||
map_string_OutputFormat;
|
|
||||||
typedef std::map<std::string, std::shared_ptr<sigrok::ChannelGroup> >
|
|
||||||
map_string_ChannelGroup;
|
|
||||||
typedef std::map<std::string, std::shared_ptr<sigrok::Option> >
|
|
||||||
map_string_Option;
|
|
||||||
typedef std::map<std::string, Glib::VariantBase>
|
|
||||||
map_string_Variant;
|
|
||||||
typedef std::map<const sigrok::ConfigKey *, Glib::VariantBase>
|
|
||||||
map_ConfigKey_Variant;
|
|
||||||
}
|
|
||||||
|
|
||||||
%attributemap(Context,
|
|
||||||
map_string_Driver, drivers, drivers);
|
|
||||||
%attributemap(Context,
|
|
||||||
map_string_InputFormat, input_formats, input_formats);
|
|
||||||
%attributemap(Context,
|
|
||||||
map_string_OutputFormat, output_formats, output_formats);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Context,
|
|
||||||
std::string, package_version, package_version);
|
|
||||||
%attributestring(sigrok::Context,
|
|
||||||
std::string, lib_version, lib_version);
|
|
||||||
|
|
||||||
%attribute(sigrok::Context,
|
|
||||||
const sigrok::LogLevel *, log_level, log_level, set_log_level);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Driver, std::string, name, name);
|
|
||||||
%attributestring(sigrok::Driver, std::string, long_name, long_name);
|
|
||||||
|
|
||||||
%attributestring(sigrok::InputFormat,
|
|
||||||
std::string, name, name);
|
|
||||||
%attributestring(sigrok::InputFormat,
|
|
||||||
std::string, description, description);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Input,
|
|
||||||
std::shared_ptr<sigrok::InputDevice>, device, device);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Option,
|
|
||||||
std::string, id, id);
|
|
||||||
%attributestring(sigrok::Option,
|
|
||||||
std::string, name, name);
|
|
||||||
%attributestring(sigrok::Option,
|
|
||||||
std::string, description, description);
|
|
||||||
/* Currently broken on Python due to some issue with variant typemaps. */
|
|
||||||
/* %attributevector(Option,
|
|
||||||
Glib::VariantBase, default_value, default_value); */
|
|
||||||
%attributevector(Option,
|
|
||||||
std::vector<Glib::VariantBase>, values, values);
|
|
||||||
|
|
||||||
%attributestring(sigrok::OutputFormat,
|
|
||||||
std::string, name, name);
|
|
||||||
%attributestring(sigrok::OutputFormat,
|
|
||||||
std::string, description, description);
|
|
||||||
%attributemap(OutputFormat,
|
|
||||||
map_string_Option, options, options);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Device, std::string, vendor, vendor);
|
|
||||||
%attributestring(sigrok::Device, std::string, model, model);
|
|
||||||
%attributestring(sigrok::Device, std::string, version, version);
|
|
||||||
|
|
||||||
%attributevector(Device,
|
|
||||||
std::vector<std::shared_ptr<sigrok::Channel> >,
|
|
||||||
channels, channels);
|
|
||||||
|
|
||||||
%attributemap(Device, map_string_ChannelGroup,
|
|
||||||
channel_groups, channel_groups);
|
|
||||||
|
|
||||||
/* Using %attributestring for shared_ptr attribute. See
|
|
||||||
http://sourceforge.net/p/swig/mailman/message/31832070/ */
|
|
||||||
%attributestring(sigrok::HardwareDevice,
|
|
||||||
std::shared_ptr<sigrok::Driver>, driver, driver);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Channel, std::string, name, name, set_name);
|
|
||||||
%attribute(sigrok::Channel, bool, enabled, enabled, set_enabled);
|
|
||||||
%attribute(sigrok::Channel, const sigrok::ChannelType *, type, type);
|
|
||||||
%attribute(sigrok::Channel, unsigned int, index, index);
|
|
||||||
|
|
||||||
%attributestring(sigrok::ChannelGroup, std::string, name, name);
|
|
||||||
%attributevector(ChannelGroup,
|
|
||||||
std::vector<std::shared_ptr<sigrok::Channel> >,
|
|
||||||
channels, channels);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Trigger, std::string, name, name);
|
|
||||||
%attributevector(Trigger,
|
|
||||||
std::vector<std::shared_ptr<sigrok::TriggerStage> >,
|
|
||||||
stages, stages);
|
|
||||||
|
|
||||||
%attribute(sigrok::TriggerStage, int, number, number);
|
|
||||||
%attributevector(TriggerStage,
|
|
||||||
std::vector<std::shared_ptr<sigrok::TriggerMatch> >,
|
|
||||||
matches, matches);
|
|
||||||
|
|
||||||
%attributestring(sigrok::TriggerMatch,
|
|
||||||
std::shared_ptr<sigrok::Channel>, channel, channel);
|
|
||||||
%attribute(sigrok::TriggerMatch, const sigrok::TriggerMatchType *, type, type);
|
|
||||||
%attribute(sigrok::TriggerMatch, float, value, value);
|
|
||||||
|
|
||||||
%attributevector(Session,
|
|
||||||
std::vector<std::shared_ptr<sigrok::Device> >,
|
|
||||||
devices, devices);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Session,
|
|
||||||
std::shared_ptr<sigrok::Trigger>, trigger, trigger, set_trigger);
|
|
||||||
|
|
||||||
%attributestring(sigrok::Session, std::string, filename, filename);
|
|
||||||
|
|
||||||
%attribute(sigrok::Packet,
|
|
||||||
const sigrok::PacketType *, type, type);
|
|
||||||
|
|
||||||
%attributemap(Meta, map_ConfigKey_Variant, config, config);
|
|
||||||
|
|
||||||
%attributevector(Analog,
|
|
||||||
std::vector<std::shared_ptr<sigrok::Channel> >, channels, channels);
|
|
||||||
%attribute(sigrok::Analog, int, num_samples, num_samples);
|
|
||||||
%attribute(sigrok::Analog, const sigrok::Quantity *, mq, mq);
|
|
||||||
%attribute(sigrok::Analog, const sigrok::Unit *, unit, unit);
|
|
||||||
%attributevector(Analog, std::vector<const sigrok::QuantityFlag *>, mq_flags, mq_flags);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
%include <libsigrokcxx/libsigrokcxx.hpp>
|
|
||||||
|
|
||||||
%include "swig/enums.i"
|
|
||||||
|
|
||||||
%include <libsigrokcxx/enums.hpp>
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
##
|
|
||||||
## This file is part of the libsigrok project.
|
|
||||||
##
|
|
||||||
## Copyright (C) 2014 Martin Ling <martin-sigrok@earth.li>
|
|
||||||
##
|
|
||||||
## This program is free software: you can redistribute it and/or modify
|
|
||||||
## it under the terms of the GNU General Public License as published by
|
|
||||||
## the Free Software Foundation, either version 3 of the License, or
|
|
||||||
## (at your option) any later version.
|
|
||||||
##
|
|
||||||
## This program is distributed in the hope that it will be useful,
|
|
||||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
## GNU General Public License for more details.
|
|
||||||
##
|
|
||||||
## You should have received a copy of the GNU General Public License
|
|
||||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
##
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
from xml.etree import ElementTree
|
|
||||||
import sys, os
|
|
||||||
|
|
||||||
language, input_file = sys.argv[1:3]
|
|
||||||
if len(sys.argv) == 4:
|
|
||||||
mode = sys.argv[3]
|
|
||||||
input_dir = os.path.dirname(input_file)
|
|
||||||
|
|
||||||
index = ElementTree.parse(input_file)
|
|
||||||
|
|
||||||
def get_text(node):
|
|
||||||
paras = node.findall('para')
|
|
||||||
return str.join('\n\n', [("".join(l)).rstrip() for l in [list(p.itertext()) for p in paras] if l])
|
|
||||||
|
|
||||||
for compound in index.findall('compound'):
|
|
||||||
if compound.attrib['kind'] != 'class':
|
|
||||||
continue
|
|
||||||
class_name = compound.find('name').text
|
|
||||||
if not class_name.startswith('sigrok::'):
|
|
||||||
continue
|
|
||||||
trimmed_name = class_name.split('::')[1]
|
|
||||||
doc = ElementTree.parse("%s/%s.xml" % (input_dir, compound.attrib['refid']))
|
|
||||||
cls = doc.find('compounddef')
|
|
||||||
brief = get_text(cls.find('briefdescription'))
|
|
||||||
if brief:
|
|
||||||
if language == 'python':
|
|
||||||
print('%%feature("docstring") %s "%s";' % (class_name, brief))
|
|
||||||
elif language == 'ruby':
|
|
||||||
print('%%feature("docstring") %s "Document-class: %s\\n%s\\n";' % (class_name, class_name.replace("sigrok", "Sigrok", 1), brief))
|
|
||||||
elif language == 'java':
|
|
||||||
print('%%typemap(javaclassmodifiers) %s "/** %s */\npublic class"' % (
|
|
||||||
class_name, brief))
|
|
||||||
constants = []
|
|
||||||
for section in cls.findall('sectiondef'):
|
|
||||||
kind = section.attrib['kind']
|
|
||||||
if kind not in ('public-func', 'public-static-attrib'):
|
|
||||||
continue
|
|
||||||
for member in section.findall('memberdef'):
|
|
||||||
member_name = member.find('name').text
|
|
||||||
brief = get_text(member.find('briefdescription')).replace('"', '\\"')
|
|
||||||
parameters = {}
|
|
||||||
for para in member.find('detaileddescription').findall('para'):
|
|
||||||
paramlist = para.find('parameterlist')
|
|
||||||
if paramlist is not None:
|
|
||||||
for param in paramlist.findall('parameteritem'):
|
|
||||||
namelist = param.find('parameternamelist')
|
|
||||||
name = namelist.find('parametername').text
|
|
||||||
description = get_text(param.find('parameterdescription'))
|
|
||||||
if description:
|
|
||||||
parameters[name] = description
|
|
||||||
if brief:
|
|
||||||
if language == 'python' and kind == 'public-func':
|
|
||||||
print(str.join('\n', [
|
|
||||||
'%%feature("docstring") %s::%s "%s' % (
|
|
||||||
class_name, member_name, brief)] + [
|
|
||||||
'@param %s %s' % (name, desc)
|
|
||||||
for name, desc in parameters.items()]) + '";')
|
|
||||||
if language == 'ruby' and kind == 'public-func':
|
|
||||||
print(str.join('\n', [
|
|
||||||
'%%feature("docstring") %s::%s "%s' % (
|
|
||||||
class_name, member_name, brief)] + [
|
|
||||||
'@param %s %s' % (name, desc)
|
|
||||||
for name, desc in parameters.items()]) + '\\n";')
|
|
||||||
elif language == 'java' and kind == 'public-func':
|
|
||||||
print(str.join('\n', [
|
|
||||||
'%%javamethodmodifiers %s::%s "/** %s' % (
|
|
||||||
class_name, member_name, brief)] + [
|
|
||||||
' * @param %s %s' % (name, desc)
|
|
||||||
for name, desc in parameters.items()])
|
|
||||||
+ ' */\npublic"')
|
|
||||||
elif kind == 'public-static-attrib':
|
|
||||||
constants.append((member_name, brief))
|
|
||||||
if language == 'java' and constants:
|
|
||||||
print('%%typemap(javacode) %s %%{' % class_name)
|
|
||||||
for member_name, brief in constants:
|
|
||||||
print(' /** %s */\n public static final %s %s = new %s(classesJNI.%s_%s_get(), false);\n' % (
|
|
||||||
brief, trimmed_name, member_name, trimmed_name,
|
|
||||||
trimmed_name, member_name))
|
|
||||||
print('%}')
|
|
||||||
elif language == 'python' and constants:
|
|
||||||
if mode == 'start':
|
|
||||||
print('%%extend %s {\n%%pythoncode %%{' % class_name)
|
|
||||||
for member_name, brief in constants:
|
|
||||||
print(' ## @brief %s\n %s = None' % (brief, member_name))
|
|
||||||
print('%}\n}')
|
|
||||||
elif mode == 'end':
|
|
||||||
print('%pythoncode %{')
|
|
||||||
for member_name, brief in constants:
|
|
||||||
print('%s.%s.__doc__ = """%s"""' % (
|
|
||||||
trimmed_name, member_name, brief))
|
|
||||||
print('%}')
|
|
||||||
elif language == 'ruby' and constants:
|
|
||||||
for member_name, brief in constants:
|
|
||||||
print('%%feature("docstring") %s::%s "%s\\n";' % (class_name, member_name, brief))
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
%include "cpointer.i"
|
||||||
|
%include "carrays.i"
|
||||||
|
%include "stdint.i"
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include "libsigrok/libsigrok.h"
|
||||||
|
%}
|
||||||
|
|
||||||
|
typedef void *gpointer;
|
||||||
|
|
||||||
|
typedef struct _GSList GSList;
|
||||||
|
|
||||||
|
struct _GSList
|
||||||
|
{
|
||||||
|
gpointer data;
|
||||||
|
GSList *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void g_slist_free(GSList *list);
|
||||||
|
|
||||||
|
GVariant *g_variant_new_uint64(uint64_t value);
|
||||||
|
GVariant *g_variant_new_boolean(gboolean value);
|
||||||
|
GVariant *g_variant_new_double(double value);
|
||||||
|
GVariant *g_variant_new_string(char *value);
|
||||||
|
GVariant *g_variant_new_tuple(GVariant *children[], unsigned long n_children);
|
||||||
|
char *g_variant_get_type_string(GVariant *value);
|
||||||
|
uint64_t g_variant_get_uint64(GVariant *value);
|
||||||
|
gboolean g_variant_get_boolean(GVariant *value);
|
||||||
|
double g_variant_get_double(GVariant *value);
|
||||||
|
char *g_variant_get_string(GVariant *value, unsigned long *length);
|
||||||
|
GVariant *g_variant_get_child_value(GVariant *value, unsigned long index);
|
||||||
|
|
||||||
|
%include "libsigrok/libsigrok.h"
|
||||||
|
#undef SR_API
|
||||||
|
#define SR_API
|
||||||
|
%ignore sr_config_info_name_get;
|
||||||
|
%include "libsigrok/proto.h"
|
||||||
|
%include "libsigrok/version.h"
|
||||||
|
|
||||||
|
%array_class(float, float_array);
|
||||||
|
%pointer_functions(GVariant *, gvariant_ptr_ptr);
|
||||||
|
%array_functions(GVariant *, gvariant_ptr_array);
|
||||||
|
%pointer_functions(struct sr_context *, sr_context_ptr_ptr);
|
||||||
|
%array_functions(struct sr_dev_driver *, sr_dev_driver_ptr_array);
|
||||||
|
%pointer_cast(gpointer, struct sr_dev_inst *, gpointer_to_sr_dev_inst_ptr);
|
||||||
|
%pointer_cast(void *, struct sr_datafeed_logic *, void_ptr_to_sr_datafeed_logic_ptr)
|
||||||
|
%pointer_cast(void *, struct sr_datafeed_analog *, void_ptr_to_sr_datafeed_analog_ptr)
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the libsigrok project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Martin Ling <martin-sigrok@earth.li>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
%{
|
|
||||||
#include <libsigrokcxx/libsigrokcxx.hpp>
|
|
||||||
using namespace std;
|
|
||||||
%}
|
|
||||||
|
|
||||||
%include "std_string.i"
|
|
||||||
%include "std_shared_ptr.i"
|
|
||||||
%include "std_vector.i"
|
|
||||||
%include "std_map.i"
|
|
||||||
#ifdef SWIGJAVA
|
|
||||||
namespace std {
|
|
||||||
template <class _Key> class set {};
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
%include "std_set.i"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
%template(StringMap) std::map<std::string, std::string>;
|
|
||||||
|
|
||||||
%template(DriverMap)
|
|
||||||
std::map<std::string, std::shared_ptr<sigrok::Driver> >;
|
|
||||||
%template(InputFormatMap)
|
|
||||||
std::map<std::string, std::shared_ptr<sigrok::InputFormat> >;
|
|
||||||
%template(OutputFormatMap)
|
|
||||||
std::map<std::string, std::shared_ptr<sigrok::OutputFormat> >;
|
|
||||||
|
|
||||||
%template(HardwareDeviceVector)
|
|
||||||
std::vector<std::shared_ptr<sigrok::HardwareDevice> >;
|
|
||||||
|
|
||||||
%template(DeviceVector)
|
|
||||||
std::vector<std::shared_ptr<sigrok::Device> >;
|
|
||||||
|
|
||||||
%template(ChannelVector)
|
|
||||||
std::vector<std::shared_ptr<sigrok::Channel> >;
|
|
||||||
|
|
||||||
%template(ChannelGroupMap)
|
|
||||||
std::map<std::string, std::shared_ptr<sigrok::ChannelGroup> >;
|
|
||||||
|
|
||||||
/* Workaround for SWIG bug. The vector template instantiation
|
|
||||||
isn't needed but somehow fixes a bug that stops the wrapper
|
|
||||||
for the map instantiation from compiling. */
|
|
||||||
%template(ConfigVector)
|
|
||||||
std::vector<const sigrok::ConfigKey *>;
|
|
||||||
|
|
||||||
%template(ConfigMap)
|
|
||||||
std::map<const sigrok::ConfigKey *, Glib::VariantBase>;
|
|
||||||
|
|
||||||
%template(ConfigSet)
|
|
||||||
std::set<const sigrok::ConfigKey *>;
|
|
||||||
|
|
||||||
/* Workaround for SWIG bug. The vector template instantiation
|
|
||||||
isn't needed but somehow fixes a bug that stops the wrapper
|
|
||||||
for the set instantiation from compiling. */
|
|
||||||
%template(CapabilityVector)
|
|
||||||
std::vector<const sigrok::Capability *>;
|
|
||||||
|
|
||||||
%template(CapabilitySet)
|
|
||||||
std::set<const sigrok::Capability *>;
|
|
||||||
|
|
||||||
%template(OptionVector)
|
|
||||||
std::vector<std::shared_ptr<sigrok::Option> >;
|
|
||||||
%template(OptionMap)
|
|
||||||
std::map<std::string, std::shared_ptr<sigrok::Option> >;
|
|
||||||
|
|
||||||
%template(VariantVector)
|
|
||||||
std::vector<Glib::VariantBase>;
|
|
||||||
%template(VariantMap)
|
|
||||||
std::map<std::string, Glib::VariantBase>;
|
|
||||||
|
|
||||||
%template(QuantityFlagVector)
|
|
||||||
std::vector<const sigrok::QuantityFlag *>;
|
|
||||||
|
|
||||||
%template(TriggerStageVector)
|
|
||||||
std::vector<std::shared_ptr<sigrok::TriggerStage> >;
|
|
||||||
|
|
||||||
%template(TriggerMatchVector)
|
|
||||||
std::vector<std::shared_ptr<sigrok::TriggerMatch> >;
|
|
||||||
1033
configure.ac
1033
configure.ac
File diff suppressed because it is too large
Load Diff
|
|
@ -1,354 +0,0 @@
|
||||||
##
|
|
||||||
## This file is part of the libsigrok project.
|
|
||||||
##
|
|
||||||
## Copyright (C) 2010-2013 Uwe Hermann <uwe@hermann-uwe.de>
|
|
||||||
##
|
|
||||||
## This program is free software; you can redistribute it and/or modify
|
|
||||||
## it under the terms of the GNU General Public License as published by
|
|
||||||
## the Free Software Foundation; either version 2 of the License, or
|
|
||||||
## (at your option) any later version.
|
|
||||||
##
|
|
||||||
## This program is distributed in the hope that it will be useful,
|
|
||||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
## GNU General Public License for more details.
|
|
||||||
##
|
|
||||||
## You should have received a copy of the GNU General Public License
|
|
||||||
## along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
##
|
|
||||||
|
|
||||||
#
|
|
||||||
# These rules do not grant any permission by itself, but flag devices
|
|
||||||
# supported by libsigrok.
|
|
||||||
# The access policy is stored in the 61-libsigrok-plugdev.rules and
|
|
||||||
# 61-libsigrok-uaccess.rules.
|
|
||||||
#
|
|
||||||
# Note: Any syntax changes here will need to be tested against the
|
|
||||||
# 'update-device-filter' Makefile target in the sigrok-androidutils
|
|
||||||
# repo, since that parses this file.
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
|
||||||
# Please keep this list sorted alphabetically by vendor/device name.
|
|
||||||
#
|
|
||||||
|
|
||||||
ACTION!="add|change", GOTO="libsigrok_rules_end"
|
|
||||||
SUBSYSTEM!="usb|usbmisc|usb_device|hidraw", GOTO="libsigrok_rules_end"
|
|
||||||
|
|
||||||
# Agilent USBTMC-connected devices
|
|
||||||
# 34405A
|
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0618", ENV{ID_SIGROK}="1"
|
|
||||||
# 34410A
|
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0607", ENV{ID_SIGROK}="1"
|
|
||||||
# 34460A
|
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="1b07", ENV{ID_SIGROK}="1"
|
|
||||||
# DSO1000 series
|
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", ENV{ID_SIGROK}="1"
|
|
||||||
# MSO7000A series
|
|
||||||
ATTRS{idVendor}=="0957", ATTRS{idProduct}=="1735", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# ASIX SIGMA
|
|
||||||
# ASIX SIGMA2
|
|
||||||
ATTRS{idVendor}=="a600", ATTRS{idProduct}=="a000", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Braintechnology USB-LPS
|
|
||||||
ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0498", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Brymen BU-86X adapter (e.g. for Brymen BM867/BM869 and possibly others).
|
|
||||||
ATTRS{idVendor}=="0820", ATTRS{idProduct}=="0001", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# ChronoVu LA8 (new VID/PID)
|
|
||||||
# ChronoVu LA16 (new VID/PID)
|
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8867", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# CWAV USBee AX
|
|
||||||
# ARMFLY AX-Pro (clone of the CWAV USBee AX)
|
|
||||||
# ARMFLY Mini-Logic (clone of the CWAV USBee AX)
|
|
||||||
# EE Electronics ESLA201A (clone of the CWAV USBee AX)
|
|
||||||
# HT USBee-AxPro (clone of the CWAV USBee AX)
|
|
||||||
# MCU123 USBee AX Pro clone (clone of the CWAV USBee AX)
|
|
||||||
# Noname LHT00SU1 (clone of the CWAV USBee AX)
|
|
||||||
# XZL_Studio AX (clone of the CWAV USBee AX)
|
|
||||||
ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0014", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# CWAV USBee DX
|
|
||||||
# HT USBee-DxPro (clone of the CWAV USBee DX), not yet supported!
|
|
||||||
# XZL_Studio DX (clone of the CWAV USBee DX)
|
|
||||||
ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0015", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# CWAV USBee SX
|
|
||||||
ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0009", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# CWAV USBee ZX
|
|
||||||
ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0005", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Cypress FX2 eval boards without EEPROM:
|
|
||||||
# Lcsoft Mini Board
|
|
||||||
# Braintechnology USB Interface V2.x
|
|
||||||
# fx2grok-tiny
|
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Dangerous Prototypes Buspirate (v3)
|
|
||||||
# ChronoVu LA8 (old VID/PID)
|
|
||||||
# ChronoVu LA16 (old VID/PID)
|
|
||||||
# ftdi-la (FT232R)
|
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Dangerous Prototypes Buspirate (v4)
|
|
||||||
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
|
|
||||||
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0001", ENV{ID_SIGROK}="1"
|
|
||||||
# DreamSourceLab DSLogic Pro
|
|
||||||
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0003", ENV{ID_SIGROK}="1"
|
|
||||||
# DreamSourceLab DScope
|
|
||||||
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0002", ENV{ID_SIGROK}="1"
|
|
||||||
# DreamSourceLab DSLogic Plus
|
|
||||||
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0020", ENV{ID_SIGROK}="1"
|
|
||||||
# DreamSourceLab DSLogic Basic
|
|
||||||
ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0021", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# GW-Instek GDM-9061 (USBTMC mode)
|
|
||||||
ATTRS{idVendor}=="2184", ATTRS{idProduct}=="0059", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# HAMEG HO720
|
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed72", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# HAMEG HO730
|
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed73", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Hantek DSO-2090
|
|
||||||
# lsusb: "04b4:2090 Cypress Semiconductor Corp."
|
|
||||||
# lsusb after FW upload: "04b5:2090 ROHM LSI Systems USA, LLC"
|
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2090", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2090", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Hantek DSO-2150
|
|
||||||
# lsusb: "04b4:2150 Cypress Semiconductor Corp."
|
|
||||||
# lsusb after FW upload: "04b5:2150 ROHM LSI Systems USA, LLC"
|
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2150", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2150", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Hantek DSO-2250
|
|
||||||
# lsusb: "04b4:2250 Cypress Semiconductor Corp."
|
|
||||||
# lsusb after FW upload: "04b5:2250 ROHM LSI Systems USA, LLC"
|
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2250", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2250", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Hantek DSO-5200
|
|
||||||
# lsusb: "04b4:5200 Cypress Semiconductor Corp."
|
|
||||||
# lsusb after FW upload: "04b5:5200 ROHM LSI Systems USA, LLC"
|
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="5200", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="5200", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Hantek DSO-5200A
|
|
||||||
# lsusb: "04b4:520a Cypress Semiconductor Corp."
|
|
||||||
# lsusb after FW upload: "04b5:520a ROHM LSI Systems USA, LLC"
|
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="520a", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="520a", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Hantek 6022BE, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Hantek 6022BE
|
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6022", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Hantek 6022BL, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Hantek 6022BL
|
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="602a", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Hantek 4032L
|
|
||||||
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="4032", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# IKALOGIC Scanalogic-2
|
|
||||||
ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4123", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# IKALOGIC ScanaPLUS
|
|
||||||
# ftdi-la (FT232H)
|
|
||||||
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
|
|
||||||
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-CO
|
|
||||||
# This is actually the generic SiLabs (Cygnal) F32x USBXpress VID:PID.
|
|
||||||
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="0002", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# LeCroy LogicStudio16
|
|
||||||
ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a001", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a002", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# LeCroy WaveRunner
|
|
||||||
# 05ff:1023: 625Zi
|
|
||||||
ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="1023", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Link Instruments MSO-19
|
|
||||||
ATTRS{idVendor}=="3195", ATTRS{idProduct}=="f190", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Logic Shrimp
|
|
||||||
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa95", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# MiniLA Mockup
|
|
||||||
# ftdi-la (FT2232H)
|
|
||||||
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 98583
|
|
||||||
# Tondaj SL-814
|
|
||||||
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
|
|
||||||
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol DS1000 series
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0588", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol 1000Z series
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04ce", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol DS2000 series
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b0", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol DS4000 series
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b1", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol DG4000 series
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0641", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol 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
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0e11", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rigol MSO5000 series
|
|
||||||
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0515", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Rohde&Schwarz HMO series mixed-signal oscilloscope (previously branded Hameg) VCP/USBTMC mode
|
|
||||||
ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0117", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0118", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0119", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Sainsmart DDS120 / Rocktech BM102, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Sainsmart DDS120
|
|
||||||
ATTRS{idVendor}=="8102", ATTRS{idProduct}=="8102", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Saleae Logic
|
|
||||||
# EE Electronics ESLA100 (clone of the Saleae Logic)
|
|
||||||
# Hantek 6022BL in LA mode (clone of the Saleae Logic)
|
|
||||||
# Instrustar ISDS205X in LA mode (clone of the Saleae Logic)
|
|
||||||
# Robomotic MiniLogic (clone of the Saleae Logic)
|
|
||||||
# Robomotic BugLogic 3 (clone of the Saleae Logic)
|
|
||||||
# MCU123 Saleae Logic clone (clone of the Saleae Logic)
|
|
||||||
ATTRS{idVendor}=="0925", ATTRS{idProduct}=="3881", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Saleae Logic16
|
|
||||||
ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1001", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Saleae Logic Pro 16
|
|
||||||
ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1006", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Siglent USBTMC devices.
|
|
||||||
# f4ec:ee3a: E.g. SDS1052DL+ scope
|
|
||||||
# f4ec:ee38: E.g. SDS1104X-E scope
|
|
||||||
# f4ed:ee3a: E.g. SDS1202X-E scope or SDG1010 waveform generator
|
|
||||||
ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="f4ed", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# sigrok FX2 LA (8ch)
|
|
||||||
# fx2grok-flat (before and after renumeration)
|
|
||||||
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608c", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# sigrok FX2 LA (16ch)
|
|
||||||
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608d", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# sigrok FX2 DSO (2ch)
|
|
||||||
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# sigrok usb-c-grok
|
|
||||||
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608f", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# SiLabs CP210x (USB CDC) UART bridge, used (among others) in:
|
|
||||||
# CEM DT-8852
|
|
||||||
# Manson HCS-3202
|
|
||||||
# MASTECH MS2115B
|
|
||||||
# MASTECH MS5308
|
|
||||||
# MASTECH MS8250D
|
|
||||||
# PeakTech 3330
|
|
||||||
# Voltcraft PPS-11815
|
|
||||||
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# SiLabs CP2110 (USB HID) UART bridge, used (among others) in:
|
|
||||||
# UNI-T UT612
|
|
||||||
# UNI-T UT-D09 multimeter cable (for various UNI-T and rebranded DMMs)
|
|
||||||
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea80", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Sysclk LWLA1016
|
|
||||||
ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6688", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Sysclk LWLA1034
|
|
||||||
ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6689", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Sysclk SLA5032 ("32CH 500M" mode)
|
|
||||||
ATTRS{idVendor}=="2961", ATTRS{idProduct}=="66b0", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Testo 435
|
|
||||||
ATTRS{idVendor}=="128d", ATTRS{idProduct}=="0003", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# UNI-T UT-D04 multimeter cable (for various UNI-T and rebranded DMMs)
|
|
||||||
# UNI-T UT325
|
|
||||||
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="e008", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# V&A VA4000 multimeter cable (for various V&A DMMs)
|
|
||||||
ATTRS{idVendor}=="04fc", ATTRS{idProduct}=="0201", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# Victor 70C
|
|
||||||
# Victor 86C
|
|
||||||
ATTRS{idVendor}=="1244", ATTRS{idProduct}=="d237", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# YiXingDianZi MDSO
|
|
||||||
ATTRS{idVendor}=="d4a2", ATTRS{idProduct}=="5660", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
# ZEROPLUS Logic Cube LAP-C series
|
|
||||||
# There are various devices in the ZEROPLUS Logic Cube series:
|
|
||||||
# 0c12:7002: LAP-16128U
|
|
||||||
# 0c12:7009: LAP-C(16064)
|
|
||||||
# 0c12:700a: LAP-C(16128)
|
|
||||||
# 0c12:700b: LAP-C(32128)
|
|
||||||
# 0c12:700c: LAP-C(321000)
|
|
||||||
# 0c12:700d: LAP-C(322000)
|
|
||||||
# 0c12:700e: LAP-C(16032)
|
|
||||||
# 0c12:7016: LAP-C(162000)
|
|
||||||
# 0c12:7025: LAP-C(16128+)
|
|
||||||
# 0c12:7100: AKIP-9101
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7002", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7007", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7009", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700a", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700b", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700c", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700d", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700e", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7016", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7025", ENV{ID_SIGROK}="1"
|
|
||||||
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7100", ENV{ID_SIGROK}="1"
|
|
||||||
|
|
||||||
LABEL="libsigrok_rules_end"
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
##
|
||||||
|
|
||||||
|
EXTRA_DIST = gnuplot_chronovu_la8.gpi \
|
||||||
|
gnuplot_rigol_ds1xx2.gpi \
|
||||||
|
gnuplot_usbeesx.gpi \
|
||||||
|
gnuplot_usbeedx8.gpi \
|
||||||
|
gnuplot_usbeedx16.gpi \
|
||||||
|
sigrok-logo-notext.png \
|
||||||
|
z60_libsigrok.rules
|
||||||
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
##
|
||||||
|
|
||||||
|
# We're setting the colors of the 8 channels to match the colors of the
|
||||||
|
# probe cables on the ChronoVu LA8:
|
||||||
|
# background = white, borders = black, axes = gray,
|
||||||
|
# ch0 = green, ch1 = orange, ch2 = white, ch3 = red,
|
||||||
|
# ch4 = gray, ch5 = brown, ch6 = blue, ch7 = yellow
|
||||||
|
set terminal png large size 2048, 1536 xffffff x000000 x404040 \
|
||||||
|
x37aa34 xff7c00 xe0e0e0 xff0000 \
|
||||||
|
x808080 x925525 x425adc xfbee13
|
||||||
|
set autoscale
|
||||||
|
set grid
|
||||||
|
set ytics ("Channel 7" 2, "Channel 6" 4, "Channel 5" 6, "Channel 4" 8, \
|
||||||
|
"Channel 3" 10, "Channel 2" 12, "Channel 1" 14, "Channel 0" 16)
|
||||||
|
set title "sigrok gnuplot output, http://www.sigrok.org, ChronoVu LA8"
|
||||||
|
set xlabel "Sample number"
|
||||||
|
set ylabel "Channel"
|
||||||
|
set output "sigrok_gnuplot.png"
|
||||||
|
|
||||||
|
plot [0:8388608] [0:18] \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($2 + 15) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($3 + 13) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($4 + 11) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($5 + 9) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($6 + 7) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($7 + 5) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($8 + 3) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($9 + 1) with lines linewidth 2 title ""
|
||||||
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
##
|
||||||
|
|
||||||
|
set terminal png large size 2048, 1536
|
||||||
|
set autoscale
|
||||||
|
set grid
|
||||||
|
set title "sigrok gnuplot output, http://www.sigrok.org, Rigol DS1xx2"
|
||||||
|
set xlabel "Time"
|
||||||
|
set ylabel "Voltage"
|
||||||
|
set output "sigrok_gnuplot.png"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Rigol DS1xx2 output is currently always 600 samples in size.
|
||||||
|
# This script currently also assumes only one channel is acquired like this:
|
||||||
|
#
|
||||||
|
# $ sigrok-cli --driver rigol-ds1xx2 --frames 1 -p CH1 ...
|
||||||
|
#
|
||||||
|
plot [0:600] \
|
||||||
|
"sigrok_gnuplot.dat" using 1 with lines linewidth 2 title "CH1"
|
||||||
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
## Copyright (C) 2012 Ivan Fedorov <oxyum@oxyum.ru>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
##
|
||||||
|
|
||||||
|
# We're setting the colors of the 16 channels to match the colors of the
|
||||||
|
# probe cables on the CWAV USBee DX:
|
||||||
|
# background = white, borders = black, axes = gray,
|
||||||
|
# ch0 = black, ch1 = brown, ch2 = red, ch3 = orange,
|
||||||
|
# ch4 = yellow, ch5 = green, ch6 = blue, ch7 = violet
|
||||||
|
# ch8 = black, ch9 = brown, chA = red, chB = orange,
|
||||||
|
# chC = yellow, chD = green, chE = blue, chF = violet
|
||||||
|
set terminal png large size 2048, 1536 xffffff x000000 x404040 \
|
||||||
|
x000000 xc25525 xff0000 xff7c00 \
|
||||||
|
xfbee13 x37ba34 x425adc x9500d3 \
|
||||||
|
x000000 xc25525 xff0000 xff7c00 \
|
||||||
|
xfbee13 x37ba34 x425adc x9500d3
|
||||||
|
set autoscale
|
||||||
|
set grid
|
||||||
|
set ytics ("Channel F" 2, "Channel E" 4, "Channel D" 6, "Channel C" 8, \
|
||||||
|
"Channel B" 10, "Channel A" 12, "Channel 9" 14, "Channel 8" 16, \
|
||||||
|
"Channel 7" 18, "Channel 6" 20, "Channel 5" 22, "Channel 4" 24, \
|
||||||
|
"Channel 3" 26, "Channel 2" 28, "Channel 1" 30, "Channel 0" 32)
|
||||||
|
set title "sigrok gnuplot output, http://www.sigrok.org, CWAV USBee DX"
|
||||||
|
set xlabel "Sample number"
|
||||||
|
set ylabel "Channel"
|
||||||
|
set output "sigrok_gnuplot.png"
|
||||||
|
|
||||||
|
plot [0:200000] [0:34] \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($6 + 31) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($7 + 29) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($8 + 27) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($9 + 25) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($5 + 23) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($4 + 21) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($3 + 19) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($2 + 17) with lines linewidth 2 title "", \
|
||||||
|
\
|
||||||
|
"sigrok_gnuplot.dat" using 1:($17 + 15) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($16 + 13) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($15 + 11) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($10 + 9) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($11 + 7) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($12 + 5) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($13 + 3) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($14 + 1) with lines linewidth 2 title ""
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
## Copyright (C) 2012 Ivan Fedorov <oxyum@oxyum.ru>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
##
|
||||||
|
|
||||||
|
# We're setting the colors of the 8 channels to match the colors of the
|
||||||
|
# probe cables on the CWAV USBee DX:
|
||||||
|
# background = white, borders = black, axes = gray,
|
||||||
|
# ch0 = black, ch1 = brown, ch2 = red, ch3 = orange,
|
||||||
|
# ch4 = yellow, ch5 = green, ch6 = blue, ch7 = violet
|
||||||
|
set terminal png large size 2048, 1536 xffffff x000000 x404040 \
|
||||||
|
x000000 xc25525 xff0000 xff7c00 \
|
||||||
|
xfbee13 x37ba34 x425adc x9500d3
|
||||||
|
set autoscale
|
||||||
|
set grid
|
||||||
|
set ytics ("Channel 7" 2, "Channel 6" 4, "Channel 5" 6, "Channel 4" 8, \
|
||||||
|
"Channel 3" 10, "Channel 2" 12, "Channel 1" 14, "Channel 0" 16)
|
||||||
|
set title "sigrok gnuplot output, http://www.sigrok.org, CWAV USBee DX"
|
||||||
|
set xlabel "Sample number"
|
||||||
|
set ylabel "Channel"
|
||||||
|
set output "sigrok_gnuplot.png"
|
||||||
|
|
||||||
|
plot [0:200000] [0:18] \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($6 + 15) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($7 + 13) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($8 + 11) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($9 + 9) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($5 + 7) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($4 + 5) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($3 + 3) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($2 + 1) with lines linewidth 2 title ""
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
##
|
||||||
|
|
||||||
|
# We're setting the colors of the 8 channels to match the colors of the
|
||||||
|
# probe cables on the CWAV USBee SX:
|
||||||
|
# background = white, borders = black, axes = gray,
|
||||||
|
# ch0 = black, ch1 = brown, ch2 = red, ch3 = orange,
|
||||||
|
# ch4 = yellow, ch5 = green, ch6 = blue, ch7 = violet
|
||||||
|
set terminal png large size 2048, 1536 xffffff x000000 x404040 \
|
||||||
|
x000000 xc25525 xff0000 xff7c00 \
|
||||||
|
xfbee13 x37ba34 x425adc x9500d3
|
||||||
|
set autoscale
|
||||||
|
set grid
|
||||||
|
set ytics ("Channel 7" 2, "Channel 6" 4, "Channel 5" 6, "Channel 4" 8, \
|
||||||
|
"Channel 3" 10, "Channel 2" 12, "Channel 1" 14, "Channel 0" 16)
|
||||||
|
set title "sigrok gnuplot output, http://www.sigrok.org, CWAV USBee SX"
|
||||||
|
set xlabel "Sample number"
|
||||||
|
set ylabel "Channel"
|
||||||
|
set output "sigrok_gnuplot.png"
|
||||||
|
|
||||||
|
plot [0:1000000] [0:18] \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($2 + 15) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($3 + 13) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($4 + 11) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($5 + 9) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($6 + 7) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($7 + 5) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($8 + 3) with lines linewidth 2 title "", \
|
||||||
|
"sigrok_gnuplot.dat" using 1:($9 + 1) with lines linewidth 2 title ""
|
||||||
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 KiB |
|
|
@ -1,234 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg id="svg2" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
||||||
<defs id="defs4">
|
|
||||||
<filter id="filter5288" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5290" stdDeviation="1.2304688"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5288-0" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5290-1" stdDeviation="1.2304688"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5354" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5356" stdDeviation="1.2575"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5354-5" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5356-2" stdDeviation="1.2575"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5412" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5414" stdDeviation="1.311875"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5412-5" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5414-6" stdDeviation="1.311875"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5470" height="1.0266" width="1.2422" color-interpolation-filters="sRGB" y="-.013320" x="-.12110">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5472" stdDeviation="1.30875"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5470-6" height="1.0266" width="1.2422" color-interpolation-filters="sRGB" y="-.013320" x="-.12110">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5472-1" stdDeviation="1.30875"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5536" height="1.0246" width="1.9962" color-interpolation-filters="sRGB" y="-.012296" x="-.49812">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5538" stdDeviation="1.2453125"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter5703" height="1" width="1" color-interpolation-filters="sRGB" y="0" x="0">
|
|
||||||
<feGaussianBlur id="feGaussianBlur5705" stdDeviation="1.709984544049459" result="result8"/>
|
|
||||||
<feTurbulence id="feTurbulence5707" baseFrequency="0.0080370942812983005 0.027820710973724884" seed="56" result="result7" numOctaves="2" type="turbulence"/>
|
|
||||||
<feComposite id="feComposite5709" operator="in" result="result6" in2="result8" in="SourceGraphic"/>
|
|
||||||
<feComposite id="feComposite5711" in="result6" in2="result7" k3="1" k2="0" k1="0" result="result2" k4="0" operator="arithmetic"/>
|
|
||||||
<feComposite id="feComposite5713" operator="in" result="fbSourceGraphic" in2="result6" in="result2"/>
|
|
||||||
<feComposite id="feComposite5715" in="fbSourceGraphic" in2="fbSourceGraphic" k3="0" k2="2.5" k1="0" result="fbSourceGraphic" k4="0" operator="arithmetic"/>
|
|
||||||
<feColorMatrix id="feColorMatrix5805" result="fbSourceGraphicAlpha" values="0" type="saturate" in="fbSourceGraphic"/>
|
|
||||||
<feColorMatrix id="feColorMatrix5807" values="1" type="saturate" result="result2" in="fbSourceGraphic"/>
|
|
||||||
<feFlood id="feFlood5809" flood-color="rgb(113,79,56)" result="result1"/>
|
|
||||||
<feBlend id="feBlend5811" result="result3" mode="multiply" in2="result2" in="result1"/>
|
|
||||||
<feComposite id="feComposite5813" operator="in" result="result4" in2="fbSourceGraphic"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter4766-3" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur4768-8" stdDeviation="0.25453125"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter4654-6" height="1.0252" width="1.5121" color-interpolation-filters="sRGB" y="-.012590" x="-.25603">
|
|
||||||
<feGaussianBlur id="feGaussianBlur4656-4" stdDeviation="0.4267185"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter4596-6" height="1.2771" width="1.0125" color-interpolation-filters="sRGB" y="-.13854" x="-.0062716">
|
|
||||||
<feGaussianBlur id="feGaussianBlur4598-4" stdDeviation="0.48343725"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter4536-1" height="1.0263" width="1.2736" color-interpolation-filters="sRGB" y="-.013154" x="-.13680">
|
|
||||||
<feGaussianBlur id="feGaussianBlur4538-3" stdDeviation="0.45059085"/>
|
|
||||||
</filter>
|
|
||||||
<linearGradient id="linearGradient4358-7">
|
|
||||||
<stop id="stop4360-7" stop-color="#aca592" offset="0"/>
|
|
||||||
<stop id="stop4362-1" stop-color="#aca592" stop-opacity="0" offset="1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<filter id="filter6293" height="1.4486" width="1.1034" color-interpolation-filters="sRGB" y="-.22428" x="-.051678">
|
|
||||||
<feGaussianBlur id="feGaussianBlur6295" stdDeviation="4.5525501"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter6293-3" height="1.4486" width="1.1034" color-interpolation-filters="sRGB" y="-.22428" x="-.051678">
|
|
||||||
<feGaussianBlur id="feGaussianBlur6295-5" stdDeviation="4.5525501"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter7222" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur7224" stdDeviation="0.93122891"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter7410" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur7412" stdDeviation="0.28402344"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter7414" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur id="feGaussianBlur7416" stdDeviation="0.28402344"/>
|
|
||||||
</filter>
|
|
||||||
<linearGradient id="linearGradient4024" y2="837.09" gradientUnits="userSpaceOnUse" x2="112.48" gradientTransform="translate(279.37)" y1="900.59" x1="-60.266">
|
|
||||||
<stop id="stop4324-0" stop-color="#6c6753" stop-opacity=".50862" offset="0"/>
|
|
||||||
<stop id="stop4332-7" stop-color="#a39e88" stop-opacity="0" offset=".45474"/>
|
|
||||||
<stop id="stop4330-01" stop-color="#a39e88" stop-opacity="0" offset=".77666"/>
|
|
||||||
<stop id="stop4326-8" stop-color="#6c6753" stop-opacity=".70690" offset="1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="linearGradient4026" y2="805.75" xlink:href="#linearGradient4358-7" gradientUnits="userSpaceOnUse" x2="1.7663" gradientTransform="translate(279.37)" y1="787.42" x1="1.1134"/>
|
|
||||||
<linearGradient id="linearGradient4028" y2="716.49" gradientUnits="userSpaceOnUse" x2="-133.64" gradientTransform="translate(279.37)" y1="715.43" x1="-134">
|
|
||||||
<stop id="stop4408-14" offset="0"/>
|
|
||||||
<stop id="stop4410-2" stop-opacity="0" offset="1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="linearGradient4030" y2="806.65" gradientUnits="userSpaceOnUse" x2="-69.701" gradientTransform="translate(279.37)" y1="839.88" x1="-69.701">
|
|
||||||
<stop id="stop4542-9" stop-color="#989078" offset="0"/>
|
|
||||||
<stop id="stop4544-5" stop-color="#aca592" stop-opacity="0" offset="1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="linearGradient4032" y2="820.96" xlink:href="#linearGradient4358-7" gradientUnits="userSpaceOnUse" x2="-8.9303" gradientTransform="translate(279.37)" y1="829.56" x1="-8.9303"/>
|
|
||||||
<linearGradient id="linearGradient4034" y2="801.74" gradientUnits="userSpaceOnUse" x2="25.605" gradientTransform="translate(279.37)" y1="911.18" x1="25.605">
|
|
||||||
<stop id="stop4704-1" stop-color="#6c6753" stop-opacity=".50862" offset="0"/>
|
|
||||||
<stop id="stop4706-4" stop-color="#a39e88" stop-opacity="0" offset=".60980"/>
|
|
||||||
<stop id="stop4708-5" stop-color="#a39e88" stop-opacity="0" offset=".77666"/>
|
|
||||||
<stop id="stop4710-4" stop-color="#6c6753" stop-opacity=".70690" offset="1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="radialGradient4036" gradientUnits="userSpaceOnUse" cy="356.19" cx="300" gradientTransform="matrix(1.3455 -.0000010547 2.0946e-7 .10026 -103.64 317.01)" r="55.264">
|
|
||||||
<stop id="stop6329" offset="0"/>
|
|
||||||
<stop id="stop6333" stop-color="#464646" offset=".60223"/>
|
|
||||||
<stop id="stop6331" stop-color="#aca592" stop-opacity="0" offset="1"/>
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient id="radialGradient4038" gradientUnits="userSpaceOnUse" cy="38.204" cx="301.23" gradientTransform="matrix(1 0 0 13.333 0 -471.18)" r="2.1213">
|
|
||||||
<stop id="stop4661" stop-color="#fff" stop-opacity=".33621" offset="0"/>
|
|
||||||
<stop id="stop4663" stop-color="#fff" stop-opacity="0" offset="1"/>
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient id="radialGradient4040" gradientUnits="userSpaceOnUse" cy="67.522" cx="302.84" gradientTransform="matrix(.74160 0 0 3.0648 78.253 -107.79)" r="7.0144">
|
|
||||||
<stop id="stop4691" stop-color="#fff" stop-opacity=".23276" offset="0"/>
|
|
||||||
<stop id="stop4693" stop-color="#fff" stop-opacity="0" offset="1"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="linearGradient4074" y2="333.77" gradientUnits="userSpaceOnUse" x2="291.22" gradientTransform="matrix(1.0518 0 0 1.0574 -15.542 -15.976)" y1="330.77" x1="291.22">
|
|
||||||
<stop id="stop5572" stop-color="#fff" stop-opacity=".36207" offset="0"/>
|
|
||||||
<stop id="stop5574" stop-opacity="0" offset="1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="linearGradient4076" y2="339.48" gradientUnits="userSpaceOnUse" x2="337.09" gradientTransform="matrix(1.0417 0 0 1.2372 -12.207 -75.58)" y1="339.48" x1="343.43">
|
|
||||||
<stop id="stop5572-3" stop-color="#fff" stop-opacity=".36207" offset="0"/>
|
|
||||||
<stop id="stop5574-2" stop-opacity="0" offset="1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="linearGradient4078" y2="304.68" gradientUnits="userSpaceOnUse" x2="300.31" gradientTransform="translate(0,4)" y1="331.2" x1="300.31">
|
|
||||||
<stop id="stop5656" stop-opacity=".30172" offset="0"/>
|
|
||||||
<stop id="stop5658" stop-opacity="0" offset="1"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<metadata id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
|
||||||
<dc:title/>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g id="layer1" transform="translate(-93.213 -870.88)">
|
|
||||||
<path id="path1648" d="m1319.8 222.81c105.62-29.815 164.49-18.697 304.84-6.9484 82.527 6.9082 180.31-11.422 237.14-8.5433 95.114 4.8165 189.98 28.25 321.13 20.545" display="none" stroke="#fff" stroke-width="1px" fill="none"/>
|
|
||||||
</g>
|
|
||||||
<g id="layer2" transform="translate(-93.213 -418.52)">
|
|
||||||
<g id="g3938" transform="matrix(.10491 0 0 .10491 85.726 417.58)">
|
|
||||||
<path id="rect10305-9-6-8-1-4-6" style="color:#000000" d="m403.34 894.39 1.1978 14.787-3.5969 6.1085-5.8543 2.6695-6.7717 1.1769-12.988-1.6568-14.183 0.0522-9.733 0.82833-9.6449 1.6044-9.7661-1.2906-9.033-0.36618-15.992 0.46239-12.627 1.1944-11.95-3.3136-8.446 0.80213-11.891 1.6831-16.155-1.6219-12.442 1.6743-9.5288 0.11012-7.6552-3.0431-11.662-3.9917-4.2875-7.8841-4.7091-13.569-2.5128-14.622 5.9409-4.9408 199.41 1.7589 4.5302 6.1849 0.35355 15.203z" fill-opacity=".27273" transform="matrix(.96999 0 0 .77327 8.9667 -253.3)" filter="url(#filter6293)"/>
|
|
||||||
<path id="rect10305-9-6-8-1-4-6-9" style="color:#000000" d="m403.34 894.39 1.1978 14.787-3.5969 6.1085-5.8543 2.6695-6.7717 1.1769-12.988-1.6568-14.183 0.0522-9.733 0.82833-9.6449 1.6044-9.7661-1.2906-9.033-0.36618-15.992 0.46239-12.627 1.1944-11.95-3.3136-8.446 0.80213-11.891 1.6831-16.155-1.6219-12.442 1.6743-9.5288 0.11012-7.6552-3.0431-11.662-3.9917-4.2875-7.8841-4.7091-13.569-2.5128-14.622 5.9409-4.9408 199.41 1.7589 4.5302 6.1849 0.35355 15.203z" fill-opacity=".60766" transform="matrix(.92725 0 0 .73948 21.739 -223.04)" filter="url(#filter6293-3)"/>
|
|
||||||
<path id="rect10305-9-6-8-1-4" style="color:#000000" d="m399.81 876.33 0.13139 28.312-3.4465 5.2698-5.6095 2.303-6.4886 1.0153-12.445-1.4293-13.59 0.045-9.3261 0.7146-9.2417 1.3841-9.3578-1.1134-8.6554-0.3159-15.323 0.3989-12.099 1.0304-11.451-2.8586-8.0929 0.692-11.394 1.452-15.48-1.3992-11.922 1.4444-9.1305 0.095-7.3352-2.6253-11.175-3.4437-4.1083-6.8016-4.5122-11.706-2.4078-12.614 202.46 0.15063z" fill-opacity=".41627" transform="matrix(.97921 0 0 .79971 6.2101 -277.03)" filter="url(#filter7222)" fill="#635f4f"/>
|
|
||||||
<g id="g6187" transform="translate(0 -456.36)">
|
|
||||||
<path id="rect10307-2-2" style="color:#000000" d="m302 787.43-10.188 1.625-7.4375-0.6875-4.1875 1.875-10.969-0.65625-15.906-0.4375-12.375-0.71875-8.7188 3.375-9.125 0.3125-3.1562 2.7812-5.5312-0.46875-5.125 3.625-0.0312 0.0312v0.0312l-5.1562 6.4062-2.875 14.812-2.4375 16.688 0.0312 14.625-0.65625 24.844 2.9062 11.156 2.4688 4.75 1.5625 4.125 2.5312 3.3438v0.0312l1.625 2.7812 3.7812 2.8125v-0.0625l7.4375 4.5938 10.156-0.125 9.0312 0.0937 11.812 1.4375 15.312-1.375 11.281 1.4375 8 0.6875 11.344-2.8438 11.969 1.0314 15.188 0.375 8.5625-0.3125 9.25-1.0938 9.1562 1.375 9.25 0.71875 13.438 0.0312 12.312-1.4062 6.4375 1 4.0625-2.1875 2.4062-7.75 1.1875-16.219 1.2188-24.219-1.8438-14.25 0.3125-10.5h0.0625l-0.4375-18.438-5.8125-6.6562-1.625-6.7812-2.0938-1.625-5.0938-8.0312-5.4062-3.1562h-5.5625l-4.9688-0.0937-3.8438-2.25-4.5 0.6875-6.8438-0.9375-5.9688 1.9375-8.9062-0.4375-11-0.40625-10.219 0.6875-16.094-2z" fill="#c8c4b7"/>
|
|
||||||
<path id="rect10307-2-93" style="color:#000000" d="m302 787.42-10.188 1.625-7.4375-0.6875-4.1875 1.875-10.969-0.65625-15.906-0.4375-12.375-0.71875-8.7188 3.375-9.125 0.3125-3.1562 2.7812-5.5312-0.46875-5.125 3.625-0.0312 0.0312v0.0312l-5.1562 6.4062-2.875 14.812-2.4375 16.688 0.0312 14.625-0.65625 24.844 2.9062 11.156 2.4688 4.75 1.5625 4.125 2.5312 3.3438v0.031l1.625 2.7812 3.7812 2.8125v-0.062l7.4375 4.5938 10.156-0.125 9.0312 0.094 11.812 1.4375 15.312-1.375 11.281 1.4375 8 0.6875 11.344-2.8438 11.969 1.0315 15.188 0.375 8.5625-0.3125 9.25-1.0938 9.1562 1.375 9.25 0.7188 13.438 0.031 12.312-1.4063 6.4375 1 4.0625-2.1875 2.4062-7.75 1.1875-16.219 1.2188-24.219-1.8438-14.25 0.3125-10.5h0.0625l-0.4375-18.438-5.8125-6.6562-1.625-6.7812-2.0938-1.625-5.0938-8.0312-5.4062-3.1562h-5.5625l-4.9688-0.0937-3.8438-2.25-4.5 0.6875-6.8438-0.9375-5.9688 1.9375-8.9062-0.4375-11-0.40625-10.219 0.6875-16.094-2z" filter="url(#filter5703)" fill="#c8c4b7"/>
|
|
||||||
<path id="rect10307-2-40" opacity=".85446" style="color:#000000" d="m240.93 788.44-8.722 3.3744-9.1332 0.29289-3.159 2.7813-5.513-0.4688-5.6193 4.9375-1.552 5.9778-0.28856 7.9063 5.6345 0.1875 2.8704 4.625 3.4762-0.65782 5.4177 1.2203 3.1183 0.18775 2.5158-1.0469 5.7268 1.7656 4.223-1.0781 4.6232 0.43755 6.4071 4.1406 6.4091-0.3437 2.0368-0.39063 13.819-0.45317 11.132-0.5937 5.4025-0.66994 5.913 1.2891 9.9355 0.18187 5.9864-2.5133 14.125 1.1596 19.16-1.0722 5.2521 1.7751 5.1057-0.33764 14.012-0.0926 7.2048 2.9051 0.94166 0.2187 16.721-5.1562 4.131-0.1875 1.6858-2.25-5.7987-6.6648-1.6251-6.7813-2.111-1.625-5.096-8.0312-5.4137-3.1563h-5.5434l-4.9814-0.0937-3.8424-2.25-4.4803 0.6875-6.8647-0.9375-5.9721 1.9375-8.8882-0.4375-10.994-0.4063-10.234 0.70711-16.08-2.0196-10.206 1.625-7.4418-0.6875-4.1765 1.875-10.981-0.63843-15.894-0.4467-12.374-0.70791z" fill="#c8c4b7"/>
|
|
||||||
<path id="path4520-4-5" opacity=".85446" style="color:#000000" d="m295.67 821.86-5.9301-1.3438-5.388 0.7188-11.132 0.5937-13.732 0.46483 20.46 0.78517 5.103 3.0313 4.5562 0.4062 5.7864-1 5.8775 1.5h6.485l3.3108-2.9375 7.943-0.5 22.887-1.0312 4.8725-0.67192 4.3831 1.1485 4.1244 2.1796 2.3844-0.1875 9.5528-1.9375 2.8859 0.8125 2.9916-0.7812 3.3564 0.75-7.1606-2.9493-14.056 0.1368-5.0615 0.33764-5.2963-1.7751-19.513 0.7187-13.74-0.875-5.9984 2.5625-9.9516-0.1562zm-55.761-3.4688-4.1917 1.0625-5.7268-1.75-2.5001 1.0625-3.1379-0.16719-5.4701-1.2169-3.4533 0.66146 2.6241 4.8352 7.0306-2.6385 15.878 4.8384 3.7688-0.7812 5.6474 1.8125 6.0446-2.0313 2.9981-1.7994-2.0564 0.39317-6.4091 0.3437-6.4383-4.1562-4.6076-0.4688zm158.33 0.3438-4.131 0.1875-16.721 5.1562 6.6976 1.5 10.525-2.5 5.6953 11.844h0.0607l-0.43965-18.449-1.6866 2.2612h0.00005z" fill="#e3e2db"/>
|
|
||||||
<path id="rect10305-9-5" opacity=".85446" style="color:#000000" d="m215.44 818 2.6312 4.8729 7.0328-2.6372 15.87 4.8498 3.7519-0.7846 5.6678 1.7952 6.0397-2.0213 2.9938-1.7933 20.527 0.7826 5.1115 3.032 4.548 0.42551 5.7911-1.0106 5.878 1.516h6.487l3.3018-2.9522 7.9493-0.5053 22.891-1.0107 4.872-0.68284 4.3742 1.143 4.1299 2.1786 2.3796-0.1734 9.55-1.9601 2.893 0.82619 2.9862-0.77539 10.993 2.4759 10.526-2.5267 5.7016 11.871-0.31026 10.481 1.8466 14.246-1.231 24.242-1.1703 16.219-2.412 7.7302-4.0684 2.197-6.4236-1.0154-12.32 1.4293-13.454-0.0453-9.2326-0.7146-9.1491-1.384-9.264 1.1134-8.5687 0.31589-15.17-0.39891-11.978-1.0304-11.336 2.8586-8.0118-0.69202-11.279-1.452-15.325 1.3991-11.802-1.4444-9.039-0.0952-10.147 0.12536-7.6278-4.7279-2.5171-5.6126-2.217-9.5113 4.6262-58.082 2.7037-13.085v0.00004 0.00003z" fill="#aca793"/>
|
|
||||||
<path id="rect10309-8-0" opacity=".85446" style="color:#000000" d="m208.41 896.27-0.005-52.06-1.4772-19.14-1.524-9.5383-1.5111-3.6499 0.26834-4.6763 5.1214-9.1629-5.202 6.4739-2.8718 14.825-2.4333 16.683 0.0321 14.616-0.6476 24.862 2.9029 11.135 2.4543 4.7714 1.5812 4.11 2.5721 3.4418 0.73955-2.6911h0.00002z" fill="#aca793"/>
|
|
||||||
<path id="path4517-1-0" opacity=".85446" style="color:#000000" d="m214.44 794.41-5.2056 3.6638-5.066 9.134-0.27706 4.7026 1.5072 3.61 1.5339 9.5625 1.4732 19.125v41.156l4.3284-54.281 2.6578-12.812 0.0607-0.2185-2.8704-4.625-5.6345-0.1875 0.28856-7.9063 1.5868-5.9587 5.6169-4.9643z" fill="#e3e2db"/>
|
|
||||||
<path id="path4522-1-19" opacity=".85446" d="m208.31 885.27-0.18225 3.4688 0.27338-3.375-0.0911-0.094zm-0.19743 3.9375-0.50119 9.6875 1.6402 2.7813 3.7665 2.8125v-0.062l-0.18225-0.125-2.5211-5.625-2.2022-9.4688z" fill-rule="evenodd" fill="#797463"/>
|
|
||||||
<path id="rect10305-9-6-1" style="color:#000000" d="m215.44 818 2.6312 4.8729 7.0328-2.6372 15.87 4.8498 3.7519-0.7846 5.6678 1.7952 6.0397-2.0213 2.9938-1.7933 20.527 0.7826 5.1115 3.032 4.548 0.42551 5.7911-1.0106 5.878 1.516h6.487l3.3018-2.9522 7.9493-0.5053 22.891-1.0107 4.872-0.68284 4.3742 1.143 4.1299 2.1786 2.3796-0.1734 9.55-1.9601 2.893 0.82619 2.9862-0.77539 10.993 2.4759 10.526-2.5267 5.7016 11.871-0.31026 10.481 1.8466 14.246-1.231 24.242-1.1703 16.219-2.412 7.7302-4.0684 2.197-6.4236-1.0153-12.32 1.4293-13.454-0.045-9.2326-0.7146-9.1491-1.3841-9.264 1.1134-8.5687 0.3159-15.17-0.3989-11.978-1.0304-11.336 2.8586-8.0118-0.692-11.279-1.452-15.325 1.3992-11.802-1.4444-9.039-0.095-10.147 0.1253-7.6278-4.7279-2.5171-5.6126-2.217-9.5113 4.6262-58.082 2.7037-13.085v0.00004 0.00003z" fill="url(#linearGradient4024)"/>
|
|
||||||
<path id="rect10307-2-9-5" style="color:#000000" d="m240.93 788.44-8.722 3.3744-9.1332 0.29289-3.159 2.7813-5.513-0.4688-5.6193 4.9375-1.552 5.9778-0.28856 7.9063 5.6345 0.1875 2.8704 4.625 3.4762-0.65782 5.4177 1.2203 3.1496 0.12525 2.4689-0.96875 5.7893 1.6875 4.1605-1.0938 4.5763 0.4688 6.4696 4.1874 6.4091-0.3437 2.0368-0.39063 13.819-0.45317 11.132-0.5937 5.388-0.75005 5.8364 1.3438 10.077 0.12495 5.9672-2.4688 14.094 1.1973 19.16-1.0722 5.2575 1.7343 5.1003-0.29682 14.045-0.15625 7.1714 2.9688 0.94166 0.2187 16.721-5.1562 4.131-0.1875 1.6858-2.25-5.7987-6.6648-1.6251-6.7813-2.111-1.625-5.096-8.0312-5.4137-3.1563h-5.5434l-4.9814-0.0937-3.8424-2.25-4.4803 0.6875-6.8647-0.9375-5.9721 1.9375-8.8882-0.4375-10.994-0.4063-10.234 0.70711-16.08-2.0196-10.206 1.625-7.4418-0.6875-4.1765 1.875-10.981-0.63843-15.894-0.4467-12.374-0.70791z" fill="url(#linearGradient4026)"/>
|
|
||||||
<path id="rect10309-8-4-6" style="color:#000000" d="m208.41 896.27-0.005-52.06-1.4772-19.14-1.524-9.5383-1.5111-3.6499 0.26834-4.6763 5.1214-9.1629-5.202 6.4739-2.8718 14.825-2.4333 16.683 0.0321 14.616-0.6476 24.862 2.9029 11.135 2.4543 4.7714 1.5812 4.11 2.5721 3.4418 0.73955-2.6911h0.00002z" fill="url(#linearGradient4028)"/>
|
|
||||||
<path id="path4517-1-6-7" style="color:#000000" d="m214.44 794.41-5.2056 3.6638-5.066 9.134-0.27706 4.7026 1.5072 3.61 1.5339 9.5625 1.4732 19.125v41.156l4.3284-54.281 2.6578-12.812 0.0607-0.2185-2.8704-4.625-5.6345-0.1875 0.28856-7.9063 1.5868-5.9587 5.6169-4.9643z" fill="url(#linearGradient4030)"/>
|
|
||||||
<path id="path4520-4-2-3" style="color:#000000" d="m295.7 821.86-5.9926-1.3438-5.3568 0.7188-11.132 0.5937-13.732 0.46483 20.46 0.78517 5.103 3.0313 4.5562 0.4062 5.7864-1 5.8775 1.5h6.485l3.3108-2.9375 7.943-0.5 22.887-1.0312 4.8725-0.67192 4.3831 1.1485 4.1244 2.1796 2.3844-0.1875 9.5528-1.9375 2.8859 0.8125 2.9916-0.7812 3.3564 0.75-7.1245-2.9375-14.092 0.125-5.0066 0.31245-5.3512-1.75-19.513 0.7187-13.803-0.875-5.8734 2.5312-9.9829-0.12495zm-55.824-3.4688-4.1605 1-5.7268-1.75-2.5314 1.0625-3.1066-0.10469-5.4701-1.2169-3.4533 0.66146 2.6241 4.8352 7.0306-2.6385 15.878 4.8384 3.7688-0.7812 5.6474 1.8125 6.0446-2.0313 2.9981-1.7994-2.0564 0.39317-6.4091 0.3437-6.5008-4.1562-4.5763-0.4688zm158.36 0.3438-4.131 0.1875-16.721 5.1562 6.6976 1.5 10.525-2.5 5.6953 11.844h0.0607l-0.43965-18.449-1.6866 2.2612h0.00005z" fill="url(#linearGradient4032)"/>
|
|
||||||
<path id="rect10305-9-6-2-0" style="color:#000000" d="m213.3 828.62-2.5971 22.13-3.1409 39.489 3.8661 12.473-2.0177-12.023 1.0763-18.998 1.913-21.72 0.90036-21.35z" filter="url(#filter4536-1)" fill="#7f7a66"/>
|
|
||||||
<path id="path4520-4-2-2-6" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m397.96 818.25-3.875 0.1875h-0.0625-0.0625l-16.719 5.1562 0.28125 0.96875 16.625-5.125h0.0312l4.0625-0.1875h0.0312v-0.5l0.375 0.28125 0.75-1.9375-1.4374 1.1562zm-179.06-1.375-0.125 0.0312-3.4375 0.65625 0.1875 0.96875 3.3438-0.625 5.375 1.1875h0.0312 0.0625l3.0938 0.125h0.125l0.0937-0.0625 2.3438-0.96875 5.5938 1.6875 0.125 0.0312 0.125-0.0312 4-0.96875h0.0312l4.4375 0.46875 6.375 4.0625 0.15625 0.0937h0.15625l6.4062-0.34375h0.0312l0.0312-0.0312 2.0312-0.375h0.0312l13.719-0.43745 11.125-0.59375h0.0312l0.0312-0.0312 5.2188-0.6875 5.9375 1.3125 0.0625 0.0312h0.0312l10 0.125h0.0937l0.0937-0.0625 5.7188-2.4688 13.75 0.875h0.0312l19.438-0.71875 5.25 1.7188 0.0937 0.0312h0.0937l5-0.3125h0.0312l14-0.125 6.9688 2.875 0.40625-0.90625-7.125-2.9375-0.0937-0.0312h-0.125l-14.094 0.125-4.8438 0.3125h-0.0312l-5.3125-1.75-0.0937-0.0312-0.0625 0.0312-19.469 0.71875h-0.0625l-13.75-0.875-0.125-0.0312-0.0936 0.0625-5.8125 2.5-9.75-0.125-6-1.3438-0.0625-0.0312-0.0937 0.0312-5.3125 0.71875h-0.0625l-11.062 0.59375h-0.0312l-13.719 0.4375 0.0312 0.8125-0.15625-0.8125-2 0.40625h-0.0625l-6.1875 0.34375-6.375-4.0938-0.0937-0.0625h-0.125l-4.5625-0.46875-0.0937-0.0312-0.0625 0.0312-4.0312 0.96875-5.5938-1.7188-0.1875-0.0312-0.15625 0.0625-2.4375 1.0312-3-0.125-5.375-1.1875-0.0937-0.0312v-0.00005z" filter="url(#filter4596-6)" fill="#afaa98"/>
|
|
||||||
<path id="rect10309-8-4-2-0" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m203.83 811.86 1.0771 3.7672 1.5312 9.4688v0.0625l1.4688 19.062v0.0312l-0.35355 51.943 0.32322 2.2981 0.32322-2.9168 0.70711-51.355v-0.0312-0.0312l-1.4688-19.125v-0.0312l-1.5312-9.5625-2.0771-3.5798z" filter="url(#filter4654-6)" fill="#888470"/>
|
|
||||||
<path id="rect10305-9-6-8-1" style="color:#000000" d="m215.44 818 2.6312 4.8729 7.0328-2.6372 15.87 4.8498 3.7519-0.7846 5.6678 1.7952 6.0397-2.0213 2.9938-1.7933 20.527 0.7826 5.1115 3.032 4.548 0.42551 5.7911-1.0106 5.878 1.516h6.487l3.3018-2.9522 7.9493-0.5053 22.891-1.0107 4.872-0.68284 4.3742 1.143 4.1299 2.1786 2.3796-0.1734 9.55-1.9601 2.893 0.82619 2.9862-0.77539 10.993 2.4759 10.526-2.5267 5.7016 11.871-0.31026 10.481 1.8466 14.246-1.231 24.242-1.1703 16.219-2.412 7.7302-4.0684 2.197-6.4236-1.0153-12.32 1.4293-13.454-0.045-9.2326-0.7146-9.1491-1.3841-9.264 1.1134-8.5687 0.3159-15.17-0.3989-11.978-1.0304-11.336 2.8586-8.0118-0.692-11.279-1.452-15.325 1.3992-11.802-1.4444-9.039-0.095-10.147 0.1253-7.6278-4.7279-2.5171-5.6126-2.217-9.5113 4.6262-58.082 2.7037-13.085v0.00004 0.00003z" fill="url(#linearGradient4034)"/>
|
|
||||||
<path id="path4517-1-6-8-9" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m209.01 798.41-0.125 0.0937-0.0312 0.15625-1.8589 6.0259v0.0625 0.0625l-0.3125 7.9062v0.5l0.5 0.0312 5.2424 0.36428 2.7263 4.0107 0.52275 0.49686-2.429-5.4656-0.125-0.21875h-0.28125l-5.125-0.15625 0.28125-7.4375 1.5312-5.6875 4.7487-4.3005-5.2648 3.5559z" filter="url(#filter4766-3)" fill="#e3e2db"/>
|
|
||||||
</g>
|
|
||||||
<path id="path7353" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m240.5 829.86v61h-22.719v5h27.719v-61h59v61h30v-61h16v61h28.5v-5h-23.5v-61h-26v61h-20v-61h-69z" fill-opacity=".40670" transform="translate(0 -452.36)" fill="#f2f1ef"/>
|
|
||||||
<path id="path7353-5" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m240.5 377.5v61h-22.719v5h1v-3h22.719v-61h68v-2h-69zm89 0v61h-19v2h20v-61h25v-2h-26zm-25 7v59h1v-59h-1zm46 0v59h1v-59h-1zm6 54v2h22.5v-2h-22.5z" filter="url(#filter7410)" fill="#4b473c"/>
|
|
||||||
<path id="path7353-8" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m308.5 829.86v60h1v-60h-1zm46 0v60h1v-60h-1zm-110 4v61h-26.719v1h27.719v-61h58v-1h-59zm89 0v61h-29v1h30v-61h15v-1h-16zm44.5 57v4h-27.5v1h28.5v-5h-1z" transform="translate(0 -452.36)" filter="url(#filter7414)" fill="#e2e0da"/>
|
|
||||||
<path id="path4332" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m295 109.34v151.53 91.531h10v-91.531-151.53h-10z" fill="#d4aa00"/>
|
|
||||||
<path id="path4332-4" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m297 109.34v243.06h6v-243.06h-6z" fill-opacity=".54440" filter="url(#filter5536)" fill="#fcff4c"/>
|
|
||||||
<path id="path4332-3" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m274.94 116.59-9.875 1.5c7.92 51.61 19.94 109.69 19.94 162.79v71.531h10v-71.531c0-54.598-12.202-113.07-20.062-164.28z" fill="#d45500"/>
|
|
||||||
<path id="path4332-3-9" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m272.94 116.59-5.875 1.5c7.92 51.61 19.94 109.69 19.94 162.79v71.531h6v-71.531c0-54.598-12.202-113.07-20.062-164.28z" fill-opacity=".40927" filter="url(#filter5470)" fill="#fca"/>
|
|
||||||
<path id="path4332-3-5" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m244.75 133.78-9.5 3.125c19.28 58.02 39.75 96.67 39.75 183.98v31.531h10v-31.531c0-88.882-21.224-129.84-40.25-187.09z" fill="#a00"/>
|
|
||||||
<path id="path4332-3-5-8" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m242.75 133.78-3.5 3.125c19.277 58.01 38.25 96.664 38.25 183.97v31.531h5.5v-31.531c0-88.882-21.224-129.84-40.25-187.09z" fill-opacity=".47490" filter="url(#filter5412)" fill="#faa"/>
|
|
||||||
<path id="path4332-3-5-7" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m214.19 156.62-8.375 5.4688c28.45 43.57 59.18 77.47 59.18 158.79v31.531h10v-31.531c0-83.882-32.86-121.45-60.812-164.25z" fill="#784421"/>
|
|
||||||
<path id="path4332-3-5-7-0" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m215.19 162.62-4.375-0.53125c28.45 43.57 57.68 77.47 57.68 158.79v31.531h4v-31.531c0-83.882-29.36-115.45-57.312-158.25z" fill-opacity=".37838" filter="url(#filter5354)" fill="#deaa87"/>
|
|
||||||
<path id="path4332-3-5-7-3" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m183.16 185.47-6.3125 7.75c28.98 23.48 78.15 58.28 78.15 127.66v31.531h10v-31.531c0-74.139-53.403-112.37-81.844-135.41z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4332-3-5-7-3-15" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m183.16 189.47-4.3125 1.75c28.98 23.48 79.65 53.27 79.65 129.66v31.531h4v-31.531c0-71.639-50.903-108.37-79.344-131.41z" fill-opacity=".31274" filter="url(#filter5288)" fill="#fff"/>
|
|
||||||
<path id="path4332-3-0" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m325.06 116.59c-7.86 51.21-20.06 109.69-20.06 164.29v71.531h10v-71.531c0-53.099 12.017-111.18 19.938-162.78l-9.875-1.5z" fill="#008000"/>
|
|
||||||
<path id="path4332-3-9-6" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m272.94 112.59-5.875 1.5c7.92 51.61 19.94 109.69 19.94 162.79v71.531h6v-71.531c0-54.598-12.202-113.07-20.062-164.28z" fill-opacity=".40927" transform="matrix(-1 0 0 1 600.06 4)" filter="url(#filter5470-6)" fill="#cfa"/>
|
|
||||||
<path id="path4332-3-5-91" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m355.25 133.78c-19.03 57.26-40.25 98.21-40.25 187.1v31.531h10v-31.531c0-87.304 20.473-125.96 39.75-183.97l-9.5-3.125z" fill="#04a"/>
|
|
||||||
<path id="path4332-3-5-8-6" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m242.75 129.78-3.5 3.125c19.277 58.01 38.25 96.664 38.25 183.97v31.531h5.5v-31.531c0-88.882-21.224-129.84-40.25-187.09z" fill-opacity=".47490" transform="matrix(-1,0,0,1,600.25,4)" filter="url(#filter5412-5)" fill="#acf"/>
|
|
||||||
<path id="path4332-3-5-7-1" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m385.81 156.62c-27.95 42.81-60.81 80.37-60.81 164.26v31.531h10v-31.531c0-81.315 30.735-115.21 59.188-158.78l-8.375-5.4688z" fill="#aa00d4"/>
|
|
||||||
<path id="path4332-3-5-7-0-9" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m275.19 158.62-4.375-0.53125c28.45 43.57 57.68 77.47 57.68 158.79v31.531h4v-31.531c0-83.882-29.36-115.45-57.312-158.25z" fill-opacity=".48263" transform="matrix(-1 0 0 1 660.81 4)" filter="url(#filter5354-5)" fill="#eaf"/>
|
|
||||||
<path id="path4332-3-5-7-3-0" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m416.84 185.47c-28.44 23.03-81.84 61.27-81.84 135.41v31.531h10v-31.531c0-69.374 49.168-104.18 78.156-127.66l-6.3125-7.75z" fill="#6f6f91"/>
|
|
||||||
<path id="path4332-3-5-7-3-15-1" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m183.16 185.47-4.3125 1.75c28.98 23.48 78.15 55.27 78.15 129.65v31.531h5v-31.531c0-69.13-50.4-108.37-78.84-131.4z" fill-opacity=".42857" transform="matrix(-1 0 0 1 598.84 4)" filter="url(#filter5288-0)" fill="#dbdbe3"/>
|
|
||||||
<path id="rect4622-0" style="color:#000000" d="m225.64 343.5h148.71v16.777c-38.37 1.6959-81.343 2.6948-148.71 0v-16.777z" fill="url(#radialGradient4036)"/>
|
|
||||||
<path id="rect4622" style="color:#000000" d="m253.93 333h92.143v20.357c-23.774 2.0577-50.401 3.2698-92.143 0v-20.357z" fill="#1a1a1a"/>
|
|
||||||
<g id="g4720" transform="translate(0,8)">
|
|
||||||
<path id="rect4625" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g4720-2" transform="matrix(.98787 -.15526 .15526 .98787 -42.271 63.417)">
|
|
||||||
<path id="rect4625-0" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657-9" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9-0" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g4720-6" transform="matrix(.94451 -.32848 .32848 .94451 -79.478 131.23)">
|
|
||||||
<path id="rect4625-93" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657-5" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9-6" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g4720-0" transform="matrix(.84411 -.53617 .53617 .84411 -103.8 224.17)">
|
|
||||||
<path id="rect4625-6" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657-2" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9-1" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g4720-4" transform="matrix(.63008 -.77653 .77653 .63008 -96.161 351.53)">
|
|
||||||
<path id="rect4625-05" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657-0" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9-2" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g4720-2-1" transform="matrix(-.98787 -.15526 -.15526 .98787 642.52 63.417)">
|
|
||||||
<path id="rect4625-0-8" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657-9-4" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9-0-8" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g4720-6-1" transform="matrix(-.94451 -.32848 -.32848 .94451 679.72 131.23)">
|
|
||||||
<path id="rect4625-93-8" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657-5-1" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9-6-7" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g4720-0-8" transform="matrix(-.84411 -.53617 -.53617 .84411 704.05 224.17)">
|
|
||||||
<path id="rect4625-6-8" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657-2-3" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9-1-5" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<g id="g4720-4-5" transform="matrix(-.63008 -.77653 -.77653 .63008 696.41 351.53)">
|
|
||||||
<path id="rect4625-05-6" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
|
|
||||||
<path id="path4657-0-4" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
|
|
||||||
<path id="rect4625-9-2-6" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
|
|
||||||
</g>
|
|
||||||
<path id="rect4622-8" style="color:#000000" d="m254.32 333.77h91.362v2.5144c-23.573 1.6946-49.974 2.6928-91.362 0v-2.5144z" fill="url(#linearGradient4074)"/>
|
|
||||||
<path id="rect4622-8-0" style="color:#000000" d="m338.82 333.63h6.7367v19.615c-3.3472-0.0173-4.745 0.15065-6.7367 0v-19.615z" fill="url(#linearGradient4076)"/>
|
|
||||||
<path id="rect4622-8-0-4" style="color:#000000" d="m255.07 308.68h90.487v24.565c-23.347 1.9827-49.495 3.1506-90.487 0v-24.565z" fill="url(#linearGradient4078)"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Creative Commons CC0 1.0 Universal (CC0-1.0, Public Domain Dedication). -->
|
|
||||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
|
||||||
<mime-type type="application/vnd.sigrok.session">
|
|
||||||
<comment>sigrok session</comment>
|
|
||||||
<glob pattern="*.sr"/>
|
|
||||||
<icon name="libsigrok"/>
|
|
||||||
</mime-type>
|
|
||||||
</mime-info>
|
|
||||||
|
|
@ -0,0 +1,265 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2010-2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
##
|
||||||
|
|
||||||
|
##
|
||||||
|
## Please keep this list sorted alphabetically by vendor/device name.
|
||||||
|
##
|
||||||
|
|
||||||
|
ACTION!="add|change", GOTO="libsigrok_rules_end"
|
||||||
|
SUBSYSTEM!="usb|usb_device", GOTO="libsigrok_rules_end"
|
||||||
|
|
||||||
|
# Acute PKLA-1216
|
||||||
|
# http://www.acute.com.tw/product/la.php
|
||||||
|
# lsusb: "05e3:0136 Genesys Logic, Inc."
|
||||||
|
ATTRS{idVendor}=="05e3", ATTRS{idProduct}=="0136", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# ASIX SIGMA and SIGMA2
|
||||||
|
# http://tools.asix.net/dbg_sigma.htm
|
||||||
|
# lsusb: "a600:a000 Asix"
|
||||||
|
ATTRS{idVendor}=="a600", ATTRS{idProduct}=="a000", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Braintechnology USB-LPS
|
||||||
|
# http://www.braintechnology.de/braintechnology/usb_lps.html
|
||||||
|
# lsusb: "16d0:0498" (no string for the vendor name available)
|
||||||
|
ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0498", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Buspirate (v3)
|
||||||
|
# http://dangerousprototypes.com/2009/11/03/bus-pirate-logic-analyzer-mode/
|
||||||
|
# lsusb: "0403:6001 Future Technology Devices International,
|
||||||
|
# Ltd FT232 USB-Serial (UART) IC"
|
||||||
|
#
|
||||||
|
# ChronoVu LA8
|
||||||
|
# http://www.chronovu.com/
|
||||||
|
# lsusb: "0403:6001 Future Technology Devices International,
|
||||||
|
# Ltd FT232 USB-Serial (UART) IC"
|
||||||
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# CWAV USBee AX
|
||||||
|
# http://www.usbee.com/ax.html
|
||||||
|
# lsusb: "08a9:0014" (no string for the vendor name available)
|
||||||
|
#
|
||||||
|
# EE Electronics ESLA201A (clone of the CWAV USBee AX)
|
||||||
|
# http://eeelec.com/xla/
|
||||||
|
# lsusb: "08a9:0014" (no string for the vendor name available)
|
||||||
|
#
|
||||||
|
# ARMFLY AX-Pro (clone of the CWAV USBee AX)
|
||||||
|
# http://www.armfly.com/product/AX-Pro/ax-pro.htm
|
||||||
|
# lsusb: "08a9:0014" (no string for the vendor name available)
|
||||||
|
ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0014", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# CWAV USBee DX
|
||||||
|
# http://www.usbee.com/dx.html
|
||||||
|
# lsusb: "08a9:0015" (no string for the vendor name available)
|
||||||
|
#
|
||||||
|
# XZL Studio DX (clone of the CWAV USBee DX)
|
||||||
|
# lsusb: "08a9:0015" (no string for the vendor name available)
|
||||||
|
ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0015", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# CWAV USBee SX
|
||||||
|
# http://www.usbee.com/sx.html
|
||||||
|
# lsusb: "08a9:0009" (no string for the vendor name available)
|
||||||
|
ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0009", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Cypress FX2 eval boards without EEPROM:
|
||||||
|
#
|
||||||
|
# Lcsoft Mini Board
|
||||||
|
# http://sigrok.org/wiki/Lcsoft_Mini_Board
|
||||||
|
# lsusb: "04b4:8613 Cypress Semiconductor Corp. CY7C68013 EZ-USB FX2 USB 2.0 Development Kit"
|
||||||
|
#
|
||||||
|
# Braintechnology USB Interface V2.x
|
||||||
|
# http://www.braintechnology.de/braintechnology/en/usb_fastinterface27.html
|
||||||
|
# lsusb: "04b4:8613 Cypress Semiconductor Corp. CY7C68013 EZ-USB FX2 USB 2.0 Development Kit"
|
||||||
|
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Hantek DSO-2090
|
||||||
|
# http://www.hantek.com.cn/english/produce_list.asp?unid=62
|
||||||
|
# lsusb: "04b4:2090 Cypress Semiconductor Corp."
|
||||||
|
# lsusb after FW upload: "04b5:2090 ROHM LSI Systems USA, LLC"
|
||||||
|
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2090", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2090", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Hantek DSO-2150
|
||||||
|
# http://www.hantek.com.cn/english/produce_list.asp?unid=63
|
||||||
|
# lsusb: "04b4:2150 Cypress Semiconductor Corp."
|
||||||
|
# lsusb after FW upload: "04b5:2150 ROHM LSI Systems USA, LLC"
|
||||||
|
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2150", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2150", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Hantek DSO-2250
|
||||||
|
# http://www.hantek.com.cn/english/produce_list.asp?unid=64
|
||||||
|
# lsusb: "04b4:2250 Cypress Semiconductor Corp."
|
||||||
|
# lsusb after FW upload: "04b5:2250 ROHM LSI Systems USA, LLC"
|
||||||
|
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2250", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2250", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Hantek DSO-5200
|
||||||
|
# http://www.hantek.com.cn/english/produce_list.asp?unid=27
|
||||||
|
# lsusb: "04b4:5200 Cypress Semiconductor Corp."
|
||||||
|
# lsusb after FW upload: "04b5:5200 ROHM LSI Systems USA, LLC"
|
||||||
|
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="5200", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="5200", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Hantek DSO-5200A
|
||||||
|
# http://www.hantek.com.cn/english/produce_list.asp?unid=66
|
||||||
|
# lsusb: "04b4:520a Cypress Semiconductor Corp."
|
||||||
|
# lsusb after FW upload: "04b5:520a ROHM LSI Systems USA, LLC"
|
||||||
|
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="520a", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="520a", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Ideofy LA-08
|
||||||
|
# http://www.ideofy.com/la-08_en
|
||||||
|
# lsusb: "1fff:0100" (no string for the vendor name available)
|
||||||
|
ATTRS{idVendor}=="1fff", ATTRS{idProduct}=="0100", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Ikalogic SCANALOGIC-2 PRO
|
||||||
|
# http://www.ikalogic.com/scanalogic2/
|
||||||
|
# lsusb: "20a0:4123" (no string for the vendor name available)
|
||||||
|
ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4123", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Intronix Logicport LA1034
|
||||||
|
# http://www.pctestinstruments.com/
|
||||||
|
# lsusb: "0403:dc48 Future Technology Devices International, Ltd"
|
||||||
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="dc48", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Lascar Electronics EL-USB series
|
||||||
|
# This is actually the generic SILabs (Cygnal) F32x USBXpress VID:PID
|
||||||
|
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="0002", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Link Instruments MSO-19
|
||||||
|
# http://www.linkinstruments.com/mso19.htm
|
||||||
|
# lsusb: "3195:f190 Silicon Labs"
|
||||||
|
ATTRS{idVendor}=="3195", ATTRS{idProduct}=="f190", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Logic Shrimp
|
||||||
|
# http://dangerousprototypes.com/docs/Logic_Shrimp_logic_analyzer
|
||||||
|
# lsusb: "04d8:fa95 Microchip Technology, Inc."
|
||||||
|
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa95", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Microchip PICkit2
|
||||||
|
# http://www.microchip.com/pickit2
|
||||||
|
# lsusb: "04d8:0033 Microchip Technology, Inc. PICkit2"
|
||||||
|
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="0033", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# MiniLA Mockup
|
||||||
|
# http://www.mikrocontroller.net/articles/Minila_Version_MockUp
|
||||||
|
# lsusb: "0403:6010 Future Technology Devices International, Ltd FT2232C Dual USB-UART/FIFO IC"
|
||||||
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Nexus-Computing OsciPrime
|
||||||
|
# http://www.osciprime.com/
|
||||||
|
# The device comes up with the default Cypress FX2 VID:PID (04b4:8613), but
|
||||||
|
# after firmware upload enumerates as this:
|
||||||
|
# lsusb: "04b4:1004 Cypress Semiconductor Corp."
|
||||||
|
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="1004", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Openbench Logic Sniffer
|
||||||
|
# http://www.gadgetfactory.net/gf/project/butterflylogic/
|
||||||
|
# http://dangerousprototypes.com/open-logic-sniffer/
|
||||||
|
# lsusb: "04d8:000a Microchip Technology, Inc."
|
||||||
|
ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# PoLabs PoScope Basic2
|
||||||
|
# http://www.poscope.com/poscope_basic_2
|
||||||
|
# lsusb: "10c4:ea67 Cygnal Integrated Products, Inc."
|
||||||
|
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea67", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# QuantAsylum QA100
|
||||||
|
# http://www.quantasylum.com/content/Products/QA100.aspx
|
||||||
|
# lsusb: "16c0:4e21 VOTI"
|
||||||
|
ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="4e21", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Rigol VS5202D
|
||||||
|
# http://int.rigol.com/prodserv/Discontinued%20products/
|
||||||
|
# lsusb: "0400:03e8 National Semiconductor Corp."
|
||||||
|
ATTRS{idVendor}=="0400", ATTRS{idProduct}=="03e8", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Rigol DS1052E/1102E
|
||||||
|
# lsusb: 1ab1:0588
|
||||||
|
ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0588", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# RockyLogic Ant8
|
||||||
|
# http://www.rockylogic.com/products/ant8.html
|
||||||
|
# lsusb: "0403:f918 Future Technology Devices International,
|
||||||
|
# Ltd Ant8 Logic Probe"
|
||||||
|
#
|
||||||
|
# RockyLogic Ant18e
|
||||||
|
# http://www.rockylogic.com/products/ant18e.html
|
||||||
|
# lsusb: "0403:f918 Future Technology Devices International,
|
||||||
|
# Ltd Ant8 Logic Probe"
|
||||||
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="f918", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Saleae Logic
|
||||||
|
# http://www.saleae.com/logic/
|
||||||
|
# lsusb: "0925:3881 Lakeview Research"
|
||||||
|
#
|
||||||
|
# EE Electronics ESLA100 (clone of the Saleae Logic)
|
||||||
|
# http://eeelec.com/xla/
|
||||||
|
# lsusb: "0925:3881 Lakeview Research"
|
||||||
|
#
|
||||||
|
# Robomotic MiniLogic (clone of the Saleae Logic)
|
||||||
|
# http://buglogic.robomotic.com/
|
||||||
|
# lsusb: "0925:3881 Lakeview Research"
|
||||||
|
#
|
||||||
|
# Robomotic BugLogic 3 (clone of the Saleae Logic)
|
||||||
|
# http://norduino.robomotic.com/products-page/categories/buglogic3/
|
||||||
|
# lsusb: "0925:3881 Lakeview Research"
|
||||||
|
ATTRS{idVendor}=="0925", ATTRS{idProduct}=="3881", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Saleae Logic16
|
||||||
|
# http://www.saleae.com/logic16/
|
||||||
|
# lsusb: "21a9:1001" (no string for the vendor name available)
|
||||||
|
ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1001", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# UNI-T UT-D04 multimeter cable (for various DMMs)
|
||||||
|
# http://sigrok.org/wiki/Device_cables#UNI-T_UT-D04
|
||||||
|
# lsusb: "1a86:e008 QinHeng Electronics HID-based serial adapater"
|
||||||
|
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="e008", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Velleman PCSU1000
|
||||||
|
# http://www.velleman.eu/products/view/?id=362986
|
||||||
|
# lsusb: "10cf:1000 Velleman Components, Inc."
|
||||||
|
ATTRS{idVendor}=="10cf", ATTRS{idProduct}=="1000", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# Victor 70C
|
||||||
|
# http://www.china-victor.com/english/en/product_data.aspx?ClassID=168&ID=121
|
||||||
|
ATTRS{idVendor}=="1244", ATTRS{idProduct}=="d237", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# ZEROPLUS Logic Cube LAP-C series
|
||||||
|
# http://www.zeroplus.com.tw/logic-analyzer_en/products.php#top_c
|
||||||
|
# lsusb: "0c12:700e Zeroplus"
|
||||||
|
# There are various devices in the ZEROPLUS Logic Cube series:
|
||||||
|
# 0x7002: LAP-16128U
|
||||||
|
# 0x7009: LAP-C(16064)
|
||||||
|
# 0x700a: LAP-C(16128)
|
||||||
|
# 0x700b: LAP-C(32128)
|
||||||
|
# 0x700c: LAP-C(321000)
|
||||||
|
# 0x700d: LAP-C(322000)
|
||||||
|
# 0x700e: LAP-C(16032)
|
||||||
|
# 0x7016: LAP-C(162000)
|
||||||
|
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7002", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7009", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700a", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700b", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700c", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700d", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700e", MODE="664", GROUP="plugdev"
|
||||||
|
ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7016", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
LABEL="libsigrok_rules_end"
|
||||||
|
|
@ -0,0 +1,388 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "device: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* Device handling in libsigrok.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup grp_devices Devices
|
||||||
|
*
|
||||||
|
* Device handling in libsigrok.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
SR_PRIV struct sr_probe *sr_probe_new(int index, int type,
|
||||||
|
gboolean enabled, const char *name)
|
||||||
|
{
|
||||||
|
struct sr_probe *probe;
|
||||||
|
|
||||||
|
if (!(probe = g_try_malloc0(sizeof(struct sr_probe)))) {
|
||||||
|
sr_err("Probe malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
probe->index = index;
|
||||||
|
probe->type = type;
|
||||||
|
probe->enabled = enabled;
|
||||||
|
if (name)
|
||||||
|
probe->name = g_strdup(name);
|
||||||
|
|
||||||
|
return probe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the specified probe in the specified device.
|
||||||
|
*
|
||||||
|
* If the probe already has a different name assigned to it, it will be
|
||||||
|
* removed, and the new name will be saved instead.
|
||||||
|
*
|
||||||
|
* @param sdi The device instance the probe is connected to.
|
||||||
|
* @param probenum The number of the probe whose name to set.
|
||||||
|
* Note that the probe numbers start at 0.
|
||||||
|
* @param name The new name that the specified probe should get. A copy
|
||||||
|
* of the string is made.
|
||||||
|
*
|
||||||
|
* @return SR_OK on success, or SR_ERR_ARG on invalid arguments.
|
||||||
|
*
|
||||||
|
* @since 0.1.0 (but the API changed in 0.2.0)
|
||||||
|
*/
|
||||||
|
SR_API int sr_dev_probe_name_set(const struct sr_dev_inst *sdi,
|
||||||
|
int probenum, const char *name)
|
||||||
|
{
|
||||||
|
GSList *l;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!sdi) {
|
||||||
|
sr_err("%s: sdi was NULL", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SR_ERR_ARG;
|
||||||
|
for (l = sdi->probes; l; l = l->next) {
|
||||||
|
probe = l->data;
|
||||||
|
if (probe->index == probenum) {
|
||||||
|
g_free(probe->name);
|
||||||
|
probe->name = g_strdup(name);
|
||||||
|
ret = SR_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable a probe on the specified device.
|
||||||
|
*
|
||||||
|
* @param sdi The device instance the probe is connected to.
|
||||||
|
* @param probenum The probe number, starting from 0.
|
||||||
|
* @param state TRUE to enable the probe, FALSE to disable.
|
||||||
|
*
|
||||||
|
* @return SR_OK on success, or SR_ERR_ARG on invalid arguments.
|
||||||
|
*
|
||||||
|
* @since 0.2.0
|
||||||
|
*/
|
||||||
|
SR_API int sr_dev_probe_enable(const struct sr_dev_inst *sdi, int probenum,
|
||||||
|
gboolean state)
|
||||||
|
{
|
||||||
|
GSList *l;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!sdi)
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
|
||||||
|
ret = SR_ERR_ARG;
|
||||||
|
for (l = sdi->probes; l; l = l->next) {
|
||||||
|
probe = l->data;
|
||||||
|
if (probe->index == probenum) {
|
||||||
|
probe->enabled = state;
|
||||||
|
ret = SR_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a trigger to the specified device (and the specified probe).
|
||||||
|
*
|
||||||
|
* If the specified probe of this device already has a trigger, it will
|
||||||
|
* be silently replaced.
|
||||||
|
*
|
||||||
|
* @param sdi Must not be NULL.
|
||||||
|
* @param probenum The probe number, starting from 0.
|
||||||
|
* @param trigger Trigger string, in the format used by sigrok-cli
|
||||||
|
*
|
||||||
|
* @return SR_OK on success, or SR_ERR_ARG on invalid arguments.
|
||||||
|
*
|
||||||
|
* @since 0.1.0 (but the API changed in 0.2.0)
|
||||||
|
*/
|
||||||
|
SR_API int sr_dev_trigger_set(const struct sr_dev_inst *sdi, int probenum,
|
||||||
|
const char *trigger)
|
||||||
|
{
|
||||||
|
GSList *l;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!sdi)
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
|
||||||
|
ret = SR_ERR_ARG;
|
||||||
|
for (l = sdi->probes; l; l = l->next) {
|
||||||
|
probe = l->data;
|
||||||
|
if (probe->index == probenum) {
|
||||||
|
/* If the probe already has a trigger, kill it first. */
|
||||||
|
g_free(probe->trigger);
|
||||||
|
probe->trigger = g_strdup(trigger);
|
||||||
|
ret = SR_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the specified device instance has the specified
|
||||||
|
* capability.
|
||||||
|
*
|
||||||
|
* @param sdi Pointer to the device instance to be checked. Must not be NULL.
|
||||||
|
* If the device's 'driver' field is NULL (virtual device), this
|
||||||
|
* function will always return FALSE (virtual devices don't have
|
||||||
|
* a hardware capabilities list).
|
||||||
|
* @param key The option that should be checked for support on the
|
||||||
|
* specified device.
|
||||||
|
*
|
||||||
|
* @return TRUE if the device has the specified option, FALSE otherwise.
|
||||||
|
* FALSE is also returned on invalid input parameters or other
|
||||||
|
* error conditions.
|
||||||
|
*
|
||||||
|
* @since 0.1.0 (but the API changed in 0.2.0)
|
||||||
|
*/
|
||||||
|
SR_API gboolean sr_dev_has_option(const struct sr_dev_inst *sdi, int key)
|
||||||
|
{
|
||||||
|
GVariant *gvar;
|
||||||
|
const int *devopts;
|
||||||
|
gsize num_opts, i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!sdi || !sdi->driver || !sdi->driver->config_list)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (sdi->driver->config_list(SR_CONF_DEVICE_OPTIONS, &gvar, NULL) != SR_OK)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
ret = FALSE;
|
||||||
|
devopts = g_variant_get_fixed_array(gvar, &num_opts, sizeof(int32_t));
|
||||||
|
for (i = 0; i < num_opts; i++) {
|
||||||
|
if (devopts[i] == key) {
|
||||||
|
ret = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_variant_unref(gvar);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
SR_PRIV struct sr_dev_inst *sr_dev_inst_new(int index, int status,
|
||||||
|
const char *vendor, const char *model, const char *version)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
|
||||||
|
if (!(sdi = g_try_malloc(sizeof(struct sr_dev_inst)))) {
|
||||||
|
sr_err("Device instance malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdi->driver = NULL;
|
||||||
|
sdi->index = index;
|
||||||
|
sdi->status = status;
|
||||||
|
sdi->inst_type = -1;
|
||||||
|
sdi->vendor = vendor ? g_strdup(vendor) : NULL;
|
||||||
|
sdi->model = model ? g_strdup(model) : NULL;
|
||||||
|
sdi->version = version ? g_strdup(version) : NULL;
|
||||||
|
sdi->probes = NULL;
|
||||||
|
sdi->conn = NULL;
|
||||||
|
sdi->priv = NULL;
|
||||||
|
|
||||||
|
return sdi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_probe *probe;
|
||||||
|
GSList *l;
|
||||||
|
|
||||||
|
for (l = sdi->probes; l; l = l->next) {
|
||||||
|
probe = l->data;
|
||||||
|
g_free(probe->name);
|
||||||
|
g_free(probe);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(sdi->priv);
|
||||||
|
g_free(sdi->vendor);
|
||||||
|
g_free(sdi->model);
|
||||||
|
g_free(sdi->version);
|
||||||
|
g_free(sdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBUSB_1_0
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
SR_PRIV struct sr_usb_dev_inst *sr_usb_dev_inst_new(uint8_t bus,
|
||||||
|
uint8_t address, struct libusb_device_handle *hdl)
|
||||||
|
{
|
||||||
|
struct sr_usb_dev_inst *udi;
|
||||||
|
|
||||||
|
if (!(udi = g_try_malloc(sizeof(struct sr_usb_dev_inst)))) {
|
||||||
|
sr_err("USB device instance malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
udi->bus = bus;
|
||||||
|
udi->address = address;
|
||||||
|
udi->devhdl = hdl;
|
||||||
|
|
||||||
|
return udi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb)
|
||||||
|
{
|
||||||
|
(void)usb;
|
||||||
|
|
||||||
|
/* Nothing to do for this device instance type. */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* Both parameters are copied to newly allocated strings, and freed
|
||||||
|
* automatically by sr_serial_dev_inst_free().
|
||||||
|
*
|
||||||
|
* @param pathname OS-specific serial port specification. Examples:
|
||||||
|
* "/dev/ttyUSB0", "/dev/ttyACM1", "/dev/tty.Modem-0", "COM1".
|
||||||
|
* @param serialcomm A serial communication parameters string, in the form
|
||||||
|
* of <speed>/<data bits><parity><stopbits>, for example
|
||||||
|
* "9600/8n1" or "600/7o2". This is an optional parameter;
|
||||||
|
* it may be filled in later.
|
||||||
|
*
|
||||||
|
* @return A pointer to a newly initialized struct sr_serial_dev_inst,
|
||||||
|
* or NULL on error.
|
||||||
|
*/
|
||||||
|
SR_PRIV struct sr_serial_dev_inst *sr_serial_dev_inst_new(const char *port,
|
||||||
|
const char *serialcomm)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
if (!port) {
|
||||||
|
sr_err("Serial port required.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(serial = g_try_malloc0(sizeof(struct sr_serial_dev_inst)))) {
|
||||||
|
sr_err("Serial device instance malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
serial->port = g_strdup(port);
|
||||||
|
if (serialcomm)
|
||||||
|
serial->serialcomm = g_strdup(serialcomm);
|
||||||
|
serial->fd = -1;
|
||||||
|
|
||||||
|
return serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
SR_PRIV void sr_serial_dev_inst_free(struct sr_serial_dev_inst *serial)
|
||||||
|
{
|
||||||
|
g_free(serial->port);
|
||||||
|
g_free(serial->serialcomm);
|
||||||
|
g_free(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_API GSList *sr_dev_list(const struct sr_dev_driver *driver)
|
||||||
|
{
|
||||||
|
if (driver && driver->dev_list)
|
||||||
|
return driver->dev_list();
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_API int sr_dev_clear(const struct sr_dev_driver *driver)
|
||||||
|
{
|
||||||
|
if (driver && driver->dev_clear)
|
||||||
|
return driver->dev_clear();
|
||||||
|
else
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_API int sr_dev_open(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!sdi || !sdi->driver || !sdi->driver->dev_open)
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
ret = sdi->driver->dev_open(sdi);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_API int sr_dev_close(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!sdi || !sdi->driver || !sdi->driver->dev_close)
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
ret = sdi->driver->dev_close(sdi);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
#include "libsigrok.h"
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
|
|
@ -52,6 +52,8 @@
|
||||||
*/
|
*/
|
||||||
SR_API const char *sr_strerror(int error_code)
|
SR_API const char *sr_strerror(int error_code)
|
||||||
{
|
{
|
||||||
|
const char *str;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: All defined SR_* error macros from libsigrok.h must have
|
* Note: All defined SR_* error macros from libsigrok.h must have
|
||||||
* an entry in this function, as well as in sr_strerror_name().
|
* an entry in this function, as well as in sr_strerror_name().
|
||||||
|
|
@ -59,32 +61,35 @@ SR_API const char *sr_strerror(int error_code)
|
||||||
|
|
||||||
switch (error_code) {
|
switch (error_code) {
|
||||||
case SR_OK:
|
case SR_OK:
|
||||||
return "no error";
|
str = "no error";
|
||||||
|
break;
|
||||||
case SR_ERR:
|
case SR_ERR:
|
||||||
return "generic/unspecified error";
|
str = "generic/unspecified error";
|
||||||
|
break;
|
||||||
case SR_ERR_MALLOC:
|
case SR_ERR_MALLOC:
|
||||||
return "memory allocation error";
|
str = "memory allocation error";
|
||||||
|
break;
|
||||||
case SR_ERR_ARG:
|
case SR_ERR_ARG:
|
||||||
return "invalid argument";
|
str = "invalid argument";
|
||||||
|
break;
|
||||||
case SR_ERR_BUG:
|
case SR_ERR_BUG:
|
||||||
return "internal error";
|
str = "internal error";
|
||||||
|
break;
|
||||||
case SR_ERR_SAMPLERATE:
|
case SR_ERR_SAMPLERATE:
|
||||||
return "invalid samplerate";
|
str = "invalid samplerate";
|
||||||
|
break;
|
||||||
case SR_ERR_NA:
|
case SR_ERR_NA:
|
||||||
return "not applicable";
|
str = "not applicable";
|
||||||
|
break;
|
||||||
case SR_ERR_DEV_CLOSED:
|
case SR_ERR_DEV_CLOSED:
|
||||||
return "device closed but should be open";
|
str = "device closed but should be open";
|
||||||
case SR_ERR_TIMEOUT:
|
break;
|
||||||
return "timeout occurred";
|
|
||||||
case SR_ERR_CHANNEL_GROUP:
|
|
||||||
return "no channel group specified";
|
|
||||||
case SR_ERR_DATA:
|
|
||||||
return "data is invalid";
|
|
||||||
case SR_ERR_IO:
|
|
||||||
return "input/output error";
|
|
||||||
default:
|
default:
|
||||||
return "unknown error";
|
str = "unknown error";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -107,6 +112,8 @@ SR_API const char *sr_strerror(int error_code)
|
||||||
*/
|
*/
|
||||||
SR_API const char *sr_strerror_name(int error_code)
|
SR_API const char *sr_strerror_name(int error_code)
|
||||||
{
|
{
|
||||||
|
const char *str;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: All defined SR_* error macros from libsigrok.h must have
|
* Note: All defined SR_* error macros from libsigrok.h must have
|
||||||
* an entry in this function, as well as in sr_strerror().
|
* an entry in this function, as well as in sr_strerror().
|
||||||
|
|
@ -114,32 +121,35 @@ SR_API const char *sr_strerror_name(int error_code)
|
||||||
|
|
||||||
switch (error_code) {
|
switch (error_code) {
|
||||||
case SR_OK:
|
case SR_OK:
|
||||||
return "SR_OK";
|
str = "SR_OK";
|
||||||
|
break;
|
||||||
case SR_ERR:
|
case SR_ERR:
|
||||||
return "SR_ERR";
|
str = "SR_ERR";
|
||||||
|
break;
|
||||||
case SR_ERR_MALLOC:
|
case SR_ERR_MALLOC:
|
||||||
return "SR_ERR_MALLOC";
|
str = "SR_ERR_MALLOC";
|
||||||
|
break;
|
||||||
case SR_ERR_ARG:
|
case SR_ERR_ARG:
|
||||||
return "SR_ERR_ARG";
|
str = "SR_ERR_ARG";
|
||||||
|
break;
|
||||||
case SR_ERR_BUG:
|
case SR_ERR_BUG:
|
||||||
return "SR_ERR_BUG";
|
str = "SR_ERR_BUG";
|
||||||
|
break;
|
||||||
case SR_ERR_SAMPLERATE:
|
case SR_ERR_SAMPLERATE:
|
||||||
return "SR_ERR_SAMPLERATE";
|
str = "SR_ERR_SAMPLERATE";
|
||||||
|
break;
|
||||||
case SR_ERR_NA:
|
case SR_ERR_NA:
|
||||||
return "SR_ERR_NA";
|
str = "SR_ERR_NA";
|
||||||
|
break;
|
||||||
case SR_ERR_DEV_CLOSED:
|
case SR_ERR_DEV_CLOSED:
|
||||||
return "SR_ERR_DEV_CLOSED";
|
str = "SR_ERR_DEV_CLOSED";
|
||||||
case SR_ERR_TIMEOUT:
|
break;
|
||||||
return "SR_ERR_TIMEOUT";
|
|
||||||
case SR_ERR_CHANNEL_GROUP:
|
|
||||||
return "SR_ERR_CHANNEL_GROUP";
|
|
||||||
case SR_ERR_DATA:
|
|
||||||
return "SR_ERR_DATA";
|
|
||||||
case SR_ERR_IO:
|
|
||||||
return "SR_ERR_IO";
|
|
||||||
default:
|
default:
|
||||||
return "unknown error code";
|
str = "unknown error code";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "filter: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* Helper functions to filter out unused probes from samples.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup grp_filter Probe filter
|
||||||
|
*
|
||||||
|
* Helper functions to filter out unused probes from samples.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove unused probes from samples.
|
||||||
|
*
|
||||||
|
* Convert sample from maximum probes -- the way the hardware driver sent
|
||||||
|
* it -- to a sample taking up only as much space as required, with
|
||||||
|
* unused probes removed.
|
||||||
|
*
|
||||||
|
* The "unit size" is the number of bytes used to store probe values.
|
||||||
|
* For example, a unit size of 1 means one byte is used (which can store
|
||||||
|
* 8 probe values, each of them is 1 bit). A unit size of 2 means we can
|
||||||
|
* store 16 probe values, 3 means we can store 24 probe values, and so on.
|
||||||
|
*
|
||||||
|
* If the data coming from the logic analyzer has a unit size of 4 for
|
||||||
|
* example (as the device has 32 probes), but only 2 of them are actually
|
||||||
|
* used in an acquisition, this function can convert the samples to only
|
||||||
|
* use up 1 byte per sample (unit size = 1) instead of 4 bytes per sample.
|
||||||
|
*
|
||||||
|
* The output will contain the probe values in the order specified via the
|
||||||
|
* probelist. For example, if in_unitsize = 4, probelist = [5, 16, 30], and
|
||||||
|
* out_unitsize = 1, then the output samples (each of them one byte in size)
|
||||||
|
* will have the following format: bit 0 = value of probe 5, bit 1 = value
|
||||||
|
* of probe 16, bit 2 = value of probe 30. Unused bit(s) in the output byte(s)
|
||||||
|
* are zero.
|
||||||
|
*
|
||||||
|
* The caller must make sure that length_in is not bigger than the memory
|
||||||
|
* actually allocated for the input data (data_in), as this function does
|
||||||
|
* not check that.
|
||||||
|
*
|
||||||
|
* @param in_unitsize The unit size (>= 1) of the input (data_in).
|
||||||
|
* @param out_unitsize The unit size (>= 1) the output shall have (data_out).
|
||||||
|
* The requested unit size must be big enough to hold as
|
||||||
|
* much data as is specified by the number of enabled
|
||||||
|
* probes in 'probelist'.
|
||||||
|
* @param probe_array Pointer to a list of probe numbers, numbered starting
|
||||||
|
* from 0. The list is terminated with -1.
|
||||||
|
* @param data_in Pointer to the input data buffer. Must not be NULL.
|
||||||
|
* @param length_in The input data length (>= 1), in number of bytes.
|
||||||
|
* @param data_out Variable which will point to the newly allocated buffer
|
||||||
|
* of output data. The caller is responsible for g_free()'ing
|
||||||
|
* the buffer when it's no longer needed. Must not be NULL.
|
||||||
|
* @param length_out Pointer to the variable which will contain the output
|
||||||
|
* data length (in number of bytes) when the function
|
||||||
|
* returns SR_OK. Must not be NULL.
|
||||||
|
*
|
||||||
|
* @return SR_OK upon success, SR_ERR_MALLOC upon memory allocation errors,
|
||||||
|
* or SR_ERR_ARG upon invalid arguments.
|
||||||
|
* If something other than SR_OK is returned, the values of
|
||||||
|
* out_unitsize, data_out, and length_out are undefined.
|
||||||
|
*
|
||||||
|
* @since 0.1.0 (but the API changed in 0.2.0)
|
||||||
|
*/
|
||||||
|
SR_API int sr_filter_probes(unsigned int in_unitsize, unsigned int out_unitsize,
|
||||||
|
const GArray *probe_array, const uint8_t *data_in,
|
||||||
|
uint64_t length_in, uint8_t **data_out,
|
||||||
|
uint64_t *length_out)
|
||||||
|
{
|
||||||
|
unsigned int in_offset, out_offset;
|
||||||
|
int *probelist, out_bit;
|
||||||
|
unsigned int i;
|
||||||
|
uint64_t sample_in, sample_out;
|
||||||
|
|
||||||
|
if (!probe_array) {
|
||||||
|
sr_err("%s: probe_array was NULL", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
probelist = (int *)probe_array->data;
|
||||||
|
|
||||||
|
if (!data_in) {
|
||||||
|
sr_err("%s: data_in was NULL", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data_out) {
|
||||||
|
sr_err("%s: data_out was NULL", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!length_out) {
|
||||||
|
sr_err("%s: length_out was NULL", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Are there more probes than the target unit size supports? */
|
||||||
|
if (probe_array->len > out_unitsize * 8) {
|
||||||
|
sr_err("%s: too many probes (%d) for the target unit "
|
||||||
|
"size (%d)", __func__, probe_array->len, out_unitsize);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(*data_out = g_try_malloc(length_in))) {
|
||||||
|
sr_err("%s: data_out malloc failed", __func__);
|
||||||
|
return SR_ERR_MALLOC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probe_array->len == in_unitsize * 8) {
|
||||||
|
/* All probes are used -- no need to compress anything. */
|
||||||
|
memcpy(*data_out, data_in, length_in);
|
||||||
|
*length_out = length_in;
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we reached this point, not all probes are used, so "compress". */
|
||||||
|
in_offset = out_offset = 0;
|
||||||
|
while (in_offset <= length_in - in_unitsize) {
|
||||||
|
memcpy(&sample_in, data_in + in_offset, in_unitsize);
|
||||||
|
sample_out = out_bit = 0;
|
||||||
|
for (i = 0; i < probe_array->len; i++) {
|
||||||
|
if (sample_in & (1 << (probelist[i])))
|
||||||
|
sample_out |= (1 << out_bit);
|
||||||
|
out_bit++;
|
||||||
|
}
|
||||||
|
memcpy((*data_out) + out_offset, &sample_out, out_unitsize);
|
||||||
|
in_offset += in_unitsize;
|
||||||
|
out_offset += out_unitsize;
|
||||||
|
}
|
||||||
|
*length_out = out_offset;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
## Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
SUBDIRS = \
|
||||||
|
agilent-dmm \
|
||||||
|
alsa \
|
||||||
|
asix-sigma \
|
||||||
|
brymen-dmm \
|
||||||
|
chronovu-la8 \
|
||||||
|
colead-slm \
|
||||||
|
common \
|
||||||
|
demo \
|
||||||
|
fluke-dmm \
|
||||||
|
fx2lafw \
|
||||||
|
hantek-dso \
|
||||||
|
lascar-el-usb \
|
||||||
|
mic-985xx \
|
||||||
|
openbench-logic-sniffer \
|
||||||
|
rigol-ds1xx2 \
|
||||||
|
serial-dmm \
|
||||||
|
tondaj-sl-814 \
|
||||||
|
uni-t-dmm \
|
||||||
|
victor-dmm \
|
||||||
|
zeroplus-logic-cube
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = libsigrokhardware.la
|
||||||
|
|
||||||
|
libsigrokhardware_la_SOURCES =
|
||||||
|
|
||||||
|
libsigrokhardware_la_LIBADD = \
|
||||||
|
common/libsigrokhwcommon.la
|
||||||
|
|
||||||
|
if HW_AGILENT_DMM
|
||||||
|
libsigrokhardware_la_LIBADD += agilent-dmm/libsigrokhwagilentdmm.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_ALSA
|
||||||
|
libsigrokhardware_la_LIBADD += alsa/libsigrokhwalsa.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if LA_ASIX_SIGMA
|
||||||
|
libsigrokhardware_la_LIBADD += asix-sigma/libsigrokhwasixsigma.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_BRYMEN_DMM
|
||||||
|
libsigrokhardware_la_LIBADD += brymen-dmm/libsigrok_hw_brymen_dmm.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if LA_CHRONOVU_LA8
|
||||||
|
libsigrokhardware_la_LIBADD += chronovu-la8/libsigrokhwchronovula8.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_COLEAD_SLM
|
||||||
|
libsigrokhardware_la_LIBADD += colead-slm/libsigrok_hw_colead_slm.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if LA_DEMO
|
||||||
|
libsigrokhardware_la_LIBADD += demo/libsigrokhwdemo.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_FLUKE_DMM
|
||||||
|
libsigrokhardware_la_LIBADD += fluke-dmm/libsigrokhwflukedmm.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if LA_FX2LAFW
|
||||||
|
libsigrokhardware_la_LIBADD += fx2lafw/libsigrokhwfx2lafw.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_HANTEK_DSO
|
||||||
|
libsigrokhardware_la_LIBADD += hantek-dso/libsigrokhw_hantek_dso.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_LASCAR_EL_USB
|
||||||
|
libsigrokhardware_la_LIBADD += lascar-el-usb/libsigrok_hw_lascar_el_usb.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_MIC_985XX
|
||||||
|
libsigrokhardware_la_LIBADD += mic-985xx/libsigrok_hw_mic_985xx.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if LA_OLS
|
||||||
|
libsigrokhardware_la_LIBADD += openbench-logic-sniffer/libsigrokhwols.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_RIGOL_DS1XX2
|
||||||
|
libsigrokhardware_la_LIBADD += rigol-ds1xx2/libsigrok_hw_rigol_ds1xx2.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_SERIAL_DMM
|
||||||
|
libsigrokhardware_la_LIBADD += serial-dmm/libsigrokhwserialdmm.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_TONDAJ_SL_814
|
||||||
|
libsigrokhardware_la_LIBADD += tondaj-sl-814/libsigrok_hw_tondaj_sl_814.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_UNI_T_DMM
|
||||||
|
libsigrokhardware_la_LIBADD += uni-t-dmm/libsigrok_hw_uni_t_dmm.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HW_VICTOR_DMM
|
||||||
|
libsigrokhardware_la_LIBADD += victor-dmm/libsigrok_hw_victor_dmm.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
if LA_ZEROPLUS_LOGIC_CUBE
|
||||||
|
libsigrokhardware_la_LIBADD += zeroplus-logic-cube/libsigrokhwzeroplus.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
if HW_AGILENT_DMM
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrokhwagilentdmm.la
|
||||||
|
|
||||||
|
libsigrokhwagilentdmm_la_SOURCES = \
|
||||||
|
api.c \
|
||||||
|
agilent-dmm.h \
|
||||||
|
sched.c
|
||||||
|
|
||||||
|
libsigrokhwagilentdmm_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
@ -17,101 +17,63 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LIBSIGROK_HARDWARE_AGILENT_DMM_PROTOCOL_H
|
|
||||||
#define LIBSIGROK_HARDWARE_AGILENT_DMM_PROTOCOL_H
|
|
||||||
|
|
||||||
#define LOG_PREFIX "agilent-dmm"
|
#ifndef LIBSIGROK_AGILENT_DMM_H
|
||||||
|
#define LIBSIGROK_AGILENT_DMM_H
|
||||||
|
|
||||||
#define MAX_CHANNELS 3
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
#define AGDMM_BUFSIZE 256
|
#define LOG_PREFIX "agilent-dmm: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
/* Always USB-serial, 1ms is plenty. */
|
#define AGDMM_BUFSIZE 256
|
||||||
#define SERIAL_WRITE_TIMEOUT_MS 1
|
|
||||||
|
|
||||||
#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
|
|
||||||
|
|
||||||
enum {
|
|
||||||
DATA_SOURCE_LIVE,
|
|
||||||
DATA_SOURCE_LOG_HAND,
|
|
||||||
DATA_SOURCE_LOG_TRIG,
|
|
||||||
DATA_SOURCE_LOG_AUTO,
|
|
||||||
DATA_SOURCE_LOG_EXPO,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Supported models */
|
/* Supported models */
|
||||||
enum {
|
enum {
|
||||||
AGILENT_U1231 = 1,
|
AGILENT_U1231A = 1,
|
||||||
AGILENT_U1232,
|
AGILENT_U1232A,
|
||||||
AGILENT_U1233,
|
AGILENT_U1233A,
|
||||||
|
AGILENT_U1251A,
|
||||||
AGILENT_U1241,
|
AGILENT_U1252A,
|
||||||
AGILENT_U1242,
|
AGILENT_U1253A,
|
||||||
|
|
||||||
KEYSIGHT_U1241C,
|
|
||||||
KEYSIGHT_U1242C,
|
|
||||||
|
|
||||||
AGILENT_U1251,
|
|
||||||
AGILENT_U1252,
|
|
||||||
AGILENT_U1253,
|
|
||||||
|
|
||||||
AGILENT_U1271,
|
|
||||||
AGILENT_U1272,
|
|
||||||
AGILENT_U1273,
|
|
||||||
AGILENT_U1273AX,
|
|
||||||
|
|
||||||
KEYSIGHT_U1281,
|
|
||||||
KEYSIGHT_U1282,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Supported device profiles */
|
/* Supported device profiles */
|
||||||
struct agdmm_profile {
|
struct agdmm_profile {
|
||||||
int model;
|
int model;
|
||||||
const char *modelname;
|
const char *modelname;
|
||||||
int nb_channels;
|
const struct agdmm_job *jobs;
|
||||||
const struct agdmm_job *jobs_live;
|
|
||||||
const struct agdmm_job *jobs_log;
|
|
||||||
const struct agdmm_recv *recvs;
|
const struct agdmm_recv *recvs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Private, per-device-instance driver context. */
|
||||||
struct dev_context {
|
struct dev_context {
|
||||||
const struct agdmm_profile *profile;
|
const struct agdmm_profile *profile;
|
||||||
struct sr_sw_limits limits;
|
uint64_t limit_samples;
|
||||||
int data_source;
|
uint64_t limit_msec;
|
||||||
|
|
||||||
const struct agdmm_job *jobs;
|
/* Opaque pointer passed in by the frontend. */
|
||||||
int current_job;
|
void *cb_data;
|
||||||
gboolean job_running;
|
|
||||||
gboolean job_again;
|
/* Runtime. */
|
||||||
int64_t jobs_start[8];
|
uint64_t num_samples;
|
||||||
|
int64_t jobqueue[8];
|
||||||
unsigned char buf[AGDMM_BUFSIZE];
|
unsigned char buf[AGDMM_BUFSIZE];
|
||||||
int buflen;
|
int buflen;
|
||||||
uint64_t cur_samplerate;
|
int cur_mq;
|
||||||
struct sr_channel *cur_channel;
|
int cur_unit;
|
||||||
struct sr_channel *cur_conf;
|
int cur_mqflags;
|
||||||
int cur_sample;
|
int cur_divider;
|
||||||
int cur_mq[MAX_CHANNELS];
|
int cur_acdc;
|
||||||
int cur_unit[MAX_CHANNELS];
|
|
||||||
int cur_mqflags[MAX_CHANNELS];
|
|
||||||
int cur_digits[MAX_CHANNELS];
|
|
||||||
int cur_encoding[MAX_CHANNELS];
|
|
||||||
int cur_exponent[MAX_CHANNELS];
|
|
||||||
int mode_tempaux;
|
int mode_tempaux;
|
||||||
int mode_continuity;
|
int mode_continuity;
|
||||||
int mode_squarewave;
|
|
||||||
int mode_dbm_dbv;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum job_type {
|
|
||||||
JOB_AGAIN = 1,
|
|
||||||
JOB_STOP,
|
|
||||||
JOB_CONF,
|
|
||||||
JOB_STAT,
|
|
||||||
JOB_FETC,
|
|
||||||
JOB_LOG,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct agdmm_job {
|
struct agdmm_job {
|
||||||
enum job_type type;
|
|
||||||
int interval;
|
int interval;
|
||||||
int (*send) (const struct sr_dev_inst *sdi);
|
int (*send) (const struct sr_dev_inst *sdi);
|
||||||
};
|
};
|
||||||
|
|
@ -123,4 +85,4 @@ struct agdmm_recv {
|
||||||
|
|
||||||
SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data);
|
SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data);
|
||||||
|
|
||||||
#endif
|
#endif /* LIBSIGROK_AGILENT_DMM_H */
|
||||||
|
|
@ -0,0 +1,333 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "agilent-dmm.h"
|
||||||
|
|
||||||
|
static const int32_t hwopts[] = {
|
||||||
|
SR_CONF_CONN,
|
||||||
|
SR_CONF_SERIALCOMM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int32_t hwcaps[] = {
|
||||||
|
SR_CONF_MULTIMETER,
|
||||||
|
SR_CONF_LIMIT_SAMPLES,
|
||||||
|
SR_CONF_LIMIT_MSEC,
|
||||||
|
SR_CONF_CONTINUOUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct agdmm_job agdmm_jobs_u123x[];
|
||||||
|
extern const struct agdmm_recv agdmm_recvs_u123x[];
|
||||||
|
extern const struct agdmm_job agdmm_jobs_u125x[];
|
||||||
|
extern const struct agdmm_recv agdmm_recvs_u125x[];
|
||||||
|
|
||||||
|
/* This works on all the Agilent U12xxA series, although the
|
||||||
|
* U127xA can apparently also run at 19200/8n1. */
|
||||||
|
#define SERIALCOMM "9600/8n1"
|
||||||
|
|
||||||
|
static const struct agdmm_profile supported_agdmm[] = {
|
||||||
|
{ AGILENT_U1231A, "U1231A", agdmm_jobs_u123x, agdmm_recvs_u123x },
|
||||||
|
{ AGILENT_U1232A, "U1232A", agdmm_jobs_u123x, agdmm_recvs_u123x },
|
||||||
|
{ AGILENT_U1233A, "U1233A", agdmm_jobs_u123x, agdmm_recvs_u123x },
|
||||||
|
{ AGILENT_U1251A, "U1251A", agdmm_jobs_u125x, agdmm_recvs_u125x },
|
||||||
|
{ AGILENT_U1252A, "U1252A", agdmm_jobs_u125x, agdmm_recvs_u125x },
|
||||||
|
{ AGILENT_U1253A, "U1253A", agdmm_jobs_u125x, agdmm_recvs_u125x },
|
||||||
|
{ 0, NULL, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver agdmm_driver_info;
|
||||||
|
static struct sr_dev_driver *di = &agdmm_driver_info;
|
||||||
|
|
||||||
|
/* Properly close and free all devices. */
|
||||||
|
static int clear_instances(void)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
GSList *l;
|
||||||
|
|
||||||
|
if (!(drvc = di->priv))
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
for (l = drvc->instances; l; l = l->next) {
|
||||||
|
if (!(sdi = l->data))
|
||||||
|
continue;
|
||||||
|
if (!(devc = sdi->priv))
|
||||||
|
continue;
|
||||||
|
serial = sdi->conn;
|
||||||
|
sr_serial_dev_inst_free(serial);
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
|
}
|
||||||
|
g_slist_free(drvc->instances);
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_init(struct sr_context *sr_ctx)
|
||||||
|
{
|
||||||
|
return std_hw_init(sr_ctx, di, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_scan(GSList *options)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_config *src;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
GSList *l, *devices;
|
||||||
|
int len, i;
|
||||||
|
const char *conn, *serialcomm;
|
||||||
|
char *buf, **tokens;
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
conn = serialcomm = NULL;
|
||||||
|
for (l = options; l; l = l->next) {
|
||||||
|
src = l->data;
|
||||||
|
switch (src->key) {
|
||||||
|
case SR_CONF_CONN:
|
||||||
|
conn = g_variant_get_string(src->data, NULL);
|
||||||
|
break;
|
||||||
|
case SR_CONF_SERIALCOMM:
|
||||||
|
serialcomm = g_variant_get_string(src->data, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!conn)
|
||||||
|
return NULL;
|
||||||
|
if (!serialcomm)
|
||||||
|
serialcomm = SERIALCOMM;
|
||||||
|
|
||||||
|
if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
serial_flush(serial);
|
||||||
|
if (serial_write(serial, "*IDN?\r\n", 7) == -1) {
|
||||||
|
sr_err("Unable to send identification string: %s.",
|
||||||
|
strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 128;
|
||||||
|
if (!(buf = g_try_malloc(len))) {
|
||||||
|
sr_err("Serial buffer malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
serial_readline(serial, &buf, &len, 150);
|
||||||
|
if (!len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
tokens = g_strsplit(buf, ",", 4);
|
||||||
|
if (!strcmp("Agilent Technologies", tokens[0])
|
||||||
|
&& tokens[2] && tokens[3]) {
|
||||||
|
for (i = 0; supported_agdmm[i].model; i++) {
|
||||||
|
if (strcmp(supported_agdmm[i].modelname, tokens[1]))
|
||||||
|
continue;
|
||||||
|
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, tokens[0],
|
||||||
|
tokens[1], tokens[3])))
|
||||||
|
return NULL;
|
||||||
|
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
|
||||||
|
sr_err("Device context malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
devc->profile = &supported_agdmm[i];
|
||||||
|
devc->cur_mq = -1;
|
||||||
|
sdi->inst_type = SR_INST_SERIAL;
|
||||||
|
sdi->conn = serial;
|
||||||
|
sdi->priv = devc;
|
||||||
|
sdi->driver = di;
|
||||||
|
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
|
||||||
|
return NULL;
|
||||||
|
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||||
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
|
devices = g_slist_append(devices, sdi);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_strfreev(tokens);
|
||||||
|
g_free(buf);
|
||||||
|
|
||||||
|
serial_close(serial);
|
||||||
|
if (!devices)
|
||||||
|
sr_serial_dev_inst_free(serial);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_dev_list(void)
|
||||||
|
{
|
||||||
|
return ((struct drv_context *)(di->priv))->instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_open(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
sdi->status = SR_ST_ACTIVE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_close(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (serial && serial->fd != -1) {
|
||||||
|
serial_close(serial);
|
||||||
|
sdi->status = SR_ST_INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_cleanup(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
clear_instances();
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("sdi->priv was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
/* TODO: not yet implemented */
|
||||||
|
if (g_variant_get_uint64(data) == 0) {
|
||||||
|
sr_err("LIMIT_MSEC can't be 0.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
devc->limit_msec = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("Setting time limit to %" PRIu64 "ms.",
|
||||||
|
devc->limit_msec);
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
devc->limit_samples = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("Setting sample limit to %" PRIu64 ".",
|
||||||
|
devc->limit_samples);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case SR_CONF_SCAN_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("sdi->priv was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->cb_data = cb_data;
|
||||||
|
|
||||||
|
/* Send header packet to the session bus. */
|
||||||
|
std_session_send_df_header(cb_data, LOG_PREFIX);
|
||||||
|
|
||||||
|
/* Poll every 100ms, or whenever some data comes in. */
|
||||||
|
serial = sdi->conn;
|
||||||
|
sr_source_add(serial->fd, G_IO_IN, 100, agdmm_receive_data, (void *)sdi);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
|
||||||
|
{
|
||||||
|
return std_hw_dev_acquisition_stop_serial(sdi, cb_data, hw_dev_close,
|
||||||
|
sdi->conn, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver agdmm_driver_info = {
|
||||||
|
.name = "agilent-dmm",
|
||||||
|
.longname = "Agilent U12xx series DMMs",
|
||||||
|
.api_version = 1,
|
||||||
|
.init = hw_init,
|
||||||
|
.cleanup = hw_cleanup,
|
||||||
|
.scan = hw_scan,
|
||||||
|
.dev_list = hw_dev_list,
|
||||||
|
.dev_clear = clear_instances,
|
||||||
|
.config_get = NULL,
|
||||||
|
.config_set = config_set,
|
||||||
|
.config_list = config_list,
|
||||||
|
.dev_open = hw_dev_open,
|
||||||
|
.dev_close = hw_dev_close,
|
||||||
|
.dev_acquisition_start = hw_dev_acquisition_start,
|
||||||
|
.dev_acquisition_stop = hw_dev_acquisition_stop,
|
||||||
|
.priv = NULL,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,474 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "agilent-dmm.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static void dispatch(const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
const struct agdmm_job *jobs;
|
||||||
|
int64_t now;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
jobs = devc->profile->jobs;
|
||||||
|
now = g_get_monotonic_time() / 1000;
|
||||||
|
for (i = 0; (&jobs[i])->interval; i++) {
|
||||||
|
if (now - devc->jobqueue[i] > (&jobs[i])->interval) {
|
||||||
|
sr_spew("Running job %d.", i);
|
||||||
|
(&jobs[i])->send(sdi);
|
||||||
|
devc->jobqueue[i] = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void receive_line(const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
const struct agdmm_recv *recvs, *recv;
|
||||||
|
GRegex *reg;
|
||||||
|
GMatchInfo *match;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
/* Strip CRLF */
|
||||||
|
while (devc->buflen) {
|
||||||
|
if (*(devc->buf + devc->buflen - 1) == '\r'
|
||||||
|
|| *(devc->buf + devc->buflen - 1) == '\n')
|
||||||
|
*(devc->buf + --devc->buflen) = '\0';
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sr_spew("Received '%s'.", devc->buf);
|
||||||
|
|
||||||
|
recv = NULL;
|
||||||
|
recvs = devc->profile->recvs;
|
||||||
|
for (i = 0; (&recvs[i])->recv_regex; i++) {
|
||||||
|
reg = g_regex_new((&recvs[i])->recv_regex, 0, 0, NULL);
|
||||||
|
if (g_regex_match(reg, (char *)devc->buf, 0, &match)) {
|
||||||
|
recv = &recvs[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_match_info_unref(match);
|
||||||
|
g_regex_unref(reg);
|
||||||
|
}
|
||||||
|
if (recv) {
|
||||||
|
recv->recv(sdi, match);
|
||||||
|
g_match_info_unref(match);
|
||||||
|
g_regex_unref(reg);
|
||||||
|
} else
|
||||||
|
sr_dbg("Unknown line '%s'.", devc->buf);
|
||||||
|
|
||||||
|
/* Done with this. */
|
||||||
|
devc->buflen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
(void)fd;
|
||||||
|
|
||||||
|
if (!(sdi = cb_data))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (revents == G_IO_IN) {
|
||||||
|
/* Serial data arrived. */
|
||||||
|
while(AGDMM_BUFSIZE - devc->buflen - 1 > 0) {
|
||||||
|
len = serial_read(serial, devc->buf + devc->buflen, 1);
|
||||||
|
if (len < 1)
|
||||||
|
break;
|
||||||
|
devc->buflen += len;
|
||||||
|
*(devc->buf + devc->buflen) = '\0';
|
||||||
|
if (*(devc->buf + devc->buflen - 1) == '\n') {
|
||||||
|
/* End of line */
|
||||||
|
receive_line(sdi);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(sdi);
|
||||||
|
|
||||||
|
if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
|
||||||
|
sdi->driver->dev_acquisition_stop(sdi, cb_data);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int agdmm_send(const struct sr_dev_inst *sdi, const char *cmd)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
|
||||||
|
sr_spew("Sending '%s'.", cmd);
|
||||||
|
strncpy(buf, cmd, 28);
|
||||||
|
if (!strncmp(buf, "*IDN?", 5))
|
||||||
|
strncat(buf, "\r\n", 32);
|
||||||
|
else
|
||||||
|
strncat(buf, "\n\r\n", 32);
|
||||||
|
if (serial_write(serial, buf, strlen(buf)) == -1) {
|
||||||
|
sr_err("Failed to send: %s.", strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_stat(const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
return agdmm_send(sdi, "STAT?");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int recv_stat_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
s = g_match_info_fetch(match, 1);
|
||||||
|
sr_spew("STAT response '%s'.", s);
|
||||||
|
|
||||||
|
/* Max, Min or Avg mode -- no way to tell which, so we'll
|
||||||
|
* set both flags to denote it's not a normal measurement. */
|
||||||
|
if (s[0] == '1')
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_MAX | SR_MQFLAG_MIN;
|
||||||
|
else
|
||||||
|
devc->cur_mqflags &= ~(SR_MQFLAG_MAX | SR_MQFLAG_MIN);
|
||||||
|
|
||||||
|
if (s[1] == '1')
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_RELATIVE;
|
||||||
|
else
|
||||||
|
devc->cur_mqflags &= ~SR_MQFLAG_RELATIVE;
|
||||||
|
|
||||||
|
/* Triggered or auto hold modes. */
|
||||||
|
if (s[2] == '1' || s[3] == '1')
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_HOLD;
|
||||||
|
else
|
||||||
|
devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
|
||||||
|
|
||||||
|
/* Temp/aux mode. */
|
||||||
|
if (s[7] == '1')
|
||||||
|
devc->mode_tempaux = TRUE;
|
||||||
|
else
|
||||||
|
devc->mode_tempaux = FALSE;
|
||||||
|
|
||||||
|
/* Continuity mode. */
|
||||||
|
if (s[16] == '1')
|
||||||
|
devc->mode_continuity = TRUE;
|
||||||
|
else
|
||||||
|
devc->mode_continuity = FALSE;
|
||||||
|
|
||||||
|
g_free(s);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int recv_stat_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
s = g_match_info_fetch(match, 1);
|
||||||
|
sr_spew("STAT response '%s'.", s);
|
||||||
|
|
||||||
|
/* Peak hold mode. */
|
||||||
|
if (s[4] == '1')
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_MAX;
|
||||||
|
else
|
||||||
|
devc->cur_mqflags &= ~SR_MQFLAG_MAX;
|
||||||
|
|
||||||
|
/* Triggered hold mode. */
|
||||||
|
if (s[7] == '1')
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_HOLD;
|
||||||
|
else
|
||||||
|
devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
|
||||||
|
|
||||||
|
g_free(s);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_fetc(const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
return agdmm_send(sdi, "FETC?");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int recv_fetc(const struct sr_dev_inst *sdi, GMatchInfo *match)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
struct sr_datafeed_analog analog;
|
||||||
|
float fvalue;
|
||||||
|
char *mstr, *eptr;
|
||||||
|
|
||||||
|
sr_spew("FETC reply '%s'.", g_match_info_get_string(match));
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
if (devc->cur_mq == -1)
|
||||||
|
/* Haven't seen configuration yet, so can't know what
|
||||||
|
* the fetched float means. Not really an error, we'll
|
||||||
|
* get metadata soon enough. */
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
if (!strcmp(g_match_info_get_string(match), "+9.90000000E+37")) {
|
||||||
|
/* An invalid measurement shows up on the display as "O.L", but
|
||||||
|
* comes through like this. Since comparing 38-digit floats
|
||||||
|
* is rather problematic, we'll cut through this here. */
|
||||||
|
fvalue = NAN;
|
||||||
|
} else {
|
||||||
|
mstr = g_match_info_fetch(match, 1);
|
||||||
|
fvalue = strtof(mstr, &eptr);
|
||||||
|
g_free(mstr);
|
||||||
|
if (fvalue == 0.0 && eptr == mstr) {
|
||||||
|
sr_err("Invalid float.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
if (devc->cur_divider > 0)
|
||||||
|
fvalue /= devc->cur_divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&analog, 0, sizeof(struct sr_datafeed_analog));
|
||||||
|
analog.mq = devc->cur_mq;
|
||||||
|
analog.unit = devc->cur_unit;
|
||||||
|
analog.mqflags = devc->cur_mqflags;
|
||||||
|
analog.probes = sdi->probes;
|
||||||
|
analog.num_samples = 1;
|
||||||
|
analog.data = &fvalue;
|
||||||
|
packet.type = SR_DF_ANALOG;
|
||||||
|
packet.payload = &analog;
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
|
||||||
|
devc->num_samples++;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_conf(const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
return agdmm_send(sdi, "CONF?");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
char *mstr;
|
||||||
|
|
||||||
|
sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
|
||||||
|
devc = sdi->priv;
|
||||||
|
mstr = g_match_info_fetch(match, 1);
|
||||||
|
if (!strcmp(mstr, "V")) {
|
||||||
|
devc->cur_mq = SR_MQ_VOLTAGE;
|
||||||
|
devc->cur_unit = SR_UNIT_VOLT;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else if(!strcmp(mstr, "MV")) {
|
||||||
|
if (devc->mode_tempaux) {
|
||||||
|
devc->cur_mq = SR_MQ_TEMPERATURE;
|
||||||
|
/* No way to detect whether Fahrenheit or Celcius
|
||||||
|
* is used, so we'll just default to Celcius. */
|
||||||
|
devc->cur_unit = SR_UNIT_CELSIUS;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else {
|
||||||
|
devc->cur_mq = SR_MQ_VOLTAGE;
|
||||||
|
devc->cur_unit = SR_UNIT_VOLT;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 1000;
|
||||||
|
}
|
||||||
|
} else if(!strcmp(mstr, "A")) {
|
||||||
|
devc->cur_mq = SR_MQ_CURRENT;
|
||||||
|
devc->cur_unit = SR_UNIT_AMPERE;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else if(!strcmp(mstr, "UA")) {
|
||||||
|
devc->cur_mq = SR_MQ_CURRENT;
|
||||||
|
devc->cur_unit = SR_UNIT_AMPERE;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 1000000;
|
||||||
|
} else if(!strcmp(mstr, "FREQ")) {
|
||||||
|
devc->cur_mq = SR_MQ_FREQUENCY;
|
||||||
|
devc->cur_unit = SR_UNIT_HERTZ;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else if(!strcmp(mstr, "RES")) {
|
||||||
|
if (devc->mode_continuity) {
|
||||||
|
devc->cur_mq = SR_MQ_CONTINUITY;
|
||||||
|
devc->cur_unit = SR_UNIT_BOOLEAN;
|
||||||
|
} else {
|
||||||
|
devc->cur_mq = SR_MQ_RESISTANCE;
|
||||||
|
devc->cur_unit = SR_UNIT_OHM;
|
||||||
|
}
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else if(!strcmp(mstr, "CAP")) {
|
||||||
|
devc->cur_mq = SR_MQ_CAPACITANCE;
|
||||||
|
devc->cur_unit = SR_UNIT_FARAD;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else
|
||||||
|
sr_dbg("Unknown first argument.");
|
||||||
|
g_free(mstr);
|
||||||
|
|
||||||
|
if (g_match_info_get_match_count(match) == 4) {
|
||||||
|
mstr = g_match_info_fetch(match, 3);
|
||||||
|
/* Third value, if present, is always AC or DC. */
|
||||||
|
if (!strcmp(mstr, "AC"))
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_AC;
|
||||||
|
else if (!strcmp(mstr, "DC"))
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_DC;
|
||||||
|
else
|
||||||
|
sr_dbg("Unknown third argument.");
|
||||||
|
g_free(mstr);
|
||||||
|
} else
|
||||||
|
devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int recv_conf_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
char *mstr;
|
||||||
|
|
||||||
|
sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
|
||||||
|
devc = sdi->priv;
|
||||||
|
mstr = g_match_info_fetch(match, 1);
|
||||||
|
if (!strncmp(mstr, "VOLT", 4)) {
|
||||||
|
devc->cur_mq = SR_MQ_VOLTAGE;
|
||||||
|
devc->cur_unit = SR_UNIT_VOLT;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
if (mstr[4] == ':') {
|
||||||
|
if (!strcmp(mstr + 4, "AC"))
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_AC;
|
||||||
|
else if (!strcmp(mstr + 4, "DC"))
|
||||||
|
devc->cur_mqflags |= SR_MQFLAG_DC;
|
||||||
|
else
|
||||||
|
/* "ACDC" appears as well, no idea what it means. */
|
||||||
|
devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
|
||||||
|
} else
|
||||||
|
devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
|
||||||
|
} else if(!strcmp(mstr, "CURR")) {
|
||||||
|
devc->cur_mq = SR_MQ_CURRENT;
|
||||||
|
devc->cur_unit = SR_UNIT_AMPERE;
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else if(!strcmp(mstr, "RES")) {
|
||||||
|
if (devc->mode_continuity) {
|
||||||
|
devc->cur_mq = SR_MQ_CONTINUITY;
|
||||||
|
devc->cur_unit = SR_UNIT_BOOLEAN;
|
||||||
|
} else {
|
||||||
|
devc->cur_mq = SR_MQ_RESISTANCE;
|
||||||
|
devc->cur_unit = SR_UNIT_OHM;
|
||||||
|
}
|
||||||
|
devc->cur_mqflags = 0;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else
|
||||||
|
sr_dbg("Unknown first argument.");
|
||||||
|
g_free(mstr);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* At least the 123x and 125x appear to have this. */
|
||||||
|
static int recv_conf(const struct sr_dev_inst *sdi, GMatchInfo *match)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
char *mstr;
|
||||||
|
|
||||||
|
sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
|
||||||
|
devc = sdi->priv;
|
||||||
|
mstr = g_match_info_fetch(match, 1);
|
||||||
|
if(!strcmp(mstr, "DIOD")) {
|
||||||
|
devc->cur_mq = SR_MQ_VOLTAGE;
|
||||||
|
devc->cur_unit = SR_UNIT_VOLT;
|
||||||
|
devc->cur_mqflags = SR_MQFLAG_DIODE;
|
||||||
|
devc->cur_divider = 0;
|
||||||
|
} else
|
||||||
|
sr_dbg("Unknown single argument.");
|
||||||
|
g_free(mstr);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This comes in whenever the rotary switch is changed to a new position.
|
||||||
|
* We could use it to determine the major measurement mode, but we already
|
||||||
|
* have the output of CONF? for that, which is more detailed. However
|
||||||
|
* we do need to catch this here, or it'll show up in some other output. */
|
||||||
|
static int recv_switch(const struct sr_dev_inst *sdi, GMatchInfo *match)
|
||||||
|
{
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
sr_spew("Switch '%s'.", g_match_info_get_string(match));
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV const struct agdmm_job agdmm_jobs_u123x[] = {
|
||||||
|
{ 143, send_stat },
|
||||||
|
{ 1000, send_conf },
|
||||||
|
{ 143, send_fetc },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV const struct agdmm_recv agdmm_recvs_u123x[] = {
|
||||||
|
{ "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u123x },
|
||||||
|
{ "^\\*([0-9])$", recv_switch },
|
||||||
|
{ "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
|
||||||
|
{ "^\"(V|MV|A|UA|FREQ),(\\d),(AC|DC)\"$", recv_conf_u123x },
|
||||||
|
{ "^\"(RES|CAP),(\\d)\"$", recv_conf_u123x},
|
||||||
|
{ "^\"(DIOD)\"$", recv_conf },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV const struct agdmm_job agdmm_jobs_u125x[] = {
|
||||||
|
{ 143, send_stat },
|
||||||
|
{ 1000, send_conf },
|
||||||
|
{ 143, send_fetc },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV const struct agdmm_recv agdmm_recvs_u125x[] = {
|
||||||
|
{ "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u125x },
|
||||||
|
{ "^\\*([0-9])$", recv_switch },
|
||||||
|
{ "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
|
||||||
|
{ "^(VOLT|CURR|RES|CAP) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)$", recv_conf_u125x },
|
||||||
|
{ "^(VOLT:[ACD]+) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)$", recv_conf_u125x },
|
||||||
|
{ "^\"(DIOD)\"$", recv_conf },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
if HW_ALSA
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrokhwalsa.la
|
||||||
|
|
||||||
|
libsigrokhwalsa_la_SOURCES = \
|
||||||
|
protocol.h \
|
||||||
|
protocol.c \
|
||||||
|
api.c
|
||||||
|
|
||||||
|
libsigrokhwalsa_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,340 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
|
||||||
|
* Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
static const int32_t hwcaps[] = {
|
||||||
|
SR_CONF_SAMPLERATE,
|
||||||
|
SR_CONF_LIMIT_SAMPLES,
|
||||||
|
SR_CONF_CONTINUOUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver alsa_driver_info;
|
||||||
|
static struct sr_dev_driver *di = &alsa_driver_info;
|
||||||
|
|
||||||
|
static int clear_instances(void)
|
||||||
|
{
|
||||||
|
struct drv_context *drvc;
|
||||||
|
|
||||||
|
if (!(drvc = di->priv))
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
g_slist_free_full(drvc->instances, (GDestroyNotify)alsa_dev_inst_clear);
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_init(struct sr_context *sr_ctx)
|
||||||
|
{
|
||||||
|
return std_hw_init(sr_ctx, di, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_scan(GSList *options)
|
||||||
|
{
|
||||||
|
return alsa_scan(options, di);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_dev_list(void)
|
||||||
|
{
|
||||||
|
return ((struct drv_context *)(di->priv))->instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_open(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
if (!(devc->hwdev)) {
|
||||||
|
sr_err("devc->hwdev was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Opening audio device '%s' for stream capture.", devc->hwdev);
|
||||||
|
ret = snd_pcm_open(&devc->capture_handle, devc->hwdev,
|
||||||
|
SND_PCM_STREAM_CAPTURE, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Can't open audio device: %s.", snd_strerror(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Initializing hardware parameter structure.");
|
||||||
|
ret = snd_pcm_hw_params_any(devc->capture_handle, devc->hw_params);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Can't initialize hardware parameter structure: %s.",
|
||||||
|
snd_strerror(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdi->status = SR_ST_ACTIVE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_close(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
if (devc->capture_handle) {
|
||||||
|
sr_dbg("Closing PCM device.");
|
||||||
|
if ((ret = snd_pcm_close(devc->capture_handle)) < 0) {
|
||||||
|
sr_err("Failed to close device: %s.",
|
||||||
|
snd_strerror(ret));
|
||||||
|
devc->capture_handle = NULL;
|
||||||
|
sdi->status = SR_ST_INACTIVE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sr_dbg("No capture handle, no need to close audio device.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_cleanup(void)
|
||||||
|
{
|
||||||
|
clear_instances();
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_SAMPLERATE:
|
||||||
|
devc = sdi->priv;
|
||||||
|
*data = g_variant_new_uint64(devc->cur_samplerate);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_SAMPLERATE:
|
||||||
|
alsa_set_samplerate(sdi, g_variant_get_uint64(data));
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
devc->limit_samples = g_variant_get_uint64(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
GVariant *gvar;
|
||||||
|
GVariantBuilder gvb;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
case SR_CONF_SAMPLERATE:
|
||||||
|
if (!sdi || !sdi->priv)
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
devc = sdi->priv;
|
||||||
|
if (!devc->samplerates) {
|
||||||
|
sr_err("Instance did not contain a samplerate list.");
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
for (i = 0; devc->samplerates[i]; i++)
|
||||||
|
;
|
||||||
|
g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
|
||||||
|
gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
|
||||||
|
devc->samplerates, i, sizeof(uint64_t));
|
||||||
|
g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
|
||||||
|
*data = g_variant_builder_end(&gvb);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
|
||||||
|
void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
int count, ret;
|
||||||
|
char *endianness;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
devc->cb_data = cb_data;
|
||||||
|
devc->num_samples = 0;
|
||||||
|
|
||||||
|
sr_dbg("Setting audio access type to RW/interleaved.");
|
||||||
|
ret = snd_pcm_hw_params_set_access(devc->capture_handle,
|
||||||
|
devc->hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Can't set audio access type: %s.", snd_strerror(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: Hardcoded for 16bits. */
|
||||||
|
if (SND_PCM_FORMAT_S16 == SND_PCM_FORMAT_S16_LE)
|
||||||
|
endianness = "little endian";
|
||||||
|
else
|
||||||
|
endianness = "big endian";
|
||||||
|
sr_dbg("Setting audio sample format to signed 16bit (%s).", endianness);
|
||||||
|
ret = snd_pcm_hw_params_set_format(devc->capture_handle,
|
||||||
|
devc->hw_params,
|
||||||
|
SND_PCM_FORMAT_S16);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Can't set audio sample format: %s.", snd_strerror(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Setting audio samplerate to %" PRIu64 "Hz.",
|
||||||
|
devc->cur_samplerate);
|
||||||
|
ret = snd_pcm_hw_params_set_rate(devc->capture_handle, devc->hw_params,
|
||||||
|
(unsigned int)devc->cur_samplerate, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Can't set audio sample rate: %s.", snd_strerror(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Setting audio channel count to %d.", devc->num_probes);
|
||||||
|
ret = snd_pcm_hw_params_set_channels(devc->capture_handle,
|
||||||
|
devc->hw_params, devc->num_probes);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Can't set channel count: %s.", snd_strerror(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Setting audio parameters.");
|
||||||
|
ret = snd_pcm_hw_params(devc->capture_handle, devc->hw_params);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Can't set parameters: %s.", snd_strerror(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Preparing audio interface for use.");
|
||||||
|
ret = snd_pcm_prepare(devc->capture_handle);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Can't prepare audio interface for use: %s.",
|
||||||
|
snd_strerror(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = snd_pcm_poll_descriptors_count(devc->capture_handle);
|
||||||
|
if (count < 1) {
|
||||||
|
sr_err("Unable to obtain poll descriptors count.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(devc->ufds = g_try_malloc(count * sizeof(struct pollfd)))) {
|
||||||
|
sr_err("Failed to malloc ufds.");
|
||||||
|
return SR_ERR_MALLOC;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Getting %d poll descriptors.", count);
|
||||||
|
ret = snd_pcm_poll_descriptors(devc->capture_handle, devc->ufds, count);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Unable to obtain poll descriptors: %s.",
|
||||||
|
snd_strerror(ret));
|
||||||
|
g_free(devc->ufds);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send header packet to the session bus. */
|
||||||
|
std_session_send_df_header(cb_data, LOG_PREFIX);
|
||||||
|
|
||||||
|
/* Poll every 10ms, or whenever some data comes in. */
|
||||||
|
sr_source_add(devc->ufds[0].fd, devc->ufds[0].events, 10,
|
||||||
|
alsa_receive_data, (void *)sdi);
|
||||||
|
|
||||||
|
// g_free(devc->ufds); /* FIXME */
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
|
||||||
|
{
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
devc->cb_data = cb_data;
|
||||||
|
|
||||||
|
sr_source_remove(devc->ufds[0].fd);
|
||||||
|
|
||||||
|
/* Send end packet to the session bus. */
|
||||||
|
sr_dbg("Sending SR_DF_END packet.");
|
||||||
|
packet.type = SR_DF_END;
|
||||||
|
sr_session_send(cb_data, &packet);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver alsa_driver_info = {
|
||||||
|
.name = "alsa",
|
||||||
|
.longname = "ALSA driver",
|
||||||
|
.api_version = 1,
|
||||||
|
.init = hw_init,
|
||||||
|
.cleanup = hw_cleanup,
|
||||||
|
.scan = hw_scan,
|
||||||
|
.dev_list = hw_dev_list,
|
||||||
|
.dev_clear = clear_instances,
|
||||||
|
.config_get = config_get,
|
||||||
|
.config_set = config_set,
|
||||||
|
.config_list = config_list,
|
||||||
|
.dev_open = hw_dev_open,
|
||||||
|
.dev_close = hw_dev_close,
|
||||||
|
.dev_acquisition_start = hw_dev_acquisition_start,
|
||||||
|
.dev_acquisition_stop = hw_dev_acquisition_stop,
|
||||||
|
.priv = NULL,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,418 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
|
||||||
|
* Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is no way to get a list of supported samplerates from ALSA. We could
|
||||||
|
* use the 'plughw' interface of ALSA, in which case any format and/or
|
||||||
|
* samplerate conversion would be performed by ALSA. However, we are interested
|
||||||
|
* in the hardware capabilities, and have the infrastructure in sigrok to do so.
|
||||||
|
* We therefore use the 'hw' interface. The downside is that the code gets a
|
||||||
|
* little bulkier, as we have to keep track of the hardware capabilities, and
|
||||||
|
* only use those that the hardware supports. Case in point, ALSA will not give
|
||||||
|
* us a list of capabilities; we have to test for each one individually. Hence,
|
||||||
|
* we keep lists of the capabilities we are interested in.
|
||||||
|
*/
|
||||||
|
static const unsigned int rates[] = {
|
||||||
|
5512,
|
||||||
|
8000,
|
||||||
|
11025,
|
||||||
|
12000,
|
||||||
|
16000,
|
||||||
|
22050,
|
||||||
|
24000,
|
||||||
|
32000,
|
||||||
|
44100,
|
||||||
|
48000,
|
||||||
|
64000,
|
||||||
|
88200,
|
||||||
|
96000,
|
||||||
|
176400,
|
||||||
|
192000,
|
||||||
|
384000,
|
||||||
|
768000, /* Yes, there are sound cards that go this high. */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void alsa_scan_handle_dev(GSList **devices,
|
||||||
|
const char *cardname, const char *alsaname,
|
||||||
|
struct sr_dev_driver *di,
|
||||||
|
snd_pcm_info_t *pcminfo)
|
||||||
|
{
|
||||||
|
struct drv_context *drvc = NULL;
|
||||||
|
struct sr_dev_inst *sdi = NULL;
|
||||||
|
struct dev_context *devc = NULL;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
int ret;
|
||||||
|
unsigned int i, offset, channels, minrate, maxrate, rate;
|
||||||
|
uint64_t hwrates[ARRAY_SIZE(rates)];
|
||||||
|
uint64_t *devrates = NULL;
|
||||||
|
snd_pcm_t *temp_handle = NULL;
|
||||||
|
snd_pcm_hw_params_t *hw_params = NULL;
|
||||||
|
char p_name[32];
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get hardware parameters:
|
||||||
|
* The number of channels, for example, are our sigrok probes. Getting
|
||||||
|
* this information needs a detour. We need to open the device, then
|
||||||
|
* query it and/or test different parameters. A side-effect of is that
|
||||||
|
* we create a snd_pcm_hw_params_t object. We take advantage of the
|
||||||
|
* situation, and pass this object in our dev_context->hw_params,
|
||||||
|
* eliminating the need to free() it and malloc() it later.
|
||||||
|
*/
|
||||||
|
ret = snd_pcm_open(&temp_handle, alsaname, SND_PCM_STREAM_CAPTURE, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Cannot open device: %s.", snd_strerror(ret));
|
||||||
|
goto scan_error_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_pcm_hw_params_malloc(&hw_params);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Error allocating hardware parameter structure: %s.",
|
||||||
|
snd_strerror(ret));
|
||||||
|
goto scan_error_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_pcm_hw_params_any(temp_handle, hw_params);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Error initializing hardware parameter structure: %s.",
|
||||||
|
snd_strerror(ret));
|
||||||
|
goto scan_error_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_hw_params_get_channels_max(hw_params, &channels);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to test if each samplerate between min and max is supported.
|
||||||
|
* Unfortunately, ALSA won't just throw a list at us.
|
||||||
|
*/
|
||||||
|
snd_pcm_hw_params_get_rate_min(hw_params, &minrate, 0);
|
||||||
|
snd_pcm_hw_params_get_rate_max(hw_params, &maxrate, 0);
|
||||||
|
for (i = 0, offset = 0; i < ARRAY_SIZE(rates); i++) {
|
||||||
|
rate = rates[i];
|
||||||
|
if (rate < minrate)
|
||||||
|
continue;
|
||||||
|
if (rate > maxrate)
|
||||||
|
break;
|
||||||
|
ret = snd_pcm_hw_params_test_rate(temp_handle, hw_params,
|
||||||
|
rate, 0);
|
||||||
|
if (ret >= 0)
|
||||||
|
hwrates[offset++] = rate;
|
||||||
|
}
|
||||||
|
hwrates[offset++] = 0;
|
||||||
|
|
||||||
|
if ((ret = snd_pcm_close(temp_handle)) < 0)
|
||||||
|
sr_err("Failed to close device: %s.", snd_strerror(ret));
|
||||||
|
temp_handle = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we are done querying the hardware parameters.
|
||||||
|
* If we made it here, we know everything we want to know, and it's
|
||||||
|
* time to create our sigrok device.
|
||||||
|
*/
|
||||||
|
sr_info("Device %s has %d channels.", alsaname, channels);
|
||||||
|
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "ALSA:",
|
||||||
|
cardname, snd_pcm_info_get_name(pcminfo)))) {
|
||||||
|
sr_err("Failed to create device instance.");
|
||||||
|
goto scan_error_cleanup;
|
||||||
|
}
|
||||||
|
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
|
||||||
|
sr_err("Device context malloc failed.");
|
||||||
|
goto scan_error_cleanup;
|
||||||
|
}
|
||||||
|
if (!(devrates = g_try_malloc(offset * sizeof(uint64_t)))) {
|
||||||
|
sr_err("Samplerate list malloc failed.");
|
||||||
|
goto scan_error_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->hwdev = g_strdup(alsaname);
|
||||||
|
devc->num_probes = channels;
|
||||||
|
devc->hw_params = hw_params;
|
||||||
|
memcpy(devrates, hwrates, offset * sizeof(uint64_t));
|
||||||
|
devc->samplerates = devrates;
|
||||||
|
|
||||||
|
sdi->priv = devc;
|
||||||
|
sdi->driver = di;
|
||||||
|
|
||||||
|
for (i = 0; i < devc->num_probes; i++) {
|
||||||
|
snprintf(p_name, sizeof(p_name), "Ch_%d", i);
|
||||||
|
if (!(probe = sr_probe_new(i, SR_PROBE_ANALOG, TRUE, p_name)))
|
||||||
|
goto scan_error_cleanup;
|
||||||
|
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||||
|
}
|
||||||
|
|
||||||
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
|
*devices = g_slist_append(*devices, sdi);
|
||||||
|
return;
|
||||||
|
|
||||||
|
scan_error_cleanup:
|
||||||
|
if (devc) {
|
||||||
|
if (devc->hwdev)
|
||||||
|
g_free(devc->hwdev);
|
||||||
|
g_free(devc);
|
||||||
|
}
|
||||||
|
if (devrates)
|
||||||
|
g_free(devrates);
|
||||||
|
if (sdi)
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
|
if (hw_params)
|
||||||
|
snd_pcm_hw_params_free(hw_params);
|
||||||
|
if (temp_handle)
|
||||||
|
if ((ret = snd_pcm_close(temp_handle)) < 0) {
|
||||||
|
sr_err("Failed to close device: %s.",
|
||||||
|
snd_strerror(ret));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan all alsa devices, and translate them to sigrok devices.
|
||||||
|
*
|
||||||
|
* Each alsa device (not alsa card) gets its own sigrok device.
|
||||||
|
*
|
||||||
|
* For example,
|
||||||
|
* hw:1,0 == sigrok device 0
|
||||||
|
* hw:1,1 == sigrok device 1
|
||||||
|
* hw:2,0 == sigrok device 2
|
||||||
|
* hw:2,1 == sigrok device 3
|
||||||
|
* hw:2,2 == sigrok device 4
|
||||||
|
* [...]
|
||||||
|
*
|
||||||
|
* We don't currently look at alsa subdevices. We only use subdevice 0.
|
||||||
|
* Every input device will have its own channels (left, right, etc). Each of
|
||||||
|
* those channels gets mapped to a different sigrok probe. A device with 4
|
||||||
|
* channels will have 4 probes from sigrok's perspective.
|
||||||
|
*/
|
||||||
|
SR_PRIV GSList *alsa_scan(GSList *options, struct sr_dev_driver *di)
|
||||||
|
{
|
||||||
|
GSList *devices = NULL;
|
||||||
|
snd_ctl_t *handle;
|
||||||
|
int card, ret, dev;
|
||||||
|
snd_ctl_card_info_t *info;
|
||||||
|
snd_pcm_info_t *pcminfo;
|
||||||
|
const char *cardname;
|
||||||
|
char hwcard[32], hwdev[32];
|
||||||
|
|
||||||
|
/* TODO */
|
||||||
|
(void)options;
|
||||||
|
|
||||||
|
if ((ret = snd_ctl_card_info_malloc(&info)) < 0) {
|
||||||
|
sr_dbg("Failed to malloc card info: %s.", snd_strerror(ret));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if ((ret = snd_pcm_info_malloc(&pcminfo) < 0)) {
|
||||||
|
sr_dbg("Cannot malloc pcm info: %s.", snd_strerror(ret));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
card = -1;
|
||||||
|
while (snd_card_next(&card) >= 0 && card >= 0) {
|
||||||
|
snprintf(hwcard, sizeof(hwcard), "hw:%d", card);
|
||||||
|
if ((ret = snd_ctl_open(&handle, hwcard, 0)) < 0) {
|
||||||
|
sr_dbg("Cannot open (%d): %s.", card, snd_strerror(ret));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((ret = snd_ctl_card_info(handle, info)) < 0) {
|
||||||
|
sr_dbg("Cannot get hardware info (%d): %s.",
|
||||||
|
card, snd_strerror(ret));
|
||||||
|
if ((ret = snd_ctl_close(handle)) < 0) {
|
||||||
|
sr_dbg("Cannot close device (%d): %s.",
|
||||||
|
card, snd_strerror(ret));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dev = -1;
|
||||||
|
while (snd_ctl_pcm_next_device(handle, &dev) >= 0 && dev >= 0) {
|
||||||
|
snprintf(hwdev, sizeof(hwdev), "%s,%d", hwcard, dev);
|
||||||
|
/*
|
||||||
|
* TODO: We always use subdevice 0, but we have yet to
|
||||||
|
* explore the possibilities opened up by other
|
||||||
|
* subdevices. Most hardware only has subdevice 0.
|
||||||
|
*/
|
||||||
|
snd_pcm_info_set_device(pcminfo, dev);
|
||||||
|
snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||||
|
snd_pcm_info_set_stream(pcminfo,
|
||||||
|
SND_PCM_STREAM_CAPTURE);
|
||||||
|
if ((ret = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
|
||||||
|
sr_dbg("Cannot get device info (%s): %s.",
|
||||||
|
hwdev, snd_strerror(ret));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cardname = snd_ctl_card_info_get_name(info);
|
||||||
|
sr_info("card %d: %s [%s], device %d: %s [%s]",
|
||||||
|
card, snd_ctl_card_info_get_id(info), cardname,
|
||||||
|
dev, snd_pcm_info_get_id(pcminfo),
|
||||||
|
snd_pcm_info_get_name(pcminfo));
|
||||||
|
|
||||||
|
alsa_scan_handle_dev(&devices, cardname, hwdev,
|
||||||
|
di, pcminfo);
|
||||||
|
}
|
||||||
|
if ((ret = snd_ctl_close(handle)) < 0) {
|
||||||
|
sr_dbg("Cannot close device (%d): %s.",
|
||||||
|
card, snd_strerror(ret));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_info_free(pcminfo);
|
||||||
|
snd_ctl_card_info_free(info);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper to be used with g_slist_free_full(); for properly freeing an alsa
|
||||||
|
* dev instance.
|
||||||
|
*/
|
||||||
|
SR_PRIV void alsa_dev_inst_clear(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv))
|
||||||
|
return;
|
||||||
|
|
||||||
|
snd_pcm_hw_params_free(devc->hw_params);
|
||||||
|
g_free((void*)devc->samplerates);
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the samplerate of the ALSA device.
|
||||||
|
*
|
||||||
|
* Changes the samplerate of the given ALSA device if the specified samplerate
|
||||||
|
* is supported by the hardware.
|
||||||
|
*
|
||||||
|
* The new samplerate is recorded, but it is not applied to the hardware. The
|
||||||
|
* samplerate is applied to the hardware only when acquisition is started via
|
||||||
|
* dev_acquisition_start(), and cannot be changed during acquisition. To change
|
||||||
|
* the samplerate, several steps are needed:
|
||||||
|
*
|
||||||
|
* 1) If acquisition is running, it must first be stopped.
|
||||||
|
* 2) dev_config_set() must be called with the new samplerate.
|
||||||
|
* 3) When starting a new acquisition, the new samplerate is applied.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
SR_PRIV int alsa_set_samplerate(const struct sr_dev_inst *sdi,
|
||||||
|
uint64_t newrate)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
size_t i;
|
||||||
|
uint64_t rate = 0;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv))
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
do {
|
||||||
|
if (newrate == devc->samplerates[i]) {
|
||||||
|
rate = newrate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (devc->samplerates[i++] != 0);
|
||||||
|
|
||||||
|
if (!rate) {
|
||||||
|
sr_err("Sample rate %" PRIu64 " not supported.", newrate);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->cur_samplerate = rate;
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int alsa_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
struct sr_datafeed_analog analog;
|
||||||
|
int16_t inbuf[4096];
|
||||||
|
int i, x, count, offset, samples_to_get;
|
||||||
|
int16_t tmp16;
|
||||||
|
const float s16norm = 1 / (float)(1 << 15);
|
||||||
|
|
||||||
|
(void)fd;
|
||||||
|
(void)revents;
|
||||||
|
|
||||||
|
sdi = cb_data;
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
memset(&analog, 0, sizeof(struct sr_datafeed_analog));
|
||||||
|
memset(inbuf, 0, sizeof(inbuf));
|
||||||
|
|
||||||
|
samples_to_get = MIN(4096 / 4, devc->limit_samples);
|
||||||
|
|
||||||
|
sr_spew("Getting %d samples from audio device.", samples_to_get);
|
||||||
|
count = snd_pcm_readi(devc->capture_handle, inbuf, samples_to_get);
|
||||||
|
|
||||||
|
if (count < 0) {
|
||||||
|
sr_err("Failed to read samples: %s.", snd_strerror(count));
|
||||||
|
return FALSE;
|
||||||
|
} else if (count != samples_to_get) {
|
||||||
|
sr_spew("Only got %d/%d samples.", count, samples_to_get);
|
||||||
|
}
|
||||||
|
|
||||||
|
analog.data = g_try_malloc0(count * sizeof(float) * devc->num_probes);
|
||||||
|
if (!analog.data) {
|
||||||
|
sr_err("Failed to malloc sample buffer.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
/*
|
||||||
|
* It's impossible to know what voltage levels the soundcard handles.
|
||||||
|
* Some handle 0 dBV rms, some 0dBV peak-to-peak, +4dbmW (600 ohm), etc
|
||||||
|
* Each of these corresponds to a different voltage, and there is no
|
||||||
|
* mechanism to determine this voltage. The best solution is to send all
|
||||||
|
* audio data as a normalized float, and let the frontend or user worry
|
||||||
|
* about the calibration.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < count; i += devc->num_probes) {
|
||||||
|
for (x = 0; x < devc->num_probes; x++) {
|
||||||
|
tmp16 = inbuf[i + x];
|
||||||
|
analog.data[offset++] = tmp16 * s16norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send a sample packet with the analog values. */
|
||||||
|
analog.probes = sdi->probes;
|
||||||
|
analog.num_samples = count;
|
||||||
|
analog.mq = SR_MQ_VOLTAGE; /* FIXME */
|
||||||
|
analog.unit = SR_UNIT_VOLT; /* FIXME */
|
||||||
|
packet.type = SR_DF_ANALOG;
|
||||||
|
packet.payload = &analog;
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
|
||||||
|
g_free(analog.data);
|
||||||
|
|
||||||
|
devc->num_samples += count;
|
||||||
|
|
||||||
|
/* Stop acquisition if we acquired enough samples. */
|
||||||
|
if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
|
||||||
|
sr_info("Requested number of samples reached.");
|
||||||
|
sdi->driver->dev_acquisition_stop(sdi, cb_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com>
|
||||||
|
* Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBSIGROK_HARDWARE_ALSA_PROTOCOL_H
|
||||||
|
#define LIBSIGROK_HARDWARE_ALSA_PROTOCOL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "alsa: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
/** Private, per-device-instance driver context. */
|
||||||
|
struct dev_context {
|
||||||
|
uint64_t cur_samplerate;
|
||||||
|
uint64_t limit_samples;
|
||||||
|
uint64_t num_samples;
|
||||||
|
uint8_t num_probes;
|
||||||
|
uint64_t *samplerates;
|
||||||
|
char *hwdev;
|
||||||
|
snd_pcm_t *capture_handle;
|
||||||
|
snd_pcm_hw_params_t *hw_params;
|
||||||
|
struct pollfd *ufds;
|
||||||
|
void *cb_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV GSList *alsa_scan(GSList *options, struct sr_dev_driver *di);
|
||||||
|
SR_PRIV void alsa_dev_inst_clear(struct sr_dev_inst *sdi);
|
||||||
|
|
||||||
|
SR_PRIV int alsa_set_samplerate(const struct sr_dev_inst *sdi,
|
||||||
|
uint64_t newrate);
|
||||||
|
|
||||||
|
SR_PRIV int alsa_receive_data(int fd, int revents, void *cb_data);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
if LA_ASIX_SIGMA
|
||||||
|
|
||||||
|
AM_CPPFLAGS = -DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrokhwasixsigma.la
|
||||||
|
|
||||||
|
libsigrokhwasixsigma_la_SOURCES = \
|
||||||
|
asix-sigma.c \
|
||||||
|
asix-sigma.h
|
||||||
|
|
||||||
|
libsigrokhwasixsigma_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Håvard Espeland <gus@ping.uio.no>,
|
||||||
|
* Copyright (C) 2010 Martin Stensgård <mastensg@ping.uio.no>
|
||||||
|
* Copyright (C) 2010 Carl Henrik Lunde <chlunde@ping.uio.no>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBSIGROK_HARDWARE_ASIX_SIGMA_ASIX_SIGMA_H
|
||||||
|
#define LIBSIGROK_HARDWARE_ASIX_SIGMA_ASIX_SIGMA_H
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "asix-sigma: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
enum sigma_write_register {
|
||||||
|
WRITE_CLOCK_SELECT = 0,
|
||||||
|
WRITE_TRIGGER_SELECT0 = 1,
|
||||||
|
WRITE_TRIGGER_SELECT1 = 2,
|
||||||
|
WRITE_MODE = 3,
|
||||||
|
WRITE_MEMROW = 4,
|
||||||
|
WRITE_POST_TRIGGER = 5,
|
||||||
|
WRITE_TRIGGER_OPTION = 6,
|
||||||
|
WRITE_PIN_VIEW = 7,
|
||||||
|
|
||||||
|
WRITE_TEST = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum sigma_read_register {
|
||||||
|
READ_ID = 0,
|
||||||
|
READ_TRIGGER_POS_LOW = 1,
|
||||||
|
READ_TRIGGER_POS_HIGH = 2,
|
||||||
|
READ_TRIGGER_POS_UP = 3,
|
||||||
|
READ_STOP_POS_LOW = 4,
|
||||||
|
READ_STOP_POS_HIGH = 5,
|
||||||
|
READ_STOP_POS_UP = 6,
|
||||||
|
READ_MODE = 7,
|
||||||
|
READ_PIN_CHANGE_LOW = 8,
|
||||||
|
READ_PIN_CHANGE_HIGH = 9,
|
||||||
|
READ_BLOCK_LAST_TS_LOW = 10,
|
||||||
|
READ_BLOCK_LAST_TS_HIGH = 11,
|
||||||
|
READ_PIN_VIEW = 12,
|
||||||
|
|
||||||
|
READ_TEST = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define REG_ADDR_LOW (0 << 4)
|
||||||
|
#define REG_ADDR_HIGH (1 << 4)
|
||||||
|
#define REG_DATA_LOW (2 << 4)
|
||||||
|
#define REG_DATA_HIGH_WRITE (3 << 4)
|
||||||
|
#define REG_READ_ADDR (4 << 4)
|
||||||
|
#define REG_DRAM_WAIT_ACK (5 << 4)
|
||||||
|
|
||||||
|
/* Bit (1 << 4) can be low or high (double buffer / cache) */
|
||||||
|
#define REG_DRAM_BLOCK (6 << 4)
|
||||||
|
#define REG_DRAM_BLOCK_BEGIN (8 << 4)
|
||||||
|
#define REG_DRAM_BLOCK_DATA (10 << 4)
|
||||||
|
|
||||||
|
#define LEDSEL0 6
|
||||||
|
#define LEDSEL1 7
|
||||||
|
|
||||||
|
#define NEXT_REG 1
|
||||||
|
|
||||||
|
#define EVENTS_PER_CLUSTER 7
|
||||||
|
|
||||||
|
#define CHUNK_SIZE 1024
|
||||||
|
|
||||||
|
struct clockselect_50 {
|
||||||
|
uint8_t async;
|
||||||
|
uint8_t fraction;
|
||||||
|
uint16_t disabled_probes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The effect of all these are still a bit unclear. */
|
||||||
|
struct triggerinout {
|
||||||
|
uint8_t trgout_resistor_enable : 1;
|
||||||
|
uint8_t trgout_resistor_pullup : 1;
|
||||||
|
uint8_t reserved1 : 1;
|
||||||
|
uint8_t trgout_bytrigger : 1;
|
||||||
|
uint8_t trgout_byevent : 1;
|
||||||
|
uint8_t trgout_bytriggerin : 1;
|
||||||
|
uint8_t reserved2 : 2;
|
||||||
|
|
||||||
|
/* Should be set same as the first two */
|
||||||
|
uint8_t trgout_resistor_enable2 : 1;
|
||||||
|
uint8_t trgout_resistor_pullup2 : 1;
|
||||||
|
|
||||||
|
uint8_t reserved3 : 1;
|
||||||
|
uint8_t trgout_long : 1;
|
||||||
|
uint8_t trgout_pin : 1; /* Use 1k resistor. Pullup? */
|
||||||
|
uint8_t trgin_negate : 1;
|
||||||
|
uint8_t trgout_enable : 1;
|
||||||
|
uint8_t trgin_enable : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct triggerlut {
|
||||||
|
/* The actual LUTs. */
|
||||||
|
uint16_t m0d[4], m1d[4], m2d[4];
|
||||||
|
uint16_t m3, m3s, m4;
|
||||||
|
|
||||||
|
/* Paramters should be sent as a single register write. */
|
||||||
|
struct {
|
||||||
|
uint8_t selc : 2;
|
||||||
|
uint8_t selpresc : 6;
|
||||||
|
|
||||||
|
uint8_t selinc : 2;
|
||||||
|
uint8_t selres : 2;
|
||||||
|
uint8_t sela : 2;
|
||||||
|
uint8_t selb : 2;
|
||||||
|
|
||||||
|
uint16_t cmpb;
|
||||||
|
uint16_t cmpa;
|
||||||
|
} params;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Trigger configuration */
|
||||||
|
struct sigma_trigger {
|
||||||
|
/* Only two probes can be used in mask. */
|
||||||
|
uint16_t risingmask;
|
||||||
|
uint16_t fallingmask;
|
||||||
|
|
||||||
|
/* Simple trigger support (<= 50 MHz). */
|
||||||
|
uint16_t simplemask;
|
||||||
|
uint16_t simplevalue;
|
||||||
|
|
||||||
|
/* TODO: Advanced trigger support (boolean expressions). */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Events for trigger operation. */
|
||||||
|
enum triggerop {
|
||||||
|
OP_LEVEL = 1,
|
||||||
|
OP_NOT,
|
||||||
|
OP_RISE,
|
||||||
|
OP_FALL,
|
||||||
|
OP_RISEFALL,
|
||||||
|
OP_NOTRISE,
|
||||||
|
OP_NOTFALL,
|
||||||
|
OP_NOTRISEFALL,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Logical functions for trigger operation. */
|
||||||
|
enum triggerfunc {
|
||||||
|
FUNC_AND = 1,
|
||||||
|
FUNC_NAND,
|
||||||
|
FUNC_OR,
|
||||||
|
FUNC_NOR,
|
||||||
|
FUNC_XOR,
|
||||||
|
FUNC_NXOR,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sigma_state {
|
||||||
|
enum {
|
||||||
|
SIGMA_UNINITIALIZED = 0,
|
||||||
|
SIGMA_IDLE,
|
||||||
|
SIGMA_CAPTURE,
|
||||||
|
SIGMA_DOWNLOAD,
|
||||||
|
} state;
|
||||||
|
|
||||||
|
uint32_t stoppos, triggerpos;
|
||||||
|
uint16_t lastts;
|
||||||
|
uint16_t lastsample;
|
||||||
|
|
||||||
|
int triggerchunk;
|
||||||
|
int chunks_downloaded;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Private, per-device-instance driver context. */
|
||||||
|
struct dev_context {
|
||||||
|
struct ftdi_context ftdic;
|
||||||
|
uint64_t cur_samplerate;
|
||||||
|
uint64_t period_ps;
|
||||||
|
uint64_t limit_msec;
|
||||||
|
struct timeval start_tv;
|
||||||
|
int cur_firmware;
|
||||||
|
int num_probes;
|
||||||
|
int samples_per_event;
|
||||||
|
int capture_ratio;
|
||||||
|
struct sigma_trigger trigger;
|
||||||
|
int use_triggers;
|
||||||
|
struct sigma_state state;
|
||||||
|
void *cb_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
##
|
||||||
|
## 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/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
if HW_BRYMEN_DMM
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrok_hw_brymen_dmm.la
|
||||||
|
|
||||||
|
libsigrok_hw_brymen_dmm_la_SOURCES = \
|
||||||
|
api.c \
|
||||||
|
parser.c \
|
||||||
|
protocol.c \
|
||||||
|
protocol.h
|
||||||
|
|
||||||
|
libsigrok_hw_brymen_dmm_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
@ -0,0 +1,313 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
static const int32_t hwopts[] = {
|
||||||
|
SR_CONF_CONN,
|
||||||
|
SR_CONF_SERIALCOMM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int32_t hwcaps[] = {
|
||||||
|
SR_CONF_MULTIMETER,
|
||||||
|
SR_CONF_LIMIT_SAMPLES,
|
||||||
|
SR_CONF_CONTINUOUS,
|
||||||
|
SR_CONF_LIMIT_MSEC,
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver brymen_bm857_driver_info;
|
||||||
|
static struct sr_dev_driver *di = &brymen_bm857_driver_info;
|
||||||
|
|
||||||
|
static int hw_init(struct sr_context *sr_ctx)
|
||||||
|
{
|
||||||
|
return std_hw_init(sr_ctx, di, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_instance(void *inst)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
if (!(sdi = inst))
|
||||||
|
return;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
sr_serial_dev_inst_free(serial);
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Properly close and free all devices. */
|
||||||
|
static int clear_instances(void)
|
||||||
|
{
|
||||||
|
struct drv_context *drvc;
|
||||||
|
|
||||||
|
if (!(drvc = di->priv))
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
g_slist_free_full(drvc->instances, free_instance);
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *brymen_scan(const char *conn, const char *serialcomm)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
GSList *devices;
|
||||||
|
int ret;
|
||||||
|
uint8_t buf[128];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sr_info("Probing port %s.", conn);
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
|
||||||
|
/* Request reading */
|
||||||
|
if ((ret = brymen_packet_request(serial)) < 0) {
|
||||||
|
sr_err("Unable to send command: %d.", ret);
|
||||||
|
goto scan_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = 128;
|
||||||
|
ret = brymen_stream_detect(serial, buf, &len, brymen_packet_length,
|
||||||
|
brymen_packet_is_valid, 1000, 9600);
|
||||||
|
if (ret != SR_OK)
|
||||||
|
goto scan_cleanup;
|
||||||
|
|
||||||
|
sr_info("Found device on port %s.", conn);
|
||||||
|
|
||||||
|
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Brymen", "BM85x", "")))
|
||||||
|
goto scan_cleanup;
|
||||||
|
|
||||||
|
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
|
||||||
|
sr_err("Device context malloc failed.");
|
||||||
|
goto scan_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdi->inst_type = SR_INST_SERIAL;
|
||||||
|
sdi->conn = serial;
|
||||||
|
drvc = di->priv;
|
||||||
|
sdi->priv = devc;
|
||||||
|
sdi->driver = di;
|
||||||
|
|
||||||
|
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
|
||||||
|
goto scan_cleanup;
|
||||||
|
|
||||||
|
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||||
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
|
devices = g_slist_append(devices, sdi);
|
||||||
|
|
||||||
|
scan_cleanup:
|
||||||
|
serial_close(serial);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_scan(GSList *options)
|
||||||
|
{
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct sr_config *src;
|
||||||
|
GSList *devices, *l;
|
||||||
|
const char *conn, *serialcomm;
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
drvc = di->priv;
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
conn = serialcomm = NULL;
|
||||||
|
for (l = options; l; l = l->next) {
|
||||||
|
src = l->data;
|
||||||
|
switch (src->key) {
|
||||||
|
case SR_CONF_CONN:
|
||||||
|
conn = g_variant_get_string(src->data, NULL);
|
||||||
|
break;
|
||||||
|
case SR_CONF_SERIALCOMM:
|
||||||
|
serialcomm = g_variant_get_string(src->data, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!conn)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (serialcomm) {
|
||||||
|
/* Use the provided comm specs. */
|
||||||
|
devices = brymen_scan(conn, serialcomm);
|
||||||
|
} else {
|
||||||
|
/* But 9600/8n1 should work all of the time. */
|
||||||
|
devices = brymen_scan(conn, "9600/8n1/dtr=1/rts=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_dev_list(void)
|
||||||
|
{
|
||||||
|
return ((struct drv_context *)(di->priv))->instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_open(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
sdi->status = SR_ST_ACTIVE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_close(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (serial && serial->fd != -1) {
|
||||||
|
serial_close(serial);
|
||||||
|
sdi->status = SR_ST_INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_cleanup(void)
|
||||||
|
{
|
||||||
|
clear_instances();
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("sdi->priv was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SR_OK;
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
devc->limit_samples = g_variant_get_uint64(data);
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
devc->limit_msec = g_variant_get_uint64(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case SR_CONF_SCAN_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
|
||||||
|
void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("sdi->priv was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->cb_data = cb_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset the number of samples to take. If we've already collected our
|
||||||
|
* quota, but we start a new session, and don't reset this, we'll just
|
||||||
|
* quit without acquiring any new samples.
|
||||||
|
*/
|
||||||
|
devc->num_samples = 0;
|
||||||
|
devc->starttime = g_get_monotonic_time();
|
||||||
|
|
||||||
|
/* Send header packet to the session bus. */
|
||||||
|
std_session_send_df_header(cb_data, LOG_PREFIX);
|
||||||
|
|
||||||
|
/* Poll every 50ms, or whenever some data comes in. */
|
||||||
|
serial = sdi->conn;
|
||||||
|
sr_source_add(serial->fd, G_IO_IN, 50,
|
||||||
|
brymen_dmm_receive_data, (void *)sdi);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
|
||||||
|
{
|
||||||
|
return std_hw_dev_acquisition_stop_serial(sdi, cb_data, hw_dev_close,
|
||||||
|
sdi->conn, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver brymen_bm857_driver_info = {
|
||||||
|
.name = "brymen-bm857",
|
||||||
|
.longname = "Brymen BM857",
|
||||||
|
.api_version = 1,
|
||||||
|
.init = hw_init,
|
||||||
|
.cleanup = hw_cleanup,
|
||||||
|
.scan = hw_scan,
|
||||||
|
.dev_list = hw_dev_list,
|
||||||
|
.dev_clear = clear_instances,
|
||||||
|
.config_get = NULL,
|
||||||
|
.config_set = config_set,
|
||||||
|
.config_list = config_list,
|
||||||
|
.dev_open = hw_dev_open,
|
||||||
|
.dev_close = hw_dev_close,
|
||||||
|
.dev_acquisition_start = hw_dev_acquisition_start,
|
||||||
|
.dev_acquisition_stop = hw_dev_acquisition_stop,
|
||||||
|
.priv = NULL,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,286 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
#define MAX_PACKET_LEN 22
|
||||||
|
|
||||||
|
/* Flags passed from the DMM. */
|
||||||
|
struct brymen_flags {
|
||||||
|
gboolean is_low_batt, is_decibel, is_duty_cycle, is_hertz, is_amp;
|
||||||
|
gboolean is_beep, is_ohm, is_fahrenheit, is_celsius, is_capacitance;
|
||||||
|
gboolean is_diode, is_volt, is_dc, is_ac;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bm850_command {
|
||||||
|
uint8_t dle;
|
||||||
|
uint8_t stx;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t arg[2];
|
||||||
|
uint8_t checksum;
|
||||||
|
uint8_t dle2;
|
||||||
|
uint8_t etx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct brymen_header {
|
||||||
|
uint8_t dle;
|
||||||
|
uint8_t stx;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct brymen_tail {
|
||||||
|
uint8_t checksum;
|
||||||
|
uint8_t dle;
|
||||||
|
uint8_t etx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only have one command because we only support the BM-857. However, the
|
||||||
|
* driver is easily extensible to support more models, as the protocols are
|
||||||
|
* very similar.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
BM_CMD_REQUEST_READING = 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int bm_send_command(uint8_t command, uint8_t arg1, uint8_t arg2,
|
||||||
|
struct sr_serial_dev_inst *serial)
|
||||||
|
{
|
||||||
|
struct bm850_command cmdout;
|
||||||
|
int written;
|
||||||
|
|
||||||
|
cmdout.dle = 0x10;
|
||||||
|
cmdout.stx = 0x02;
|
||||||
|
cmdout.cmd = command;
|
||||||
|
cmdout.arg[0] = arg1;
|
||||||
|
cmdout.arg[1] = arg2;
|
||||||
|
cmdout.checksum = arg1 ^ arg2;
|
||||||
|
cmdout.dle2 = 0x10;
|
||||||
|
cmdout.etx = 0x03;
|
||||||
|
|
||||||
|
/* TODO: How to compute the checksum? Hardware seems to ignore it. */
|
||||||
|
|
||||||
|
/* Request reading. */
|
||||||
|
written = serial_write(serial, &cmdout, sizeof(cmdout));
|
||||||
|
if (written != sizeof(cmdout))
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial)
|
||||||
|
{
|
||||||
|
return bm_send_command(BM_CMD_REQUEST_READING, 0, 0, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len)
|
||||||
|
{
|
||||||
|
struct brymen_header *hdr;
|
||||||
|
int packet_len;
|
||||||
|
size_t buflen;
|
||||||
|
|
||||||
|
buflen = *len;
|
||||||
|
hdr = (void *)buf;
|
||||||
|
|
||||||
|
/* Did we receive a complete header yet? */
|
||||||
|
if (buflen < sizeof(*hdr))
|
||||||
|
return PACKET_NEED_MORE_DATA;
|
||||||
|
|
||||||
|
if (hdr->dle != 0x10 || hdr->stx != 0x02)
|
||||||
|
return PACKET_INVALID_HEADER;
|
||||||
|
|
||||||
|
/* Our packet includes the header, the payload, and the tail. */
|
||||||
|
packet_len = sizeof(*hdr) + hdr->len + sizeof(struct brymen_tail);
|
||||||
|
|
||||||
|
/* In case we pick up an invalid header, limit our search. */
|
||||||
|
if (packet_len > MAX_PACKET_LEN) {
|
||||||
|
sr_spew("Header specifies an invalid payload length: %i.",
|
||||||
|
hdr->len);
|
||||||
|
return PACKET_INVALID_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
*len = packet_len;
|
||||||
|
sr_spew("Expecting a %d-byte packet.", *len);
|
||||||
|
return PACKET_HEADER_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
struct brymen_header *hdr;
|
||||||
|
struct brymen_tail *tail;
|
||||||
|
int i;
|
||||||
|
uint8_t chksum = 0;
|
||||||
|
uint8_t *payload;
|
||||||
|
|
||||||
|
payload = (uint8_t *)(buf + sizeof(struct brymen_header));
|
||||||
|
|
||||||
|
hdr = (void *)buf;
|
||||||
|
tail = (void *)(payload + hdr->len);
|
||||||
|
|
||||||
|
for (i = 0; i< hdr->len; i++)
|
||||||
|
chksum ^= payload[i];
|
||||||
|
|
||||||
|
if (tail->checksum != chksum) {
|
||||||
|
sr_dbg("Packet has invalid checksum 0x%.2x. Expected 0x%.2x.",
|
||||||
|
chksum, tail->checksum);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_value(const char *strbuf, int len, float *floatval)
|
||||||
|
{
|
||||||
|
int s, d;
|
||||||
|
char str[32];
|
||||||
|
|
||||||
|
if (strstr(strbuf, "OL")) {
|
||||||
|
sr_dbg("Overlimit.");
|
||||||
|
*floatval = INFINITY;
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(str, 0, sizeof(str));
|
||||||
|
/* Spaces may interfere with strtod parsing the exponent. Strip them. */
|
||||||
|
for (s = 0, d = 0; s < len; s++)
|
||||||
|
if (strbuf[s] != ' ')
|
||||||
|
str[d++] = strbuf[s];
|
||||||
|
/* Yes, it's that simple! */
|
||||||
|
*floatval = strtod(str, NULL);
|
||||||
|
|
||||||
|
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 sr_brymen_parse(const uint8_t *buf, float *floatval,
|
||||||
|
struct sr_datafeed_analog *analog, void *info)
|
||||||
|
{
|
||||||
|
struct brymen_flags flags;
|
||||||
|
struct brymen_header *hdr;
|
||||||
|
uint8_t *bfunc;
|
||||||
|
int asciilen;
|
||||||
|
|
||||||
|
(void)info;
|
||||||
|
|
||||||
|
hdr = (void *)buf;
|
||||||
|
bfunc = (uint8_t *)(buf + sizeof(struct brymen_header));
|
||||||
|
|
||||||
|
analog->mqflags = 0;
|
||||||
|
|
||||||
|
/* Give some debug info about the package. */
|
||||||
|
asciilen = hdr->len - 4;
|
||||||
|
sr_dbg("DMM flags: %.2x %.2x %.2x %.2x",
|
||||||
|
bfunc[3], bfunc[2], bfunc[1], bfunc[0]);
|
||||||
|
/* Value is an ASCII string. */
|
||||||
|
sr_dbg("DMM packet: \"%.*s\"", asciilen, bfunc + 4);
|
||||||
|
|
||||||
|
parse_flags(buf, &flags);
|
||||||
|
parse_value((const char *)(bfunc + 4), asciilen, floatval);
|
||||||
|
|
||||||
|
if (flags.is_volt) {
|
||||||
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
|
analog->unit = SR_UNIT_VOLT;
|
||||||
|
}
|
||||||
|
if (flags.is_amp) {
|
||||||
|
analog->mq = SR_MQ_CURRENT;
|
||||||
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
|
}
|
||||||
|
if (flags.is_ohm) {
|
||||||
|
if (flags.is_beep)
|
||||||
|
analog->mq = SR_MQ_CONTINUITY;
|
||||||
|
else
|
||||||
|
analog->mq = SR_MQ_RESISTANCE;
|
||||||
|
analog->unit = SR_UNIT_OHM;
|
||||||
|
}
|
||||||
|
if (flags.is_hertz) {
|
||||||
|
analog->mq = SR_MQ_FREQUENCY;
|
||||||
|
analog->unit = SR_UNIT_HERTZ;
|
||||||
|
}
|
||||||
|
if (flags.is_duty_cycle) {
|
||||||
|
analog->mq = SR_MQ_DUTY_CYCLE;
|
||||||
|
analog->unit = SR_UNIT_PERCENTAGE;
|
||||||
|
}
|
||||||
|
if (flags.is_capacitance) {
|
||||||
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
|
analog->unit = SR_UNIT_FARAD;
|
||||||
|
}
|
||||||
|
if (flags.is_fahrenheit) {
|
||||||
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
|
analog->unit = SR_UNIT_FAHRENHEIT;
|
||||||
|
}
|
||||||
|
if (flags.is_celsius) {
|
||||||
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
|
}
|
||||||
|
if (flags.is_capacitance) {
|
||||||
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
|
analog->unit = SR_UNIT_FARAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The high-end Brymen models have a configurable reference impedance.
|
||||||
|
* When the reference impedance is changed, the DMM sends one packet
|
||||||
|
* with the value of the new reference impedance. Both decibel and ohm
|
||||||
|
* flags are set in this case, so we must be careful to correctly
|
||||||
|
* identify the value as ohm, not dBmW.
|
||||||
|
*/
|
||||||
|
if (flags.is_decibel && !flags.is_ohm) {
|
||||||
|
analog->mq = SR_MQ_POWER;
|
||||||
|
analog->unit = SR_UNIT_DECIBEL_MW;
|
||||||
|
/*
|
||||||
|
* For some reason, dBm measurements are sent by the multimeter
|
||||||
|
* with a value three orders of magnitude smaller than the
|
||||||
|
* displayed value.
|
||||||
|
*/
|
||||||
|
*floatval *= 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags.is_diode)
|
||||||
|
analog->mqflags |= SR_MQFLAG_DIODE;
|
||||||
|
/* We can have both AC+DC in a single measurement. */
|
||||||
|
if (flags.is_ac)
|
||||||
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
|
if (flags.is_dc)
|
||||||
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
|
|
||||||
|
if (flags.is_low_batt)
|
||||||
|
sr_info("Low battery!");
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
/* parser.c */
|
||||||
|
SR_PRIV int sr_brymen_parse(const uint8_t *buf, float *floatval,
|
||||||
|
struct sr_datafeed_analog *analog, void *info);
|
||||||
|
|
||||||
|
static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
float floatval;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
struct sr_datafeed_analog analog;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
analog.num_samples = 1;
|
||||||
|
analog.mq = -1;
|
||||||
|
|
||||||
|
sr_brymen_parse(buf, &floatval, &analog, NULL);
|
||||||
|
analog.data = &floatval;
|
||||||
|
|
||||||
|
analog.probes = sdi->probes;
|
||||||
|
|
||||||
|
if (analog.mq != -1) {
|
||||||
|
/* Got a measurement. */
|
||||||
|
packet.type = SR_DF_ANALOG;
|
||||||
|
packet.payload = &analog;
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
devc->num_samples++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_new_data(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
int len, status, offset = 0;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
serial = sdi->conn;
|
||||||
|
|
||||||
|
/* Try to get as much data as the buffer can hold. */
|
||||||
|
len = DMM_BUFSIZE - devc->buflen;
|
||||||
|
len = serial_read(serial, devc->buf + devc->buflen, len);
|
||||||
|
if (len < 1) {
|
||||||
|
sr_err("Serial port read error: %d.", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
devc->buflen += len;
|
||||||
|
status = PACKET_INVALID_HEADER;
|
||||||
|
|
||||||
|
/* Now look for packets in that data. */
|
||||||
|
while (status != PACKET_NEED_MORE_DATA) {
|
||||||
|
/* We don't have a header, look for one. */
|
||||||
|
if (devc->next_packet_len == 0) {
|
||||||
|
len = devc->buflen - offset;
|
||||||
|
status = brymen_packet_length(devc->buf + offset, &len);
|
||||||
|
if (status == PACKET_HEADER_OK) {
|
||||||
|
/* We know how large the packet will be. */
|
||||||
|
devc->next_packet_len = len;
|
||||||
|
} else if (status == PACKET_NEED_MORE_DATA) {
|
||||||
|
/* We didn't yet receive the full header. */
|
||||||
|
devc->next_packet_len = 0;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* Invalid header. Move on. */
|
||||||
|
devc->next_packet_len = 0;
|
||||||
|
offset++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We know how the packet size, but did we receive all of it? */
|
||||||
|
if (devc->buflen - offset < devc->next_packet_len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* We should have a full packet here, so we can check it. */
|
||||||
|
if (brymen_packet_is_valid(devc->buf + offset)) {
|
||||||
|
handle_packet(devc->buf + offset, sdi);
|
||||||
|
offset += devc->next_packet_len;
|
||||||
|
} else {
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We are done with this packet. Look for a new one. */
|
||||||
|
devc->next_packet_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have any data left, move it to the beginning of our buffer. */
|
||||||
|
memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
|
||||||
|
devc->buflen -= offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
int ret;
|
||||||
|
int64_t time;
|
||||||
|
|
||||||
|
(void)fd;
|
||||||
|
|
||||||
|
if (!(sdi = cb_data))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
|
||||||
|
if (revents == G_IO_IN) {
|
||||||
|
/* Serial data arrived. */
|
||||||
|
handle_new_data(sdi);
|
||||||
|
} else {
|
||||||
|
/* Timeout, send another packet request. */
|
||||||
|
if ((ret = brymen_packet_request(serial)) < 0) {
|
||||||
|
sr_err("Failed to request packet: %d.", ret);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
|
||||||
|
sr_info("Requested number of samples reached, stopping.");
|
||||||
|
sdi->driver->dev_acquisition_stop(sdi, cb_data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devc->limit_msec) {
|
||||||
|
time = (g_get_monotonic_time() - devc->starttime) / 1000;
|
||||||
|
if (time > (int64_t)devc->limit_msec) {
|
||||||
|
sr_info("Requested time limit reached, stopping.");
|
||||||
|
sdi->driver->dev_acquisition_stop(sdi, cb_data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to find a valid packet in a serial data stream.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param buf Buffer containing the bytes to write.
|
||||||
|
* @param buflen Size of the buffer.
|
||||||
|
* @param get_packet_size Callback that assesses the size of incoming packets.
|
||||||
|
* @param is_valid Callback that assesses whether the packet is valid or not.
|
||||||
|
* @param timeout_ms The timeout after which, if no packet is detected, to
|
||||||
|
* abort scanning.
|
||||||
|
* @param baudrate The baudrate of the serial port. This parameter is not
|
||||||
|
* critical, but it helps fine tune the serial port polling
|
||||||
|
* delay.
|
||||||
|
*
|
||||||
|
* @return SR_OK if a valid packet is found within the given timeout,
|
||||||
|
* SR_ERR upon failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
|
||||||
|
uint8_t *buf, size_t *buflen,
|
||||||
|
packet_length_t get_packet_size,
|
||||||
|
packet_valid_t is_valid,
|
||||||
|
uint64_t timeout_ms, int baudrate)
|
||||||
|
{
|
||||||
|
int64_t start, time, byte_delay_us;
|
||||||
|
size_t ibuf, i, maxlen;
|
||||||
|
int status, len, packet_len, stream_len;
|
||||||
|
|
||||||
|
maxlen = *buflen;
|
||||||
|
|
||||||
|
sr_dbg("Detecting packets on FD %d (timeout = %" PRIu64
|
||||||
|
"ms, baudrate = %d).", serial->fd, timeout_ms, baudrate);
|
||||||
|
|
||||||
|
/* Assume 8n1 transmission. That is 10 bits for every byte. */
|
||||||
|
byte_delay_us = 10 * (1000000 / baudrate);
|
||||||
|
start = g_get_monotonic_time();
|
||||||
|
|
||||||
|
packet_len = i = ibuf = len = 0;
|
||||||
|
while (ibuf < maxlen) {
|
||||||
|
len = serial_read(serial, &buf[ibuf], maxlen - ibuf);
|
||||||
|
if (len > 0) {
|
||||||
|
ibuf += len;
|
||||||
|
sr_spew("Read %d bytes.", len);
|
||||||
|
}
|
||||||
|
|
||||||
|
time = g_get_monotonic_time() - start;
|
||||||
|
time /= 1000;
|
||||||
|
|
||||||
|
stream_len = ibuf - i;
|
||||||
|
if (stream_len > 0 && packet_len == 0) {
|
||||||
|
/* How large of a packet are we expecting? */
|
||||||
|
packet_len = stream_len;
|
||||||
|
status = get_packet_size(&buf[i], &packet_len);
|
||||||
|
switch(status) {
|
||||||
|
case PACKET_HEADER_OK:
|
||||||
|
/* We know how much data we need to wait for. */
|
||||||
|
break;
|
||||||
|
case PACKET_NEED_MORE_DATA:
|
||||||
|
/* We did not receive the full header. */
|
||||||
|
packet_len = 0;
|
||||||
|
break;
|
||||||
|
case PACKET_INVALID_HEADER:
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* We had enough data, but here was an error in
|
||||||
|
* parsing the header. Restart parsing from the
|
||||||
|
* next byte.
|
||||||
|
*/
|
||||||
|
packet_len = 0;
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((stream_len >= packet_len) && (packet_len != 0)) {
|
||||||
|
/* We have at least a packet's worth of data. */
|
||||||
|
if (is_valid(&buf[i])) {
|
||||||
|
sr_spew("Found valid %d-byte packet after "
|
||||||
|
"%" PRIu64 "ms.", packet_len, time);
|
||||||
|
*buflen = ibuf;
|
||||||
|
return SR_OK;
|
||||||
|
} else {
|
||||||
|
sr_spew("Got %d bytes, but not a valid "
|
||||||
|
"packet.", packet_len);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not a valid packet. Continue searching. */
|
||||||
|
i++;
|
||||||
|
packet_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time >= (int64_t)timeout_ms) {
|
||||||
|
/* Timeout */
|
||||||
|
sr_dbg("Detection timed out after %dms.", time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_usleep(byte_delay_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
*buflen = ibuf;
|
||||||
|
sr_err("Didn't find a valid packet (read %d bytes).", ibuf);
|
||||||
|
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBSIGROK_HARDWARE_BRYMEN_DMM_PROTOCOL_H
|
||||||
|
#define LIBSIGROK_HARDWARE_BRYMEN_DMM_PROTOCOL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "brymen-dmm: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
#define DMM_BUFSIZE 256
|
||||||
|
|
||||||
|
enum packet_len_status {
|
||||||
|
PACKET_HEADER_OK,
|
||||||
|
PACKET_NEED_MORE_DATA,
|
||||||
|
PACKET_INVALID_HEADER,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Private, per-device-instance driver context. */
|
||||||
|
struct dev_context {
|
||||||
|
/** The current sampling limit (in number of samples). */
|
||||||
|
uint64_t limit_samples;
|
||||||
|
|
||||||
|
/** The current sampling limit (in ms). */
|
||||||
|
uint64_t limit_msec;
|
||||||
|
|
||||||
|
/** Opaque pointer passed in by the frontend. */
|
||||||
|
void *cb_data;
|
||||||
|
|
||||||
|
/** The current number of already received samples. */
|
||||||
|
uint64_t num_samples;
|
||||||
|
|
||||||
|
/** Start time of acquisition session */
|
||||||
|
int64_t starttime;
|
||||||
|
|
||||||
|
uint8_t buf[DMM_BUFSIZE];
|
||||||
|
int bufoffset;
|
||||||
|
int buflen;
|
||||||
|
int next_packet_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that assesses the size and status of the incoming packet.
|
||||||
|
*
|
||||||
|
* @return PACKET_HEADER_OK - This is a proper packet header.
|
||||||
|
* PACKET_NEED_MORE_DATA The buffer does not contain the entire header.
|
||||||
|
* PACKET_INVALID_HEADER Not a valid start of packet.
|
||||||
|
*/
|
||||||
|
typedef int (*packet_length_t)(const uint8_t *buf, int *len);
|
||||||
|
|
||||||
|
SR_PRIV int brymen_dmm_receive_data(int fd, int revents, void *cb_data);
|
||||||
|
SR_PRIV int brymen_packet_request(struct sr_serial_dev_inst *serial);
|
||||||
|
|
||||||
|
SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len);
|
||||||
|
SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf);
|
||||||
|
|
||||||
|
SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
|
||||||
|
uint8_t *buf, size_t *buflen,
|
||||||
|
packet_length_t get_packet_size,
|
||||||
|
packet_valid_t is_valid,
|
||||||
|
uint64_t timeout_ms, int baudrate);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
##
|
##
|
||||||
## This file is part of the libsigrok project.
|
## This file is part of the libsigrok project.
|
||||||
##
|
##
|
||||||
## Copyright (C) 2017 Stefan Bruens <stefan.bruens@rwth-aachen.de>
|
## Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
##
|
##
|
||||||
## 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
|
||||||
|
|
@ -14,19 +14,21 @@
|
||||||
## GNU General Public License for more details.
|
## GNU General Public License for more details.
|
||||||
##
|
##
|
||||||
## You should have received a copy of the GNU General Public License
|
## You should have received a copy of the GNU General Public License
|
||||||
## along with this program; if not, see <http://www.gnu.org/licenses/>.
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
##
|
##
|
||||||
|
|
||||||
# Grant access permissions to users who are currently logged in locally.
|
if LA_CHRONOVU_LA8
|
||||||
# This is the default policy for systems using systemd-logind (or a
|
|
||||||
# compatible replacement).
|
|
||||||
#
|
|
||||||
# This file, when installed, must be installed with a name lexicographically
|
|
||||||
# sorted later than the accompanied 60-libsigrok.rules, and earlier than
|
|
||||||
# the systemd upstream 71-seat.rules.
|
|
||||||
|
|
||||||
ACTION!="add|change", GOTO="libsigrok_rules_uaccess_end"
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrokhwchronovula8.la
|
||||||
|
|
||||||
ENV{ID_SIGROK}=="1", TAG+="uaccess"
|
libsigrokhwchronovula8_la_SOURCES = \
|
||||||
|
api.c \
|
||||||
|
protocol.c \
|
||||||
|
protocol.h
|
||||||
|
|
||||||
LABEL="libsigrok_rules_uaccess_end"
|
libsigrokhwchronovula8_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
@ -0,0 +1,533 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ftdi.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver chronovu_la8_driver_info;
|
||||||
|
static struct sr_dev_driver *di = &chronovu_la8_driver_info;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will be initialized via config_list()/SR_CONF_SAMPLERATE.
|
||||||
|
*
|
||||||
|
* Min: 1 sample per 0.01us -> sample time is 0.084s, samplerate 100MHz
|
||||||
|
* Max: 1 sample per 2.55us -> sample time is 21.391s, samplerate 392.15kHz
|
||||||
|
*/
|
||||||
|
SR_PRIV uint64_t chronovu_la8_samplerates[255] = { 0 };
|
||||||
|
|
||||||
|
/* Note: Continuous sampling is not supported by the hardware. */
|
||||||
|
SR_PRIV const int32_t chronovu_la8_hwcaps[] = {
|
||||||
|
SR_CONF_LOGIC_ANALYZER,
|
||||||
|
SR_CONF_SAMPLERATE,
|
||||||
|
SR_CONF_LIMIT_MSEC, /* TODO: Not yet implemented. */
|
||||||
|
SR_CONF_LIMIT_SAMPLES, /* TODO: Not yet implemented. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The ChronoVu LA8 can have multiple PIDs. Older versions shipped with
|
||||||
|
* a standard FTDI USB VID/PID of 0403:6001, newer ones have 0403:8867.
|
||||||
|
*/
|
||||||
|
static const uint16_t usb_pids[] = {
|
||||||
|
0x6001,
|
||||||
|
0x8867,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Function prototypes. */
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
|
||||||
|
|
||||||
|
static int clear_instances(void)
|
||||||
|
{
|
||||||
|
GSList *l;
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
|
||||||
|
/* Properly close all devices. */
|
||||||
|
for (l = drvc->instances; l; l = l->next) {
|
||||||
|
if (!(sdi = l->data)) {
|
||||||
|
/* Log error, but continue cleaning up the rest. */
|
||||||
|
sr_err("%s: sdi was NULL, continuing.", __func__);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sdi->priv) {
|
||||||
|
devc = sdi->priv;
|
||||||
|
ftdi_free(devc->ftdic);
|
||||||
|
}
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
|
}
|
||||||
|
g_slist_free(drvc->instances);
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_init(struct sr_context *sr_ctx)
|
||||||
|
{
|
||||||
|
return std_hw_init(sr_ctx, di, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_scan(GSList *options)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
GSList *devices;
|
||||||
|
unsigned int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
(void)options;
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
|
||||||
|
/* Allocate memory for our private device context. */
|
||||||
|
if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
|
||||||
|
sr_err("Device context malloc failed.");
|
||||||
|
goto err_free_nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set some sane defaults. */
|
||||||
|
devc->ftdic = NULL;
|
||||||
|
devc->cur_samplerate = SR_MHZ(100); /* 100MHz == max. samplerate */
|
||||||
|
devc->limit_msec = 0;
|
||||||
|
devc->limit_samples = 0;
|
||||||
|
devc->cb_data = NULL;
|
||||||
|
memset(devc->mangled_buf, 0, BS);
|
||||||
|
devc->final_buf = NULL;
|
||||||
|
devc->trigger_pattern = 0x00; /* Value irrelevant, see trigger_mask. */
|
||||||
|
devc->trigger_mask = 0x00; /* All probes are "don't care". */
|
||||||
|
devc->trigger_timeout = 10; /* Default to 10s trigger timeout. */
|
||||||
|
devc->trigger_found = 0;
|
||||||
|
devc->done = 0;
|
||||||
|
devc->block_counter = 0;
|
||||||
|
devc->divcount = 0; /* 10ns sample period == 100MHz samplerate */
|
||||||
|
devc->usb_pid = 0;
|
||||||
|
|
||||||
|
/* Allocate memory where we'll store the de-mangled data. */
|
||||||
|
if (!(devc->final_buf = g_try_malloc(SDRAM_SIZE))) {
|
||||||
|
sr_err("final_buf malloc failed.");
|
||||||
|
goto err_free_devc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory for the FTDI context (ftdic) and initialize it. */
|
||||||
|
if (!(devc->ftdic = ftdi_new())) {
|
||||||
|
sr_err("%s: ftdi_new failed.", __func__);
|
||||||
|
goto err_free_final_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for the device and temporarily open it. */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(usb_pids); i++) {
|
||||||
|
sr_dbg("Probing for VID/PID %04x:%04x.", USB_VENDOR_ID,
|
||||||
|
usb_pids[i]);
|
||||||
|
ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID,
|
||||||
|
usb_pids[i], USB_DESCRIPTION, NULL);
|
||||||
|
if (ret == 0) {
|
||||||
|
sr_dbg("Found LA8 device (%04x:%04x).",
|
||||||
|
USB_VENDOR_ID, usb_pids[i]);
|
||||||
|
devc->usb_pid = usb_pids[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devc->usb_pid == 0)
|
||||||
|
goto err_free_ftdic;
|
||||||
|
|
||||||
|
/* Register the device with libsigrok. */
|
||||||
|
sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
|
||||||
|
USB_VENDOR_NAME, USB_MODEL_NAME, USB_MODEL_VERSION);
|
||||||
|
if (!sdi) {
|
||||||
|
sr_err("%s: sr_dev_inst_new failed.", __func__);
|
||||||
|
goto err_close_ftdic;
|
||||||
|
}
|
||||||
|
sdi->driver = di;
|
||||||
|
sdi->priv = devc;
|
||||||
|
|
||||||
|
for (i = 0; chronovu_la8_probe_names[i]; i++) {
|
||||||
|
if (!(probe = sr_probe_new(i, SR_PROBE_LOGIC, TRUE,
|
||||||
|
chronovu_la8_probe_names[i])))
|
||||||
|
return NULL;
|
||||||
|
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||||
|
}
|
||||||
|
|
||||||
|
devices = g_slist_append(devices, sdi);
|
||||||
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
|
|
||||||
|
/* Close device. We'll reopen it again when we need it. */
|
||||||
|
(void) la8_close(devc); /* Log, but ignore errors. */
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
|
||||||
|
err_close_ftdic:
|
||||||
|
(void) la8_close(devc); /* Log, but ignore errors. */
|
||||||
|
err_free_ftdic:
|
||||||
|
ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
|
||||||
|
err_free_final_buf:
|
||||||
|
g_free(devc->final_buf);
|
||||||
|
err_free_devc:
|
||||||
|
g_free(devc);
|
||||||
|
err_free_nothing:
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_dev_list(void)
|
||||||
|
{
|
||||||
|
return ((struct drv_context *)(di->priv))->instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_open(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("%s: sdi->priv was NULL.", __func__);
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Opening LA8 device (%04x:%04x).", USB_VENDOR_ID,
|
||||||
|
devc->usb_pid);
|
||||||
|
|
||||||
|
/* Open the device. */
|
||||||
|
if ((ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID,
|
||||||
|
devc->usb_pid, USB_DESCRIPTION, NULL)) < 0) {
|
||||||
|
sr_err("%s: ftdi_usb_open_desc: (%d) %s",
|
||||||
|
__func__, ret, ftdi_get_error_string(devc->ftdic));
|
||||||
|
(void) la8_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
sr_dbg("Device opened successfully.");
|
||||||
|
|
||||||
|
/* Purge RX/TX buffers in the FTDI chip. */
|
||||||
|
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
|
||||||
|
sr_err("%s: ftdi_usb_purge_buffers: (%d) %s",
|
||||||
|
__func__, ret, ftdi_get_error_string(devc->ftdic));
|
||||||
|
(void) la8_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
|
goto err_dev_open_close_ftdic;
|
||||||
|
}
|
||||||
|
sr_dbg("FTDI buffers purged successfully.");
|
||||||
|
|
||||||
|
/* Enable flow control in the FTDI chip. */
|
||||||
|
if ((ret = ftdi_setflowctrl(devc->ftdic, SIO_RTS_CTS_HS)) < 0) {
|
||||||
|
sr_err("%s: ftdi_setflowcontrol: (%d) %s",
|
||||||
|
__func__, ret, ftdi_get_error_string(devc->ftdic));
|
||||||
|
(void) la8_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
|
goto err_dev_open_close_ftdic;
|
||||||
|
}
|
||||||
|
sr_dbg("FTDI flow control enabled successfully.");
|
||||||
|
|
||||||
|
/* Wait 100ms. */
|
||||||
|
g_usleep(100 * 1000);
|
||||||
|
|
||||||
|
sdi->status = SR_ST_ACTIVE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
err_dev_open_close_ftdic:
|
||||||
|
(void) la8_close(devc); /* Log, but ignore errors. */
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_close(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
if (sdi->status == SR_ST_ACTIVE) {
|
||||||
|
sr_dbg("Status ACTIVE, closing device.");
|
||||||
|
(void) la8_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
|
} else {
|
||||||
|
sr_spew("Status not ACTIVE, nothing to do.");
|
||||||
|
}
|
||||||
|
|
||||||
|
sdi->status = SR_ST_INACTIVE;
|
||||||
|
|
||||||
|
g_free(devc->final_buf);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_cleanup(void)
|
||||||
|
{
|
||||||
|
if (!di->priv)
|
||||||
|
/* Can get called on an unused driver, doesn't matter. */
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
clear_instances();
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_SAMPLERATE:
|
||||||
|
if (sdi) {
|
||||||
|
devc = sdi->priv;
|
||||||
|
*data = g_variant_new_uint64(devc->cur_samplerate);
|
||||||
|
sr_spew("%s: Returning samplerate: %" PRIu64 "Hz.",
|
||||||
|
__func__, devc->cur_samplerate);
|
||||||
|
} else
|
||||||
|
return SR_ERR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("%s: sdi->priv was NULL.", __func__);
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_SAMPLERATE:
|
||||||
|
if (set_samplerate(sdi, g_variant_get_uint64(data)) == SR_ERR) {
|
||||||
|
sr_err("%s: setting samplerate failed.", __func__);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
sr_dbg("SAMPLERATE = %" PRIu64, devc->cur_samplerate);
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
if (g_variant_get_uint64(data) == 0) {
|
||||||
|
sr_err("%s: LIMIT_MSEC can't be 0.", __func__);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
devc->limit_msec = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("LIMIT_MSEC = %" PRIu64, devc->limit_msec);
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
if (g_variant_get_uint64(data) < MIN_NUM_SAMPLES) {
|
||||||
|
sr_err("%s: LIMIT_SAMPLES too small.", __func__);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
devc->limit_samples = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("LIMIT_SAMPLES = %" PRIu64, devc->limit_samples);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
GVariant *gvar;
|
||||||
|
GVariantBuilder gvb;
|
||||||
|
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
chronovu_la8_hwcaps,
|
||||||
|
ARRAY_SIZE(chronovu_la8_hwcaps),
|
||||||
|
sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
case SR_CONF_SAMPLERATE:
|
||||||
|
fill_supported_samplerates_if_needed();
|
||||||
|
g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
|
||||||
|
gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
|
||||||
|
chronovu_la8_samplerates,
|
||||||
|
ARRAY_SIZE(chronovu_la8_samplerates),
|
||||||
|
sizeof(uint64_t));
|
||||||
|
g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
|
||||||
|
*data = g_variant_builder_end(&gvb);
|
||||||
|
break;
|
||||||
|
case SR_CONF_TRIGGER_TYPE:
|
||||||
|
*data = g_variant_new_string(TRIGGER_TYPE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int receive_data(int fd, int revents, void *cb_data)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
(void)fd;
|
||||||
|
(void)revents;
|
||||||
|
|
||||||
|
if (!(sdi = cb_data)) {
|
||||||
|
sr_err("%s: cb_data was NULL.", __func__);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("%s: sdi->priv was NULL.", __func__);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!devc->ftdic) {
|
||||||
|
sr_err("%s: devc->ftdic was NULL.", __func__);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get one block of data. */
|
||||||
|
if ((ret = la8_read_block(devc)) < 0) {
|
||||||
|
sr_err("%s: la8_read_block error: %d.", __func__, ret);
|
||||||
|
hw_dev_acquisition_stop(sdi, sdi);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to get exactly NUM_BLOCKS blocks (i.e. 8MB) of data. */
|
||||||
|
if (devc->block_counter != (NUM_BLOCKS - 1)) {
|
||||||
|
devc->block_counter++;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Sampling finished, sending data to session bus now.");
|
||||||
|
|
||||||
|
/* All data was received and demangled, send it to the session bus. */
|
||||||
|
for (i = 0; i < NUM_BLOCKS; i++)
|
||||||
|
send_block_to_session_bus(devc, i);
|
||||||
|
|
||||||
|
hw_dev_acquisition_stop(sdi, sdi);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
|
||||||
|
void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
uint8_t buf[4];
|
||||||
|
int bytes_written;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("%s: sdi->priv was NULL.", __func__);
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!devc->ftdic) {
|
||||||
|
sr_err("%s: devc->ftdic was NULL.", __func__);
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->divcount = samplerate_to_divcount(devc->cur_samplerate);
|
||||||
|
if (devc->divcount == 0xff) {
|
||||||
|
sr_err("%s: Invalid divcount/samplerate.", __func__);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configure_probes(sdi) != SR_OK) {
|
||||||
|
sr_err("Failed to configure probes.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill acquisition parameters into buf[]. */
|
||||||
|
buf[0] = devc->divcount;
|
||||||
|
buf[1] = 0xff; /* This byte must always be 0xff. */
|
||||||
|
buf[2] = devc->trigger_pattern;
|
||||||
|
buf[3] = devc->trigger_mask;
|
||||||
|
|
||||||
|
/* Start acquisition. */
|
||||||
|
bytes_written = la8_write(devc, buf, 4);
|
||||||
|
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
sr_err("Acquisition failed to start: %d.", bytes_written);
|
||||||
|
return SR_ERR;
|
||||||
|
} else if (bytes_written != 4) {
|
||||||
|
sr_err("Acquisition failed to start: %d.", bytes_written);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Hardware acquisition started successfully.");
|
||||||
|
|
||||||
|
devc->cb_data = cb_data;
|
||||||
|
|
||||||
|
/* Send header packet to the session bus. */
|
||||||
|
std_session_send_df_header(cb_data, LOG_PREFIX);
|
||||||
|
|
||||||
|
/* Time when we should be done (for detecting trigger timeouts). */
|
||||||
|
devc->done = (devc->divcount + 1) * 0.08388608 + time(NULL)
|
||||||
|
+ devc->trigger_timeout;
|
||||||
|
devc->block_counter = 0;
|
||||||
|
devc->trigger_found = 0;
|
||||||
|
|
||||||
|
/* Hook up a dummy handler to receive data from the LA8. */
|
||||||
|
sr_source_add(-1, G_IO_IN, 0, receive_data, (void *)sdi);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
|
||||||
|
{
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
sr_dbg("Stopping acquisition.");
|
||||||
|
sr_source_remove(-1);
|
||||||
|
|
||||||
|
/* Send end packet to the session bus. */
|
||||||
|
sr_dbg("Sending SR_DF_END.");
|
||||||
|
packet.type = SR_DF_END;
|
||||||
|
sr_session_send(cb_data, &packet);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver chronovu_la8_driver_info = {
|
||||||
|
.name = "chronovu-la8",
|
||||||
|
.longname = "ChronoVu LA8",
|
||||||
|
.api_version = 1,
|
||||||
|
.init = hw_init,
|
||||||
|
.cleanup = hw_cleanup,
|
||||||
|
.scan = hw_scan,
|
||||||
|
.dev_list = hw_dev_list,
|
||||||
|
.dev_clear = clear_instances,
|
||||||
|
.config_get = config_get,
|
||||||
|
.config_set = config_set,
|
||||||
|
.config_list = config_list,
|
||||||
|
.dev_open = hw_dev_open,
|
||||||
|
.dev_close = hw_dev_close,
|
||||||
|
.dev_acquisition_start = hw_dev_acquisition_start,
|
||||||
|
.dev_acquisition_stop = hw_dev_acquisition_stop,
|
||||||
|
.priv = NULL,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,511 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ftdi.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
/* Probes are numbered 0-7. */
|
||||||
|
SR_PRIV const char *chronovu_la8_probe_names[NUM_PROBES + 1] = {
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV void fill_supported_samplerates_if_needed(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (chronovu_la8_samplerates[0] != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < 255; i++)
|
||||||
|
chronovu_la8_samplerates[254 - i] = SR_MHZ(100) / (i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given samplerate is supported by the LA8 hardware.
|
||||||
|
*
|
||||||
|
* @param samplerate The samplerate (in Hz) to check.
|
||||||
|
* @return 1 if the samplerate is supported/valid, 0 otherwise.
|
||||||
|
*/
|
||||||
|
SR_PRIV int is_valid_samplerate(uint64_t samplerate)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fill_supported_samplerates_if_needed();
|
||||||
|
|
||||||
|
for (i = 0; i < 255; i++) {
|
||||||
|
if (chronovu_la8_samplerates[i] == samplerate)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_err("Invalid samplerate (%" PRIu64 "Hz).", samplerate);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a samplerate (in Hz) to the 'divcount' value the LA8 wants.
|
||||||
|
*
|
||||||
|
* LA8 hardware: sample period = (divcount + 1) * 10ns.
|
||||||
|
* Min. value for divcount: 0x00 (10ns sample period, 100MHz samplerate).
|
||||||
|
* Max. value for divcount: 0xfe (2550ns sample period, 392.15kHz samplerate).
|
||||||
|
*
|
||||||
|
* @param samplerate The samplerate in Hz.
|
||||||
|
* @return The divcount value as needed by the hardware, or 0xff upon errors.
|
||||||
|
*/
|
||||||
|
SR_PRIV uint8_t samplerate_to_divcount(uint64_t samplerate)
|
||||||
|
{
|
||||||
|
if (samplerate == 0) {
|
||||||
|
sr_err("%s: samplerate was 0.", __func__);
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_valid_samplerate(samplerate)) {
|
||||||
|
sr_err("%s: Can't get divcount, samplerate invalid.", __func__);
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (SR_MHZ(100) / samplerate) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data of a certain length to the LA8's FTDI device.
|
||||||
|
*
|
||||||
|
* @param devc The struct containing private per-device-instance data. Must not
|
||||||
|
* be NULL. devc->ftdic must not be NULL either.
|
||||||
|
* @param buf The buffer containing the data to write. Must not be NULL.
|
||||||
|
* @param size The number of bytes to write. Must be >= 0.
|
||||||
|
* @return The number of bytes written, or a negative value upon errors.
|
||||||
|
*/
|
||||||
|
SR_PRIV int la8_write(struct dev_context *devc, uint8_t *buf, int size)
|
||||||
|
{
|
||||||
|
int bytes_written;
|
||||||
|
|
||||||
|
/* Note: Caller checked that devc and devc->ftdic != NULL. */
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
sr_err("%s: buf was NULL.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < 0) {
|
||||||
|
sr_err("%s: size was < 0.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_written = ftdi_write_data(devc->ftdic, buf, size);
|
||||||
|
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
sr_err("%s: ftdi_write_data: (%d) %s.", __func__,
|
||||||
|
bytes_written, ftdi_get_error_string(devc->ftdic));
|
||||||
|
(void) la8_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
|
} else if (bytes_written != size) {
|
||||||
|
sr_err("%s: bytes to write: %d, bytes written: %d.",
|
||||||
|
__func__, size, bytes_written);
|
||||||
|
(void) la8_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a certain amount of bytes from the LA8's FTDI device.
|
||||||
|
*
|
||||||
|
* @param devc The struct containing private per-device-instance data. Must not
|
||||||
|
* be NULL. devc->ftdic must not be NULL either.
|
||||||
|
* @param buf The buffer where the received data will be stored. Must not
|
||||||
|
* be NULL.
|
||||||
|
* @param size The number of bytes to read. Must be >= 1.
|
||||||
|
* @return The number of bytes read, or a negative value upon errors.
|
||||||
|
*/
|
||||||
|
SR_PRIV int la8_read(struct dev_context *devc, uint8_t *buf, int size)
|
||||||
|
{
|
||||||
|
int bytes_read;
|
||||||
|
|
||||||
|
/* Note: Caller checked that devc and devc->ftdic != NULL. */
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
sr_err("%s: buf was NULL.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size <= 0) {
|
||||||
|
sr_err("%s: size was <= 0.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_read = ftdi_read_data(devc->ftdic, buf, size);
|
||||||
|
|
||||||
|
if (bytes_read < 0) {
|
||||||
|
sr_err("%s: ftdi_read_data: (%d) %s.", __func__,
|
||||||
|
bytes_read, ftdi_get_error_string(devc->ftdic));
|
||||||
|
} else if (bytes_read != size) {
|
||||||
|
// sr_err("%s: Bytes to read: %d, bytes read: %d.",
|
||||||
|
// __func__, size, bytes_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int la8_close(struct dev_context *devc)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!devc) {
|
||||||
|
sr_err("%s: devc was NULL.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!devc->ftdic) {
|
||||||
|
sr_err("%s: devc->ftdic was NULL.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = ftdi_usb_close(devc->ftdic)) < 0) {
|
||||||
|
sr_err("%s: ftdi_usb_close: (%d) %s.",
|
||||||
|
__func__, ret, ftdi_get_error_string(devc->ftdic));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the ChronoVu LA8 USB port and reset the LA8 sequencer logic.
|
||||||
|
*
|
||||||
|
* @param devc The struct containing private per-device-instance data.
|
||||||
|
* @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
|
||||||
|
*/
|
||||||
|
SR_PRIV int la8_close_usb_reset_sequencer(struct dev_context *devc)
|
||||||
|
{
|
||||||
|
/* Magic sequence of bytes for resetting the LA8 sequencer logic. */
|
||||||
|
uint8_t buf[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!devc) {
|
||||||
|
sr_err("%s: devc was NULL.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!devc->ftdic) {
|
||||||
|
sr_err("%s: devc->ftdic was NULL.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devc->ftdic->usb_dev) {
|
||||||
|
/* Reset the LA8 sequencer logic, then wait 100ms. */
|
||||||
|
sr_dbg("Resetting sequencer logic.");
|
||||||
|
(void) la8_write(devc, buf, 8); /* Ignore errors. */
|
||||||
|
g_usleep(100 * 1000);
|
||||||
|
|
||||||
|
/* Purge FTDI buffers, then reset and close the FTDI device. */
|
||||||
|
sr_dbg("Purging buffers, resetting+closing FTDI device.");
|
||||||
|
|
||||||
|
/* Log errors, but ignore them (i.e., don't abort). */
|
||||||
|
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0)
|
||||||
|
sr_err("%s: ftdi_usb_purge_buffers: (%d) %s.",
|
||||||
|
__func__, ret, ftdi_get_error_string(devc->ftdic));
|
||||||
|
if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
|
||||||
|
sr_err("%s: ftdi_usb_reset: (%d) %s.", __func__,
|
||||||
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
|
if ((ret = ftdi_usb_close(devc->ftdic)) < 0)
|
||||||
|
sr_err("%s: ftdi_usb_close: (%d) %s.", __func__,
|
||||||
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close USB device, deinitialize and free the FTDI context. */
|
||||||
|
ftdi_free(devc->ftdic); /* Returns void. */
|
||||||
|
devc->ftdic = NULL;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the ChronoVu LA8.
|
||||||
|
*
|
||||||
|
* The LA8 must be reset after a failed read/write operation or upon timeouts.
|
||||||
|
*
|
||||||
|
* @param devc The struct containing private per-device-instance data.
|
||||||
|
* @return SR_OK upon success, SR_ERR upon failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int la8_reset(struct dev_context *devc)
|
||||||
|
{
|
||||||
|
uint8_t buf[BS];
|
||||||
|
time_t done, now;
|
||||||
|
int bytes_read;
|
||||||
|
|
||||||
|
if (!devc) {
|
||||||
|
sr_err("%s: devc was NULL.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!devc->ftdic) {
|
||||||
|
sr_err("%s: devc->ftdic was NULL.", __func__);
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Resetting the device.");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Purge pending read data from the FTDI hardware FIFO until
|
||||||
|
* no more data is left, or a timeout occurs (after 20s).
|
||||||
|
*/
|
||||||
|
done = 20 + time(NULL);
|
||||||
|
do {
|
||||||
|
/* TODO: Ignore errors? Check for < 0 at least! */
|
||||||
|
bytes_read = la8_read(devc, (uint8_t *)&buf, BS);
|
||||||
|
now = time(NULL);
|
||||||
|
} while ((done > now) && (bytes_read > 0));
|
||||||
|
|
||||||
|
/* Reset the LA8 sequencer logic and close the USB port. */
|
||||||
|
(void) la8_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
|
|
||||||
|
sr_dbg("Device reset finished.");
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int configure_probes(const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
const struct sr_probe *probe;
|
||||||
|
const GSList *l;
|
||||||
|
uint8_t probe_bit;
|
||||||
|
char *tc;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
devc->trigger_pattern = 0;
|
||||||
|
devc->trigger_mask = 0; /* Default to "don't care" for all probes. */
|
||||||
|
|
||||||
|
for (l = sdi->probes; l; l = l->next) {
|
||||||
|
probe = (struct sr_probe *)l->data;
|
||||||
|
|
||||||
|
if (!probe) {
|
||||||
|
sr_err("%s: probe was NULL.", __func__);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip disabled probes. */
|
||||||
|
if (!probe->enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Skip (enabled) probes with no configured trigger. */
|
||||||
|
if (!probe->trigger)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Note: Must only be run if probe->trigger != NULL. */
|
||||||
|
if (probe->index < 0 || probe->index > 7) {
|
||||||
|
sr_err("%s: Invalid probe index %d, must be "
|
||||||
|
"between 0 and 7.", __func__, probe->index);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
probe_bit = (1 << (probe->index));
|
||||||
|
|
||||||
|
/* Configure the probe's trigger mask and trigger pattern. */
|
||||||
|
for (tc = probe->trigger; tc && *tc; tc++) {
|
||||||
|
devc->trigger_mask |= probe_bit;
|
||||||
|
|
||||||
|
/* Sanity check, LA8 only supports low/high trigger. */
|
||||||
|
if (*tc != '0' && *tc != '1') {
|
||||||
|
sr_err("%s: Invalid trigger '%c', only "
|
||||||
|
"'0'/'1' supported.", __func__, *tc);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*tc == '1')
|
||||||
|
devc->trigger_pattern |= probe_bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Trigger mask = 0x%x, trigger pattern = 0x%x.",
|
||||||
|
devc->trigger_mask, devc->trigger_pattern);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
/* Note: Caller checked that sdi and sdi->priv != NULL. */
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
sr_spew("Trying to set samplerate to %" PRIu64 "Hz.", samplerate);
|
||||||
|
|
||||||
|
fill_supported_samplerates_if_needed();
|
||||||
|
|
||||||
|
/* Check if this is a samplerate supported by the hardware. */
|
||||||
|
if (!is_valid_samplerate(samplerate))
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
/* Set the new samplerate. */
|
||||||
|
devc->cur_samplerate = samplerate;
|
||||||
|
|
||||||
|
sr_dbg("Samplerate set to %" PRIu64 "Hz.", devc->cur_samplerate);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a block of data from the LA8.
|
||||||
|
*
|
||||||
|
* @param devc The struct containing private per-device-instance data. Must not
|
||||||
|
* be NULL. devc->ftdic must not be NULL either.
|
||||||
|
* @return SR_OK upon success, or SR_ERR upon errors.
|
||||||
|
*/
|
||||||
|
SR_PRIV int la8_read_block(struct dev_context *devc)
|
||||||
|
{
|
||||||
|
int i, byte_offset, m, mi, p, index, bytes_read;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
/* Note: Caller checked that devc and devc->ftdic != NULL. */
|
||||||
|
|
||||||
|
sr_spew("Reading block %d.", devc->block_counter);
|
||||||
|
|
||||||
|
bytes_read = la8_read(devc, devc->mangled_buf, BS);
|
||||||
|
|
||||||
|
/* If first block read got 0 bytes, retry until success or timeout. */
|
||||||
|
if ((bytes_read == 0) && (devc->block_counter == 0)) {
|
||||||
|
do {
|
||||||
|
sr_spew("Reading block 0 (again).");
|
||||||
|
bytes_read = la8_read(devc, devc->mangled_buf, BS);
|
||||||
|
/* TODO: How to handle read errors here? */
|
||||||
|
now = time(NULL);
|
||||||
|
} while ((devc->done > now) && (bytes_read == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if block read was successful or a timeout occured. */
|
||||||
|
if (bytes_read != BS) {
|
||||||
|
sr_err("Trigger timed out. Bytes read: %d.", bytes_read);
|
||||||
|
(void) la8_reset(devc); /* Ignore errors. */
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* De-mangle the data. */
|
||||||
|
sr_spew("Demangling block %d.", devc->block_counter);
|
||||||
|
byte_offset = devc->block_counter * BS;
|
||||||
|
m = byte_offset / (1024 * 1024);
|
||||||
|
mi = m * (1024 * 1024);
|
||||||
|
for (i = 0; i < BS; i++) {
|
||||||
|
p = i & (1 << 0);
|
||||||
|
index = m * 2 + (((byte_offset + i) - mi) / 2) * 16;
|
||||||
|
index += (devc->divcount == 0) ? p : (1 - p);
|
||||||
|
devc->final_buf[index] = devc->mangled_buf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV void send_block_to_session_bus(struct dev_context *devc, int block)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint8_t sample, expected_sample;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
struct sr_datafeed_logic logic;
|
||||||
|
int trigger_point; /* Relative trigger point (in this block). */
|
||||||
|
|
||||||
|
/* Note: No sanity checks on devc/block, caller is responsible. */
|
||||||
|
|
||||||
|
/* Check if we can find the trigger condition in this block. */
|
||||||
|
trigger_point = -1;
|
||||||
|
expected_sample = devc->trigger_pattern & devc->trigger_mask;
|
||||||
|
for (i = 0; i < BS; i++) {
|
||||||
|
/* Don't continue if the trigger was found previously. */
|
||||||
|
if (devc->trigger_found)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also, don't continue if triggers are "don't care", i.e. if
|
||||||
|
* no trigger conditions were specified by the user. In that
|
||||||
|
* case we don't want to send an SR_DF_TRIGGER packet at all.
|
||||||
|
*/
|
||||||
|
if (devc->trigger_mask == 0x00)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sample = *(devc->final_buf + (block * BS) + i);
|
||||||
|
|
||||||
|
if ((sample & devc->trigger_mask) == expected_sample) {
|
||||||
|
trigger_point = i;
|
||||||
|
devc->trigger_found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If no trigger was found, send one SR_DF_LOGIC packet. */
|
||||||
|
if (trigger_point == -1) {
|
||||||
|
/* Send an SR_DF_LOGIC packet to the session bus. */
|
||||||
|
sr_spew("Sending SR_DF_LOGIC packet (%d bytes) for "
|
||||||
|
"block %d.", BS, block);
|
||||||
|
packet.type = SR_DF_LOGIC;
|
||||||
|
packet.payload = &logic;
|
||||||
|
logic.length = BS;
|
||||||
|
logic.unitsize = 1;
|
||||||
|
logic.data = devc->final_buf + (block * BS);
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We found the trigger, so some special handling is needed. We have
|
||||||
|
* to send an SR_DF_LOGIC packet with the samples before the trigger
|
||||||
|
* (if any), then the SD_DF_TRIGGER packet itself, then another
|
||||||
|
* SR_DF_LOGIC packet with the samples after the trigger (if any).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TODO: Send SR_DF_TRIGGER packet before or after the actual sample? */
|
||||||
|
|
||||||
|
/* If at least one sample is located before the trigger... */
|
||||||
|
if (trigger_point > 0) {
|
||||||
|
/* Send pre-trigger SR_DF_LOGIC packet to the session bus. */
|
||||||
|
sr_spew("Sending pre-trigger SR_DF_LOGIC packet, "
|
||||||
|
"start = %d, length = %d.", block * BS, trigger_point);
|
||||||
|
packet.type = SR_DF_LOGIC;
|
||||||
|
packet.payload = &logic;
|
||||||
|
logic.length = trigger_point;
|
||||||
|
logic.unitsize = 1;
|
||||||
|
logic.data = devc->final_buf + (block * BS);
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the SR_DF_TRIGGER packet to the session bus. */
|
||||||
|
sr_spew("Sending SR_DF_TRIGGER packet, sample = %d.",
|
||||||
|
(block * BS) + trigger_point);
|
||||||
|
packet.type = SR_DF_TRIGGER;
|
||||||
|
packet.payload = NULL;
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
|
||||||
|
/* If at least one sample is located after the trigger... */
|
||||||
|
if (trigger_point < (BS - 1)) {
|
||||||
|
/* Send post-trigger SR_DF_LOGIC packet to the session bus. */
|
||||||
|
sr_spew("Sending post-trigger SR_DF_LOGIC packet, "
|
||||||
|
"start = %d, length = %d.",
|
||||||
|
(block * BS) + trigger_point, BS - trigger_point);
|
||||||
|
packet.type = SR_DF_LOGIC;
|
||||||
|
packet.payload = &logic;
|
||||||
|
logic.length = BS - trigger_point;
|
||||||
|
logic.unitsize = 1;
|
||||||
|
logic.data = devc->final_buf + (block * BS) + trigger_point;
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBSIGROK_HARDWARE_CHRONOVU_LA8_PROTOCOL_H
|
||||||
|
#define LIBSIGROK_HARDWARE_CHRONOVU_LA8_PROTOCOL_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <ftdi.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "la8: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
#define USB_VENDOR_ID 0x0403
|
||||||
|
#define USB_DESCRIPTION "ChronoVu LA8"
|
||||||
|
#define USB_VENDOR_NAME "ChronoVu"
|
||||||
|
#define USB_MODEL_NAME "LA8"
|
||||||
|
#define USB_MODEL_VERSION ""
|
||||||
|
|
||||||
|
#define NUM_PROBES 8
|
||||||
|
#define TRIGGER_TYPE "01"
|
||||||
|
#define SDRAM_SIZE (8 * 1024 * 1024)
|
||||||
|
#define MIN_NUM_SAMPLES 1
|
||||||
|
|
||||||
|
#define BS 4096 /* Block size */
|
||||||
|
#define NUM_BLOCKS 2048 /* Number of blocks */
|
||||||
|
|
||||||
|
/* Private, per-device-instance driver context. */
|
||||||
|
struct dev_context {
|
||||||
|
/** FTDI device context (used by libftdi). */
|
||||||
|
struct ftdi_context *ftdic;
|
||||||
|
|
||||||
|
/** The currently configured samplerate of the device. */
|
||||||
|
uint64_t cur_samplerate;
|
||||||
|
|
||||||
|
/** The current sampling limit (in ms). */
|
||||||
|
uint64_t limit_msec;
|
||||||
|
|
||||||
|
/** The current sampling limit (in number of samples). */
|
||||||
|
uint64_t limit_samples;
|
||||||
|
|
||||||
|
void *cb_data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A buffer containing some (mangled) samples from the device.
|
||||||
|
* Format: Pretty mangled-up (due to hardware reasons), see code.
|
||||||
|
*/
|
||||||
|
uint8_t mangled_buf[BS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An 8MB buffer where we'll store the de-mangled samples.
|
||||||
|
* Format: Each sample is 1 byte, MSB is channel 7, LSB is channel 0.
|
||||||
|
*/
|
||||||
|
uint8_t *final_buf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger pattern (MSB = channel 7, LSB = channel 0).
|
||||||
|
* A 1 bit matches a high signal, 0 matches a low signal on a probe.
|
||||||
|
* Only low/high triggers (but not e.g. rising/falling) are supported.
|
||||||
|
*/
|
||||||
|
uint8_t trigger_pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger mask (MSB = channel 7, LSB = channel 0).
|
||||||
|
* A 1 bit means "must match trigger_pattern", 0 means "don't care".
|
||||||
|
*/
|
||||||
|
uint8_t trigger_mask;
|
||||||
|
|
||||||
|
/** Time (in seconds) before the trigger times out. */
|
||||||
|
uint64_t trigger_timeout;
|
||||||
|
|
||||||
|
/** Tells us whether an SR_DF_TRIGGER packet was already sent. */
|
||||||
|
int trigger_found;
|
||||||
|
|
||||||
|
/** TODO */
|
||||||
|
time_t done;
|
||||||
|
|
||||||
|
/** Counter/index for the data block to be read. */
|
||||||
|
int block_counter;
|
||||||
|
|
||||||
|
/** The divcount value (determines the sample period) for the LA8. */
|
||||||
|
uint8_t divcount;
|
||||||
|
|
||||||
|
/** This ChronoVu LA8's USB PID (multiple versions exist). */
|
||||||
|
uint16_t usb_pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* protocol.c */
|
||||||
|
extern const int32_t chronovu_la8_hwcaps[];
|
||||||
|
extern uint64_t chronovu_la8_samplerates[];
|
||||||
|
extern SR_PRIV const char *chronovu_la8_probe_names[];
|
||||||
|
SR_PRIV void fill_supported_samplerates_if_needed(void);
|
||||||
|
SR_PRIV int is_valid_samplerate(uint64_t samplerate);
|
||||||
|
SR_PRIV uint8_t samplerate_to_divcount(uint64_t samplerate);
|
||||||
|
SR_PRIV int la8_write(struct dev_context *devc, uint8_t *buf, int size);
|
||||||
|
SR_PRIV int la8_read(struct dev_context *devc, uint8_t *buf, int size);
|
||||||
|
SR_PRIV int la8_close(struct dev_context *devc);
|
||||||
|
SR_PRIV int la8_close_usb_reset_sequencer(struct dev_context *devc);
|
||||||
|
SR_PRIV int la8_reset(struct dev_context *devc);
|
||||||
|
SR_PRIV int configure_probes(const struct sr_dev_inst *sdi);
|
||||||
|
SR_PRIV int set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
|
||||||
|
SR_PRIV int la8_read_block(struct dev_context *devc);
|
||||||
|
SR_PRIV void send_block_to_session_bus(struct dev_context *devc, int block);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
if HW_COLEAD_SLM
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrok_hw_colead_slm.la
|
||||||
|
|
||||||
|
libsigrok_hw_colead_slm_la_SOURCES = \
|
||||||
|
api.c \
|
||||||
|
protocol.c \
|
||||||
|
protocol.h
|
||||||
|
|
||||||
|
libsigrok_hw_colead_slm_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
@ -0,0 +1,280 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* The Colead SL-5868P uses this. */
|
||||||
|
#define SERIALCOMM "2400/8n1"
|
||||||
|
|
||||||
|
static const int32_t hwopts[] = {
|
||||||
|
SR_CONF_CONN,
|
||||||
|
SR_CONF_SERIALCOMM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int32_t hwcaps[] = {
|
||||||
|
SR_CONF_SOUNDLEVELMETER,
|
||||||
|
SR_CONF_LIMIT_SAMPLES,
|
||||||
|
SR_CONF_LIMIT_MSEC,
|
||||||
|
SR_CONF_CONTINUOUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver colead_slm_driver_info;
|
||||||
|
static struct sr_dev_driver *di = &colead_slm_driver_info;
|
||||||
|
|
||||||
|
/* Properly close and free all devices. */
|
||||||
|
static int clear_instances(void)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
GSList *l;
|
||||||
|
|
||||||
|
if (!(drvc = di->priv))
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
for (l = drvc->instances; l; l = l->next) {
|
||||||
|
if (!(sdi = l->data))
|
||||||
|
continue;
|
||||||
|
if (!(devc = sdi->priv))
|
||||||
|
continue;
|
||||||
|
serial = sdi->conn;
|
||||||
|
sr_serial_dev_inst_free(serial);
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
|
}
|
||||||
|
g_slist_free(drvc->instances);
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_init(struct sr_context *sr_ctx)
|
||||||
|
{
|
||||||
|
return std_hw_init(sr_ctx, di, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_scan(GSList *options)
|
||||||
|
{
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct sr_config *src;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
GSList *devices, *l;
|
||||||
|
const char *conn, *serialcomm;
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
|
||||||
|
conn = serialcomm = NULL;
|
||||||
|
for (l = options; l; l = l->next) {
|
||||||
|
src = l->data;
|
||||||
|
switch (src->key) {
|
||||||
|
case SR_CONF_CONN:
|
||||||
|
conn = g_variant_get_string(src->data, NULL);
|
||||||
|
break;
|
||||||
|
case SR_CONF_SERIALCOMM:
|
||||||
|
serialcomm = g_variant_get_string(src->data, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!conn)
|
||||||
|
return NULL;
|
||||||
|
if (!serialcomm)
|
||||||
|
serialcomm = SERIALCOMM;
|
||||||
|
|
||||||
|
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Colead",
|
||||||
|
"SL-5868P", NULL)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
|
||||||
|
sr_dbg("Device context malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sdi->conn = sr_serial_dev_inst_new(conn, serialcomm)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sdi->inst_type = SR_INST_SERIAL;
|
||||||
|
sdi->priv = devc;
|
||||||
|
sdi->driver = di;
|
||||||
|
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
|
||||||
|
return NULL;
|
||||||
|
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||||
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
|
devices = g_slist_append(devices, sdi);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_dev_list(void)
|
||||||
|
{
|
||||||
|
return ((struct drv_context *)(di->priv))->instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_open(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
sdi->status = SR_ST_ACTIVE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_close(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (serial && serial->fd != -1) {
|
||||||
|
serial_close(serial);
|
||||||
|
sdi->status = SR_ST_INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_cleanup(void)
|
||||||
|
{
|
||||||
|
clear_instances();
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("sdi->priv was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
/* TODO: not yet implemented */
|
||||||
|
if (g_variant_get_uint64(data) == 0) {
|
||||||
|
sr_err("LIMIT_MSEC can't be 0.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
devc->limit_msec = g_variant_get_uint64(data);;
|
||||||
|
sr_dbg("Setting time limit to %" PRIu64 "ms.",
|
||||||
|
devc->limit_msec);
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
devc->limit_samples = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("Setting sample limit to %" PRIu64 ".",
|
||||||
|
devc->limit_samples);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case SR_CONF_SCAN_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
|
||||||
|
void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("sdi->priv was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->cb_data = cb_data;
|
||||||
|
|
||||||
|
/* Send header packet to the session bus. */
|
||||||
|
std_session_send_df_header(cb_data, LOG_PREFIX);
|
||||||
|
|
||||||
|
/* Poll every 150ms, or whenever some data comes in. */
|
||||||
|
serial = sdi->conn;
|
||||||
|
sr_source_add(serial->fd, G_IO_IN, 150, colead_slm_receive_data,
|
||||||
|
(void *)sdi);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
|
||||||
|
{
|
||||||
|
return std_hw_dev_acquisition_stop_serial(sdi, cb_data, hw_dev_close,
|
||||||
|
sdi->conn, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver colead_slm_driver_info = {
|
||||||
|
.name = "colead-slm",
|
||||||
|
.longname = "Colead SLM",
|
||||||
|
.api_version = 1,
|
||||||
|
.init = hw_init,
|
||||||
|
.cleanup = hw_cleanup,
|
||||||
|
.scan = hw_scan,
|
||||||
|
.dev_list = hw_dev_list,
|
||||||
|
.dev_clear = clear_instances,
|
||||||
|
.config_get = NULL,
|
||||||
|
.config_set = config_set,
|
||||||
|
.config_list = config_list,
|
||||||
|
.dev_open = hw_dev_open,
|
||||||
|
.dev_close = hw_dev_close,
|
||||||
|
.dev_acquisition_start = hw_dev_acquisition_start,
|
||||||
|
.dev_acquisition_stop = hw_dev_acquisition_stop,
|
||||||
|
.priv = NULL,
|
||||||
|
};
|
||||||
|
|
@ -17,22 +17,20 @@
|
||||||
* 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 <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <string.h>
|
#include "libsigrok.h"
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
static void process_packet(const struct sr_dev_inst *sdi)
|
static void process_packet(const struct sr_dev_inst *sdi)
|
||||||
{
|
{
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
struct sr_datafeed_packet packet;
|
struct sr_datafeed_packet packet;
|
||||||
struct sr_datafeed_analog analog;
|
struct sr_datafeed_analog analog;
|
||||||
struct sr_analog_encoding encoding;
|
|
||||||
struct sr_analog_meaning meaning;
|
|
||||||
struct sr_analog_spec spec;
|
|
||||||
GString *dbg;
|
GString *dbg;
|
||||||
float fvalue;
|
float fvalue;
|
||||||
int checksum, mode, i;
|
int checksum, mode, i;
|
||||||
|
|
@ -74,17 +72,17 @@ static void process_packet(const struct sr_dev_inst *sdi)
|
||||||
}
|
}
|
||||||
fvalue /= 10;
|
fvalue /= 10;
|
||||||
|
|
||||||
sr_analog_init(&analog, &encoding, &meaning, &spec, 1);
|
memset(&analog, 0, sizeof(struct sr_datafeed_analog));
|
||||||
analog.meaning->mq = SR_MQ_SOUND_PRESSURE_LEVEL;
|
analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
|
||||||
analog.meaning->unit = SR_UNIT_DECIBEL_SPL;
|
analog.unit = SR_UNIT_DECIBEL_SPL;
|
||||||
analog.meaning->channels = sdi->channels;
|
analog.probes = sdi->probes;
|
||||||
analog.num_samples = 1;
|
analog.num_samples = 1;
|
||||||
analog.data = &fvalue;
|
analog.data = &fvalue;
|
||||||
|
|
||||||
/* High nibble should only have 0x01 or 0x02. */
|
/* High nibble should only have 0x01 or 0x02. */
|
||||||
mode = (devc->buf[2] >> 4) & 0x0f;
|
mode = (devc->buf[2] >> 4) & 0x0f;
|
||||||
if (mode == 0x02)
|
if (mode == 0x02)
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_HOLD;
|
analog.mqflags |= SR_MQFLAG_HOLD;
|
||||||
else if (mode != 0x01) {
|
else if (mode != 0x01) {
|
||||||
sr_dbg("unknown measurement mode 0x%.2x", mode);
|
sr_dbg("unknown measurement mode 0x%.2x", mode);
|
||||||
return;
|
return;
|
||||||
|
|
@ -95,89 +93,88 @@ static void process_packet(const struct sr_dev_inst *sdi)
|
||||||
* weighting. */
|
* weighting. */
|
||||||
mode = devc->buf[2] & 0x0f;
|
mode = devc->buf[2] & 0x0f;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
||||||
break;
|
break;
|
||||||
case 0x1:
|
case 0x1:
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
||||||
break;
|
break;
|
||||||
case 0x2:
|
case 0x2:
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
|
analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
||||||
break;
|
break;
|
||||||
case 0x3:
|
case 0x3:
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
|
analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
||||||
break;
|
break;
|
||||||
case 0x4:
|
case 0x4:
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
|
analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
||||||
break;
|
break;
|
||||||
case 0x5:
|
case 0x5:
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
|
analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
||||||
break;
|
break;
|
||||||
case 0x6:
|
case 0x6:
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
|
analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
|
||||||
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
||||||
break;
|
break;
|
||||||
case 0x7:
|
case 0x7:
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
|
analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
|
||||||
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
||||||
break;
|
break;
|
||||||
case 0x8:
|
case 0x8:
|
||||||
/* 10-second mean, but we don't have MQ flags to express it. */
|
/* 10-second mean, but we don't have MQ flags to express it. */
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_LAT \
|
analog.mqflags |= SR_MQFLAG_SPL_LAT \
|
||||||
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
||||||
break;
|
break;
|
||||||
case 0x9:
|
case 0x9:
|
||||||
/* Mean over a time period between 11 seconds and 24 hours.
|
/* Mean over a time period between 11 seconds and 24 hours.
|
||||||
* Which is so silly that there's no point in expressing
|
* Which is so silly that there's no point in expressing
|
||||||
* either this or the previous case. */
|
* either this or the previous case. */
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_LAT \
|
analog.mqflags |= SR_MQFLAG_SPL_LAT \
|
||||||
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_F;
|
||||||
break;
|
break;
|
||||||
case 0xa:
|
case 0xa:
|
||||||
/* 10-second mean. */
|
/* 10-second mean. */
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_LAT \
|
analog.mqflags |= SR_MQFLAG_SPL_LAT \
|
||||||
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
||||||
break;
|
break;
|
||||||
case 0xb:
|
case 0xb:
|
||||||
/* Mean over a time period between 11 seconds and 24 hours. */
|
/* Mean over a time period between 11 seconds and 24 hours. */
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_LAT \
|
analog.mqflags |= SR_MQFLAG_SPL_LAT \
|
||||||
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
|
||||||
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
| SR_MQFLAG_SPL_TIME_WEIGHT_S;
|
||||||
break;
|
break;
|
||||||
case 0xc:
|
case 0xc:
|
||||||
/* Internal calibration on 1kHz sine at 94dB, not useful
|
/* Internal calibration on 1kHz sine at 94dB, not useful
|
||||||
* to anything but the device. */
|
* to anything but the device. */
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
|
analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
|
||||||
break;
|
break;
|
||||||
case 0xd:
|
case 0xd:
|
||||||
/* Internal calibration on 1kHz sine at 94dB, not useful
|
/* Internal calibration on 1kHz sine at 94dB, not useful
|
||||||
* to anything but the device. */
|
* to anything but the device. */
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
|
analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sr_dbg("unknown configuration 0x%.2x", mode);
|
sr_dbg("unknown configuration 0x%.2x", mode);
|
||||||
return;
|
return;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
packet.type = SR_DF_ANALOG;
|
packet.type = SR_DF_ANALOG;
|
||||||
packet.payload = &analog;
|
packet.payload = &analog;
|
||||||
sr_session_send(sdi, &packet);
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
|
||||||
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
devc->num_samples++;
|
||||||
|
|
||||||
if (sr_sw_limits_check(&devc->limits))
|
|
||||||
sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
|
SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
|
@ -185,7 +182,7 @@ SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
|
||||||
const struct sr_dev_inst *sdi;
|
const struct sr_dev_inst *sdi;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
struct sr_serial_dev_inst *serial;
|
struct sr_serial_dev_inst *serial;
|
||||||
int delay_ms, len;
|
int len;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
||||||
(void)fd;
|
(void)fd;
|
||||||
|
|
@ -202,21 +199,20 @@ SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
|
||||||
serial = sdi->conn;
|
serial = sdi->conn;
|
||||||
if (devc->state == IDLE) {
|
if (devc->state == IDLE) {
|
||||||
if (serial_read_nonblocking(serial, buf, sizeof(buf)) != 1 || buf[0] != 0x10)
|
if (serial_read(serial, buf, 128) != 1 || buf[0] != 0x10)
|
||||||
/* Nothing there, or caught the tail end of a previous packet,
|
/* Nothing there, or caught the tail end of a previous packet,
|
||||||
* or some garbage. Unless it's a single "data ready" byte,
|
* or some garbage. Unless it's a single "data ready" byte,
|
||||||
* we don't want it. */
|
* we don't want it. */
|
||||||
return TRUE;
|
return TRUE;
|
||||||
/* Got 0x10, "measurement ready". */
|
/* Got 0x10, "measurement ready". */
|
||||||
delay_ms = serial_timeout(serial, 1);
|
if (serial_write(serial, "\x20", 1) == -1)
|
||||||
if (serial_write_blocking(serial, "\x20", 1, delay_ms) < 1)
|
sr_err("unable to send command: %s", strerror(errno));
|
||||||
sr_err("unable to send command");
|
|
||||||
else {
|
else {
|
||||||
devc->state = COMMAND_SENT;
|
devc->state = COMMAND_SENT;
|
||||||
devc->buflen = 0;
|
devc->buflen = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
len = serial_read_nonblocking(serial, devc->buf + devc->buflen,
|
len = serial_read(serial, devc->buf + devc->buflen,
|
||||||
10 - devc->buflen);
|
10 - devc->buflen);
|
||||||
if (len < 1)
|
if (len < 1)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
@ -236,3 +232,4 @@ SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBSIGROK_HARDWARE_COLEAD_SLM_PROTOCOL_H
|
||||||
|
#define LIBSIGROK_HARDWARE_COLEAD_SLM_PROTOCOL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "colead-slm: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IDLE,
|
||||||
|
COMMAND_SENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Private, per-device-instance driver context. */
|
||||||
|
struct dev_context {
|
||||||
|
/** The current sampling limit (in number of samples). */
|
||||||
|
uint64_t limit_samples;
|
||||||
|
|
||||||
|
/** The current sampling limit (in ms). */
|
||||||
|
uint64_t limit_msec;
|
||||||
|
|
||||||
|
/** Opaque pointer passed in by the frontend. */
|
||||||
|
void *cb_data;
|
||||||
|
|
||||||
|
/** The current number of already received samples. */
|
||||||
|
uint64_t num_samples;
|
||||||
|
int state;
|
||||||
|
char buf[10];
|
||||||
|
int buflen;
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
SUBDIRS = dmm
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrokhwcommon.la
|
||||||
|
|
||||||
|
libsigrokhwcommon_la_SOURCES = serial.c
|
||||||
|
|
||||||
|
if NEED_USB
|
||||||
|
libsigrokhwcommon_la_SOURCES += ezusb.c usb.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
libsigrokhwcommon_la_LIBADD = dmm/libsigrok_hw_common_dmm.la
|
||||||
|
|
||||||
|
libsigrokhwcommon_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrok_hw_common_dmm.la
|
||||||
|
|
||||||
|
libsigrok_hw_common_dmm_la_SOURCES = \
|
||||||
|
es51922.c \
|
||||||
|
fs9721.c \
|
||||||
|
fs9922.c \
|
||||||
|
metex14.c \
|
||||||
|
rs9lcd.c
|
||||||
|
|
||||||
|
libsigrok_hw_common_dmm_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,431 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cyrustek ES51922 protocol parser.
|
||||||
|
*
|
||||||
|
* Communication parameters: Unidirectional, 19230/7o1
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "es51922: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
/* Factors for the respective measurement mode (0 means "invalid"). */
|
||||||
|
static const float factors[8][8] = {
|
||||||
|
{1e-4, 1e-3, 1e-2, 1e-1, 1e-5, 0, 0, 0}, /* V */
|
||||||
|
{1e-8, 1e-7, 0, 0, 0, 0, 0, 0}, /* uA */
|
||||||
|
{1e-6, 1e-5, 0, 0, 0, 0, 0, 0}, /* mA */
|
||||||
|
{1e-3, 0, 0, 0, 0, 0, 0, 0}, /* 22A */
|
||||||
|
{1e-4, 1e-3, 1e-2, 1e-1, 1, 0, 0, 0}, /* Manual A */
|
||||||
|
{1e-2, 1e-1, 1, 1e1, 1e2, 1e3, 1e4, 0}, /* Resistance */
|
||||||
|
{1e-2, 1e-1, 0, 1, 1e1, 1e2, 1e3, 1e4}, /* Frequency */
|
||||||
|
{1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5}, /* Capacitance */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int parse_value(const uint8_t *buf, float *result)
|
||||||
|
{
|
||||||
|
int sign, intval;
|
||||||
|
float floatval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bytes 1-5: Value (5 decimal digits)
|
||||||
|
*
|
||||||
|
* Over limit: "0L." on the display, "22580" as protocol "digits".
|
||||||
|
* (chip max. value is 22000, so 22580 is out of range)
|
||||||
|
*
|
||||||
|
* Example: "OL.", auto-range mega-ohm mode
|
||||||
|
* Hex: 36 32 32 35 38 30 33 31 30 30 32 30 0d 0a
|
||||||
|
* ASCII: 2 2 5 8 0
|
||||||
|
*/
|
||||||
|
if (!strncmp((const char *)&buf[1], "22580", 5)) {
|
||||||
|
sr_spew("Over limit.");
|
||||||
|
*result = INFINITY;
|
||||||
|
return SR_OK;
|
||||||
|
} else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
|
||||||
|
!isdigit(buf[3]) || !isdigit(buf[4]) || !isdigit(buf[5])) {
|
||||||
|
sr_err("Value contained invalid digits: %02x %02x %02x %02x "
|
||||||
|
"%02x (%c %c %c %c %c).",
|
||||||
|
buf[1], buf[2], buf[3], buf[4], buf[5]);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
intval = 0;
|
||||||
|
intval += (buf[1] - '0') * 10000;
|
||||||
|
intval += (buf[2] - '0') * 1000;
|
||||||
|
intval += (buf[3] - '0') * 100;
|
||||||
|
intval += (buf[4] - '0') * 10;
|
||||||
|
intval += (buf[5] - '0') * 1;
|
||||||
|
|
||||||
|
floatval = (float)intval;
|
||||||
|
|
||||||
|
/* Note: The decimal point position will be parsed later. */
|
||||||
|
|
||||||
|
/* Byte 7: Sign bit (and other stuff) */
|
||||||
|
sign = ((buf[7] & (1 << 2)) != 0) ? -1 : 1;
|
||||||
|
|
||||||
|
/* Apply sign. */
|
||||||
|
floatval *= sign;
|
||||||
|
|
||||||
|
sr_spew("The display value is %f.", floatval);
|
||||||
|
|
||||||
|
*result = floatval;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_range(uint8_t b, float *floatval,
|
||||||
|
const struct es51922_info *info)
|
||||||
|
{
|
||||||
|
int idx, mode;
|
||||||
|
|
||||||
|
idx = b - '0';
|
||||||
|
|
||||||
|
if (!(idx >= 0 && idx <= 7)) {
|
||||||
|
sr_dbg("Invalid range byte / index: 0x%02x / 0x%02x.", b, idx);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse range byte (depends on the measurement mode). */
|
||||||
|
if (info->is_voltage)
|
||||||
|
mode = 0; /* V */
|
||||||
|
else if (info->is_current && info->is_micro)
|
||||||
|
mode = 1; /* uA */
|
||||||
|
else if (info->is_current && info->is_milli)
|
||||||
|
mode = 2; /* mA */
|
||||||
|
else if (info->is_current && !info->is_micro && !info->is_milli)
|
||||||
|
mode = 3; /* 22A */
|
||||||
|
else if (info->is_current && !info->is_auto)
|
||||||
|
mode = 4; /* Manual A */
|
||||||
|
else if (info->is_resistance)
|
||||||
|
mode = 5; /* Resistance */
|
||||||
|
else if (info->is_frequency)
|
||||||
|
mode = 6; /* Frequency */
|
||||||
|
else if (info->is_capacitance)
|
||||||
|
mode = 7; /* Capacitance */
|
||||||
|
else {
|
||||||
|
sr_dbg("Invalid mode, range byte was: 0x%02x.", b);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (factors[mode][idx] == 0) {
|
||||||
|
sr_dbg("Invalid factor for range byte: 0x%02x.", b);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply respective factor (mode-dependent) on the value. */
|
||||||
|
*floatval *= factors[mode][idx];
|
||||||
|
sr_dbg("Applying factor %f, new value is %f.",
|
||||||
|
factors[mode][idx], *floatval);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_flags(const uint8_t *buf, struct es51922_info *info)
|
||||||
|
{
|
||||||
|
/* Get is_judge and is_vbar early on, we'll need it. */
|
||||||
|
info->is_judge = (buf[7] & (1 << 3)) != 0;
|
||||||
|
info->is_vbar = (buf[11] & (1 << 2)) != 0;
|
||||||
|
|
||||||
|
/* Byte 6: Function */
|
||||||
|
switch (buf[6]) {
|
||||||
|
case 0x3b: /* V */
|
||||||
|
info->is_voltage = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x3d: /* uA */
|
||||||
|
info->is_auto = info->is_micro = info->is_current = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x3f: /* mA */
|
||||||
|
info->is_auto = info->is_milli = info->is_current = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x30: /* 22A */
|
||||||
|
info->is_current = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x39: /* Manual A */
|
||||||
|
info->is_auto = FALSE; /* Manual mode */
|
||||||
|
info->is_current = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x33: /* Resistance */
|
||||||
|
info->is_resistance = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x35: /* Continuity */
|
||||||
|
info->is_continuity = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x31: /* Diode */
|
||||||
|
info->is_diode = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x32: /* Frequency / duty cycle */
|
||||||
|
if (info->is_judge)
|
||||||
|
info->is_frequency = TRUE;
|
||||||
|
else
|
||||||
|
info->is_duty_cycle = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x36: /* Capacitance */
|
||||||
|
info->is_capacitance = TRUE;
|
||||||
|
break;
|
||||||
|
case 0x34: /* Temperature */
|
||||||
|
info->is_temperature = TRUE;
|
||||||
|
if (info->is_judge)
|
||||||
|
info->is_celsius = TRUE;
|
||||||
|
else
|
||||||
|
info->is_fahrenheit = TRUE;
|
||||||
|
/* IMPORTANT: The digits always represent Celsius! */
|
||||||
|
break;
|
||||||
|
case 0x3e: /* ADP */
|
||||||
|
info->is_adp = TRUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Invalid function byte: 0x%02x.", buf[6]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Byte 7: Status */
|
||||||
|
/* Bits [6:4]: Always 0b011 */
|
||||||
|
info->is_judge = (buf[7] & (1 << 3)) != 0;
|
||||||
|
info->is_sign = (buf[7] & (1 << 2)) != 0;
|
||||||
|
info->is_batt = (buf[7] & (1 << 1)) != 0; /* Battery low */
|
||||||
|
info->is_ol = (buf[7] & (1 << 0)) != 0; /* Input overflow */
|
||||||
|
|
||||||
|
/* Byte 8: Option 1 */
|
||||||
|
/* Bits [6:4]: Always 0b011 */
|
||||||
|
info->is_max = (buf[8] & (1 << 3)) != 0;
|
||||||
|
info->is_min = (buf[8] & (1 << 2)) != 0;
|
||||||
|
info->is_rel = (buf[8] & (1 << 1)) != 0;
|
||||||
|
info->is_rmr = (buf[8] & (1 << 0)) != 0;
|
||||||
|
|
||||||
|
/* Byte 9: Option 2 */
|
||||||
|
/* Bits [6:4]: Always 0b011 */
|
||||||
|
info->is_ul = (buf[9] & (1 << 3)) != 0;
|
||||||
|
info->is_pmax = (buf[9] & (1 << 2)) != 0; /* Max. peak value */
|
||||||
|
info->is_pmin = (buf[9] & (1 << 1)) != 0; /* Min. peak value */
|
||||||
|
/* Bit 0: Always 0 */
|
||||||
|
|
||||||
|
/* Byte 10: Option 3 */
|
||||||
|
/* Bits [6:4]: Always 0b011 */
|
||||||
|
info->is_dc = (buf[10] & (1 << 3)) != 0;
|
||||||
|
info->is_ac = (buf[10] & (1 << 2)) != 0;
|
||||||
|
info->is_auto = (buf[10] & (1 << 1)) != 0;
|
||||||
|
info->is_vahz = (buf[10] & (1 << 0)) != 0;
|
||||||
|
|
||||||
|
/* Byte 11: Option 4 */
|
||||||
|
/* Bits [6:3]: Always 0b0110 */
|
||||||
|
info->is_vbar = (buf[11] & (1 << 2)) != 0;
|
||||||
|
info->is_hold = (buf[11] & (1 << 1)) != 0;
|
||||||
|
info->is_lpf = (buf[11] & (1 << 0)) != 0; /* Low pass filter on */
|
||||||
|
|
||||||
|
/* Byte 12: Always '\r' (carriage return, 0x0d, 13) */
|
||||||
|
|
||||||
|
/* Byte 13: Always '\n' (newline, 0x0a, 10) */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_flags(struct sr_datafeed_analog *analog,
|
||||||
|
float *floatval, const struct es51922_info *info)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Note: is_micro etc. are not used directly to multiply/divide
|
||||||
|
* floatval, this is handled via parse_range() and factors[][].
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Measurement modes */
|
||||||
|
if (info->is_voltage) {
|
||||||
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
|
analog->unit = SR_UNIT_VOLT;
|
||||||
|
}
|
||||||
|
if (info->is_current) {
|
||||||
|
analog->mq = SR_MQ_CURRENT;
|
||||||
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
|
}
|
||||||
|
if (info->is_resistance) {
|
||||||
|
analog->mq = SR_MQ_RESISTANCE;
|
||||||
|
analog->unit = SR_UNIT_OHM;
|
||||||
|
}
|
||||||
|
if (info->is_frequency) {
|
||||||
|
analog->mq = SR_MQ_FREQUENCY;
|
||||||
|
analog->unit = SR_UNIT_HERTZ;
|
||||||
|
}
|
||||||
|
if (info->is_capacitance) {
|
||||||
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
|
analog->unit = SR_UNIT_FARAD;
|
||||||
|
}
|
||||||
|
if (info->is_temperature && info->is_celsius) {
|
||||||
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
|
}
|
||||||
|
if (info->is_temperature && info->is_fahrenheit) {
|
||||||
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
|
analog->unit = SR_UNIT_FAHRENHEIT;
|
||||||
|
}
|
||||||
|
if (info->is_continuity) {
|
||||||
|
analog->mq = SR_MQ_CONTINUITY;
|
||||||
|
analog->unit = SR_UNIT_BOOLEAN;
|
||||||
|
*floatval = (*floatval < 0.0) ? 0.0 : 1.0;
|
||||||
|
}
|
||||||
|
if (info->is_diode) {
|
||||||
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
|
analog->unit = SR_UNIT_VOLT;
|
||||||
|
}
|
||||||
|
if (info->is_duty_cycle) {
|
||||||
|
analog->mq = SR_MQ_DUTY_CYCLE;
|
||||||
|
analog->unit = SR_UNIT_PERCENTAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Measurement related flags */
|
||||||
|
if (info->is_ac)
|
||||||
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
|
if (info->is_dc)
|
||||||
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
|
if (info->is_auto)
|
||||||
|
analog->mqflags |= SR_MQFLAG_AUTORANGE;
|
||||||
|
if (info->is_hold)
|
||||||
|
/*
|
||||||
|
* Note: HOLD only affects the number displayed on the LCD,
|
||||||
|
* but not the value sent via the protocol! It also does not
|
||||||
|
* affect the bargraph on the LCD.
|
||||||
|
*/
|
||||||
|
analog->mqflags |= SR_MQFLAG_HOLD;
|
||||||
|
if (info->is_max)
|
||||||
|
analog->mqflags |= SR_MQFLAG_MAX;
|
||||||
|
if (info->is_min)
|
||||||
|
analog->mqflags |= SR_MQFLAG_MIN;
|
||||||
|
if (info->is_rel)
|
||||||
|
analog->mqflags |= SR_MQFLAG_RELATIVE;
|
||||||
|
|
||||||
|
/* Other flags */
|
||||||
|
if (info->is_judge)
|
||||||
|
sr_spew("Judge bit is set.");
|
||||||
|
if (info->is_batt)
|
||||||
|
sr_spew("Battery is low.");
|
||||||
|
if (info->is_ol)
|
||||||
|
sr_spew("Input overflow.");
|
||||||
|
if (info->is_pmax)
|
||||||
|
sr_spew("pMAX active, LCD shows max. peak value.");
|
||||||
|
if (info->is_pmin)
|
||||||
|
sr_spew("pMIN active, LCD shows min. peak value.");
|
||||||
|
if (info->is_vahz)
|
||||||
|
sr_spew("VAHZ active.");
|
||||||
|
if (info->is_vbar)
|
||||||
|
sr_spew("VBAR active.");
|
||||||
|
if (info->is_lpf)
|
||||||
|
sr_spew("Low-pass filter feature is active.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean flags_valid(const struct es51922_info *info)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
|
||||||
|
/* Does the packet have more than one multiplier? */
|
||||||
|
count = 0;
|
||||||
|
count += (info->is_nano) ? 1 : 0;
|
||||||
|
count += (info->is_micro) ? 1 : 0;
|
||||||
|
count += (info->is_milli) ? 1 : 0;
|
||||||
|
/* Note: No 'kilo' or 'mega' bits per se in this protocol. */
|
||||||
|
if (count > 1) {
|
||||||
|
sr_err("More than one multiplier detected in packet.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Does the packet "measure" more than one type of value? */
|
||||||
|
count = 0;
|
||||||
|
count += (info->is_voltage) ? 1 : 0;
|
||||||
|
count += (info->is_current) ? 1 : 0;
|
||||||
|
count += (info->is_resistance) ? 1 : 0;
|
||||||
|
count += (info->is_frequency) ? 1 : 0;
|
||||||
|
count += (info->is_capacitance) ? 1 : 0;
|
||||||
|
count += (info->is_temperature) ? 1 : 0;
|
||||||
|
count += (info->is_continuity) ? 1 : 0;
|
||||||
|
count += (info->is_diode) ? 1 : 0;
|
||||||
|
count += (info->is_duty_cycle) ? 1 : 0;
|
||||||
|
if (count > 1) {
|
||||||
|
sr_err("More than one measurement type detected in packet.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Both AC and DC set? */
|
||||||
|
if (info->is_ac && info->is_dc) {
|
||||||
|
sr_err("Both AC and DC flags detected in packet.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV gboolean sr_es51922_packet_valid(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
struct es51922_info info;
|
||||||
|
|
||||||
|
memset(&info, 0x00, sizeof(struct es51922_info));
|
||||||
|
parse_flags(buf, &info);
|
||||||
|
|
||||||
|
if (!flags_valid(&info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (buf[12] != '\r' || buf[13] != '\n') {
|
||||||
|
sr_spew("Packet doesn't end with \\r\\n.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a protocol packet.
|
||||||
|
*
|
||||||
|
* @param buf Buffer containing the protocol packet. Must not be NULL.
|
||||||
|
* @param floatval Pointer to a float variable. That variable will contain the
|
||||||
|
* result value upon parsing success. Must not be NULL.
|
||||||
|
* @param analog Pointer to a struct sr_datafeed_analog. The struct will be
|
||||||
|
* filled with data according to the protocol packet.
|
||||||
|
* Must not be NULL.
|
||||||
|
* @param info Pointer to a struct es51922_info. The struct will be filled
|
||||||
|
* with data according to the protocol packet. Must not be NULL.
|
||||||
|
*
|
||||||
|
* @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
|
||||||
|
* 'analog' variable contents are undefined and should not be used.
|
||||||
|
*/
|
||||||
|
SR_PRIV int sr_es51922_parse(const uint8_t *buf, float *floatval,
|
||||||
|
struct sr_datafeed_analog *analog, void *info)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct es51922_info *info_local;
|
||||||
|
|
||||||
|
info_local = (struct es51922_info *)info;
|
||||||
|
|
||||||
|
if ((ret = parse_value(buf, floatval)) != SR_OK) {
|
||||||
|
sr_err("Error parsing value: %d.", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(info_local, 0x00, sizeof(struct es51922_info));
|
||||||
|
parse_flags(buf, info_local);
|
||||||
|
handle_flags(analog, floatval, info_local);
|
||||||
|
|
||||||
|
return parse_range(buf[0], floatval, info_local);
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -30,15 +31,21 @@
|
||||||
* - The protocol seems to be exactly the same.
|
* - The protocol seems to be exactly the same.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <libsigrok/libsigrok.h>
|
#include "libsigrok.h"
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
#define LOG_PREFIX "fs9721"
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "fs9721: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
static int parse_digit(uint8_t b)
|
static int parse_digit(uint8_t b)
|
||||||
{
|
{
|
||||||
|
|
@ -64,7 +71,7 @@ static int parse_digit(uint8_t b)
|
||||||
case 0x3f:
|
case 0x3f:
|
||||||
return 9;
|
return 9;
|
||||||
default:
|
default:
|
||||||
sr_dbg("Invalid digit byte: 0x%02x.", b);
|
sr_err("Invalid digit byte: 0x%02x.", b);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +83,7 @@ static gboolean sync_nibbles_valid(const uint8_t *buf)
|
||||||
/* Check the synchronization nibbles, and make sure they all match. */
|
/* Check the synchronization nibbles, and make sure they all match. */
|
||||||
for (i = 0; i < FS9721_PACKET_SIZE; i++) {
|
for (i = 0; i < FS9721_PACKET_SIZE; i++) {
|
||||||
if (((buf[i] >> 4) & 0x0f) != (i + 1)) {
|
if (((buf[i] >> 4) & 0x0f) != (i + 1)) {
|
||||||
sr_dbg("Sync nibble in byte %d (0x%02x) is invalid.",
|
sr_err("Sync nibble in byte %d (0x%02x) is invalid.",
|
||||||
i, buf[i]);
|
i, buf[i]);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +104,7 @@ static gboolean flags_valid(const struct fs9721_info *info)
|
||||||
count += (info->is_kilo) ? 1 : 0;
|
count += (info->is_kilo) ? 1 : 0;
|
||||||
count += (info->is_mega) ? 1 : 0;
|
count += (info->is_mega) ? 1 : 0;
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
sr_dbg("More than one multiplier detected in packet.");
|
sr_err("More than one multiplier detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,26 +117,26 @@ static gboolean flags_valid(const struct fs9721_info *info)
|
||||||
count += (info->is_volt) ? 1 : 0;
|
count += (info->is_volt) ? 1 : 0;
|
||||||
count += (info->is_percent) ? 1 : 0;
|
count += (info->is_percent) ? 1 : 0;
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
sr_dbg("More than one measurement type detected in packet.");
|
sr_err("More than one measurement type detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Both AC and DC set? */
|
/* Both AC and DC set? */
|
||||||
if (info->is_ac && info->is_dc) {
|
if (info->is_ac && info->is_dc) {
|
||||||
sr_dbg("Both AC and DC flags detected in packet.");
|
sr_err("Both AC and DC flags detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RS232 flag not set? */
|
/* RS232 flag not set? */
|
||||||
if (!info->is_rs232) {
|
if (!info->is_rs232) {
|
||||||
sr_dbg("No RS232 flag detected in packet.");
|
sr_err("No RS232 flag detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_value(const uint8_t *buf, float *result, int *exponent)
|
static int parse_value(const uint8_t *buf, float *result)
|
||||||
{
|
{
|
||||||
int i, sign, intval = 0, digits[4];
|
int i, sign, intval = 0, digits[4];
|
||||||
uint8_t digit_bytes[4];
|
uint8_t digit_bytes[4];
|
||||||
|
|
@ -178,16 +185,15 @@ static int parse_value(const uint8_t *buf, float *result, int *exponent)
|
||||||
|
|
||||||
/* Decimal point position. */
|
/* Decimal point position. */
|
||||||
if ((buf[3] & (1 << 3)) != 0) {
|
if ((buf[3] & (1 << 3)) != 0) {
|
||||||
*exponent = -3;
|
floatval /= 1000;
|
||||||
sr_spew("Decimal point after first digit.");
|
sr_spew("Decimal point after first digit.");
|
||||||
} else if ((buf[5] & (1 << 3)) != 0) {
|
} else if ((buf[5] & (1 << 3)) != 0) {
|
||||||
*exponent = -2;
|
floatval /= 100;
|
||||||
sr_spew("Decimal point after second digit.");
|
sr_spew("Decimal point after second digit.");
|
||||||
} else if ((buf[7] & (1 << 3)) != 0) {
|
} else if ((buf[7] & (1 << 3)) != 0) {
|
||||||
*exponent = -1;
|
floatval /= 10;
|
||||||
sr_spew("Decimal point after third digit.");
|
sr_spew("Decimal point after third digit.");
|
||||||
} else {
|
} else {
|
||||||
*exponent = 0;
|
|
||||||
sr_spew("No decimal point in the number.");
|
sr_spew("No decimal point in the number.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,69 +250,66 @@ static void parse_flags(const uint8_t *buf, struct fs9721_info *info)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
|
static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
|
||||||
int *exponent, const struct fs9721_info *info)
|
const struct fs9721_info *info)
|
||||||
{
|
{
|
||||||
/* Factors */
|
/* Factors */
|
||||||
if (info->is_nano)
|
if (info->is_nano)
|
||||||
*exponent -= 9;
|
*floatval /= 1000000000;
|
||||||
if (info->is_micro)
|
if (info->is_micro)
|
||||||
*exponent -= 6;
|
*floatval /= 1000000;
|
||||||
if (info->is_milli)
|
if (info->is_milli)
|
||||||
*exponent -= 3;
|
*floatval /= 1000;
|
||||||
if (info->is_kilo)
|
if (info->is_kilo)
|
||||||
*exponent += 3;
|
*floatval *= 1000;
|
||||||
if (info->is_mega)
|
if (info->is_mega)
|
||||||
*exponent += 6;
|
*floatval *= 1000000;
|
||||||
*floatval *= powf(10, *exponent);
|
|
||||||
|
|
||||||
/* Measurement modes */
|
/* Measurement modes */
|
||||||
if (info->is_volt) {
|
if (info->is_volt) {
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
}
|
}
|
||||||
if (info->is_ampere) {
|
if (info->is_ampere) {
|
||||||
analog->meaning->mq = SR_MQ_CURRENT;
|
analog->mq = SR_MQ_CURRENT;
|
||||||
analog->meaning->unit = SR_UNIT_AMPERE;
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
}
|
}
|
||||||
if (info->is_ohm) {
|
if (info->is_ohm) {
|
||||||
analog->meaning->mq = SR_MQ_RESISTANCE;
|
analog->mq = SR_MQ_RESISTANCE;
|
||||||
analog->meaning->unit = SR_UNIT_OHM;
|
analog->unit = SR_UNIT_OHM;
|
||||||
}
|
}
|
||||||
if (info->is_hz) {
|
if (info->is_hz) {
|
||||||
analog->meaning->mq = SR_MQ_FREQUENCY;
|
analog->mq = SR_MQ_FREQUENCY;
|
||||||
analog->meaning->unit = SR_UNIT_HERTZ;
|
analog->unit = SR_UNIT_HERTZ;
|
||||||
}
|
}
|
||||||
if (info->is_farad) {
|
if (info->is_farad) {
|
||||||
analog->meaning->mq = SR_MQ_CAPACITANCE;
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
analog->meaning->unit = SR_UNIT_FARAD;
|
analog->unit = SR_UNIT_FARAD;
|
||||||
}
|
}
|
||||||
if (info->is_beep) {
|
if (info->is_beep) {
|
||||||
analog->meaning->mq = SR_MQ_CONTINUITY;
|
analog->mq = SR_MQ_CONTINUITY;
|
||||||
analog->meaning->unit = SR_UNIT_BOOLEAN;
|
analog->unit = SR_UNIT_BOOLEAN;
|
||||||
*floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
|
*floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
|
||||||
}
|
}
|
||||||
if (info->is_diode) {
|
if (info->is_diode) {
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
}
|
}
|
||||||
if (info->is_percent) {
|
if (info->is_percent) {
|
||||||
analog->meaning->mq = SR_MQ_DUTY_CYCLE;
|
analog->mq = SR_MQ_DUTY_CYCLE;
|
||||||
analog->meaning->unit = SR_UNIT_PERCENTAGE;
|
analog->unit = SR_UNIT_PERCENTAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Measurement related flags */
|
/* Measurement related flags */
|
||||||
if (info->is_ac)
|
if (info->is_ac)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AC;
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
if (info->is_dc)
|
if (info->is_dc)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
if (info->is_auto)
|
if (info->is_auto)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
|
analog->mqflags |= SR_MQFLAG_AUTORANGE;
|
||||||
if (info->is_diode)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
|
|
||||||
if (info->is_hold)
|
if (info->is_hold)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_HOLD;
|
analog->mqflags |= SR_MQFLAG_HOLD;
|
||||||
if (info->is_rel)
|
if (info->is_rel)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_RELATIVE;
|
analog->mqflags |= SR_MQFLAG_RELATIVE;
|
||||||
|
|
||||||
/* Other flags */
|
/* Other flags */
|
||||||
if (info->is_rs232)
|
if (info->is_rs232)
|
||||||
|
|
@ -337,7 +340,7 @@ SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf)
|
||||||
*
|
*
|
||||||
* @param buf Buffer containing the 14-byte protocol packet. Must not be NULL.
|
* @param buf Buffer containing the 14-byte protocol packet. Must not be NULL.
|
||||||
* @param floatval Pointer to a float variable. That variable will contain the
|
* @param floatval Pointer to a float variable. That variable will contain the
|
||||||
* result value upon parsing success. Must not be NULL.
|
* result value upon parsing success. Mut not be NULL.
|
||||||
* @param analog Pointer to a struct sr_datafeed_analog. The struct will be
|
* @param analog Pointer to a struct sr_datafeed_analog. The struct will be
|
||||||
* filled with data according to the protocol packet.
|
* filled with data according to the protocol packet.
|
||||||
* Must not be NULL.
|
* Must not be NULL.
|
||||||
|
|
@ -350,21 +353,18 @@ SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf)
|
||||||
SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
|
SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
|
||||||
struct sr_datafeed_analog *analog, void *info)
|
struct sr_datafeed_analog *analog, void *info)
|
||||||
{
|
{
|
||||||
int ret, exponent = 0;
|
int ret;
|
||||||
struct fs9721_info *info_local;
|
struct fs9721_info *info_local;
|
||||||
|
|
||||||
info_local = info;
|
info_local = (struct fs9721_info *)info;
|
||||||
|
|
||||||
if ((ret = parse_value(buf, floatval, &exponent)) != SR_OK) {
|
if ((ret = parse_value(buf, floatval)) != SR_OK) {
|
||||||
sr_dbg("Error parsing value: %d.", ret);
|
sr_err("Error parsing value: %d.", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_flags(buf, info_local);
|
parse_flags(buf, info_local);
|
||||||
handle_flags(analog, floatval, &exponent, info_local);
|
handle_flags(analog, floatval, info_local);
|
||||||
|
|
||||||
analog->encoding->digits = -exponent;
|
|
||||||
analog->spec->spec_digits = -exponent;
|
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -373,12 +373,12 @@ SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info)
|
||||||
{
|
{
|
||||||
struct fs9721_info *info_local;
|
struct fs9721_info *info_local;
|
||||||
|
|
||||||
info_local = info;
|
info_local = (struct fs9721_info *)info;
|
||||||
|
|
||||||
/* User-defined FS9721_LP3 flag 'c2c1_00' means temperature (C). */
|
/* User-defined FS9721_LP3 flag 'c2c1_00' means temperature (C). */
|
||||||
if (info_local->is_c2c1_00) {
|
if (info_local->is_c2c1_00) {
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
analog->meaning->unit = SR_UNIT_CELSIUS;
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,12 +386,12 @@ SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info)
|
||||||
{
|
{
|
||||||
struct fs9721_info *info_local;
|
struct fs9721_info *info_local;
|
||||||
|
|
||||||
info_local = info;
|
info_local = (struct fs9721_info *)info;
|
||||||
|
|
||||||
/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
|
/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
|
||||||
if (info_local->is_c2c1_01) {
|
if (info_local->is_c2c1_01) {
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
analog->meaning->unit = SR_UNIT_CELSIUS;
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,12 +399,12 @@ SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info)
|
||||||
{
|
{
|
||||||
struct fs9721_info *info_local;
|
struct fs9721_info *info_local;
|
||||||
|
|
||||||
info_local = info;
|
info_local = (struct fs9721_info *)info;
|
||||||
|
|
||||||
/* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
|
/* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
|
||||||
if (info_local->is_c2c1_10) {
|
if (info_local->is_c2c1_10) {
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
analog->meaning->unit = SR_UNIT_CELSIUS;
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -412,39 +412,17 @@ SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *i
|
||||||
{
|
{
|
||||||
struct fs9721_info *info_local;
|
struct fs9721_info *info_local;
|
||||||
|
|
||||||
info_local = info;
|
info_local = (struct fs9721_info *)info;
|
||||||
|
|
||||||
/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (F). */
|
/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (F). */
|
||||||
if (info_local->is_c2c1_01) {
|
if (info_local->is_c2c1_01) {
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
analog->meaning->unit = SR_UNIT_FAHRENHEIT;
|
analog->unit = SR_UNIT_FAHRENHEIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
|
/* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
|
||||||
if (info_local->is_c2c1_10) {
|
if (info_local->is_c2c1_10) {
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
analog->meaning->unit = SR_UNIT_CELSIUS;
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog *analog, void *info)
|
|
||||||
{
|
|
||||||
struct fs9721_info *info_local;
|
|
||||||
|
|
||||||
info_local = info;
|
|
||||||
|
|
||||||
/* User-defined FS9721_LP3 flag 'c2c1_00' means MAX. */
|
|
||||||
if (info_local->is_c2c1_00)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_MAX;
|
|
||||||
|
|
||||||
/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
|
|
||||||
if (info_local->is_c2c1_01) {
|
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
|
||||||
analog->meaning->unit = SR_UNIT_CELSIUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* User-defined FS9721_LP3 flag 'c2c1_11' means MIN. */
|
|
||||||
if (info_local->is_c2c1_11)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_MIN;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -14,22 +14,29 @@
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fortune Semiconductor FS9922-DMM3/FS9922-DMM4 protocol parser.
|
* Fortune Semiconductor FS9922-DMM3/FS9922-DMM4 protocol parser.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <libsigrok/libsigrok.h>
|
#include "libsigrok.h"
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
#define LOG_PREFIX "fs9922"
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "fs9922: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
static gboolean flags_valid(const struct fs9922_info *info)
|
static gboolean flags_valid(const struct fs9922_info *info)
|
||||||
{
|
{
|
||||||
|
|
@ -43,7 +50,7 @@ static gboolean flags_valid(const struct fs9922_info *info)
|
||||||
count += (info->is_kilo) ? 1 : 0;
|
count += (info->is_kilo) ? 1 : 0;
|
||||||
count += (info->is_mega) ? 1 : 0;
|
count += (info->is_mega) ? 1 : 0;
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
sr_dbg("More than one multiplier detected in packet.");
|
sr_err("More than one multiplier detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,26 +73,26 @@ static gboolean flags_valid(const struct fs9922_info *info)
|
||||||
count += (info->is_celsius) ? 1 : 0;
|
count += (info->is_celsius) ? 1 : 0;
|
||||||
count += (info->is_fahrenheit) ? 1 : 0;
|
count += (info->is_fahrenheit) ? 1 : 0;
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
sr_dbg("More than one measurement type detected in packet.");
|
sr_err("More than one measurement type detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Both AC and DC set? */
|
/* Both AC and DC set? */
|
||||||
if (info->is_ac && info->is_dc) {
|
if (info->is_ac && info->is_dc) {
|
||||||
sr_dbg("Both AC and DC flags detected in packet.");
|
sr_err("Both AC and DC flags detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Both Celsius and Fahrenheit set? */
|
/* Both Celsius and Fahrenheit set? */
|
||||||
if (info->is_celsius && info->is_fahrenheit) {
|
if (info->is_celsius && info->is_fahrenheit) {
|
||||||
sr_dbg("Both Celsius and Fahrenheit flags detected in packet.");
|
sr_err("Both Celsius and Fahrenheit flags detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_value(const uint8_t *buf, float *result, int *exponent)
|
static int parse_value(const uint8_t *buf, float *result)
|
||||||
{
|
{
|
||||||
int sign, intval;
|
int sign, intval;
|
||||||
float floatval;
|
float floatval;
|
||||||
|
|
@ -96,7 +103,7 @@ static int parse_value(const uint8_t *buf, float *result, int *exponent)
|
||||||
} else if (buf[0] == '-') {
|
} else if (buf[0] == '-') {
|
||||||
sign = -1;
|
sign = -1;
|
||||||
} else {
|
} else {
|
||||||
sr_dbg("Invalid sign byte: 0x%02x.", buf[0]);
|
sr_err("Invalid sign byte: 0x%02x.", buf[0]);
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,10 +118,8 @@ static int parse_value(const uint8_t *buf, float *result, int *exponent)
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
} else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
|
} else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
|
||||||
!isdigit(buf[3]) || !isdigit(buf[4])) {
|
!isdigit(buf[3]) || !isdigit(buf[4])) {
|
||||||
sr_dbg("Value contained invalid digits: %02x %02x %02x %02x ("
|
sr_err("Value contained invalid digits: %02x %02x %02x %02x ("
|
||||||
"%c %c %c %c).",
|
"%c %c %c %c).", buf[1], buf[2], buf[3], buf[4]);
|
||||||
buf[1], buf[2], buf[3], buf[4],
|
|
||||||
buf[1], buf[2], buf[3], buf[4]);
|
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
intval = 0;
|
intval = 0;
|
||||||
|
|
@ -135,17 +140,17 @@ static int parse_value(const uint8_t *buf, float *result, int *exponent)
|
||||||
* used, but '0'/'1'/'2'/'4' is actually correct.
|
* used, but '0'/'1'/'2'/'4' is actually correct.
|
||||||
*/
|
*/
|
||||||
if (buf[6] != '0' && buf[6] != '1' && buf[6] != '2' && buf[6] != '4') {
|
if (buf[6] != '0' && buf[6] != '1' && buf[6] != '2' && buf[6] != '4') {
|
||||||
sr_dbg("Invalid decimal point value: 0x%02x.", buf[6]);
|
sr_err("Invalid decimal point value: 0x%02x.", buf[6]);
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
if (buf[6] == '0')
|
if (buf[6] == '0')
|
||||||
*exponent = 0;
|
floatval /= 1;
|
||||||
else if (buf[6] == '1')
|
else if (buf[6] == '1')
|
||||||
*exponent = -3;
|
floatval /= 1000;
|
||||||
else if (buf[6] == '2')
|
else if (buf[6] == '2')
|
||||||
*exponent = -2;
|
floatval /= 100;
|
||||||
else if (buf[6] == '4')
|
else if (buf[6] == '4')
|
||||||
*exponent = -1;
|
floatval /= 10;
|
||||||
|
|
||||||
/* Apply sign. */
|
/* Apply sign. */
|
||||||
floatval *= sign;
|
floatval *= sign;
|
||||||
|
|
@ -189,7 +194,7 @@ static void parse_flags(const uint8_t *buf, struct fs9922_info *info)
|
||||||
info->is_beep = (buf[9] & (1 << 3)) != 0;
|
info->is_beep = (buf[9] & (1 << 3)) != 0;
|
||||||
info->is_diode = (buf[9] & (1 << 2)) != 0;
|
info->is_diode = (buf[9] & (1 << 2)) != 0;
|
||||||
info->is_percent = (buf[9] & (1 << 1)) != 0;
|
info->is_percent = (buf[9] & (1 << 1)) != 0;
|
||||||
info->is_z4 = (buf[9] & (1 << 0)) != 0; /* User symbol 4 */
|
info->is_z4 = (buf[8] & (1 << 0)) != 0; /* User symbol 4 */
|
||||||
|
|
||||||
/* Byte 10 */
|
/* Byte 10 */
|
||||||
info->is_volt = (buf[10] & (1 << 7)) != 0;
|
info->is_volt = (buf[10] & (1 << 7)) != 0;
|
||||||
|
|
@ -223,82 +228,79 @@ static void parse_flags(const uint8_t *buf, struct fs9922_info *info)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
|
static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
|
||||||
int *exponent, const struct fs9922_info *info)
|
const struct fs9922_info *info)
|
||||||
{
|
{
|
||||||
/* Factors */
|
/* Factors */
|
||||||
if (info->is_nano)
|
if (info->is_nano)
|
||||||
*exponent -= 9;
|
*floatval /= 1000000000;
|
||||||
if (info->is_micro)
|
if (info->is_micro)
|
||||||
*exponent -= 6;
|
*floatval /= 1000000;
|
||||||
if (info->is_milli)
|
if (info->is_milli)
|
||||||
*exponent -= 3;
|
*floatval /= 1000;
|
||||||
if (info->is_kilo)
|
if (info->is_kilo)
|
||||||
*exponent += 3;
|
*floatval *= 1000;
|
||||||
if (info->is_mega)
|
if (info->is_mega)
|
||||||
*exponent += 6;
|
*floatval *= 1000000;
|
||||||
*floatval *= powf(10, *exponent);
|
|
||||||
|
|
||||||
/* Measurement modes */
|
/* Measurement modes */
|
||||||
if (info->is_volt || info->is_diode) {
|
if (info->is_volt || info->is_diode) {
|
||||||
/* Note: In "diode mode" both is_diode and is_volt are set. */
|
/* Note: In "diode mode" both is_diode and is_volt are set. */
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
}
|
}
|
||||||
if (info->is_ampere) {
|
if (info->is_ampere) {
|
||||||
analog->meaning->mq = SR_MQ_CURRENT;
|
analog->mq = SR_MQ_CURRENT;
|
||||||
analog->meaning->unit = SR_UNIT_AMPERE;
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
}
|
}
|
||||||
if (info->is_ohm) {
|
if (info->is_ohm) {
|
||||||
analog->meaning->mq = SR_MQ_RESISTANCE;
|
analog->mq = SR_MQ_RESISTANCE;
|
||||||
analog->meaning->unit = SR_UNIT_OHM;
|
analog->unit = SR_UNIT_OHM;
|
||||||
}
|
}
|
||||||
if (info->is_hfe) {
|
if (info->is_hfe) {
|
||||||
analog->meaning->mq = SR_MQ_GAIN;
|
analog->mq = SR_MQ_GAIN;
|
||||||
analog->meaning->unit = SR_UNIT_UNITLESS;
|
analog->unit = SR_UNIT_UNITLESS;
|
||||||
}
|
}
|
||||||
if (info->is_hertz) {
|
if (info->is_hertz) {
|
||||||
analog->meaning->mq = SR_MQ_FREQUENCY;
|
analog->mq = SR_MQ_FREQUENCY;
|
||||||
analog->meaning->unit = SR_UNIT_HERTZ;
|
analog->unit = SR_UNIT_HERTZ;
|
||||||
}
|
}
|
||||||
if (info->is_farad) {
|
if (info->is_farad) {
|
||||||
analog->meaning->mq = SR_MQ_CAPACITANCE;
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
analog->meaning->unit = SR_UNIT_FARAD;
|
analog->unit = SR_UNIT_FARAD;
|
||||||
}
|
}
|
||||||
if (info->is_celsius) {
|
if (info->is_celsius) {
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
analog->meaning->unit = SR_UNIT_CELSIUS;
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
}
|
}
|
||||||
if (info->is_fahrenheit) {
|
if (info->is_fahrenheit) {
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
analog->meaning->unit = SR_UNIT_FAHRENHEIT;
|
analog->unit = SR_UNIT_FAHRENHEIT;
|
||||||
}
|
}
|
||||||
if (info->is_beep) {
|
if (info->is_beep) {
|
||||||
analog->meaning->mq = SR_MQ_CONTINUITY;
|
analog->mq = SR_MQ_CONTINUITY;
|
||||||
analog->meaning->unit = SR_UNIT_BOOLEAN;
|
analog->unit = SR_UNIT_BOOLEAN;
|
||||||
*floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
|
*floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
|
||||||
}
|
}
|
||||||
if (info->is_percent) {
|
if (info->is_percent) {
|
||||||
analog->meaning->mq = SR_MQ_DUTY_CYCLE;
|
analog->mq = SR_MQ_DUTY_CYCLE;
|
||||||
analog->meaning->unit = SR_UNIT_PERCENTAGE;
|
analog->unit = SR_UNIT_PERCENTAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Measurement related flags */
|
/* Measurement related flags */
|
||||||
if (info->is_ac)
|
if (info->is_ac)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AC;
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
if (info->is_dc)
|
if (info->is_dc)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
if (info->is_auto)
|
if (info->is_auto)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
|
analog->mqflags |= SR_MQFLAG_AUTORANGE;
|
||||||
if (info->is_diode)
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
|
|
||||||
if (info->is_hold)
|
if (info->is_hold)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_HOLD;
|
analog->mqflags |= SR_MQFLAG_HOLD;
|
||||||
if (info->is_max)
|
if (info->is_max)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_MAX;
|
analog->mqflags |= SR_MQFLAG_MAX;
|
||||||
if (info->is_min)
|
if (info->is_min)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_MIN;
|
analog->mqflags |= SR_MQFLAG_MIN;
|
||||||
if (info->is_rel)
|
if (info->is_rel)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_RELATIVE;
|
analog->mqflags |= SR_MQFLAG_RELATIVE;
|
||||||
|
|
||||||
/* Other flags */
|
/* Other flags */
|
||||||
if (info->is_apo)
|
if (info->is_apo)
|
||||||
|
|
@ -356,35 +358,18 @@ SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf)
|
||||||
SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
|
SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
|
||||||
struct sr_datafeed_analog *analog, void *info)
|
struct sr_datafeed_analog *analog, void *info)
|
||||||
{
|
{
|
||||||
int ret, exponent = 0;
|
int ret;
|
||||||
struct fs9922_info *info_local;
|
struct fs9922_info *info_local;
|
||||||
|
|
||||||
info_local = info;
|
info_local = (struct fs9922_info *)info;
|
||||||
|
|
||||||
if ((ret = parse_value(buf, floatval, &exponent)) != SR_OK) {
|
if ((ret = parse_value(buf, floatval)) != SR_OK) {
|
||||||
sr_dbg("Error parsing value: %d.", ret);
|
sr_err("Error parsing value: %d.", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_flags(buf, info_local);
|
parse_flags(buf, info_local);
|
||||||
handle_flags(analog, floatval, &exponent, info_local);
|
handle_flags(analog, floatval, info_local);
|
||||||
|
|
||||||
analog->encoding->digits = -exponent;
|
|
||||||
analog->spec->spec_digits = -exponent;
|
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog *analog, void *info)
|
|
||||||
{
|
|
||||||
struct fs9922_info *info_local;
|
|
||||||
|
|
||||||
info_local = info;
|
|
||||||
|
|
||||||
/* User-defined z1 flag means "diode mode". */
|
|
||||||
if (info_local->is_z1) {
|
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,337 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Metex 14-bytes ASCII protocol parser.
|
||||||
|
*
|
||||||
|
* This should work for various multimeters which use this kind of protocol,
|
||||||
|
* even though there is some variation in which modes each DMM supports.
|
||||||
|
*
|
||||||
|
* It does _not_ work for all Metex DMMs, some use a quite different protocol.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "metex14: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
static int parse_value(const uint8_t *buf, float *result)
|
||||||
|
{
|
||||||
|
int i, sign, intval = 0, factor, decimal_point = 0, is_ol;
|
||||||
|
float floatval;
|
||||||
|
uint8_t digit;
|
||||||
|
|
||||||
|
/* Byte 3: Sign (' ' or '-') */
|
||||||
|
if (buf[3] == ' ') {
|
||||||
|
sign = 1;
|
||||||
|
} else if (buf[3] == '-') {
|
||||||
|
sign = -1;
|
||||||
|
} else {
|
||||||
|
sr_err("Invalid sign byte: 0x%02x.", buf[3]);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bytes 5-7: Over limit (various forms) */
|
||||||
|
is_ol = 0;
|
||||||
|
is_ol += (!strncmp((char *)&buf[5], ".OL", 3)) ? 1 : 0;
|
||||||
|
is_ol += (!strncmp((char *)&buf[5], "O.L", 3)) ? 1 : 0;
|
||||||
|
is_ol += (!strncmp((char *)&buf[5], "OL.", 3)) ? 1 : 0;
|
||||||
|
is_ol += (!strncmp((char *)&buf[5], " OL", 3)) ? 1 : 0;
|
||||||
|
if (is_ol != 0) {
|
||||||
|
sr_spew("Over limit.");
|
||||||
|
*result = INFINITY;
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bytes 4-8: Value (up to 4 digits) and decimal point */
|
||||||
|
factor = 1000;
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
digit = buf[4 + i];
|
||||||
|
/* Convert spaces to '0', so that we can parse them. */
|
||||||
|
if (digit == ' ')
|
||||||
|
digit = '0';
|
||||||
|
if (digit == '.') {
|
||||||
|
decimal_point = i;
|
||||||
|
} else if (isdigit(digit)) {
|
||||||
|
intval += (digit - '0') * factor;
|
||||||
|
factor /= 10;
|
||||||
|
} else {
|
||||||
|
sr_err("Invalid digit byte: 0x%02x.", digit);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floatval = (float)intval;
|
||||||
|
|
||||||
|
/* Decimal point position */
|
||||||
|
if (decimal_point == 0 || decimal_point == 4) {
|
||||||
|
/* TODO: Doesn't happen? */
|
||||||
|
} else if (decimal_point == 1) {
|
||||||
|
floatval /= 1000;
|
||||||
|
} else if (decimal_point == 2) {
|
||||||
|
floatval /= 100;
|
||||||
|
} else if (decimal_point == 3) {
|
||||||
|
floatval /= 10;
|
||||||
|
} else {
|
||||||
|
sr_err("Invalid decimal point position: %d.", decimal_point);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply sign. */
|
||||||
|
floatval *= sign;
|
||||||
|
|
||||||
|
sr_spew("The display value is %f.", floatval);
|
||||||
|
|
||||||
|
*result = floatval;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_flags(const char *buf, struct metex14_info *info)
|
||||||
|
{
|
||||||
|
/* Bytes 0-1: Measurement mode */
|
||||||
|
/* Note: Protocol doesn't distinguish "resistance" from "beep" mode. */
|
||||||
|
info->is_ac = !strncmp(buf, "AC", 2);
|
||||||
|
info->is_dc = !strncmp(buf, "DC", 2);
|
||||||
|
info->is_resistance = !strncmp(buf, "OH", 2);
|
||||||
|
info->is_capacity = !strncmp(buf, "CA", 2);
|
||||||
|
info->is_temperature = !strncmp(buf, "TE", 2);
|
||||||
|
info->is_diode = !strncmp(buf, "DI", 2);
|
||||||
|
info->is_frequency = !strncmp(buf, "FR", 2);
|
||||||
|
info->is_gain = !strncmp(buf, "DB", 2);
|
||||||
|
info->is_hfe = !strncmp(buf, "HF", 2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: "DB" shows the logarithmic ratio of input voltage to a
|
||||||
|
* pre-stored (user-changeable) value in the DMM.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (info->is_dc || info->is_ac)
|
||||||
|
info->is_volt = TRUE;
|
||||||
|
|
||||||
|
/* Byte 2: Always space (0x20). */
|
||||||
|
|
||||||
|
/* Bytes 3-8: See parse_value(). */
|
||||||
|
|
||||||
|
/* Bytes 9-12: Unit */
|
||||||
|
if (!strncmp(buf + 9, " A", 4))
|
||||||
|
info->is_ampere = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " mA", 4))
|
||||||
|
info->is_milli = info->is_ampere = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " uA", 4))
|
||||||
|
info->is_micro = info->is_ampere = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " V", 4))
|
||||||
|
info->is_volt = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " mV", 4))
|
||||||
|
info->is_milli = info->is_volt = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " Ohm", 4))
|
||||||
|
info->is_ohm = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, "KOhm", 4))
|
||||||
|
info->is_kilo = info->is_ohm = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, "MOhm", 4))
|
||||||
|
info->is_mega = info->is_ohm = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " nF", 4))
|
||||||
|
info->is_nano = info->is_farad = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " uF", 4))
|
||||||
|
info->is_micro = info->is_farad = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " KHz", 4))
|
||||||
|
info->is_kilo = info->is_hertz = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " C", 4))
|
||||||
|
info->is_celsius = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " DB", 4))
|
||||||
|
info->is_decibel = TRUE;
|
||||||
|
else if (!strncmp(buf + 9, " ", 4))
|
||||||
|
info->is_unitless = TRUE;
|
||||||
|
|
||||||
|
/* Byte 13: Always '\r' (carriage return, 0x0d, 13) */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
|
||||||
|
const struct metex14_info *info)
|
||||||
|
{
|
||||||
|
/* Factors */
|
||||||
|
if (info->is_nano)
|
||||||
|
*floatval /= 1000000000;
|
||||||
|
if (info->is_micro)
|
||||||
|
*floatval /= 1000000;
|
||||||
|
if (info->is_milli)
|
||||||
|
*floatval /= 1000;
|
||||||
|
if (info->is_kilo)
|
||||||
|
*floatval *= 1000;
|
||||||
|
if (info->is_mega)
|
||||||
|
*floatval *= 1000000;
|
||||||
|
|
||||||
|
/* Measurement modes */
|
||||||
|
if (info->is_volt) {
|
||||||
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
|
analog->unit = SR_UNIT_VOLT;
|
||||||
|
}
|
||||||
|
if (info->is_ampere) {
|
||||||
|
analog->mq = SR_MQ_CURRENT;
|
||||||
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
|
}
|
||||||
|
if (info->is_ohm) {
|
||||||
|
analog->mq = SR_MQ_RESISTANCE;
|
||||||
|
analog->unit = SR_UNIT_OHM;
|
||||||
|
}
|
||||||
|
if (info->is_hertz) {
|
||||||
|
analog->mq = SR_MQ_FREQUENCY;
|
||||||
|
analog->unit = SR_UNIT_HERTZ;
|
||||||
|
}
|
||||||
|
if (info->is_farad) {
|
||||||
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
|
analog->unit = SR_UNIT_FARAD;
|
||||||
|
}
|
||||||
|
if (info->is_celsius) {
|
||||||
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
|
}
|
||||||
|
if (info->is_diode) {
|
||||||
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
|
analog->unit = SR_UNIT_VOLT;
|
||||||
|
}
|
||||||
|
if (info->is_gain) {
|
||||||
|
analog->mq = SR_MQ_GAIN;
|
||||||
|
analog->unit = SR_UNIT_DECIBEL_VOLT;
|
||||||
|
}
|
||||||
|
if (info->is_hfe) {
|
||||||
|
analog->mq = SR_MQ_GAIN;
|
||||||
|
analog->unit = SR_UNIT_UNITLESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Measurement related flags */
|
||||||
|
if (info->is_ac)
|
||||||
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
|
if (info->is_dc)
|
||||||
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean flags_valid(const struct metex14_info *info)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
|
||||||
|
/* Does the packet have more than one multiplier? */
|
||||||
|
count = 0;
|
||||||
|
count += (info->is_nano) ? 1 : 0;
|
||||||
|
count += (info->is_micro) ? 1 : 0;
|
||||||
|
count += (info->is_milli) ? 1 : 0;
|
||||||
|
count += (info->is_kilo) ? 1 : 0;
|
||||||
|
count += (info->is_mega) ? 1 : 0;
|
||||||
|
if (count > 1) {
|
||||||
|
sr_err("More than one multiplier detected in packet.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Does the packet "measure" more than one type of value? */
|
||||||
|
count = 0;
|
||||||
|
count += (info->is_ac) ? 1 : 0;
|
||||||
|
count += (info->is_dc) ? 1 : 0;
|
||||||
|
count += (info->is_resistance) ? 1 : 0;
|
||||||
|
count += (info->is_capacity) ? 1 : 0;
|
||||||
|
count += (info->is_temperature) ? 1 : 0;
|
||||||
|
count += (info->is_diode) ? 1 : 0;
|
||||||
|
count += (info->is_frequency) ? 1 : 0;
|
||||||
|
if (count > 1) {
|
||||||
|
sr_err("More than one measurement type detected in packet.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Both AC and DC set? */
|
||||||
|
if (info->is_ac && info->is_dc) {
|
||||||
|
sr_err("Both AC and DC flags detected in packet.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial)
|
||||||
|
{
|
||||||
|
const uint8_t wbuf = 'D';
|
||||||
|
|
||||||
|
sr_spew("Requesting DMM packet.");
|
||||||
|
|
||||||
|
return (serial_write(serial, &wbuf, 1) == 1) ? SR_OK : SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
struct metex14_info info;
|
||||||
|
|
||||||
|
memset(&info, 0x00, sizeof(struct metex14_info));
|
||||||
|
parse_flags((const char *)buf, &info);
|
||||||
|
|
||||||
|
if (!flags_valid(&info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (buf[13] != '\r')
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a protocol packet.
|
||||||
|
*
|
||||||
|
* @param buf Buffer containing the protocol packet. Must not be NULL.
|
||||||
|
* @param floatval Pointer to a float variable. That variable will be modified
|
||||||
|
* in-place depending on the protocol packet. Must not be NULL.
|
||||||
|
* @param analog Pointer to a struct sr_datafeed_analog. The struct will be
|
||||||
|
* filled with data according to the protocol packet.
|
||||||
|
* Must not be NULL.
|
||||||
|
* @param info Pointer to a struct metex14_info. The struct will be filled
|
||||||
|
* with data according to the protocol packet. Must not be NULL.
|
||||||
|
*
|
||||||
|
* @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
|
||||||
|
* 'analog' variable contents are undefined and should not be used.
|
||||||
|
*/
|
||||||
|
SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
|
||||||
|
struct sr_datafeed_analog *analog, void *info)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct metex14_info *info_local;
|
||||||
|
|
||||||
|
info_local = (struct metex14_info *)info;
|
||||||
|
|
||||||
|
/* Don't print byte 13. That one contains the carriage return. */
|
||||||
|
sr_dbg("DMM packet: \"%.13s\"", buf);
|
||||||
|
|
||||||
|
if ((ret = parse_value(buf, floatval)) != SR_OK) {
|
||||||
|
sr_err("Error parsing value: %d.", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(info_local, 0x00, sizeof(struct metex14_info));
|
||||||
|
parse_flags((const char *)buf, info_local);
|
||||||
|
handle_flags(analog, floatval, info_local);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
@ -28,15 +28,21 @@
|
||||||
* and protocol is used on any other device.
|
* and protocol is used on any other device.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <libsigrok/libsigrok.h>
|
#include "libsigrok.h"
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
#define LOG_PREFIX "rs9lcd"
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "rs9lcd: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
/* Byte 1 of the packet, and the modes it represents */
|
/* Byte 1 of the packet, and the modes it represents */
|
||||||
#define IND1_HZ (1 << 7)
|
#define IND1_HZ (1 << 7)
|
||||||
|
|
@ -168,7 +174,7 @@ static gboolean selection_good(const struct rs9lcd_packet *rs_packet)
|
||||||
count += (rs_packet->indicatrix2 & IND2_MICRO) ? 1 : 0;
|
count += (rs_packet->indicatrix2 & IND2_MICRO) ? 1 : 0;
|
||||||
count += (rs_packet->indicatrix2 & IND2_NANO) ? 1 : 0;
|
count += (rs_packet->indicatrix2 & IND2_NANO) ? 1 : 0;
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
sr_dbg("More than one multiplier detected in packet.");
|
sr_err("More than one multiplier detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +190,7 @@ static gboolean selection_good(const struct rs9lcd_packet *rs_packet)
|
||||||
count += (rs_packet->indicatrix2 & IND2_DUTY) ? 1 : 0;
|
count += (rs_packet->indicatrix2 & IND2_DUTY) ? 1 : 0;
|
||||||
count += (rs_packet->indicatrix2 & IND2_HFE) ? 1 : 0;
|
count += (rs_packet->indicatrix2 & IND2_HFE) ? 1 : 0;
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
sr_dbg("More than one measurement type detected in packet.");
|
sr_err("More than one measurement type detected in packet.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,21 +255,18 @@ static uint8_t decode_digit(uint8_t raw_digit)
|
||||||
case LCD_9:
|
case LCD_9:
|
||||||
return 9;
|
return 9;
|
||||||
default:
|
default:
|
||||||
sr_dbg("Invalid digit byte: 0x%02x.", raw_digit);
|
sr_err("Invalid digit byte: 0x%02x.", raw_digit);
|
||||||
return 0xff;
|
return 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static double lcd_to_double(const struct rs9lcd_packet *rs_packet, int type,
|
static double lcd_to_double(const struct rs9lcd_packet *rs_packet, int type)
|
||||||
int *exponent)
|
|
||||||
{
|
{
|
||||||
double rawval = 0;
|
double rawval = 0, multiplier = 1;
|
||||||
uint8_t digit, raw_digit;
|
uint8_t digit, raw_digit;
|
||||||
gboolean dp_reached = FALSE;
|
gboolean dp_reached = FALSE;
|
||||||
int i, end;
|
int i, end;
|
||||||
|
|
||||||
*exponent = 0;
|
|
||||||
|
|
||||||
/* end = 1: Don't parse last digit. end = 0: Parse all digits. */
|
/* end = 1: Don't parse last digit. end = 0: Parse all digits. */
|
||||||
end = (type == READ_TEMP) ? 1 : 0;
|
end = (type == READ_TEMP) ? 1 : 0;
|
||||||
|
|
||||||
|
|
@ -282,25 +285,26 @@ static double lcd_to_double(const struct rs9lcd_packet *rs_packet, int type,
|
||||||
if ((i < 3) && (raw_digit & DP_MASK))
|
if ((i < 3) && (raw_digit & DP_MASK))
|
||||||
dp_reached = TRUE;
|
dp_reached = TRUE;
|
||||||
if (dp_reached)
|
if (dp_reached)
|
||||||
*exponent -= 1;
|
multiplier /= 10;
|
||||||
rawval = rawval * 10 + digit;
|
rawval = rawval * 10 + digit;
|
||||||
}
|
}
|
||||||
|
rawval *= multiplier;
|
||||||
if (rs_packet->info & INFO_NEG)
|
if (rs_packet->info & INFO_NEG)
|
||||||
rawval *= -1;
|
rawval *= -1;
|
||||||
|
|
||||||
/* See if we need to multiply our raw value by anything. */
|
/* See if we need to multiply our raw value by anything. */
|
||||||
if (rs_packet->indicatrix2 & IND2_NANO)
|
if (rs_packet->indicatrix1 & IND2_NANO)
|
||||||
*exponent -= 9;
|
rawval *= 1E-9;
|
||||||
else if (rs_packet->indicatrix2 & IND2_MICRO)
|
else if (rs_packet->indicatrix2 & IND2_MICRO)
|
||||||
*exponent -= 6;
|
rawval *= 1E-6;
|
||||||
else if (rs_packet->indicatrix1 & IND1_MILI)
|
else if (rs_packet->indicatrix1 & IND1_MILI)
|
||||||
*exponent -= 3;
|
rawval *= 1E-3;
|
||||||
else if (rs_packet->indicatrix1 & IND1_KILO)
|
else if (rs_packet->indicatrix1 & IND1_KILO)
|
||||||
*exponent += 3;
|
rawval *= 1E3;
|
||||||
else if (rs_packet->indicatrix1 & IND1_MEGA)
|
else if (rs_packet->indicatrix1 & IND1_MEGA)
|
||||||
*exponent += 6;
|
rawval *= 1E6;
|
||||||
|
|
||||||
return rawval * powf(10, *exponent);
|
return rawval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean is_celsius(const struct rs9lcd_packet *rs_packet)
|
static gboolean is_celsius(const struct rs9lcd_packet *rs_packet)
|
||||||
|
|
@ -323,123 +327,118 @@ SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval,
|
||||||
struct sr_datafeed_analog *analog, void *info)
|
struct sr_datafeed_analog *analog, void *info)
|
||||||
{
|
{
|
||||||
const struct rs9lcd_packet *rs_packet = (void *)buf;
|
const struct rs9lcd_packet *rs_packet = (void *)buf;
|
||||||
int exponent;
|
|
||||||
double rawval;
|
double rawval;
|
||||||
|
|
||||||
(void)info;
|
(void)info;
|
||||||
|
|
||||||
rawval = lcd_to_double(rs_packet, READ_ALL, &exponent);
|
rawval = lcd_to_double(rs_packet, READ_ALL);
|
||||||
|
|
||||||
switch (rs_packet->mode) {
|
switch (rs_packet->mode) {
|
||||||
case MODE_DC_V:
|
case MODE_DC_V:
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
break;
|
break;
|
||||||
case MODE_AC_V:
|
case MODE_AC_V:
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AC;
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
break;
|
break;
|
||||||
case MODE_DC_UA: /* Fall through */
|
case MODE_DC_UA: /* Fall through */
|
||||||
case MODE_DC_MA: /* Fall through */
|
case MODE_DC_MA: /* Fall through */
|
||||||
case MODE_DC_A:
|
case MODE_DC_A:
|
||||||
analog->meaning->mq = SR_MQ_CURRENT;
|
analog->mq = SR_MQ_CURRENT;
|
||||||
analog->meaning->unit = SR_UNIT_AMPERE;
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
break;
|
break;
|
||||||
case MODE_AC_UA: /* Fall through */
|
case MODE_AC_UA: /* Fall through */
|
||||||
case MODE_AC_MA: /* Fall through */
|
case MODE_AC_MA: /* Fall through */
|
||||||
case MODE_AC_A:
|
case MODE_AC_A:
|
||||||
analog->meaning->mq = SR_MQ_CURRENT;
|
analog->mq = SR_MQ_CURRENT;
|
||||||
analog->meaning->unit = SR_UNIT_AMPERE;
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AC;
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
break;
|
break;
|
||||||
case MODE_OHM:
|
case MODE_OHM:
|
||||||
analog->meaning->mq = SR_MQ_RESISTANCE;
|
analog->mq = SR_MQ_RESISTANCE;
|
||||||
analog->meaning->unit = SR_UNIT_OHM;
|
analog->unit = SR_UNIT_OHM;
|
||||||
break;
|
break;
|
||||||
case MODE_FARAD:
|
case MODE_FARAD:
|
||||||
analog->meaning->mq = SR_MQ_CAPACITANCE;
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
analog->meaning->unit = SR_UNIT_FARAD;
|
analog->unit = SR_UNIT_FARAD;
|
||||||
break;
|
break;
|
||||||
case MODE_CONT:
|
case MODE_CONT:
|
||||||
analog->meaning->mq = SR_MQ_CONTINUITY;
|
analog->mq = SR_MQ_CONTINUITY;
|
||||||
analog->meaning->unit = SR_UNIT_BOOLEAN;
|
analog->unit = SR_UNIT_BOOLEAN;
|
||||||
rawval = is_shortcirc(rs_packet);
|
rawval = is_shortcirc(rs_packet);
|
||||||
break;
|
break;
|
||||||
case MODE_DIODE:
|
case MODE_DIODE:
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
|
||||||
break;
|
break;
|
||||||
case MODE_HZ: /* Fall through */
|
case MODE_HZ: /* Fall through */
|
||||||
case MODE_VOLT_HZ: /* Fall through */
|
case MODE_VOLT_HZ: /* Fall through */
|
||||||
case MODE_AMP_HZ:
|
case MODE_AMP_HZ:
|
||||||
analog->meaning->mq = SR_MQ_FREQUENCY;
|
analog->mq = SR_MQ_FREQUENCY;
|
||||||
analog->meaning->unit = SR_UNIT_HERTZ;
|
analog->unit = SR_UNIT_HERTZ;
|
||||||
break;
|
break;
|
||||||
case MODE_LOGIC:
|
case MODE_LOGIC:
|
||||||
/*
|
/*
|
||||||
* No matter whether or not we have an actual voltage reading,
|
* No matter whether or not we have an actual voltage reading,
|
||||||
* we are measuring voltage, so we set our MQ as VOLTAGE.
|
* we are measuring voltage, so we set our MQ as VOLTAGE.
|
||||||
*/
|
*/
|
||||||
analog->meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
if (!isnan(rawval)) {
|
if (!isnan(rawval)) {
|
||||||
/* We have an actual voltage. */
|
/* We have an actual voltage. */
|
||||||
analog->meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
} else {
|
} else {
|
||||||
/* We have either HI or LOW. */
|
/* We have either HI or LOW. */
|
||||||
analog->meaning->unit = SR_UNIT_BOOLEAN;
|
analog->unit = SR_UNIT_BOOLEAN;
|
||||||
rawval = is_logic_high(rs_packet);
|
rawval = is_logic_high(rs_packet);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MODE_HFE:
|
case MODE_HFE:
|
||||||
analog->meaning->mq = SR_MQ_GAIN;
|
analog->mq = SR_MQ_GAIN;
|
||||||
analog->meaning->unit = SR_UNIT_UNITLESS;
|
analog->unit = SR_UNIT_UNITLESS;
|
||||||
break;
|
break;
|
||||||
case MODE_DUTY: /* Fall through */
|
case MODE_DUTY: /* Fall through */
|
||||||
case MODE_VOLT_DUTY: /* Fall through */
|
case MODE_VOLT_DUTY: /* Fall through */
|
||||||
case MODE_AMP_DUTY:
|
case MODE_AMP_DUTY:
|
||||||
analog->meaning->mq = SR_MQ_DUTY_CYCLE;
|
analog->mq = SR_MQ_DUTY_CYCLE;
|
||||||
analog->meaning->unit = SR_UNIT_PERCENTAGE;
|
analog->unit = SR_UNIT_PERCENTAGE;
|
||||||
break;
|
break;
|
||||||
case MODE_WIDTH: /* Fall through */
|
case MODE_WIDTH: /* Fall through */
|
||||||
case MODE_VOLT_WIDTH: /* Fall through */
|
case MODE_VOLT_WIDTH: /* Fall through */
|
||||||
case MODE_AMP_WIDTH:
|
case MODE_AMP_WIDTH:
|
||||||
analog->meaning->mq = SR_MQ_PULSE_WIDTH;
|
analog->mq = SR_MQ_PULSE_WIDTH;
|
||||||
analog->meaning->unit = SR_UNIT_SECOND;
|
analog->unit = SR_UNIT_SECOND;
|
||||||
break;
|
break;
|
||||||
case MODE_TEMP:
|
case MODE_TEMP:
|
||||||
analog->meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
/* We need to reparse. */
|
/* We need to reparse. */
|
||||||
rawval = lcd_to_double(rs_packet, READ_TEMP, &exponent);
|
rawval = lcd_to_double(rs_packet, READ_TEMP);
|
||||||
analog->meaning->unit = is_celsius(rs_packet) ?
|
analog->unit = is_celsius(rs_packet) ?
|
||||||
SR_UNIT_CELSIUS : SR_UNIT_FAHRENHEIT;
|
SR_UNIT_CELSIUS : SR_UNIT_FAHRENHEIT;
|
||||||
break;
|
break;
|
||||||
case MODE_DBM:
|
case MODE_DBM:
|
||||||
analog->meaning->mq = SR_MQ_POWER;
|
analog->mq = SR_MQ_POWER;
|
||||||
analog->meaning->unit = SR_UNIT_DECIBEL_MW;
|
analog->unit = SR_UNIT_DECIBEL_MW;
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AC;
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sr_dbg("Unknown mode: %d.", rs_packet->mode);
|
sr_err("Unknown mode: %d.", rs_packet->mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rs_packet->info & INFO_HOLD)
|
if (rs_packet->info & INFO_HOLD)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_HOLD;
|
analog->mqflags |= SR_MQFLAG_HOLD;
|
||||||
if (rs_packet->digit4 & DIG4_MAX)
|
if (rs_packet->digit4 & DIG4_MAX)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_MAX;
|
analog->mqflags |= SR_MQFLAG_MAX;
|
||||||
if (rs_packet->indicatrix2 & IND2_MIN)
|
if (rs_packet->indicatrix2 & IND2_MIN)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_MIN;
|
analog->mqflags |= SR_MQFLAG_MIN;
|
||||||
if (rs_packet->info & INFO_AUTO)
|
if (rs_packet->info & INFO_AUTO)
|
||||||
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
|
analog->mqflags |= SR_MQFLAG_AUTORANGE;
|
||||||
|
|
||||||
*floatval = rawval;
|
*floatval = rawval;
|
||||||
|
|
||||||
analog->encoding->digits = -exponent;
|
|
||||||
analog->spec->spec_digits = -exponent;
|
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -21,19 +21,23 @@
|
||||||
* Helper functions for the Cypress EZ-USB / FX2 series chips.
|
* Helper functions for the Cypress EZ-USB / FX2 series chips.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gstdio.h>
|
#include <glib/gstdio.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <libsigrok/libsigrok.h>
|
#include "libsigrok.h"
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
#define LOG_PREFIX "ezusb"
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "ezusb: "
|
||||||
#define FW_CHUNKSIZE (4 * 1024)
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear)
|
SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear)
|
||||||
{
|
{
|
||||||
|
|
@ -52,51 +56,46 @@ SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SR_PRIV int ezusb_install_firmware(struct sr_context *ctx,
|
SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl,
|
||||||
libusb_device_handle *hdl,
|
const char *filename)
|
||||||
const char *name)
|
|
||||||
{
|
{
|
||||||
unsigned char *firmware;
|
FILE *fw;
|
||||||
size_t length, offset, chunksize;
|
int offset, chunksize, ret, result;
|
||||||
int ret, result;
|
unsigned char buf[4096];
|
||||||
|
|
||||||
/* Max size is 64 kiB since the value field of the setup packet,
|
sr_info("Uploading firmware at %s", filename);
|
||||||
* which holds the firmware offset, is only 16 bit wide.
|
if ((fw = g_fopen(filename, "rb")) == NULL) {
|
||||||
*/
|
sr_err("Unable to open firmware file %s for reading: %s",
|
||||||
firmware = sr_resource_load(ctx, SR_RESOURCE_FIRMWARE,
|
filename, strerror(errno));
|
||||||
name, &length, 1 << 16);
|
|
||||||
if (!firmware)
|
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
|
}
|
||||||
sr_info("Uploading firmware '%s'.", name);
|
|
||||||
|
|
||||||
result = SR_OK;
|
result = SR_OK;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
while (offset < length) {
|
while (1) {
|
||||||
chunksize = MIN(length - offset, FW_CHUNKSIZE);
|
chunksize = fread(buf, 1, 4096, fw);
|
||||||
|
if (chunksize == 0)
|
||||||
|
break;
|
||||||
ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR |
|
ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR |
|
||||||
LIBUSB_ENDPOINT_OUT, 0xa0, offset,
|
LIBUSB_ENDPOINT_OUT, 0xa0, offset,
|
||||||
0x0000, firmware + offset,
|
0x0000, buf, chunksize, 100);
|
||||||
chunksize, 100);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
sr_err("Unable to send firmware to device: %s.",
|
sr_err("Unable to send firmware to device: %s.",
|
||||||
libusb_error_name(ret));
|
libusb_error_name(ret));
|
||||||
g_free(firmware);
|
result = SR_ERR;
|
||||||
return SR_ERR;
|
break;
|
||||||
}
|
}
|
||||||
sr_info("Uploaded %zu bytes.", chunksize);
|
sr_info("Uploaded %d bytes", chunksize);
|
||||||
offset += chunksize;
|
offset += chunksize;
|
||||||
}
|
}
|
||||||
g_free(firmware);
|
fclose(fw);
|
||||||
|
sr_info("Firmware upload done");
|
||||||
sr_info("Firmware upload done.");
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
SR_PRIV int ezusb_upload_firmware(struct sr_context *ctx, libusb_device *dev,
|
SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
|
||||||
int configuration, const char *name)
|
const char *filename)
|
||||||
{
|
{
|
||||||
struct libusb_device_handle *hdl;
|
struct libusb_device_handle *hdl;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -110,7 +109,7 @@ SR_PRIV int ezusb_upload_firmware(struct sr_context *ctx, libusb_device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The libusb Darwin backend is broken: it can report a kernel driver being
|
* The libusbx darwin backend is broken: it can report a kernel driver being
|
||||||
* active, but detaching it always returns an error.
|
* active, but detaching it always returns an error.
|
||||||
*/
|
*/
|
||||||
#if !defined(__APPLE__)
|
#if !defined(__APPLE__)
|
||||||
|
|
@ -132,7 +131,7 @@ SR_PRIV int ezusb_upload_firmware(struct sr_context *ctx, libusb_device *dev,
|
||||||
if ((ezusb_reset(hdl, 1)) < 0)
|
if ((ezusb_reset(hdl, 1)) < 0)
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
|
|
||||||
if (ezusb_install_firmware(ctx, hdl, name) < 0)
|
if (ezusb_install_firmware(hdl, filename) < 0)
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
|
|
||||||
if ((ezusb_reset(hdl, 0)) < 0)
|
if ((ezusb_reset(hdl, 0)) < 0)
|
||||||
|
|
@ -0,0 +1,879 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
* Copyright (C) 2010-2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <glob.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "serial: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
// FIXME: Must be moved, or rather passed as function argument.
|
||||||
|
#ifdef _WIN32
|
||||||
|
static HANDLE hdl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the specified serial port.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param flags Flags to use when opening the serial port. Possible flags
|
||||||
|
* include SERIAL_RDWR, SERIAL_RDONLY, SERIAL_NONBLOCK.
|
||||||
|
*
|
||||||
|
* If the serial structure contains a serialcomm string, it will be
|
||||||
|
* passed to serial_set_paramstr() after the port is opened.
|
||||||
|
*
|
||||||
|
* @return SR_OK on success, SR_ERR on failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags)
|
||||||
|
{
|
||||||
|
int flags_local = 0;
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD desired_access = 0, flags_and_attributes = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!serial) {
|
||||||
|
sr_dbg("Invalid serial port.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Opening serial port '%s' (flags %d).", serial->port, flags);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Map 'flags' to the OS-specific settings. */
|
||||||
|
desired_access |= GENERIC_READ;
|
||||||
|
flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
|
||||||
|
if (flags & SERIAL_RDWR)
|
||||||
|
desired_access |= GENERIC_WRITE;
|
||||||
|
if (flags & SERIAL_NONBLOCK)
|
||||||
|
flags_and_attributes |= FILE_FLAG_OVERLAPPED;
|
||||||
|
|
||||||
|
hdl = CreateFile(serial->port, desired_access, 0, 0,
|
||||||
|
OPEN_EXISTING, flags_and_attributes, 0);
|
||||||
|
if (hdl == INVALID_HANDLE_VALUE) {
|
||||||
|
sr_err("Error opening serial port '%s'.", serial->port);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Map 'flags' to the OS-specific settings. */
|
||||||
|
if (flags & SERIAL_RDWR)
|
||||||
|
flags_local |= O_RDWR;
|
||||||
|
if (flags & SERIAL_RDONLY)
|
||||||
|
flags_local |= O_RDONLY;
|
||||||
|
if (flags & SERIAL_NONBLOCK)
|
||||||
|
flags_local |= O_NONBLOCK;
|
||||||
|
|
||||||
|
if ((serial->fd = open(serial->port, flags_local)) < 0) {
|
||||||
|
sr_err("Error opening serial port '%s': %s.", serial->port,
|
||||||
|
strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Opened serial port '%s' (fd %d).", serial->port, serial->fd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (serial->serialcomm)
|
||||||
|
return serial_set_paramstr(serial, serial->serialcomm);
|
||||||
|
else
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the specified serial port.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
*
|
||||||
|
* @return SR_OK on success, SR_ERR on failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int serial_close(struct sr_serial_dev_inst *serial)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!serial) {
|
||||||
|
sr_dbg("Invalid serial port.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial->fd == -1) {
|
||||||
|
sr_dbg("Cannot close unopened serial port %s (fd %d).",
|
||||||
|
serial->port, serial->fd);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Closing serial port %s (fd %d).", serial->port, serial->fd);
|
||||||
|
ret = SR_OK;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Returns non-zero upon success, 0 upon failure. */
|
||||||
|
if (CloseHandle(hdl) == 0)
|
||||||
|
ret = SR_ERR;
|
||||||
|
#else
|
||||||
|
/* Returns 0 upon success, -1 upon failure. */
|
||||||
|
if (close(serial->fd) < 0) {
|
||||||
|
sr_err("Error closing serial port: %s (fd %d).", strerror(errno),
|
||||||
|
serial->fd);
|
||||||
|
ret = SR_ERR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
serial->fd = -1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush serial port buffers.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
*
|
||||||
|
* @return SR_OK on success, SR_ERR on failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!serial) {
|
||||||
|
sr_dbg("Invalid serial port.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial->fd == -1) {
|
||||||
|
sr_dbg("Cannot flush unopened serial port %s (fd %d).",
|
||||||
|
serial->port, serial->fd);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Flushing serial port %s (fd %d).", serial->port, serial->fd);
|
||||||
|
ret = SR_OK;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Returns non-zero upon success, 0 upon failure. */
|
||||||
|
if (PurgeComm(hdl, PURGE_RXCLEAR | PURGE_TXCLEAR) == 0) {
|
||||||
|
sr_err("Error flushing serial port: %s.", strerror(errno));
|
||||||
|
ret = SR_ERR;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Returns 0 upon success, -1 upon failure. */
|
||||||
|
if (tcflush(serial->fd, TCIOFLUSH) < 0) {
|
||||||
|
sr_err("Error flushing serial port: %s.", strerror(errno));
|
||||||
|
ret = SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a number of bytes to the specified serial port.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param buf Buffer containing the bytes to write.
|
||||||
|
* @param count Number of bytes to write.
|
||||||
|
*
|
||||||
|
* @return The number of bytes written, or -1 upon failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
|
||||||
|
const void *buf, size_t count)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (!serial) {
|
||||||
|
sr_dbg("Invalid serial port.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial->fd == -1) {
|
||||||
|
sr_dbg("Cannot use unopened serial port %s (fd %d).",
|
||||||
|
serial->port, serial->fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD tmp = 0;
|
||||||
|
|
||||||
|
/* FIXME */
|
||||||
|
/* Returns non-zero upon success, 0 upon failure. */
|
||||||
|
WriteFile(hdl, buf, count, &tmp, NULL);
|
||||||
|
#else
|
||||||
|
/* Returns the number of bytes written, or -1 upon failure. */
|
||||||
|
ret = write(serial->fd, buf, count);
|
||||||
|
if (ret < 0)
|
||||||
|
sr_err("Write error: %s.", strerror(errno));
|
||||||
|
else
|
||||||
|
sr_spew("Wrote %d/%d bytes (fd %d).", ret, count, serial->fd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a number of bytes from the specified serial port.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param buf Buffer where to store the bytes that are read.
|
||||||
|
* @param count The number of bytes to read.
|
||||||
|
*
|
||||||
|
* @return The number of bytes read, or -1 upon failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (!serial) {
|
||||||
|
sr_dbg("Invalid serial port.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial->fd == -1) {
|
||||||
|
sr_dbg("Cannot use unopened serial port %s (fd %d).",
|
||||||
|
serial->port, serial->fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD tmp = 0;
|
||||||
|
|
||||||
|
/* FIXME */
|
||||||
|
/* Returns non-zero upon success, 0 upon failure. */
|
||||||
|
return ReadFile(hdl, buf, count, &tmp, NULL);
|
||||||
|
#else
|
||||||
|
/* Returns the number of bytes read, or -1 upon failure. */
|
||||||
|
ret = read(serial->fd, buf, count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set serial parameters for the specified serial port.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param baudrate The baudrate to set.
|
||||||
|
* @param bits The number of data bits to use.
|
||||||
|
* @param parity The parity setting to use (0 = none, 1 = even, 2 = odd).
|
||||||
|
* @param stopbits The number of stop bits to use (1 or 2).
|
||||||
|
* @param flowcontrol The flow control settings to use (0 = none, 1 = RTS/CTS,
|
||||||
|
* 2 = XON/XOFF).
|
||||||
|
*
|
||||||
|
* @return SR_OK upon success, SR_ERR upon failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
|
||||||
|
int bits, int parity, int stopbits,
|
||||||
|
int flowcontrol, int rts, int dtr)
|
||||||
|
{
|
||||||
|
if (!serial) {
|
||||||
|
sr_dbg("Invalid serial port.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial->fd == -1) {
|
||||||
|
sr_dbg("Cannot configure unopened serial port %s (fd %d).",
|
||||||
|
serial->port, serial->fd);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Setting serial parameters on port %s (fd %d).", serial->port,
|
||||||
|
serial->fd);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
DCB dcb;
|
||||||
|
|
||||||
|
if (!GetCommState(hdl, &dcb)) {
|
||||||
|
sr_err("Failed to get comm state on port %s (fd %d): %d.",
|
||||||
|
serial->port, serial->fd, GetLastError());
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (baudrate) {
|
||||||
|
/*
|
||||||
|
* The baudrates 50/75/134/150/200/1800/230400/460800 do not seem to
|
||||||
|
* have documented CBR_* macros.
|
||||||
|
*/
|
||||||
|
case 110:
|
||||||
|
dcb.BaudRate = CBR_110;
|
||||||
|
break;
|
||||||
|
case 300:
|
||||||
|
dcb.BaudRate = CBR_300;
|
||||||
|
break;
|
||||||
|
case 600:
|
||||||
|
dcb.BaudRate = CBR_600;
|
||||||
|
break;
|
||||||
|
case 1200:
|
||||||
|
dcb.BaudRate = CBR_1200;
|
||||||
|
break;
|
||||||
|
case 2400:
|
||||||
|
dcb.BaudRate = CBR_2400;
|
||||||
|
break;
|
||||||
|
case 4800:
|
||||||
|
dcb.BaudRate = CBR_4800;
|
||||||
|
break;
|
||||||
|
case 9600:
|
||||||
|
dcb.BaudRate = CBR_9600;
|
||||||
|
break;
|
||||||
|
case 14400:
|
||||||
|
dcb.BaudRate = CBR_14400; /* Not available on Unix? */
|
||||||
|
break;
|
||||||
|
case 19200:
|
||||||
|
dcb.BaudRate = CBR_19200;
|
||||||
|
break;
|
||||||
|
case 38400:
|
||||||
|
dcb.BaudRate = CBR_38400;
|
||||||
|
break;
|
||||||
|
case 57600:
|
||||||
|
dcb.BaudRate = CBR_57600;
|
||||||
|
break;
|
||||||
|
case 115200:
|
||||||
|
dcb.BaudRate = CBR_115200;
|
||||||
|
break;
|
||||||
|
case 128000:
|
||||||
|
dcb.BaudRate = CBR_128000; /* Not available on Unix? */
|
||||||
|
break;
|
||||||
|
case 256000:
|
||||||
|
dcb.BaudRate = CBR_256000; /* Not available on Unix? */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported baudrate: %d.", baudrate);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
sr_spew("Configuring baudrate to %d (%d).", baudrate, dcb.BaudRate);
|
||||||
|
|
||||||
|
sr_spew("Configuring %d data bits.", bits);
|
||||||
|
dcb.ByteSize = bits;
|
||||||
|
|
||||||
|
sr_spew("Configuring %d stop bits.", stopbits);
|
||||||
|
switch (stopbits) {
|
||||||
|
/* Note: There's also ONE5STOPBITS == 1.5 (unneeded so far). */
|
||||||
|
case 1:
|
||||||
|
dcb.StopBits = ONESTOPBIT;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
dcb.StopBits = TWOSTOPBITS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported stopbits number: %d.", stopbits);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (parity) {
|
||||||
|
/* Note: There's also SPACEPARITY, MARKPARITY (unneeded so far). */
|
||||||
|
case SERIAL_PARITY_NONE:
|
||||||
|
sr_spew("Configuring no parity.");
|
||||||
|
dcb.Parity = NOPARITY;
|
||||||
|
break;
|
||||||
|
case SERIAL_PARITY_EVEN:
|
||||||
|
sr_spew("Configuring even parity.");
|
||||||
|
dcb.Parity = EVENPARITY;
|
||||||
|
break;
|
||||||
|
case SERIAL_PARITY_ODD:
|
||||||
|
sr_spew("Configuring odd parity.");
|
||||||
|
dcb.Parity = ODDPARITY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported parity setting: %d.", parity);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rts != -1) {
|
||||||
|
sr_spew("Setting RTS %s.", rts ? "high" : "low");
|
||||||
|
if (rts)
|
||||||
|
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||||
|
else
|
||||||
|
dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dtr != -1) {
|
||||||
|
sr_spew("Setting DTR %s.", dtr ? "high" : "low");
|
||||||
|
if (dtr)
|
||||||
|
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||||
|
else
|
||||||
|
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetCommState(hdl, &dcb)) {
|
||||||
|
sr_err("Failed to set comm state on port %s (fd %d): %d.",
|
||||||
|
serial->port, serial->fd, GetLastError());
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
struct termios term;
|
||||||
|
speed_t baud;
|
||||||
|
int ret, controlbits;
|
||||||
|
|
||||||
|
if (tcgetattr(serial->fd, &term) < 0) {
|
||||||
|
sr_err("tcgetattr() error on port %s (fd %d): %s.",
|
||||||
|
serial->port, serial->fd, strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (baudrate) {
|
||||||
|
case 50:
|
||||||
|
baud = B50;
|
||||||
|
break;
|
||||||
|
case 75:
|
||||||
|
baud = B75;
|
||||||
|
break;
|
||||||
|
case 110:
|
||||||
|
baud = B110;
|
||||||
|
break;
|
||||||
|
case 134:
|
||||||
|
baud = B134;
|
||||||
|
break;
|
||||||
|
case 150:
|
||||||
|
baud = B150;
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
baud = B200;
|
||||||
|
break;
|
||||||
|
case 300:
|
||||||
|
baud = B300;
|
||||||
|
break;
|
||||||
|
case 600:
|
||||||
|
baud = B600;
|
||||||
|
break;
|
||||||
|
case 1200:
|
||||||
|
baud = B1200;
|
||||||
|
break;
|
||||||
|
case 1800:
|
||||||
|
baud = B1800;
|
||||||
|
break;
|
||||||
|
case 2400:
|
||||||
|
baud = B2400;
|
||||||
|
break;
|
||||||
|
case 4800:
|
||||||
|
baud = B4800;
|
||||||
|
break;
|
||||||
|
case 9600:
|
||||||
|
baud = B9600;
|
||||||
|
break;
|
||||||
|
case 19200:
|
||||||
|
baud = B19200;
|
||||||
|
break;
|
||||||
|
case 38400:
|
||||||
|
baud = B38400;
|
||||||
|
break;
|
||||||
|
case 57600:
|
||||||
|
baud = B57600;
|
||||||
|
break;
|
||||||
|
case 115200:
|
||||||
|
baud = B115200;
|
||||||
|
break;
|
||||||
|
case 230400:
|
||||||
|
baud = B230400;
|
||||||
|
break;
|
||||||
|
#if !defined(__APPLE__) && !defined(__OpenBSD__)
|
||||||
|
case 460800:
|
||||||
|
baud = B460800;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported baudrate: %d.", baudrate);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Configuring output baudrate to %d (%d).", baudrate, baud);
|
||||||
|
if (cfsetospeed(&term, baud) < 0) {
|
||||||
|
sr_err("cfsetospeed() error: %s.", strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Configuring input baudrate to %d (%d).", baudrate, baud);
|
||||||
|
if (cfsetispeed(&term, baud) < 0) {
|
||||||
|
sr_err("cfsetispeed() error: %s.", strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Configuring %d data bits.", bits);
|
||||||
|
term.c_cflag &= ~CSIZE;
|
||||||
|
switch (bits) {
|
||||||
|
case 8:
|
||||||
|
term.c_cflag |= CS8;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
term.c_cflag |= CS7;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported data bits number %d.", bits);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_spew("Configuring %d stop bits.", stopbits);
|
||||||
|
term.c_cflag &= ~CSTOPB;
|
||||||
|
switch (stopbits) {
|
||||||
|
case 1:
|
||||||
|
term.c_cflag &= ~CSTOPB;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
term.c_cflag |= CSTOPB;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported stopbits number %d.", stopbits);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
term.c_iflag &= ~(IXON | IXOFF);
|
||||||
|
term.c_cflag &= ~CRTSCTS;
|
||||||
|
switch (flowcontrol) {
|
||||||
|
case 0:
|
||||||
|
/* No flow control. */
|
||||||
|
sr_spew("Configuring no flow control.");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
sr_spew("Configuring RTS/CTS flow control.");
|
||||||
|
term.c_cflag |= CRTSCTS;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
sr_spew("Configuring XON/XOFF flow control.");
|
||||||
|
term.c_iflag |= IXON | IXOFF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported flow control setting %d.", flowcontrol);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
term.c_iflag &= ~IGNPAR;
|
||||||
|
term.c_cflag &= ~(PARODD | PARENB);
|
||||||
|
switch (parity) {
|
||||||
|
case SERIAL_PARITY_NONE:
|
||||||
|
sr_spew("Configuring no parity.");
|
||||||
|
term.c_iflag |= IGNPAR;
|
||||||
|
break;
|
||||||
|
case SERIAL_PARITY_EVEN:
|
||||||
|
sr_spew("Configuring even parity.");
|
||||||
|
term.c_cflag |= PARENB;
|
||||||
|
break;
|
||||||
|
case SERIAL_PARITY_ODD:
|
||||||
|
sr_spew("Configuring odd parity.");
|
||||||
|
term.c_cflag |= PARENB | PARODD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unsupported parity setting %d.", parity);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn off all serial port cooking. */
|
||||||
|
term.c_iflag &= ~(ISTRIP | INLCR | ICRNL);
|
||||||
|
term.c_oflag &= ~(ONLCR | OCRNL | ONOCR);
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
|
||||||
|
term.c_oflag &= ~OFILL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Disable canonical mode, and don't echo input characters. */
|
||||||
|
term.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
|
||||||
|
/* Write the configured settings. */
|
||||||
|
if (tcsetattr(serial->fd, TCSADRAIN, &term) < 0) {
|
||||||
|
sr_err("tcsetattr() error: %s.", strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rts != -1) {
|
||||||
|
sr_spew("Setting RTS %s.", rts ? "high" : "low");
|
||||||
|
controlbits = TIOCM_RTS;
|
||||||
|
if ((ret = ioctl(serial->fd, rts ? TIOCMBIS : TIOCMBIC,
|
||||||
|
&controlbits)) < 0) {
|
||||||
|
sr_err("Error setting RTS: %s.", strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dtr != -1) {
|
||||||
|
sr_spew("Setting DTR %s.", dtr ? "high" : "low");
|
||||||
|
controlbits = TIOCM_DTR;
|
||||||
|
if ((ret = ioctl(serial->fd, dtr ? TIOCMBIS : TIOCMBIC,
|
||||||
|
&controlbits)) < 0) {
|
||||||
|
sr_err("Error setting DTR: %s.", strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set serial parameters for the specified serial port.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param paramstr A serial communication parameters string, in the form
|
||||||
|
* of <speed>/<data bits><parity><stopbits><flow>, for example "9600/8n1" or
|
||||||
|
* "600/7o2" or "460800/8n1/flow=2" where flow is 0 for none, 1 for rts/cts and 2 for xon/xoff.
|
||||||
|
*
|
||||||
|
* @return SR_OK upon success, SR_ERR upon failure.
|
||||||
|
*/
|
||||||
|
#define SERIAL_COMM_SPEC "^(\\d+)/([78])([neo])([12])(.*)$"
|
||||||
|
SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
|
||||||
|
const char *paramstr)
|
||||||
|
{
|
||||||
|
GRegex *reg;
|
||||||
|
GMatchInfo *match;
|
||||||
|
int speed, databits, parity, stopbits, flow, rts, dtr, i;
|
||||||
|
char *mstr, **opts, **kv;
|
||||||
|
|
||||||
|
speed = databits = parity = stopbits = flow = 0;
|
||||||
|
rts = dtr = -1;
|
||||||
|
sr_spew("Parsing parameters from \"%s\".", paramstr);
|
||||||
|
reg = g_regex_new(SERIAL_COMM_SPEC, 0, 0, NULL);
|
||||||
|
if (g_regex_match(reg, paramstr, 0, &match)) {
|
||||||
|
if ((mstr = g_match_info_fetch(match, 1)))
|
||||||
|
speed = strtoul(mstr, NULL, 10);
|
||||||
|
g_free(mstr);
|
||||||
|
if ((mstr = g_match_info_fetch(match, 2)))
|
||||||
|
databits = strtoul(mstr, NULL, 10);
|
||||||
|
g_free(mstr);
|
||||||
|
if ((mstr = g_match_info_fetch(match, 3))) {
|
||||||
|
switch (mstr[0]) {
|
||||||
|
case 'n':
|
||||||
|
parity = SERIAL_PARITY_NONE;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
parity = SERIAL_PARITY_EVEN;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
parity = SERIAL_PARITY_ODD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free(mstr);
|
||||||
|
if ((mstr = g_match_info_fetch(match, 4)))
|
||||||
|
stopbits = strtoul(mstr, NULL, 10);
|
||||||
|
g_free(mstr);
|
||||||
|
if ((mstr = g_match_info_fetch(match, 5)) && mstr[0] != '\0') {
|
||||||
|
if (mstr[0] != '/') {
|
||||||
|
sr_dbg("missing separator before extra options");
|
||||||
|
speed = 0;
|
||||||
|
} else {
|
||||||
|
/* A set of "key=value" options separated by / */
|
||||||
|
opts = g_strsplit(mstr + 1, "/", 0);
|
||||||
|
for (i = 0; opts[i]; i++) {
|
||||||
|
kv = g_strsplit(opts[i], "=", 2);
|
||||||
|
if (!strncmp(kv[0], "rts", 3)) {
|
||||||
|
if (kv[1][0] == '1')
|
||||||
|
rts = 1;
|
||||||
|
else if (kv[1][0] == '0')
|
||||||
|
rts = 0;
|
||||||
|
else {
|
||||||
|
sr_dbg("invalid value for rts: %c", kv[1][0]);
|
||||||
|
speed = 0;
|
||||||
|
}
|
||||||
|
} else if (!strncmp(kv[0], "dtr", 3)) {
|
||||||
|
if (kv[1][0] == '1')
|
||||||
|
dtr = 1;
|
||||||
|
else if (kv[1][0] == '0')
|
||||||
|
dtr = 0;
|
||||||
|
else {
|
||||||
|
sr_dbg("invalid value for dtr: %c", kv[1][0]);
|
||||||
|
speed = 0;
|
||||||
|
}
|
||||||
|
} else if (!strncmp(kv[0], "flow", 4)) {
|
||||||
|
if (kv[1][0] == '0')
|
||||||
|
flow = 0;
|
||||||
|
else if (kv[1][0] == '1')
|
||||||
|
flow = 1;
|
||||||
|
else if (kv[1][0] == '2')
|
||||||
|
flow = 2;
|
||||||
|
else {
|
||||||
|
sr_dbg("invalid value for flow: %c", kv[1][0]);
|
||||||
|
speed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_strfreev(kv);
|
||||||
|
}
|
||||||
|
g_strfreev(opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free(mstr);
|
||||||
|
}
|
||||||
|
g_match_info_unref(match);
|
||||||
|
g_regex_unref(reg);
|
||||||
|
|
||||||
|
if (speed) {
|
||||||
|
return serial_set_params(serial, speed, databits, parity,
|
||||||
|
stopbits, flow, rts, dtr);
|
||||||
|
} else {
|
||||||
|
sr_dbg("Could not infer speed from parameter string.");
|
||||||
|
return SR_ERR_ARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a line from the specified serial port.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param buf Buffer where to store the bytes that are read.
|
||||||
|
* @param buflen Size of the buffer.
|
||||||
|
* @param timeout_ms How long to wait for a line to come in.
|
||||||
|
*
|
||||||
|
* Reading stops when CR of LR is found, which is stripped from the buffer.
|
||||||
|
*
|
||||||
|
* @return SR_OK on success, SR_ERR on failure.
|
||||||
|
*/
|
||||||
|
SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
|
||||||
|
int *buflen, gint64 timeout_ms)
|
||||||
|
{
|
||||||
|
gint64 start;
|
||||||
|
int maxlen, len;
|
||||||
|
|
||||||
|
if (!serial || serial->fd == -1) {
|
||||||
|
sr_dbg("Invalid serial port.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial->fd == -1) {
|
||||||
|
sr_dbg("Cannot use unopened serial port %s (fd %d).",
|
||||||
|
serial->port, serial->fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout_ms *= 1000;
|
||||||
|
start = g_get_monotonic_time();
|
||||||
|
|
||||||
|
maxlen = *buflen;
|
||||||
|
*buflen = len = 0;
|
||||||
|
while(1) {
|
||||||
|
len = maxlen - *buflen - 1;
|
||||||
|
if (len < 1)
|
||||||
|
break;
|
||||||
|
len = serial_read(serial, *buf + *buflen, 1);
|
||||||
|
if (len > 0) {
|
||||||
|
*buflen += len;
|
||||||
|
*(*buf + *buflen) = '\0';
|
||||||
|
if (*buflen > 0 && (*(*buf + *buflen - 1) == '\r'
|
||||||
|
|| *(*buf + *buflen - 1) == '\n')) {
|
||||||
|
/* Strip CR/LF and terminate. */
|
||||||
|
*(*buf + --*buflen) = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g_get_monotonic_time() - start > timeout_ms)
|
||||||
|
/* Timeout */
|
||||||
|
break;
|
||||||
|
g_usleep(2000);
|
||||||
|
}
|
||||||
|
if (*buflen)
|
||||||
|
sr_dbg("Received %d: '%s'.", *buflen, *buf);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to find a valid packet in a serial data stream.
|
||||||
|
*
|
||||||
|
* @param serial Previously initialized serial port structure.
|
||||||
|
* @param buf Buffer containing the bytes to write.
|
||||||
|
* @param count Size of the buffer.
|
||||||
|
* @param packet_size Size, in bytes, of a valid packet.
|
||||||
|
* @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 serial_stream_detect(struct sr_serial_dev_inst *serial,
|
||||||
|
uint8_t *buf, size_t *buflen,
|
||||||
|
size_t packet_size, packet_valid_t is_valid,
|
||||||
|
uint64_t timeout_ms, int baudrate)
|
||||||
|
{
|
||||||
|
uint64_t start, time, byte_delay_us;
|
||||||
|
size_t ibuf, i, maxlen;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
maxlen = *buflen;
|
||||||
|
|
||||||
|
sr_dbg("Detecting packets on FD %d (timeout = %" PRIu64
|
||||||
|
"ms, baudrate = %d).", serial->fd, timeout_ms, baudrate);
|
||||||
|
|
||||||
|
if (maxlen < (packet_size / 2) ) {
|
||||||
|
sr_err("Buffer size must be at least twice the packet size.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assume 8n1 transmission. That is 10 bits for every byte. */
|
||||||
|
byte_delay_us = 10 * (1000000 / baudrate);
|
||||||
|
start = g_get_monotonic_time();
|
||||||
|
|
||||||
|
i = ibuf = len = 0;
|
||||||
|
while (ibuf < maxlen) {
|
||||||
|
len = serial_read(serial, &buf[ibuf], 1);
|
||||||
|
if (len > 0) {
|
||||||
|
ibuf += len;
|
||||||
|
} else if (len == 0) {
|
||||||
|
/* No logging, already done in serial_read(). */
|
||||||
|
} else {
|
||||||
|
/* Error reading byte, but continuing anyway. */
|
||||||
|
}
|
||||||
|
|
||||||
|
time = g_get_monotonic_time() - start;
|
||||||
|
time /= 1000;
|
||||||
|
|
||||||
|
if ((ibuf - i) >= packet_size) {
|
||||||
|
/* We have at least a packet's worth of data. */
|
||||||
|
if (is_valid(&buf[i])) {
|
||||||
|
sr_spew("Found valid %d-byte packet after "
|
||||||
|
"%" PRIu64 "ms.", (ibuf - i), time);
|
||||||
|
*buflen = ibuf;
|
||||||
|
return SR_OK;
|
||||||
|
} else {
|
||||||
|
sr_spew("Got %d bytes, but not a valid "
|
||||||
|
"packet.", (ibuf - i));
|
||||||
|
}
|
||||||
|
/* Not a valid packet. Continue searching. */
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (time >= timeout_ms) {
|
||||||
|
/* Timeout */
|
||||||
|
sr_dbg("Detection timed out after %dms.", time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_usleep(byte_delay_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
*buflen = ibuf;
|
||||||
|
|
||||||
|
sr_err("Didn't find a valid packet (read %d bytes).", *buflen);
|
||||||
|
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
* Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <libusb.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* SR_CONF_CONN takes one of these: */
|
||||||
|
#define CONN_USB_VIDPID "^([0-9a-z]{4})\\.([0-9a-z]{4})$"
|
||||||
|
#define CONN_USB_BUSADDR "^(\\d+)\\.(\\d+)$"
|
||||||
|
|
||||||
|
/* Some USBTMC-specific enums, as defined in the USBTMC standard. */
|
||||||
|
#define SUBCLASS_USBTMC 0x03
|
||||||
|
#define USBTMC_USB488 0x01
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "usb: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find USB devices according to a connection string.
|
||||||
|
*
|
||||||
|
* @param usb_ctx libusb context to use while scanning.
|
||||||
|
* @param conn Connection string specifying the device(s) to match. This
|
||||||
|
* can be of the form "<bus>.<address>", or "<vendorid>.<productid>".
|
||||||
|
*
|
||||||
|
* @return A GSList of struct sr_usb_dev_inst, with bus and address fields
|
||||||
|
* matching the device that matched the connection string. The GSList and
|
||||||
|
* its contents must be freed by the caller.
|
||||||
|
*/
|
||||||
|
SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn)
|
||||||
|
{
|
||||||
|
struct sr_usb_dev_inst *usb;
|
||||||
|
struct libusb_device **devlist;
|
||||||
|
struct libusb_device_descriptor des;
|
||||||
|
GSList *devices;
|
||||||
|
GRegex *reg;
|
||||||
|
GMatchInfo *match;
|
||||||
|
int vid, pid, bus, addr, b, a, ret, i;
|
||||||
|
char *mstr;
|
||||||
|
|
||||||
|
vid = pid = bus = addr = 0;
|
||||||
|
reg = g_regex_new(CONN_USB_VIDPID, 0, 0, NULL);
|
||||||
|
if (g_regex_match(reg, conn, 0, &match)) {
|
||||||
|
if ((mstr = g_match_info_fetch(match, 1)))
|
||||||
|
vid = strtoul(mstr, NULL, 16);
|
||||||
|
g_free(mstr);
|
||||||
|
|
||||||
|
if ((mstr = g_match_info_fetch(match, 2)))
|
||||||
|
pid = strtoul(mstr, NULL, 16);
|
||||||
|
g_free(mstr);
|
||||||
|
sr_dbg("Trying to find USB device with VID:PID = %04x:%04x.",
|
||||||
|
vid, pid);
|
||||||
|
} else {
|
||||||
|
g_match_info_unref(match);
|
||||||
|
g_regex_unref(reg);
|
||||||
|
reg = g_regex_new(CONN_USB_BUSADDR, 0, 0, NULL);
|
||||||
|
if (g_regex_match(reg, conn, 0, &match)) {
|
||||||
|
if ((mstr = g_match_info_fetch(match, 1)))
|
||||||
|
bus = strtoul(mstr, NULL, 10);
|
||||||
|
g_free(mstr);
|
||||||
|
|
||||||
|
if ((mstr = g_match_info_fetch(match, 2)))
|
||||||
|
addr = strtoul(mstr, NULL, 10);
|
||||||
|
g_free(mstr);
|
||||||
|
sr_dbg("Trying to find USB device with bus.address = "
|
||||||
|
"%d.%d.", bus, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_match_info_unref(match);
|
||||||
|
g_regex_unref(reg);
|
||||||
|
|
||||||
|
if (vid + pid + bus + addr == 0) {
|
||||||
|
sr_err("Neither VID:PID nor bus.address was specified.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bus > 64) {
|
||||||
|
sr_err("Invalid bus specified: %d.", bus);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr > 127) {
|
||||||
|
sr_err("Invalid address specified: %d.", addr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Looks like a valid USB device specification, but is it connected? */
|
||||||
|
devices = NULL;
|
||||||
|
libusb_get_device_list(usb_ctx, &devlist);
|
||||||
|
for (i = 0; devlist[i]; i++) {
|
||||||
|
if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
|
||||||
|
sr_err("Failed to get device descriptor: %s.",
|
||||||
|
libusb_error_name(ret));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vid + pid && (des.idVendor != vid || des.idProduct != pid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
b = libusb_get_bus_number(devlist[i]);
|
||||||
|
a = libusb_get_device_address(devlist[i]);
|
||||||
|
if (bus + addr && (b != bus || a != addr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sr_dbg("Found USB device (VID:PID = %04x:%04x, bus.address = "
|
||||||
|
"%d.%d).", des.idVendor, des.idProduct, b, a);
|
||||||
|
|
||||||
|
usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
|
||||||
|
libusb_get_device_address(devlist[i]), NULL);
|
||||||
|
devices = g_slist_append(devices, usb);
|
||||||
|
}
|
||||||
|
libusb_free_device_list(devlist, 1);
|
||||||
|
|
||||||
|
sr_dbg("Found %d device(s).", g_slist_length(devices));
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find USB devices supporting the USBTMC class
|
||||||
|
*
|
||||||
|
* @param usb_ctx libusb context to use while scanning.
|
||||||
|
*
|
||||||
|
* @return A GSList of struct sr_usb_dev_inst, with bus and address fields
|
||||||
|
* indicating devices with USBTMC support.
|
||||||
|
*/
|
||||||
|
SR_PRIV GSList *sr_usb_find_usbtmc(libusb_context *usb_ctx)
|
||||||
|
{
|
||||||
|
struct sr_usb_dev_inst *usb;
|
||||||
|
struct libusb_device **devlist;
|
||||||
|
struct libusb_device_descriptor des;
|
||||||
|
struct libusb_config_descriptor *confdes;
|
||||||
|
const struct libusb_interface_descriptor *intfdes;
|
||||||
|
GSList *devices;
|
||||||
|
int confidx, intfidx, ret, i;
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
libusb_get_device_list(usb_ctx, &devlist);
|
||||||
|
for (i = 0; devlist[i]; i++) {
|
||||||
|
if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
|
||||||
|
sr_err("Failed to get device descriptor: %s.",
|
||||||
|
libusb_error_name(ret));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
|
||||||
|
if (libusb_get_config_descriptor(devlist[i], confidx, &confdes) != 0) {
|
||||||
|
sr_err("Failed to get configuration descriptor.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
|
||||||
|
intfdes = confdes->interface[intfidx].altsetting;
|
||||||
|
if (intfdes->bInterfaceClass != LIBUSB_CLASS_APPLICATION
|
||||||
|
|| intfdes->bInterfaceSubClass != SUBCLASS_USBTMC
|
||||||
|
|| intfdes->bInterfaceProtocol != USBTMC_USB488)
|
||||||
|
continue;
|
||||||
|
sr_dbg("Found USBTMC device (VID:PID = %04x:%04x, bus.address = "
|
||||||
|
"%d.%d).", des.idVendor, des.idProduct,
|
||||||
|
libusb_get_bus_number(devlist[i]),
|
||||||
|
libusb_get_device_address(devlist[i]));
|
||||||
|
|
||||||
|
usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
|
||||||
|
libusb_get_device_address(devlist[i]), NULL);
|
||||||
|
devices = g_slist_append(devices, usb);
|
||||||
|
}
|
||||||
|
libusb_free_config_descriptor(confdes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libusb_free_device_list(devlist, 1);
|
||||||
|
|
||||||
|
sr_dbg("Found %d device(s).", g_slist_length(devices));
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb)
|
||||||
|
{
|
||||||
|
struct libusb_device **devlist;
|
||||||
|
struct libusb_device_descriptor des;
|
||||||
|
int ret, r, cnt, i, a, b;
|
||||||
|
|
||||||
|
sr_dbg("Trying to open USB device %d.%d.", usb->bus, usb->address);
|
||||||
|
|
||||||
|
if ((cnt = libusb_get_device_list(usb_ctx, &devlist)) < 0) {
|
||||||
|
sr_err("Failed to retrieve device list: %s.",
|
||||||
|
libusb_error_name(cnt));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SR_ERR;
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
if ((r = libusb_get_device_descriptor(devlist[i], &des)) < 0) {
|
||||||
|
sr_err("Failed to get device descriptor: %s.",
|
||||||
|
libusb_error_name(r));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = libusb_get_bus_number(devlist[i]);
|
||||||
|
a = libusb_get_device_address(devlist[i]);
|
||||||
|
if (b != usb->bus || a != usb->address)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((r = libusb_open(devlist[i], &usb->devhdl)) < 0) {
|
||||||
|
sr_err("Failed to open device: %s.",
|
||||||
|
libusb_error_name(r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Opened USB device (VID:PID = %04x:%04x, bus.address = "
|
||||||
|
"%d.%d).", des.idVendor, des.idProduct, b, a);
|
||||||
|
|
||||||
|
ret = SR_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_device_list(devlist, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
if LA_DEMO
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrokhwdemo.la
|
||||||
|
|
||||||
|
libsigrokhwdemo_la_SOURCES = \
|
||||||
|
demo.c
|
||||||
|
|
||||||
|
libsigrokhwdemo_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,545 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
|
||||||
|
* Copyright (C) 2011 Olivier Fauchon <olivier@aixmarseille.com>
|
||||||
|
* Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <io.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
|
||||||
|
#endif
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "demo: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
|
/* TODO: Number of probes should be configurable. */
|
||||||
|
#define NUM_PROBES 8
|
||||||
|
|
||||||
|
#define DEMONAME "Demo device"
|
||||||
|
|
||||||
|
/* The size of chunks to send through the session bus. */
|
||||||
|
/* TODO: Should be configurable. */
|
||||||
|
#define BUFSIZE 4096
|
||||||
|
|
||||||
|
#define STR_PATTERN_SIGROK "sigrok"
|
||||||
|
#define STR_PATTERN_RANDOM "random"
|
||||||
|
#define STR_PATTERN_INC "incremental"
|
||||||
|
#define STR_PATTERN_ALL_LOW "all-low"
|
||||||
|
#define STR_PATTERN_ALL_HIGH "all-high"
|
||||||
|
|
||||||
|
/* Supported patterns which we can generate */
|
||||||
|
enum {
|
||||||
|
/**
|
||||||
|
* Pattern which spells "sigrok" using '0's (with '1's as "background")
|
||||||
|
* when displayed using the 'bits' output format.
|
||||||
|
*/
|
||||||
|
PATTERN_SIGROK,
|
||||||
|
|
||||||
|
/** Pattern which consists of (pseudo-)random values on all probes. */
|
||||||
|
PATTERN_RANDOM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pattern which consists of incrementing numbers.
|
||||||
|
* TODO: Better description.
|
||||||
|
*/
|
||||||
|
PATTERN_INC,
|
||||||
|
|
||||||
|
/** Pattern where all probes have a low logic state. */
|
||||||
|
PATTERN_ALL_LOW,
|
||||||
|
|
||||||
|
/** Pattern where all probes have a high logic state. */
|
||||||
|
PATTERN_ALL_HIGH,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Private, per-device-instance driver context. */
|
||||||
|
struct dev_context {
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
int pipe_fds[2];
|
||||||
|
GIOChannel *channel;
|
||||||
|
uint64_t cur_samplerate;
|
||||||
|
uint64_t limit_samples;
|
||||||
|
uint64_t limit_msec;
|
||||||
|
uint8_t sample_generator;
|
||||||
|
uint64_t samples_counter;
|
||||||
|
void *cb_data;
|
||||||
|
int64_t starttime;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int hwcaps[] = {
|
||||||
|
SR_CONF_LOGIC_ANALYZER,
|
||||||
|
SR_CONF_DEMO_DEV,
|
||||||
|
SR_CONF_SAMPLERATE,
|
||||||
|
SR_CONF_PATTERN_MODE,
|
||||||
|
SR_CONF_LIMIT_SAMPLES,
|
||||||
|
SR_CONF_LIMIT_MSEC,
|
||||||
|
SR_CONF_CONTINUOUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint64_t samplerates[] = {
|
||||||
|
SR_HZ(1),
|
||||||
|
SR_GHZ(1),
|
||||||
|
SR_HZ(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *pattern_strings[] = {
|
||||||
|
"sigrok",
|
||||||
|
"random",
|
||||||
|
"incremental",
|
||||||
|
"all-low",
|
||||||
|
"all-high",
|
||||||
|
};
|
||||||
|
|
||||||
|
/* We name the probes 0-7 on our demo driver. */
|
||||||
|
static const char *probe_names[NUM_PROBES + 1] = {
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t pattern_sigrok[] = {
|
||||||
|
0x4c, 0x92, 0x92, 0x92, 0x64, 0x00, 0x00, 0x00,
|
||||||
|
0x82, 0xfe, 0xfe, 0x82, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7c, 0x82, 0x82, 0x92, 0x74, 0x00, 0x00, 0x00,
|
||||||
|
0xfe, 0x12, 0x12, 0x32, 0xcc, 0x00, 0x00, 0x00,
|
||||||
|
0x7c, 0x82, 0x82, 0x82, 0x7c, 0x00, 0x00, 0x00,
|
||||||
|
0xfe, 0x10, 0x28, 0x44, 0x82, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xbe, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Private, per-device-instance driver context. */
|
||||||
|
/* TODO: struct context as with the other drivers. */
|
||||||
|
|
||||||
|
/* List of struct sr_dev_inst, maintained by dev_open()/dev_close(). */
|
||||||
|
SR_PRIV struct sr_dev_driver demo_driver_info;
|
||||||
|
static struct sr_dev_driver *di = &demo_driver_info;
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
|
||||||
|
|
||||||
|
static int clear_instances(void)
|
||||||
|
{
|
||||||
|
/* Nothing needed so far. */
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_init(struct sr_context *sr_ctx)
|
||||||
|
{
|
||||||
|
return std_hw_init(sr_ctx, di, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_scan(GSList *options)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
GSList *devices;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
(void)options;
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
|
||||||
|
sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, DEMONAME, NULL, NULL);
|
||||||
|
if (!sdi) {
|
||||||
|
sr_err("Device instance creation failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
sdi->driver = di;
|
||||||
|
|
||||||
|
for (i = 0; probe_names[i]; i++) {
|
||||||
|
if (!(probe = sr_probe_new(i, SR_PROBE_LOGIC, TRUE,
|
||||||
|
probe_names[i])))
|
||||||
|
return NULL;
|
||||||
|
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||||
|
}
|
||||||
|
|
||||||
|
devices = g_slist_append(devices, sdi);
|
||||||
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
|
|
||||||
|
if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
|
||||||
|
sr_err("Device context malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->sdi = sdi;
|
||||||
|
devc->cur_samplerate = SR_KHZ(200);
|
||||||
|
devc->limit_samples = 0;
|
||||||
|
devc->limit_msec = 0;
|
||||||
|
devc->sample_generator = PATTERN_SIGROK;
|
||||||
|
|
||||||
|
sdi->priv = devc;
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_dev_list(void)
|
||||||
|
{
|
||||||
|
return ((struct drv_context *)(di->priv))->instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_open(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
sdi->status = SR_ST_ACTIVE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_close(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
sdi->status = SR_ST_INACTIVE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_cleanup(void)
|
||||||
|
{
|
||||||
|
GSList *l;
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
int ret = SR_OK;
|
||||||
|
|
||||||
|
if (!(drvc = di->priv))
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
/* Properly close and free all devices. */
|
||||||
|
for (l = drvc->instances; l; l = l->next) {
|
||||||
|
if (!(sdi = l->data)) {
|
||||||
|
/* Log error, but continue cleaning up the rest. */
|
||||||
|
sr_err("%s: sdi was NULL, continuing", __func__);
|
||||||
|
ret = SR_ERR_BUG;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
|
}
|
||||||
|
g_slist_free(drvc->instances);
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *const devc = sdi->priv;
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_SAMPLERATE:
|
||||||
|
*data = g_variant_new_uint64(devc->cur_samplerate);
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
*data = g_variant_new_uint64(devc->limit_samples);
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
*data = g_variant_new_uint64(devc->limit_msec);
|
||||||
|
break;
|
||||||
|
case SR_CONF_PATTERN_MODE:
|
||||||
|
switch (devc->sample_generator) {
|
||||||
|
case PATTERN_SIGROK:
|
||||||
|
*data = g_variant_new_string(STR_PATTERN_SIGROK);
|
||||||
|
break;
|
||||||
|
case PATTERN_RANDOM:
|
||||||
|
*data = g_variant_new_string(STR_PATTERN_RANDOM);
|
||||||
|
break;
|
||||||
|
case PATTERN_INC:
|
||||||
|
*data = g_variant_new_string(STR_PATTERN_INC);
|
||||||
|
break;
|
||||||
|
case PATTERN_ALL_LOW:
|
||||||
|
*data = g_variant_new_string(STR_PATTERN_ALL_LOW);
|
||||||
|
break;
|
||||||
|
case PATTERN_ALL_HIGH:
|
||||||
|
*data = g_variant_new_string(STR_PATTERN_ALL_HIGH);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const char *stropt;
|
||||||
|
|
||||||
|
struct dev_context *const devc = sdi->priv;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (id == SR_CONF_SAMPLERATE) {
|
||||||
|
devc->cur_samplerate = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("%s: setting samplerate to %" PRIu64, __func__,
|
||||||
|
devc->cur_samplerate);
|
||||||
|
ret = SR_OK;
|
||||||
|
} else if (id == SR_CONF_LIMIT_SAMPLES) {
|
||||||
|
devc->limit_msec = 0;
|
||||||
|
devc->limit_samples = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("%s: setting limit_samples to %" PRIu64, __func__,
|
||||||
|
devc->limit_samples);
|
||||||
|
ret = SR_OK;
|
||||||
|
} else if (id == SR_CONF_LIMIT_MSEC) {
|
||||||
|
devc->limit_msec = g_variant_get_uint64(data);
|
||||||
|
devc->limit_samples = 0;
|
||||||
|
sr_dbg("%s: setting limit_msec to %" PRIu64, __func__,
|
||||||
|
devc->limit_msec);
|
||||||
|
ret = SR_OK;
|
||||||
|
} else if (id == SR_CONF_PATTERN_MODE) {
|
||||||
|
stropt = g_variant_get_string(data, NULL);
|
||||||
|
ret = SR_OK;
|
||||||
|
if (!strcmp(stropt, STR_PATTERN_SIGROK)) {
|
||||||
|
devc->sample_generator = PATTERN_SIGROK;
|
||||||
|
} else if (!strcmp(stropt, STR_PATTERN_RANDOM)) {
|
||||||
|
devc->sample_generator = PATTERN_RANDOM;
|
||||||
|
} else if (!strcmp(stropt, STR_PATTERN_INC)) {
|
||||||
|
devc->sample_generator = PATTERN_INC;
|
||||||
|
} else if (!strcmp(stropt, STR_PATTERN_ALL_LOW)) {
|
||||||
|
devc->sample_generator = PATTERN_ALL_LOW;
|
||||||
|
} else if (!strcmp(stropt, STR_PATTERN_ALL_HIGH)) {
|
||||||
|
devc->sample_generator = PATTERN_ALL_HIGH;
|
||||||
|
} else {
|
||||||
|
ret = SR_ERR;
|
||||||
|
}
|
||||||
|
sr_dbg("%s: setting pattern to %d",
|
||||||
|
__func__, devc->sample_generator);
|
||||||
|
} else {
|
||||||
|
ret = SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
GVariant *gvar;
|
||||||
|
GVariantBuilder gvb;
|
||||||
|
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
case SR_CONF_SAMPLERATE:
|
||||||
|
g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
|
||||||
|
gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
|
||||||
|
ARRAY_SIZE(samplerates), sizeof(uint64_t));
|
||||||
|
g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
|
||||||
|
*data = g_variant_builder_end(&gvb);
|
||||||
|
break;
|
||||||
|
case SR_CONF_PATTERN_MODE:
|
||||||
|
*data = g_variant_new_strv(pattern_strings, ARRAY_SIZE(pattern_strings));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void samples_generator(uint8_t *buf, uint64_t size,
|
||||||
|
struct dev_context *devc)
|
||||||
|
{
|
||||||
|
static uint64_t p = 0;
|
||||||
|
uint64_t i;
|
||||||
|
|
||||||
|
/* TODO: Needed? */
|
||||||
|
memset(buf, 0, size);
|
||||||
|
|
||||||
|
switch (devc->sample_generator) {
|
||||||
|
case PATTERN_SIGROK: /* sigrok pattern */
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
*(buf + i) = ~(pattern_sigrok[
|
||||||
|
p++ % sizeof(pattern_sigrok)] >> 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PATTERN_RANDOM: /* Random */
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
*(buf + i) = (uint8_t)(rand() & 0xff);
|
||||||
|
break;
|
||||||
|
case PATTERN_INC: /* Simple increment */
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
*(buf + i) = p++;
|
||||||
|
break;
|
||||||
|
case PATTERN_ALL_LOW: /* All probes are low */
|
||||||
|
memset(buf, 0x00, size);
|
||||||
|
break;
|
||||||
|
case PATTERN_ALL_HIGH: /* All probes are high */
|
||||||
|
memset(buf, 0xff, size);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sr_err("Unknown pattern: %d.", devc->sample_generator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback handling data */
|
||||||
|
static int receive_data(int fd, int revents, void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *devc = cb_data;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
struct sr_datafeed_logic logic;
|
||||||
|
uint8_t buf[BUFSIZE];
|
||||||
|
static uint64_t samples_to_send, expected_samplenum, sending_now;
|
||||||
|
int64_t time, elapsed;
|
||||||
|
|
||||||
|
(void)fd;
|
||||||
|
(void)revents;
|
||||||
|
|
||||||
|
/* How many "virtual" samples should we have collected by now? */
|
||||||
|
time = g_get_monotonic_time();
|
||||||
|
elapsed = time - devc->starttime;
|
||||||
|
expected_samplenum = elapsed * devc->cur_samplerate / 1000000;
|
||||||
|
/* Of those, how many do we still have to send? */
|
||||||
|
samples_to_send = expected_samplenum - devc->samples_counter;
|
||||||
|
|
||||||
|
if (devc->limit_samples) {
|
||||||
|
samples_to_send = MIN(samples_to_send,
|
||||||
|
devc->limit_samples - devc->samples_counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (samples_to_send > 0) {
|
||||||
|
sending_now = MIN(samples_to_send, sizeof(buf));
|
||||||
|
samples_to_send -= sending_now;
|
||||||
|
samples_generator(buf, sending_now, devc);
|
||||||
|
|
||||||
|
packet.type = SR_DF_LOGIC;
|
||||||
|
packet.payload = &logic;
|
||||||
|
logic.length = sending_now;
|
||||||
|
logic.unitsize = 1;
|
||||||
|
logic.data = buf;
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
devc->samples_counter += sending_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devc->limit_samples &&
|
||||||
|
devc->samples_counter >= devc->limit_samples) {
|
||||||
|
sr_info("Requested number of samples reached.");
|
||||||
|
hw_dev_acquisition_stop(devc->sdi, cb_data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
|
||||||
|
void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *const devc = sdi->priv;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
devc->cb_data = cb_data;
|
||||||
|
devc->samples_counter = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting two channels connected by a pipe is a remnant from when the
|
||||||
|
* demo driver generated data in a thread, and collected and sent the
|
||||||
|
* data in the main program loop.
|
||||||
|
* They are kept here because it provides a convenient way of setting
|
||||||
|
* up a timeout-based polling mechanism.
|
||||||
|
*/
|
||||||
|
if (pipe(devc->pipe_fds)) {
|
||||||
|
/* TODO: Better error message. */
|
||||||
|
sr_err("%s: pipe() failed", __func__);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->channel = g_io_channel_unix_new(devc->pipe_fds[0]);
|
||||||
|
|
||||||
|
g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL);
|
||||||
|
|
||||||
|
/* Set channel encoding to binary (default is UTF-8). */
|
||||||
|
g_io_channel_set_encoding(devc->channel, NULL, NULL);
|
||||||
|
|
||||||
|
/* Make channels to unbuffered. */
|
||||||
|
g_io_channel_set_buffered(devc->channel, FALSE);
|
||||||
|
|
||||||
|
sr_session_source_add_channel(devc->channel, G_IO_IN | G_IO_ERR,
|
||||||
|
40, receive_data, devc);
|
||||||
|
|
||||||
|
/* Send header packet to the session bus. */
|
||||||
|
std_session_send_df_header(cb_data, LOG_PREFIX);
|
||||||
|
|
||||||
|
/* We use this timestamp to decide how many more samples to send. */
|
||||||
|
devc->starttime = g_get_monotonic_time();
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *const devc = sdi->priv;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
|
||||||
|
(void)cb_data;
|
||||||
|
|
||||||
|
sr_dbg("Stopping aquisition.");
|
||||||
|
|
||||||
|
sr_session_source_remove_channel(devc->channel);
|
||||||
|
g_io_channel_shutdown(devc->channel, FALSE, NULL);
|
||||||
|
g_io_channel_unref(devc->channel);
|
||||||
|
devc->channel = NULL;
|
||||||
|
|
||||||
|
/* Send last packet. */
|
||||||
|
packet.type = SR_DF_END;
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver demo_driver_info = {
|
||||||
|
.name = "demo",
|
||||||
|
.longname = "Demo driver and pattern generator",
|
||||||
|
.api_version = 1,
|
||||||
|
.init = hw_init,
|
||||||
|
.cleanup = hw_cleanup,
|
||||||
|
.scan = hw_scan,
|
||||||
|
.dev_list = hw_dev_list,
|
||||||
|
.dev_clear = clear_instances,
|
||||||
|
.config_get = config_get,
|
||||||
|
.config_set = config_set,
|
||||||
|
.config_list = config_list,
|
||||||
|
.dev_open = hw_dev_open,
|
||||||
|
.dev_close = hw_dev_close,
|
||||||
|
.dev_acquisition_start = hw_dev_acquisition_start,
|
||||||
|
.dev_acquisition_stop = hw_dev_acquisition_stop,
|
||||||
|
.priv = NULL,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
##
|
||||||
|
## This program is free software: you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation, either version 3 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
if HW_FLUKE_DMM
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrokhwflukedmm.la
|
||||||
|
|
||||||
|
libsigrokhwflukedmm_la_SOURCES = \
|
||||||
|
api.c \
|
||||||
|
fluke.c \
|
||||||
|
fluke-dmm.h
|
||||||
|
|
||||||
|
libsigrokhwflukedmm_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
@ -0,0 +1,374 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "fluke-dmm.h"
|
||||||
|
|
||||||
|
static const int32_t hwopts[] = {
|
||||||
|
SR_CONF_CONN,
|
||||||
|
SR_CONF_SERIALCOMM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int32_t hwcaps[] = {
|
||||||
|
SR_CONF_MULTIMETER,
|
||||||
|
SR_CONF_LIMIT_SAMPLES,
|
||||||
|
SR_CONF_LIMIT_MSEC,
|
||||||
|
SR_CONF_CONTINUOUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver flukedmm_driver_info;
|
||||||
|
static struct sr_dev_driver *di = &flukedmm_driver_info;
|
||||||
|
|
||||||
|
static char *scan_conn[] = {
|
||||||
|
/* 287/289 */
|
||||||
|
"115200/8n1",
|
||||||
|
/* 187/189 */
|
||||||
|
"9600/8n1",
|
||||||
|
/* Scopemeter 190 series */
|
||||||
|
"1200/8n1",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct flukedmm_profile supported_flukedmm[] = {
|
||||||
|
{ FLUKE_187, "187", 100, 1000 },
|
||||||
|
{ FLUKE_287, "287", 100, 1000 },
|
||||||
|
{ FLUKE_190, "199B", 1000, 3500 },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Properly close and free all devices. */
|
||||||
|
static int clear_instances(void)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
GSList *l;
|
||||||
|
|
||||||
|
if (!(drvc = di->priv))
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
for (l = drvc->instances; l; l = l->next) {
|
||||||
|
if (!(sdi = l->data))
|
||||||
|
continue;
|
||||||
|
if (!(devc = sdi->priv))
|
||||||
|
continue;
|
||||||
|
serial = sdi->conn;
|
||||||
|
sr_serial_dev_inst_free(serial);
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
|
}
|
||||||
|
g_slist_free(drvc->instances);
|
||||||
|
drvc->instances = NULL;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_init(struct sr_context *sr_ctx)
|
||||||
|
{
|
||||||
|
return std_hw_init(sr_ctx, di, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *fluke_scan(const char *conn, const char *serialcomm)
|
||||||
|
{
|
||||||
|
struct sr_dev_inst *sdi;
|
||||||
|
struct drv_context *drvc;
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_probe *probe;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
GSList *devices;
|
||||||
|
int retry, len, i, s;
|
||||||
|
char buf[128], *b, **tokens;
|
||||||
|
|
||||||
|
if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
drvc = di->priv;
|
||||||
|
b = buf;
|
||||||
|
retry = 0;
|
||||||
|
devices = NULL;
|
||||||
|
/* We'll try the discovery sequence three times in case the device
|
||||||
|
* is not in an idle state when we send ID. */
|
||||||
|
while (!devices && retry < 3) {
|
||||||
|
retry++;
|
||||||
|
serial_flush(serial);
|
||||||
|
if (serial_write(serial, "ID\r", 3) == -1) {
|
||||||
|
sr_err("Unable to send ID string: %s.",
|
||||||
|
strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Response is first a CMD_ACK byte (ASCII '0' for OK,
|
||||||
|
* or '1' to signify an error. */
|
||||||
|
len = 128;
|
||||||
|
serial_readline(serial, &b, &len, 150);
|
||||||
|
if (len != 1)
|
||||||
|
continue;
|
||||||
|
if (buf[0] != '0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* If CMD_ACK was OK, ID string follows. */
|
||||||
|
len = 128;
|
||||||
|
serial_readline(serial, &b, &len, 850);
|
||||||
|
if (len < 10)
|
||||||
|
continue;
|
||||||
|
if (strcspn(buf, ",") < 15)
|
||||||
|
/* Looks like it's comma-separated. */
|
||||||
|
tokens = g_strsplit(buf, ",", 3);
|
||||||
|
else
|
||||||
|
/* Fluke 199B, at least, uses semicolon. */
|
||||||
|
tokens = g_strsplit(buf, ";", 3);
|
||||||
|
if (!strncmp("FLUKE", tokens[0], 5)
|
||||||
|
&& tokens[1] && tokens[2]) {
|
||||||
|
for (i = 0; supported_flukedmm[i].model; i++) {
|
||||||
|
if (strcmp(supported_flukedmm[i].modelname, tokens[0] + 6))
|
||||||
|
continue;
|
||||||
|
/* Skip leading spaces in version number. */
|
||||||
|
for (s = 0; tokens[1][s] == ' '; s++);
|
||||||
|
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Fluke",
|
||||||
|
tokens[0] + 6, tokens[1] + s)))
|
||||||
|
return NULL;
|
||||||
|
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
|
||||||
|
sr_err("Device context malloc failed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
devc->profile = &supported_flukedmm[i];
|
||||||
|
sdi->conn = serial;
|
||||||
|
sdi->priv = devc;
|
||||||
|
sdi->driver = di;
|
||||||
|
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
|
||||||
|
return NULL;
|
||||||
|
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||||
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
|
devices = g_slist_append(devices, sdi);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_strfreev(tokens);
|
||||||
|
if (devices)
|
||||||
|
/* Found one. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
serial_close(serial);
|
||||||
|
if (!devices)
|
||||||
|
sr_serial_dev_inst_free(serial);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_scan(GSList *options)
|
||||||
|
{
|
||||||
|
struct sr_config *src;
|
||||||
|
GSList *l, *devices;
|
||||||
|
int i;
|
||||||
|
const char *conn, *serialcomm;
|
||||||
|
|
||||||
|
conn = serialcomm = NULL;
|
||||||
|
for (l = options; l; l = l->next) {
|
||||||
|
src = l->data;
|
||||||
|
switch (src->key) {
|
||||||
|
case SR_CONF_CONN:
|
||||||
|
conn = g_variant_get_string(src->data, NULL);
|
||||||
|
break;
|
||||||
|
case SR_CONF_SERIALCOMM:
|
||||||
|
serialcomm = g_variant_get_string(src->data, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!conn)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (serialcomm) {
|
||||||
|
/* Use the provided comm specs. */
|
||||||
|
devices = fluke_scan(conn, serialcomm);
|
||||||
|
} else {
|
||||||
|
for (i = 0; scan_conn[i]; i++) {
|
||||||
|
if ((devices = fluke_scan(conn, scan_conn[i])))
|
||||||
|
break;
|
||||||
|
/* The Scopemeter 199B, at least, requires this
|
||||||
|
* after all the 115k/9.6k confusion. */
|
||||||
|
g_usleep(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *hw_dev_list(void)
|
||||||
|
{
|
||||||
|
return ((struct drv_context *)(di->priv))->instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_open(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
|
||||||
|
return SR_ERR;
|
||||||
|
|
||||||
|
sdi->status = SR_ST_ACTIVE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_close(struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
serial = sdi->conn;
|
||||||
|
if (serial && serial->fd != -1) {
|
||||||
|
serial_close(serial);
|
||||||
|
sdi->status = SR_ST_INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_cleanup(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
clear_instances();
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("sdi->priv was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
/* TODO: not yet implemented */
|
||||||
|
if (g_variant_get_uint64(data) == 0) {
|
||||||
|
sr_err("LIMIT_MSEC can't be 0.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
devc->limit_msec = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("Setting time limit to %" PRIu64 "ms.",
|
||||||
|
devc->limit_msec);
|
||||||
|
break;
|
||||||
|
case SR_CONF_LIMIT_SAMPLES:
|
||||||
|
devc->limit_samples = g_variant_get_uint64(data);
|
||||||
|
sr_dbg("Setting sample limit to %" PRIu64 ".",
|
||||||
|
devc->limit_samples);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
|
||||||
|
{
|
||||||
|
|
||||||
|
(void)sdi;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case SR_CONF_SCAN_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
|
hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SR_ERR_NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
|
||||||
|
void *cb_data)
|
||||||
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
struct sr_serial_dev_inst *serial;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
|
||||||
|
if (!(devc = sdi->priv)) {
|
||||||
|
sr_err("sdi->priv was NULL.");
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
devc->cb_data = cb_data;
|
||||||
|
|
||||||
|
/* Send header packet to the session bus. */
|
||||||
|
std_session_send_df_header(cb_data, LOG_PREFIX);
|
||||||
|
|
||||||
|
/* Poll every 100ms, or whenever some data comes in. */
|
||||||
|
serial = sdi->conn;
|
||||||
|
sr_source_add(serial->fd, G_IO_IN, 50, fluke_receive_data, (void *)sdi);
|
||||||
|
|
||||||
|
if (serial_write(serial, "QM\r", 3) == -1) {
|
||||||
|
sr_err("Unable to send QM: %s.", strerror(errno));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
devc->cmd_sent_at = g_get_monotonic_time() / 1000;
|
||||||
|
devc->expect_response = TRUE;
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
|
||||||
|
{
|
||||||
|
return std_hw_dev_acquisition_stop_serial(sdi, cb_data, hw_dev_close,
|
||||||
|
sdi->conn, LOG_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV struct sr_dev_driver flukedmm_driver_info = {
|
||||||
|
.name = "fluke-dmm",
|
||||||
|
.longname = "Fluke 18x/28x series DMMs",
|
||||||
|
.api_version = 1,
|
||||||
|
.init = hw_init,
|
||||||
|
.cleanup = hw_cleanup,
|
||||||
|
.scan = hw_scan,
|
||||||
|
.dev_list = hw_dev_list,
|
||||||
|
.dev_clear = clear_instances,
|
||||||
|
.config_get = NULL,
|
||||||
|
.config_set = config_set,
|
||||||
|
.config_list = config_list,
|
||||||
|
.dev_open = hw_dev_open,
|
||||||
|
.dev_close = hw_dev_close,
|
||||||
|
.dev_acquisition_start = hw_dev_acquisition_start,
|
||||||
|
.dev_acquisition_stop = hw_dev_acquisition_stop,
|
||||||
|
.priv = NULL,
|
||||||
|
};
|
||||||
|
|
@ -17,23 +17,26 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LIBSIGROK_HARDWARE_FLUKE_DMM_PROTOCOL_H
|
|
||||||
#define LIBSIGROK_HARDWARE_FLUKE_DMM_PROTOCOL_H
|
|
||||||
|
|
||||||
#define LOG_PREFIX "fluke-dmm"
|
#ifndef LIBSIGROK_FLUKE_DMM_H
|
||||||
|
#define LIBSIGROK_FLUKE_DMM_H
|
||||||
|
|
||||||
#define FLUKEDMM_BUFSIZE 256
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "fluke-dmm: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
/* Always USB-serial, 1ms is plenty. */
|
#define FLUKEDMM_BUFSIZE 256
|
||||||
#define SERIAL_WRITE_TIMEOUT_MS 1
|
|
||||||
|
|
||||||
/* Supported models */
|
/* Supported models */
|
||||||
enum {
|
enum {
|
||||||
FLUKE_187 = 1,
|
FLUKE_187 = 1,
|
||||||
FLUKE_189,
|
|
||||||
FLUKE_287,
|
FLUKE_287,
|
||||||
FLUKE_190,
|
FLUKE_190,
|
||||||
FLUKE_289,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Supported device profiles */
|
/* Supported device profiles */
|
||||||
|
|
@ -46,21 +49,28 @@ struct flukedmm_profile {
|
||||||
int timeout;
|
int timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Private, per-device-instance driver context. */
|
||||||
struct dev_context {
|
struct dev_context {
|
||||||
const struct flukedmm_profile *profile;
|
const struct flukedmm_profile *profile;
|
||||||
struct sr_sw_limits limits;
|
uint64_t limit_samples;
|
||||||
|
uint64_t limit_msec;
|
||||||
|
|
||||||
|
/* Opaque pointer passed in by the frontend. */
|
||||||
|
void *cb_data;
|
||||||
|
|
||||||
|
/* Runtime. */
|
||||||
|
uint64_t num_samples;
|
||||||
char buf[FLUKEDMM_BUFSIZE];
|
char buf[FLUKEDMM_BUFSIZE];
|
||||||
int buflen;
|
int buflen;
|
||||||
int64_t cmd_sent_at;
|
int64_t cmd_sent_at;
|
||||||
int expect_response;
|
int expect_response;
|
||||||
int meas_type;
|
int meas_type;
|
||||||
int is_relative;
|
int is_relative;
|
||||||
enum sr_mq mq;
|
int mq;
|
||||||
enum sr_unit unit;
|
int unit;
|
||||||
enum sr_mqflag mqflags;
|
int mqflags;
|
||||||
};
|
};
|
||||||
|
|
||||||
SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data);
|
SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data);
|
||||||
|
|
||||||
#endif
|
#endif /* LIBSIGROK_FLUKE_DMM_H */
|
||||||
|
|
@ -17,258 +17,253 @@
|
||||||
* 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 <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <libsigrok/libsigrok.h>
|
#include "libsigrok.h"
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
#include "protocol.h"
|
#include "fluke-dmm.h"
|
||||||
|
|
||||||
static void handle_qm_18x(const struct sr_dev_inst *sdi, char **tokens)
|
|
||||||
|
static struct sr_datafeed_analog *handle_qm_18x(const struct sr_dev_inst *sdi,
|
||||||
|
char **tokens)
|
||||||
{
|
{
|
||||||
struct dev_context *devc;
|
struct sr_datafeed_analog *analog;
|
||||||
struct sr_datafeed_packet packet;
|
|
||||||
struct sr_datafeed_analog analog;
|
|
||||||
struct sr_analog_encoding encoding;
|
|
||||||
struct sr_analog_meaning meaning;
|
|
||||||
struct sr_analog_spec spec;
|
|
||||||
float fvalue;
|
float fvalue;
|
||||||
char *e, *u;
|
char *e, *u;
|
||||||
gboolean is_oor;
|
gboolean is_oor;
|
||||||
|
|
||||||
devc = sdi->priv;
|
(void)sdi;
|
||||||
|
|
||||||
if (strcmp(tokens[0], "QM") || !tokens[1])
|
if (strcmp(tokens[0], "QM") || !tokens[1])
|
||||||
return;
|
return NULL;
|
||||||
|
|
||||||
if ((e = strstr(tokens[1], "Out of range"))) {
|
if ((e = strstr(tokens[1], "Out of range"))) {
|
||||||
is_oor = TRUE;
|
is_oor = TRUE;
|
||||||
fvalue = -1;
|
fvalue = -1;
|
||||||
while (*e && *e != '.')
|
|
||||||
e++;
|
|
||||||
} else {
|
} else {
|
||||||
is_oor = FALSE;
|
is_oor = FALSE;
|
||||||
/* Delimit the float, since sr_atof_ascii() wants only
|
fvalue = strtof(tokens[1], &e);
|
||||||
* a valid float here. */
|
if (fvalue == 0.0 && e == tokens[1]) {
|
||||||
e = tokens[1];
|
|
||||||
while (*e && *e != ' ')
|
|
||||||
e++;
|
|
||||||
*e++ = '\0';
|
|
||||||
if (sr_atof_ascii(tokens[1], &fvalue) != SR_OK || fvalue == 0.0) {
|
|
||||||
/* Happens all the time, when switching modes. */
|
/* Happens all the time, when switching modes. */
|
||||||
sr_dbg("Invalid float.");
|
sr_dbg("Invalid float.");
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (*e && *e == ' ')
|
while(*e && *e == ' ')
|
||||||
e++;
|
e++;
|
||||||
|
|
||||||
/* TODO: Use proper 'digits' value for this device (and its modes). */
|
if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))))
|
||||||
sr_analog_init(&analog, &encoding, &meaning, &spec, 2);
|
return NULL;
|
||||||
analog.data = &fvalue;
|
if (!(analog->data = g_try_malloc(sizeof(float))))
|
||||||
analog.meaning->channels = sdi->channels;
|
return NULL;
|
||||||
analog.num_samples = 1;
|
analog->probes = sdi->probes;
|
||||||
|
analog->num_samples = 1;
|
||||||
if (is_oor)
|
if (is_oor)
|
||||||
fvalue = NAN;
|
*analog->data = NAN;
|
||||||
analog.meaning->mq = 0;
|
else
|
||||||
|
*analog->data = fvalue;
|
||||||
|
analog->mq = -1;
|
||||||
|
|
||||||
if ((u = strstr(e, "V DC")) || (u = strstr(e, "V AC"))) {
|
if ((u = strstr(e, "V DC")) || (u = strstr(e, "V AC"))) {
|
||||||
analog.meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
analog.meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
if (!is_oor && e[0] == 'm')
|
if (!is_oor && e[0] == 'm')
|
||||||
fvalue /= 1000;
|
*analog->data /= 1000;
|
||||||
/* This catches "V AC", "V DC" and "V AC+DC". */
|
/* This catches "V AC", "V DC" and "V AC+DC". */
|
||||||
if (strstr(u, "AC"))
|
if (strstr(u, "AC"))
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
||||||
if (strstr(u, "DC"))
|
if (strstr(u, "DC"))
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
} else if ((u = strstr(e, "dBV")) || (u = strstr(e, "dBm"))) {
|
} else if ((u = strstr(e, "dBV")) || (u = strstr(e, "dBm"))) {
|
||||||
analog.meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
if (u[2] == 'm')
|
if (u[2] == 'm')
|
||||||
analog.meaning->unit = SR_UNIT_DECIBEL_MW;
|
analog->unit = SR_UNIT_DECIBEL_MW;
|
||||||
else
|
else
|
||||||
analog.meaning->unit = SR_UNIT_DECIBEL_VOLT;
|
analog->unit = SR_UNIT_DECIBEL_VOLT;
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
||||||
} else if ((u = strstr(e, "Ohms"))) {
|
} else if ((u = strstr(e, "Ohms"))) {
|
||||||
analog.meaning->mq = SR_MQ_RESISTANCE;
|
analog->mq = SR_MQ_RESISTANCE;
|
||||||
analog.meaning->unit = SR_UNIT_OHM;
|
analog->unit = SR_UNIT_OHM;
|
||||||
if (is_oor)
|
if (is_oor)
|
||||||
fvalue = INFINITY;
|
*analog->data = INFINITY;
|
||||||
else if (e[0] == 'k')
|
else if (e[0] == 'k')
|
||||||
fvalue *= 1000;
|
*analog->data *= 1000;
|
||||||
else if (e[0] == 'M')
|
else if (e[0] == 'M')
|
||||||
fvalue *= 1000000;
|
*analog->data *= 1000000;
|
||||||
} else if (!strcmp(e, "nS")) {
|
} else if (!strcmp(e, "nS")) {
|
||||||
analog.meaning->mq = SR_MQ_CONDUCTANCE;
|
analog->mq = SR_MQ_CONDUCTANCE;
|
||||||
analog.meaning->unit = SR_UNIT_SIEMENS;
|
analog->unit = SR_UNIT_SIEMENS;
|
||||||
*((float *)analog.data) /= 1e+9;
|
*analog->data /= 1e+9;
|
||||||
} else if ((u = strstr(e, "Farads"))) {
|
} else if ((u = strstr(e, "Farads"))) {
|
||||||
analog.meaning->mq = SR_MQ_CAPACITANCE;
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
analog.meaning->unit = SR_UNIT_FARAD;
|
analog->unit = SR_UNIT_FARAD;
|
||||||
if (!is_oor) {
|
if (!is_oor) {
|
||||||
if (e[0] == 'm')
|
if (e[0] == 'm')
|
||||||
fvalue /= 1e+3;
|
*analog->data /= 1e+3;
|
||||||
else if (e[0] == 'u')
|
else if (e[0] == 'u')
|
||||||
fvalue /= 1e+6;
|
*analog->data /= 1e+6;
|
||||||
else if (e[0] == 'n')
|
else if (e[0] == 'n')
|
||||||
fvalue /= 1e+9;
|
*analog->data /= 1e+9;
|
||||||
}
|
}
|
||||||
} else if ((u = strstr(e, "Deg C")) || (u = strstr(e, "Deg F"))) {
|
} else if ((u = strstr(e, "Deg C")) || (u = strstr(e, "Deg F"))) {
|
||||||
analog.meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
if (u[4] == 'C')
|
if (u[4] == 'C')
|
||||||
analog.meaning->unit = SR_UNIT_CELSIUS;
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
else
|
else
|
||||||
analog.meaning->unit = SR_UNIT_FAHRENHEIT;
|
analog->unit = SR_UNIT_FAHRENHEIT;
|
||||||
} else if ((u = strstr(e, "A AC")) || (u = strstr(e, "A DC"))) {
|
} else if ((u = strstr(e, "A AC")) || (u = strstr(e, "A DC"))) {
|
||||||
analog.meaning->mq = SR_MQ_CURRENT;
|
analog->mq = SR_MQ_CURRENT;
|
||||||
analog.meaning->unit = SR_UNIT_AMPERE;
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
/* This catches "A AC", "A DC" and "A AC+DC". */
|
/* This catches "A AC", "A DC" and "A AC+DC". */
|
||||||
if (strstr(u, "AC"))
|
if (strstr(u, "AC"))
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
||||||
if (strstr(u, "DC"))
|
if (strstr(u, "DC"))
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
if (!is_oor) {
|
if (!is_oor) {
|
||||||
if (e[0] == 'm')
|
if (e[0] == 'm')
|
||||||
fvalue /= 1e+3;
|
*analog->data /= 1e+3;
|
||||||
else if (e[0] == 'u')
|
else if (e[0] == 'u')
|
||||||
fvalue /= 1e+6;
|
*analog->data /= 1e+6;
|
||||||
}
|
}
|
||||||
} else if ((u = strstr(e, "Hz"))) {
|
} else if ((u = strstr(e, "Hz"))) {
|
||||||
analog.meaning->mq = SR_MQ_FREQUENCY;
|
analog->mq = SR_MQ_FREQUENCY;
|
||||||
analog.meaning->unit = SR_UNIT_HERTZ;
|
analog->unit = SR_UNIT_HERTZ;
|
||||||
if (e[0] == 'k')
|
if (e[0] == 'k')
|
||||||
fvalue *= 1e+3;
|
*analog->data *= 1e+3;
|
||||||
} else if (!strcmp(e, "%")) {
|
} else if (!strcmp(e, "%")) {
|
||||||
analog.meaning->mq = SR_MQ_DUTY_CYCLE;
|
analog->mq = SR_MQ_DUTY_CYCLE;
|
||||||
analog.meaning->unit = SR_UNIT_PERCENTAGE;
|
analog->unit = SR_UNIT_PERCENTAGE;
|
||||||
} else if ((u = strstr(e, "ms"))) {
|
} else if ((u = strstr(e, "ms"))) {
|
||||||
analog.meaning->mq = SR_MQ_PULSE_WIDTH;
|
analog->mq = SR_MQ_PULSE_WIDTH;
|
||||||
analog.meaning->unit = SR_UNIT_SECOND;
|
analog->unit = SR_UNIT_SECOND;
|
||||||
fvalue /= 1e+3;
|
*analog->data /= 1e+3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analog.meaning->mq != 0) {
|
if (analog->mq == -1) {
|
||||||
/* Got a measurement. */
|
/* Not a valid measurement. */
|
||||||
packet.type = SR_DF_ANALOG;
|
g_free(analog->data);
|
||||||
packet.payload = &analog;
|
g_free(analog);
|
||||||
sr_session_send(sdi, &packet);
|
analog = NULL;
|
||||||
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return analog;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_qm_28x(const struct sr_dev_inst *sdi, char **tokens)
|
static struct sr_datafeed_analog *handle_qm_28x(const struct sr_dev_inst *sdi,
|
||||||
|
char **tokens)
|
||||||
{
|
{
|
||||||
struct dev_context *devc;
|
struct sr_datafeed_analog *analog;
|
||||||
struct sr_datafeed_packet packet;
|
|
||||||
struct sr_datafeed_analog analog;
|
|
||||||
struct sr_analog_encoding encoding;
|
|
||||||
struct sr_analog_meaning meaning;
|
|
||||||
struct sr_analog_spec spec;
|
|
||||||
float fvalue;
|
float fvalue;
|
||||||
|
char *eptr;
|
||||||
|
|
||||||
devc = sdi->priv;
|
(void)sdi;
|
||||||
|
|
||||||
if (!tokens[1])
|
if (!tokens[1])
|
||||||
return;
|
return NULL;
|
||||||
|
|
||||||
if (sr_atof_ascii(tokens[0], &fvalue) != SR_OK || fvalue == 0.0) {
|
fvalue = strtof(tokens[0], &eptr);
|
||||||
sr_err("Invalid float '%s'.", tokens[0]);
|
if (fvalue == 0.0 && eptr == tokens[0]) {
|
||||||
return;
|
sr_err("Invalid float.");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Use proper 'digits' value for this device (and its modes). */
|
if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))))
|
||||||
sr_analog_init(&analog, &encoding, &meaning, &spec, 2);
|
return NULL;
|
||||||
analog.data = &fvalue;
|
if (!(analog->data = g_try_malloc(sizeof(float))))
|
||||||
analog.meaning->channels = sdi->channels;
|
return NULL;
|
||||||
analog.num_samples = 1;
|
analog->probes = sdi->probes;
|
||||||
analog.meaning->mq = 0;
|
analog->num_samples = 1;
|
||||||
|
*analog->data = fvalue;
|
||||||
|
analog->mq = -1;
|
||||||
|
|
||||||
if (!strcmp(tokens[1], "VAC") || !strcmp(tokens[1], "VDC")) {
|
if (!strcmp(tokens[1], "VAC") || !strcmp(tokens[1], "VDC")) {
|
||||||
analog.meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
analog.meaning->unit = SR_UNIT_VOLT;
|
analog->unit = SR_UNIT_VOLT;
|
||||||
if (!strcmp(tokens[2], "NORMAL")) {
|
if (!strcmp(tokens[2], "NORMAL")) {
|
||||||
if (tokens[1][1] == 'A') {
|
if (tokens[1][1] == 'A') {
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_AC;
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_RMS;
|
analog->mqflags |= SR_MQFLAG_RMS;
|
||||||
} else
|
} else
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
} else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
|
} else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
|
||||||
fvalue = NAN;
|
*analog->data = NAN;
|
||||||
} else
|
} else
|
||||||
analog.meaning->mq = 0;
|
analog->mq = -1;
|
||||||
} else if (!strcmp(tokens[1], "dBV") || !strcmp(tokens[1], "dBm")) {
|
} else if (!strcmp(tokens[1], "dBV") || !strcmp(tokens[1], "dBm")) {
|
||||||
analog.meaning->mq = SR_MQ_VOLTAGE;
|
analog->mq = SR_MQ_VOLTAGE;
|
||||||
if (tokens[1][2] == 'm')
|
if (tokens[1][2] == 'm')
|
||||||
analog.meaning->unit = SR_UNIT_DECIBEL_MW;
|
analog->unit = SR_UNIT_DECIBEL_MW;
|
||||||
else
|
else
|
||||||
analog.meaning->unit = SR_UNIT_DECIBEL_VOLT;
|
analog->unit = SR_UNIT_DECIBEL_VOLT;
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
analog->mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
|
||||||
} else if (!strcmp(tokens[1], "CEL") || !strcmp(tokens[1], "FAR")) {
|
} else if (!strcmp(tokens[1], "CEL") || !strcmp(tokens[1], "FAR")) {
|
||||||
if (!strcmp(tokens[2], "NORMAL")) {
|
if (!strcmp(tokens[2], "NORMAL")) {
|
||||||
analog.meaning->mq = SR_MQ_TEMPERATURE;
|
analog->mq = SR_MQ_TEMPERATURE;
|
||||||
if (tokens[1][0] == 'C')
|
if (tokens[1][0] == 'C')
|
||||||
analog.meaning->unit = SR_UNIT_CELSIUS;
|
analog->unit = SR_UNIT_CELSIUS;
|
||||||
else
|
else
|
||||||
analog.meaning->unit = SR_UNIT_FAHRENHEIT;
|
analog->unit = SR_UNIT_FAHRENHEIT;
|
||||||
}
|
}
|
||||||
} else if (!strcmp(tokens[1], "OHM")) {
|
} else if (!strcmp(tokens[1], "OHM")) {
|
||||||
if (!strcmp(tokens[3], "NONE")) {
|
if (!strcmp(tokens[3], "NONE")) {
|
||||||
analog.meaning->mq = SR_MQ_RESISTANCE;
|
analog->mq = SR_MQ_RESISTANCE;
|
||||||
analog.meaning->unit = SR_UNIT_OHM;
|
analog->unit = SR_UNIT_OHM;
|
||||||
if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
|
if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
|
||||||
fvalue = INFINITY;
|
*analog->data = INFINITY;
|
||||||
} else if (strcmp(tokens[2], "NORMAL"))
|
} else if (strcmp(tokens[2], "NORMAL"))
|
||||||
analog.meaning->mq = 0;
|
analog->mq = -1;
|
||||||
} else if (!strcmp(tokens[3], "OPEN_CIRCUIT")) {
|
} else if (!strcmp(tokens[3], "OPEN_CIRCUIT")) {
|
||||||
analog.meaning->mq = SR_MQ_CONTINUITY;
|
analog->mq = SR_MQ_CONTINUITY;
|
||||||
analog.meaning->unit = SR_UNIT_BOOLEAN;
|
analog->unit = SR_UNIT_BOOLEAN;
|
||||||
fvalue = 0.0;
|
*analog->data = 0.0;
|
||||||
} else if (!strcmp(tokens[3], "SHORT_CIRCUIT")) {
|
} else if (!strcmp(tokens[3], "SHORT_CIRCUIT")) {
|
||||||
analog.meaning->mq = SR_MQ_CONTINUITY;
|
analog->mq = SR_MQ_CONTINUITY;
|
||||||
analog.meaning->unit = SR_UNIT_BOOLEAN;
|
analog->unit = SR_UNIT_BOOLEAN;
|
||||||
fvalue = 1.0;
|
*analog->data = 1.0;
|
||||||
}
|
}
|
||||||
} else if (!strcmp(tokens[1], "F")
|
} else if (!strcmp(tokens[1], "F")
|
||||||
&& !strcmp(tokens[2], "NORMAL")
|
&& !strcmp(tokens[2], "NORMAL")
|
||||||
&& !strcmp(tokens[3], "NONE")) {
|
&& !strcmp(tokens[3], "NONE")) {
|
||||||
analog.meaning->mq = SR_MQ_CAPACITANCE;
|
analog->mq = SR_MQ_CAPACITANCE;
|
||||||
analog.meaning->unit = SR_UNIT_FARAD;
|
analog->unit = SR_UNIT_FARAD;
|
||||||
} else if (!strcmp(tokens[1], "AAC") || !strcmp(tokens[1], "ADC")) {
|
} else if (!strcmp(tokens[1], "AAC") || !strcmp(tokens[1], "ADC")) {
|
||||||
analog.meaning->mq = SR_MQ_CURRENT;
|
analog->mq = SR_MQ_CURRENT;
|
||||||
analog.meaning->unit = SR_UNIT_AMPERE;
|
analog->unit = SR_UNIT_AMPERE;
|
||||||
if (!strcmp(tokens[2], "NORMAL")) {
|
if (!strcmp(tokens[2], "NORMAL")) {
|
||||||
if (tokens[1][1] == 'A') {
|
if (tokens[1][1] == 'A') {
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_AC;
|
analog->mqflags |= SR_MQFLAG_AC;
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_RMS;
|
analog->mqflags |= SR_MQFLAG_RMS;
|
||||||
} else
|
} else
|
||||||
analog.meaning->mqflags |= SR_MQFLAG_DC;
|
analog->mqflags |= SR_MQFLAG_DC;
|
||||||
} else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
|
} else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) {
|
||||||
fvalue = NAN;
|
*analog->data = NAN;
|
||||||
} else
|
} else
|
||||||
analog.meaning->mq = 0;
|
analog->mq = -1;
|
||||||
} else if (!strcmp(tokens[1], "Hz") && !strcmp(tokens[2], "NORMAL")) {
|
} if (!strcmp(tokens[1], "Hz") && !strcmp(tokens[2], "NORMAL")) {
|
||||||
analog.meaning->mq = SR_MQ_FREQUENCY;
|
analog->mq = SR_MQ_FREQUENCY;
|
||||||
analog.meaning->unit = SR_UNIT_HERTZ;
|
analog->unit = SR_UNIT_HERTZ;
|
||||||
} else if (!strcmp(tokens[1], "PCT") && !strcmp(tokens[2], "NORMAL")) {
|
} else if (!strcmp(tokens[1], "PCT") && !strcmp(tokens[2], "NORMAL")) {
|
||||||
analog.meaning->mq = SR_MQ_DUTY_CYCLE;
|
analog->mq = SR_MQ_DUTY_CYCLE;
|
||||||
analog.meaning->unit = SR_UNIT_PERCENTAGE;
|
analog->unit = SR_UNIT_PERCENTAGE;
|
||||||
} else if (!strcmp(tokens[1], "S") && !strcmp(tokens[2], "NORMAL")) {
|
} else if (!strcmp(tokens[1], "S") && !strcmp(tokens[2], "NORMAL")) {
|
||||||
analog.meaning->mq = SR_MQ_PULSE_WIDTH;
|
analog->mq = SR_MQ_PULSE_WIDTH;
|
||||||
analog.meaning->unit = SR_UNIT_SECOND;
|
analog->unit = SR_UNIT_SECOND;
|
||||||
} else if (!strcmp(tokens[1], "SIE") && !strcmp(tokens[2], "NORMAL")) {
|
} else if (!strcmp(tokens[1], "SIE") && !strcmp(tokens[2], "NORMAL")) {
|
||||||
analog.meaning->mq = SR_MQ_CONDUCTANCE;
|
analog->mq = SR_MQ_CONDUCTANCE;
|
||||||
analog.meaning->unit = SR_UNIT_SIEMENS;
|
analog->unit = SR_UNIT_SIEMENS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analog.meaning->mq != 0) {
|
if (analog->mq == -1) {
|
||||||
/* Got a measurement. */
|
/* Not a valid measurement. */
|
||||||
packet.type = SR_DF_ANALOG;
|
g_free(analog->data);
|
||||||
packet.payload = &analog;
|
g_free(analog);
|
||||||
sr_session_send(sdi, &packet);
|
analog = NULL;
|
||||||
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return analog;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens)
|
static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens)
|
||||||
|
|
@ -308,10 +303,8 @@ static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens)
|
||||||
return;
|
return;
|
||||||
meas_char = strtol(tokens[4], NULL, 10);
|
meas_char = strtol(tokens[4], NULL, 10);
|
||||||
|
|
||||||
devc->mq = 0;
|
devc->mq = devc->unit = -1;
|
||||||
devc->unit = 0;
|
|
||||||
devc->mqflags = 0;
|
devc->mqflags = 0;
|
||||||
|
|
||||||
switch (meas_unit) {
|
switch (meas_unit) {
|
||||||
case 1:
|
case 1:
|
||||||
devc->mq = SR_MQ_VOLTAGE;
|
devc->mq = SR_MQ_VOLTAGE;
|
||||||
|
|
@ -323,7 +316,7 @@ static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens)
|
||||||
else if (meas_char == 3)
|
else if (meas_char == 3)
|
||||||
devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
|
devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
|
||||||
else if (meas_char == 15)
|
else if (meas_char == 15)
|
||||||
devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
|
devc->mqflags |= SR_MQFLAG_DIODE;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
devc->mq = SR_MQ_CURRENT;
|
devc->mq = SR_MQ_CURRENT;
|
||||||
|
|
@ -355,7 +348,7 @@ static void handle_qm_19x_meta(const struct sr_dev_inst *sdi, char **tokens)
|
||||||
default:
|
default:
|
||||||
sr_dbg("unknown unit: %d", meas_unit);
|
sr_dbg("unknown unit: %d", meas_unit);
|
||||||
}
|
}
|
||||||
if (devc->mq == 0 && devc->unit == 0)
|
if (devc->mq == -1 && devc->unit == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If we got here, we know how to interpret the measurement. */
|
/* If we got here, we know how to interpret the measurement. */
|
||||||
|
|
@ -374,10 +367,8 @@ static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens)
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
struct sr_datafeed_packet packet;
|
struct sr_datafeed_packet packet;
|
||||||
struct sr_datafeed_analog analog;
|
struct sr_datafeed_analog analog;
|
||||||
struct sr_analog_encoding encoding;
|
|
||||||
struct sr_analog_meaning meaning;
|
|
||||||
struct sr_analog_spec spec;
|
|
||||||
float fvalue;
|
float fvalue;
|
||||||
|
char *eptr;
|
||||||
|
|
||||||
if (!strcmp(tokens[0], "9.9E+37")) {
|
if (!strcmp(tokens[0], "9.9E+37")) {
|
||||||
/* An invalid measurement shows up on the display as "OL", but
|
/* An invalid measurement shows up on the display as "OL", but
|
||||||
|
|
@ -385,17 +376,19 @@ static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens)
|
||||||
* is rather problematic, we'll cut through this here. */
|
* is rather problematic, we'll cut through this here. */
|
||||||
fvalue = NAN;
|
fvalue = NAN;
|
||||||
} else {
|
} else {
|
||||||
if (sr_atof_ascii(tokens[0], &fvalue) != SR_OK || fvalue == 0.0) {
|
fvalue = strtof(tokens[0], &eptr);
|
||||||
|
if (fvalue == 0.0 && eptr == tokens[0]) {
|
||||||
sr_err("Invalid float '%s'.", tokens[0]);
|
sr_err("Invalid float '%s'.", tokens[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
if (devc->mq == 0 || devc->unit == 0)
|
if (devc->mq == -1 || devc->unit == -1)
|
||||||
/* Don't have valid metadata yet. */
|
/* Don't have valid metadata yet. */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
if (devc->mq == SR_MQ_RESISTANCE && isnan(fvalue))
|
if (devc->mq == SR_MQ_RESISTANCE && isnan(fvalue))
|
||||||
fvalue = INFINITY;
|
fvalue = INFINITY;
|
||||||
else if (devc->mq == SR_MQ_CONTINUITY) {
|
else if (devc->mq == SR_MQ_CONTINUITY) {
|
||||||
|
|
@ -405,25 +398,25 @@ static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens)
|
||||||
fvalue = 1.0;
|
fvalue = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Use proper 'digits' value for this device (and its modes). */
|
analog.probes = sdi->probes;
|
||||||
sr_analog_init(&analog, &encoding, &meaning, &spec, 2);
|
|
||||||
analog.meaning->channels = sdi->channels;
|
|
||||||
analog.num_samples = 1;
|
analog.num_samples = 1;
|
||||||
analog.data = &fvalue;
|
analog.data = &fvalue;
|
||||||
analog.meaning->mq = devc->mq;
|
analog.mq = devc->mq;
|
||||||
analog.meaning->unit = devc->unit;
|
analog.unit = devc->unit;
|
||||||
analog.meaning->mqflags = 0;
|
analog.mqflags = 0;
|
||||||
packet.type = SR_DF_ANALOG;
|
packet.type = SR_DF_ANALOG;
|
||||||
packet.payload = &analog;
|
packet.payload = &analog;
|
||||||
sr_session_send(sdi, &packet);
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
devc->num_samples++;
|
||||||
|
|
||||||
sr_sw_limits_update_samples_read(&devc->limits, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_line(const struct sr_dev_inst *sdi)
|
static void handle_line(const struct sr_dev_inst *sdi)
|
||||||
{
|
{
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
struct sr_serial_dev_inst *serial;
|
struct sr_serial_dev_inst *serial;
|
||||||
|
struct sr_datafeed_packet packet;
|
||||||
|
struct sr_datafeed_analog *analog;
|
||||||
int num_tokens, n, i;
|
int num_tokens, n, i;
|
||||||
char cmd[16], **tokens;
|
char cmd[16], **tokens;
|
||||||
|
|
||||||
|
|
@ -441,14 +434,15 @@ static void handle_line(const struct sr_dev_inst *sdi)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
analog = NULL;
|
||||||
tokens = g_strsplit(devc->buf, ",", 0);
|
tokens = g_strsplit(devc->buf, ",", 0);
|
||||||
if (tokens[0]) {
|
if (tokens[0]) {
|
||||||
if (devc->profile->model == FLUKE_187 || devc->profile->model == FLUKE_189) {
|
if (devc->profile->model == FLUKE_187) {
|
||||||
devc->expect_response = FALSE;
|
devc->expect_response = FALSE;
|
||||||
handle_qm_18x(sdi, tokens);
|
analog = handle_qm_18x(sdi, tokens);
|
||||||
} else if (devc->profile->model == FLUKE_287 || devc->profile->model == FLUKE_289) {
|
} else if (devc->profile->model == FLUKE_287) {
|
||||||
devc->expect_response = FALSE;
|
devc->expect_response = FALSE;
|
||||||
handle_qm_28x(sdi, tokens);
|
analog = handle_qm_28x(sdi, tokens);
|
||||||
} else if (devc->profile->model == FLUKE_190) {
|
} else if (devc->profile->model == FLUKE_190) {
|
||||||
devc->expect_response = FALSE;
|
devc->expect_response = FALSE;
|
||||||
for (num_tokens = 0; tokens[num_tokens]; num_tokens++);
|
for (num_tokens = 0; tokens[num_tokens]; num_tokens++);
|
||||||
|
|
@ -464,8 +458,9 @@ static void handle_line(const struct sr_dev_inst *sdi)
|
||||||
/* Slip the request in now, before the main
|
/* Slip the request in now, before the main
|
||||||
* timer loop asks for metadata again. */
|
* timer loop asks for metadata again. */
|
||||||
n = sprintf(cmd, "QM %d\r", devc->meas_type);
|
n = sprintf(cmd, "QM %d\r", devc->meas_type);
|
||||||
if (serial_write_blocking(serial, cmd, n, SERIAL_WRITE_TIMEOUT_MS) < 0)
|
if (serial_write(serial, cmd, n) == -1)
|
||||||
sr_err("Unable to send QM (measurement).");
|
sr_err("Unable to send QM (measurement): %s.",
|
||||||
|
strerror(errno));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Response to QM <n> measurement request. */
|
/* Response to QM <n> measurement request. */
|
||||||
|
|
@ -475,6 +470,17 @@ static void handle_line(const struct sr_dev_inst *sdi)
|
||||||
}
|
}
|
||||||
g_strfreev(tokens);
|
g_strfreev(tokens);
|
||||||
devc->buflen = 0;
|
devc->buflen = 0;
|
||||||
|
|
||||||
|
if (analog) {
|
||||||
|
/* Got a measurement. */
|
||||||
|
packet.type = SR_DF_ANALOG;
|
||||||
|
packet.payload = analog;
|
||||||
|
sr_session_send(devc->cb_data, &packet);
|
||||||
|
devc->num_samples++;
|
||||||
|
g_free(analog->data);
|
||||||
|
g_free(analog);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
|
SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
|
||||||
|
|
@ -496,8 +502,8 @@ SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
|
||||||
serial = sdi->conn;
|
serial = sdi->conn;
|
||||||
if (revents == G_IO_IN) {
|
if (revents == G_IO_IN) {
|
||||||
/* Serial data arrived. */
|
/* Serial data arrived. */
|
||||||
while (FLUKEDMM_BUFSIZE - devc->buflen - 1 > 0) {
|
while(FLUKEDMM_BUFSIZE - devc->buflen - 1 > 0) {
|
||||||
len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
|
len = serial_read(serial, devc->buf + devc->buflen, 1);
|
||||||
if (len < 1)
|
if (len < 1)
|
||||||
break;
|
break;
|
||||||
devc->buflen++;
|
devc->buflen++;
|
||||||
|
|
@ -510,8 +516,8 @@ SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sr_sw_limits_check(&devc->limits)) {
|
if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
|
||||||
sr_dev_acquisition_stop(sdi);
|
sdi->driver->dev_acquisition_stop(sdi, cb_data);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -522,11 +528,13 @@ SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
|
||||||
* out-of-sync or temporary disconnect issues. */
|
* out-of-sync or temporary disconnect issues. */
|
||||||
if ((devc->expect_response == FALSE && elapsed > devc->profile->poll_period)
|
if ((devc->expect_response == FALSE && elapsed > devc->profile->poll_period)
|
||||||
|| elapsed > devc->profile->timeout) {
|
|| elapsed > devc->profile->timeout) {
|
||||||
if (serial_write_blocking(serial, "QM\r", 3, SERIAL_WRITE_TIMEOUT_MS) < 0)
|
if (serial_write(serial, "QM\r", 3) == -1)
|
||||||
sr_err("Unable to send QM.");
|
sr_err("Unable to send QM: %s.", strerror(errno));
|
||||||
devc->cmd_sent_at = now;
|
devc->cmd_sent_at = now;
|
||||||
devc->expect_response = TRUE;
|
devc->expect_response = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
##
|
||||||
|
## This file is part of the libsigrok project.
|
||||||
|
##
|
||||||
|
## 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/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
if LA_FX2LAFW
|
||||||
|
|
||||||
|
AM_CPPFLAGS = -DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
|
||||||
|
|
||||||
|
# Local lib, this is NOT meant to be installed!
|
||||||
|
noinst_LTLIBRARIES = libsigrokhwfx2lafw.la
|
||||||
|
|
||||||
|
libsigrokhwfx2lafw_la_SOURCES = \
|
||||||
|
command.c \
|
||||||
|
command.h \
|
||||||
|
fx2lafw.c \
|
||||||
|
fx2lafw.h
|
||||||
|
|
||||||
|
libsigrokhwfx2lafw_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
#include "libsigrok-internal.h"
|
||||||
|
#include "fx2lafw.h"
|
||||||
|
#include "command.h"
|
||||||
|
|
||||||
|
SR_PRIV int command_get_fw_version(libusb_device_handle *devhdl,
|
||||||
|
struct version_info *vi)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
|
||||||
|
LIBUSB_ENDPOINT_IN, CMD_GET_FW_VERSION, 0x0000, 0x0000,
|
||||||
|
(unsigned char *)vi, sizeof(struct version_info), 100);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Unable to get version info: %s.",
|
||||||
|
libusb_error_name(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int command_get_revid_version(libusb_device_handle *devhdl,
|
||||||
|
uint8_t *revid)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
|
||||||
|
LIBUSB_ENDPOINT_IN, CMD_GET_REVID_VERSION, 0x0000, 0x0000,
|
||||||
|
revid, 1, 100);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Unable to get REVID: %s.", libusb_error_name(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SR_PRIV int command_start_acquisition(libusb_device_handle *devhdl,
|
||||||
|
uint64_t samplerate, gboolean samplewide)
|
||||||
|
{
|
||||||
|
struct cmd_start_acquisition cmd;
|
||||||
|
int delay = 0, ret;
|
||||||
|
|
||||||
|
/* Compute the sample rate. */
|
||||||
|
if (samplewide && samplerate > MAX_16BIT_SAMPLE_RATE) {
|
||||||
|
sr_err("Unable to sample at %" PRIu64 "Hz "
|
||||||
|
"when collecting 16-bit samples.", samplerate);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((SR_MHZ(48) % samplerate) == 0) {
|
||||||
|
cmd.flags = CMD_START_FLAGS_CLK_48MHZ;
|
||||||
|
delay = SR_MHZ(48) / samplerate - 1;
|
||||||
|
if (delay > MAX_SAMPLE_DELAY)
|
||||||
|
delay = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delay == 0 && (SR_MHZ(30) % samplerate) == 0) {
|
||||||
|
cmd.flags = CMD_START_FLAGS_CLK_30MHZ;
|
||||||
|
delay = SR_MHZ(30) / samplerate - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_info("GPIF delay = %d, clocksource = %sMHz.", delay,
|
||||||
|
(cmd.flags & CMD_START_FLAGS_CLK_48MHZ) ? "48" : "30");
|
||||||
|
|
||||||
|
if (delay <= 0 || delay > MAX_SAMPLE_DELAY) {
|
||||||
|
sr_err("Unable to sample at %" PRIu64 "Hz.", samplerate);
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.sample_delay_h = (delay >> 8) & 0xff;
|
||||||
|
cmd.sample_delay_l = delay & 0xff;
|
||||||
|
|
||||||
|
/* Select the sampling width. */
|
||||||
|
cmd.flags |= samplewide ? CMD_START_FLAGS_SAMPLE_16BIT :
|
||||||
|
CMD_START_FLAGS_SAMPLE_8BIT;
|
||||||
|
|
||||||
|
/* Send the control message. */
|
||||||
|
ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
|
||||||
|
LIBUSB_ENDPOINT_OUT, CMD_START, 0x0000, 0x0000,
|
||||||
|
(unsigned char *)&cmd, sizeof(cmd), 100);
|
||||||
|
if (ret < 0) {
|
||||||
|
sr_err("Unable to send start command: %s.",
|
||||||
|
libusb_error_name(ret));
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SR_OK;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the libsigrok project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBSIGROK_HARDWARE_FX2LAFW_COMMAND_H
|
||||||
|
#define LIBSIGROK_HARDWARE_FX2LAFW_COMMAND_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include "libsigrok.h"
|
||||||
|
|
||||||
|
/* Protocol commands */
|
||||||
|
#define CMD_GET_FW_VERSION 0xb0
|
||||||
|
#define CMD_START 0xb1
|
||||||
|
#define CMD_GET_REVID_VERSION 0xb2
|
||||||
|
|
||||||
|
#define CMD_START_FLAGS_WIDE_POS 5
|
||||||
|
#define CMD_START_FLAGS_CLK_SRC_POS 6
|
||||||
|
|
||||||
|
#define CMD_START_FLAGS_SAMPLE_8BIT (0 << CMD_START_FLAGS_WIDE_POS)
|
||||||
|
#define CMD_START_FLAGS_SAMPLE_16BIT (1 << CMD_START_FLAGS_WIDE_POS)
|
||||||
|
|
||||||
|
#define CMD_START_FLAGS_CLK_30MHZ (0 << CMD_START_FLAGS_CLK_SRC_POS)
|
||||||
|
#define CMD_START_FLAGS_CLK_48MHZ (1 << CMD_START_FLAGS_CLK_SRC_POS)
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
struct version_info {
|
||||||
|
uint8_t major;
|
||||||
|
uint8_t minor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cmd_start_acquisition {
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t sample_delay_h;
|
||||||
|
uint8_t sample_delay_l;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
SR_PRIV int command_get_fw_version(libusb_device_handle *devhdl,
|
||||||
|
struct version_info *vi);
|
||||||
|
SR_PRIV int command_get_revid_version(libusb_device_handle *devhdl,
|
||||||
|
uint8_t *revid);
|
||||||
|
SR_PRIV int command_start_acquisition(libusb_device_handle *devhdl,
|
||||||
|
uint64_t samplerate, gboolean samplewide);
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,29 +18,29 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LIBSIGROK_HARDWARE_FX2LAFW_PROTOCOL_H
|
#ifndef LIBSIGROK_HARDWARE_FX2LAFW_FX2LAFW_H
|
||||||
#define LIBSIGROK_HARDWARE_FX2LAFW_PROTOCOL_H
|
#define LIBSIGROK_HARDWARE_FX2LAFW_FX2LAFW_H
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <libusb.h>
|
|
||||||
#include <libsigrok/libsigrok.h>
|
|
||||||
#include "libsigrok-internal.h"
|
|
||||||
|
|
||||||
#define LOG_PREFIX "fx2lafw"
|
/* Message logging helpers with subsystem-specific prefix string. */
|
||||||
|
#define LOG_PREFIX "fx2lafw: "
|
||||||
|
#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
|
||||||
|
#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
|
||||||
|
#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
|
||||||
|
|
||||||
#define USB_INTERFACE 0
|
#define USB_INTERFACE 0
|
||||||
#define USB_CONFIGURATION 1
|
#define USB_CONFIGURATION 1
|
||||||
#define NUM_TRIGGER_STAGES 4
|
#define NUM_TRIGGER_STAGES 4
|
||||||
|
#define TRIGGER_TYPE "01"
|
||||||
|
|
||||||
#define MAX_RENUM_DELAY_MS 3000
|
#define MAX_RENUM_DELAY_MS 3000
|
||||||
#define NUM_SIMUL_TRANSFERS 32
|
#define NUM_SIMUL_TRANSFERS 32
|
||||||
#define MAX_EMPTY_TRANSFERS (NUM_SIMUL_TRANSFERS * 2)
|
#define MAX_EMPTY_TRANSFERS (NUM_SIMUL_TRANSFERS * 2)
|
||||||
|
|
||||||
#define NUM_CHANNELS 16
|
|
||||||
|
|
||||||
#define FX2LAFW_REQUIRED_VERSION_MAJOR 1
|
#define FX2LAFW_REQUIRED_VERSION_MAJOR 1
|
||||||
|
|
||||||
#define MAX_8BIT_SAMPLE_RATE SR_MHZ(24)
|
#define MAX_8BIT_SAMPLE_RATE SR_MHZ(24)
|
||||||
|
|
@ -49,27 +49,12 @@
|
||||||
/* 6 delay states of up to 256 clock ticks */
|
/* 6 delay states of up to 256 clock ticks */
|
||||||
#define MAX_SAMPLE_DELAY (6 * 256)
|
#define MAX_SAMPLE_DELAY (6 * 256)
|
||||||
|
|
||||||
|
/* Software trigger implementation: positive values indicate trigger stage. */
|
||||||
|
#define TRIGGER_FIRED -1
|
||||||
|
|
||||||
#define DEV_CAPS_16BIT_POS 0
|
#define DEV_CAPS_16BIT_POS 0
|
||||||
#define DEV_CAPS_AX_ANALOG_POS 1
|
|
||||||
|
|
||||||
#define DEV_CAPS_16BIT (1 << DEV_CAPS_16BIT_POS)
|
#define DEV_CAPS_16BIT (1 << DEV_CAPS_16BIT_POS)
|
||||||
#define DEV_CAPS_AX_ANALOG (1 << DEV_CAPS_AX_ANALOG_POS)
|
|
||||||
|
|
||||||
/* Protocol commands */
|
|
||||||
#define CMD_GET_FW_VERSION 0xb0
|
|
||||||
#define CMD_START 0xb1
|
|
||||||
#define CMD_GET_REVID_VERSION 0xb2
|
|
||||||
|
|
||||||
#define CMD_START_FLAGS_CLK_CTL2_POS 4
|
|
||||||
#define CMD_START_FLAGS_WIDE_POS 5
|
|
||||||
#define CMD_START_FLAGS_CLK_SRC_POS 6
|
|
||||||
|
|
||||||
#define CMD_START_FLAGS_CLK_CTL2 (1 << CMD_START_FLAGS_CLK_CTL2_POS)
|
|
||||||
#define CMD_START_FLAGS_SAMPLE_8BIT (0 << CMD_START_FLAGS_WIDE_POS)
|
|
||||||
#define CMD_START_FLAGS_SAMPLE_16BIT (1 << CMD_START_FLAGS_WIDE_POS)
|
|
||||||
|
|
||||||
#define CMD_START_FLAGS_CLK_30MHZ (0 << CMD_START_FLAGS_CLK_SRC_POS)
|
|
||||||
#define CMD_START_FLAGS_CLK_48MHZ (1 << CMD_START_FLAGS_CLK_SRC_POS)
|
|
||||||
|
|
||||||
struct fx2lafw_profile {
|
struct fx2lafw_profile {
|
||||||
uint16_t vid;
|
uint16_t vid;
|
||||||
|
|
@ -82,14 +67,10 @@ struct fx2lafw_profile {
|
||||||
const char *firmware;
|
const char *firmware;
|
||||||
|
|
||||||
uint32_t dev_caps;
|
uint32_t dev_caps;
|
||||||
|
|
||||||
const char *usb_manufacturer;
|
|
||||||
const char *usb_product;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dev_context {
|
struct dev_context {
|
||||||
const struct fx2lafw_profile *profile;
|
const struct fx2lafw_profile *profile;
|
||||||
GSList *enabled_analog_channels;
|
|
||||||
/*
|
/*
|
||||||
* Since we can't keep track of an fx2lafw device after upgrading
|
* Since we can't keep track of an fx2lafw device after upgrading
|
||||||
* the firmware (it renumerates into a different device address
|
* the firmware (it renumerates into a different device address
|
||||||
|
|
@ -98,36 +79,25 @@ struct dev_context {
|
||||||
*/
|
*/
|
||||||
int64_t fw_updated;
|
int64_t fw_updated;
|
||||||
|
|
||||||
const uint64_t *samplerates;
|
/* Device/capture settings */
|
||||||
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;
|
|
||||||
|
|
||||||
gboolean trigger_fired;
|
/* Operational settings */
|
||||||
gboolean acq_aborted;
|
|
||||||
gboolean sample_wide;
|
gboolean sample_wide;
|
||||||
struct soft_trigger_logic *stl;
|
uint16_t trigger_mask[NUM_TRIGGER_STAGES];
|
||||||
|
uint16_t trigger_value[NUM_TRIGGER_STAGES];
|
||||||
|
int trigger_stage;
|
||||||
|
uint16_t trigger_buffer[NUM_TRIGGER_STAGES];
|
||||||
|
|
||||||
uint64_t num_frames;
|
int num_samples;
|
||||||
uint64_t sent_samples;
|
|
||||||
int submitted_transfers;
|
int submitted_transfers;
|
||||||
int empty_transfer_count;
|
int empty_transfer_count;
|
||||||
|
|
||||||
|
void *cb_data;
|
||||||
unsigned int num_transfers;
|
unsigned int num_transfers;
|
||||||
struct libusb_transfer **transfers;
|
struct libusb_transfer **transfers;
|
||||||
struct sr_context *ctx;
|
int *usbfd;
|
||||||
void (*send_data_proc)(struct sr_dev_inst *sdi,
|
|
||||||
uint8_t *data, size_t length, size_t sample_width);
|
|
||||||
uint8_t *logic_buffer;
|
|
||||||
float *analog_buffer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di);
|
|
||||||
SR_PRIV struct dev_context *fx2lafw_dev_new(void);
|
|
||||||
SR_PRIV int fx2lafw_start_acquisition(const struct sr_dev_inst *sdi);
|
|
||||||
SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue