Compare commits

..

No commits in common. "hw/sq50" and "dsupstream" have entirely different histories.

528 changed files with 30190 additions and 165950 deletions

117
.gitignore vendored
View File

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

View File

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

2663
Doxyfile

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

140
HACKING
View File

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

File diff suppressed because it is too large Load Diff

837
NEWS
View File

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

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

View File

@ -12,13 +12,9 @@ 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...

View File

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

404
backend.c Normal file
View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
%attributestring(sigrok::ConfigKey, std::string, identifier, identifier);

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
## ##
## This file is part of the libsigrok project. ## This file is part of the libsigrok project.
## ##
## Copyright (C) 2017 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"

View File

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

View File

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

View File

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

66
bindings/swig/libsigrok.i Normal file
View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

28
contrib/Makefile.am Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

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

265
contrib/z60_libsigrok.rules Normal file
View File

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

388
device.c Normal file
View File

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

View File

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

170
filter.c Normal file
View File

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

125
hardware/Makefile.am Normal file
View File

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

View File

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

View File

@ -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
/* Message logging helpers with subsystem-specific prefix string. */
#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)
#define MAX_CHANNELS 3
#define AGDMM_BUFSIZE 256 #define AGDMM_BUFSIZE 256
/* Always USB-serial, 1ms is plenty. */
#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 */

333
hardware/agilent-dmm/api.c Normal file
View File

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

View File

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

34
hardware/alsa/Makefile.am Normal file
View File

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

340
hardware/alsa/api.c Normal file
View File

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

418
hardware/alsa/protocol.c Normal file
View File

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

62
hardware/alsa/protocol.h Normal file
View File

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

View File

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

View File

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

View File

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

313
hardware/brymen-dmm/api.c Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
## ##
## This file is part of the libsigrok project. ## This file is part of the libsigrok project.
## ##
## Copyright (C) 2017 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

533
hardware/chronovu-la8/api.c Normal file
View File

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

View File

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

View File

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

View File

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

280
hardware/colead-slm/api.c Normal file
View File

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

View File

@ -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;
@ -96,42 +94,42 @@ static void process_packet(const struct sr_dev_inst *sdi)
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;
@ -139,45 +137,44 @@ static void process_packet(const struct sr_dev_inst *sdi)
/* 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;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

879
hardware/common/serial.c Normal file
View File

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

245
hardware/common/usb.c Normal file
View File

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

32
hardware/demo/Makefile.am Normal file
View File

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

545
hardware/demo/demo.c Normal file
View File

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

View File

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

374
hardware/fluke-dmm/api.c Normal file
View File

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

View File

@ -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
/* 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)
#define FLUKEDMM_BUFSIZE 256 #define FLUKEDMM_BUFSIZE 256
/* Always USB-serial, 1ms is plenty. */
#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 */

View File

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

View File

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

112
hardware/fx2lafw/command.c Normal file
View File

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

View File

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

1051
hardware/fx2lafw/fx2lafw.c Normal file

File diff suppressed because it is too large Load Diff

View File

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