[libsigrok] 03/12: New upstream version 0.4.0

Zoltan Gyarmati zgyarmati-guest at moszumanska.debian.org
Wed Mar 29 21:34:54 UTC 2017


This is an automated email from the git hooks/post-receive script.

zgyarmati-guest pushed a commit to branch master
in repository libsigrok.

commit 278340a28b2895db0793c09d71ffb44f410a70e5
Author: Zoltan Gyarmati <Zoltan Gyarmati mr.zoltan.gyarmati at gmail.com>
Date:   Sun Feb 26 20:06:38 2017 +0100

    New upstream version 0.4.0
---
 ChangeLog                                          | 10837 ++++++++++++++++-
 Doxyfile                                           |    48 +-
 HACKING                                            |    32 +-
 Makefile.am                                        |   802 +-
 Makefile.in                                        |  4039 ++++---
 NEWS                                               |   330 +-
 README                                             |    72 +-
 README.devices                                     |    11 +-
 aclocal.m4                                         |  1093 +-
 autostuff/ar-lib                                   |     2 +-
 autostuff/compile                                  |     2 +-
 autostuff/config.guess                             |   233 +-
 autostuff/config.sub                               |    60 +-
 autostuff/depcomp                                  |     2 +-
 autostuff/install-sh                               |   373 +-
 autostuff/ltmain.sh                                |     4 +-
 autostuff/missing                                  |     2 +-
 autostuff/test-driver                              |    15 +-
 bindings/cxx/ConfigKey_methods.cpp                 |   118 +
 bindings/cxx/ConfigKey_methods.hpp                 |    10 +
 bindings/cxx/ConfigKey_methods.i                   |     1 +
 Doxyfile => bindings/cxx/Doxyfile                  |    80 +-
 bindings/cxx/QuantityFlag_methods.cpp              |    21 +
 bindings/cxx/QuantityFlag_methods.hpp              |     7 +
 bindings/cxx/classes.cpp                           |  1477 +++
 bindings/cxx/enums.py                              |   175 +
 bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp |   998 ++
 bindings/cxx/libsigrokcxx.pc.in                    |    13 +
 Doxyfile => bindings/java/Doxyfile                 |    87 +-
 bindings/java/org/sigrok/core/classes/classes.i    |   378 +
 .../sigrok/core/interfaces/DatafeedCallback.java   |     9 +
 .../org/sigrok/core/interfaces/LogCallback.java    |     8 +
 Doxyfile => bindings/python/Doxyfile               |    89 +-
 bindings/python/setup.py                           |   100 +
 bindings/python/sigrok/__init__.py                 |    20 +
 bindings/python/sigrok/core/__init__.py            |    21 +
 bindings/python/sigrok/core/classes.i              |   551 +
 bindings/swig/classes.i                            |   220 +
 bindings/swig/doc.py                               |   114 +
 bindings/swig/templates.i                          |    96 +
 config.h.in                                        |   224 +-
 configure                                          | 11603 +++++++++++++++----
 configure.ac                                       |  1123 +-
 contrib/z60_libsigrok.rules                        |    33 +-
 device.c                                           |   530 -
 hardware/common/usb.c                              |   269 -
 hardware/serial-dmm/api.c                          |   632 -
 hardware/serial-dmm/protocol.h                     |   146 -
 hardware/sysclk-lwla/api.c                         |   612 -
 hardware/sysclk-lwla/lwla.h                        |   122 -
 hardware/sysclk-lwla/protocol.c                    |   985 --
 hardware/sysclk-lwla/protocol.h                    |   263 -
 hardware/uni-t-dmm/api.c                           |   425 -
 hwdriver.c                                         |   796 --
 libsigrok.h => include/libsigrok/libsigrok.h       |   731 +-
 include/libsigrok/proto.h                          |   246 +
 version.h => include/libsigrok/version.h.in        |    16 +-
 input/binary.c                                     |   151 -
 input/chronovu_la8.c                               |   206 -
 input/csv.c                                        |   868 --
 input/input.c                                      |    76 -
 input/vcd.c                                        |   546 -
 input/wav.c                                        |   204 -
 libsigrok-internal.h                               |   616 -
 libsigrok.pc.in                                    |     5 +-
 m4/ax_cxx_compile_stdcxx_11.m4                     |   142 +
 {autostuff => m4}/libtool.m4                       |     0
 {autostuff => m4}/ltoptions.m4                     |     0
 {autostuff => m4}/ltsugar.m4                       |     0
 {autostuff => m4}/ltversion.m4                     |     0
 {autostuff => m4}/lt~obsolete.m4                   |     0
 m4/sigrok.m4                                       |   449 +
 output/csv.c                                       |   220 -
 output/output.c                                    |   120 -
 proto.h                                            |   163 -
 session.c                                          |   835 --
 session_file.c                                     |   504 -
 src/analog.c                                       |   356 +
 backend.c => src/backend.c                         |   230 +-
 src/device.c                                       |   722 ++
 src/dmm/bm25x.c                                    |   215 +
 hardware/common/dmm/fs9721.c => src/dmm/dtm0660.c  |   242 +-
 {hardware/common => src}/dmm/es519xx.c             |    21 +-
 {hardware/common => src}/dmm/fs9721.c              |    21 +-
 {hardware/common => src}/dmm/fs9922.c              |    15 +-
 {hardware/common => src}/dmm/m2110.c               |     5 +-
 {hardware/common => src}/dmm/metex14.c             |    58 +-
 {hardware/common => src}/dmm/rs9lcd.c              |     7 +-
 src/dmm/ut372.c                                    |   136 +
 src/dmm/ut71x.c                                    |   352 +
 src/dmm/vc870.c                                    |   439 +
 src/drivers.c                                      |   351 +
 error.c => src/error.c                             |    11 +-
 {hardware/common => src}/ezusb.c                   |    60 +-
 .../protocol.h => src/fallback.c                   |    30 +-
 .../hardware}/agilent-dmm/agilent-dmm.h            |    19 +-
 {hardware => src/hardware}/agilent-dmm/api.c       |   123 +-
 {hardware => src/hardware}/agilent-dmm/sched.c     |   197 +-
 {hardware => src/hardware}/appa-55ii/api.c         |    84 +-
 {hardware => src/hardware}/appa-55ii/protocol.c    |    15 +-
 {hardware => src/hardware}/appa-55ii/protocol.h    |     2 +-
 src/hardware/asix-sigma/api.c                      |   445 +
 .../hardware/asix-sigma/protocol.c                 |   700 +-
 .../hardware/asix-sigma/protocol.h                 |    34 +-
 {hardware => src/hardware}/atten-pps3xxx/api.c     |   140 +-
 .../hardware}/atten-pps3xxx/protocol.c             |    17 +-
 .../hardware}/atten-pps3xxx/protocol.h             |     8 +-
 src/hardware/baylibre-acme/api.c                   |   447 +
 src/hardware/baylibre-acme/gpio.c                  |   166 +
 src/hardware/baylibre-acme/gpio.h                  |    41 +
 src/hardware/baylibre-acme/protocol.c              |   804 ++
 src/hardware/baylibre-acme/protocol.h              |    97 +
 src/hardware/beaglelogic/api.c                     |   426 +
 src/hardware/beaglelogic/beaglelogic.h             |   210 +
 src/hardware/beaglelogic/protocol.c                |   112 +
 src/hardware/beaglelogic/protocol.h                |    71 +
 {hardware => src/hardware}/brymen-bm86x/api.c      |    87 +-
 {hardware => src/hardware}/brymen-bm86x/protocol.c |    22 +-
 {hardware => src/hardware}/brymen-bm86x/protocol.h |     3 +-
 {hardware => src/hardware}/brymen-dmm/api.c        |    68 +-
 {hardware => src/hardware}/brymen-dmm/parser.c     |     6 +-
 {hardware => src/hardware}/brymen-dmm/protocol.c   |    23 +-
 {hardware => src/hardware}/brymen-dmm/protocol.h   |     4 +-
 {hardware => src/hardware}/cem-dt-885x/api.c       |   160 +-
 {hardware => src/hardware}/cem-dt-885x/protocol.c  |    40 +-
 {hardware => src/hardware}/cem-dt-885x/protocol.h  |     6 +-
 {hardware => src/hardware}/center-3xx/api.c        |    89 +-
 {hardware => src/hardware}/center-3xx/protocol.c   |    24 +-
 {hardware => src/hardware}/center-3xx/protocol.h   |    13 +-
 {hardware => src/hardware}/chronovu-la/api.c       |   276 +-
 {hardware => src/hardware}/chronovu-la/protocol.c  |    84 +-
 {hardware => src/hardware}/chronovu-la/protocol.h  |     7 +-
 {hardware => src/hardware}/colead-slm/api.c        |    80 +-
 {hardware => src/hardware}/colead-slm/protocol.c   |   173 +-
 {hardware => src/hardware}/colead-slm/protocol.h   |     2 +-
 .../hardware}/conrad-digi-35-cpu/api.c             |    65 +-
 .../hardware}/conrad-digi-35-cpu/protocol.c        |     8 +-
 .../hardware}/conrad-digi-35-cpu/protocol.h        |     3 +-
 {hardware => src/hardware}/demo/demo.c             |   585 +-
 src/hardware/deree-de5000/api.c                    |    86 +
 {hardware => src/hardware}/fluke-dmm/api.c         |    96 +-
 {hardware => src/hardware}/fluke-dmm/fluke-dmm.h   |     3 +
 {hardware => src/hardware}/fluke-dmm/fluke.c       |    52 +-
 {hardware => src/hardware}/fx2lafw/api.c           |   429 +-
 src/hardware/fx2lafw/dslogic.c                     |   238 +
 src/hardware/fx2lafw/dslogic.h                     |   132 +
 {hardware => src/hardware}/fx2lafw/protocol.c      |   332 +-
 {hardware => src/hardware}/fx2lafw/protocol.h      |    42 +-
 {hardware => src/hardware}/gmc-mh-1x-2x/api.c      |   192 +-
 {hardware => src/hardware}/gmc-mh-1x-2x/protocol.c |   165 +-
 {hardware => src/hardware}/gmc-mh-1x-2x/protocol.h |    22 +-
 src/hardware/gwinstek-gds-800/api.c                |   282 +
 src/hardware/gwinstek-gds-800/protocol.c           |   283 +
 src/hardware/gwinstek-gds-800/protocol.h           |    63 +
 {hardware => src/hardware}/hameg-hmo/api.c         |   169 +-
 {hardware => src/hardware}/hameg-hmo/protocol.c    |   385 +-
 {hardware => src/hardware}/hameg-hmo/protocol.h    |    16 +-
 {hardware => src/hardware}/hantek-dso/api.c        |   589 +-
 {hardware => src/hardware}/hantek-dso/dso.c        |   102 +-
 {hardware => src/hardware}/hantek-dso/dso.h        |    18 +-
 src/hardware/hung-chang-dso-2100/api.c             |   764 ++
 src/hardware/hung-chang-dso-2100/protocol.c        |   464 +
 src/hardware/hung-chang-dso-2100/protocol.h        |    69 +
 .../hardware}/ikalogic-scanalogic2/api.c           |   130 +-
 .../hardware}/ikalogic-scanalogic2/protocol.c      |    94 +-
 .../hardware}/ikalogic-scanalogic2/protocol.h      |    12 +-
 .../hardware}/ikalogic-scanaplus/api.c             |    83 +-
 .../hardware}/ikalogic-scanaplus/protocol.c        |     1 +
 .../hardware}/ikalogic-scanaplus/protocol.h        |     4 +-
 {hardware => src/hardware}/kecheng-kc-330b/api.c   |    90 +-
 .../hardware}/kecheng-kc-330b/protocol.c           |    16 +-
 .../hardware}/kecheng-kc-330b/protocol.h           |     6 +-
 src/hardware/kern-scale/api.c                      |   267 +
 src/hardware/kern-scale/protocol.c                 |   141 +
 .../hardware/kern-scale}/protocol.h                |    70 +-
 src/hardware/korad-kaxxxxp/api.c                   |   436 +
 src/hardware/korad-kaxxxxp/protocol.c              |   406 +
 src/hardware/korad-kaxxxxp/protocol.h              |   120 +
 {hardware => src/hardware}/lascar-el-usb/api.c     |    93 +-
 .../hardware}/lascar-el-usb/protocol.c             |    98 +-
 .../hardware}/lascar-el-usb/protocol.h             |    15 +-
 src/hardware/lecroy-logicstudio/api.c              |   559 +
 src/hardware/lecroy-logicstudio/protocol.c         |  1189 ++
 src/hardware/lecroy-logicstudio/protocol.h         |   107 +
 src/hardware/manson-hcs-3xxx/api.c                 |   440 +
 src/hardware/manson-hcs-3xxx/protocol.c            |   261 +
 src/hardware/manson-hcs-3xxx/protocol.h            |    99 +
 src/hardware/maynuo-m97/api.c                      |   521 +
 src/hardware/maynuo-m97/protocol.c                 |   212 +
 src/hardware/maynuo-m97/protocol.h                 |   164 +
 {hardware => src/hardware}/mic-985xx/api.c         |   114 +-
 {hardware => src/hardware}/mic-985xx/protocol.c    |    24 +-
 {hardware => src/hardware}/mic-985xx/protocol.h    |    13 +-
 src/hardware/motech-lps-30x/api.c                  |   855 ++
 src/hardware/motech-lps-30x/protocol.c             |   242 +
 src/hardware/motech-lps-30x/protocol.h             |   119 +
 {hardware => src/hardware}/norma-dmm/api.c         |   153 +-
 {hardware => src/hardware}/norma-dmm/protocol.c    |    51 +-
 {hardware => src/hardware}/norma-dmm/protocol.h    |    11 +-
 .../hardware}/openbench-logic-sniffer/api.c        |   214 +-
 .../hardware}/openbench-logic-sniffer/protocol.c   |   157 +-
 .../hardware}/openbench-logic-sniffer/protocol.h   |    27 +-
 .../hardware/pipistrello-ols}/api.c                |   508 +-
 src/hardware/pipistrello-ols/protocol.c            |   682 ++
 .../hardware/pipistrello-ols}/protocol.h           |    69 +-
 {hardware => src/hardware}/rigol-ds/api.c          |   340 +-
 {hardware => src/hardware}/rigol-ds/protocol.c     |   462 +-
 {hardware => src/hardware}/rigol-ds/protocol.h     |    15 +-
 {hardware => src/hardware}/saleae-logic16/api.c    |   203 +-
 .../hardware}/saleae-logic16/protocol.c            |   536 +-
 .../hardware}/saleae-logic16/protocol.h            |    27 +-
 src/hardware/scpi-pps/api.c                        |   663 ++
 src/hardware/scpi-pps/profiles.c                   |   549 +
 src/hardware/scpi-pps/protocol.c                   |   122 +
 src/hardware/scpi-pps/protocol.h                   |   161 +
 src/hardware/serial-dmm/api.c                      |   615 +
 {hardware => src/hardware}/serial-dmm/protocol.c   |   150 +-
 src/hardware/serial-dmm/protocol.h                 |    88 +
 src/hardware/sysclk-lwla/api.c                     |   824 ++
 {hardware => src/hardware}/sysclk-lwla/lwla.c      |   139 +-
 src/hardware/sysclk-lwla/lwla.h                    |   168 +
 src/hardware/sysclk-lwla/lwla1016.c                |   477 +
 src/hardware/sysclk-lwla/lwla1034.c                |   602 +
 src/hardware/sysclk-lwla/protocol.c                |   590 +
 src/hardware/sysclk-lwla/protocol.h                |   170 +
 {hardware => src/hardware}/teleinfo/api.c          |   121 +-
 {hardware => src/hardware}/teleinfo/protocol.c     |    16 +-
 {hardware => src/hardware}/teleinfo/protocol.h     |     4 +-
 src/hardware/testo/api.c                           |   540 +
 src/hardware/testo/protocol.c                      |   291 +
 src/hardware/testo/protocol.h                      |    84 +
 {hardware => src/hardware}/tondaj-sl-814/api.c     |    70 +-
 .../hardware}/tondaj-sl-814/protocol.c             |    23 +-
 .../hardware}/tondaj-sl-814/protocol.h             |     2 +-
 src/hardware/uni-t-dmm/api.c                       |   425 +
 {hardware => src/hardware}/uni-t-dmm/protocol.c    |    77 +-
 {hardware => src/hardware}/uni-t-dmm/protocol.h    |    44 +-
 {hardware => src/hardware}/uni-t-ut32x/api.c       |   100 +-
 {hardware => src/hardware}/uni-t-ut32x/protocol.c  |    22 +-
 {hardware => src/hardware}/uni-t-ut32x/protocol.h  |     6 +-
 {hardware => src/hardware}/victor-dmm/api.c        |   143 +-
 {hardware => src/hardware}/victor-dmm/protocol.c   |    77 +-
 {hardware => src/hardware}/victor-dmm/protocol.h   |     2 +-
 src/hardware/yokogawa-dlm/api.c                    |   719 ++
 src/hardware/yokogawa-dlm/protocol.c               |  1188 ++
 src/hardware/yokogawa-dlm/protocol.h               |   127 +
 src/hardware/yokogawa-dlm/protocol_wrappers.c      |   359 +
 src/hardware/yokogawa-dlm/protocol_wrappers.h      |    91 +
 .../hardware}/zeroplus-logic-cube/analyzer.c       |    64 +-
 .../hardware}/zeroplus-logic-cube/analyzer.h       |    12 +-
 .../hardware}/zeroplus-logic-cube/api.c            |   269 +-
 .../hardware}/zeroplus-logic-cube/gl_usb.c         |    15 +-
 .../hardware}/zeroplus-logic-cube/gl_usb.h         |     2 +-
 .../hardware}/zeroplus-logic-cube/protocol.c       |     7 +-
 .../hardware}/zeroplus-logic-cube/protocol.h       |     3 +-
 src/hwdriver.c                                     |   902 ++
 src/input/binary.c                                 |   173 +
 src/input/chronovu_la8.c                           |   187 +
 src/input/csv.c                                    |   815 ++
 src/input/input.c                                  |   586 +
 src/input/raw_analog.c                             |   283 +
 src/input/trace32_ad.c                             |   803 ++
 src/input/vcd.c                                    |   647 ++
 src/input/wav.c                                    |   372 +
 src/lcr/es51919.c                                  |   946 ++
 src/libsigrok-internal.h                           |  1257 ++
 log.c => src/log.c                                 |   155 +-
 src/modbus/modbus.c                                |   578 +
 src/modbus/modbus_serial_rtu.c                     |   209 +
 {output => src/output}/analog.c                    |   180 +-
 {output => src/output}/ascii.c                     |    71 +-
 {output => src/output}/binary.c                    |    13 +-
 {output => src/output}/bits.c                      |    69 +-
 {output => src/output}/chronovu_la8.c              |    29 +-
 src/output/csv.c                                   |   358 +
 {output => src/output}/gnuplot.c                   |    37 +-
 {output => src/output}/hex.c                       |    70 +-
 {output => src/output}/ols.c                       |    29 +-
 src/output/output.c                                |   361 +
 src/output/srzip.c                                 |   334 +
 {output => src/output}/vcd.c                       |    39 +-
 src/output/wav.c                                   |   386 +
 src/resource.c                                     |   380 +
 src/scale/kern.c                                   |   218 +
 src/scpi.h                                         |   143 +
 src/scpi/helpers.c                                 |   136 +
 {hardware/common => src/scpi}/scpi.c               |   197 +-
 src/scpi/scpi_libgpib.c                            |   167 +
 {hardware/common => src/scpi}/scpi_serial.c        |    41 +-
 {hardware/common => src/scpi}/scpi_tcp.c           |    39 +-
 {hardware/common => src/scpi}/scpi_usbtmc_libusb.c |   217 +-
 {hardware/common => src/scpi}/scpi_visa.c          |    25 +-
 {hardware/common => src/scpi}/scpi_vxi.c           |    53 +-
 {hardware/common => src/scpi}/vxi.h                |     2 +
 {hardware/common => src/scpi}/vxi_clnt.c           |     3 +-
 {hardware/common => src/scpi}/vxi_xdr.c            |     1 +
 {hardware/common => src}/serial.c                  |   535 +-
 src/session.c                                      |  1628 +++
 session_driver.c => src/session_driver.c           |   267 +-
 src/session_file.c                                 |   322 +
 src/soft-trigger.c                                 |   222 +
 std.c => src/std.c                                 |    28 +-
 strutil.c => src/strutil.c                         |   157 +-
 src/transform/invert.c                             |    99 +
 tests/check_output_all.c => src/transform/nop.c    |    48 +-
 src/transform/scale.c                              |   134 +
 src/transform/transform.c                          |   287 +
 src/trigger.c                                      |   181 +
 src/usb.c                                          |   512 +
 version.c => src/version.c                         |     3 +-
 tests/analog.c                                     |   216 +
 tests/{check_core.c => core.c}                     |     3 +-
 tests/device.c                                     |    81 +
 tests/{check_driver_all.c => driver_all.c}         |    27 +-
 tests/{check_input_all.c => input_all.c}           |     7 +-
 tests/{check_input_binary.c => input_binary.c}     |   141 +-
 tests/lib.c                                        |   118 +-
 tests/lib.h                                        |    20 +-
 tests/{check_main.c => main.c}                     |     8 +-
 tests/output_all.c                                 |   117 +
 tests/session.c                                    |   214 +
 tests/{check_strutil.c => strutil.c}               |    23 +-
 tests/transform_all.c                              |   117 +
 tests/trigger.c                                    |   281 +
 tests/{check_version.c => version.c}               |     3 +-
 version.h.in                                       |    69 -
 326 files changed, 76957 insertions(+), 21760 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a42baf6..33cb561 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,10839 @@
-commit 8f54ac8be1768261e113cb15fcdf81013f712674
+commit c5190cca26932c50bfa0e918681bd7b2dcc557eb
 Author: Uwe Hermann <uwe at hermann-uwe.de>
-Date:   Tue May 6 23:01:26 2014 +0200
+Date:   Fri Jan 29 23:37:35 2016 +0100
 
-    Doxyfile/Doxyfile_internal: Bump version number to 0.3.0.
+    Doxyfile: Set version to 0.4.0.
+
+commit 89c82260b0bbfa4885b2b1d737d825c4bdeda3c9
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Jan 29 22:57:48 2016 +0100
+
+    Bump libtool version (not package version) to 3:0:0.
+    
+    The last release (0.3.0) had the libtool version (current:revision:age)
+    set to 2:0:0. Since this release adds, removes, and changes interfaces,
+    the new version is 3:0:0.
+    
+    http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
+    
+    This changes the library filename (e.g. on Linux) from libsigrok.so.2.0.0
+    to libsigrok.so.3.0.0, the SONAME (+symlink) becomes libsigrok.so.3.
+
+commit 18b9bbb39cd236dd05acc40e21f5a1a16aad2e5d
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Aug 17 00:48:13 2015 +0200
+
+    NEWS: Update for upcoming 0.4.0 release.
+
+commit 10c4ca9c5bd9b2e917759e9fddc6f8655895b92c
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Jan 29 23:13:32 2016 +0100
+
+    hameg-hmo/yokogawa-dlm: Fix warning about pointer typecast
+
+commit a084a8f2a4fd5a48e659e36ae47bc393fd9d6ffc
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Jan 29 21:59:06 2016 +0100
+
+    SCPI: Do not use RL1 lockout for Yokogawa devices
+
+commit b18b8a92028475ca3d9ab33c06248d37075837be
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Jan 29 21:39:20 2016 +0100
+
+    yokogawa-dlm: Fix array_float_get()
+
+commit 8cccbac8da97397b61aa094c67e62ee922b628ed
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Jan 29 21:35:16 2016 +0100
+
+    hameg-hmo: Fix array_float_get() and also use it for the time base
+
+commit fe227d17aeda0c460367883f57c3b683fe4798a6
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Jan 29 21:13:07 2016 +0100
+
+    hameg-hmo: Add missing 20/50V vdiv entries
+
+commit e786b19467f2a77c492baafee2705c9f0edcee47
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Jan 29 21:08:49 2016 +0100
+
+    hameg-hmo: Add more supported scope models
+
+commit b90400ed89b6bdb4b0ffaed2fc3acb768ee3ca40
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Jan 29 18:31:00 2016 +0100
+
+    README: Bump bindings requirements to the upcoming 0.4.0 release.
+
+commit db5449b918ee51c73d04bf3fded6d5b8c49b96df
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Jan 29 18:30:32 2016 +0100
+
+    README: Mention the missing libieee1284 requirement.
+
+commit b165a24234b44a39e87661132ee0c92b49dac5ea
+Author: Lars-Peter Clausen <lars at metafoo.de>
+Date:   Sat Jan 23 14:21:15 2016 +0100
+
+    hung-chang-dso-2100: Fix session source fd
+    
+    For session sources without a file descriptor to poll a negative number
+    should be passed for the fd parameter. The hung-chang-dso-2100 driver
+    currently passes 0 instead, which is the stdin stream. Fix the issue by
+    passing -1 for the fd parameter.
+    
+    Signed-off-by: Lars-Peter Clausen <lars at metafoo.de>
+
+commit eb8e6cd2cbcb9c552418dcd0b7973b38b88c805f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Jan 29 18:00:11 2016 +0100
+
+    Use libusb_error_name() more often for better diagnostics.
+
+commit 9dfacd870618806defe64a0cbe1ccd330e11cb3d
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Jan 28 23:17:50 2016 +0100
+
+    demo: fix infinite loop with 0 channels of one type
+    
+    The following command line exhibits the inifinit loop:
+      sigrok-cli -d demo:analog_channels=0 --samples=8
+
+commit e9227902208d2ab96349b60788596dfc2222941a
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Jan 28 22:48:44 2016 +0100
+
+    configure: replace buggy AX_RUBY_EXT by a proper implementation
+    
+    This actually allows to get the proper DLEXT expected by Ruby on
+    the target system.
+
+commit e469259a577069529e60c9741f098a0c5c56f813
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Jan 28 22:45:28 2016 +0100
+
+    configure: check for swig version >= 3.0.8 for Ruby bindings
+
+commit 3f48fc82b5bc13e749b30a47dd6ca06f8cd2b00e
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sun Jan 10 12:10:10 2016 +0100
+
+    input: vcd: by default do not limit number of channels
+    
+    Accept (and set to default) 0 for numchannels which means 'unlimited'.
+    I think it is convenient to read all channels of a vcd file by default.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit ab464eb3dc7fc9646592d882ca79aa0a8fa5101b
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sun Jan 10 12:08:54 2016 +0100
+
+    input: vcd: register channels when parsing header not when initializing
+    
+    Benefits:
+    
+    * only channels really used in vcd will be added
+    * we can give them the proper name found in the vcd file
+    * less code :)
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 34724ffa340b4df9706aa02d686563ed72b7840f
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sat Jan 9 23:17:02 2016 +0100
+
+    input: vcd: support 1 bit vectors
+    
+    Use the new process_bit() function to parse 1 bit vectors, too.
+    
+    This is the first step to fix bug #723. Minimal testcase vcd:
+    
+    $timescale 1 ns $end
+    $var wire 1 n0 addr_0 $end
+    $var wire 1 n1 addr_1 $end
+    $enddefinitions $end
+    #0
+    0n0
+    b1 n1
+    #1
+    1n0
+    b0 n1
+    #2
+    0n0
+    b1 n1
+    #3
+    1n0
+    b0 n1
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 36dacf17bc5fe333c6557d073011e2033b6f544f
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sat Jan 9 23:05:58 2016 +0100
+
+    input: vcd: refactor parsing a bit
+    
+    Move to a separate function which we want to reuse later.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit a66175c2b1e11c961329aa2634fa9de7f3a45d5a
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sat Jan 9 08:17:57 2016 +0100
+
+    input: vcd: avoid needless copying
+    
+    Current code moves the identifier string one byte to the front to
+    overwrite the bit value, so 'tokens[i]' is a string to compare against
+    the desired value. This copying is unnecessary, just pass a properly
+    setup pointer.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 76bc28c3f1cbd88f01eda7251e9f4f1d88c506f2
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sat Jan 9 18:27:14 2016 +0100
+
+    input: vcd: try to continue when a vector was found
+    
+    No need to bail out on vectors. As long as there are tokens left, we can
+    try to parse the rest. Also, print a message so the user knows what's
+    going on. Here is a testcase vcd:
+    
+    $timescale 1 ns $end
+    $var wire 1 n0 addr_0 $end
+    $var wire 1 n1 addr_1 $end
+    $enddefinitions $end
+    #0
+    0n0
+    b1 n1
+    #1
+    1n0
+    b0 n1
+    #2
+    0n0
+    b1 n1
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 73f052d329574e6fa9fc8bebcc7682b120da5bab
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sat Jan 9 17:40:10 2016 +0100
+
+    input: vcd: properly bail out on missing identifiers
+    
+    If we hit the missing identifier case, then we reached the end of the
+    token list. So, we should break out of the loop, and not continue.
+    Otherwise we will go past the end of the array as this minimal testcase
+    shows:
+    
+    $timescale 1 ns $end
+    $var wire 1 n0 addr_0 $end
+    $enddefinitions $end
+    1
+    
+    gives:
+    
+    $ ./sigrok-cli -I vcd -i no_mod.vcd -O vcd -o /tmp/o.vcd
+    Segmentation fault
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit e85e550d92e680f19c9be759dfc2b29ce72ae7c5
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Wed Dec 30 22:04:38 2015 +0100
+
+    input: vcd: allow optional index item
+    
+    A $var block can have an optional index item which looks like '[<sth>]'.
+    Parse it, too, and append it to the channel name.
+    
+    This fixes bug #322. A first version was posted by Simon Richter. This
+    version is rebased and simplified.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 650847e7d3caf097042397bd4522a7a393cb89ab
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Jan 28 18:46:55 2016 +0100
+
+    hameg-hmo: Fix some compiler warnings.
+
+commit 8fff75196f79d60369cc05545409fc567c6b7bf4
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Jan 8 23:02:20 2016 +0100
+
+    hameg-hmo: Replace floating point comparison for vdiv
+    
+    This should fix bug #731.
+
+commit a5be5d5bc008ebdab19b7ed2efe5e5a5b99bb349
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Jan 8 23:00:55 2016 +0100
+
+    Trace32 import module: Send trigger only once
+    
+    As we're downsampling, several record time stamps can match the specified
+    trigger time. For this reason, it's possible that several trigger packets
+    are sent when a file is loaded. This prevents the issue and sends a
+    trigger packet only on the first matching record.
+
+commit 27d44cf6e0eaaa75979510596d6148193a8434c0
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Jan 11 14:31:47 2016 +0100
+
+    Implement Ruby bindings on top of SWIG/C++ bindings.
+
+commit 0441038e2f37da659cf46a1878fc6906926425d6
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Jan 11 14:32:22 2016 +0100
+
+    Makefile: use PYSIGROK_FLAGS only for building python bindings
+
+commit 1b4aedc06f4ea5e63c604bf37e5c95c592fab73a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Jan 7 23:46:36 2016 +0100
+
+    Use ALL_ZERO in a few more places.
+
+commit ff7c7cda93a8457777eda9466c561d62313a9d49
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Jan 7 23:22:42 2016 +0100
+
+    lecroy-logicstudio: Fix some compiler warnings on MinGW.
+    
+    src/hardware/lecroy-logicstudio/protocol.c: In function 'handle_fetch_samples_done':
+    src/hardware/lecroy-logicstudio/protocol.c:261:3: warning: passing argument 6 of 'libusb_fill_bulk_transfer' from incompatible pointer type
+       recv_bulk_transfer, (void *)sdi, USB_TIMEOUT_MS);
+       ^
+    In file included from ./src/libsigrok-internal.h:31:0,
+                     from src/hardware/lecroy-logicstudio/protocol.h:26,
+                     from src/hardware/lecroy-logicstudio/protocol.c:23:
+    /home/uwe/sr_mingw/include/libusb-1.0/libusb.h:1546:20: note: expected 'libusb_transfer_cb_fn' but argument is of type 'void (*)(struct libusb_transfer *)'
+     static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer,
+                        ^
+    src/hardware/lecroy-logicstudio/protocol.c: In function 'fetch_samples_async':
+    src/hardware/lecroy-logicstudio/protocol.c:314:4: warning: passing argument 6 of 'write_registers_async' from incompatible pointer type
+        handle_fetch_samples_done);
+        ^
+    src/hardware/lecroy-logicstudio/protocol.c:200:12: note: expected 'libusb_transfer_cb_fn' but argument is of type 'void (*)(struct libusb_transfer *)'
+     static int write_registers_async(const struct sr_dev_inst *sdi,
+                ^
+    src/hardware/lecroy-logicstudio/protocol.c: In function 'lls_start_acquisition':
+    src/hardware/lecroy-logicstudio/protocol.c:1122:3: warning: passing argument 6 of 'libusb_fill_interrupt_transfer' from incompatible pointer type
+       recv_intr_transfer, (void *) sdi, USB_TIMEOUT_MS);
+       ^
+    In file included from ./src/libsigrok-internal.h:31:0,
+                     from src/hardware/lecroy-logicstudio/protocol.h:26,
+                     from src/hardware/lecroy-logicstudio/protocol.c:23:
+    /home/uwe/sr_mingw/include/libusb-1.0/libusb.h:1602:20: note: expected 'libusb_transfer_cb_fn' but argument is of type 'void (*)(struct libusb_transfer *)'
+     static inline void libusb_fill_interrupt_transfer(
+                        ^
+
+commit d64b5f43ccaab6d490da05b84fa9b31b7cccfcb7
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Dec 23 15:25:18 2015 +0100
+
+    sysclk-lwla: Minor whitespace and consistency fixes.
+
+commit 0cadb8a350578d857423a3bf3c0ed990cdb2608e
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Dec 22 19:36:19 2015 +0100
+
+    Minor whitespace and cosmetic fixes.
+
+commit 3591481e871eec154a764e1c78004095144de5b5
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sun Jan 3 22:27:45 2016 +0100
+
+    dmm: vc870: drop unused variable
+    
+    There is no status bit for RMS. We know about RMS if certain modes are
+    active. So, drop this superfluous variable.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit ee2e9be21be59e9455a78ce3598db49e3b38e6f6
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sun Jan 3 22:27:44 2016 +0100
+
+    dmm: vc870: support effective voltage & current
+    
+    And rename the status variable because in sigrok the term RMS is used
+    instead of "effective value".
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 2e1c4817c7f89b6a7eab8e9801b25d73a95089fb
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sun Jan 3 22:27:43 2016 +0100
+
+    dmm: vc870: render POWER_FACTOR as such and not as frequency
+    
+    The primary display is the power factor, the secondary is the frequency.
+    This got mixed up, so change the order. We also need to fix the
+    conversion factor.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 4867dd177f1dbaf99e44c29548d614d3f324eeb9
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sun Jan 3 22:27:42 2016 +0100
+
+    dmm: vc870: keep the order when processing modes
+    
+    is_power_apparent_power is index 0 of function 0x39, so it is better to
+    process it first and the later indices after that (we need to add
+    another one with a different patch later).
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit b4a0770ef8dc5deba8072679039ac4288cbc3454
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sun Jan 3 22:27:41 2016 +0100
+
+    dmm: vc870: fix AC conversion factors
+    
+    Testing showed that AC current needs to be handled different from DC.
+    Note that ACA is still untested because of limited testing equipment.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit b89462e45706e9b4f65eced96c7d76347f4b5569
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Sun Jan 3 22:27:40 2016 +0100
+
+    dmm: vc870: show display value properly in debug output
+    
+    It was confusing to see the display value (5 digits) printed in debug
+    output as a float. Print it the same way as shown on the real device,
+    without comma, of course.
+    This also allows to simplify the code a little.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 32ba0d80054df01767ec323c621ab1c6bc5f310c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Jan 4 18:53:30 2016 +0100
+
+    resource: Replace ssize_t with gssize to avoid unistd.h
+
+commit c61e208d26325c38c9c928b00890a70f55633ddf
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Jan 3 22:22:57 2016 +0000
+
+    python: Provide sensible __str__ and __repr__ functions for enum values.
+    
+    This fixes bug #668.
+
+commit f014a8fd4cbea56c3ecd23a5b2cbb7c38d905253
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jan 3 03:00:45 2016 +0100
+
+    proto.h: Add missing unistd.h #include.
+    
+    The ssize_t and size_t usage requires the unistd.h #include.
+    
+    Thanks to Daniel Glöckner for the bug report.
+    
+    This fixes bug #721.
+
+commit e43887683cc1920fa0d6f9e557bff0e62fa7c8a3
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jan 3 02:51:01 2016 +0100
+
+    baylibre-acme: Replace g_close() with close() for now.
+    
+    This g_close(), the only one in the whole code-base, would unnecessarily
+    raise the minimum glib version to 2.36.
+    
+    Thanks to Daniel Glöckner for the report.
+    
+    This fixes bug #724.
+
+commit 8c529898116e9a6bf7083fb3b748b24d006392a4
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Dec 30 18:39:48 2015 +0100
+
+    C++: Avoid std::map::emplace() for GCC 4.7 compatibility
+    
+    This fixes bug #720.
+
+commit 6954fdbca9e3bca9acbbe6e1eaf4e4e85062bc1e
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jan 3 00:39:44 2016 +0100
+
+    src/backend.c: Add missing winsock2.h #include.
+
+commit fa69e191b2d50f5feac40c67cf225d4d6c71f940
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Dec 29 23:49:15 2015 +0000
+
+    win32: Call WSAStartup() at sr_init() time.
+    
+    Should fix bug #692.
+
+commit 8141a0325c2d31a37ecaeef6532d932112700f01
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Dec 31 18:47:55 2015 +0100
+
+    Rename sr_driver_scan_options() to sr_driver_scan_options_list().
+
+commit 0c697a4b339451514d4814974708816ce7858b8e
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Dec 31 18:45:17 2015 +0100
+
+    Rename sr_dev_config_capabilities() to sr_dev_config_capabilities_list().
+
+commit 7d7fd93c947dcaa94f3d90bfb6b0bf3ce01d774d
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Dec 31 18:43:18 2015 +0100
+
+    Restore number "categories" in sr_configkey.
+
+commit e318f01b2f17d60fc4b25feb7d23e948cb5ffb8f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 3 01:59:42 2015 +0000
+
+    Make SR_CONF_MASK an internal constant.
+
+commit cffdc3e63b132795bfcaab91864c1c6b124e87fa
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Nov 2 01:03:07 2015 +0000
+
+    Make SR_CONF_{SCAN,DEVICE}_OPTIONS into internal constants.
+    
+    These are no longer needed in the public API as we have new wrapper functions.
+    They are still used internally by drivers.
+
+commit 36bb818d6f10ac1187f160f4c2ab1169aeb4e86f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Nov 2 22:05:02 2015 +0000
+
+    bindings: New configuration enumeration API.
+    
+    The new methods wrap the new libsigrok C API helper functions and eliminate
+    the need to pass complex types back to the user and wrap them with SWIG.
+    
+    Fixes bugs #479 and #480.
+
+commit 8f3168b89bcd79b555d86601270d81e5c8e3bdfe
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Nov 2 00:23:23 2015 +0000
+
+    Add new sr_driver_scan_options() helper function.
+    
+    This function replaces the pattern of calling config_list() with
+    SR_CONF_SCAN_OPTIONS to obtain a list of scan options.
+
+commit e7136c626f433bcdc101eaa24191ce5682803540
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Nov 2 00:40:40 2015 +0000
+
+    Add new sr_dev_options() helper function.
+    
+    This function replaces the pattern of calling config_list() with
+    SR_CONF_DEVICE_OPTIONS to obtain a list of device options. Note
+    that this does not include the SR_CONF_{GET,SET,LIST} bitmask,
+    which is now retrieved for a specific key by calling
+    sr_dev_config_capabilties().
+
+commit 71e9c54dab31384224b566c705d6084253f08bdd
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Nov 2 21:35:26 2015 +0000
+
+    Add new sr_dev_config_capabilities() helper function.
+
+commit 12f2f640cbe9167ad386a2d9069ec882d70cda5b
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Nov 2 20:52:00 2015 +0000
+
+    bindings: Wrap enum sr_configcap as Capability class.
+
+commit c57aa1ac19b437281e60c394483a15aec9080916
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Nov 2 20:46:28 2015 +0000
+
+    Make SR_CONF_{GET,SET,LIST} into a new enum.
+
+commit 039a2fd78a61ad8c6e9159be9ba06b605a456155
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Dec 27 12:26:06 2015 +0100
+
+    src/usb.c: Only allow hex characters in CONN_USB_VIDPID.
+    
+    (and also allow case-insensitive specification)
+    
+    Thanks to Hannu Vuolasaho for the fix.
+
+commit 52fb2d448480fd820fadff464a676e61dabc557f
+Author: Wolfram Sang <wsa at the-dreams.de>
+Date:   Tue Dec 29 12:45:07 2015 +0100
+
+    modbus: silence a build warning
+    
+    Refactor handling the size of modbus_devs, so it doesn't produce a build
+    warning and still allows the compiler to remove unused code.
+    
+    This fixes bug #637. It could be reverted once modbus_devs
+    unconditionally has a member in the struct.
+    
+    Signed-off-by: Wolfram Sang <wsa at the-dreams.de>
+
+commit 0a1f7b09b3fa4cc4da29c7acf53717e14b004b63
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Dec 22 20:15:57 2015 +0100
+
+    Prefer postfix-increment for consistency across the code-base.
+    
+    Only when there are technical reasons use prefix-increment.
+
+commit a078d3ec527c0a8180f59086e62a8905ae79aa65
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Dec 25 23:20:00 2015 +0100
+
+    korad-kaxxxxp: Add workaround for a Korad KA3005P issue.
+    
+    In some situations, the reply to the *IDN? command contains an
+    additional trailing 0x01 byte for unknown reasons.
+    
+    This issue seems to be reproducible by changing the voltage using the knobs
+    on the device, then turning on the output and turning it off again.
+    
+    The next korad-kaxxxxp scan() operation would contain the trailing 0x01
+    byte, which would lead to the detection of the device in libsigrok no
+    longer working until the next power-cycle.
+    
+    Work around this issue by treating both the ID string with and without
+    the trailing 0x01 byte as valid.
+
+commit ae9ca5b1df8137083e37588665f7322306346420
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Dec 24 01:19:11 2015 +0100
+
+    korad-kaxxxxp: Add support for the Velleman PS3005D.
+
+commit 1d80e1c64140bb74857042751c460962b445cb35
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Dec 22 23:42:36 2015 +0100
+
+    sysclk-lwla: Use static array for LWLA1034 init sequence
+    
+    Just as in the LWLA1016 initialization, make use of a static array
+    for the constant part of the LWLA1034 capture setup sequence.
+
+commit e35a4592488d7492a46921dd8bd130eae5faf11a
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Dec 22 15:52:40 2015 +0100
+
+    sysclk-lwla: Attempt initialization three times
+    
+    This is a desperate measure to improve the success rate of device
+    initialization even after it got into a bad state. Combine this
+    with a reduced USB timeout (1 second) so that if it fails, it fails
+    quickly. Also ignore USB errors from the initial dummy read of the
+    device test ID.
+
+commit 04f2428354068c6ed85ad090dd4baac253e940c4
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Dec 5 18:16:39 2015 +0100
+
+    sysclk-lwla: Use static array for init sequence
+
+commit 786485772ffd28dfb1ca7375ca64118ccd7af25c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Dec 5 17:47:52 2015 +0100
+
+    sysclk-lwla: Work around short transfer quirk
+    
+    Detect whether the FX2 firmware of the LWLA device exhibits the
+    short transfer bug. If so, work around the problem by limiting
+    reads to at most 64 bytes at a time. This slows down the memory
+    read after acquisition quite noticably, but makes the device
+    usable even in adverse conditions.
+
+commit 940805ce7dbd26c32f2e8b3512fdc54432b412db
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Dec 5 10:06:15 2015 +0100
+
+    sysclk-lwla: Skip unused registers in status poll
+    
+    Reduce the number of long registers read in bulk during status
+    polling from 10 to 5. The LWLA1034 driver used to do that already
+    in an earlier iteration, which was then changed to be more like
+    the original vendor software. The reason for bringing it back now
+    is that it reduces the response size to 40 bytes, which works
+    around the spurious 64 byte limit bug in the FX2 firmware of the
+    LWLA devices.
+
+commit ef7df53d3609413341b521ab64f223f80e49a01a
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Dec 14 21:47:46 2015 +0100
+
+    sysclk-lwla: Declare model_info structs as extern
+    
+    This fixes bug 714.
+
+commit fc6cbfce2ba3f7f15f79acd6754ec6745e091ae6
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Dec 5 10:41:16 2015 +0100
+
+    sysclk-lwla: Close USB handle on drain error
+
+commit 407b6e2cff817c6b05f1f038e2fecd94ad11fa86
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Dec 4 20:08:07 2015 +0100
+
+    sysclk-lwla: Various cosmetic improvements
+
+commit 6d2897e394950bb661a27fd33dbcd14d8c56c61f
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Dec 20 20:20:56 2015 +0100
+
+    Add the Lauterbach Trace32 logic analyzer data import module
+
+commit 8a66b0777ce093fcab54ae9cf89a645cc03c6731
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Mon Dec 7 20:26:11 2015 +0100
+
+    Add RL64 and RL64S endianness helper macros
+
+commit d01c4c56d57491edb1d430646e88cf5341d878e6
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Mon Dec 7 20:14:19 2015 +0100
+
+    Whitespace fixes
+
+commit 30726a8a3222e8effcc63c7a25db1546b6d558fd
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Dec 21 16:22:44 2015 +0100
+
+    input/raw_analog: Fix two compiler warnings.
+    
+    ../src/input/raw_analog.c:63:67: warning: integer overflow in expression [-Woverflow]
+      { "S32_LE",     { 4, TRUE,  FALSE, FALSE, 0, TRUE, { 1, INT32_MAX+1}, { 0, 1}}},
+                                                                       ^
+    ../src/input/raw_analog.c:65:67: warning: integer overflow in expression [-Woverflow]
+      { "S32_BE",     { 4, TRUE,  FALSE, TRUE,  0, TRUE, { 1, INT32_MAX+1}, { 0, 1}}},
+                                                                       ^
+
+commit 21cbe810fe12fbbc93b6f80c950c377528353442
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Dec 21 15:58:03 2015 +0100
+
+    input/raw_analog: Use ARRAY_SIZE.
+
+commit 221cec31fc51ffeeb2ad222bb9e796229fa7e9c4
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Sun Nov 29 08:03:57 2015 +0100
+
+    input/raw_analog: set scale and offset appropriately
+
+commit e6b15cb5e609c9263f501d3b12965206feddb42b
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Sat Nov 28 19:55:12 2015 +0100
+
+    input/raw_analog: Add input module for raw analog signals
+
+commit 4d376e082cbc17da30cf20d18bec8f65b14b7ae0
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Wed Nov 25 01:41:41 2015 +0100
+
+    analog: add conversion from various integer formats to float
+
+commit 74c9a8d2bd361d59cd6eb6e20c26a37c6f4d12a1
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Sat Nov 28 16:52:08 2015 +0100
+
+    input/wav: initialize channel list before going into ready state
+    
+    The sr_input_dev_inst_get API documentation guarantees an input is fully
+    initialized as soon as the device instance is returned. An sdi
+    implementation should not set sdi_ready any earlier.
+    
+    This fixes parts of bug #387.
+
+commit 7cccc9155c6cdca46deee310be112eb23b374c3b
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Sat Nov 28 23:18:50 2015 +0100
+
+    bindings/cxx: make sure the config value reference is kept
+    
+    fixes glib warning:
+    GLib-CRITICAL **: g_variant_unref: assertion 'value->ref_count > 0' failed
+
+commit 8dd5d426f777b47bb3135cc3bb9cde7348801f52
+Author: Tilman Sauerbeck <tilman at code-monkey.de>
+Date:   Sun Nov 29 20:49:40 2015 +0100
+
+    z60_libsigrok.rules: Add udev rule for LeCroy LogicStudio16.
+
+commit c7b17bcba3751c7849229089cce8773bb4355be7
+Author: Tilman Sauerbeck <tilman at code-monkey.de>
+Date:   Thu Oct 8 20:13:53 2015 +0200
+
+    lecroy-logicstudio: Initial driver implementation.
+    
+    This supports both 8 and 16 channel modes with samplerates up to
+    500 MHz. Voltage thresholds are currently fixed at 1.58V.
+
+commit 17b93fd7c0c7af90042eac513d837cf1ec2b5f9c
+Author: Tilman Sauerbeck <tilman at code-monkey.de>
+Date:   Mon Nov 16 19:47:04 2015 +0100
+
+    Add the RB64 macro.
+    
+    Reads an unsigned 64 bit integer from memory.
+
+commit 8eb4299c5b5dac419b769d2e6581707cec47ea6b
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sun Nov 8 17:37:34 2015 +0100
+
+    bindings: Fix doc extraction for enums
+    
+    The source file enum.hpp was not found when not building in the source
+    tree.  Also, extraction of the brief description did not work correctly
+    when there was additional XML markup inside the <para> element.
+
+commit 8abdf0066e4203eafe5b65ed0a995d4588961126
+Author: Hannu Vuolasaho <vuokkosetae at gmail.com>
+Date:   Fri Nov 27 20:10:14 2015 +0200
+
+    korad-kaxxxxp: Workaround for Korad device bug
+    
+    The sixth character from ISET? is read and discarded. If the device is
+    turned off and on again, this won't be there and causes 10 ms delay in
+    every ISET? Luckily, this value isn't queried that often. To get the
+    sixth byte, the *IDN? command has to be issued before ISET?.
+
+commit bcf9384d3d2cad0effb5ef2769697d8af05b8a17
+Author: Hannu Vuolasaho <vuokkosetae at gmail.com>
+Date:   Mon Nov 30 22:47:56 2015 +0200
+
+    korad-kaxxxxp: Fix typo in Korad driver device enum
+
+commit 17124cf9adf2b1017180bab854bf8a2d50639e0b
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Tue Nov 24 22:31:35 2015 +0100
+
+    output/wav: use the right buffer for SR_DF_ANALOG
+
+commit 8b5aefc681c1d4158ce4c0c95f2a2888ea274a12
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Tue Nov 24 21:03:04 2015 +0100
+
+    input/wav: fix and simplify conversion of integer samples
+    
+    Size of individual samples is specified by unitsize, not samplesize.
+    The sample immediate is not necessary.
+
+commit b944e336d60cb9948b757f47a97b9f3bf145f534
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Tue Nov 24 20:55:14 2015 +0100
+
+    input/wav: increase search range for data chunk, report errors
+
+commit 288f8ce23cf0cf9a66e0ffe824f693009f942d07
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Sun Nov 22 22:14:15 2015 +0100
+
+    input/wav: fix error in offset calculation
+
+commit c7224164a073461d9be8dc54120dc13673e65f61
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Fri Nov 27 00:44:50 2015 +0100
+
+    output/wav: track and free memory for float conversion buffer
+
+commit c01378c95f34e60d3b67f84dc3a6c3c6c154186d
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Thu Nov 26 22:55:55 2015 +0100
+
+    input: fix leak of config data in several input modules
+
+commit da3d141f04e9a17bd41c5adfb5c83369fe3759df
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Thu Nov 26 04:29:28 2015 +0100
+
+    output: fix options memory leak
+
+commit 2dbe445d55f0d7166bb481f4991e10bec4cd2789
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Thu Nov 26 03:07:47 2015 +0100
+
+    output/analog: track and free memory for float conversion buffer
+    
+    ==18779== 800,000 bytes in 196 blocks are definitely lost in loss record 29 of 29
+    ==18779==    at 0x4C29110: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+    ==18779==    by 0x4E635C3: receive (analog.c:319)
+    ==18779==    by 0x40870B: datafeed_in (session.c:316)
+    ==18779==    by 0x4E59D4E: sr_session_send (session.c:1201)
+    ==18779==    by 0x4E59F8B: sr_session_send (session.c:1159)
+    ==18779==    by 0x4E62595: send_chunk (wav.c:234)
+    ==18779==    by 0x4E62A06: process_buffer (wav.c:290)
+    ==18779==    by 0x40954A: load_input_file_module (input.c:123)
+    ==18779==    by 0x4097AB: load_input_file (input.c:157)
+    ==18779==    by 0x40531E: main (main.c:288)
+
+commit 877a6d09d54d48814e8cf27a67eab0756136f2ae
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Thu Nov 26 03:04:39 2015 +0100
+
+    session: free memory for datafeed callbacks, reported by valgrind
+    
+    ==17549== 32 (16 direct, 16 indirect) bytes in 1 blocks are definitely lost in loss record 22 of 39
+    ==17549==    at 0x4C29110: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+    ==17549==    by 0x5359200: g_malloc (in /usr/lib64/libglib-2.0.so.0.4200.2)
+    ==17549==    by 0x536EE2D: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.4200.2)
+    ==17549==    by 0x5370165: g_slist_append (in /usr/lib64/libglib-2.0.so.0.4200.2)
+    ==17549==    by 0x4E595C3: sr_session_datafeed_callback_add (session.c:512)
+    ==17549==    by 0x409527: load_input_file_module (input.c:111)
+    ==17549==    by 0x4097AB: load_input_file (input.c:157)
+    ==17549==    by 0x40531E: main (main.c:288)
+
+commit fe7b8efc6b36aa707365f1d23c645303a5f2307a
+Author: Stefan Brüns <stefan.bruens at rwth-aachen.de>
+Date:   Thu Nov 26 02:30:42 2015 +0100
+
+    session: fix use after free of session->devs as reported by valgrind
+    
+    ==7478== Invalid write of size 8
+    ==7478==    at 0x4E59182: sr_session_dev_remove_all (session.c:302)
+    ==7478==    by 0x4E591CD: sr_session_destroy (session.c:265)
+    ==7478==    by 0x4095D9: load_input_file_module (input.c:143)
+    ==7478==    by 0x4097AB: load_input_file (input.c:157)
+    ==7478==    by 0x40531E: main (main.c:288)
+    ==7478==  Address 0x7877eb8 is 88 bytes inside a block of size 96 free'd
+    ==7478==    at 0x4C2A37C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+    ==7478==    by 0x4E5F454: sr_input_free (input.c:573)
+    ==7478==    by 0x4095C3: load_input_file_module (input.c:140)
+    ==7478==    by 0x4097AB: load_input_file (input.c:157)
+    ==7478==    by 0x40531E: main (main.c:288)
+
+commit b3cfc6e98e34e54a11d5a9563f6f7ac282c92983
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Nov 29 00:23:53 2015 +0100
+
+    Make all sizeof() consistently use parenthesis.
+
+commit 09ffac33b7af7bba92f2be461dfb8a01e96e2d60
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Nov 27 23:44:11 2015 +0100
+
+    sysclk-lwla: Simplify trigger mask generation
+
+commit 93ed0241aaf6e791e113b741435fa857891e86c2
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Nov 27 22:47:29 2015 +0100
+
+    sysclk-lwla: Drain pending replies on initialization
+    
+    When opening the device, drain any pending data from the USB
+    buffers so that the device won't get stuck on crashes etc.
+
+commit 567674b4f8a71ff369fad9a31f7d9db00abbd6bc
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Nov 27 21:26:13 2015 +0100
+
+    sysclk-lwla: Remove double USB set configuration
+    
+    This was a pointless attempt to make the reset hiccups go away.
+    It didn't help, the problem must be something else.
+
+commit 7ed808179f5542c903a476266c4b175a6f2a18a7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Nov 27 14:32:28 2015 +0100
+
+    sysclk-lwla: Cut down on size_t overuse
+    
+    Do not use size_t for values whose width is defined by the device,
+    not the host. Also don't use size_t for simple indices with known
+    small range, unless type compatibility considerations apply.
+
+commit 43af7604d0910e9831d027d5a53ca806cee8bdfc
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Nov 27 03:19:43 2015 +0100
+
+    sysclk-lwla: Fix copy'n'paste mistake in comment
+
+commit 9e9dba7b41bdd87d4ce442bc9bf6507518e97000
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 27 00:15:15 2015 +0100
+
+    Use the ALLZERO macro in more places.
+
+commit 2c5bdf1bbdf8d68b8ff9b352c790f50f23d90a23
+Author: Hannu Vuolasaho <vuokkosetae at gmail.com>
+Date:   Wed Nov 25 00:56:23 2015 +0200
+
+    Support for Korad KA3005P
+
+commit 16fc7ee29ffa196d29a20fddd81234792f88b12c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 27 00:16:05 2015 +0100
+
+    korad-kdxxxxp: Rename driver to korad-kaxxxxp.
+    
+    This matches the supported / supportable devices better.
+
+commit 89b3a0d8d1cd44e3959a5c332954f34d6e9bd685
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 27 00:14:05 2015 +0100
+
+    doxygen: Only use @since on public API functions.
+
+commit 2cdd40205e1532a847a94f96d60ab0611e9db3ed
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 27 00:12:57 2015 +0100
+
+    z60_libsigrok.rules: Add sigrok fx2lafw VID/PID pairs.
+
+commit db0e5c999df2872e9be422a6ec48920eb3f4eb3f
+Author: Petteri Aimonen <jpa at git.mail.kapsi.fi>
+Date:   Wed Feb 4 17:46:33 2015 +0200
+
+    VCD input: Chunk up the samples in 1MB blocks.
+    
+    This gives about 100x speed improvement when converting VCD->SR.
+    
+    Also should allow practically unlimited number of channels.
+    
+    This fixes bug #551.
+
+commit 3393f185a5109832db68966a44b46204cc5c53c5
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Nov 23 20:36:08 2015 +0100
+
+    session: Remove debug spew on I/O events
+    
+    This really is a bit much with some drivers that do lots of small
+    I/O transfers.
+
+commit e8cd7d602c521ded30497e20cfc2dae35c7012f1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Nov 22 17:51:05 2015 +0100
+
+    contrib: Add SysClk LWLA1016 to udev rules
+
+commit be64f90b53d09d9720dc6e06ff8ab61d96c03932
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Nov 22 17:45:54 2015 +0100
+
+    sysclk-lwla: Implement support for LWLA1016
+    
+    Refactor the sysclk-lwla driver to separate the generic logic from
+    the model-specific implementation. Based on this, implement support
+    for the SysClk LWLA1016 device.
+
+commit ce19d4c6157b2998aa88a2f32670dd7bdcca02e8
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Nov 10 20:52:09 2015 +0100
+
+    sysclk-lwla: Remove global driver instance pointer
+    
+    Obtain the sr_dev_driver pointer from the device instance so that
+    the remaining references to the global di pointer can be removed.
+
+commit e4ce146fefe7cfd475b9d8fdec1101b188b746db
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Nov 21 20:27:25 2015 +0100
+
+    sr_usb_find(): Increase the 'bus' limit to 255.
+    
+    On some systems it can happen that the USB 'bus' number is a lot larger
+    than 64, but sr_usb_find() currently errors out if it is > 64.
+    
+    Example:
+    Bus 250 Device 006: ID 1ab1:04ce 1ab1 DS1000Z Series[...]
+    
+    Increase that limit so that the code will work everywhere. This bus number
+    is queried via libusb_get_bus_number() which returns an uint8_t, so we're
+    limiting to 255 here.
+    
+    Thanks to 'ssi' on IRC for reporting the issue.
+
+commit d79244dc342ae5cc3f402145cc510fd7b030cc1e
+Author: Matthieu Gaillet <matthieu at gaillet.be>
+Date:   Wed Nov 4 22:58:15 2015 +0100
+
+    serial-dmm: Add support for Velleman DVM4100 & PeakTech 3415.
+    
+    (both are using the new dtm0660 DMM parser)
+
+commit eed3dec849336f610e75e4bb80961391d4e931ea
+Author: Matthieu Gaillet <matthieu at gaillet.be>
+Date:   Wed Nov 4 22:58:15 2015 +0100
+
+    Add dtm0660 15-byte DMM protocol parser.
+
+commit b98b70222fd756784e7bb55a4d3356e0e3405679
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Nov 15 19:01:13 2015 +0100
+
+    usb.c: Fix usb_get_port_path() issue on Mac OS X.
+    
+    Apparently (some versions of) Mac OS X have the same problem with
+    usb_get_port_path() as FreeBSD does. Work around this in the same way.
+    
+    Thanks to hanyazou at gmail.com for the patch!
+    
+    This fixes bug #673.
+
+commit 382bea8250c8f69fff2f6b947a6ad0d884328610
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Nov 15 16:12:35 2015 +0100
+
+    chronovu-la: Add missing libusb dependencies.
+    
+    The chronovu-la driver now uses libusb (in addition to libftdi), so add
+    a missing <libusb.h> #include and libusb dependency in configure.ac.
+
+commit 67f890d5bb259539b930df15146f138974a849de
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Nov 9 00:38:18 2015 +0100
+
+    chronovu-la: Properly handle multiple devices.
+    
+    The driver should now be able to cope with e.g. multiple ChronoVu LA8
+    and/or ChronoVu LA16 devices being connected to the same PC.
+    
+    It now also provides the serial number and connection ID, which can be
+    used by frontends to differentiate multiple devices.
+    
+    Also improve the scanopts / drvopts / devopts lists handling.
+    
+    This fixes bug #504.
+
+commit d0f1fa0758e9f95beac0a431d96dddb86de50760
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Nov 8 17:38:02 2015 +0100
+
+    asix-sigma: Drop unneeded asix_sigma_driver_info.
+    
+    This is not needed, but also causes linker issues on some platforms.
+
+commit 2c24077466a299ead689c90f01f55f6d86c7386b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 6 18:18:34 2015 +0100
+
+    Constify a lot more items.
+    
+    This fixes various compiler warnings when -Wdiscarded-qualifiers is used.
+
+commit 45ec8f775680124d49e82251f258104cf4414f17
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Nov 7 22:29:12 2015 +0100
+
+    build: Delete generated files on make clean
+
+commit 7252a09e8fb0b436851ab3fd041d014b0ba4addd
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Nov 7 22:20:19 2015 +0100
+
+    build: Enable uninstall of Java bindings
+    
+    This is needed for make distcheck to work.
+
+commit 9448951db5de17f6fc969e5bc2e68e5985c6fa27
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Nov 7 22:08:45 2015 +0100
+
+    build: Skip Python bindings during distcheck
+    
+    setup.py attempts to relink the bindings at installation time,
+    which breaks distcheck. Thus, disable the Python bindings during
+    distcheck for now.
+
+commit 0a9d05dc7cf555fb29be0780b8274bdec34956e5
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Nov 7 21:57:20 2015 +0100
+
+    build: Distribute missing files
+
+commit 37829c15b5ddbe6f01c6b9373cf026fac9e4fcab
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Nov 7 21:51:40 2015 +0100
+
+    build: Make ChangeLog generation distcheck-safe
+
+commit e5ef649be3167dbb9f6403e2c1d0e2e1b09d3a0c
+Author: Tilman Sauerbeck <tilman at code-monkey.de>
+Date:   Mon Nov 2 21:59:00 2015 +0100
+
+    Constify sr_dev_driver::name and sr_dev_driver::long_name.
+    
+    This fixes a bunch of warnings when compiling with -Wwrite-strings.
+
+commit 3ba56876b4a5469b0c39a6f162f517ce8610b9e4
+Author: jry <jrysig at gmail.com>
+Date:   Thu Nov 5 11:41:27 2015 +0100
+
+    asix-sigma: Split into api.c and protocol.[ch] modules.
+
+commit ee95d6bd8b94dfd6ac67d850e17879a4e5875b6a
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Mon Nov 2 17:16:58 2015 +0100
+
+    Java: Remove explicit constructor deletes since SWIG 2 doesn't like them
+    
+      bindings/java/org/sigrok/core/classes/classes.i:247: Error: \
+        Syntax error in input(3).
+
+commit e13648d0416aa0cb3c81f234389c92a30d46d527
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sun Nov 1 23:29:32 2015 +0100
+
+    Java: Fix leaking global refs
+    
+    This fixes issue #690.
+
+commit c470ae86dade608f747340f6f0699d657d33fd21
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sun Nov 1 23:27:41 2015 +0100
+
+    Java: Don't use JNIEnv* captured by lambdas, it may be invalid for the context
+
+commit 908aad38164869c2ecf066a73962a3864c644587
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sun Nov 1 15:59:47 2015 +0100
+
+    Java: Fill jlongs with 0 when doing SWIG style pointer marshalling
+    
+    This prevents valgrind complaints and also makes pointer comparison
+    from the Java side work.
+
+commit 32fd1edc2333b78c671afd3fc1f87a1bce925bd1
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sun Nov 1 15:58:00 2015 +0100
+
+    Revert "Java: Avoid dangerous writes via casted pointers"
+    
+    This reverts commit 865de99391194e45a0e80ea534fc5e27d5287925.
+    
+    This fixes bug #688.
+
+commit ae2cdde4d20e600480ae2cb86b7488fa13400812
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Nov 1 23:42:21 2015 +0000
+
+    python: Fix string conversions for Python 3.
+    
+    Fixes bug #478.
+
+commit 5dd538ded0d5873541af7cdf2e54c3e6d8297b49
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Nov 2 14:30:24 2015 +0100
+
+    build: Set CXXFLAGS when building Python module
+    
+    Set both CFLAGS and CXXFLAGS when executing setup.py to build
+    the Python bindings. Newer versions of distutils/setuptools have
+    apparently started to pick up the latter when compiling C++.
+
+commit 3d8691326666a32925aa555f3088185ccd5d846b
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Nov 1 21:40:28 2015 +0100
+
+    build: Match up Python headers with interpreter version
+    
+    Check for either Python 2 or Python 3 header files depending on
+    the version of the Python interpreter. Also require the module
+    version to exactly match the interpreter version.
+    
+    This may fix bug #645.
+
+commit 8105e82913aface2a430c51ef3e9a45cfce68170
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Nov 1 19:38:40 2015 +0100
+
+    openbench-logic-sniffer: Avoid recreating event source
+    
+    With the current driver API and the corresponding session event
+    handling, it is not possible to destroy and then re-create an
+    event source with the same key within the same main loop iteration.
+    
+    The next generation driver API will fix this problem. But for now,
+    just change the driver to make do without that sort of thing. Also
+    increase the I/O timeout to 100 ms to be safer in the event of all
+    kind of delays the OS environment may induce.
+    
+    This fixes bug #678.
+
+commit d09a82a8fe04f9554f99c5141dd9eb7552612156
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Oct 30 16:41:30 2015 +0100
+
+    Remove obsolete SR_INPUT_META_MIMETYPE metadata key.
+    
+    This was never really implemented, since getting the mimetype of a
+    file or stream in a cross-platform way is a gigantic tangle.
+
+commit c958ab59d60992504046d73c7bf41db4fd7747fd
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Oct 30 16:38:54 2015 +0100
+
+    input/csv: Remove obsolete mimetype format match.
+    
+    Mimetypes never worked, and in any case this caused a segfault due to a
+    missing SR_INPUT_META_REQUIRED flag.
+    
+    This fixes bug #681.
+
+commit b04a532fa3fc9d544add735821cc003015f96abc
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Oct 31 16:02:30 2015 +0100
+
+    build: Also look for python-2.7 pkg-config module
+    
+    Apparently Gentoo names the module python-2.7 instead of python27.
+
+commit 58cc125b0f645726fac9133bf06d3a91b75b8c27
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Oct 31 10:19:51 2015 +0100
+
+    sysclk-lwla: Read full 64 bit of capture duration field
+    
+    Evaluate all 64 bit of the duration field in the capture status
+    record. Although unlikely in practical use, due to compression
+    it is possible for the duration in ms to exceed 32 bit.
+
+commit 30f34dbdf0ee8a389b4219ed7808bec64421f3e0
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Oct 31 01:39:03 2015 +0100
+
+    sysclk-lwla: Define constants for long registers
+
+commit 586ff70a2116d36ddd9521704c4531dfc93d6ec1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Oct 31 01:11:31 2015 +0100
+
+    sysclk-lwla: Clarify use of SRAM control registers
+    
+    Assign more meaningful names to things and introduce new constants.
+
+commit a6dc3dacab23bf7c8da008f21a03e2a1242e77ea
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 30 15:07:25 2015 +0100
+
+    zip: Provide fallback if zip_discard() is unavailable
+    
+    zip_discard() was introduced in libzip 0.11, which some systems
+    do not have yet. Provide a fallback replacement for zip_discard(),
+    and reduce the requirement to libzip 0.10 again.
+    
+    This fixes bug #674.
+
+commit 014512254abcc74d203376f2e038053b94cf1116
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Oct 29 22:53:25 2015 +0100
+
+    C++: Declare std::default_delete friend as struct
+    
+    std::default_delete<> is originally defined as struct, not class.
+    This does not really make much of a difference, but some compilers
+    complain when the struct/class tag is not consistent across all
+    declarations of the type.
+
+commit cea8c3124f982606f4b466374c568d0f723b8197
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Oct 29 21:48:49 2015 +0100
+
+    C++: Suppress deprecation warnings from glibmm
+    
+    Ignore warnings due to use of std::auto_ptr<> in the glibmm headers.
+    This should be reverted once glibmm is fixed.
+
+commit 8ebf1469a91b7f7899d84386dee021575752b6d8
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Oct 29 22:39:44 2015 +0100
+
+    build: Use common SWIG defines for Python and Java
+    
+    Make the Python and Java bindings use the same set of preprocessor
+    macros for the SWIG parsing stage, taken from a make variable. Add
+    G_GNUC_{BEGIN,END}_IGNORE_DEPRECATIONS to that set.
+
+commit e1172cf847e522b07d063c36c3f54b0a5dc8429f
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 25 03:44:55 2015 +0100
+
+    sysclk-lwla: Read test word twice during initialization
+    
+    During initialization of the LWLA1034, read the 64-bit test word
+    twice and verify the result of the second read only. This better
+    matches what the original vendor software does.
+
+commit 3a322bbc3b0f4d79a79cffac00aeb05ff69ce535
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 25 02:26:44 2015 +0100
+
+    sysclk-lwla: Clarify function of registers 10B0 to 10BC
+    
+    Apparently, these four registers form an interface for indirect
+    access to another internal 64 bit wide memory. This is likely the
+    same memory as that accessed by the bulk transfer commands 7 and 8.
+
+commit 8e0f9bf782cb9e84a244bbd1b91db96a3f4f56e6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Oct 29 23:59:12 2015 +0100
+
+    Makefile.am: Avoid non-portable -t option for $(INSTALL).
+    
+    The 'install' tool doesn't have a -t option on all platforms
+    (e.g. Mac OS X or FreeBSD).
+
+commit 9e5366df4f83d3f5c0afb1489bb2d4d4ea426e18
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Oct 29 18:51:09 2015 +0100
+
+    korad-kdxxxxp: Minor cosmetics and consistency changes.
+
+commit c40ed60f27a3c918b29f72a73074b97ef0ecfe86
+Author: Hannu Vuolasaho <vuokkosetae at gmail.com>
+Date:   Thu Oct 22 01:05:37 2015 +0300
+
+    Over voltage and current protection support
+    
+    Developed against Velleman LABPS3005D and seems to work.
+
+commit b16d975a5c879b817583512b944b57198b71644e
+Author: Hannu Vuolasaho <vuokkosetae at gmail.com>
+Date:   Wed Oct 21 19:04:37 2015 +0300
+
+    Support for regulation status and fix for mysterious M
+    
+    Added support for SR_CONF_REGULATION which returns value for CH1
+    Also VELLEMAN LABPS3005D (only device currently supported) sends single
+    'M' character in beginning of return value, which is specially discarded.
+
+commit 865de99391194e45a0e80ea534fc5e27d5287925
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Oct 26 07:29:50 2015 +0100
+
+    Java: Avoid dangerous writes via casted pointers
+    
+    Writing to an object through a pointer of incompatible type is
+    really evil. Even when the data size matches, it is still a
+    violation of strict aliasing rules.
+    
+    Replace all instances by direct casts of the value, without the
+    unnecessary and dangerous indirection.
+
+commit b0b0e2009cae6b1e85992acb5213944aeff5fb53
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Oct 26 05:47:21 2015 +0100
+
+    SWIG: Avoid unseparated template closing brackets
+    
+    SWIG 2.0.12 fails with a syntax error on ">>".
+
+commit 4aa9a1e5698c74e2295f52313c33804761176fc1
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 23:30:49 2015 +0000
+
+    C++: Catch exceptions from stoi() and stod().
+    
+    Fixes bug #483.
+
+commit 90bd8829b147b495c0fcd39ede6665a2acefba0e
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 19:26:42 2015 +0000
+
+    java: Add doxygen output directory to .gitignore.
+
+commit f2831ab35b4988930535dfd135884873b1b5b00d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 16:02:54 2015 +0000
+
+    java: Usable wrapping of Configurable.config_keys().
+
+commit 36e3f6a9cecffe72cfb85f85ed3ea7a43f586fec
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 14:43:15 2015 +0000
+
+    Makefile.am: Make SWIG wrappers also depend on new templates.i file.
+
+commit df979d6dc6949b1d5c3814b177cb71d6c40d03d4
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 14:04:49 2015 +0000
+
+    python: Give all enum values __doc__ strings.
+
+commit ef9643a2bbb9f3c9a93a7a8c54100e32c1b3d3da
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 12:06:20 2015 +0000
+
+    python: Add docstrings for enum constants.
+
+commit 8fb7efe20311903dd8101962107d21a02a9884ce
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 03:01:30 2015 +0000
+
+    java: Add docstrings for enum constants.
+
+commit 0bcdeb90c4d689a8d51797c266c76946619f482e
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 02:07:35 2015 +0000
+
+    C++: Include enum classes when generating documentation.
+
+commit ace872d5296ab7352e94f09746c574db256c07d6
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 01:57:26 2015 +0000
+
+    C++: Declare namespace in enums.hpp so it can be used independently.
+
+commit e5c22906e804f540647ab182d9538499964eb633
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 25 01:39:16 2015 +0000
+
+    java: Make enum values available as normal constants.
+
+commit 564d009e88456e03e11e92a3e4cad4512ebf513d
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Oct 26 07:11:20 2015 +0100
+
+    Java: Remove obsolete SourceCallback interface
+
+commit 9e7176bd00016554a15b2687939d6e7b8536b2eb
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Oct 26 07:04:10 2015 +0100
+
+    C++: Avoid const ref args to appease Java bindings
+    
+    The Java bindings currently have some weird problem with function
+    arguments passed by const reference. Not all types are affected,
+    but the collection types that involve custom typemaps are.
+    
+    For now, revert back to pass-by-value for the problematic types.
+
+commit bf03d63565f60d758f53ecde0b30631463e44609
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Oct 26 05:18:06 2015 +0100
+
+    C++: Use C++98 syntax for default arguments to appease SWIG
+    
+    Looks like SWIG silently ignores default arguments specified via
+    aggregate initialization. This is rather unfortunate, especially
+    if the argument types are complex.
+
+commit a98729a742ebbb9df9dbb9ce8aae1cc4c3f1e00d
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Oct 15 19:21:28 2015 +0200
+
+    C++: Replace custom deleters with std::default_delete
+    
+    Replace custom Deleter classes with std::default_delete<>, declared
+    as friend so it can invoke the private destructor. Inexplicably,
+    std::shared_ptr<> does not use default_delete<> by default, so it
+    is still necessary to explicitly specify the deleter when creating
+    shared_ptr instances.
+    
+    With this, unique_ptr and shared_ptr instances now use the same
+    default delete mechanism.
+
+commit f17b45465500f1d1aad58479802018949004cce5
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Oct 15 18:32:44 2015 +0200
+
+    C++: Use smart pointers instead of manual delete
+    
+    Make use of std::unique_ptr<> to manage the lifetime of members
+    or container elements, so that manual invocations of delete can
+    be avoided. This also provides for exception safety.
+    
+    Since std::unique_ptr<> is only movable but not copyable, adapt
+    the code to avoid copies and assignments of these pointers.
+
+commit d5d7b09eb76de71a42b780b97aee5f5019d2d799
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 17:57:30 2015 +0200
+
+    C++: Move C struct pointers out of ownership classes
+    
+    Reduce needless over-generalization. There is no design need
+    for the ParentOwned and UserOwned classes to contain the C base
+    struct pointer. Instead, just make the _structure pointer a
+    private member of each class that needs one.
+
+commit b6ab954d67b584af662323ae79ae6da0fbd3a588
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 17:11:37 2015 +0200
+
+    C++: Rename get_shared_pointer() to share_owned_by()
+    
+    This makes it clearer that this method assigns the parent
+    (owner) reference.
+
+commit 67b82fc9c9b66baacc9f12b7e65a1e4ff0065618
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 17:01:05 2015 +0200
+
+    C++: Use shared_from_this() exclusively on this
+    
+    Never call shared_from_this() on any object other than "this".
+    Adapt the API so that it can be made protected.
+
+commit 21d1bec60e6d69f6f2ef6f3dc3e07219153285c0
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 14:51:51 2015 +0200
+
+    C++: Make most members private instead of protected
+    
+    Use protected only for members which are actually needed by
+    sub-classes. Declare all the rest private.
+
+commit 4c9208a799064a4eb3c76701edf3a007811dbc85
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 14:31:18 2015 +0200
+
+    SWIG: Hack around SWIG segfault on private destructors
+    
+    Apparently this problem has been fixed in SWIG 3.0.6. However,
+    until we can require that version, define "private" as "protected"
+    when running the SWIG parser.
+
+commit 15bebf575da5fe1f587a052c3dc254eea1bf7659
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 13:57:42 2015 +0200
+
+    C++: Make value get accessors const
+    
+    Declare accessor methods that return value members const. For now,
+    skip all cases where constness would have to be applied transitively
+    to shared objects.
+
+commit a73d49263611b0d65a3ca1ad85bbee592cf1a234
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 12:35:01 2015 +0200
+
+    C++: Make some methods static to match the C API
+    
+    Context::package_version() and Context::lib_version() do not access
+    context state and should be static. However, leave the logging
+    related methods alone for now, as making them static would entail
+    making the callback data a global static, since the C API lacks
+    destroy notification callbacks.
+
+commit 0ab8e5d22bbb82674a825125c4cb23b8f3b78858
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Oct 14 20:56:51 2015 +0200
+
+    C++: Declare all callbacks invoked from C noexcept
+    
+    If one of these functions does throw an exception, std::terminate()
+    will be called. Without this, the behavior is undefined since the C
+    stack is not prepared to deal with exceptions.
+
+commit 15914cdb0fc6c4c4e0acd410dd58177d0aa17fd2
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 12:19:27 2015 +0200
+
+    C++: Use noexcept instead of throw()
+    
+    Runtime-checked exception specifications via throw() are
+    deprecated in C++11.
+
+commit c6e18e007a2ee65cc1fe03fb7e86ac5fe47d7907
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 12:12:28 2015 +0200
+
+    SWIG: Define "noexcept" empty to work around SWIG bug
+    
+    The SWIG 2.0.12 on my system bails out with a syntax error on the
+    "noexcept" keyword in C++11 code. Define "noexcept" to nothing (for
+    the SWIG parser only) to work around this problem.
+
+commit 6c11b49607bb7f51277983e8aabcfd7a50abaf83
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 11:39:07 2015 +0200
+
+    C++: Make most constructors explicit
+    
+    Unless implicit conversion is desired, constructors that can be
+    called with one argument should be marked as "explicit".
+
+commit ce3e1e6132d7f6242f10bc8b8e205a6714451321
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 10:48:12 2015 +0200
+
+    C++: Do not use C-style casts
+    
+    Never ever.
+
+commit 58e21229dd553446fd389f75c6f595da9567f886
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 03:38:19 2015 +0200
+
+    C++: Consistently use nullptr instead of NULL
+
+commit d370545d6071690e7a8cc747db1134ced091e0de
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 11 03:12:02 2015 +0200
+
+    C++: Use move() and avoid passing containers by value
+    
+    Make use of std::move() to transfer arguments passed in by value.
+    Take complex container arguments by const reference, as passing
+    those by value is rather unorthodox even for C++11 style code.
+
+commit 211cc97471b2b837dbd4a78e796d61cea58a1b04
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Oct 25 23:05:49 2015 +0100
+
+    tests: Drop another obsolete sr_analog_float_to_string() test.
+
+commit a00106b7f819a2e64a65cfe767edae93bf2fb403
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Oct 24 22:27:45 2015 +0100
+
+    scpi: Move closing of discovered devices to sr_scpi_scan_resource().
+
+commit b7b873cea33d23b8368bef067934d03eab45c76f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Oct 24 21:33:34 2015 +0100
+
+    rigol-ds: After successfully finding a device, close it properly.
+
+commit d0fa4ac1e786490b47b9f4284ad045f65d1e058d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Oct 21 00:29:50 2015 +0100
+
+    java: Don't use SWIG attribute mechanism.
+    
+    Using the attribute mechanism results in badly named wrappers like
+    getLog_level(), as well as incompletely applied typemaps for templated
+    container types. If we just avoid this mechanism entirely, we get the
+    same foo() and set_foo() accessors as we have in the C++ API.
+
+commit d4db558810aba7c3ee66510a036f48cecf0d9d0d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Oct 21 00:20:18 2015 +0100
+
+    java: Remove overrides for overloaded methods.
+    
+    These are now wrapped correctly without needing this.
+
+commit f3095e7e2e2a4cc034a9253179c04dc5d129e882
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Oct 21 00:18:03 2015 +0100
+
+    java: Remove need for conversion methods on container wrappers.
+
+commit c7855def613435e18c1424ce87ffda8a42f1c53f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Oct 20 23:14:45 2015 +0100
+
+    SWIG: Declare template specialisations for containers before typemaps.
+    
+    It seems this is necessary to correctly apply typemaps involving
+    these template specialisations.
+
+commit 7977054d61554b6b24195f2255935881a92d935d
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Wed Sep 23 00:30:06 2015 +0200
+
+    scpi_usbtmc_libusb: remove libusb_clear_halt() hack which is not useful anymore
+
+commit a73665a3fa56f5997e20b24cc8c4fb8831ca131f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Oct 24 21:36:20 2015 +0200
+
+    transform/scale: Fix g_variant_new() argument.
+
+commit 938bdc25b766125350ba1fd46f5f95fdc813fb32
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Sep 24 21:43:06 2015 +0200
+
+    scpi_usbtmc_libusb: set_configuration only if it is not already set
+    
+    This avoids the issues described here:
+    http://libusb.sourceforge.net/api-1.0/caveats.html#configsel
+
+commit e40e9ca28d81d5ac43e1cb306390cfb7463b89e8
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Wed Sep 23 00:24:54 2015 +0200
+
+    scpi_usbtmc_libusb: add Rigol DS2000 to the RL1 blacklist
+    
+    The Rigol DS2000 series also give a USB timeout when trying to apply
+    RL1 lock or unlock.
+
+commit 222fdfd526fde6d8450067675679eca6cc5d211b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Oct 24 21:14:50 2015 +0200
+
+    Drop unneeded sr_analog_float_to_string().
+    
+    A simple g_strdup_printf() is sufficient, no need for an extra
+    libsigrok API call here.
+
+commit a5c38703eeea8661e563c30631601b8334dd2b7c
+Author: Tilman Sauerbeck <tilman at code-monkey.de>
+Date:   Thu Oct 15 22:38:12 2015 +0200
+
+    drivers: Fix behaviour when trying to set an invalid capture ratio.
+    
+    Trying to configure an invalid capture ratio would reset the
+    previously configured value. Instead, we should just reject the
+    new value and keep the original one.
+
+commit 087c4d59c09ee159700d3f5a01dfe2f77cbf0a8c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Oct 24 20:51:13 2015 +0200
+
+    fx2lafw: Drop obsolete macro usage.
+
+commit 3e91de2bd679e253570df91e4010f2a7c403f8c1
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Oct 21 23:41:50 2015 +0200
+
+    fx2lafw: Add the official fx2lafw sigrok VID/PID pairs.
+    
+    As supported by sigrok-firmware-fx2lafw >= 0.1.3:
+    
+     - 1D50:608C: fx2lafw-sigrok-fx2-8ch.fw
+    
+     - 1D50:608D: fx2lafw-sigrok-fx2-16ch.fw
+
+commit 22fb1bffc33b47f575c4b1c967b5051ddb505c5c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Oct 15 19:49:57 2015 +0200
+
+    analog.c: Various Doxygen additions and improvements.
+
+commit 5cee3d08e4943f9b0c844ee85f8c3d4a043cb654
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Oct 15 19:40:59 2015 +0200
+
+    analog.c: Return SR_ERR_ARG upon invalid arguments.
+    
+    (instead of segfaulting)
+
+commit 8ea0c90268574ccb4ff43344f7bb08f15c776a82
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Oct 15 19:38:02 2015 +0200
+
+    sr_analog_float_to_string(): Make 'digits' argument unsigned.
+
+commit 6b71bf1ba076f697bde8e41dbaa413bb3ec44326
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Oct 15 12:13:43 2015 +0200
+
+    Add a few unit tests for the analog conversion functions.
+
+commit dd13d47a9ef6c9a5c1c1920d8ac57e22bd1d9960
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Oct 2 16:18:35 2015 +0200
+
+    bindings: Use SR_DF_ANALOG, drop SR_DF_ANALOG_OLD support.
+    
+    All SR_DF_ANALOG_OLD packets are automatically converted to SR_DF_ANALOG
+    in the session already.
+
+commit edb691fcedb767093885c61d19d8d490c9c8c9c2
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Sep 29 18:34:55 2015 +0200
+
+    SR_DF_ANALOG2 and sr_datafeed_analog2 renames.
+    
+    Rename SR_DF_ANALOG2 to SR_DF_ANALOG, and 'struct sr_datafeed_analog2'
+    to 'struct sr_datafeed_analog'.
+
+commit 5faebab2903dc91949edc31f0a4b118d86090a30
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Sep 29 13:55:46 2015 +0200
+
+    SR_DF_ANALOG_OLD and sr_datafeed_analog_old renames.
+    
+    Rename SR_DF_ANALOG to SR_DF_ANALOG_OLD, and 'struct sr_datafeed_analog'
+    to 'struct sr_datafeed_analog_old'.
+
+commit ca79993bba12e8cb48c4327e92b9c296c10a9866
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Sep 10 00:34:53 2015 +0100
+
+    session: Convert from SR_DF_ANALOG to SR_DF_ANALOG2 automatically.
+    
+    This fixes bus #640.
+
+commit 453629c1375ff86494f44129aaebbc618b8830cf
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Sep 10 00:19:23 2015 +0100
+
+    output/csv: Support SR_DF_ANALOG2.
+
+commit b73cac758e32a28c56b9f021405b04838face3e3
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Sep 10 00:03:18 2015 +0100
+
+    output/wav: Support SR_DF_ANALOG2.
+
+commit 0662a7d083b29730f376a34cb1b7009b307db979
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Sep 9 23:57:18 2015 +0100
+
+    transform/invert: Support SR_DF_ANALOG2.
+
+commit b1aa4f34bc5f48ed9e08ff63eb762f9b0e4ceb60
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Sep 9 23:49:01 2015 +0100
+
+    transform/scale: Support SR_DF_ANALOG2.
+
+commit 2d237f3ce88eacbaf6ff84a38fd99f2ed6ed0f8f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Sep 9 23:39:15 2015 +0100
+
+    transform/scale: Use a rational rather than floating point factor.
+
+commit 85aa1b599fdb40d8a8df65744186688f85e53dd7
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Oct 20 21:05:36 2015 +0100
+
+    java: Fix SWIG warnings due to dodgy %extend redefinitions.
+    
+    If we're going to %extend these methods, we need to firstly ignore the
+    originals, and secondly implement all possible argument combinations.
+    
+    This fixes the rest of bug #417.
+
+commit e66728886ff684e286a2544986116251e4c5531e
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Oct 20 20:38:37 2015 +0100
+
+    python: Prevent warning about deprecated NumPy API.
+    
+    Without this we get:
+    
+    /usr/include/python2.7/numpy/npy_1_7_deprecated_api.h:15:2: warning: #warning
+    "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API
+    NPY_1_7_API_VERSION" [-Wcpp]
+    
+    As far as I'm aware we're not using any deprecated NumPy C API features.
+    
+    This fixes part of bug #417.
+
+commit fa72105fe621bce7767f2b3512223182f2bf6f33
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Oct 10 04:45:13 2015 +0200
+
+    python: Fix PyObject_CallMethod() arguments
+    
+    Pass nullptr instead of the empty string for format, and remove
+    the excess varargs argument. Also, avoid NULL in favor of nullptr.
+
+commit 2c3c9b999cd5c1130afeeee4094400592c9e1bde
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Oct 10 04:38:54 2015 +0200
+
+    python: Wrap session stop callback
+
+commit ee9953ef12514d833c6719777dfc285d56c0ffc1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 9 19:46:14 2015 +0200
+
+    session: Make event source injection API private
+    
+    Also remove the corresponding functionality from the bindings.
+
+commit f91cf612dfde284c8a146417644fbfba05cca1a2
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 9 19:24:51 2015 +0200
+
+    C++: Add bindings for session stop notification
+
+commit 2e5e3df4e4a18e3c539220c650f2e2ba188430c7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 9 15:49:12 2015 +0200
+
+    session: Keep reference to main context while running
+
+commit 5de0fc55a6ac8da084caf427f6131482a40855dc
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 9 02:35:47 2015 +0200
+
+    session: Make sr_session_run() optional
+    
+    Introduce a new API function sr_session_stopped_callback_set()
+    which can be used to receive notification when a session stops
+    running. This allows applications to integrate libsigrok event
+    processing with their own main loop, instead of blocking in
+    sr_session_run().
+
+commit 45d835edc7f1ce505245056021216d4bdc8a0ae0
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Oct 8 20:51:55 2015 +0200
+
+    resource: Do not require size to stay valid after close
+    
+    Chalk one up for uber-correctness, just in case.
+
+commit 0a4549ff1851180e4ed390132c1233304e07751b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Oct 16 19:48:13 2015 +0200
+
+    Add support for the Tenma 77-7732 multimeter.
+
+commit 20f9f1fbaa6c61019b0dfaef27fdf40010cce0c5
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Oct 16 19:38:52 2015 +0200
+
+    Add support for the Tenma 77-9380A multimeter.
+
+commit 896fc9c7231c7567cda84404eb0fcdd889b556a2
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 16 20:45:43 2015 +0200
+
+    build: Require libzip 0.11
+    
+    This is needed for zip_discard().
+
+commit eaa6a7a3e8f9b62e6aa139f976c243266628263f
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 16 20:35:32 2015 +0200
+
+    srzip: Avoid recent-ish zip_file_add()
+
+commit ef2bcf114f1d40d7016eefc668caf3d91541be34
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 16 21:28:07 2015 +0200
+
+    korad-kdxxxxp: Use PRIi64 format for int64_t
+
+commit b1cadcfbef49ab24dbcf0987b927c4bffa207fcc
+Author: Karl Palsson <karlp at remake.is>
+Date:   Tue Oct 13 16:19:24 2015 +0000
+
+    drivers: Add support for Tenma 72-7730 multimeter
+    
+    Tested with a 72-7730 device.
+    
+    Signed-off-by: Karl Palsson <karlp at remake.is>
+
+commit 739a1ec4f8714ee77f19c393345a67d321982c11
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Oct 13 23:26:40 2015 +0200
+
+    configure.ac: Korad KDxxxxP depends on libserialport.
+
+commit d70830427530115003f3c8a592a853965ba4fc10
+Author: Hannu Vuolasaho <vuokkosetae at gmail.com>
+Date:   Fri Sep 25 16:43:17 2015 +0200
+
+    Initial driver for Korad KDxxxxP (Velleman LABPS3005D)
+    
+    With this driver it is possible to set voltage target and current
+    limit. Also enabling and disabling the output is possible.
+    
+    Analog output sends read back values from output. If output is
+    disabled analog outputs 0.00.
+    
+    In protocol.c there is a g_usleep() call. This gives almost
+    every time enough time for PSU to parse and process input.
+    
+    Multichannel devices aren't supported.
+
+commit e75ee7de25d52356ba7cc44b7141b24283e49d1a
+Author: Hannu Vuolasaho <vuokkosetae at gmail.com>
+Date:   Thu Sep 24 21:58:38 2015 +0200
+
+    korad-kdxxxxp: Initial driver skeleton.
+
+commit a7da85f5299dd4162b26ab9c64224e52b52470ab
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Fri Oct 9 15:52:28 2015 +0200
+
+    baylibre-acme: Fix EEPROM fields endianness.
+    
+    Use RB32 instead of RL32 since integer types in probe EEPROM are in network
+    byte order.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 4c3a4bca321df48455d4484753783dc6da17aa15
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Fri Sep 25 14:48:30 2015 -0700
+
+    baylibre-acme: Read EEPROM contents into buffer before processing.
+    
+    Instead of using a non-standard packed attribute, read the contents of the
+    probe EEPROM into a buffer and then process it using the R* macros.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 453a0c2ece77fc2dc7cb8a3025a1dec9cd5bfb6e
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Sat Sep 19 20:08:26 2015 +0200
+
+    baylibre-acme: Use fixed-size integer types in struct probe_eeprom.
+    
+    Standard integer types may differ in size on different targets. Use fixed-size
+    types instead.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 7f4d69305b1d86cbe3785b5d041dcac1351f0731
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Oct 4 22:11:39 2015 +0200
+
+    C++: Remove leftover Context::begin_save() method
+
+commit 655b116cc102a2ada89e95fd3daa728aa78f10eb
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Oct 3 14:08:32 2015 +0200
+
+    build: Do not define FIRMWARE_DIR on Windows
+    
+    The hard-coded location is bound to be wrong anyway. Instead, rely
+    on the new resource lookup code to find the firmware files in a
+    location relative to the library or executable.
+
+commit 127c9cec824721bf52e8951f330d1d5a16c1a9b0
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Oct 3 13:58:50 2015 +0200
+
+    resource: Make definition of FIRMWARE_DIR optional
+
+commit 2c38a41a60b8ebb8137d3b5b14ae652c66af6034
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 2 20:09:46 2015 +0200
+
+    session-file: Use 32-bit int for channel count
+    
+    SR_CONF_NUM_LOGIC_CHANNELS is defined as SR_T_INT32. Create the
+    GVariant with the correct type to avoid a type mismatch error in
+    sr_variant_type_check().
+
+commit 5eff221e8c9a84be71a564441c6edf705f8ba8fa
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Oct 2 00:36:10 2015 +0200
+
+    usb: Skip add/remove of FD on destroyed source
+    
+    This avoids nasty GLib-CRITICALs on Windows with some drivers.
+
+commit e2eaf8580aaa8869a694b3c6af4c7f832a752b31
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 27 00:03:21 2015 +0200
+
+    C++: Wrap resource access API
+    
+    Introduce a ResourceReader delegate class with virtual methods
+    corresponding to the C callback functions.
+
+commit 8e2d6c9db788785466d61fdac4d8fdc1535bc20c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Sep 26 22:41:05 2015 +0200
+
+    drivers: Load firmware via new resource API
+
+commit 7d89fd60e5bce5bbb3271a5ecd54c3c6c63dc6bf
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Sep 26 18:10:28 2015 +0200
+
+    resource: Move sr_file_get_size() to resource.c
+
+commit bee246665b05a59f91ff7d51040cbaee614ab0c7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Sep 26 13:38:30 2015 +0200
+
+    resource: New internal API for accessing resource files
+    
+    The resource API provides a generic means for accessing resources
+    that are bundled with sigrok, such as device firmware files. Since
+    the manner of resource bundling is platform-dependent, users of
+    libsigrok may override the functions used to open, close and read
+    a resource. The default implementation accesses resources as files
+    located in one of the XDG data directories or a directory defined
+    at compile time.
+
+commit 98654c99daf85bd2a81a9c87f517e800c31e34f2
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 20 20:32:03 2015 +0200
+
+    srzip: Avoid low-level FD-based I/O
+    
+    Use in-memory buffers instead of temporary files. This avoids
+    the need for low-level I/O on the FD returned by g_mkstemp().
+    Refactor the code accordingly. Also plug a number of leaks and
+    tighten the error checking.
+
+commit 5e1fb33469841b194e606752b865fa0d08b99067
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 20 14:10:42 2015 +0200
+
+    session-file: Remove old session save API
+    
+    Completely remove the old session save code that has been
+    superseded by the srzip output module. Also refactor a bit,
+    plug a number of leaks and tighten the error checking.
+
+commit 8d4097ffa69249be0fac8ab5d6a6e1e0b5fc368b
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 20 14:07:01 2015 +0200
+
+    virtual-session: Plug ZIP archive leak
+    
+    Refactor a bit to keep the code manageable. Also allow
+    stopping of a running acquisition.
+
+commit 02e49ae5d2e37daa6e3e76a4123000bc5d77ee1f
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 20 14:06:06 2015 +0200
+
+    virtual-session: Advertise all config keys
+
+commit 4619fab47acf4fb0a1c035072c2e8552b4bcbae3
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Sep 19 20:43:25 2015 +0200
+
+    input: Use fseeko/ftello to get the size of a file
+    
+    Introduce the sr_file_get_size() utility function to retrieve the
+    size of an open FILE stream. This is based on fseeko() followed by
+    ftello(), which are POSIX functions but quite portable in practice.
+    Since these calls operate on FILE streams instead of filenames, the
+    issue of filename encoding no longer arises.
+
+commit 5e364d4fe0b6367717d7dcf3389685b8fe985211
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Sep 19 00:24:50 2015 +0200
+
+    input: Clean up input file scanning
+    
+    Do not use Unix low-level I/O for reading a regular input file.
+    Read in the file header once and re-use the buffer for all input
+    modules participating in the scan. Also re-use a prefilled metadata
+    table instead of creating it anew for each input module tried.
+
+commit ff85d65ec69bdc53b258d20df64e5cd022339f30
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Sep 30 19:42:19 2015 +0200
+
+    drivers.c: Fix HAVE_HW_GWINSTEK_GDS_800 position.
+
+commit b11afbb142a9ddc3fd2c99f3d3be39683cdec4ac
+Author: Martin Lederhilger <martin.lederhilger at gmx.at>
+Date:   Sun Aug 16 19:52:02 2015 +0200
+
+    gwinstek-gds-800: Initial driver implementation.
+
+commit 7c198f968b8145321e3b7066c2a549b028a6320b
+Author: Martin Lederhilger <martin.lederhilger at gmx.at>
+Date:   Sun Aug 16 14:52:49 2015 +0200
+
+    gwinstek-gds-800: Initial driver skeleton.
+
+commit 6e799d0799abb2c8c0ca75238fff902e31453108
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Sep 25 09:00:05 2015 +0200
+
+    manson-hcs-3xxx: Fix use-after-free and memory leaks.
+    
+    Thanks to Hannu Vuolasaho for the report!
+
+commit e57057aee778e723da572a6b5e2bd01526cc7beb
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Sep 25 08:52:58 2015 +0200
+
+    Fix a few "value never read" scan-build warnings.
+    
+    This fixes parts of bug #423.
+    
+    The list of fixed warnings:
+    
+    src/output/srzip.c:285:3: warning: Value stored to 'ret' is never read
+                    ret = zip_append(o, logic->data, logic->unitsize, logic->length);
+                    ^     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    src/scpi/scpi.c:610:2: warning: Value stored to 'ret' is never read
+            ret = SR_OK;
+            ^     ~~~~~
+    src/scpi/scpi.c:667:2: warning: Value stored to 'ret' is never read
+            ret = SR_OK;
+            ^     ~~~~~
+    src/dmm/vc870.c:410:2: warning: Value stored to 'info_local' is never read
+            info_local = (struct vc870_info *)info;
+            ^            ~~~~~~~~~~~~~~~~~~~~~~~~~
+    src/hardware/conrad-digi-35-cpu/api.c:130:2: warning: Value stored to 'ret' is never read
+            ret = SR_OK;
+            ^     ~~~~~
+    src/hardware/fx2lafw/api.c:658:2: warning: Value stored to 'timeout' is never read
+            timeout = fx2lafw_get_timeout(devc);
+            ^         ~~~~~~~~~~~~~~~~~~~~~~~~~
+    src/hardware/gmc-mh-1x-2x/protocol.c:941:3: warning: Value stored to 'retc' is never read
+                    retc = SR_ERR_ARG;
+                    ^      ~~~~~~~~~~
+    src/hardware/gmc-mh-1x-2x/api.c:168:2: warning: Value stored to 'model' is never read
+            model = METRAHIT_NONE;
+            ^       ~~~~~~~~~~~~~
+    src/hardware/ikalogic-scanalogic2/api.c:325:2: warning: Value stored to 'ret' is never read
+            ret = SR_OK;
+            ^     ~~~~~
+    src/hardware/openbench-logic-sniffer/api.c:185:3: warning: Value stored to 'devc' is never read
+                    devc = sdi->priv;
+                    ^      ~~~~~~~~~
+    src/hardware/rigol-ds/api.c:813:3: warning: Value stored to 'devc' is never read
+                    devc = sdi->priv;
+                    ^      ~~~~~~~~~
+    src/hardware/scpi-pps/api.c:405:2: warning: Value stored to 'ret' is never read
+            ret = SR_OK;
+            ^     ~~~~~
+    src/hardware/yokogawa-dlm/api.c:239:2: warning: Value stored to 'ret' is never read
+            ret = SR_ERR_NA;
+            ^     ~~~~~~~~~
+
+commit 36cbd69e12d8957592c94420d98552bb00d67be6
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 22 16:12:01 2015 +0200
+
+    demo: Strictly round up the number of samples to send
+    
+    This avoids getting stuck when the time limit is less than
+    half of the sampling interval.
+
+commit 98c01fe1277e5a386377347635a8a7fee8abc534
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 22 06:54:33 2015 +0200
+
+    demo: Increase timer interval to 100 ms
+    
+    Timer intervals shorter than about 100 ms are unnecessarily taxing
+    on system resources. Also, on systems like Windows the smallest
+    resolvable time unit without using high precision timers is about
+    15 ms. Regular timer intervals should be well above that value to
+    avoid being dominated by noise and round-off.
+
+commit a49a320deaddbcc8caedb3247a695b97e22b37b3
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 22 06:49:17 2015 +0200
+
+    demo: Fix continuous mode and honor time limit
+    
+    Also deal more gracefully with changes to the samplerate while
+    running.
+
+commit 01b062390aa2fa488571f02b743fc4e6e6167657
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 22 00:53:28 2015 +0200
+
+    scpi/usbtmc: Add Agilent DSO1000 series to RL1 blacklist.
+    
+    These are rebadged Rigol DS1000 scopes, and suffer from the same USBTMC
+    implementation.
+
+commit de285cce11aca3afd0d4adfdd514c691e6a71c64
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 21 13:45:36 2015 +0200
+
+    scpi/usbtmc: Implement Rigol DS1000 workaround on any firmware version.
+    
+    Firmware versions starting with 00.02.04 apparently cause the in and out
+    bulk endpoints to end up in a HALT state. This is likely related to the
+    larger transfer size quirk implemented in the Linux kernel for the Rigol
+    DS1000: this USBTMC implementation does not have that workaround.
+    
+    Instead, if the firmware version is >= 00.02.04, both endpoints have the HALT
+    condition cleared on device close.
+    
+    This fixes bug #354.
+
+commit 04229f7bfc750f2b67e8dd54ac82ae6bb7eae1e4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 21 13:41:03 2015 +0200
+
+    scpi: Pass SCPI device instance to open and close callbacks.
+    
+    Only close() really needs this (for access to a Rigol firmware quirk),
+    but do the same in open() for consistency.
+
+commit 27cd728f8a52026be5d83890c3d911fa29ee62f9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 21 12:39:33 2015 +0200
+
+    scpi/libgpib: Fix format argument in error message.
+
+commit 6703bb2983deb65a3ce162acbae251ee487fb6af
+Author: Scott Allen <saydisp-git at yahoo.ca>
+Date:   Fri Sep 18 21:42:24 2015 -0400
+
+    Fix RadioShack 22-812 DMM incorrect readings.
+    
+    The wrong byte was being used to test for the nano indicator.
+    
+    This resulted in reported resistance and capacitance readings being off
+    by orders of magnitude.
+    
+    This fixes bug #657.
+
+commit 115826529c6e7b69aff731c87e54ecb19cc2b649
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Sep 19 16:28:52 2015 +0200
+
+    scpi/usbtmc: Implement blacklist for RL1 feature.
+    
+    This is initially for the Rigol DS1000 series which pretends to support
+    RL1, but doesn't.
+
+commit b07a1b04e5da31e02b45ddd32e6b006b0329e02c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Sep 7 09:15:52 2015 +0100
+
+    sr_analog_to_float: Avoid comparison between signed and unsigned.
+    
+    The check for p == q is basically checking whether p/q == 1. We should
+    be normalising the rational before it gets here though, so in this case
+    we should have p == q == 1 here.
+
+commit 53e5d3d14d30707ea7d84e75e469314a0ae570bf
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Sep 9 23:41:27 2015 +0100
+
+    sr_rational_set: Accept signed numerator.
+
+commit 18ea6b76fdc77c4b1c1cc08bea4cd42d465787e6
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Sep 7 09:15:35 2015 +0100
+
+    sr_rational: Make numerator signed.
+
+commit 83c1dbd9b547edc3f0aec80d7427b14672654d4a
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 5 18:26:30 2015 +0100
+
+    sr_packet_free: Support SR_DF_ANALOG2.
+
+commit dbdfa4fb50f1ecf16b1d88d0d938c6235474128d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 5 18:22:33 2015 +0100
+
+    sr_packet_copy: Support SR_DF_ANALOG2.
+
+commit 7d65dd3a8654ff303c76b917865941e3ae379458
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Sep 17 18:14:09 2015 +0100
+
+    sr_analog_to_float: Support packets with multiple channels.
+
+commit 3e27754989f631bd059d2797c61cdf1c884b1f1e
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Sep 17 02:04:19 2015 +0100
+
+    sr_analog_to_float: Fix byte reordering.
+
+commit fa74a26bc94c718bf1b965d553e89c29b4d7133c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Sep 10 00:45:32 2015 +0100
+
+    output/analog: Allocate correct buffer size for sr_analog_to_float().
+
+commit 39e0113533dedca8f929fcf682881f1c5afee362
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 5 18:03:23 2015 +0100
+
+    sr_packet_copy: Allocate memory to copy analog data.
+
+commit 2a8f2d41adcd0aa9e498c4eea2a5f82263039e5c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Sep 19 17:59:54 2015 +0200
+
+    No need to check return value of libusb_get_device_descriptor().
+    
+    Since libusb 1.0.16 this is guaranteed to always succeed.
+    
+    This fixes bug #658.
+
+commit 8de8551b8809b0818c20690c3014df6e6c4ee7fc
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Sep 19 17:58:22 2015 +0200
+
+    Remove unnecessary call to libusb_get_device_descriptor().
+
+commit 069d9f25d9f63e35885a7e4c83e8587f0356d8f9
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Sep 20 16:43:43 2015 +0200
+
+    scpi-pps: cleaner rewriting of output regulation GVariant
+
+commit d66c93ccdafabffc0dda4f16bf3924a829cdb3bd
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Sep 20 16:22:21 2015 +0200
+
+    scpi-pps: sr_scpi_get_string() is already called by scpi_cmd_resp()
+    
+    This double call was causing the following error:
+    sr: scpi_usbtmc: USBTMC bulk in transfer error: LIBUSB_ERROR_TIMEOUT.
+
+commit 6ab604c5c2001b5d47a00ce635c0a2438bed4d06
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Sep 20 16:19:48 2015 +0200
+
+    scpi: add obviously missing else statements
+
+commit 485a285abea07fa296ee56517cf95b4dbab69e29
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Sep 20 16:18:18 2015 +0200
+
+    scpi: don't stop parsing table at command 0 which is a valid command
+    
+    command 0 is SCPI_CMD_REMOTE
+
+commit 06f63a749ea15ae88e8183404a0d6e3d1fd85de3
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Sep 20 16:15:01 2015 +0200
+
+    scpi: scpi_cmd_resp() shouldn't return SR_OK without filling the gvar
+    
+    scpi-pps at line 212 assumes that an SR_OK return means that the gvar
+    is valid, which leads to the following error:
+    ** GLib:ERROR:/build/glib2.0-2.45.8/./glib/gvarianttypeinfo.c:184:g_variant_type_info_check: assertion failed: (0 <= index && index < 24)
+
+commit baed0211a1ecada69c5ab53cf979e2e543a0ceaa
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Sep 17 23:31:04 2015 +0200
+
+    scpi/usbtmc: Support RL1 feature.
+    
+    This automatically locks out local controls when a USBTMC device is
+    opened, and returns local control on close.
+
+commit b41bbfdbe2ae6076a6e074ef9652d299657e83fd
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Wed Sep 16 15:24:40 2015 +0200
+
+    baylibre-acme: gpio: Don't set direction in get/set functions.
+    
+    GPIO direction should be set once right after exporting. There's no need
+    to reset it again - in fact it's a bug which causes the probe to be reset
+    every time the value is read/set and gives incorrect results when reading
+    the GPIO values with direction == 'in'.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 4678bdb60792377cec015d7e18cf8ffd80419c69
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Wed Sep 16 14:18:11 2015 +0200
+
+    baylibre-acme: Add basic support for ACME revB.
+    
+    Revision B of ACME hardware introduces probes with on-board at24cs02
+    EEPROM. Extend the ACME driver to support reading the contents of
+    the EEPROM via linux' sysfs interface.
+    
+    Also: make the driver be able to tell the difference between revisions,
+    add new GPIO layout and set the shunt resistance for revB at probe
+    registration.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 8f1961209b399eb29933168e8126e59b5943f854
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Wed Sep 16 13:30:54 2015 +0200
+
+    baylibre-acme: Check for power-switch presence at probe's initialization.
+    
+    Only perform a single check at initialization time to see if the probe is
+    equipped with a power-switch. This is done in preparation for revision B
+    support which has this kind of information encoded in EEPROM.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 133016f6ab9fe8a360d5cfc13d0b6ac68cc14bcf
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Wed Sep 16 10:55:42 2015 +0200
+
+    baylibre-acme: Move enum channel_type to protocol.c.
+    
+    This enum is private and only used within protocol.c. Don't expose it
+    in protocol.h.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit ad6181e25ea04c1cfed6bd89d108a60454ab6cb6
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Sep 16 22:35:13 2015 +0200
+
+    sysclk-lwla: Set USB device configuration
+    
+    After opening the USB device, set the device configuration to 1.
+    Actually, do it twice, just as the vendor driver seems to do. This
+    is supposed to trigger a lightweight reset of the device.
+    
+    Originally, I omitted this reset sequence from the sigrok driver
+    because it simply did not work at all for me. However, it does seem
+    to work now, so that may have been a problem in libusb or the kernel
+    which is now fixed.
+    
+    With some luck, this change may finally fix #327.
+
+commit c81069b33760f6de36466410ad4bbc9cb762e938
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Sep 16 21:31:42 2015 +0200
+
+    sysclk-lwla: Clean up open/closed state handling
+    
+    Use states SR_ST_ACTIVE and SR_ST_INACTIVE to indicate that the
+    device is open or closed, respectively. Do not use any of the
+    other state values. Improve the robustness of the open and close
+    methods in face of errors. Introduce a separate flag to indicate
+    that a running acquisition should be canceled.
+
+commit 225d3cb0c52fc3df5b26fec94d3b3d2de796a41c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 30 18:20:41 2015 +0200
+
+    sysclk-lwla: Streamline trigger setup logic
+    
+    Prepare the trigger masks at config_commit() time, so that the
+    trigger setup can be validated before starting an acquisition.
+    Accordingly, do actually report validation errors back to the
+    caller.
+
+commit 2a09aac26862e8b8c88baa74634f6cb8f62352fc
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Sep 16 23:33:45 2015 +0200
+
+    fx2lafw, sysclk-lwla: Avoid g_stat()
+    
+    It turns out that g_stat() breaks apart when using 64 bit stat on
+    32-bit systems. Since the actual type of GStatBuf is decided when
+    glib/gstdio.h is included, it is thus possible for GLib itself to
+    be compiled with a different type than user code.
+    
+    Ouch. Unfortunately going back to plain stat() also means that we
+    lose Unicode filename support on Windows.
+
+commit eb2373f1674706bd050c9b7c00927e42b70ea0f2
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 15 18:26:51 2015 +0200
+
+    configure: Check for numpy Python module
+    
+    This fixes bug #533.
+
+commit 9851d35b0aa4745f93a4812af8f25d21de041020
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Sep 10 18:52:15 2015 +0200
+
+    lascar-el-usb: Fix copy-paste errors.
+
+commit 3182d4ebb3618e8745e83650f2568bf42d612f93
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 14 17:54:48 2015 +0200
+
+    Makefile.am: Shorten ChangeLog target a bit.
+
+commit 1a65c5e86fc040608160c77a57008f69b3fd05ca
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 14 00:01:18 2015 +0200
+
+    motech-lps-30x: Fix blocking serial write timeout.
+    
+    This fixes bug #438.
+
+commit d545c81c96b5058e52e5354269972876026dd7e3
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 14 00:00:22 2015 +0200
+
+    norma-dmm: Fix blocking serial write timeout.
+    
+    This fixes bug #434.
+
+commit 936b1c00e86855b854390d8cdb54ccb3d631dec0
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Sep 13 23:57:32 2015 +0200
+
+    gmc-mh-1x-2x: Fix blocking serial write timeout.
+    
+    This fixes bug #432.
+
+commit 8b9eb639b2e12ab2f9d710af7342f26283747904
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Sep 13 23:55:22 2015 +0200
+
+    conrad-digi-35-cpu: Fix blocking serial write timeout.
+    
+    This fixes bug #430.
+
+commit b9ed8eabfd5cc35cfc2c7b61d5d64fa18b478205
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Sep 13 23:53:55 2015 +0200
+
+    brymen-dmm: Fix blocking serial write timeout.
+    
+    This fixes bug #427.
+
+commit 84d328ac9810d4b210ef1b0fd04c7f47bde1f928
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Sep 13 23:48:09 2015 +0200
+
+    center-3xx: Fix blocking serial write timeout.
+    
+    This fixes bug #428.
+
+commit 5360d6d70684bc4e3ed4bbe31c842242b9d32761
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Sep 13 23:47:16 2015 +0200
+
+    mic-985xx: Fix blocking serial write timeout.
+    
+    This fixes bug #433.
+
+commit 896e1a45e0f7004a993c9d5d7928f284f2ba91cf
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Sep 13 23:20:03 2015 +0200
+
+    manson-hcs-3xxx: Fix blocking serial write timeout.
+    
+    This fixes bug #437.
+
+commit 32c45845f70c41137cbfa7e772bf79791e269895
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Sep 13 22:44:40 2015 +0200
+
+    tondaj-sl-814: Fix blocking serial write timeout.
+    
+    This fixes bug #436.
+
+commit 428d79a810fd4e2b905faf06f0b2e6cadaed82fe
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Sep 13 21:43:17 2015 +0200
+
+    Drop obsolete SR_CONF_MEASURED_2ND_QUANTITY.
+
+commit 000f504f246f713a86d653e0d6ee6c98a07e74fd
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 20:12:12 2015 +0200
+
+    classes.cpp: Define _XOPEN_SOURCE for isascii()
+    
+    The GNU libstdc++ headers use isascii(), which is not part of any
+    POSIX standard. On BSD, this breaks the build. It is however part
+    of XOPEN, which on Linux is apparently enabled implicitly for C++.
+    
+    This should fix #649.
+
+commit 56c8705e37314dafe35b680d2254b6f844e0ecc2
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 18:35:28 2015 +0200
+
+    Build: Move _POSIX_C_SOURCE definition to config.h
+    
+    Do not redefine it though when already set, so that it can be
+    overridden by the user, or indirectly by the compiler settings.
+
+commit 6ec6c43b4738dbc7091f4a49a4ec80ea6102cb52
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 18:22:17 2015 +0200
+
+    Build: Include <config.h> first in all source files
+    
+    Since Autoconf places some important feature flags only into the
+    configuration header, it is necessary to include it globally to
+    guarantee a consistent build.
+
+commit 4d6a50085ea9ea4f25850b22737f621a3e13d953
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Sep 11 23:22:54 2015 +0200
+
+    libsigrok-internal.h: Remove unused prototypes
+    
+    The sr_source_* prototypes are not used anywhere, so remove them.
+    Also get rid of the SERIAL_PARITY_* aliases for SP_PARITY_*.
+
+commit cbc1413f31f3946ce79da5540cfbeace8924c9d1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Sep 11 23:08:10 2015 +0200
+
+    serial: Make serial device event sources more robust
+    
+    Disallow polling for input/error and output-ready events at the
+    same time, and ensure only a single FD event source is installed.
+    Also, do not leak if the FD event source is removed by means
+    other than calling serial_source_remove().
+
+commit 0c536bcd004aa4b4f0ba9673b4d460551e8b16c1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 15:39:04 2015 +0200
+
+    Fix two more format warnings uncovered by MinGW build
+
+commit 7419638d4c5d21dadccfd8f234c5671c5330dc33
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 15:07:54 2015 +0200
+
+    Build: Force ISO-conforming format syntax on MinGW
+    
+    On MinGW, two implementations of printf() are available: either
+    the Microsoft native one or a standard-conforming replacement from
+    gnulib. Since we build in C99 mode, headers such as <inttypes.h>
+    already select the standard-conforming variant. However, MinGW's
+    GCC does not seem to know about this and assumes MS-style format
+    syntax by default, which triggers a lot of wrong warnings.
+    
+    Thus, on MinGW, explicitly decorate sr_log() with the gnu_printf
+    format flavor attribute. Also use GLib's printf replacements in
+    the logging implementation to make sure we link to a conforming
+    printf on any platform, independently of the compiler flags.
+    
+    This gets rid of the mistaken -Wformat warnings for sr_log(), but
+    does not cover functions such as g_strdup_printf() which do not
+    explicitly specify the gnu_printf flavor in the format attribute.
+    This can be overcome by adding "-D__printf__=__gnu_printf__" to
+    CPPFLAGS, but it would be inappropriate for libsigrok to define
+    this on its own.
+
+commit 22c50ed973443db12a5569dbc3d698bf98167833
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 12:32:03 2015 +0200
+
+    log: Bring back the "sr: " default log prefix
+    
+    This got lost accidentally with the removal of the logdomain API.
+
+commit 782b16447b25ce31d41764ae91681a6aa4f3fe0d
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 11:58:44 2015 +0200
+
+    log: Remove sr_log_logdomain_{get,set} from the API
+    
+    The confusingly named sr_log_logdomain_set() simply set a global
+    string prefixed to the log message by the default log callback.
+    This is pretty much useless, misleadingly named, and not used by
+    either sigrok-cli or PulseView.
+
+commit 6433156c3275df933e4bf6dcfb020c91fca0ae86
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 02:29:38 2015 +0200
+
+    Fix log varargs bugs indicated by -Wformat
+    
+    A few of these were pretty serious, like missing arguments,
+    passing integers where a string was expected, and so on.
+    In some places, change the types used by the code rather than
+    just the format strings.
+
+commit 6d9da8efbf1429301922eb7ab1551866362544ab
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Sep 13 00:58:36 2015 +0200
+
+    log: Output timestamps at level SR_LOG_DBG too
+    
+    SR_LOG_DBG and above are targeted at developers, so it makes sense
+    to extend timestamp output to that. Also sanitize the calculation
+    of the timestamp components a bit.
+
+commit be92d5b4ee4a7ebc5096e9e4718be0ed34ce690c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Sep 12 22:41:22 2015 +0200
+
+    log: Use generalized sr_log() to implement logging helpers
+    
+    Get rid of the specicialized sr_err(), sr_warn(), etc. functions.
+    Instead, define the logging helper macros in terms of sr_log(),
+    and remove the sr_log() helper macro so that no function is hidden
+    by a macro anymore.
+    
+    Decorate sr_log() with G_GNUC_PRINTF to detect varargs errors. This
+    unearthed a gazillion warnings all over the place which will have
+    to be fixed.
+    
+    Also convert the helper macros to ISO C99 __VA_ARGS__ style instead
+    of relying on a GNU C extension. Paste the log prefix directly into
+    the format string to make this work.
+
+commit ab0b34584c94349c12ad7b33fb6b52f4f1242755
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Sep 11 19:15:55 2015 +0200
+
+    session: Return immediately if there are no event sources
+    
+    Some drivers, such as zeroplus-logic-cube, run everything they do
+    right away in dev_acquisition_start(), never installing any event
+    sources. Handle that evilness by returning from sr_session_run()
+    immediately if there are no sources.
+
+commit c2bf5506ee7864975917a7b6e7e93b78226887ce
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Sep 10 09:46:45 2015 +0200
+
+    session: Port to GLib main loop
+    
+    Replace the custom session main loop with the GLib main loop.
+    This is phase one of the port, which leaves the session and
+    driver APIs unchanged while replacing the internals.
+
+commit 041b579d530af4c9bbdb534d0c932c177f1b9014
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Sep 10 20:41:55 2015 +0200
+
+    serial: On Windows, include <windows.h> for HANDLE
+
+commit 69f68553ae4004d74a702b60274754ef7b93cc14
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Sep 10 19:38:10 2015 +0200
+
+    sigrok.m4: Remove SR_PROG_MAKE_NO_PRINT_DIRECTORY
+
+commit bcc3694ff76ce61ab3754b4ad3e1a27bc8ea162b
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Sep 10 19:35:17 2015 +0200
+
+    Build: Use GNUMAKEFLAGS for --no-print-directory
+
+commit 96e05afa77df9d3ee49ba602503476a798f4db06
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Sep 10 18:36:30 2015 +0200
+
+    hung-chang-dso-2100: Enable only if libieee1284 is available
+
+commit c9404469185faf035390aeb6ced077a70601b756
+Author: Daniel Glöckner <daniel-gl at gmx.net>
+Date:   Mon Sep 7 02:18:14 2015 +0200
+
+    hung-chang-dso-2100: Add driver
+    
+    The Hung-Chang DSO-2100 is a parallel port PC oscilloscope sold back
+    in 1999 under brand names like Protek and Voltcraft.
+    
+    This inital version of the driver has the following limitations:
+    
+     - Hardcoded calibration values. All parameters are set to 50%.
+     - No support for auto triggering
+     - No support for TV sync trigger modes
+     - No support for the "scroll acquisition" mode
+    
+    In scroll acquisition mode the device behaves more like a multimeter
+    and reports the current voltage of a probe on request. While in this
+    mode the sample rate is limited by the parallel port interface, it is
+    the only way to capture both channels at the same time (well, sort of).
+    
+    Calibration would need auto triggering. The calibration values are very
+    temperature dependent and the device takes literally hours to reach its
+    final temperature. Every vdiv setting needs its own set of calibration
+    values. Without hardware modifications, the calibration settings wear
+    of in less than a second while waiting for a trigger because the
+    capacitors storing those values are not recharged in state 0x21.
+
+commit 05ac4a9828fc73bee0d420e96104d9cf3c5b658a
+Author: Daniel Glöckner <daniel-gl at gmx.net>
+Date:   Mon Sep 7 02:18:13 2015 +0200
+
+    hung-chang-dso-2100: Initial driver skeleton.
+
+commit 7237e91262251a138cf150f9fcfe7b05d0e5904b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Sep 9 09:16:58 2015 +0200
+
+    portability: Use g_strerror() in favor of strerror().
+
+commit 34577da641d836dcf27433fee3c6384565ca7847
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Sep 9 09:17:59 2015 +0200
+
+    portability: Use g_ascii_strcasecmp() in favor of strcasecmp().
+    
+    This is more portable and guaranteed locale-independent.
+
+commit ba3070ff3412bd547d2f1554430818de9f003733
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Sep 9 08:21:01 2015 +0200
+
+    Makefile.am: Use @ORDER@ instead of |.
+    
+    This is set to | (or left empty) by SR_PROG_MAKE_ORDER_ONLY for
+    portability reasons, since not all Make implementations support
+    order-only prerequisites.
+
+commit fd31865ef42ad6b48f47a616c1aebd9e5b4c1ec1
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Sep 7 12:32:05 2015 +0100
+
+    Don't rebuild Python or Java bindings unless C++ interface changes.
+
+commit 42f4619d6b436e1db7685692281ae60999527106
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Tue Sep 8 19:09:54 2015 +0200
+
+    Fix #550 by allowing an empty trigger match list
+    
+    1e7659609bddd40471faf9a8e81ee7554d77dce9 is related
+    to this change and required to fix #550 as well.
+
+commit 1e7659609bddd40471faf9a8e81ee7554d77dce9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 8 14:25:10 2015 +0200
+
+    Don't free trigger when destroying the session.
+    
+    The trigger must be freed by the client.
+
+commit 4512ccd36c141fb881f4cfbbc607a727182b69a6
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 8 04:47:46 2015 +0200
+
+    Build: Show compiler versions in summary
+
+commit a6e8a8f766359e098fb12e8e015137223cd7437e
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 8 04:42:22 2015 +0200
+
+    sigrok.m4: Add SR_PROG_VERSION macro
+
+commit c178386178e60347d81cd818d5f5bf163fa7ebe7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 8 04:10:44 2015 +0200
+
+    sigrok.m4: Suppress make variable for ORDER
+
+commit 39cf2004dc389632cf5bbae9557ed0ee7c86f1e9
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 8 04:02:04 2015 +0200
+
+    Build: Fix restore of LIBS after temporary change
+
+commit 1b8d3a6d09c277d7401bc9d8a1e24c45f01e8f8a
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 17:27:54 2015 +0200
+
+    session: Compile USB-specific code only if available
+
+commit c1adbb1e235629d62be02e8b579478ed1594da44
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 15:01:52 2015 +0200
+
+    Build: Add configure check for libusb event-abstraction
+    
+    This is intended to make people notice when libusb is too old
+    for the new Windows code. However, this is not foolproof, since
+    the libusb version may be different at runtime.
+
+commit 534b634ce2e58682d454c5b61ea9f94408dc01e4
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 12:41:29 2015 +0200
+
+    USB: Handle live changes to the set of FDs to poll
+    
+    Introduce new internal session API for changing the set of polled
+    file descriptors for an already installed event source. Use the
+    new API to apply changes to the USB poll FDs when requested to do
+    so by libusb. Doing so is necessary to make the generic USB code
+    work on Windows.
+
+commit 1b0c6b6d1539b7b49bd1d6cdfab13feb8ddfd795
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 08:50:29 2015 +0200
+
+    USB: On Windows, assume G_IO_IN for the event mask
+
+commit 3a1a6d6b04d7937f025adb557c9051fb21dffa92
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 07:25:51 2015 +0200
+
+    session: More main loop debug output
+
+commit 4b9e2532135a07cfda6e2ac7a68c69486411b2b7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Sep 4 23:47:08 2015 +0200
+
+    USB: Remove Windows-specific event handling code
+    
+    For the generic code to work on Windows, a version of libusb
+    with the experimental event-abstraction changes is required.
+
+commit 2defc4116b57a73a69c1ad284a22bd2b2f7d8c68
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 23:57:03 2015 +0200
+
+    sigrok.m4: Update serial
+
+commit 3841c4c58db129870722e004ef0a96286bc3caab
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 23:38:52 2015 +0200
+
+    Build: Use SR_PROG_MAKE_NO_PRINT_DIRECTORY
+
+commit 82b01e42f8c1bace29fb273417571543548618ad
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 23:38:28 2015 +0200
+
+    sigrok.m4: Add SR_PROG_MAKE_NO_PRINT_DIRECTORY macro
+
+commit 6aa7fcf16a7ff39f85612d7aae39a7df20ae7ea1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 22:32:13 2015 +0200
+
+    Build: Show compiler and flags in configure summary
+
+commit 5b9e9bba746c25f3b3f0a962dc5586eef3d78197
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 22:28:14 2015 +0200
+
+    Build: Use SR_PROG_MAKE_ORDER_ONLY
+    
+    As of now, the resulting substitution @ORDER@ is unused.
+
+commit c2b0f42989bb0f206e736dff5ac5946d93618545
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 22:24:44 2015 +0200
+
+    sigrok.m4: Add SR_PROG_MAKE_ORDER_ONLY macro
+
+commit 8c2024e021895aa129f4bf607dfc2f6cab8640d1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 21:49:17 2015 +0200
+
+    Build: Implement libieee1284 check via SR_ARG_OPT_CHECK
+
+commit 38662688e8063cc21612629b06fca1c5b3f4af04
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 21:18:50 2015 +0200
+
+    sigrok.m4: Add generic macro for optional dependencies
+    
+    Provide SR_ARG_OPT_CHECK, a generalized variant of SRG_ARG_OPT_PKG
+    that can be used with custom check commands. Implement the latter
+    in terms of SR_ARG_OPT_CHECK.
+
+commit aff94d065e3ef50fa022b3e79b0dc435ae2c5b0a
+Author: Martin Lederhilger <martin.lederhilger at gmx.at>
+Date:   Mon Sep 7 22:43:20 2015 +0200
+
+    scpi: Fix incorrect serial_read_nonblocking call().
+    
+    There was a problem in scpi_serial.c in the scpi_serial_read_data()
+    function. Incoming data was written at the read position in the buffer,
+    although it should be written at the count position in the buffer.
+
+commit dd7a4a71caf4a0c0a7d2a24b780cbdffd6602621
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Sep 7 14:33:30 2015 +0200
+
+    drivers: Match dummy FD passed to source add/remove
+
+commit 5a7d8dac9042e5db6f2e058f20d7344ad322d17a
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Sep 5 03:56:16 2015 +0200
+
+    USB: Handle the case of a callback removing its event source
+
+commit 358c4ed5b8d9c010cfe78ca44607a2b376593cee
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Sep 4 18:51:55 2015 +0200
+
+    USB: On Windows, block to wait for libusb events
+    
+    This is another attempt at getting the mess that is libusb event
+    handling on Windows under control. Until libusb makes its HANDLEs
+    available for polling, we have no choice but to block while waiting
+    for libusb events. Since we do not want to force drivers to deal
+    with multi-threading issues, that means we have to block in the
+    session main loop.
+    
+    Fortunately, it turns out that our drivers aren't using multiple
+    event sources, so it is actually possible to block the main loop
+    without disrupting too much. This also gets rid of the USB thread
+    on Windows. Thankfully, libusb does not seem to care that we are
+    now calling libusb_handle_events_timeout_completed() twice per
+    iteration: first a blocking call (with timeout) in the callback
+    wrapper, followed by the non-blocking call in the driver-supplied
+    callback.
+
+commit 92248e7821d6ed98390088dab3a4edd329ef414a
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 1 03:36:03 2015 +0200
+
+    session: Allow multiple poll FDs per event source
+    
+    Turns out that having one event source per libusb poll FD is
+    a bad idea. There is only a single callback for all poll FDs,
+    and libusb expects to be called only once per poll iteration,
+    no matter how many FDs triggered.
+    
+    Also, they should all share the same timeout, which should get
+    reset on events from any polled FD. The new timeout handling made
+    this problem apparent, as it caused the callback to be invoked
+    multiple times on timeouts, once for each separate event source.
+    
+    In order to fix this, change the implementation to allow for an
+    arbitrary number of poll FDs per event source. This number is
+    zero for timer FDs, one for normal I/O sources, and one or more
+    for libusb sources (Unix only).
+    
+    Also, on Windows, do not get an additional timeout from libusb
+    in the event loop. This is only appropriate when polling the
+    libusb FDs directly, which we aren't doing on Windows.
+
+commit 7637fa35b57b82df733bdee07a0f08f2ab323a13
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Sep 1 00:36:30 2015 +0200
+
+    session: More poll spewing
+
+commit 15408b51f3f165f13fb19c647b5991e0b755817c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 31 21:11:08 2015 +0200
+
+    log: Output time stamps at log level spew
+
+commit c650d3ecbfeea1015df5ca38fa941bcb01254ed8
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 31 19:08:37 2015 +0200
+
+    drivers: Use timer sources instead of polling stdin
+
+commit 027bf07796c340509bd2b010021f36a5990e51d0
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 31 03:55:41 2015 +0200
+
+    demo: Use simple timer source instead of pipe
+    
+    Get rid of the Unix pipe and the GIOChannel wrapping it. Instead,
+    install a simple timer event source with the appropriate timeout.
+
+commit b5887bd0c308d78a151d3d3ff44d1f5ef28a8c4d
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 31 03:23:13 2015 +0200
+
+    session: Add poll debug spew
+
+commit faa5d7d997718acdd2b293a32dd62e28268b391f
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 31 02:35:57 2015 +0200
+
+    session: Unify handling of I/O and timer sources
+    
+    Handle I/O sources and timer ("dummy") sources within the same
+    polling loop, so that both may be used together. Slightly change
+    the API to improve consistency: a timeout value of -1 now disables
+    the timeout, and 0 makes the source always time out immediately.
+    The "dummy" sources already behaved that way, although it wasn't
+    documented as such.
+    
+    Make sure that I/O events are processed preferentially: Skip any
+    timeout callbacks if an I/O event occurred within the same poll
+    iteration. This applies to both timer/idle sources and timeouts
+    of I/O sources.
+    
+    Do not create dummy GPollFDs for timer/idle sources. Instead,
+    split the sources array into an I/O section and a timer section,
+    and create corresponding GPollFDs only for the I/O section. Use
+    GArray to simplify the handling of the dynamic arrays.
+
+commit 62d7945f8059ccbf56dfa2e5eb60671dd5bc959b
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 30 21:43:30 2015 +0200
+
+    session: Properly accumulate event source timeouts
+    
+    Keep track of when source timeouts are due and properly compare
+    against accumulated elapsed time between invocations. This prevents
+    sources with short timeouts from blocking other sources with longer
+    timeouts indefinitely.
+
+commit 4399cc0f41077cd975601a095fd272a2bf27bb99
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 30 15:25:33 2015 +0200
+
+    session: Do not expect meaningful errno on non-UNIX
+    
+    Looking at the g_poll() implementations for various systems, it
+    appears that on Windows the return value is 0 if the wait was
+    interrupted, and errno is never set. Also, the MacOS X wrapper
+    around select() does not clear revents on timeout.
+    
+    To deal with these issues, check for EINTR only on Unices, and
+    assume revents to be invalid unless g_poll() returned a positive
+    value.
+
+commit 32af282c5e1d64e878438aafe7c69efd2b4a4bd6
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 30 14:23:38 2015 +0200
+
+    session: Check for errors from g_poll()
+    
+    If the call to g_poll() in sr_session_iteration() fails, report
+    the error back to the caller. Do not treat EINTR as error though.
+    
+    Check for session abort only if a source callback was actually
+    invoked, or at least once if none of the callbacks are invoked.
+    Stop checking for abort if the session has already been stopped,
+    just in case a callback sets abort_session again.
+    
+    Also change the documentation to match the actual behavior.
+
+commit 89efe06460024c2a33e57178f5812bf23c2f9888
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 30 12:20:03 2015 +0200
+
+    session: Fix USB timeout handling
+    
+    In sr_session_iteration(), remove the inverted evaluation of the
+    block parameter if a USB source is present. This stops the deluge
+    of USB event callbacks due to the timeout always being zero.
+    
+    Also, just for cleanliness, initialize the revents member of each
+    GPollFD instance to zero.
+
+commit 452986bec416f00ac7fd573a460b9374d47fbf54
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Sep 2 14:07:06 2015 +0200
+
+    Build: Fix installation of Python bindings
+    
+    Apparently setup.py install behaves differently when given
+    the --root option, so omit that if DESTDIR is empty.
+    
+    Fixes #644.
+
+commit a2e4d88205d08729d7a2f43dbd818ac68fdcf693
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Aug 31 21:08:08 2015 +0200
+
+    python: Fix the build for Python >= 3.
+    
+    SWIG_init() returns void for Python 2.x and 'PyObject *' for Python 3.
+    
+    Use an #if to handle both cases properly, otherwise the Python bindings
+    for either Python 2 or 3 will fail to build.
+    
+    Python 3.x failure:
+    
+    sigrok/core/classes_wrap.cpp: In function ‘PyObject* PyInit__classes()’:
+    sigrok/core/classes_wrap.cpp:59002:5: error: return-statement with no
+    value, in function returning ‘PyObject* {aka _object*}’ [-fpermissive]
+         return;
+         ^
+    
+    Python 2.x failure:
+    
+    In file included from /usr/include/dirent.h:244:0,
+                     from /usr/include/glib-2.0/glib/gdir.h:32,
+                     from /usr/include/glib-2.0/glib.h:45,
+                     from /usr/include/pygobject-3.0/pygobject.h:7,
+                     from sigrok/core/classes_wrap.cpp:3179:
+    sigrok/core/classes_wrap.cpp: In function ‘void init_classes()’:
+    sigrok/core/classes_wrap.cpp:59002:12: error: return-statement with a
+    value, in function returning 'void' [-fpermissive]
+         return NULL;
+                ^
+
+commit b05409d7923df5205c07e5cce2d2791eb74cf268
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 30 21:15:02 2015 +0200
+
+    Remove SR_OK_CONTINUE.
+    
+    This brings error reporting back to the usual: either return codes
+    are SR_OK (0) or they are < 1 indicating an error.
+    
+    This fixes bug #633.
+
+commit b93006789f5cbce740bb2cece5a87d2969076e25
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 30 21:13:51 2015 +0200
+
+    output/csv: Avoid using SR_OK_CONTINUE.
+
+commit 2d05415f2e4358838c5238d99ef67140ea238768
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 30 20:30:32 2015 +0200
+
+    scpi: Avoid using SR_OK_CONTINUE.
+
+commit 2634b7781242796352616a571b794700f409baf7
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 16 20:03:10 2015 +0200
+
+    serial: Check event ptr before using.
+    
+    This avoids a NULL dereference when called twice.
+
+commit 661cc2ec46ea3b7f3b7f560604559b9d54e8448d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Aug 17 01:58:45 2015 +0200
+
+    Convert SR_CONF_MEASURED_QUANTITY to SR_T_MQ.
+
+commit 83478149b1d5a0476c15241dfce1fb2cf014e0c5
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Aug 29 23:01:02 2015 +0200
+
+    deree-de5000: Drop SR_CONF_MEASURED_QUANTITY functionality.
+    
+    This was superfluous -- there is no need to be able to query the
+    last MQ(s) sent by the device, since they're already being sent
+    along with the measurements in analog packets.
+    
+    Since there is also no way to change the MQ (that is done with the
+    buttons on the device), there is no need to even list the possible
+    MQs.
+
+commit 75772c721d45d3b33618388e41d2d874c4f88302
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Aug 29 22:20:29 2015 +0200
+
+    Change SR_T_MQLIST to SR_T_MQ.
+    
+    The need to make this a list no longer applies.
+    
+    SR_T_MQ is thus a type consisting of a tuple with two elements: the first
+    item is the MQ (type G_VARIANT_TYPE_UINT32), and the second is the MQ
+    flags value (G_VARIANT_TYPE_UINT64).
+
+commit 28d86fa9a5d24bbcafeddbe2330f18a3e91501b1
+Author: Daniel Glöckner <daniel-gl at gmx.net>
+Date:   Thu Aug 27 11:21:48 2015 +0200
+
+    Check for libieee1284
+    
+    Will be needed by the Hung-Chang DSO-2100 driver.
+
+commit 4ec436c4d5b1712c9b5162171586e9bbedf41f09
+Author: Daniel Glöckner <daniel-gl at gmx.net>
+Date:   Sat Aug 22 17:39:39 2015 +0200
+
+    sr_session_send: pass transformed packet to datafeed callbacks
+    
+    After the packet has been passed through the transformation modules,
+    the transformed data is in packet_in but the following code uses
+    the packet variable which still points to the original input.
+    
+    This fixes bug #631.
+
+commit 1e6b5b930340cc87e6c49e0563b289267ae00cb4
+Author: Daniel Glöckner <daniel-gl at gmx.net>
+Date:   Sat Aug 22 18:00:30 2015 +0200
+
+    hantek-dso: fix memory leak
+    
+    This fixes bug #632.
+
+commit 96127d0feaf7333fba880c5bcf1267de6360e6a8
+Author: Daniel Glöckner <daniel-gl at gmx.net>
+Date:   Sat Aug 22 17:31:43 2015 +0200
+
+    lascar-el-usb: fix memory leak
+    
+    This fixes bug #630.
+
+commit 80e20c10e3de3250f339affbf2abcd4b832f7b60
+Author: Daniel Glöckner <daniel-gl at gmx.net>
+Date:   Sat Aug 22 17:19:33 2015 +0200
+
+    es51919: fix memory leak
+    
+    This fixes bug #629.
+
+commit d40b8557a9625cccfb9bef99c77dd803bdd356e7
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Aug 29 14:50:44 2015 +0200
+
+    Don't set _POSIX_C_SOURCE for VXI/RPC related files.
+    
+    Make vxi.h the first #include in all affected files and #undef the
+    _POSIX_C_SOURCE macro in vxi.h.
+    
+    This avoids various build issues on e.g. FreeBSD or Mac OS X where
+    setting _POSIX_C_SOURCE leads to the unavailability of certain types
+    such as u_long (as used in the VXI/RPC code).
+
+commit c05a0ba528e96671b1c94c1971d4671b4767f0e9
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 29 01:54:17 2015 +0200
+
+    Build: Pass compiler flags from make to setup.py
+    
+    Extend setup.py to allow environment variables to be set on the
+    command line. Use that functionality to replace the pkg-config
+    invocations with flags passed on from make. Suppress the annoying
+    -Wstrict-prototypes warning by overriding the OPT variable.
+    
+    Also move the "cd bindings/python" from Makefile.am to setup.py
+    to side-step problems with "cd" in make rules.
+    
+    This also fixes bug #628.
+
+commit 5b869e978a67802bc56713534987870a279405cb
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Aug 27 12:30:13 2015 +0200
+
+    Build: Go back to _POSIX_C_SOURCE=200112L
+
+commit bbb3996c06f2b1ebc9529d1e2181ddc55f70d9da
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Aug 26 23:09:03 2015 +0200
+
+    configure: Enable largefile support on 32-bit systems
+
+commit 8006cdc53d4f52f055559b95986b13c3c3826745
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Aug 28 16:26:36 2015 +0200
+
+    configure.ac: Add libserialport dependency to kern-scale.
+
+commit ca7dbb56161f9f4b7f842103fca59d41d559c793
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Aug 28 16:26:07 2015 +0200
+
+    Various key lists: Add reminders of what needs updates upon changes.
+
+commit 55c9f09dbc7edd24b0da8dda8837aff6a932e3c5
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Aug 28 16:10:15 2015 +0200
+
+    session.c: Fix key order.
+
+commit f7bcc68604cea023043c3067d8bd952d86d69a89
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Aug 28 14:25:24 2015 +0200
+
+    analog.c: Fix key order, add missing items.
+
+commit c984f2f997ab2c5fdeef72d131e04a92619f89fd
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Aug 28 14:14:10 2015 +0200
+
+    output/analog: Fix key order, add missing items.
+
+commit 29ae6f0880eb8dab93507382b5b35d36df8ef1e0
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Aug 28 14:01:09 2015 +0200
+
+    hwdriver.c: Fix key order, add missing items.
+
+commit 607dcdeae13d04fd5c6d41da46d9c29a04206a86
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Aug 27 22:06:26 2015 +0200
+
+    kern-scale: Add initial driver contents.
+    
+    This currently supports (and is tested on) the KERN EW 6200-2NM,
+    other models will be added later.
+
+commit 9380ec2f05e67518b1b23057749df3684a0cf05e
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Aug 27 21:42:46 2015 +0200
+
+    kern-scale: Initial driver skeleton.
+
+commit e5d953b5592c1ca2af9a0f64f2dfc817a931e59a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Aug 27 21:21:03 2015 +0200
+
+    Add a protocol parser for KERN scales.
+    
+    (KERN & SOHN GmbH, http://www.kern-sohn.com/)
+
+commit 34eaf4bcbbff5e2165f38afc9123c5ddd9a40079
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Aug 27 21:21:34 2015 +0200
+
+    output/analog: Support mass related units / MQ flags.
+
+commit 28af4c714e2e3fd032e800f2c60dcda8953f5a3b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Aug 27 20:44:23 2015 +0200
+
+    Add a few scale related flags.
+    
+     - SR_MQ_MASS: Mass, as measured by scales / balances.
+    
+     - SR_UNIT_*: Various units of mass.
+    
+     - SR_MQFLAG_UNSTABLE: A flag denoting that a value has not
+       yet stabilized (settled). E.g. when placing an object on a scale
+       it'll take a few moments until a stable reading is available.
+       Measurement values marked with SR_MQFLAG_UNSTABLE denote that they
+       are "unsettled", unstable values (not yet stabilized).
+       The absence of SR_MQFLAG_UNSTABLE denotes that the value is stable.
+    
+     - SR_CONF_SCALE: A device class for weighing scales / balances.
+
+commit 68799618f6ed6e4becc862e9e1061c61e31dd1fe
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Aug 26 13:55:53 2015 +0200
+
+    Build: Define feature test macro _DEFAULT_SOURCE
+    
+    This basically makes glibc expose the same set of features as
+    if gcc was invoked without any restricting -std=c* option. Unlike
+    _GNU_SOURCE however, it does not enable GNU-specific extensions.
+    
+    So, with this macro defined the behavior of Linux with glibc
+    should match that of other platforms.
+
+commit 65489c1ada6a362a492b7929c1a567a80266f202
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Aug 26 13:13:39 2015 +0200
+
+    Build: Use TESTS prefix instead of CHECK for flag variables
+    
+    In order to avoid confusion of the flags-gathering pkg-config
+    result with the actual test for the availability of "check",
+    change the pkg-config output variable prefix from CHECK to TESTS.
+
+commit a1f7c854c502cca8fe7e5576b9a01ad1086f9170
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Aug 17 01:43:22 2015 +0200
+
+    Add SR_T_MQLIST.
+    
+    This type consists of an array, with each item a two-member tuple,
+    representing an MQ/MQflags pair: the first item is the MQ (type
+    G_VARIANT_TYPE_UINT32), and the second is the MQ flags value
+    (G_VARIANT_TYPE_UINT64).
+    
+    A GVariant of type SR_T_MQLIST can thus always represent more than
+    one MQ/MQflag pair.
+
+commit 0176c92fea8f47f6f9030a4a02f84d414741068f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Aug 17 01:38:10 2015 +0200
+
+    Add key info tables for MQ and MQflags.
+
+commit 2fb60e23299037456a626ad65d15b89a78393917
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Aug 17 01:30:14 2015 +0200
+
+    Replace sr_config_info with sr_key_info.
+    
+    The tables defined with this struct can now be used for information
+    on items other than config keys.
+    
+    Functions to access these tables have been renamed sr_key_info_[name_]get.
+    These take an extra argument, keytype, which should be set to SR_KEY_CONFIG
+    to get the config key tables. Other key types will be added.
+
+commit 0b2b92f6c3db384cad10e3229e3fa149fc429ef4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 16 20:02:21 2015 +0200
+
+    scpi/usbtmc: Use sr_usb_close().
+
+commit 67e95ed37d57c9bcdff452eb5fe4c4416069410e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 16 20:00:26 2015 +0200
+
+    Add sr_usb_close().
+
+commit 558d438d1f944284be13bec06db7ca67876ea234
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Aug 17 02:02:03 2015 +0200
+
+    scpi: Strip leading/trailing spaces from *IDN? response.
+
+commit c0d257790a7ad0ddc8e83736fa2ade0bf57f3661
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 16 20:44:37 2015 +0200
+
+    scpi: Propagate error codes in API functions.
+    
+    This allows client drivers to detect e.g. timeout conditions.
+
+commit 9c24d16a1d01a01f88f254fbd5f5e413e796f3cf
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 16 15:54:02 2015 +0200
+
+    Make sr_next_enabled_channel() from scpi-pps available library-wide.
+
+commit 91ef511db2370904f8765a13e315fbddaf5ffe07
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 16 15:12:34 2015 +0200
+
+    scpi: Make helper functions from scpi-pps available library-wide.
+
+commit 5a1afc0907abfee5848484f944789213d6be9752
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 16 01:04:04 2015 +0200
+
+    scpi: Move SCPI-related definitions to separate header file.
+
+commit 64bc73f5282ec0e7da6e00a8d078191e5375dc5c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Aug 25 17:01:22 2015 +0200
+
+    sigrok.m4: License header consistency fixes.
+    
+    Use the same wording and formatting as in the rest of the codebase.
+
+commit e662d9e8e5c6a5d677194fc217da3a88040387a3
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Aug 25 12:50:35 2015 +0200
+
+    Build: Limit system features to C99 and POSIX
+    
+    Use -std=c99 to try and enforce standard compliance.
+
+commit 2d2240bb99e343379e9648e10d796d107705f06f
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Aug 25 00:17:09 2015 +0200
+
+    Build: Fix SR_CHECK_COMPILE_FLAGS
+    
+    Do not put "no" into the flags if the check fails.
+
+commit ba1c29dc2227dc0d4c47e189ce0682ee63a862a7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 24 22:20:30 2015 +0200
+
+    Build: Reduce unnecessary config substitutions
+    
+    Variable substitutions with a constant value may as well be
+    set directly in Makefile.am.
+
+commit bc8ff24de6948efee1313c7884e56090d8424c8e
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 24 22:06:27 2015 +0200
+
+    Build: Leave LIBS alone during configure
+    
+    Put the extra libraries into SR_EXTRA_LIBS instead of LIBS.
+    Create an SR_CHECK_LIBS macro to make that easy. Substitute
+    SR_EXTRA_LIBS into libsigrok.pc, too.
+
+commit 4ed3d9b6db3192ef57c2dccd788c5c6f6e783011
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 24 11:18:09 2015 +0200
+
+    Build: Simplify driver dependency check
+
+commit 2d143826b0862a25a599825e50d78b58b53c67ac
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 24 09:25:37 2015 +0200
+
+    Build: Add private copy of C++11 check macro
+    
+    Place a copy of ax_cxx_compile_stdcxx_11.m4 from the Autoconf
+    macro archive into our private m4/ directory. This is cleaner
+    than trying to parse M4 file versions etc. Plus, the macro is
+    now always available.
+
+commit 0ab8386a5c0b6fa58ce2a8c3aa2ca337bcb49005
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 24 00:08:01 2015 +0200
+
+    Build: Prefer newer versions of libftdi and SWIG
+
+commit 26b67f11415046f8b100e32f37bb09e86731107d
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 23 23:53:01 2015 +0200
+
+    Build: Use commas to separate missing dependencies
+    
+    Beautify the output. Also fix the PyGObject test.
+
+commit be21d81a154df69df77cc3350296c2addae7dab9
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 23 21:17:36 2015 +0200
+
+    Build: Reduce autogen.sh to a trivial stub
+    
+    Use autoreconf instead of invoking the various Auto tools
+    separately. Get rid of the Darwin-specific guesswork -- it
+    does not make sense to handle this at the level of libsigrok.
+    
+    People should set up their ACLOCAL_PATH themselves as appropriate
+    for their own system; just as they already need to set up various
+    other paths.
+
+commit b84a733105eea68cbc568850eba20bd571debad7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 23 21:08:54 2015 +0200
+
+    Build: Check for accepted compiler flags
+    
+    Introduce the SR_CHECK_COMPILE_FLAGS macro and use it to check
+    for additional compiler flags. Put the accepted flags into the
+    separate substitution variable SR_EXTRA_CFLAGS.
+    
+    With this and the preceding changes, bug #578 should now be fixed.
+
+commit 1bb196e4969b299d316bdcff2201a44dd517f240
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 23 20:07:38 2015 +0200
+
+    Build: Move package check macros to m4/sigrok.m4
+    
+    Define generic macros for pkg-config dependency checks etc. in
+    m4/sigrok.m4, so they can be easily reused by downstream modules.
+
+commit 4cf2f34f430be5d38a7973c31f811fdb15a58329
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 23 15:02:30 2015 +0200
+
+    Build: Make compiler warnings configurable
+    
+    Use the SR_ARG_ENABLE_WARNINGS macro to configure and check for
+    the availability of compiler warning flags. Maintain separate
+    sets of warning flags for C and C++.
+    
+    The configure option --enable-warnings=[min|max|fatal|no] can
+    be used to set the compiler warning level for all languages.
+    The default level is "max".
+
+commit 24138539c1a11dd221443317b4941f151bcbdb12
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 23 14:52:00 2015 +0200
+
+    Build: Move custom Autoconf macros to separate file
+    
+    Place custom Autoconf macros which other sigrok modules may
+    re-use into a separate file m4/sigrok.m4. Also, introduce new
+    macros for defining the package and library versions, and for
+    gathering compiler warning flags.
+
+commit 2abad185cdb2e43417a2df91143fab7ecc700550
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 22 20:27:53 2015 +0200
+
+    Clean up .gitignore
+
+commit ea1d5351455e68ae967f48956b980f9b185ddf53
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 22 20:58:11 2015 +0200
+
+    Build: Place Autoconf macros into m4/ directory
+    
+    This separates the M4 macros from the other cruft in autostuff/
+    to prepare for the introduction of custom macro files.
+
+commit cb4d28efd1b261c36abc74eb9a117c5b322b481b
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 22 00:07:48 2015 +0200
+
+    Build: Do not omit first argument to m4_warn()
+    
+    It seems that contrary to what the documentation says, leaving
+    the category argument to m4_warn() empty is not allowed. Use
+    the "unsupported" category for lack of a better choice.
+
+commit aafcb3fe87809d3e6d12b9a440adccf516fb3eb5
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Aug 19 15:44:43 2015 +0200
+
+    FreeBSD: Also check for swig3.0.
+    
+    On FreeBSD (for example) the SWIG binary can also be called "swig3.0".
+    
+    This fixes (together with the recent "swig2.0" addition) bug #557.
+
+commit a9cb82ace2fb98f04078996ee6eb29d227b7abb2
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Aug 18 21:24:39 2015 +0200
+
+    Build: Get rid of recursive make invocations
+    
+    Do not invoke $(MAKE) just to execute some clean rule.
+    This gets rid of the "jobserver unavailable" warnings.
+
+commit 90420ef6bcc9dc081a3dc39e09df546fbc1e5753
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Aug 18 20:09:53 2015 +0200
+
+    Build: Tell setup.py where to find SWIG
+
+commit 3f03ffaf2f7c9c65d0bf35e611df8fb720ed0e8c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Aug 18 18:01:54 2015 +0200
+
+    FreeBSD: Also check for swig2.0 in addition to swig.
+    
+    On FreeBSD (for example) the SWIG binary is called "swig2.0".
+    
+    This fixes bug #557.
+
+commit 731c01f248b8b7b9bd87e6b21ee92f806eff762b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Aug 18 17:58:28 2015 +0200
+
+    FreeBSD: Fix compile/link error due to missing libusb_get_version().
+    
+    The FreeBSD libusb-1.0 API implementation doesn't have libusb_get_version().
+    Use our CONF_LIBUSB_1_0_VERSION macro instead.
+
+commit dbde3b65133bca6a00d589b735be5f4ba0049194
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Aug 18 18:06:08 2015 +0200
+
+    Build: Avoid $< in explicit rules
+    
+    Although useful, makes other than GNU make do not like this.
+    Name the prerequisite explicitly instead, and circumvent any
+    VPATH substitution other makes may do.
+
+commit da0763d0968c5725d75bddedae6029aab837606b
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Aug 18 17:46:29 2015 +0200
+
+    Build: Skip C++ CFLAGS and LIBS check when disabled
+    
+    It is not a good idea to invoke PKG_CHECK_MODULES with an empty
+    list of modules to check, so skip the invocation in that case.
+
+commit b8b727e45f55a91171d52f93ac387784914ffbf1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Tue Aug 18 15:02:01 2015 +0200
+
+    Build: Fix typo that broke the driver enable options
+
+commit d37c7109bf4e3b3e3ed0a477d96c1cc77b1623f7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 17 05:08:48 2015 +0200
+
+    Build: Make SR_DRIVER() handle dependencies
+    
+    Turn the hardware driver dependency handling on its head, by
+    making SR_DRIVER() take a list of dependencies to check for.
+
+commit 7c16e74d45b91123206efee2116d86462c8e707c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Mon Aug 17 02:08:39 2015 +0200
+
+    Build: Make dependency on git change more robust
+    
+    It can sometimes happen that .git/HEAD or .git/refs/head/*, which
+    are added as config.status dependencies during configure, do not
+    exist anymore at build time.  For instance, when the current branch
+    is deleted after switching to a different one.
+    
+    Wrap the dependencies inside $(wildcard ...) to avoid this problem.
+    Note that this is a GNU make feature.  However, it should be fine
+    as it is only used for git builds.  Even if a non-GNU make is used,
+    the construct will hopefully just expand to nothing.
+
+commit 965d68898a08c30b26925483290498f627fd309e
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Aug 16 23:56:43 2015 +0200
+
+    Build: Show glib/libzip version requirements in configure summary.
+
+commit 2700e63d482d2e60eab1f95dfd726b7572c43a27
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Aug 16 23:53:23 2015 +0200
+
+    Build: Fix a typo causing libftdi1 detection issues.
+
+commit 2cbde1519811c36313a9ee8f57c99313c243e911
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sun Aug 16 18:53:32 2015 +0200
+
+    Build: Properly namespace the version defines
+    
+    Use the CONF_ prefix for the configured host define as well as
+    the various build-time version macros.
+
+commit 1f61c22ff5e0d1d169894ad606831b44fde19468
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 15:59:52 2015 +0200
+
+    Build: Cleanup up and modernize autotools setup
+    
+    Note that some --enable options of configure have become
+    --with or --without options.
+
+commit c5f179a9753fde952fcfd5585378f5605ab90741
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 02:28:54 2015 +0200
+
+    Build: Simplify the hardware driver selection
+    
+    Replace DRIVER() and DRIVER2() by a single SR_DRIVER() macro.
+    Derive the names of shell variables and preprocessor defines
+    programatically to cut down on repetition.
+
+commit 7e2cadd003e67991d7ed4905463f131762faeed5
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 19:55:02 2015 +0200
+
+    Add dist tarball to gitignore
+
+commit 5a3e34285da1544941fd314079f05a1715fc3de3
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 19:43:29 2015 +0200
+
+    setup.py: Do VPATH search for swig/enums.i
+    
+    Also, in swig/classes.i include swig/enums.i rather than plain
+    enums.i, to guard against future name clashes.
+
+commit cf5b338e0736d97e363ea3c1652261354d1d7522
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 19:18:36 2015 +0200
+
+    Build: Depend on swig/enums.i where appropriate
+
+commit bc81463bb8d1dcd5eb8105dc9e6cd1fd1049a4b9
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 19:02:15 2015 +0200
+
+    enums.py: Place swig/enums.i into build directory
+
+commit 122322a9ba21f755f1efe36df0288c125052e99b
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 18:33:05 2015 +0200
+
+    Build: Do not distribute generated headers
+    
+    version.h and enums.hpp are generated at build time and should
+    not be included in the distribution tarball.  This seems to have
+    triggered a weird error when doing a VPATH build of the dist
+    tarball.
+
+commit 9ac018d0fc7119dc7d016e9952bb7825b247d504
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 18:10:50 2015 +0200
+
+    Build: Pass nostdinc option to automake
+    
+    It seems automake automatically adds the directory containing
+    the generated version.h to the include path.  Use nostdinc to
+    disable default includes altogether.
+
+commit b5f0731971349eaad70d6fd2f8c704c4f5f3802c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 17:54:14 2015 +0200
+
+    Build: Use angle brackets for enums.cpp include
+
+commit 31ac7735ea82342fcef3ea16ebdf81bcf812d27c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 16:59:47 2015 +0200
+
+    Build: Distribute bindings sources
+    
+    A couple of files referenced during the build were missing from
+    the distribution, causing a tarball build failure.
+
+commit 8b2a184327900fd7d08bb09f58699d62e2578eea
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Sat Aug 15 16:10:36 2015 +0200
+
+    Build: Prefix directory to include of enums.hpp
+    
+    This fixes a build problem due to the reduced include search paths
+    introduced by my recent changes.  Also fix a couple of other
+    includes to use angle brackets.
+
+commit 7f289d14ec52a352cc2c6a8671175a55f2f67083
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Aug 14 21:01:59 2015 +0200
+
+    Build: Append git revision hash to version
+    
+    Append the git revision hash to the libsigrok package version,
+    unless HEAD exactly matches a release tag.  Note that this does
+    not affect the version known to autoconf -- e.g. source tarballs
+    created by make dist will not receive a revision suffix.
+    
+    Changes to git HEAD automatically trigger a reconfiguration.
+    Uncommitted changes do not, which is why I left out the -dirty
+    suffix.
+
+commit b9eb8e1a8cdb4ac6449cfa881da96bcf9bc687ad
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Aug 13 00:38:47 2015 +0200
+
+    Consistently use SR_PACKAGE_VERSION instead of VERSION
+
+commit 27a2497dc2ef29d7ca348e8103033f09c71d28e1
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Aug 14 18:03:47 2015 +0200
+
+    Build: Delay expansion of $datadir in FIRMWARE_DIR
+    
+    Make it so that $(datadir) is resolved at make time, as per
+    autotools recommendations.  Note that $datadir is not fully
+    resolved at configure time to begin with, i.e. part of it
+    already was evaluated at make time.
+
+commit b2478a23db9a966678aa6cb8c4f0b53b13b13fa6
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Fri Aug 14 17:53:06 2015 +0200
+
+    Build: Make version.h a configuration header
+    
+    Use the proper tool for the job and make libsigrok/version.h
+    a secondary configuration header, so that autoconf's AC_DEFINE
+    machinery can be used to generate it.  Note that the header
+    template is still hand-written, enabling fine control of the
+    content.
+
+commit 4960aeb03533f503c32c50cdd94277d561112c1a
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Aug 13 02:06:41 2015 +0200
+
+    tests: Include <libsigrok/libsigrok.h>
+    
+    This matches the global style and avoids the "../" path
+    components.
+
+commit c1aae90038456a61d0f9313d34e6107c3440d3e7
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Thu Aug 13 01:18:51 2015 +0200
+
+    Build: Set local include directories in Makefile.am
+    
+    Move the include flags for files in the source tree from
+    configure.ac to Makefile.am where they belong.  Also use
+    AM_CPPFLAGS instead of CFLAGS/CXXFLAGS to make sure the
+    files in the build/source tree are always picked up first.
+    
+    Also, remove the include/libsigrok sub-directory from the
+    search path, thereby making the <libsigrok/> prefix mandatory
+    when building libsigrok itself.  This matches the convention
+    already imposed on users of the library.
+
+commit 3cd4b381744eb88fd4ba32565bd408c33b431629
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Aug 15 23:40:29 2015 +0200
+
+    Introduce OutputFlag
+
+commit 7df31ae8978fd719646384ba1889fd69a038a45c
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Aug 1 21:41:32 2015 +0200
+
+    output/srzip: Prevent memory leak in case filename is empty
+
+commit bd7b83cf631d515ae7e9560400cef2d9f662299a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Aug 15 21:06:30 2015 +0200
+
+    backend: Output lib versions, host, and endianness in sr_init().
+    
+    This should help to more easily debug issues reported by users.
+
+commit 6d930b4159c2df2a500610f50c6df360f4dcd29a
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Fri Aug 14 01:03:57 2015 +0200
+
+    brymen-bm86x: add some error checking
+
+commit c442ffda0fc6fa9bc3c5397a21ef1d04f01a64a1
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Aug 15 17:16:01 2015 +0200
+
+    Various minor cosmetics and consistency fixes.
+
+commit ad0293f19c96385cc6e44f552de9636cb34f53a9
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Aug 15 17:09:29 2015 +0200
+
+    Drop comment mentioning non-existing function.
+
+commit dff0a894356803e238c9ad49c147fbe9aa81c568
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Aug 5 17:23:54 2015 +0200
+
+    Doxygen consistency fixes (@foo instead of \foo).
+
+commit 8d5228015d0f07ce6626aa52ffb80bc4129f7dbe
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Jul 29 19:31:43 2015 +0200
+
+    Various errno/strerror() related fixes.
+    
+     - Don't #include <errno.h> in files that don't actually need it.
+    
+     - Don't use strerror() on error codes from functions that don't set
+       errno. Replace strerror() with sr_strerror() for libsigrok functions.
+
+commit 6c7d80afcaf043eb8128a3102519032850fec1af
+Author: Hubert CHAUMETTE <hchaumette at baylibre.com>
+Date:   Fri Jul 31 09:44:09 2015 +0200
+
+    libsigrok: demo: close pipes on dev_acquisition_stop()
+    
+    Fixes a bug where new acquisition failes due to leftover pipes from
+    previous acquisitions:
+    	sr: demo: dev_acquisition_start: pipe() failed
+    Indeed, PulseView had 2024 pipes opened. With this fix, it stabilizes at
+    33 with sampling thread active.
+    
+    Signed-off-by: Hubert CHAUMETTE <hchaumette at baylibre.com>
+
+commit 37875f7506143d97f44014260f07e6408ee8c9dc
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Jul 30 07:29:11 2015 +0200
+
+    output/srzip: Use sr_output->filename instead of option
+    
+    This fixes parts of bug #570.
+
+commit 81b3ce374c3b6d48e5ed321ac7a871ce4248a0bb
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Wed Jul 29 21:19:49 2015 +0200
+
+    Add filename field to sr_output and make it accessible
+    
+    This fixes parts of bug #570.
+
+commit 176c7219bbb509d8e3e149315db7db6bd2fb9655
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jul 25 19:50:36 2015 +0200
+
+    Only build BayLibre ACME driver if <sys/timerfd.h> is available.
+    
+    This fixes bug #610 (Android build issue).
+
+commit a394bd28136cad6536f6eab09803ae6f3fa9cb68
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jul 25 16:59:04 2015 +0200
+
+    ols: Don't #include <libserialport.h> directly.
+    
+    The driver is not using sp_* calls directly, so no need to #include
+    the header file.
+
+commit e6b8f35fa70c4fac4b73ec6ccd7292e1c8ed2372
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jul 18 18:03:29 2015 +0200
+
+    Bump version to 0.4.0 (the upcoming next major release).
+
+commit 025f6ed862b3a1dc8aa5deb3e44b4c2230ef3aa6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jul 18 17:54:41 2015 +0200
+
+    Append "-git" to the version string.
+    
+    This avoids confusion with the released tarballs.
+
+commit 338143ea0371133c2b3f56bb2cc5e8726c0141d8
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Jul 9 21:05:44 2015 +0200
+
+    Convert a few more occurrences of d->priv to d->context.
+
+commit 41812aca436805b0614f2a8f31cf2f8ce494aea0
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Mon Jul 6 21:55:36 2015 +0200
+
+    Fix #442 by renaming sr_dev_driver.priv to .context
+
+commit c11a1e6122998b247df66c59d2076eb814818497
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Jul 10 01:03:16 2015 +0200
+
+    fx2lafw: Use the USB_INTERFACE #define.
+
+commit dc2903bbdbc97d4d0968ffc23d475cf21adc8575
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Jul 10 00:59:48 2015 +0200
+
+    fx2lafw: Fix segfault wrt libusb_detach_kernel_driver().
+    
+    Move the libusb_detach_kernel_driver() call after the code that
+    sets the usb->devhdl pointer, otherwise it'll be NULL and result
+    in a segfault.
+    
+      #0  libusb_kernel_driver_active (dev=0x0, interface_number=0) at libusb/core.c:1711
+      #1  dev_open (sdi=0x12d99f0) at src/hardware/fx2lafw/api.c:374
+      [...]
+    
+    Tested on a device with the default Cypress VID/PID and one with
+    the Saleae Logic VID/PID, both works fine.
+
+commit 39e4517759d391e5572e81d9796d4d8c6892d25e
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Mon Jul 6 21:20:26 2015 +0200
+
+    fx2lafw: Fix #445 by detaching the kernel driver (if any)
+    
+    This avoids the need to run "rmmod usbtest" on Linux for devices
+    with the standard Cypress FX2 USB VID/PID (04b4:8613).
+
+commit 536141ff49330b48ed8230547a9bec738d84a309
+Author: Daniel King <damaki.dmk at gmail.com>
+Date:   Mon Jul 6 21:16:29 2015 +0200
+
+    Demo: Fix #314 by always honoring sample limit changes
+
+commit e2b99f04d8edd268b220ad7369f3029da34f3ac4
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jul 5 21:06:05 2015 +0200
+
+    Fix #505 by providing a separate property list for the analog group
+
+commit 815e3cb83e715fad442c491d406f6ceba79e3a74
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jul 5 18:01:45 2015 +0200
+
+    Fix #574 by setting up the transfer first, then starting acquisition
+
+commit d586a7f4bd0d83a1e432d5ca1ee63af94bdb4e13
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Wed Jul 1 12:30:09 2015 +0200
+
+    baylibre-acme: Remove unnecessary close().
+    
+    The timerfd descriptor is closed automatically by
+    g_io_channel_shutdown(). No need to close it manually.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 7a1a3a36a98eda90c2fe4a78326ea68afe94a041
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Wed Jul 1 10:47:38 2015 +0200
+
+    baylibre-acme: Use SR_HZ_TO_NS() when configuring the timerfd.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 7e5a048ff26d516291c3d382c7d70bdbf42628d3
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Tue Jun 30 15:07:50 2015 +0200
+
+    baylibre-acme: Add a workaround for slow data acquisition.
+    
+    At high sampling rates and maximum channels we are not able to acquire
+    samples fast enough, even though frontends still think that samples
+    arrive on time. This causes visible shifts in frontend plots.
+    
+    To compensate for the delay introduce the following workaround: check
+    if we are late (if any clock events have been missed) and resend the
+    last frame n times (n == number of missed clock events).
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit a0648b1a12699429d8a70b8eeb05942885bd32b3
+Author: Daniel Lezcano <daniel.lezcano at linaro.org>
+Date:   Thu Jun 4 19:01:27 2015 +0200
+
+    baylibre-acme: Use timerfd instead of a fake pipe.
+    
+    Currently baylibre-acme uses a fake pipe as the input channel required by
+    libsigrok API and calls sleep() in the data acquisition callback to create
+    intervals between measurements.
+    
+    Switch to a more elegant approach: use Linux' timerfd and set a periodic
+    timer equal to the sampling rate. Then read the data every time the timer
+    expires.
+    
+    Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit f9b0ab6b2db8b997a79e0dde37b7240779c76cea
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu May 28 15:45:56 2015 +0200
+
+    baylibre-acme: Close sysfs files after stopping the acquisition.
+    
+    Add a missing call to dev_acquisition_close() in dev_acquisition_stop().
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 4e88b86cc81ce6569c4bbf947fc2f0f47f4ce8a1
+Author: Daniel Lezcano <daniel.lezcano at linaro.org>
+Date:   Wed May 27 20:22:02 2015 +0200
+
+    baylibre-acme: Optimize reading of values from sysfs.
+    
+    Opening a file has a cost (security, allocation, syscalls). The
+    read_sample() function always does an open/read/close sequence.
+    
+    In order to optimize that, let's open the file at the moment the
+    acquisition starts, close it when the acquisition stops and make
+    read_sample() only lseek() to the beginning of the file and read
+    the value.
+    
+    Signed-off-by: Daniel Lezcano <daniel.lezcano at free.fr>
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit fe473123ba836445d477149f2e2c0ae372fc0c4c
+Author: Daniel Elstner <daniel.kitta at gmail.com>
+Date:   Wed Jun 24 19:16:49 2015 +0200
+
+    sysclk-lwla: Do not create channels in reverse order
+    
+    This was originally done as an optimization in combination with a list
+    reversal which has since been removed from the code.  Thus, un-reverse
+    the channels so that the UI lists them in the correct order again.
+
+commit a93086528e5b476812e42b423f7a8a53812af6f4
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Jun 23 00:39:59 2015 +0200
+
+    yokogawa-dlm: Minor cosmetics, consistency fixes, typos.
+
+commit 49f49cb55e948e5e05d1334fd5236a0ed02a8e4c
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jun 7 11:52:30 2015 +0200
+
+    yokogawa-dlm: Return correct value for SR_CONF_HDIV
+
+commit 2dcf82e9cb486dc235c1e0b96334d4edb0f19130
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jun 7 11:51:40 2015 +0200
+
+    yokogawa-dlm: Remove dlm_setup_channels()
+
+commit c65a021c050b3ef05ccc844cf24ee8e620eb669e
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jun 7 11:50:29 2015 +0200
+
+    yokogawa-dlm: Introduce config_channel_set()
+
+commit f3c60fb6affce2ebd81250db1d60bee733e5f479
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jun 7 11:40:39 2015 +0200
+
+    yokogawa-dlm: Config get/set/list handler updates
+
+commit f77afcf0aa421af3f8ae29d7b877a9cd114f3339
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jun 7 11:37:27 2015 +0200
+
+    yokogawa-dlm: Fix number of digital groups
+
+commit 6fd78a9fd0fbad97a486225c1e9efd562fa07b7b
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jun 7 11:36:38 2015 +0200
+
+    yokogawa-dlm: Prevent duplicate channel index
+
+commit 7048bb1f356305caf458862ebdf22ae0645dae66
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Jun 7 11:35:21 2015 +0200
+
+    yokogawa-dlm: Add support for the DL9000 series
+
+commit b7c53d48a8170dc07bee1bafb481db3e2ea38e14
+Author: Daniel Gröber <dxld at darkboxed.org>
+Date:   Sun May 3 00:31:49 2015 +0200
+
+    fx2lafw: Add DSLogic Pro and DSCope firmware loading support
+
+commit 65e0036635a54b3273436b4ea7918aea25bad788
+Author: Carl-Fredrik Sundström <audio.cf at gmail.com>
+Date:   Mon Jun 22 16:53:51 2015 +0200
+
+    dslogic: Fix incorrect samplerate setting.
+
+commit aae2273b99b9155ea1b91bc65eb4c91e06e3c8e2
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Wed May 6 00:32:30 2015 -0700
+
+    bindings/python: Handle import failures without crashing
+    
+    When the import of gi.repository.GLib failed, we would get a NULL
+    pointer that we passed along without any checking. In this situation,
+    the entire program would crash with a segmentation fault, and no
+    message to indicate the problem.
+    
+    When the import fails, abort the SWIG init and print a message. The
+    Python interpreter then prints out a backtrace, which can be useful
+    in tracking down the problem.
+
+commit 9a5185c736b8ab2fdfb7a1b55e5a3a4ad4ff5cfb
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Sat May 16 17:30:47 2015 -0700
+
+    scpi-pps: Chroma 62000P: Get capabilities from device name
+    
+    The Chroma 62000P series comes in various models with different
+    current and voltage capabilities. These are encoded in the *IDN
+    string, so just get them from there, rather than needing a profile
+    for every model.
+
+commit 5281993e0ad0cd2dcb4e0d17aa399db91f500ba0
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Fri May 8 20:54:08 2015 -0700
+
+    scpi-pps: Add profile for Chroma 62024P-80-60 DC Source
+
+commit df705a7af74b156d24b9fa63ca7928e1ea780be2
+Author: Jiří Pinkava <j-pi at seznam.cz>
+Date:   Sat May 30 00:00:19 2015 +0200
+
+    Java: install files into DESTDIR
+    
+    This fixes bug #537.
+
+commit 84ab9da11fc9c1c90667f0e234423d4d306580dd
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Jun 11 18:27:43 2015 +0200
+
+    saleae-logic16: Emit debug info of which bitstream is used.
+
+commit c86813962979777f53432e7e3392b1d2f2b661b4
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sat May 9 13:16:28 2015 +0200
+
+    saleae-logic16: Support new bitstream version 1.3 with renumbered registers
+
+commit da005885c8ebbf98772f75126764f758b2640d11
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jun 7 20:58:46 2015 +0200
+
+    scpi: Update names of a few *_OUTPUT_* items.
+
+commit ae1827f557a90e49dc3428bc931bddc5437e7868
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jun 7 20:42:33 2015 +0200
+
+    modbus: Consistently use the "Modbus" spelling.
+    
+    (as per modbus.org spelling)
+
+commit f54ebe0c066447e547d1d57d4bc612981b7d679d
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jun 7 20:40:32 2015 +0200
+
+    modbus: Minor typos, cosmetics and consistency fixes.
+
+commit ffb580cf732f060f167cbb3b910299395dd67aaa
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Feb 23 00:28:06 2015 +0100
+
+    Actual implementation for the maynuo-m97 driver.
+
+commit e1ccfb191062d13b98bb7536d162a044e0758678
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Feb 1 23:49:03 2015 +0100
+
+    maynuo-m97: Initial driver skeleton.
+
+commit e77e32a3bebcaadabb12eb29ceaf2d30371d16f0
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Feb 23 00:26:48 2015 +0100
+
+    libsigrok.h: add new type of device: ELECTRONIC_LOAD.
+
+commit c4b78389227b4d94ed211618f12a25f49d1ba8a3
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Mar 15 23:58:17 2015 +0100
+
+    libsigrok.h: Add SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE.
+
+commit 4c938fc27f1679d5239133d2a821ab7f39a256de
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Feb 23 00:20:26 2015 +0100
+
+    modbus: add a serial RTU backend.
+
+commit daa39012054a10007986b2463ac61efe4cdd6ac8
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Feb 23 00:13:31 2015 +0100
+
+    Add a modbus communication helper module.
+
+commit a2632bcafcba2c3d3ea08db44a497ad6d05f1c5f
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Feb 23 00:26:16 2015 +0100
+
+    libsigrok-internal.h: add helper macro to read floats from unaligend memory.
+
+commit 7a0b98b544ca00f351295f21f895442680b1c014
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Mar 15 18:43:06 2015 +0100
+
+    Rename SR_CONF_OUTPUT_* to SR_CONF_*.
+    
+    All those options are currently applied only to power-supplies
+    but they could apply as well to electronic loads, except for the
+    fact that electronic loads channels are called inputs and not
+    outputs.
+    Also when you think about an SMU (or any kind of 4-quadrants
+    power-supply), their channels can both source and sink current,
+    so they can be considered as input as much as output.
+    Those SR_CONF_* are thus renamed so that they can be used in all
+    those situations.
+
+commit c1603f4574c8ce9648b51ea587b99cfb0bb02420
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sat May 9 23:06:10 2015 +0200
+
+    scpi-pps: don't fail acquisition start for devices with no CMD_SELECT_CHANNEL
+    
+    For devices such as the HP 6632B the following invocation was failing due to
+    scpi_cmd(sdi, SCPI_CMD_SELECT_CHANNEL, ...) returning SR_OK_CONTINUE.
+    
+    ./sigrok-cli -d scpi-pps:conn=/dev/ttyUSB0:serialcomm=9600/8n1 --continuous
+    sr: session: sr_session_start: could not start an acquisition (not enough data to decide error status yet)
+    Failed to start session.
+
+commit bbc42811d0c29e37e449d38d897b8e9cecdc8ba3
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Wed Apr 29 01:08:17 2015 +0200
+
+    Use G_PI instead of defining our own version of PI.
+
+commit 562a34908e47cf3b698a822b2e8177dc26422358
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu May 7 03:15:35 2015 +0200
+
+    scpi-pps: Minor cleanups.
+
+commit c80cf3e02eaa8e3154a69a7cae48c361d45ebb4f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu May 7 02:52:00 2015 +0200
+
+    scpi-pps: Fix a compiler warning.
+    
+        CC       src/hardware/scpi-pps/profiles.lo
+      src/hardware/scpi-pps/profiles.c:67:2: warning: missing initializer
+      for field 'frequency' of 'const struct channel_spec'
+      [-Wmissing-field-initializers]
+        { "1", { 0, 60, 0.0001 }, { 0, 25, 0.1 } },
+        ^
+      In file included from src/hardware/scpi-pps/profiles.c:24:0:
+      src/hardware/scpi-pps/protocol.h:106:8: note: 'frequency' declared
+      here
+        float frequency[3];
+              ^
+
+commit 8cb5affe00fbe43fe3587090d1cfcaf6f4030856
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu May 7 02:49:59 2015 +0200
+
+    scpi-pps: Make some more items static.
+
+commit 5c9e56c95b9e0756a3849d3d45bd40e90330043c
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Thu Apr 23 09:38:14 2015 -0700
+
+    spci-pps: Add profile for Agilent N5767A DC source
+
+commit 6c0c9dd257353c5942d51024270dc82d7099a6de
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Wed Apr 29 17:33:03 2015 -0700
+
+    spci-pps/profiles: Support frequency control in Chroma 61604
+
+commit 4264f1c03b3cd0e7d2634e207c79b1973789af28
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Wed Apr 29 17:26:47 2015 -0700
+
+    scpi-pps: Add infrastructure for controlling output frequency
+    
+    This patch only adds the needed infrastructure to control output
+    frequency in the same manner as output voltage or current limit. This
+    does require a new field in the channel_spec struct, for the sake of
+    symmetry.
+
+commit a77585d4ae95e103ebf3ae03fc0a219366c4c0c7
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Wed Apr 29 17:05:59 2015 -0700
+
+    global: Add configuration key for output frequency target
+    
+    This will be used to set up the output frequency of AC sources.
+
+commit b94dd07b0823ae1607ce8159681a31833a01e199
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Wed Apr 29 16:58:24 2015 -0700
+
+    global: Treat SR_CONF_OUTPUT_FREQUENCY as float instead of uint64_t
+    
+    This makes 'output_frequency' symmetrical with 'output_current' and
+    'output_voltage'. On a more fundamental level, there's no reason why
+    frequency should be treated as a discrete quantity, other than
+    "es51919 used it this way".
+
+commit e8686e3ae36c190a88f1a15aebb7294bb6491578
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Tue Apr 28 13:03:58 2015 -0700
+
+    asix-sigma: Avoid use of variable length arrays
+    
+    This was only done once in sigma_write_register().
+
+commit 682fb08c88af64181a56c2db14ca3cef323c3ead
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Thu Apr 30 16:18:46 2015 -0700
+
+    python: classes.i: Declare strings with std::string
+    
+    This makes it consistent with the rest of the string declarations in
+    this file, all of which use std::string.
+
+commit 4ee1e2f35feb6b78640e6c8bd64b84cfd0675fd8
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Thu Apr 23 09:39:23 2015 -0700
+
+    spci-pps: Add profile for Chroma 61604 AC Source
+    
+    Only the capabilities which map directly to SCPI commands supported by
+    sigrok are implemented at this time. This is sufficient to control
+    the most often used functionality of this AC source
+
+commit 0c08023f506c164e6d7bdf46cd82ef437a468997
+Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+Date:   Wed Apr 15 21:30:10 2015 -0700
+
+    scpi: Accept *IDN responses with more than four tokens
+    
+    Some devices with more than one microcontroller report the firmware
+    version for each of them, giving us more than four tokens. When that
+    happens, sigrok aborts, even though it received a valid response.
+    
+    This happens, for example with the Chroma 61604:
+    'Chroma ATE,61604,001060,1.25,1.34,1.20'
+
+commit e35e1e7babee3cb3939e56f06322fa14f8580625
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Apr 27 00:06:03 2015 +0200
+
+    Compile with -std=c11 along with _POSIX_C_SOURCE=200112L.
+    
+    With gcc 5.1 released and defaulting to std=gnu11, the code will be compiled
+    according to different standards depending on the compiler version so we
+    should better specify explicitly what standard we are targetting.
+    C11 is now quite mature, it is supported in the just release Debian stable
+    (gcc 4.9) and also in old-stable (gcc 4.7), so there should be no reason to
+    use anything more ancient.
+    We also should have no reason to need any non-standard GNU extension.
+    So using only C11 + POSIX sounds like the best option right now.
+
+commit 76372c5a9ce25d94ee992a5c84b8135f0d945293
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Apr 27 00:42:05 2015 +0200
+
+    Define our own constant for PI.
+    
+    M_PI is not defined in the C standard nor in POSIX, it is a
+    non-standard GNU extension.
+
+commit ba464a121766fda8f8a1799b1b140b225c9774ee
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Apr 27 00:30:15 2015 +0200
+
+    strcasecmp() is defined in strings.h.
+
+commit 94b138a3c3d07cf5581c07536c53a97cebf885d9
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Apr 27 00:13:23 2015 +0200
+
+    wav: Stricter check for valid chunk ID.
+    
+    isascii() is a superset of isalpha() and isblank() so the current
+    code doesn't really make sense.
+    Moreover, isascii(x) is just a funky and non standard way to
+    write x < 128.
+
+commit 2b51d48b386a2050d0ab34f242161617877d8e5f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Apr 20 10:02:34 2015 +0100
+
+    C++: Change arguments of Input::send() from std::string to data+length.
+
+commit 8d80146722786dc682ee7fa8cf168ab6f06a8271
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Apr 19 18:39:35 2015 +0200
+
+    Lower libserialport requirement to 0.1.1.
+
+commit 468665dfa7a030ba3746e2796a196a47cacf4f76
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Apr 16 22:33:29 2015 +0200
+
+    Fix various warnings when building without libusb.
+    
+      ../src/backend.c: In function 'sr_init':
+      ../src/backend.c:435:1: warning: label 'done' defined but not used [-Wunused-label]
+       done:
+       ^
+    
+      ../src/device.c: In function 'sr_dev_inst_connid_get':
+      ../src/device.c:525:7: warning: unused variable 'connection_id' [-Wunused-variable]
+        char connection_id[64];
+             ^
+      ../src/device.c:524:20: warning: unused variable 'b' [-Wunused-variable]
+        int r, cnt, i, a, b;
+                          ^
+      ../src/device.c:524:17: warning: unused variable 'a' [-Wunused-variable]
+        int r, cnt, i, a, b;
+                       ^
+      ../src/device.c:524:14: warning: unused variable 'i' [-Wunused-variable]
+        int r, cnt, i, a, b;
+                    ^
+      ../src/device.c:524:9: warning: unused variable 'cnt' [-Wunused-variable]
+        int r, cnt, i, a, b;
+               ^
+      ../src/device.c:524:6: warning: unused variable 'r' [-Wunused-variable]
+        int r, cnt, i, a, b;
+            ^
+      ../src/device.c:523:22: warning: unused variable 'drvc' [-Wunused-variable]
+        struct drv_context *drvc;
+                          ^
+
+commit 17dda6c5d46a9152fb72de2b89376ac0914fffdc
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Apr 15 21:08:13 2015 +0200
+
+    ols: Whitespace fixes.
+
+commit d6fe5201d5182dc8447c34a1040a407846d3b7eb
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Apr 17 17:17:53 2015 +0200
+
+    .gitignore: Add tests/main.
+
+commit 192d37e728b13393e9987f3cc888670b60efd197
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Mon Apr 13 11:44:14 2015 +0200
+
+    baylibre-acme: Add a missing return value check.
+    
+    Check the return value of sr_gpio_setval_export() in bl_acme_set_power_off()
+    and return an appropriate error if the call fails.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 09d217a40af9a6b23d11d9c0dbdf78f9aa76682c
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Mon Apr 13 11:44:13 2015 +0200
+
+    Revert "baylibre-acme: Fix a compiler warning."
+    
+    This reverts commit 4cd97e5ad7bb63cb90d209506464fafd9f9eef8a.
+    
+    We should actually check the return value of sr_gpio_setval_export().
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 55462b8ba9f48931de9b9a7bfbc182a1d113b3b5
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Apr 12 19:28:03 2015 +0200
+
+    windows: Fix various compiler warnings.
+    
+    Add LIBUSB_CALL where needed to avoid warnings such as the following:
+    
+      In file included from src/hardware/hantek-dso/api.c:34:0:
+      src/hardware/hantek-dso/dso.h:212:13:
+      note: expected 'libusb_transfer_cb_fn' but argument is of type 'void (*)(struct libusb_transfer *)'
+       SR_PRIV int dso_get_channeldata(const struct sr_dev_inst *sdi,
+                   ^
+
+commit 76598cda5465e74e7427b5613f5859f02c2d2ad6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Apr 12 18:54:43 2015 +0200
+
+    input/wav: windows: Fix a compiler warning.
+    
+      src/input/wav.c:41:0: warning: "WAVE_FORMAT_PCM" redefined
+       #define WAVE_FORMAT_PCM          0x0001
+       ^
+      In file included from [...]/include/windows.h:86:0,
+                       from [...]/include/libusb-1.0/libusb.h:70,
+                       from ./src/libsigrok-internal.h:31,
+                       from src/input/wav.c:28:
+      [...]/include/mmsystem.h:482:0: note: this is the location of the previous definition
+       #define WAVE_FORMAT_PCM 1
+       ^
+
+commit 4cd97e5ad7bb63cb90d209506464fafd9f9eef8a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Apr 12 17:26:48 2015 +0200
+
+    baylibre-acme: Fix a compiler warning.
+    
+        CC       src/hardware/baylibre-acme/protocol.lo
+      ../src/hardware/baylibre-acme/protocol.c: In function 'bl_acme_set_power_off':
+      ../src/hardware/baylibre-acme/protocol.c:417:6: warning: variable 'val' set but not used [-Wunused-but-set-variable]
+        int val;
+            ^
+
+commit 7c91c22a31a5017117116fa1fe6c2addbac813f1
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Tue Apr 7 16:55:24 2015 +0200
+
+    baylibre-acme: Set update_interval when modifing samplerate.
+    
+    Both ina2xx and tmp401 linux drivers used by baylibre-acme expose
+    the standard hwmon update_interval attribute, which affects the internal
+    update interval of the chip.
+    
+    When setting samplerate for data acquisition try to modify this
+    attribute accordingly.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 1fe04eb8d6c84c0013413991d0632022fc0643f7
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Fri Apr 3 14:58:11 2015 +0200
+
+    baylibre-acme: Dynamically check per probe config options.
+    
+    PROBE_FACTOR and POWER_OFF options are advertised for all ACME probes
+    (channel groups) regardless of whether they actually have given capability.
+    
+    Check these options in config_list() at runtime and only advertise them
+    for probes which support them.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 1190c65397e8246bd3b437006bacc5b7dc9d2bde
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Apr 12 16:34:26 2015 +0200
+
+    Fix a USB timeout related issue in sr_session_iteration().
+    
+    This issue could lead to e.g. crashes when an OLS was used.
+    
+    This fixes bug #571.
+
+commit b65630f78da2930fb828f6c6388c8655036fe8a4
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Apr 9 23:13:30 2015 +0200
+
+    uni-t-dmm: Add a missing NULL (fixes a crash on Android).
+    
+    This caused an non-terminated driver list, which lead to a crash
+    on Android (at least on ARM).
+
+commit d93c14707e1700861339d86dd8fdd5fbefa170e6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Apr 9 20:02:17 2015 +0200
+
+    dslogic: Fix FPGA bitstream upload.
+
+commit 4df5739a9f4272e57d737dc3a957b2f723bbaad1
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Apr 9 19:55:08 2015 +0200
+
+    dslogic: Add #defines for timeouts and delays.
+
+commit eac0c6132752224f46661ddc5a37a1e0e4694c27
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Apr 8 19:14:05 2015 +0200
+
+    rigol-ds: Drop unneeded check of g_strdup_printf() result.
+
+commit 2d31e8bcbd98f0dee9047749324baedfcdd2be62
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Apr 8 18:50:07 2015 +0200
+
+    configure.ac: Drop unneeded AC_CANONICAL_SYSTEM.
+
+commit b4aa89de4cfc44947bc0fab54d7e22bafa418ecb
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Apr 7 02:35:08 2015 +0200
+
+    autogen.sh: Drop obsolete MinGW/MSYS items.
+    
+    For Windows builds (which require MinGW-w64) we currently support:
+    
+     - cross-builds using MXE (mxe.cc) and possibly other cross-compile setups
+    
+     - native builds using MSYS2 (sf.net/projects/msys2/)
+    
+    Neither of those require explicitly specifying ACLOCAL_DIR.
+
+commit fc8f82e9650127b73a6bdcdad565b8e7f8f34e5b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Apr 7 02:29:09 2015 +0200
+
+    cxx: Makefile.am: Use libtool's -no-undefined option.
+
+commit ff101fca121dd43e6323e572a5c655b6cab1e791
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 18 12:30:26 2015 +0000
+
+    windows: Fix building shared library on MinGW.
+
+commit 032da34b786333a1af811235c5cf29855479f0b6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Apr 4 20:57:22 2015 +0200
+
+    sr_driver_list() now takes a context pointer.
+    
+    This requires sr_hw_cleanup_all() and sanity_check_all_drivers()
+    to also take a context.
+    
+    The (runtime) generation of the driver list now happens in sr_init()
+    and sr_driver_list() always returns that pre-generated list. This fixes
+    a segfault when (correctly) invoking multiple sr_init() and sr_exit()
+    calls with different contexts (caught by the unit tests).
+    
+    This fixes bug #565.
+
+commit 07962655ecd48304d0663e0a91e54f52076c3f8b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Apr 4 19:28:19 2015 +0200
+
+    link-mso: Eliminate unneeded NUM_CHANNELS.
+
+commit 0f2627632944d27fcd561a8b3a4713869b326232
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Apr 3 21:05:02 2015 +0200
+
+    fx2lafw: Use libusb_error_name() for reporting transfer status.
+
+commit 07ffa5b315caab0d6d703e04f09035bc0f2b3ee4
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Apr 1 01:53:50 2015 +0200
+
+    Replace some magic numbers with a #define.
+
+commit dc89faeace016d0fbd8314937d0335a2ae76de14
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Mar 28 19:36:47 2015 +0100
+
+    Reduce unnecessarily high indentation level in some places.
+
+commit 2ea67fc9bf2b8e777aa45f88477f8880e5bc6286
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 27 08:43:45 2015 +0100
+
+    Minor cosmetics and consistency fixes.
+
+commit b1f8310376265f622c6f7d81c0cc0580598ea973
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 27 08:39:49 2015 +0100
+
+    Don't check g_free() arguments for NULL.
+    
+    The g_free() call is guaranteed to not segfault when NULL is passed.
+
+commit e742b88f9aef970f8d6755858c930a47f79782fa
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Mar 31 23:14:41 2015 +0200
+
+    cem-dt-885x: Add a missing break statement.
+
+commit 72d69b7cd1fe52145634450e43a739e66068a588
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Mar 31 23:14:24 2015 +0200
+
+    zeroplus: Add a missing break statement.
+    
+    This fixes a bug when trying to trigger on a channel being 0/low.
+
+commit 2cca921d919cba518dd1e281be678b77f6b8b6df
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Mar 31 22:58:50 2015 +0200
+
+    Fix a typo in a for loop (wrong variable).
+
+commit ce3ecb70494b5bcf483607d6ecf0906394623601
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Mar 26 21:57:56 2015 +0100
+
+    sysclk-lwla: Use ARRAY_SIZE instead of G_N_ELEMENTS.
+    
+    Both do exactly the same, but we consistently use ARRAY_SIZE
+    in the rest of the code-base.
+
+commit f05406117d67de3bd8aa5f904573bdd5d7ba401f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Mar 26 18:20:31 2015 +0000
+
+    uni-t-dmm: Declare each meter type in only one place.
+
+commit ca1c21ca3e603b4f383e56ddebbafaa73d89f894
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Mar 26 23:49:35 2015 +0100
+
+    unit tests: Update for sr_session_new() API change.
+
+commit c879dca3d71f321c2a972594a2186f01f398dce8
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Mar 25 18:25:57 2015 +0100
+
+    bindings/cxx/classes.cpp: Fix sr_session_load() invocation.
+
+commit 60f6b00144d5b5a2446e55f0389009a468a79a7d
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Mar 25 17:37:01 2015 +0100
+
+    bindings/cxx/classes.cpp: Fix a typo.
+
+commit bb5f61105bac2d7826416472e2168d8f59300aea
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 25 05:16:06 2015 +0000
+
+    Call libusb_get_next_timeout() to get minimum timeout for g_poll().
+    
+    We should have been doing this all along, but we get away with it
+    on Linux where libusb can do everything with fds, and we get away
+    with it for many drivers that have a short timeout on their events.
+    
+    On Windows though, handling this correctly is essential.
+    
+    Fixes bug #343.
+
+commit 4ed5d21d048c8feed085530b7fda6ed265a5913f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 25 01:43:30 2015 +0000
+
+    Store a context pointer in struct sr_session.
+
+commit 61e6e2da45373acea5dab93a6ede57aae9901b1b
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 25 01:41:10 2015 +0000
+
+    Make sr_session_new() and sr_session_load() require a context.
+
+commit c72981ac41f3cfc1ae770111fc74a88bc8416a97
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Mar 24 23:35:29 2015 +0100
+
+    Revert "session_file.c: Use config_*() wrappers."
+    
+    This temporarily reverts commit 421bc3eba02f060319c752a26461148fc93563ec.
+    
+    We cannot yet use the sr_config_*() wrappers, otherwise loading *.sr
+    files is broken. A fix is being worked on.
+
+commit f6c30de4b031ab9a8d838c1ad6ff4118b03c0dc4
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Mar 24 20:06:17 2015 +0100
+
+    Initial fix attempt for a thread-related issue on Windows.
+    
+    This is a partial fix for bug #343, which lead to a large amount of
+    handles being created, and eventually to a frontend "hang".
+    
+    It's not yet a "full" fix as some issues are still observable,
+    but it successfully improves the situation on Windows to the extent
+    that frontend hangs due to large amounts of handles no longer seem
+    to happen.
+    
+    Thanks to Boris Gjenero <boris.gjenero at gmail.com> for the debugging
+    efforts, testing, and updating of this patch!
+    
+    Additionally, this seems to also fix a "SysClk LWLA hanging" bug
+    and apparently not receiving any samples during an acquisition
+    (tested on an LWLA1034).
+    
+    This closes bug #328.
+
+commit 515ab0889ebde4b373d620044a1a98da37153056
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Mar 23 20:18:39 2015 +0100
+
+    Various #include file cosmetic fixes.
+    
+    Generally include system headers before local headers, unless there's
+    a technical reason to use another order.
+
+commit 98fec29ecbb5093856b69311be8e937c162edded
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Mar 23 20:09:08 2015 +0100
+
+    Various NULL-check consistency fixes.
+
+commit a95f142e88fa5368adfabf87544acfdeed7d7604
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Mar 23 19:54:53 2015 +0100
+
+    Some more g_try_*alloc() fixes.
+    
+    As per documented rules in HACKING, we don't check "small" allocations.
+
+commit 1a46cc62e2b528bcaeb1f8dc0c952a81b3bcba5c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Mar 22 23:07:30 2015 +0100
+
+    Improve readability and clarity of some numbers.
+
+commit f3f19d1131025b68d29a11273b627c83d748e7ea
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Mar 22 16:04:18 2015 +0100
+
+    Fix a bunch of typos.
+
+commit 7637cc60ba0201202eec98f66d8494e6ba3d53bd
+Author: Mike Walters <mike at flomp.net>
+Date:   Tue Mar 24 15:54:53 2015 +0000
+
+    Add udev rule for Rigol 1000Z series
+
+commit 8249889dfa871a45d57a6f513d4a1a5751673558
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Mar 22 00:17:07 2015 +0100
+
+    demo: Fix SR_CONF_DEVICE_OPTIONS variant type.
+
+commit 0f34cb472368be61aa2e7bc9d9d1b25bb28aa560
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Mar 21 20:12:50 2015 +0100
+
+    Channel names consistency fixes and simplifications.
+
+commit db24496ac8575e917996e7812279987f2141c298
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Mar 21 19:35:30 2015 +0100
+
+    Remove unneeded #endif comments.
+
+commit 93b118da4fec6976df924eb77121f07b361b8330
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 20 20:14:39 2015 +0100
+
+    Consistency and whitespace fixes for switch statements.
+
+commit d0148a506ed1007518704d72c0410d4361668fa4
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Mar 14 03:09:40 2015 +0100
+
+    Make memset() invocations consistent across all files.
+
+commit 0c5f2abc6697504b5d760dfa56cc90bea180198f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Mar 11 23:32:39 2015 +0100
+
+    Random whitespace and other minor fixes.
+
+commit 421bc3eba02f060319c752a26461148fc93563ec
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Mar 11 20:21:34 2015 +0100
+
+    session_file.c: Use config_*() wrappers.
+
+commit dcd438ee3523098201c7937e75e55775da3b506f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Mar 8 00:28:17 2015 +0100
+
+    Simplify a few config_set() callbacks.
+    
+    Also, extended logging and random whitespace fixes.
+
+commit 329733d92c5004f0fe308eff26b9537fded2cdf3
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 20 14:48:20 2015 +0100
+
+    Constify a few arrays and variables.
+
+commit 53cda65a6bff58efed83b9a1c9b058f7d713ba19
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 20 14:39:24 2015 +0100
+
+    Remove unneeded explicit array size specification.
+
+commit 1beccaed464a4d92a070988a0331fe399f9f7a7a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 20 14:36:47 2015 +0100
+
+    Various minor whitespace fixes.
+
+commit e8be616955f2bc4e2c72fc55d899056ad68fb75c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 20 14:34:18 2015 +0100
+
+    tondaj-sl-814: Add missing SR_PRIV.
+
+commit 145d794facd93b182e6c85d7613ce2e6d9a66d48
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Mar 21 18:46:00 2015 +0100
+
+    serial-dmm: Use g_malloc()/g_free().
+
+commit c35276dd92b948d0c8c71bd816a8a75453895e81
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Mar 21 18:43:17 2015 +0100
+
+    vc870: Fix a compiler warning.
+    
+      src/dmm/vc870.c: In function 'flags_valid':
+      src/dmm/vc870.c:380:54: warning: unused parameter 'info'
+      [-Wunused-parameter]
+       static gboolean flags_valid(const struct vc870_info *info)
+
+commit 1a8639164e4e44a43fe1558e30823606f7b607b3
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Mar 21 18:26:52 2015 +0100
+
+    Minor cosmetics, cleanups.
+
+commit 8852eb75d14730484e66f9e0ff2b177934615a6f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Mar 21 13:17:51 2015 +0100
+
+    serial-dmm: Drop obsolete extern declaration.
+
+commit bcbef5ed709a56e8cec3d7d0b5f73a00a4040984
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Mar 21 09:07:49 2015 +0000
+
+    serial-dmm: Declare each meter type in only one place.
+
+commit 4f840ce965b1c30c5ab75afecc56193cbaf5c1b3
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Mar 21 00:47:31 2015 +0000
+
+    Pass driver struct pointer to driver callbacks.
+    
+    This lays the groundwork for subdrivers to share callbacks without
+    needing a separate wrapper function for each subdriver.
+
+commit 9e60a31fb954493754770fe2192db74b947c6867
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Mar 20 20:39:29 2015 +0000
+
+    Construct driver array at runtime, from an array of per-file arrays.
+    
+    This lays the groundwork for drivers to define their own array of
+    subdrivers, rather than having to list each subdriver here.
+
+commit 702f42e8eb33d8d1ffb5b748097428c0f4434c6d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Mar 19 16:15:52 2015 +0000
+
+    rigol-ds: Add DS1000Z series support.
+    
+    Tested on an MSO1104Z with firmware 00.04.02.SP4.
+    
+    The analog channels are captured correctly. For the MSO series, with digital
+    channels, there are two outstanding issues:
+    
+    1. Logic data is retrieved per-channel, one byte per sample, with the value
+       in the LSB of each byte. The current datafeed logic format doesn't allow
+       this format to be passed on directly. I suggest we resolve that rather than
+       making the driver buffer and interleave the data.
+    
+       As stands, the code will retrieve data for all channels and pass it onto
+       the datafeed with unitsize=1. Channel D0 can used correctly if selected
+       alone. For other channels, data is passed to the frontend but the API does
+       not provide a way to associate it with the correct channel.
+    
+    2. Channels CH3 and CH4 are multiplexed with D0-7 and D8-15 respectively, so
+       enabling these is mutually exclusive. We don't currently have a way to
+       express this constraint to the frontend.
+
+commit c36f78f7728e8b5263bed440530a61caa6e30a26
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Dec 29 23:30:41 2014 +0100
+
+    Add initial Voltcraft VC-870 support.
+    
+    There are a few details that have yet to be implemented or
+    reverse engineered and tested.
+
+commit ded3e5087c53a85058b0e4332d4a37bb27d464ff
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 20 20:35:07 2015 +0100
+
+    libsigrok.h: Add SR_MQ_POWER_FACTOR and SR_MQ_APPARENT_POWER.
+
+commit 350b8b07f0d84653c0e8bbb4ef61d670d24e7c2a
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Wed Mar 18 14:26:43 2015 +0100
+
+    baylibre-acme: Fix a double free in bl_acme_set_shunt().
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit cfcdf576775a6e53f25dec50ec710bfc89723147
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 18 18:10:56 2015 +0000
+
+    scpi-pps: Add profile for Rigol DP821A.
+
+commit 7d4f1741e377099b9151226eb950ccbf89281fb0
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 18 18:02:06 2015 +0000
+
+    scpi-pps: Initialise sdi with status SR_ST_INACTIVE.
+    
+    Without this change a segfault occurs at exit after scan, because
+    dev_close() is called and the device is believed to be open.
+
+commit 40c2c9159cde208105fa2631019dc857b9918907
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Mar 19 19:22:04 2015 +0000
+
+    rigol-ds: Fix broken channel group check in config_list().
+
+commit f579d08bc592e0ca6a6183e8f75fcd2d4bc72710
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Mar 19 17:50:24 2015 +0000
+
+    rigol-ds: Data source is a device option, not per channel group.
+
+commit 98bfc4741f76b07e7db4c9628a6924004fc4fff4
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 18 15:14:29 2015 +0000
+
+    rigol-ds: Use sr_scpi_get_bool().
+
+commit bff16ba817a5609e91ca803892354ab7dbc396df
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 18 16:35:39 2015 +0000
+
+    rigol-ds: Fix double free.
+    
+    std_dev_clear() frees all channel groups, so this one is not required.
+
+commit 16aca7661b7ab34a399c323bb9214721e2b1be0c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Mar 18 16:35:08 2015 +0000
+
+    rigol-ds: Fix wrong channel group malloc size.
+
+commit 6f1346fbd7bbb6cca97d8a6c02b36156fce24ec2
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Mar 19 21:55:48 2015 +0000
+
+    Change API of channel accessor functions to take struct sr_channel *.
+
+commit 837b08660a2ffaef0d2e47ef9555bd3af3f4874f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Mar 19 21:41:51 2015 +0000
+
+    Add sdi pointer to struct sr_channel.
+
+commit 5e23fcab889c62864b92aa3ad6902ce3e9f5be49
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Mar 19 21:37:33 2015 +0000
+
+    Simplify channel creation.
+    
+    We always follow sr_channel_new() with a call to add the channel to the sdi.
+    Tidy up a bit by adding this functionality to sr_channel_new() instead.
+
+commit bc497772512c2fd37516964ade58b69448aae37c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Mar 18 23:08:26 2015 +0100
+
+    fx2lafw: Replace obsoleted strncmp() calls.
+    
+    The fixed lengths for strncmp() can no longer be used since strings of
+    various lengths can be passed to match_manuf_prod(). Use strcmp() instead.
+
+commit 6fcf3f0a22e3dd2467fd09b0cad92b36e0409fc4
+Author: eightdot <gituser at eightdot.eu>
+Date:   Mon Mar 16 10:34:08 2015 +0100
+
+    Various fixes/updates to make the driver compile.
+    
+    This patchset was originally done by eightdot <gituser at eightdot.eu> by
+    manually forward-porting parts of the changes done by Bert Vermeulen (see
+    previous commits), but then heavily modified by Uwe Hermann to be based on
+    top off the (git-)rebased patches from Bert Vermeulen instead.
+    
+    Note: This initial DSLogic code is *not* yet in a working or usable
+    state. It should be considered as a basis for further work only, for now.
+
+commit b9d530920fa97ab92d5f78f6f00a1ffc73259f2f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri May 16 23:04:14 2014 +0200
+
+    fx2lafw: Basic acquisition support for DSLogic.
+
+commit 0e6510b8fc9a4411684d26c1c2d8493d3637cb95
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu May 15 22:01:34 2014 +0200
+
+    Add udev rule for DSLogic.
+
+commit a7d7f93c1ba6f08c243b894dda29b8ac0349bf10
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat May 3 09:37:00 2014 -0700
+
+    fx2lafw: scan/firmware support for DSLogic.
+
+commit c04cf9aa819093e51f382bf5cd28e9ef2565256f
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Mar 5 18:28:10 2015 +0100
+
+    output: Accept analog packets in csv output module.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit f7ab2f231a5c3f52f386084fa4318ee0781031ec
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 13 19:12:47 2015 +0100
+
+    ols: Fix a compiler warning (unused variable).
+    
+        CC       src/hardware/openbench-logic-sniffer/api.lo
+      ../src/hardware/openbench-logic-sniffer/api.c: In function 'scan':
+      ../src/hardware/openbench-logic-sniffer/api.c:103:10: warning: unused
+      variable 'probefd' [-Wunused-variable]
+        GPollFD probefd;
+                ^
+
+commit ed936ccc7c386c01c3a88f8c005f0d041f02f451
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Mar 13 08:33:22 2015 +0100
+
+    ols: Fix detection and acquisition on Windows.
+    
+    Use the more portable sp_input_waiting() instead of g_poll() with FDs.
+    Thanks to Martin Ling for the hints. This is tested on Linux and Win7
+    using an OLS; scanning for the device and starting an acquisition works.
+    
+    Also, add some more debug output.
+    
+    This fixes bug #562.
+
+commit 5fabeeac6af940b52f67ac8c925952accc9b39b3
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Fri Feb 27 16:48:17 2015 +0100
+
+    baylibre-acme: correctly handle channel group options
+    
+    Split device options into general and channel group settings, and
+    adjust config_list() callback appropriately.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit bf622e6d00e7985e36a0ddb643e2bf05d66679a6
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Mar 1 14:26:54 2015 +0000
+
+    Rename SR_CONF_NUM_TIMEBASE to SR_CONF_NUM_HDIV.
+
+commit 6df8e239de62694b30a3bb061e421ea12363c185
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Mar 2 12:23:32 2015 +0100
+
+    ut372: Minor cosmetics.
+
+commit 12318aab968eb27954d4b1497eecc31f9f3c3c1d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Mar 1 23:20:21 2015 +0000
+
+    ut372: Support count mode.
+
+commit b9c10ae18b24984bb230e2c4ae8f5d067d183815
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Mar 1 23:14:58 2015 +0000
+
+    Add SR_MQ_COUNT for event count measurements.
+
+commit 8c9092b00b9c2451a8daf62bdfe75082566b2f89
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Mar 1 23:07:11 2015 +0000
+
+    ut372: Handle flags correctly.
+    
+    Packets will now be rejected if the device is not displaying RPM.
+
+commit 118268a2fe27e0ec998589e228e4b9d87a760883
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Mar 1 22:52:52 2015 +0000
+
+    ut372: Break out character-pair decoding to a separate function.
+
+commit 472bef399078cd7e80f35940a527828a1c841c46
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Mar 1 22:26:21 2015 +0000
+
+    ut372: Implement initial protocol parser.
+    
+    For now this only works correctly if the device is in the default
+    state showing current RPM. The flags are not checked.
+
+commit f3cde30904b2bc5ac5b1782d5e65d1ec8ce60ed6
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Mar 1 21:13:30 2015 +0000
+
+    ut372: Initial sub-driver skeleton.
+
+commit 2cb63065f525655d3abccc1b26edd7275937b034
+Author: Baruch Even <baruch at ev-en.org>
+Date:   Fri Feb 27 15:15:26 2015 +0200
+
+    serial-dmm: Add MASTECH MS8250B as a supported DMM.
+    
+    It is an alias and uses the FS9721 driver.
+
+commit 25609412233a67514fed29b9c87824ad5a00884f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Feb 26 18:55:17 2015 +0100
+
+    configure.ac: Two more fixes for the baylibre-acme detection.
+
+commit d9c3331d12d3060d33b1d2ee592c26c1d927dce8
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Feb 21 20:57:27 2015 +0100
+
+    asix-sigma: Use the more portable g_usleep().
+
+commit f80b3fe2340c1f4599c4194e6171a65ec44d43f3
+Author: Vladislav Ivanov <vlad-mbx at ya.ru>
+Date:   Thu Feb 26 08:32:59 2015 +0100
+
+    configure.ac: Fix baylibre-acme OS check.
+
+commit 380ee96fdfe0895ca0aa0b158d5c332ef08f8b3c
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Tue Feb 17 18:27:04 2015 +0100
+
+    baylibre-acme: don't report ACME as detected if no probes are present
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit b1e034f7584ee71b116aeca7f50ae3ec738ccbf0
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Wed Feb 18 13:21:18 2015 +0100
+
+    Doxyfile: Make exclude patterns more specific.
+    
+    Current exclude patterns lead to unwanted exclusion of all paths
+    containing common directory names like output, bindings etc. even
+    if those names occur higher in the directory structure.
+    
+    Make exclude patterns more specific by prefixing them with src/.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 3d14ce49e49e5dc299f3f08a3e294e59aed7d4ac
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 17 16:10:33 2015 +0100
+
+    baylibre-acme: Fix vendor/device name.
+
+commit e00b3f5897d9b5b85b552f9f8da7684229b9bdfc
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 17 15:55:37 2015 +0100
+
+    Various Doxygen updates.
+
+commit 02a2bf688f25a50ea05276be75fba8b4f644fca6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Feb 16 22:39:19 2015 +0100
+
+    unit tests: Drop unneeded check_ filename prefix.
+
+commit d258022db0b79dffc2bbdde0882e821966dbc312
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Feb 16 01:53:15 2015 +0100
+
+    baylibre-acme: Drop unneeded comment.
+    
+    There's indeed no g_fclose() unfortunately. The g_*() wrappers for file
+    handling are mainly there to deal with portability issues in file names
+    (encoding, character sets, etc) on different platforms.
+
+commit 3452785431dea578275d2dfd584709c5f7c6db9a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Feb 16 01:51:53 2015 +0100
+
+    baylibre-acme: Fix a compiler warning.
+    
+    Use PRIu64 to avoid the following compiler warning:
+    
+        CC       src/hardware/baylibre-acme/gpio.lo
+      protocol.c: In function 'bl_acme_set_shunt':
+      protocol.c:341:2: warning: format '%llu' expects argument of type 'long long unsigned int', but argument 3 has type 'uint64_t' [-Wformat=]
+        g_fprintf(fd, "%llu\n", MOHM_TO_UOHM(shunt));
+        ^
+
+commit 391e23a97fe5b919f7f0716702bd1b151c601f66
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Feb 16 01:49:47 2015 +0100
+
+    baylibre-acme: Minor coding-style, cosmetics.
+
+commit 740ad48ac8acd3357b1178ff09620b9e3a87c10e
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Feb 12 14:53:55 2015 +0100
+
+    baylibre-acme: Add support for SR_CONF_POWER_OFF.
+    
+    Allow to remotely cut the power at ACME probe level.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 289eebd7ca9f1229fe7e9d318c4895069ed82d6e
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Feb 12 14:53:54 2015 +0100
+
+    baylibre-acme: Add Linux-specific GPIO helpers.
+    
+    These functions allow to export, read and set GPIOs using Linux
+    sysfs interface.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 61f2b7f74cd2d05cacb2bfb3cad2c2d67c856f47
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Feb 12 14:53:53 2015 +0100
+
+    baylibre-acme: Add support for probe factor setting.
+    
+    Implement support for SR_CONF_PROBE_FACTOR setting in BayLibre ACME
+    driver. Given the channel-group parameter this allows to set the
+    shunt resistance for each probe.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit d3c81725aeff955b45da41d6f4810569d660e40d
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Feb 12 14:53:52 2015 +0100
+
+    SR_CONF_PROBE_FACTOR: New option.
+    
+    Add new configuration option allowing to modify the probe factor
+    for oscilloscopes and power-monitors.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit a0b277ba67169ce4343dc7ce14d350b930ee4eef
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Feb 12 14:53:51 2015 +0100
+
+    configure.ac: Compile baylibre-acme driver for Linux only.
+    
+    The driver for BayLibre ACME depends on Linux-specific sysfs
+    interfaces to ina226 and tmp435 devices. Exclude it for different
+    targets.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 6b80b80dcf2c6f6cdc4c8a782085846c861bd10d
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Feb 12 14:53:50 2015 +0100
+
+    baylibre-acme: Driver implementation.
+    
+    Implement basic functionalities for baylibre-acme. Add support
+    for common config options, device detection and sample reading.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit dfaee1de1711cdfba7bd780b448188819691a2db
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Feb 12 14:53:49 2015 +0100
+
+    baylibre-acme: Initial driver skeleton.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 96cb7faac37568df717de175ae7800aeebeb918f
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sat Feb 14 23:54:13 2015 +0100
+
+    rigol-ds: fix the smallest supported vdiv for the DS2000 series.
+
+commit 81b85663f3511317ecbd86527057539a12806885
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sat Feb 14 23:52:33 2015 +0100
+
+    rigol-ds: return the actual hardware num_vdiv and vdiv list.
+
+commit c33ff3771bcbab940ee7da88c89e70b61c26d0d3
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sat Feb 14 23:48:36 2015 +0100
+
+    rigol-ds: fix search for the closest vdiv.
+    
+    We try to find the smallest diff by comparing each diff with
+    the previously known smallest diff, so initially, this smallest diff
+    should be INFINITY so that we are sure to find a smaller one.
+    
+    This fixes the following exception:
+    sr: rigol-ds: Negative vdiv index: -1.
+    Caught exception: not applicable
+
+commit f44f7e61a37f0b42dc06e85278b0b152ddc70ab2
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Feb 14 19:23:34 2015 +0100
+
+    rigol-ds: Add missing "break" statements.
+
+commit d50725e0126a5d52d97e7ea49e37c66c15108156
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Feb 14 19:08:39 2015 +0100
+
+    rigol-ds: Add missing 20/50/100V vdiv entries.
+    
+    These are available on e.g. Rigol DS1102E (or "upgraded" DS1052E).
+    
+    Without this, if one of the channels happens to have been set to
+    one of the missing vdiv settings frontends (e.g. PulseView) will
+    have some trouble using the scope:
+    
+      sr: hwdriver: sr_config_get(): key 30012 (vdiv) sdi 0x11bcb70 cg CH1
+      sr: rigol-ds: Negative vdiv index: -1.
+      std::exception
+
+commit e1b5b7e735608bcabb674d807f8321e9e8e00fa5
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Feb 14 18:34:04 2015 +0100
+
+    rigol-ds: Add more debug output.
+
+commit d5c4144e2cd5129da947277c24b699066d0bec72
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Fri Feb 13 16:37:24 2015 +0100
+
+    rigol-ds: handle full word trigger slope in config_get().
+    
+    Some scope can return POSITIVE/NEGATIVE instead of POS/NEG,
+    so accept this as well.
+    
+    This closes bug #558.
+
+commit b0c9d1d1c2cc9f57716caeef4288a03839d03266
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Feb 12 15:56:58 2015 +0100
+
+    rigol-ds: SR_CONF_TRIGGER_SLOPE is actually listable.
+
+commit 73931b7cc732f490ba6d2413707e50462666fa86
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Feb 12 11:30:52 2015 +0100
+
+    input/vcd: fix parse_header() return value check.
+    
+    Mixing tests for both a boolean and an SR_ERR at the same time is not
+    really a good idea.
+    parse_header() actually returns a boolean so only check if it returns FALSE.
+    
+    This fixes the following gcc-5 warning:
+    
+    src/input/vcd.c: In function 'receive':
+    src/input/vcd.c:506:34: warning: logical not is only applied to the left hand side of comparison [-Wlogical-not-parentheses]
+       if (!parse_header(in, in->buf) != SR_OK)
+                                      ^
+
+commit 2617c81a4b101714bf90173f4da0724007c58219
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Feb 12 11:24:11 2015 +0100
+
+    Remove the inline qualification from sr_rational_set().
+    
+    Inlining can only happen in the same compilation unit where the
+    function was defined, so there is no sense declaring an inline
+    function in a header if this function is not defined in this
+    same header.
+    
+    This fixes the following gcc-5 warning:
+    
+    In file included from include/libsigrok/libsigrok.h:1066:0,
+                     from src/version.c:21:
+    include/libsigrok/proto.h:36:20: warning: inline function 'sr_rational_set' declared but never defined
+     SR_API inline void sr_rational_set(struct sr_rational *r, uint64_t p, uint64_t q);
+                        ^
+
+commit ee29d92e140a7b516b4ad24b2cd3047bfa5497cd
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Feb 12 11:14:37 2015 +0100
+
+    Correctly copy sr_datafeed_meta in sr_packet_copy().
+    
+    Commit 5801d558 replaced g_slist_copy_deep() by some incorrect code
+    that actually leaks the newly allocated memory, instead of doing
+    a deep copy.
+    
+    This new version should be more correct, more concise, and it fixes
+    the following warning:
+    
+    src/session.c: In function 'sr_packet_copy':
+    src/session.c:1025:38: warning: passing argument 2 of 'g_slist_foreach' from incompatible pointer type [-Wincompatible-pointer-types]
+       g_slist_foreach(meta_copy->config, (GCopyFunc)copy_src, NULL);
+                                             ^
+    In file included from /usr/include/glib-2.0/glib/gmain.h:26:0,
+                     from /usr/include/glib-2.0/glib/giochannel.h:33,
+                     from /usr/include/glib-2.0/glib.h:54,
+                     from src/session.c:24:
+    /usr/include/glib-2.0/glib/gslist.h:125:10: note: expected 'GFunc {aka void (*)(void *, void *)}' but argument is of type 'void * (*)(const void *, void *)'
+    void     g_slist_foreach                 (GSList           *list,
+             ^
+
+commit c4f9582714563b1929670061e6d2f87d8a64d152
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Feb 11 16:26:12 2015 +0100
+
+    sr_strerror_name(): Add missing SR_ERR_IO entry.
+
+commit 3c5582595a024e6221018eced858e62f0bed849b
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Mon Feb 9 18:31:14 2015 +0100
+
+    SR_ERR_IO: new error code
+    
+    Add new error code which can be used to notify the user about
+    general input/output errors.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 3cdff6cd2d1ce64e901540fa86a263666222ac07
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Mon Feb 9 18:31:09 2015 +0100
+
+    configure.ac: Add AC_CANONICAL_SYSTEM macro.
+    
+    In order to determine the target OS when cross-compiling libsigrok
+    we need autotools to set the 'target_os' variable. This macro
+    determines the system type and sets output variables to the names
+    of the canonical system types.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 187cfc604e51ee534fef8dc33278caa8eb1ff66d
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 10 22:55:41 2015 +0100
+
+    backend: Add basic transform module sanity checks.
+
+commit 8677e4e7be9aa857d273f37da2beb21d457878c4
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 10 22:47:36 2015 +0100
+
+    transform: Add a few basic unit tests.
+
+commit d74d30bb14b9cb240e244caff7179926c72630e6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Feb 11 09:34:38 2015 +0100
+
+    transform: Add an "invert" transform module.
+    
+    This inverts the data values:
+    
+     - An analog value of x becomes 1/x.
+    
+     - A digital value of 0 becomes 1 (and vice versa).
+
+commit 43caa4662388ccc58c0ed8d8e861c52d10d5ded2
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 10 22:25:52 2015 +0100
+
+    transform: Add a "scale" transform module.
+
+commit 39f1752eff0cde7eaedcc5ed950d56f4f244ea32
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 10 21:38:21 2015 +0100
+
+    transform: Add a "nop" transform module.
+    
+    This doesn't do anything, just passes input packets on unmodified.
+
+commit c0a1e532f57c0405621a06f5b360c1ef25aa4c52
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 10 21:24:23 2015 +0100
+
+    transform: Hook up transforms.
+
+commit 988357ca2f0cb0d0e066111c12b9c0de74a53a1b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 10 20:49:46 2015 +0100
+
+    transform: Add a basic set of API calls.
+
+commit 790320f605062718115d791d2b46c1e54bc4908e
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Feb 10 20:42:50 2015 +0100
+
+    transform: Add struct sr_transform and struct sr_transform_module.
+
+commit b595a45876b351e9259248ae9c7afc3593d53f24
+Author: Uffe Jakobsen <uffe at uffe.org>
+Date:   Tue Feb 10 14:09:28 2015 +0100
+
+    Fix problem building with BSD make
+    
+    This fixes bug #556:
+    
+    Bug 556 - libsigrok fails to build with BSD Make
+
+commit d378f1009999f8b9a1037a9f417ea5518d9a6d37
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 31 22:41:04 2015 +0100
+
+    Makefile.am: Add NEED_SERIAL condition.
+
+commit 2182e775116dba1ef61b62de12480ab5bb21c4b1
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 31 22:29:07 2015 +0100
+
+    Makefile.am: Unconditionally build src/lcr/es51919.c.
+    
+    We do the same with DMM parsers currently, and this change also
+    fixes an issue in sigrok-util's 'new-driver' script.
+    
+    This fixes bug #545.
+
+commit 479b7e469ad5ee24e621c1848debe9e325e854e2
+Author: Daniel Elstner <daniel.elstner at kdab.com>
+Date:   Sat Jan 31 21:08:14 2015 +0100
+
+    Update list of files ignored by git
+
+commit 1f501d72ef58861e27c6c7fcbb26780ad41010ef
+Author: Daniel Elstner <daniel.elstner at kdab.com>
+Date:   Sat Jan 31 21:07:28 2015 +0100
+
+    configure: Avoid bashism breaking the C++ bindings
+
+commit 57ba5f3d56a3e2927d35229a684cc9b4b8435789
+Author: Daniel Elstner <daniel.elstner at kdab.com>
+Date:   Sat Jan 31 21:04:14 2015 +0100
+
+    sysclk-lwla: Widen constant to 64 bit before shifting
+    
+    (lwla_convert_trigger): Fix trigger mask computation bug introduced
+    by recent change:  Widen constant to 64 bit before shifting so that
+    channel nunmbers beyond 32 are processed correctly.
+
+commit 5801d558e717d02947761854f22d64afd223d8c8
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Jan 29 08:51:31 2015 +0100
+
+    Lower dependency to glib 2.32.
+    
+    By avoiding g_slist_copy_deep() for now, we can easily allow libsigrok
+    to build against glib 2.32 (less hassle for users of stable/older
+    distros or OSes).
+
+commit 8656a71790133d4de42252a1e75b4209c03b4983
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Jan 27 09:00:34 2015 +0100
+
+    output/gnuplot: Use .dat as suggested file extension.
+    
+    Gnuplot doesn't have any "official" file name extension(s). It uses
+    (at least) two different types of files basically:
+    
+     - "control files": These can have many different somewhat commonly
+       used extensions such as .gpi, .gnu, .gnuplot, .gp, .plt, .gih,
+       others. These files don't contain data, only Gnuplot commands such
+       as 'set yrange [75:105]', 'set ylabel "foo" offset 1', and so on.
+    
+     - "data files": This is what libsigrok reads and writes. These files
+       contain actual data to be graphed by Gnuplot (with the help of a
+       specially-crafted control file, see above). The data is usually in
+       a tab-separated format. The common file extension is usually .dat,
+       though many others are possible as well.
+
+commit 4fb0a5f8a022b4b551f0dcac5c458c7108ea613c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Jan 27 08:33:51 2015 +0100
+
+    in/out: Minor consistency renames.
+    
+     - 'struct sr_input *' variables are consistently named 'in'.
+     - 'struct sr_input_module *' variables are consistently named 'imod'.
+    
+     - 'struct sr_output *' variables are consistently named 'o'.
+     - 'struct sr_output_module *' variables are consistently named 'omod'.
+
+commit a82c83c6a506a41154344593948de859e5b9ae7e
+Author: Joel Holdsworth <joel at airwebreathe.org.uk>
+Date:   Wed Jan 21 01:02:37 2015 -0500
+
+    output/wav: Improved description
+
+commit 8a174d23427735617d69c7502ed8dcade786bbf9
+Author: Joel Holdsworth <joel at airwebreathe.org.uk>
+Date:   Wed Jan 21 01:02:14 2015 -0500
+
+    output: Added preferred file extension field
+    
+    This fixes parts of bug #541.
+
+commit c7bc82ffa1b09a228a8395049e2b691cd7bd85f8
+Author: Joel Holdsworth <joel at airwebreathe.org.uk>
+Date:   Wed Jan 21 00:56:51 2015 -0500
+
+    input: Added preferred file extension field
+    
+    This fixes parts of bug #541.
+
+commit dc7125bb7cfe34f63695ea928dda17594dfac3d2
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Jan 26 15:26:15 2015 +0100
+
+    cxx: Fix a linking issue.
+    
+    Fix "undefined reference to `sigrok::EnumValue<sigrok::LogLevel,
+    sr_loglevel>::_values'", which happens at least when using clang(++),
+    e.g. on Linux, Mac OS X, or FreeBSD.
+    
+    This fixes bug #534.
+    
+    Thanks to Uffe Jakobsen and Martin Ling for reporting and investigating!
+
+commit 19643b96e27cdeb84123fc91158b25a0d1a6368d
+Author: Uffe Jakobsen <uffe at uffe.org>
+Date:   Thu Jan 22 01:11:22 2015 +0100
+
+    Fix FreeBSD issue with libusb_get_port_numbers()
+    
+    Currently (as of date 20150122) an ioctl problem within the
+    FreeBSD kernel is preventing libusb_get_port_numbers() from working.
+    Hence calls to libusb_get_port_numbers() will always return 0.
+    This makes it impossible to establish a physical path the the usb device.
+    
+    This problem has existed "forever" -
+    meaning that libusb_get_port_numbers() has never worked.
+    
+    A fix is committed to FreeBSD "current" head  -
+    and will later be merged (MFC'ed) to maintenance branches.
+    See: https://svnweb.freebsd.org/base?view=revision&revision=277417
+    
+    Additionally FreeBSD requires that devices prior to calling
+    libusb_get_port_numbers() have been opened with libusb_open().
+    
+    The patch is "forwards-compatible".
+    Currently it acts specificly to libusb_get_port_numbers()
+    currently returning 0 on FreeBSD.
+    In these situations it constructs an artificial path to the device.
+    When FreeBSD kernels appears with proper working ioctl
+    supporting libusb_get_port_numbers() the code will construct
+    proper physical paths for newer kernels - while still generating
+    artificial physical paths for older defective kernels.
+
+commit 7a8a1aba3797ed250f7b0e149ea4a6306be364b8
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Jan 22 10:22:59 2015 +0100
+
+    demo: implement averaging support
+    
+    Add support for averaging and avg_samples option to the demo device.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit 9ed444e6226c5d36b637708864361429f77f07a8
+Author: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+Date:   Thu Jan 22 10:22:58 2015 +0100
+
+    new config options: averaging
+    
+    Add new config options to libsigrok - 'averaging', which allows to
+    enable averaging of samples and 'avg_samples' for setting the number
+    of samples to be averaged over in each cycle.
+    
+    Signed-off-by: Bartosz Golaszewski <bgolaszewski at baylibre.com>
+
+commit d5062672dd185e351f8d91e851820df069b6429e
+Author: Uffe Jakobsen <uffe at uffe.org>
+Date:   Fri Jan 23 23:55:14 2015 +0100
+
+    Check for librevisa >= 0.0.20130412 - the latest official release
+
+commit 7a54232ba06b0715e4c98a913d1b316c8c521c2c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jan 18 16:02:54 2015 +0100
+
+    sr_input_new(): Add missing @param comment.
+    
+    This fixes a Doxygen warning:
+    
+      src/input/input.c:209: warning: The following parameters of sr_input_new(const struct sr_input_module *imod, GHashTable *options) are not documented:
+        parameter 'imod'
+
+commit 48d92e2c2e6291412e8a4d2218184112ebf0fad9
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Jan 18 22:43:02 2015 +0000
+
+    C++: Fix segfault where input/output options are NULL.
+
+commit 718a9d382ff03857105c737526effbc0d097349c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 17 21:47:59 2015 +0100
+
+    Fix two warnings when libserialport is not used.
+    
+        CC       src/fallback.lo
+      src/fallback.c: In function 'sr_serial_list':
+      src/fallback.c:26:59: warning: unused parameter 'driver' [-Wunused-parameter]
+       SR_API GSList *sr_serial_list(const struct sr_dev_driver *driver)
+                                                                 ^
+      src/fallback.c: In function 'sr_serial_free':
+      src/fallback.c:31:51: warning: unused parameter 'serial' [-Wunused-parameter]
+       SR_API void sr_serial_free(struct sr_serial_port *serial)
+
+commit 0959ed3d028d1110d36f2c67840872c2ebc54e36
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 17 20:29:05 2015 +0100
+
+    Install libsigrokcxx headers in separate directory.
+    
+    Don't mix libsigrokcxx header files into the $prefix/include/libsigrok
+    directory. Use a separate $prefix/include/libsigrokcxx instead.
+
+commit 161dc24d845acfc00108dd33eac7c42430d21e17
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 17 20:11:53 2015 +0100
+
+    cxx: Rename include/libsigrok/ to include/libsigrokcxx/.
+
+commit 1b40fdb88108699cf9d912f3d7aadffb4dc04050
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 17 20:05:15 2015 +0100
+
+    Rename libsigrok.hpp to libsigrokcxx.hpp.
+    
+    This avoids confusion of libsigrok.h vs. libsigrok.hpp and makes it
+    clearer that this is the main libsigrokcxx header.
+
+commit e0e6aecc20ac57c30b276df129b1151b8eab0508
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 17 19:59:44 2015 +0100
+
+    libsigrok.hpp: Fix incorrect glibmm.h #include.
+    
+    As per upstream docs "#include <glibmm.h>" is the correct usage,
+    the "glibmm-2.4" directory is handled correctly via pkg-config:
+    
+     $ pkg-config --cflags glibmm-2.4
+     -I/usr/include/glibmm-2.4 [...]
+
+commit afba88adb5befa17f0d0af31e53f792431aeee44
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 17 19:53:44 2015 +0100
+
+    libsigrokcxx: Drop Requires.private entry.
+    
+    libsigrokcxx.pc has a "Requires" field listing "libsigrok" which
+    will cause libsigrok's "Requires.private" entries to be used/inherited
+    when 'pkg-config --libs --static libsigrokcxx' is used.
+
+commit 52ff4f6a04cbdb966ee538bd447ae206215b528a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 17 18:49:04 2015 +0100
+
+    Rename libsigrokxx to the more common libsigrokcxx.
+
+commit f3256bb7060c56934ce9ad1bcffb318de2d899f4
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Jan 12 01:19:12 2015 +0100
+
+    Makefile.am: Use $(MKDIR_P) everywhere.
+
+commit 3b95bd9152e6f54e259caf0909af86768c518e7a
+Author: Mathias Katzer <mkatzer at gmx.de>
+Date:   Sun Jan 11 01:40:28 2015 +0100
+
+    scpi: Fix incomplete data issue for e.g. Hameg HMO1024.
+    
+    On a Hameg HMO1024 you get incomplete data because the USB transfer takes
+    longer than the scpi->read_timeout_ms of 1 second that is defined in
+    scpi_dev_inst_new(). Therefore reset the timeout in sr_scpi_get_string()
+    whenever the device sends a partial response.
+
+commit f62f595bfc730070fbf95f7a75c45aef9622fdd4
+Author: Mathias Katzer <mkatzer at gmx.de>
+Date:   Sun Jan 11 01:39:41 2015 +0100
+
+    hameg-hmo: Fix double-free issue.
+
+commit a1b61e6e04246d7619d3cb7e3c6ab77ab6d2e3d5
+Author: Mathias Katzer <mkatzer at gmx.de>
+Date:   Sun Jan 11 01:38:20 2015 +0100
+
+    hameg-hmo: Set SR_CONF_GET | SR_CONF_SET for frame limit.
+    
+    Reading analog data from an HMO1024 (Firmware 04.527) failed because the
+    frame limit could not be set.
+
+commit 6ae536258977b8a38bd7c58ad9a86fec5e4feece
+Author: Jiří Pinkava <j-pi at seznam.cz>
+Date:   Fri Jan 9 00:45:48 2015 +0100
+
+    Python bindings: respect DESTDIR.
+    
+    Thanks Aurelien Jacobs <aurel at gnuage.org> for a suggested
+    fix/improvement.
+
+commit 787ec9dbd9613d34e0afed6ec145f24917e450c5
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Dec 20 17:33:24 2014 +0200
+
+    Limit frames instead of samples.
+    
+    Since every individual measurement is represented by a single frame
+    and a "sample" isn't all that meaningful concept in this context,
+    it makes more sense to define possible limit in number of frames.
+    Make the es51919 driver to support setting a frame limit instead of
+    a sample limit.
+
+commit a6413fa58e455fb4a654720218183e297835226c
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Dec 20 17:32:26 2014 +0200
+
+    Use frames to group a single measurement result together.
+    
+    In most, but not all, modes the ES51919 reports two separate
+    analog values for each measurement sample. These values are
+    mapped to two separate channels and sent in two separate
+    packets.
+    
+    A client program needs a way to determine which results are
+    parts of the same measurement and also know when a complete
+    measurement is received so it can process the sample. Use
+    the frame begin and end packets to separate groups that each
+    represent a single complete measurement.
+
+commit bb983c666a074d8e16c5f6c16e20870bc6c5d727
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Fri Dec 19 21:45:56 2014 +0200
+
+    Fix the channel indexes of the es51919 driver.
+    
+    Set the channel indexes to unique values instead of setting
+    them all zero.
+
+commit 3e2f04cf2637054846acaf5cf19f765e29e60fed
+Author: Rene Hopf <renehopf at mac.com>
+Date:   Tue Jan 6 02:31:52 2015 +0100
+
+    fix autogen.sh on OS X. This fixes bug #516.
+
+commit 15eea61a470617c2e8df066180821f532b3cbf88
+Author: Uffe Jakobsen <uffe at uffe.org>
+Date:   Wed Dec 31 00:32:44 2014 +0100
+
+    Fix compile error on FreeBSD
+
+commit b1a7ca3b605a8a7e7ce97dfc9eb211e9b3a5c918
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 3 18:03:50 2015 +0100
+
+    serial.c: Cosmetics/consistency/documentation fixes.
+
+commit 24287ea9e3dd0c6f7fc2299eaf725346b8c1fea2
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Sun Dec 7 00:56:22 2014 +0100
+
+    Add a public API to list available serial ports.
+
+commit 3fc66eda9f62ed5520e237ad6bad036d27bf96c2
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Jan 3 17:38:06 2015 +0100
+
+    Mention the Building#FAQ wiki page for common issues.
+
+commit cd5623ca86d517d5e9d02d195ad2f744e8e16614
+Author: Jiří Pinkava <j-pi at seznam.cz>
+Date:   Sat Nov 22 04:52:58 2014 +0100
+
+    fix numpy include path
+
+commit e26f5af3593b6d826a8ad7bed399935197454f2c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Dec 6 22:27:47 2014 +0100
+
+    README: Require SWIG >= 2.0.0.
+    
+    We use %shared_ptr for example, which was introduced in SWIG 2.0.0.
+
+commit 5e1b68c6e7567e0dcf7aff1307c3ffd4852cc15a
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Nov 25 22:49:39 2014 +0100
+
+    Only save enabled channels to a session file.
+    
+    This reverts bc96d5f08fe59ea7c51799b148946f5003c90927 which is not
+    needed anymore since we have a better fix for #410 and #495.
+
+commit a160a0c344f5bcb24eeb01c5698f3a50eaee7d79
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Nov 25 22:44:30 2014 +0100
+
+    session_file: Enable only the probes that are actually listed in metadata.
+    
+    This is a better fix for #410 and #495.
+
+commit 0d6478d7aa7f7b422b1c2b8186856093e93469c1
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Dec 3 09:09:31 2014 +0100
+
+    README: Improve and update the requirements list.
+
+commit 974fb0fffaffb0b2d1437ee280e4308a252741a0
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Nov 28 00:21:10 2014 +0100
+
+    saleae-logic16: Show libusb transfer status name instead of code.
+
+commit a11e10ec91d596a8d882dffe28b2abea65855ffb
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Nov 27 23:22:05 2014 +0100
+
+    saleae-logic16: Change two errors into warnings.
+    
+    Related to #466, convert two more sanity checks from errors into warnings.
+    This may allow more devices to work with libsigrok.
+
+commit 5cfcab6603414489181ded122b51579f7b546024
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Nov 27 01:00:40 2014 +0100
+
+    saleae-logic16: Clearer error message.
+
+commit e743a47d6d23050c72aa1276c5df4a45a6bc5357
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Nov 25 22:12:33 2014 +0100
+
+    beaglelogic: Add SR_CONF_CAPTURE_RATIO support.
+
+commit 5a971f66a37df7c4dbe7799b3c7fc7eb30055a61
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Nov 25 22:11:44 2014 +0100
+
+    saleae-logic16: Add SR_CONF_CAPTURE_RATIO support.
+
+commit 7bfcb25cf1aa4fe7ddc177292bcf2ee4d9b1446d
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Nov 25 12:28:36 2014 +0100
+
+    fx2lafw: Add SR_CONF_CAPTURE_RATIO support.
+
+commit fe5a735553470fe372ff1c12eb55398bd0f098b8
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Nov 25 12:23:34 2014 +0100
+
+    soft-trigger: Add support for pre-triggering.
+
+commit bc96d5f08fe59ea7c51799b148946f5003c90927
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Nov 24 02:21:15 2014 +0100
+
+    Always save all channels to a session file.
+    
+    This works around limitations of the current API that screw up saving
+    only enabled channels. See bug 410. And bug 495.
+
+commit e835e8080b6278137ea90f3b10e5d8bc51136a91
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Nov 24 01:11:17 2014 +0100
+
+    bindings: Session::set_trigger(): Fix segfault condition.
+    
+    sr_session_trigger_set(sess, NULL) is a valid thing to do, meaning that
+    any trigger shall be removed from the session.
+    
+    This closes bugs #491 and #496.
+
+commit 9f42e2e6beb6f09b137501bcf402b36a64dcd211
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Nov 24 00:50:11 2014 +0100
+
+    sr_session_trigger_{get,set}: Document, add error checks.
+
+commit c8965e545957209652f0bc79f88a1b39d8ff7ce2
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Nov 24 00:49:38 2014 +0100
+
+    Add a few unit tests for sr_session_trigger_{get,set}.
+
+commit b6085eb17901306b686a9269e5d2660d88561dc3
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Nov 23 21:09:37 2014 +0100
+
+    manson-hcs-3xxx: Fix incorrect SR_CONF_SCAN_OPTIONS handling.
+    
+    The SR_CONF_SCAN_OPTIONS key must be listable with or without sdi,
+    otherwise the device will not be detected by frontends.
+
+commit 84b448ee060c9ca40ca9324c0df9a49f4f4bd32b
+Author: Jiří Pinkava <j-pi at seznam.cz>
+Date:   Sun Nov 23 17:38:28 2014 +0100
+
+    Do not check for JDK headers if Java bindings are disabled.
+    
+    This removes an unnecessary build dependency on JDK and fixes
+    build troubles on systems where javac is present but JDK is not
+    installed and Java bindings are disabled by ./configure --disable-java.
+
+commit 2f004b4bc15c891b474adcba59a2224f009828af
+Author: Vincent Palatin <vpalatin at chromium.org>
+Date:   Mon Nov 10 08:17:07 2014 -0800
+
+    usb_get_port_path(): fix libusb error checking
+    
+    When libusb cannot access a device, libusb_get_port_numbers() will return
+    an error. Check the return code rather than doing invalid pointer
+    operations (out-of-bound read).
+    
+    Avoid segfaults at sigrok-cli startup on my setup where some USB devices are
+    not accessible and also make Valgrind happier.
+    
+    Signed-off-by: Vincent Palatin <vpalatin at chromium.org>
+
+commit 68ac991dbaa05ab91459419400b21b93f02d0e76
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Nov 23 17:47:58 2014 +0100
+
+    Publish config key capabilities on session driver.
+
+commit 90cefe0cc7d498ed8a8c7cdb1e95c16296ca5059
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Nov 20 03:11:14 2014 +0100
+
+    Add sr_rational_set() convenience function.
+
+commit cd3c4df35acdde1920fcaac92c0fd85591167e7a
+Author: Tim Hatch <tim at timhatch.com>
+Date:   Sun Nov 2 15:29:38 2014 -0800
+
+    saleae-logic16: Downgrade error during capture to a message.
+    
+    Some clone doesn't set this to the exact same value, and both bits in 0x48 are
+    marked as unknown at
+    http://sigrok.org/wiki/Saleae_Logic16/Firmware#FPGA_variables
+    
+    This fixes bug #466.
+
+commit 372c041bd8202d1ffa340bf00e065685f10a33f5
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Nov 23 13:10:32 2014 +0100
+
+    unit tests: Disable timeout for one of the test cases.
+    
+    This one can take a while, thus disable the timeout.
+
+commit 5fcc5909cc5f870e581fd367fd7b86b58c58e7c6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Nov 22 22:06:02 2014 +0100
+
+    python: Silence some warnings via -Wno-uninitialized.
+    
+    Silence some warnings when building the Python bindings:
+    
+      sigrok/core/classes_wrap.cpp: In function ‘PyObject* _wrap_new_OutputFormatMap(PyObject*, PyObject*)’:
+      sigrok/core/classes_wrap.cpp:5232:4: warning: ‘argv[0]’ may be used uninitialized in this function [-Wmaybe-uninitialized]
+          res = SWIG_ConvertPtr(obj,(void**)&p,swig::type_info<map_type>(),0);
+          ^
+      sigrok/core/classes_wrap.cpp:14383:13: note: ‘argv[0]’ was declared here
+         PyObject *argv[2];
+                   ^
+      sigrok/core/classes_wrap.cpp: In function ‘PyObject* _wrap_new_ChannelGroupMap(PyObject*, PyObject*)’:
+      sigrok/core/classes_wrap.cpp:5232:4: warning: ‘argv[0]’ may be used uninitialized in this function [-Wmaybe-uninitialized]
+          res = SWIG_ConvertPtr(obj,(void**)&p,swig::type_info<map_type>(),0);
+          ^
+      sigrok/core/classes_wrap.cpp:23356:13: note: ‘argv[0]’ was declared here
+         PyObject *argv[2];
+                   ^
+    
+    We add -Wno-uninitialized since the warnings are harmless and we really
+    don't care about them in the generated classes_wrap.cpp.
+    
+    This fixes parts of #417.
+
+commit 47af616fd787179bd10e29147dbabd28d09aea8c
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Nov 22 21:07:48 2014 +0100
+
+    Fix a bug causing one of the unit tests not being run.
+
+commit cc8be68f22b05fd00649b8c38854e37cd4805f57
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Nov 22 21:03:39 2014 +0100
+
+    trigger: Add some more error handling.
+    
+    All of these error conditions are checked via the unit tests.
+    
+    Also, add the following missing entries (analog trigger types):
+    
+     - SR_TRIGGER_RISING
+     - SR_TRIGGER_EDGE
+
+commit c8412d6c69d20e3a91616321c748265edc15a17e
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Nov 22 20:13:37 2014 +0100
+
+    Add a few unit tests for sr_trigger_*().
+
+commit a445f8aa080d18964ac45f352f6e61022b0a12bd
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Nov 22 19:32:28 2014 +0100
+
+    trigger: Add API documentation.
+
+commit 54ab1dcdc4d372765401bc2513f2692f488f4e1d
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Nov 22 16:53:02 2014 +0100
+
+    Add missing entries to sr_config_info_data[].
+    
+    Newly added keys:
+    
+     - SR_CONF_SAMPLE_INTERVAL
+     - SR_CONF_NUM_TIMEBASE
+     - SR_CONF_NUM_VDIV
+     - SR_CONF_CENTER_FREQUENCY
+     - SR_CONF_DEVICE_MODE
+     - SR_CONF_SCAN_OPTIONS
+     - SR_CONF_DEVICE_OPTIONS
+     - SR_CONF_DEVICE_MODE
+     - SR_CONF_TEST_MODE
+    
+    Also, keep the same ordering and grouping as in libsigrok.h.
+
+commit 91219afc75c9aa1d0c5e2da5c03343c1e43eb6df
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 21 19:02:10 2014 +0100
+
+    Use g_malloc0() consistently, simplify error handling.
+    
+    Use g_malloc0() for small allocations and assume they always
+    succeed. Simplify error handling in a few places accordingly.
+    
+    Don't always sanity-check parameters for non-public (SR_PRIV)
+    functions, we require the developers to invoke them correctly.
+    This allows further error handling simplifications.
+
+commit c368e6f3d248a73d69cd0c2c4a7c88a92def55e3
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 21 09:07:04 2014 +0100
+
+    Don't check sr_channel_new() return value (always succeeds).
+    
+    We now use g_malloc0() for the allocation and assume the allocation
+    will always succeed, thus sr_channel_new() will always return a
+    valid new channel.
+
+commit f57d8ffe66612a1fdc20ed09c222f8ea59bd84d4
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 21 02:26:24 2014 +0100
+
+    Consistently use g_malloc0() for allocating devc.
+    
+    We assume the allocation will always succeed, hence no need for
+    checking the returned value.
+
+commit aac29cc192ccf82b64e77b5e6b11b411da32deed
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 21 02:01:36 2014 +0100
+
+    Eliminate sr_dev_inst_new().
+
+commit a9b2283fd038a2a8c3e2dc1ede4fcc51d5e62c7c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Nov 20 03:08:55 2014 +0100
+
+    Fix invalid pointer dereference.
+
+commit cf0280fa1ba3532e1d3aa15b86323c9de7d5365c
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Wed Nov 19 23:54:01 2014 +0100
+
+    yokogawa-dlm: Publish driver options.
+
+commit 413f1944d166ae4f8f400b01a9467460620ccf8e
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Wed Nov 19 23:53:35 2014 +0100
+
+    motech-lps-30x: Publish driver options.
+
+commit f3ba3c119c3918dfa9cbde74afe21bd4c5609b44
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Wed Nov 19 23:53:10 2014 +0100
+
+    manson-hcs-3xxx: Publish driver options.
+
+commit 6ec3ef9b9255098240baec5e61a6a89d22960690
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Wed Nov 19 23:52:51 2014 +0100
+
+    hameg-hmo: Publish driver options.
+
+commit 8a58419d37bdaff02f1e5540ec76f3bffdaf951f
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Wed Nov 19 14:12:48 2014 +0100
+
+    Finish fixing broken sr_config_list() logic.
+
+commit a24da9a81358644265465325d12579cd8aa34ba5
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Nov 19 01:23:48 2014 +0000
+
+    Make sr_analog_*_to_string() functions allocate the necessary buffers.
+
+commit e07edc83d6311e36f2cbb5f3ae8a0f0edb526d18
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 18 23:43:38 2014 +0100
+
+    mic-985xx: Correctly report SR_CONF_THERMOMETER/_HYGROMETER.
+    
+    Some models only log temperature, others log temperature and humidity
+    (so they need different drvopts).
+
+commit 20a7cd07c9d1460e335fc9c3e39378ab708ef4bc
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 18 23:19:48 2014 +0100
+
+    mic-985xx: Drop unneeded #define.
+
+commit d6e1e6c4e15b57ac3b209c2fb9b151161e815c0b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 18 23:19:21 2014 +0100
+
+    mic-985xx: Publish driver options.
+
+commit 489c338884ee592635a9166907832ac39e8195a7
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 18 19:42:31 2014 +0100
+
+    center-3xx: Drop unneeded #define.
+
+commit 6685e9a6b3fe698d2c4e7d9725e2f8f9ffd83273
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 18 19:16:39 2014 +0100
+
+    center-3xx: Publish driver options.
+
+commit 5c868fef4383e2e8ec4b1d09608c1c0a0f158410
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 18:10:19 2014 +0100
+
+    Revert "victor-dmm: Set spec digits to 3, matching the display."
+    
+    This reverts commit 28b424349363a9002e7af7300a7f2941a8469004.
+
+commit 63ea6141b6ed7ae9d268892a1d76db5e7969ac38
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 17:41:36 2014 +0100
+
+    ols: Publish driver options.
+
+commit f9dada0b60fdde85c8f5605b136f2c37bc42be85
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 17:31:58 2014 +0100
+
+    agilent-dmm: Now really fix driver options.
+
+commit 42a47a9a4b25728436bf7fa8c798b4f8f05a789a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 16:50:47 2014 +0100
+
+    appa-55ii: Publish driver options.
+
+commit 5ecd9049e53112e75526c2887d3f57165b688c3f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 16:46:39 2014 +0100
+
+    hantek-dso: Fix driver options.
+
+commit 1f889afd619833ce93ef6afa066ba0797d9ec46c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 16:32:51 2014 +0100
+
+    atten-pps3xxx: Fix driver options.
+
+commit 023c73ae0550ac529b93cb6a5fc9f390448f42e1
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 16:42:52 2014 +0100
+
+    cem-dt-885x: Fix driver options.
+
+commit 9d9cf1c4b902c4556ba32d6f6c5566b48f3d1515
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 16:38:37 2014 +0100
+
+    scpi-pps: Fix driver options.
+
+commit 2ff11e50a58480705e688b36dcbc17ad0b5beb09
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 16:30:47 2014 +0100
+
+    asix-sigma: Fix driver options.
+
+commit 820c48f8c294d29660bfb2d081c575e138568909
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 15:51:38 2014 +0100
+
+    output/analog: Add option to restrict number of decimal digits printed.
+    
+    This is a feature restricted to the new analog struct.
+    
+    By default all the digits available in the encoding struct are printed.
+    The option "digits", when set to "spec", changes this to print the
+    number given in the spec struct.
+
+commit db6fa867a4bec0a1dc0742281df3ab7bd76787d2
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 15:46:59 2014 +0100
+
+    Add debug spew to all sr_config_(get|set|list) calls.
+
+commit cf3db38193bf1b2d36628ebe57a0204d77f68da3
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 15:46:24 2014 +0100
+
+    Fix broken sr_config_list() logic.
+
+commit 1e4a7cace29aaec9aad3a3845591ff45b8226a12
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 15:39:46 2014 +0100
+
+    demo: Fix driver options.
+
+commit ff6b76a14552e97b7eb07ff297a326e250134817
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 15:35:30 2014 +0100
+
+    fx2lafw: Publish driver options.
+
+commit ce4bd05256a891d45b0013c33cdd9f2597c3bc80
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 15:34:52 2014 +0100
+
+    agilent-dmm: Publish driver options.
+
+commit 5533392828cb08b2c6a008d6e2a52c7a01139f42
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 11:19:18 2014 +0100
+
+    victor-dmm: Publish driver options.
+
+commit a5892391b02689d7babd98d44e6537b34cef8e80
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 01:16:27 2014 +0100
+
+    Add analog helper sr_analog_unit_to_string().
+
+commit c2a25ebb8fbf992cdf9fee96d44f1c27f1f48f51
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 01:15:41 2014 +0100
+
+    Add analog helper sr_analog_float_to_string().
+
+commit 1954dfa96310e286bea6b4d55629ba08cee2f417
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 00:24:37 2014 +0100
+
+    Show SR_DF_ANALOG2 packets in debug output.
+
+commit 28b424349363a9002e7af7300a7f2941a8469004
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 18 00:24:08 2014 +0100
+
+    victor-dmm: Set spec digits to 3, matching the display.
+
+commit 4b4fdeea947950725b704a7af65b166e46521882
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Nov 17 23:08:49 2014 +0100
+
+    analog: Fix analog_to_float typos, and cleanup.
+    
+    This was adding 1 to every measurement being converted.
+
+commit 41caa31909882a34b88c8b8a844d555816b78453
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Fri Nov 14 00:50:46 2014 +0100
+
+    Add a sr_analog_init() API to initialize sr_datafeed_analog2 struct.
+    
+    It fills the fields with reasonable default values that should suit
+    most of the drivers. Drivers are obviously free to override the fields
+    they want after initializing.
+
+commit 62f155f0c784509dbec98fc7c80b03284a271237
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Nov 17 11:38:37 2014 +0100
+
+    demo: Fix analog output at low samplerate.
+
+commit 3772c04990d1f81ce3946b48034f441adaa13271
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Nov 17 11:40:22 2014 +0100
+
+    demo: Fix square pattern output shorter than other patterns.
+
+commit c6dde8125ad8a0bf6845362e5782ce19f26d83b9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Nov 17 13:20:05 2014 +0100
+
+    Check driver capabilities before sr_config_get/set/list.
+
+commit baa0c2ae677940345d2ec62b0b6fdf17e7d2d09d
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Nov 15 00:41:10 2014 +0100
+
+    autogen.sh: Also warn if AX_CXX_COMPILE_STDCXX_11 is not available.
+    
+    This fixes an error message when the file is not installed at all:
+    
+      ./autogen.sh: 67: [: Illegal number:
+    
+    Turn the AX_CXX_COMPILE_STDCXX_11 errors into warnings though, since
+    it is (and should be) possible to build libsigrok (just the C library)
+    just fine, even without that macro or with an older version of it.
+
+commit ff50ad701e6f945cec972465a35c285dbaddd329
+Author: Jens Steinhauser <jens.steinhauser at gmail.com>
+Date:   Fri Nov 14 01:43:25 2014 +0100
+
+    Check the version of the AX_CXX_COMPILE_STDCXX_11 macro.
+
+commit adfba7368ac297bba20b0afbc7d7322309508b30
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Nov 14 20:24:43 2014 +0100
+
+    Refactor scan options check.
+
+commit b8721d7cf0ee95c51fb6a3d357f371b314b5aeab
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 14 20:01:12 2014 +0100
+
+    bindings: Fix out-of-tree build.
+    
+    This closes #473.
+
+commit b71356d63104635116e556cd51daa0904e28fc99
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Nov 14 16:47:46 2014 +0100
+
+    bindings: Re-enable Java, but ignore create_analog_packet().
+    
+    This will need some fixing.
+
+commit 071151b578821a5019d718ad03db81b2cef3245e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Nov 14 12:22:54 2014 +0100
+
+    sr_driver_scan: Enforce options passed in by client.
+
+commit 4b664cd6ce7792b58190cdcc92347ded44a8a46a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Nov 14 11:37:11 2014 +0100
+
+    demo: Use allocation for model string.
+
+commit 676877f6cee574f2a32805b946e56710db502264
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Nov 12 23:34:20 2014 +0100
+
+    mic-985xx: Convert to use SR_DF_ANALOG2.
+
+commit a84a26d98ace158fa9e0b0e6d385928ceb91221a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Nov 12 16:31:30 2014 +0100
+
+    victor-dmm: Convert to use SR_DF_ANALOG2.
+
+commit e02e9e6a1c8002e461f27a831b96c370f363b5d5
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Nov 12 17:05:13 2014 +0100
+
+    output/analog: Add SR_DF_ANALOG2 support.
+
+commit fb019a0e4d28fe381ce8142f3ec00a6e8731e094
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Nov 12 17:05:13 2014 +0100
+
+    Add sr_analog_to_float().
+
+commit d2e0f5853930e805bb70ee7e2c59144af59f9013
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Nov 12 14:46:51 2014 +0100
+
+    Add SR_DF_ANALOG2 and related structs.
+    
+    New structs:
+     - sr_rational
+     - sr_datafeed_analog2
+     - sr_analog_encoding
+     - sr_analog_meaning
+     - sr_analog_spec
+
+commit d2a929ab85d6adb9ea64feb785abf21caebea447
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Nov 13 20:38:56 2014 +0000
+
+    bindings: Fix enums.py compatibility with Python 3.
+
+commit 624d16100e0c850e2c28e10638a9e5a37cd938f1
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Nov 13 19:15:37 2014 +0100
+
+    bindings: Add Session::context().
+
+commit 57621c6d60b1eb31d0a3071fc362547c88c5d395
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Thu Nov 13 18:40:44 2014 +0100
+
+    configure.ac: Temporarily disable Java bindings.
+    
+    Those need a bugfix to make them build again:
+    
+      bindings/java/org/sigrok/core/classes/Context.java:92: error: method Context_create_analog_packet in class classesJNI cannot be applied to given types;
+            long cPtr = classesJNI.Context_create_analog_packet(swigCPtr, this, ChannelVector.getCPtr(tempchannels), SWIGTYPE_p_float.getCPtr(data_pointer), num_samples, Quantity.getCPtr(mq), mq, Unit.getCPtr(unit), unit, QuantityFlagVector.getCPtr(mqflags), mqflags);
+                                  ^
+        required: long,Context,Vector<Channel>,long,long,long,Quantity,long,Unit,long,QuantityFlagVector
+        found: long,Context,long,long,long,long,Quantity,long,Unit,long,QuantityFlagVector
+        reason: actual argument long cannot be converted to Vector<Channel> by method invocation conversion
+      1 error
+      Makefile:3352: recipe for target 'bindings/java/sigrok-core.jar' failed
+
+commit 304be4a77186f4524e3894af8279690f28ffd98f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Nov 12 12:37:22 2014 +0000
+
+    bindings: Add packet constructors.
+
+commit b2db9f3bbeaa26bb63f8adfabf47ff7e068b1952
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Nov 12 11:08:22 2014 +0000
+
+    bindings: Add QuantityFlag::mask_from_flags() method.
+
+commit 9fa5b426ec7c0a77eb9401f3dd10ad463a8d1ac7
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Nov 12 02:23:02 2014 +0000
+
+    bindings: Add UserDevice wrapping.
+
+commit 0af636bed97c174bea46e61e961eaa1b0b162e0f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Nov 12 02:06:15 2014 +0100
+
+    Change sr_dev_inst_new() to take no parameters.
+    
+    Change all callers to set the fields manually as needed.
+
+commit c7e455625807d31fcaf95f36a23f1afeba033e1f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Nov 12 00:04:28 2014 +0100
+
+    HACKING: Document the new malloc related guidelines.
+
+commit 487c23fc998ec94e7d37d0bd40c5dfb7a41b651b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 23:52:53 2014 +0100
+
+    HACKING: Minor updates.
+
+commit ed6b4c4747ed98374feb59ac5758a24bd4d9a63f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Nov 12 01:00:17 2014 +0000
+
+    python: Wrap Analog::data() as a NumPy array.
+
+commit b20635771c249f9548af414f21a92b296e284475
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 23:54:39 2014 +0000
+
+    bindings: Expose Analog::channels() as an attribute.
+
+commit ea22dc108b1da4c1f43c5cb2b8433a44fad726bf
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 23:44:26 2014 +0000
+
+    python: Return correct PacketPayload subclasses from Packet.payload()
+
+commit 75fb30365e1caebb402f0eb4331bdde0f27f3120
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 21:04:44 2014 +0000
+
+    python: Fix error handling for callbacks.
+
+commit f0c0dab5a9a463f87ca723cd4a4d6241311e0a0e
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 20:11:32 2014 +0000
+
+    python: Implement equality checks for EnumValue derived classes.
+    
+    Fixes bug #443.
+
+commit 7a36ceacb96c4fdbeea164d7368b4eef1ad545f9
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 18:26:50 2014 +0000
+
+    bindings: Support per-language extensions to EnumValue wrappers.
+
+commit 444d6a3975787583494fa91cbb20c26d0ac858b5
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 15:29:35 2014 +0000
+
+    python: Fix mapping of vector & map attributes to Python types.
+    
+    Fixes bug #382.
+
+commit 062430a2fbd74a7ff153b77d92523daae9ec18a7
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 14:50:55 2014 +0000
+
+    bindings: Use new %attributevector and %attributemap macros.
+
+commit e480df0c4593d80df617b1a3da0065c0fb91a5ac
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 01:24:19 2014 +0000
+
+    bindings: Expose ConfigKey::identifier as an attribute.
+
+commit 189461b25193d099b8a1087d8fda97958ae31ed5
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Nov 11 01:18:58 2014 +0000
+
+    bindings: Expose EnumValue::id and EnumValue::name as attributes.
+
+commit fe4096fde6edf35d93bca8293172cf261e33ad8a
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Oct 29 14:36:45 2014 +0000
+
+    bindings: Wrap EnumValue base class.
+
+commit 0bc1a7613ac2ad4c5b1148b9714791769a2b834c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Oct 29 14:33:46 2014 +0000
+
+    bindings: Rename ConfigKey::get(string) to get_by_identifier().
+    
+    The polymorphism with get(int) causes problems here when a char * is passed.
+
+commit 9d229ecb9e301921284c6264ffc1d956572873df
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Oct 29 14:31:31 2014 +0000
+
+    cxx: Implement more of EnumValue in template.
+
+commit 3250d8c7e05f2d6a3ffbdf2499af8a13fa99fe39
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Nov 11 23:23:09 2014 +0100
+
+    output: Add srzip, the session file format.
+    
+    The 'filename' option is required: this module creates the zip file
+    itself, and never actually outputs anything back to the calling frontend.
+
+commit ccd3f5e5eb05741001fcaa704c1720de8f0f66d3
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 22:45:20 2014 +0100
+
+    unit tests: Add a test case for sr_dev_inst_channel_add().
+
+commit 6b1adfaa86de2195498a0078b0fb50fd76f5aa38
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 21:59:23 2014 +0100
+
+    unit tests: Add a test case for sr_dev_inst_user_new().
+
+commit e705ce3bf6203b03efd66390b02c2352c62229bc
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 21:58:49 2014 +0100
+
+    Add sr_dev_inst_user_new(), sr_dev_inst_channel_add().
+
+commit 924866d48d0cf1638df3d3ba254f037baa2a661a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 21:28:16 2014 +0100
+
+    uni tests: Use sr_dev_inst_channels_get() to fix the build.
+
+commit 80fe524740f28f44111d9f98bbb0e44294bdf3d2
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 16:55:37 2014 +0100
+
+    bindings: Use getters now that 'struct sr_dev_inst' is opaque.
+
+commit 2f5f97056a57d0cdf6fa0e6fc51b1a406d33452f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 12:44:37 2014 +0100
+
+    Constify the sdi parameter of all sr_dev_inst_*() getters.
+
+commit 96727ef016b9fd26a37691008243182c5a52cb60
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 12:24:08 2014 +0100
+
+    Make 'struct sr_dev_inst' opaque.
+
+commit e437da2b86ba7604fc315996dd89014dbb94d101
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Nov 11 11:51:53 2014 +0100
+
+    Add sr_dev_inst_channels_get() and sr_dev_inst_channel_groups_get().
+
+commit 3f2cd87f36fa8bb6fddc4ace7a6c9571e7c2309d
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Nov 9 20:04:09 2014 +0100
+
+    session_driver.c: Allow querying of SR_CONF_CAPTURE_UNITSIZE.
+
+commit 6508992d04bf972ae98b8c7e97201c5164501687
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Nov 2 16:40:40 2014 +0100
+
+    Brymen BM25x: Drop unneeded _ser/_SER suffix.
+    
+    There's only one cable for this DMM, thus there's no need for a
+    _ser/_SER suffix or for specifying the cable name.
+
+commit bce75f947dfc35eb67caa90f3c8dc9f20bdc36cb
+Author: Uffe Jakobsen <uffe at uffe.org>
+Date:   Sat Nov 1 19:35:30 2014 +0100
+
+    ols: Fix a serial port related issue on FreeBSD.
+    
+    Add sp_drain() to ensure bytes have actually been transmitted
+    over the wire.
+    
+    This fixes bug #414.
+
+commit 7aebe22d107df4548700bef900be66971658fcac
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Nov 1 13:18:09 2014 +0100
+
+    Only sr_dev_inst_free() should free channel groups.
+
+commit aab4b8cb70e08369760383d9b9302085db298a44
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Oct 29 22:46:57 2014 +0100
+
+    fx2lafw: Fix wide (16bit) sampling case.
+    
+    This fixes bug #373.
+
+commit e3594306a98cdb158164d091fec57291b9a4a208
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Oct 29 13:26:57 2014 +0100
+
+    zeroplus-logic-cube: Add support for AKIP-9101.
+    
+    Thanks to Nikita Nazarenko for the patch.
+
+commit 9c6a2913fdf11857774418b18665f8f684762a31
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Oct 26 20:51:21 2014 +0100
+
+    Make sr_dev_inst_connid_get() available without libusb
+
+commit 933defaa03abe4f3f01eff8067c3f0060914b050
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Oct 27 17:03:02 2014 +0100
+
+    hantek-dso: Fix driver/global/channel group config keys.
+    
+    This was way behind and did not yet support channel groups.
+
+commit 6fad08e6abfa4e5ee708f584e530c6b611a65cdb
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Oct 27 16:59:01 2014 +0100
+
+    Change SR_CONF_FILTER key to a boolean type.
+    
+    This was an ill-defined string before, now it's simply something
+    you turn on or off on a channel.
+
+commit e7ba5a994b24bec2bb59e0d01f3649154e7bb32c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Oct 27 00:46:19 2014 +0100
+
+    asix-sigma: Publish driver options.
+
+commit a258204e00715c44b0d013ee3141ce890c8ea71c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Oct 27 00:37:04 2014 +0100
+
+    scpi-pps: Publish driver options.
+
+commit 390795c0999ae3a41b97f9a8e2c154c81e6d064e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Oct 26 23:54:55 2014 +0100
+
+    demo: Rearrange driver and device options.
+
+commit d7125bfa1ea1bc06c9df74d1aff6c4d50c62080c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Oct 26 23:36:10 2014 +0100
+
+    cem-dt-885x: Publish driver options.
+
+commit d6fa8ace94ac0c17855c3433cca038a150689495
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Oct 26 23:33:46 2014 +0100
+
+    atten-pps3xxx: Publish driver options.
+
+commit a700a3a4bfef6c92a2085ae5e611fabfb639878a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Oct 27 16:58:33 2014 +0100
+
+    More robust searching for config keys.
+
+commit 51b1b95edb5768a202a0f3a05847282ea9ea1897
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Oct 26 23:32:29 2014 +0100
+
+    Add config info for device type and limit config keys.
+
+commit 8769478c0749a1a74a87a5476d6bfb9ef7e8f93f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Oct 25 17:14:30 2014 +0200
+
+    Add support for the UNI-T UT71x series (aka Voltcraft VC-920/940/960).
+    
+    Tested on the Voltcraft VC-920 and VC-940 (both UT-D02 and UT-D04
+    cables), but it should work for all devices in this series without
+    any changes.
+
+commit 626027df0fef0e9bebc5744bb11f5fcd1f396c10
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sat Oct 25 11:36:58 2014 +0200
+
+    Add UNI-T UT71x DMM parser.
+
+commit ce48b174da534120e7b725cd664743a6e9a75956
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 23 00:22:07 2014 +0200
+
+    sr_dev_inst_connid_get() requires libusb.
+
+commit cf49d66bc6dc2903ac1380464fe5f3004eaf508d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Oct 22 22:19:41 2014 +0200
+
+    hantek-dso: Properly zero out MQ flags.
+
+commit 61b0292217465ea50ebbf8f9c960330d8c1cbacd
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Oct 22 22:18:52 2014 +0200
+
+    Accept subtype of expected GVariant values.
+
+commit a42a39ac378534782d44f166b879e84cefa66ed6
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Mon Oct 20 23:03:20 2014 +0300
+
+    Export LCR meter 'auto' bits as config keys instead of mqflags.
+    
+    The automatic selections of the measured quantity and equivalent circuit
+    model are more part of the configuration of the meter than attributes
+    of the measurement result. To reflect this, model them as config keys
+    instead of mqflags. This allows a driver that supports remote control to
+    implement 'set' method for them and has the additional benefit of saveing
+    two flag bits.
+
+commit 160691b9008b2e13f981f6b8876cbbeb247c773d
+Author: Jens Steinhauser <jens.steinhauser at gmail.com>
+Date:   Sun Oct 19 19:53:08 2014 +0200
+
+    input/csv: Skip header line.
+
+commit c3eadb0760121c2ab3c34aedba92d477d222dce9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Oct 18 23:20:47 2014 +0200
+
+    scpi-pps: Add support for Fluke/Philips PM2800 series.
+
+commit c1d56d20131a0f34d819721f7fd9c4952482557f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Oct 18 23:18:48 2014 +0200
+
+    scpi-pps: Move non-standard data type processing to SCPI parser.
+
+commit 4a471029c230a7e2e0b15528d22bf30fb7cf2472
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Oct 18 23:12:56 2014 +0200
+
+    scpi-pps: Add channel probe facility to scan.
+    
+    If the number and specs of the device's channels are not static, i.e.
+    need to be probed, this facility is needed.
+    
+    Initially this will be used for the Philips PM2800 series, where only
+    the model returned by *IDN? is needed. However this could also be used
+    to do actual discovery with vendor-specific SCPI commands.
+
+commit 99d090d8e5a7d52ceb3db58455d53cf55472c6f9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Oct 18 23:10:46 2014 +0200
+
+    Fix typos.
+
+commit b1b1944e19f8dd4837293ecd7e2d01daa429068c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Oct 18 23:09:47 2014 +0200
+
+    Free channel group private storage when clearing device instance.
+
+commit c7c8994c204cc0bc86d07fe671b6c469ba5d6b50
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Mon Oct 13 18:56:16 2014 +0300
+
+    Rename "SERIAL" -> "SERIES".
+    
+    Use the more correct term "SERIES" for the corresponding equivalent
+    circuit model.
+
+commit 34d117afb4e07bc060426e6843ca1341c2431b49
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Mon Oct 13 18:51:32 2014 +0300
+
+    Allow building libsigrok without libserialport.
+    
+    Since the DER EE DE-5000 driver is currently the only user of
+    the es51919 module and it depends on the libserialport, compile
+    the module only if the DER EE DE-5000 driver is enabled. This
+    allows libsigrok to be built without libserialport again.
+
+commit e8cbb22314f8a1c4aafd55b582281f54d4d35c07
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 22:44:31 2014 +0200
+
+    Auto-set sdi->connection_id for serial devices in the getter
+
+commit d1314831e473056db017c742011d620648f152ad
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 22:43:05 2014 +0200
+
+    yokogawa-dlm: Populate sdi->serial_num and sdi->version
+
+commit b3fccc851b1bbeb6955ddec9007cea72a3e9e3fd
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 22:42:50 2014 +0200
+
+    rigol-ds: Populate sdi->serial_num
+
+commit b33db61c48dd8ac2f6e7e3262da2d9399bd4bd53
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 22:42:38 2014 +0200
+
+    hameg-hmo: Populate sdi->serial_num
+
+commit d08667c53b52ff7015de883dfc45906323eb2aa5
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 22:42:19 2014 +0200
+
+    scpi-pps: Populate sdi->serial_num and fix hw_info mem leak
+
+commit b2c02b0747a413be1ebfb26a15ce692ad6beb49e
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 22:41:57 2014 +0200
+
+    scpi: Populate sdi->connection_id
+
+commit 0699ccdd92229a82f947993ec6c15adbcf43e867
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 22:40:58 2014 +0200
+
+    sysclk-lwla: Let the sdi getter populate sdi->connection_id
+    
+    This fixes bug #441.
+
+commit c962d07cba8d5ee4f76ffb5fb360e028770dc782
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 22:15:54 2014 +0200
+
+    ikalogic-scanalogic2: Let the sdi getter popuplage sdi->connection_id
+    
+    This fixes bug #440.
+
+commit 0157fdce99dfb867b68a0799cfa3d5586042cb1f
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Thu Oct 16 20:17:04 2014 +0200
+
+    Provide getters for sr_dev_inst member access
+
+commit 984e4b0db88cddc880f5a76bc97ebbba7cb7c39d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Oct 17 03:00:55 2014 +0200
+
+    scpi-pps: Start acquisition on the first enabled channel.
+
+commit 624503ae90a3583edd34fc28a62a037640984e36
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Oct 17 03:00:05 2014 +0200
+
+    scpi-pps: Optimize channel selection.
+    
+    This avoids sending a channel selection command to the device if
+    the channel we're working on is already on the same underlying
+    output channel.
+
+commit ee2860ee110367d137559e0aaf91b2859e045968
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 16 15:24:27 2014 +0200
+
+    scpi-pps: Disable beeper during session.
+    
+    At least the Rigol DP800 series trigger the beeper when changing
+    channels remotely. Which gets rather annoying when doing acquisition
+    on three channels as fast as you can.
+
+commit bf48ccebeed885652312188770ec893f7425cb43
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 16 14:55:56 2014 +0200
+
+    scpi-pps: Properly clean up acquisition session.
+
+commit 60475cd78820ede32383cc838326d691bb364b07
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 16 13:23:21 2014 +0200
+
+    scpi-pps: Use only standard SCPI for Rigol DP800 series.
+
+commit ae431bc8d607ee381052eed1727f644fbc1fd89d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 16 13:22:51 2014 +0200
+
+    Fix short names for SR_CONF_OUTPUT_VOLTAGE_TARGET/_CURRENT_LIMIT.
+
+commit d3a401c15be9a4e798678fe557b655977d960b72
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 12 17:57:27 2014 +0100
+
+    bindings: Remove Device::description().
+
+commit 1411f7d8e9fa5d90d8140fc51eb44562dd626b7c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 12 17:53:10 2014 +0100
+
+    bindings: Add Session::filename() and corresponding SWIG attribute.
+
+commit 73a1eb017be9369597f404e61ed8b79767a58383
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 12 17:48:44 2014 +0100
+
+    bindings: Add accessor to obtain parent object.
+
+commit f591826cc739acdcb1f145f4e12a1b995b114c93
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Oct 10 19:37:47 2014 +0100
+
+    bindings: add SWIG attribute for Packet::type.
+
+commit f36f7d02826a4240c5bb56ccc0dfd7d689093765
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 12 16:18:30 2014 +0100
+
+    C++: Don't clear Session::_owned_devices() in Session::remove_devices().
+    
+    Owned devices are freed only when the session is destroyed.
+
+commit 1de3ccede95a7b3535b67c6ade510715fe85d4d3
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 12 15:56:36 2014 +0100
+
+    Track sdis created by sr_session_load(), and free in sr_session_destroy().
+
+commit c0e3ba4b79fd2b3dfcd4ec95d5b3d79ead0e10c4
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Oct 12 15:39:36 2014 +0100
+
+    Correct description of sr_session.devs.
+
+commit ca95e90fb37f127b3d58d90a8b40ce1ca85641d2
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Oct 15 12:03:00 2014 +0200
+
+    Use SR_CONF_OUTPUT_VOLTAGE_TARGET and _CURRENT_LIMIT.
+    
+    These describe them better; the two are fundamentally different
+    things to set.
+
+commit 3982b93840d99c9176185b0054f9dec70f776395
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Oct 13 16:23:48 2014 +0200
+
+    configure.ac: Drop ols Windows exception, should be obsolete.
+    
+    (see #205)
+
+commit 53a81803e416946c2d85edfc15b50d5a1926b1a9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Oct 10 16:00:33 2014 +0200
+
+    scpi-pps: Split boolean set options into enable/disable.
+    
+    This makes it easier to support devices that need something else
+    than "ON" or "OFF".
+
+commit b9a348f56c5999f263a2a30c42c49ec8547a2eef
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Oct 13 11:39:15 2014 +0200
+
+    Rename SR_CONF_LCR_METER to SR_CONF_LCRMETER.
+    
+    (to be consistent with some other key names)
+
+commit 3ea46116dea1636222ec528c2708d99adff1b4bc
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Oct 13 01:06:09 2014 +0200
+
+    libsigrok-internal.h: Fix some outdated comments.
+
+commit b50891952d22d1c5012a99ad643ee3dd58495420
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:35 2014 +0300
+
+    Add driver for DER EE DE-5000 LCR meter.
+    
+    Add a driver for the DER EE DE-5000 LCR meter. This meter is based on
+    the Cyrustek ES51919/ES51920 chipset and communicates with the host
+    computer via an optional connectivity kit.
+    
+    The kit uses an optoisolated unidirectional link to connect to the
+    meter and an USB cable on the host side. Internally the connection is
+    using the FTDI FT232R USB UART chip i.e. from the host computer point
+    of view the meter is connected into an RS-232 serial port.
+    
+    This driver implements just a thin shim layer for registering the
+    driver and uses the es51919 module for all the actual work.
+
+commit 6bcb3ee8763bbfdb94ff6f764a2fa91a7468e37f
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:34 2014 +0300
+
+    Add protocol decoder for Cyrustek ES51919 LCR meter chip.
+    
+    Add a protocol decoder for the Cyrustek ES51919 LCR meter chip.
+    This chipset (together with ES51920 front-end) is supposedly used
+    by multiple different portable LCR meters including at least
+    DER EE DE-5000, Yihua V&A VA520, Mastech MS5308, Uni-T UT612,
+    CEM DT-9935 and various OEM rebadges of them.
+    
+    The communication protocol seems to be implemented on the Cyrustek
+    chip itself so all the different models are expected to use the
+    same protocol if they implement a host connection. Unfortunately
+    the protocol is not available in the public documentation of the
+    chipset, so this implementation is based on reverse engineering it
+    from traffic captures.
+    
+    The actual connection between the meter and the host computer may be
+    different from meter to meter even when based on the same chip. This
+    module implements a decoder for the protocol and some common helper
+    functions for interfacing with the meter via an RS-232 serial port.
+
+commit 02c7c482a60f3209adb7c44f4ff52ac087a0aeb0
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:33 2014 +0300
+
+    Add pretty-printer for new units and flags.
+    
+    Add support for printing henries and degrees as well as the new
+    reference and auto flags.
+
+commit 0f5b241ec91b3ca89d5fb2da2327c624a55c58c9
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:32 2014 +0300
+
+    Add config key for output frequency.
+    
+    Add a config key for getting/setting the output frequency. The value
+    will be an uint64 and measured in hertz.
+
+commit 0ffce50d449bb9d6ea79e20409f03ba108be344f
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:31 2014 +0300
+
+    Add config key for LCR meter type.
+    
+    Add a config key that can be used to indicate that the device is
+    an LCR meter.
+
+commit ae27f281655a4d5720be9024f87ceba1531da60d
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:30 2014 +0300
+
+    Add two new 'auto' flags.
+    
+    Add flags for indicating that the meter has selected the measured
+    quantitiy and/or the used measurement model automatically. These
+    are similar to the existing auto-range flag.
+
+commit 23601f2c7eb332311a4343417dcafab4565ad9bb
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:29 2014 +0300
+
+    Add quantity and flag for difference measurements.
+    
+    Add 'SR_MQ_DIFFERENCE' quantity for reporting relative difference
+    between the current measurement and the reference value. The value
+    of this quantity will normally be reported in percents. Add also
+    the flag 'SR_MQFLAG_REFERENCE' for indicating that the reported
+    value is not the current measurement but the reference value instead.
+
+commit 87d8124577099b2c4cbad1276985a9ed9cdf4d4f
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:28 2014 +0300
+
+    Add measured quantities for LCR meters.
+    
+    Add parallel and serial model inductance, capacitance and resistance
+    for LCR meter measurements. Add also secondary quantities many LCR
+    meters calculate, namely dissipation factor, quality factor and phase
+    angle.
+
+commit 01789adc72da9952285d43414bb59f17adc31e2c
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Sat Oct 11 15:47:27 2014 +0300
+
+    Add two new units.
+    
+    Add degrees and henrys to the list of supported units. Degree is
+    an unit of plane angle where one degree is 1/360th of a full circle.
+    Henry is the SI unit of inductance.
+
+commit 8b4f0d6a1106545495b665906747e2edddf239aa
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Oct 12 19:59:41 2014 +0200
+
+    README: Bump glib version to 2.34.
+
+commit 76bc5f6376104e8b65322011b67aafbd45348fe2
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Oct 7 00:02:10 2014 +0200
+
+    usb: Simplify usb_get_port_path() and fix writing into a const char *.
+
+commit d099d8802111c775465de18cb69c746c342d9ac1
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 9 23:42:24 2014 +0200
+
+    Fix sr_dev_has_option().
+    
+    This wasn't taking the SR_CONF_GET/_SET/_LIST flags into account.
+    
+    Thanks to Janne Huttunen for spotting this.
+
+commit 818df9f8ec3d035eb52292286469ba03521a51b3
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Oct 8 23:14:01 2014 +0200
+
+    build: Require glib 2.34.
+
+commit f4d3a4fb9ae62b4764cbb8181ab9bef95e7b6348
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Oct 8 03:00:47 2014 +0200
+
+    ols: Fix serial port timeout.
+
+commit 9e6d9bee2a6771aab111ad95140caf520310b109
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Oct 8 02:23:20 2014 +0200
+
+    colead-slm: Fix serial port timeout.
+
+commit 174bf146e5973cb7f11892c54972bc99e4a74eae
+Author: Jens Steinhauser <jens.steinhauser at gmail.com>
+Date:   Tue Oct 7 14:01:34 2014 +0200
+
+    bindings: Better error handling in enumeration get() function.
+    
+    Prior to this patch a call to get() with an invalid enumeration value
+    would raise an exception that would, for example, terminate the python
+    interpreter, whereas now the exception is handled and translated into a
+    proper python exception.
+
+commit 2eb1612d463e9232af437d9a6c1b3897da2367e9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Oct 6 12:12:13 2014 +0200
+
+    atten-pps3xxx: Use serial_timeout().
+
+commit c5cfc735e50adc53e073355e5521b1c79266441e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Oct 6 12:10:25 2014 +0200
+
+    serial: Add serial_timeout().
+    
+    This calculates a proper timeout value for blocking writes on the
+    given serial port, for the given number of bytes. Timeout is based
+    on a fixed 10ms OS overhead, baud rate, data bits and stop bits.
+
+commit 945cfd4fdd3d2aa2758e1957c7c4c1440badcf8b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Oct 5 12:29:28 2014 +0200
+
+    atten-pps3xxx: Fix serial port timeout.
+
+commit 204014007f30a3980e6abb35b91bf654d190d81f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Oct 4 10:37:07 2014 +0200
+
+    fluke-dmm: Fix blocking serial write timeout.
+
+commit 95779b43b882bff8ce36828f812482d69c0cfa0a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Oct 4 02:30:27 2014 +0200
+
+    agilent-dmm: Fix blocking serial write timeout.
+
+commit 4ab01c3564ddec604d0b6a9daf76fcba277da287
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Oct 4 02:24:41 2014 +0200
+
+    input/csv: Fix size_t printing.
+
+commit 026d6b2ff616ca03a42286e7d7a9a33bce9bf053
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Oct 3 20:11:47 2014 +0200
+
+    unit tests: Fix build due to recent changes.
+
+commit 9092e66888ffd9dad76f4c491f5ad080cfd3e5c9
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 28 14:30:20 2014 +0100
+
+    Add a configurable read timeout to blocking SCPI reads, default 1s.
+
+commit 57d355a7bd67ef6b15d4dbbded82a8ca98962511
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Oct 3 13:39:06 2014 +0200
+
+    Add some Agilent USBTMC PIDs.
+
+commit eead2782427ee5da9b793527f9484ac827a7bec5
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 28 13:05:33 2014 +0100
+
+    Add a timeout parameter to blocking serial calls.
+    
+    Set this new parameter to 0 (no timeout) at every call site. This is
+    consistent with previous behaviour, so cannot cause any regressions.
+    
+    Waiting forever for a serial operation is clearly always wrong. Without
+    specific knowledge of each device and driver however, I can't choose
+    appropriate timeouts for each call. The maintainers of these drivers
+    will need to do so, and also add appropriate handling of timeouts.
+    
+    When this commit is merged, a bug should be entered for each driver
+    that is touched by it.
+
+commit 7ce59a31334cdb9b12d9b1c166b542acf1899e00
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 16:01:27 2014 +0200
+
+    Avoid serial_write_blocking() warnings.
+
+commit 081c214eace0c9088cbcbd9a3d448f6fac5e98f4
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 28 12:54:56 2014 +0100
+
+    Fix similar broken error handling on several serial calls.
+
+commit 7f22cd9554884f328234189ef368cda65a8fc3e0
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 15:06:36 2014 +0200
+
+    kecheng-kc-330b: Fix missing time/frequency weighting.
+
+commit 8219214ff077db3f72a2996da417c0bea3563987
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 15:03:18 2014 +0200
+
+    hantek-dso: Code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 385cb66058e534b57ca473449dfe57f43e246c6a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 15:02:14 2014 +0200
+
+    demo: Code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit f4d0020ec693e41513add7d4f186cdaf9dd3488d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 15:00:37 2014 +0200
+
+    fluke-dmm: Code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 65c8d48f1418700dae72c8b6e39eb0d77b1ea17d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 14:58:57 2014 +0200
+
+    chronovu-la8: Code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 382cb19f29f9fe973199ebaf906264bd2eee5818
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 14:53:20 2014 +0200
+
+    asix-sigma: Trigger code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 90486ba835ff82c316d63b3d3b69581615d9245a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 14:11:35 2014 +0200
+
+    agilent-dmm: Code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit f3616a0857c558fd0ff566ca6dc5730599be9194
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 14:07:31 2014 +0200
+
+    scpi/vxc: Avoid NULL dereference.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 18078d0532d3daa013ad712356d93c28e1ae64fd
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 13:59:44 2014 +0200
+
+    input/csv: Code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 74e1f6f53c47889ddfa67a35703d6f13e54a1df4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 13:55:37 2014 +0200
+
+    input/chronovu_la8: Code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 577a9fe4235d5b090aaec562ea8013c79712da7e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 13:55:21 2014 +0200
+
+    input/binary: Code cleanup.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 640982114329b28aaca3390913037eddd8c6d550
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 13:51:19 2014 +0200
+
+    input: Avoid NULL dereference.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit 19e43ab21badac678f4a452e0dd629a0f7917d16
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 12:57:19 2014 +0200
+
+    sr_parse_sizestring: Deal with invalid strings better.
+    
+    This cleans up a warning generated by clang's static analyzer.
+
+commit d386a52f64e39c9165135aea6ecc613dd665dec7
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Oct 2 11:32:52 2014 +0200
+
+    Clean up sr_session_load().
+    
+    This cleans up some warnings generated by clang's static analyzer.
+    The function now also returns SR_ERR to signify the specified filename
+    does not point to a valid session file.
+    
+    Other SR_ERR_* returns indicate a session file was found, but loading
+    failed.
+
+commit 98d39b919abff9412c5de9d02a926680d572fb83
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 30 17:32:09 2014 +0100
+
+    Return sensible Device::description() for session and input devices.
+
+commit ca4e307a934ba395bdc3e2a48b83835d05a047c2
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 30 16:05:27 2014 +0100
+
+    C++: Fix management of SessionDevice objects.
+
+commit 584f76a78a9d687dcf396f0c620fe3093b38e70a
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 30 13:19:27 2014 +0100
+
+    C++: Add spaces in Device::description().
+
+commit d1075e5acf811d6e6a0d5a87df1ff8f5ce6bf901
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 30 11:50:07 2014 +0100
+
+    C++: Expose device serial number and connection ID.
+
+commit 4c7c4194cbd1d172a8ea37206d6aff9a09c1fb91
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 30 11:07:55 2014 +0100
+
+    C++: Expose config key capabilities.
+
+commit 9c51e8ec56715074ca756fd54e63b360096113e2
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 27 16:29:34 2014 +0100
+
+    bindings: Update for input API changes.
+
+commit 753793eff5f713e07a42f2c7a177502aeb764654
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 23 12:05:31 2014 +0200
+
+    Clean up internal input API docs.
+
+commit 60107497fed4b307a7d64d23e3d8d6137f64c4e4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 23 12:04:35 2014 +0200
+
+    input: Use SR_ERR_NA instead of SR_OK_CONTINUE.
+
+commit d5cc282ff8026173c14ff6957482a24b2d6feef3
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 23 11:27:50 2014 +0200
+
+    input: sr_input_free() is now a void function.
+    
+    Its backend, input_module.cleanup(), is now also a void function.
+
+commit 7066fd466038bb4a8d09751f8a53c2452c5fefc1
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 23 11:12:33 2014 +0200
+
+    input: Add sr_input_end().
+    
+    This signifies to the module instance no more input will come. This
+    will cause the module to process any data it may have buffered. The
+    SR_DF_END packet will also typically be sent at this time.
+
+commit 89da5b3b54d3b87bff49e716c4d2b36080fe7a07
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 22 16:52:38 2014 +0200
+
+    input: Free instance-private storage at instance free.
+
+commit d0181813315114b88ad38cf276045ee5c311ca3c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 22 15:22:41 2014 +0200
+
+    input: Add sdi_ready flag to struct sr_input.
+    
+    When an input module instance has received enough input to fully
+    populate the struct sr_dev_inst, sdi_ready is set to TRUE and its
+    receive() method returns immediately. Any remaining received data
+    is buffered until the next time the function is called.
+
+commit 66c91e2509e2d995fa0b576104aa5ee04e469f21
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Sep 28 15:45:59 2014 +0200
+
+    Removal of sdi->index, step 8: fix victor-dmm
+
+commit bde9fbd1978ca2454e6b46eb65efe217a1f872bd
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Sep 28 14:33:03 2014 +0200
+
+    Removal of sdi->index, step 7: fix testo
+
+commit c79d4444fef482b57aee78b227c52e4a98fb0615
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Sep 28 11:10:26 2014 +0200
+
+    Removal of sdi->index, step 6: fix sysclk-lwla
+
+commit 65340dd5df1e596f1eb3bac54db19a697c7c68ef
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 23:03:52 2014 +0200
+
+    Removal of sdi->index, step 5: fix ikalogic-scanalogic2
+
+commit aed4ad0beaf64062752039a13f9a95326aa1df87
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 22:33:00 2014 +0200
+
+    Removal of sdi->index, step 4: fix trivial sr_dev_inst_new() calls
+
+commit f2209364737835ba78126cf7f2a707f63182f0e6
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 22:29:10 2014 +0200
+
+    Removal of sdi->index, step 3: sr_dev_inst_new() calls for input mods
+
+commit c6805a02cca9b0b17392841bc1afea8beaf333ed
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 22:26:06 2014 +0200
+
+    Removal of sdi->index, step 2: remove it from sr_session_load()
+
+commit 1b9e567b086ebd854d2db4d74dddb6bbf0277a72
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 22:20:51 2014 +0200
+
+    Removal of sdi->index, step 1: remove it from headers and helper funcs
+
+commit ce7d3578e3b1438ca472abea8b3844c3debd249f
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 22:15:45 2014 +0200
+
+    saleae-logic16: Use physical USB connection instead of sdi->index
+
+commit 71580ef1f3cf1a5979a2c52e9819de34d525ecbf
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 21:56:18 2014 +0200
+
+    Remove sdi->index from openbench-logic-sniffer and pipistrello-ols
+
+commit 395206f460cc4002feedca2265cf374b90c9c047
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 21:51:56 2014 +0200
+
+    hantek-dso: Use physical USB connection instead of sdi->index
+
+commit ee4f9bb1bd554848f86e09bb99c0fa8356f21b60
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sat Sep 27 20:48:47 2014 +0200
+
+    zeroplus-logic-cube: Use physical USB connection instead of sdi->index
+
+commit 03a4c07aff345f0b603199ad17d1dad01bb67293
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Sep 30 00:37:03 2014 +0200
+
+    C++: handle uint32_t SR_CONF keys which contain key capabilities
+    
+    This adapts the C++ bindings according to commits 584560f and 5827f61.
+
+commit 5e2c86eb3184fdc57341f6a1dd482f83556bf1d9
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Fri Sep 26 19:25:36 2014 +0200
+
+    fx2lafw: Use physical USB connection instead of sdi->index
+    
+    Previously, sdi->index was used to tell several identical fx2lafw-compatible
+    devices apart. This was a bit of a hack, so this patch makes it use physical
+    device connections instead. They're guaranteed to remain the same even if
+    the USB device reconnects.
+
+commit 8143cfdc90fc164e083a19c60b5b5d32a6b34f47
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 26 01:33:15 2014 +0200
+
+    Add sr_packet_copy/_free functions.
+    
+    These will be used to make a copy of a packet on the session bus,
+    valid for the duration of the device instance that generated it.
+
+commit 98edee7926b9827eec743bbfbfde9025089dc502
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 21:41:25 2014 +0100
+
+    serial: Eliminate serial_read(), serial_write() and SERIAL_NONBLOCK.
+
+commit 6c592ece705d4ec39ea3003a335cfe20d63aea47
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:30:24 2014 +0100
+
+    std_serial_dev_open: Remove SERIAL_NONBLOCK flag.
+    
+    All calls in drivers are now explicitly (non)blocking.
+
+commit 406e0c79cfbc5893635bbdece6f5c8b2ca9c9b25
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:29:13 2014 +0100
+
+    scpi_serial: Mark read/write calls nonblocking, remove SERIAL_NONBLOCK.
+    
+    These calls were already nonblocking. Having marked them as such, the flag can
+    be removed.
+
+commit 7ef93fb09fef3904e333cfbd28aacd418d324ad9
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:25:47 2014 +0100
+
+    tondaj-sl-814: Remove SERIAL_NONBLOCK flag.
+    
+    All calls in the driver are now explicitly (non)blocking.
+
+commit 539a85596febaca66c038b346e95068913c9cb45
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:24:33 2014 +0100
+
+    tondaj-sl-814: Mark serial read/write calls as blocking.
+    
+    These calls are executed from an event handler and were previously nonblocking,
+    but they have no partial read/write handling. The code is already marked TODO
+    for improvement.
+
+commit 47d98603e706e8c4f2da2b031f31c6dd2cc3b246
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:19:51 2014 +0100
+
+    tondaj-sl-814: Mark serial read call as nonblocking.
+    
+    This call was already nonblocking since the driver opens the port with the
+    SERIAL_NONBLOCK flag. Partial reads are handled.
+
+commit 9333691aaf1b08f597caa6a6490ebb56441f4a38
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:17:02 2014 +0100
+
+    teleinfo: Mark serial read as nonblocking, remove SERIAL_NONBLOCK flag.
+    
+    This call was already nonblocking due to the flag.
+
+commit 4ded59eef0db5deeffe554e05833eb0c2f09d90f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:15:36 2014 +0100
+
+    openbench-logic-sniffer: Remove SERIAL_NONBLOCK flag.
+    
+    All calls in this driver are already explicitly (non)blocking.
+
+commit d12ef776d3e81f30ca75bbdab7ea96341ad930a4
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:14:49 2014 +0100
+
+    norma-dmm: Remove SERIAL_NONBLOCK flag.
+    
+    All calls in the driver are now explicitly (non)blocking.
+
+commit 92714255b375d72458ddbac6c632d81f51fb9900
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:14:05 2014 +0100
+
+    norma-dmm: Make serial write call block.
+    
+    This call is executed from an event handler and was previously nonblocking,
+    but has no partial write handling. It sends a short packet so should be OK
+    to block, most likely the output buffer will be empty anyway.
+
+commit 8849f45ad71585e8eb3090733c911f6f15cc2a3f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:12:16 2014 +0100
+
+    norma-dmm: Make write call block.
+    
+    This call was previously nonblocking, but has no partial write handling.
+    It is called in the scan where it is free to block.
+
+commit 32950cc071715efae178aac92e7975c8ed185fd1
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:11:23 2014 +0100
+
+    norma-dmm: Mark serial read call as nonblocking.
+    
+    This call was already nonblocking since the driver opens the port with the
+    SERIAL_NONBLOCK flag. It only reads one byte, and a zero result is handled
+    appropriately.
+
+commit 5b980134c527de2f7971a69f7264fffd726625ea
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:05:03 2014 +0100
+
+    motech-lps-30x: Remove SERIAL_NONBLOCK.
+    
+    All calls in the driver are now explicitly (non)blocking.
+
+commit 856dccb7a6af99dbc4955053daca1b219bd6931d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:03:26 2014 +0100
+
+    motech-lps-30x: Make serial write call block.
+    
+    This call was previously explicitly nonblocking, but has no partial write
+    handling. It sends a short packet so should be OK to block, most likely the
+    output buffer will be empty anyway.
+
+commit e050124095c3e0109ead1fc162504cc513a4e08c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:59:45 2014 +0100
+
+    motech-lps-30x: Mark serial read call as nonblocking.
+    
+    This call was already nonblocking since the driver opens the port with the
+    SERIAL_NONBLOCK flag. It only reads one byte, and a zero result is handled
+    appropriately.
+
+commit 51056d61ec66124b236072ce9d28162ddecdcdcd
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:55:21 2014 +0100
+
+    mic-985xx: Remove SERIAL_NONBLOCK call.
+    
+    All calls in the driver are now explicitly (non)blocking.
+
+commit 3e2373247cf0b3c8557fb718d988b21a0299770c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:54:00 2014 +0100
+
+    mic-985xx: Make serial write call block.
+    
+    This call is executed from an event handler and was previously nonblocking,
+    but has no partial write handling. It sends a short packet so should be OK
+    to block, most likely the output buffer will be empty anyway.
+
+commit 98842e53eb5440b508f40d94af38ff003673c567
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:52:20 2014 +0100
+
+    mic-985xx: Mark serial read as nonblocking.
+    
+    This call was already nonblocking since the driver opens the port with the
+    SERIAL_NONBLOCK flag. Partial reads are handled.
+
+commit e13e354f892d25b0842790e420cceba1007d0823
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:45:36 2014 +0100
+
+    gmc-mh-1x-2x: Remove SERIAL_NONBLOCK flag.
+    
+    All calls in the driver are now explicitly (non)blocking.
+
+commit 612db3c44b5ab493d8fce9457c5807b15d169ea6
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:44:20 2014 +0100
+
+    gmc-mh-1x-2x: Make serial write calls block.
+    
+    These calls are executed from an event handler and were previously nonblocking,
+    but have no partial write handling. They send short packets so should be OK to
+    block, most likely the output buffer will be empty anyway.
+    
+    Fix error handling for some: serial_write can return any negative error code.
+
+commit e1960467ce43cd4e327579c3a0537b611d43f31e
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:40:47 2014 +0100
+
+    gmc-mh-1x-2x: Make serial write call block, and fix error handling.
+    
+    This call was previously nonblocking, but there is no handling of partial
+    writes. It is called from config_set where it is free to block.
+    
+    Also fix error handling: serial_write can return any negative error code,
+    not just -1.
+
+commit 4a0eda2b701d4ef26dc3f58f4ff00bbbbb21b246
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:36:00 2014 +0100
+
+    gmc-mh-1x-2x: Mark serial read calls as nonblocking.
+    
+    These calls were already nonblocking since the driver opens the port with the
+    SERIAL_NONBLOCK flag. They only read one byte. A return value of zero is not
+    handled, but should not occur in theory due to the G_IO_IN check. It might be
+    good to add handling of a zero return anyway, since I'm not sure if this is
+    always accurate.
+
+commit 0714a010ccd12514861eb24411fdb156299658a9
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:33:34 2014 +0100
+
+    gmc-mh-1x-2x: Mark serial read call as nonblocking.
+    
+    This call was already nonblocking since the driver opens the port with the
+    SERIAL_NONBLOCK flag. It only reads one byte, and a zero result is handled
+    appropriately.
+
+commit bb27c76513c887ad28bbafafb34858ee2e548c6d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:26:16 2014 +0100
+
+    fluke-dmm: Make serial write calls block, and fix error handling.
+    
+    These calls are executed from an event handler and were previously nonblocking,
+    but have no partial write handling. They send short packets so should be OK to
+    block, most likely the output buffer will be empty anyway.
+    
+    Also fix error handling for these calls, which seems to have been retained from
+    previous direct usage of write() to a serial port fd.
+
+commit 50276118ca45dc3dbf91f2fc77fc1f611cf1e57d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:16:04 2014 +0100
+
+    fluke-dmm: Make serial write calls block, and fix error handling.
+    
+    These calls were previously nonblocking, but have no partial write handling.
+    They are made from scan and acquisition_start contexts where they are free
+    to block.
+    
+    Remove the SERIAL_NONBLOCK at open, which only applied during scan, since all
+    calls in the scan are now explicitly blocking.
+    
+    Also fix error handling for these calls, which appears to have been kept
+    from a previous direct usage of write() on a serial port fd.
+
+commit 707fa85ad5fa7e3f74dbb6863891e80a26126b25
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:12:48 2014 +0100
+
+    fluke-dmm: Mark serial read call as nonblocking.
+    
+    This call was already nonblocking since the driver opens the port with the
+    SERIAL_NONBLOCK flag. Only one byte is read. The case of 0 being returned
+    is not handled, but the call is only made if G_IO_IN occurred so in theory,
+    there should be a byte available. It might be wise to add handling for a
+    return of 0 nonetheless, as I'm not sure if this is always accurate.
+
+commit 55e32714f994aaf4841b59bc67c82d263aeb0a70
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:07:14 2014 +0100
+
+    conrad-digi-35-cpu: Make serial write call block.
+    
+    This call was previously nonblocking, but there is no partial write handling.
+    It is only called from config_set so it is free to block.
+
+commit 0ac161bba0d9caaafcc2df946eaf242379eb4bfd
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 18:05:56 2014 +0100
+
+    conrad-digi-35-cpu: Remove SERIAL_NONBLOCK flag from open at scan time.
+    
+    This open call is only made to check access, the port is closed immediately
+    afterwards. The flag therefore has no effect.
+
+commit c9fc06d7f0b13798f9ce6f4187c0a676aa32a4d0
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 17:54:18 2014 +0100
+
+    colead-slm: Mark serial read calls as nonblocking.
+    
+    This is an odd one. These calls are made from a receive handler so should not
+    block, and appear to be setup correctly to handle partial reads or no data
+    available. However, the driver was not opening the port with SERIAL_NONBLOCK
+    so these calls would have been blocking. Make them nonblocking.
+
+commit 258a23134ddf6b71f6dc8032ff4919ef50c20b97
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 17:51:50 2014 +0100
+
+    center-3xx: Remove SERIAL_NONBLOCK flag.
+    
+    All serial calls in this driver are now explicitly (non)blocking.
+
+commit c1da74fc0a345a01d5734ee93e035cf808803598
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 17:51:25 2014 +0100
+
+    center-3xx: Make serial write call block.
+    
+    This call is executed from an event handler and was previously nonblocking,
+    but has no partial write handling. It sends a short packet so should be OK
+    to block, most likely the output buffer will be empty anyway.
+
+commit 3582ce8a01ef4f4befd3fccc28c5788196d48499
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 17:46:39 2014 +0100
+
+    center-3xx: Mark serial read call as nonblocking.
+    
+    This call was already nonblocking because the driver opens the port with the
+    SERIAL_NONBLOCK flag. Partial reads are handled.
+
+commit d7b269da8fc0a5dc2334a07f0fa371825aaceb34
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 17:41:05 2014 +0100
+
+    cem-dt-885x: Mark serial access as nonblocking, remove SERIAL_NONBLOCK flag.
+    
+    These calls were already nonblocking since this driver opened the port with
+    the SERIAL_NONBLOCK flag. Having marked them as such, we can remove the flag.
+    
+    Also remove an unnecessary reopen of the port to change its blocking status.
+
+commit ca4266a02f83b84c6baae1593641a37da4e8701c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:49:27 2014 +0100
+
+    brymen-dmm: Remove SERIAL_NONBLOCK flag.
+    
+    All serial read/write calls in the driver are now explicitly (non)blocking.
+
+commit 776313d997f1748ff89cb073f45e3560ad661136
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:47:28 2014 +0100
+
+    brymen-dmm: Make serial write call block.
+    
+    This call is executed from an event handler and was previously nonblocking,
+    but has no partial write handling. It sends a short packet so should be OK
+    to block, most likely the output buffer will be empty anyway.
+
+commit 44be13b1f8dfa67345585af8018b60c17c01025d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:42:26 2014 +0100
+
+    brymen-dmm: Mark serial read calls as nonblocking.
+    
+    The driver opens the port with SERIAL_NONBLOCK so these were already
+    nonblocking, and are handled appropriately.
+
+commit 5305266a28c1af09267f43f8941b6012be3d954f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:34:09 2014 +0100
+
+    atten-pps3xxx: Remove SERIAL_NONBLOCK.
+    
+    All serial read/write calls in the driver are now explicitly (non)blocking.
+
+commit c3116bc3115fb9f759fc9f4cb38f19746e533877
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:36:42 2014 +0100
+
+    atten-pps3xxx: Make serial write call block.
+    
+    This call is executed from an event handler and was previously nonblocking,
+    but has no partial write handling. It sends a short packet so should be OK
+    to block, most likely the output buffer will be empty anyway.
+
+commit e7b4103697627bc985b77feb4c05dc67da8aebc0
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:32:37 2014 +0100
+
+    atten-pps3xxx: Make serial write call block in scan.
+    
+    This is called at scan time so free to block. There is no partial write
+    handling and a response is expected afterwards. This should therefore be a
+    blocking call.
+
+commit 25dd083128e0cf60f9f23c7ac2649ec6829a954f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:29:57 2014 +0100
+
+    appa-55ii: Mark serial read nonblocking and remove SERIAL_NONBLOCK.
+    
+    This is the only read/write call in the driver. It is already non-blocking
+    and is handled appropriately.
+
+commit 64c536be5257604fb92bc727ed17cab626c1d455
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:17:47 2014 +0100
+
+    aglient-dmm: Remove SERIAL_NONBLOCK flag.
+    
+    All serial calls in this driver are now explicitly (non)blocking.
+
+commit a5053ddd216fb9ad9a7df6f4ce82dda863133685
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:14:23 2014 +0100
+
+    agilent-dmm: Make serial write call block in send.
+    
+    This call is executed from an event handler context was previously
+    nonblocking, however there is no handling for a partial write.
+    
+    The output buffer is unlikely to be full and the commands to be sent
+    are short, so it should be OK to make this a blocking call.
+
+commit 485b9ae34d37395ef1a77b5aedb337446aafddb9
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:11:05 2014 +0100
+
+    agilent-dmm: Make serial write call block in scan.
+    
+    This call is executed at scan time so is free to block. There is no
+    handling for a partial write and a response is expected immediately
+    afterwards. It should therefore be a blocking call.
+
+commit e0b781a45b5c6d13d3947c50571ddf22ee455e30
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:07:08 2014 +0100
+
+    agilent-dmm: Mark serial read call as nonblocking.
+    
+    This driver opens the port with the SERIAL_NONBLOCK flag, so this call is
+    already a nonblocking one, and is handled appropriately.
+
+commit 2e360339f9ead64ced1747e3ce49863b18db22ee
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 02:01:27 2014 +0100
+
+    serial-dmm: Remove SERIAL_NONBLOCK flag.
+    
+    All serial read/write calls in this driver are now explicitly
+    nonblocking so there is no need for this flag.
+
+commit 4277ac349ced6224e8093fea79b6988315fbb780
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 01:58:54 2014 +0100
+
+    serial-dmm: Mark serial read/write calls as nonblocking.
+    
+    This driver opens the port with the SERIAL_NONBLOCK flag so these calls were
+    already non-blocking.
+
+commit 02bd1d029858678add65cb436e2ba664673068a9
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 01:52:14 2014 +0100
+
+    colead-slm: Mark serial write call as blocking.
+    
+    This driver opens the port without the SERIAL_NONBLOCK flag,
+    so this call was already blocking.
+
+commit bbff0fe0d242542c1585cc9e94b9d4638632a049
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 16 01:49:53 2014 +0100
+
+    manson-hcs-3xxx: Mark serial read call as blocking.
+    
+    This driver opens the port without the SERIAL_NONBLOCK flag, so this call is
+    already a blocking one.
+
+commit bf505eed6b01f8283d9d1c526199d8e6128cf95a
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 19:35:39 2014 +0100
+
+    serial_readline: Implement using sp_blocking_read.
+
+commit 18e4d5bc45ec5571270040df5433fd7f6b79e03e
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 21 21:26:36 2014 +0100
+
+    serial_stream_detect: Make read nonblocking.
+    
+    This code implements its own waiting based on baudrate, so the read itself
+    should be nonblocking. In general it will have been already, since drivers
+    almost universally use the SERIAL_NONBLOCK flag.
+
+commit 2fe6210af6a1e8028967fa267dac9429b7952eef
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Tue Sep 23 17:43:27 2014 +0200
+
+    Add serial_num and connection_id fields to sr_dev_inst
+
+commit db156e5409b44bb90a81c1fcd160c2313fc156a0
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Mon Sep 22 23:08:17 2014 +0200
+
+    Add usb_get_port_path() helper function
+    
+    There is currently no way to uniquely identify USB devices in
+    libsigrok. It uses the "bus.address" scheme which is only
+    constant as long as the device remains attached to the bus.
+    In order to allow USB device persistence in PulseView, devices
+    need to provide a unique identifier even in absence of a
+    serial number. This function is the first step as it provides
+    the ability to query the physical location of a USB device.
+
+commit f12d9979481c059825ee6aef2d6fdb85f0b2bba1
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 23 22:40:47 2014 +0200
+
+    demo: Fix pattern mode and amplitude option publishing.
+
+commit 9a10ce65d62526a24cbca2ee9e1cf55e132d6f7a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 23 22:27:34 2014 +0200
+
+    colead-slm: Properly check acquisition sample limit.
+
+commit 79f92686c620cfd06e3ed9fd1a20087343e4778a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 19 00:21:30 2014 +0200
+
+    session: Don't try to send packets if no session has been set.
+    
+    This indicates a bug, but let's not segfault if it happens.
+
+commit 04c2f202f26f8875f4c52a48dffc320498d2c0e0
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Sep 17 22:04:22 2014 +0200
+
+    input/chronovu_la8: Fix broken I/O loop.
+
+commit af945a6625474e039fb7730483c5d07aa7f45969
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Sep 19 01:03:32 2014 +0200
+
+    Typos and consistency fixes.
+
+commit d4ccb6bd8fed2354eac92ea66ffdb274f0f78702
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Sep 17 17:41:41 2014 +0200
+
+    yokogawa-dlm: Don't implement dummy scan options.
+
+commit 5827f61b641cfd326a9cf2ea534eb4f9481a8187
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Sep 17 15:28:29 2014 +0200
+
+    Publish config key capabilities.
+    
+    Every driver now publishes its device option config keys, i.e. the
+    list fetched with sr_config_list(SR_CONF_DEVICE_OPTIONS), with a
+    set of flags indicating which methods are implemented by the driver
+    for that key.
+    
+    The config keys are OR'ed with any combination of SR_CONF_GET,
+    SR_CONF_SET and SR_CONF_LIST. These are defined as the high bits
+    of the uint32_t config key. Clients can OR config keys with
+    SR_CONF_MASK to strip out these bits. This mask will be kept up to
+    date if other bits are added to the capabilities list; clients MUST
+    therefore use SR_CONF_MASK for this.
+    
+    Some keys don't have capability bits added, such as the informative
+    device type keys (SR_CONF_MULTIMETER, SR_CONF_OSCILLOSCOPE, ...) and
+    SR_CONF_CONTINUOUS.
+    
+    Scan options do not have capabilities bits.
+
+commit 138589b02e3dd6aaad64e94a48efdf47d9b7d7d1
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 16 23:51:38 2014 +0200
+
+    beaglelogic: Add missing scan options, and cleanup.
+
+commit a0e0bb4149081eda06714f1158639f2dadcfa9d8
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 16 22:19:02 2014 +0200
+
+    Consistently use 'scanopts' variable across all drivers.
+
+commit f254bc4bba68d2cade0c8f7993d8fa8d3d9b556a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 16 22:11:39 2014 +0200
+
+    Consistently use 'devopts' variable across all drivers.
+
+commit 584560f142e1b17b9f4ef9069bd3724f2f77e750
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 16 17:49:20 2014 +0200
+
+    Change type of SR_CONF keys to uint32_t.
+
+commit a4e47454580b83972f052b5ace8e687d54d9425c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 14 05:33:24 2014 +0100
+
+    C++: Make hardware device instances user owned.
+
+commit 8fa3fc7c601a058832f21d970e6ede3ae8488141
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 14 05:32:00 2014 +0100
+
+    bindings: Make documentation script work under python 3.x.
+
+commit f90ed2d1afbb22a90e26b92a34ae55215c1685ca
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 14 04:53:06 2014 +0100
+
+    python: fix conversion to string variants.
+
+commit 13fef1ed246930715b5a020b17fe79313cf5eed4
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 14 04:47:12 2014 +0100
+
+    hwdriver: Validate types of GVariants passed by user.
+
+commit da89e23a2864a15f0cc548c8f803ff4ecc042cd5
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Sep 14 02:31:10 2014 +0100
+
+    Use AM_PATH_PYTHON macro to find Python binary.
+    
+    A specific interpreter can now be passed to configure e.g:
+    
+    ./configure PYTHON=python3
+
+commit db560903d196c39bceeaa543317fa602a02b51ac
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 13 20:23:46 2014 +0100
+
+    C++: use constructors for default argument declarations.
+
+commit 35114c3394679a4da54b930328409b3223507bad
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 13 20:23:23 2014 +0100
+
+    C++: include <cmath> for NAN.
+
+commit 3b161085731fc6e86f7decedee34f55096282581
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Sep 11 21:25:52 2014 +0100
+
+    bindings: Remove 'get_' prefix from all accessors.
+
+commit edbd09250b185950646ee5928b2e7112161168cc
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Sep 11 16:16:04 2014 +0100
+
+    scpi: Remove trailing carriage returns.
+
+commit 25f94dfebced0db7b0c0c403a1c90c517017fca5
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Wed Sep 10 23:55:15 2014 +0200
+
+    tests: Fix binary input test.
+
+commit 25f20faf0df4c2f124c4dea0786dc042fcd41ed3
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Sep 10 22:55:50 2014 +0200
+
+    input: Add debug output.
+
+commit 88189019acbfbd9f696bba3a83de60b3a524b754
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Sep 10 22:53:28 2014 +0200
+
+    input/binary: Fix broken I/O loop.
+
+commit 013ec84b83a64088b524bb0b3a923c98b7ae9ea4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Sep 10 18:21:51 2014 +0200
+
+    Don't start session with 0-channel devices.
+    
+    Drivers aren't really able to deal with that in some cases, and it
+    denotes a frontend bug.
+
+commit cfd8ec53abc53ad40a94cbb881776b58ed976d3a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 9 19:53:55 2014 +0200
+
+    scpi: Don't return NULL device from unsuccessful scan.
+
+commit bfc8679937df1b62ee961f1c576555fc8575e5ad
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 9 13:22:24 2014 +0200
+
+    scpi-pps: Clean up HP 6632B profile.
+
+commit 0d14e30ba01fb8258a57cb894feff56990a9cd13
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 8 23:23:51 2014 +0200
+
+    configure.ac: Add and use DRIVER2() to shorten file.
+
+commit d3619a9b63f49a07795d91c130b37df4857843f8
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 8 23:13:12 2014 +0200
+
+    configure.ac: Shorten per-driver lines a bit.
+
+commit 416de3ba0576147e14fe0b77106f96339e177d65
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 8 23:09:05 2014 +0200
+
+    configure.ac: Don't duplicate driver name.
+    
+    The AC_DEFINEs don't need any driver names really, those only end up
+    as code comments in config.h and are otherwise useless. Thus, don't
+    duplicate them, we already have the driver names in DRIVER().
+
+commit 331f56543a10dddf261ab1208ed5c28ad418463a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 8 23:31:54 2014 +0200
+
+    scpi-pps: Fix compile warning.
+
+commit fdedbfcdef0b0d0b36855df3f118efb219048368
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 8 23:30:21 2014 +0200
+
+    scpi-pps: Fix config_set checks.
+
+commit a0ca437de3f0c3912b944fa48cbb673ad491b208
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 8 22:58:14 2014 +0200
+
+    configure.ac: Show SCPI backends that'll be compiled.
+
+commit 62ea3ef5520e8935f00c4d2bdebaa2e1528baa0e
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 8 22:30:53 2014 +0200
+
+    configure.ac: Whitespace, cosmetics, sorting, etc.
+
+commit d4cf45e516eec56267e6f7aae087e3fb040af67b
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 6 17:28:27 2014 +0100
+
+    C++: Whitespace fix.
+
+commit cac58676e987d06b890366ac4078a1e4fb1cbdc3
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 6 16:06:00 2014 +0100
+
+    C++: Add SessionDevice class for devices owned by loaded sessions.
+
+commit be43d5d58472a54fbf49df93781be4775a831a41
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 6 14:56:20 2014 +0100
+
+    C++: Preserve original channel ordering.
+
+commit d9eed47d5bbf9c1a706fbda7ae56f87f7202f995
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 6 13:53:09 2014 +0100
+
+    C++: Add Configurable::config_check() method.
+
+commit d54190a3631250f23c51cd73770e9cfdd9076fe8
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 6 13:08:51 2014 +0100
+
+    C++: Add Configurable::config_keys() method.
+
+commit 59b74d28c94b566f252ffb268314526ce14c3b33
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 6 13:08:29 2014 +0100
+
+    C++: Make Driver inherit Configurable.
+
+commit 4f7bcf0ec35c18f10da51530767efcff62ddc88f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Sep 6 12:29:36 2014 +0100
+
+    C++: Fix recursion loop in PacketPayload::get_shared_pointer().
+
+commit effb9dd1c22dd10e1f276544bdadf4d3acf7dcc3
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Mon Sep 8 00:47:27 2014 +0200
+
+    rigol-ds: replace some magic numbers by appropriate constant or variable
+    
+    This fixes bug #406.
+
+commit bc4a2a46edf4149df702ef7111e08b50d53ad12c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 8 12:44:16 2014 +0200
+
+    Add support for HP 6632B.
+
+commit 01b0257aefc10874012e9bc066802a2c3a308801
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 8 03:26:19 2014 +0200
+
+    scpi-pps: Create one channel per MQ/output combination.
+    
+    This allows frontends to trivially select which MQs on which channels
+    they want to see; others won't even be fetched.
+
+commit 379d2609651e16956ae51b90e324e82d0e85b854
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 8 03:25:41 2014 +0200
+
+    Add private storage pointer to struct sr_channel.
+
+commit 478c8d923e026fe2dda707f67336326da635b2eb
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 8 00:02:03 2014 +0200
+
+    scpi-pps: Simplify SCPI command handling.
+
+commit 3222ee103d008fa25a125e51536d13a03a010c6a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 5 12:52:57 2014 +0200
+
+    scpi-pps: Add support for Rigol DP831A/DP832A.
+
+commit 58b77c41ff815d0157c58d052dfed9b087db42d8
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 5 12:50:07 2014 +0200
+
+    scpi-pps: Use regex to match model names.
+
+commit 22c18b03707834251b14a4f77d92aee19188dcbc
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 5 03:49:25 2014 +0200
+
+    scpi-pps: Generalize vendor name cleanup.
+
+commit d4eabea847e675bc333be1e51ead4e0be6fc976b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 5 03:23:32 2014 +0200
+
+    scpi-pps: Add support for Rigol DP832.
+
+commit 9e45cd41fd1618238c8a3afc56d4031c984bc3c5
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 5 03:20:32 2014 +0200
+
+    scpi-pps: Add basic cross-vendor PPS functionality.
+
+commit a1eaa9e066ff8d86db8a1fba6615204d442c53d7
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 5 02:47:22 2014 +0200
+
+    Revamp PPS-related config keys.
+
+commit ca1a7cb56fbfeeb1c5b27ea87eb8603ca3ca888b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 31 17:20:39 2014 +0200
+
+    scpi-pps: Initial driver skeleton.
+
+commit af1e487e3f8268bd3cecf778a26fa391ea2d1a31
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Thu Sep 4 22:06:54 2014 +0200
+
+    brymen-bm86x: Add current loop sensor support.
+
+commit 7343ad1ec72860b5d11ca8f35c4d014a1fa5b643
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 5 18:33:48 2014 +0200
+
+    scpi/gpib: Rename backend to scpi_libgpib.
+    
+    libgpib is the userspace component to linux-gpib's kernel modules that
+    implement low-level interface drivers.
+    
+    When libsigrok gets userspace GPIB interface drivers, that backend will
+    be the "official" scpi_gpib.
+
+commit d6e63a2c686a5bb6bef1adf394ce36c12db503ff
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Sep 5 18:26:12 2014 +0200
+
+    build: Use pkgconfig for libgpib.
+
+commit bb2a4ed407fe2986dce404244b4f2096536e2dac
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Sep 5 10:34:04 2014 +0100
+
+    Add GPIB SCPI backend using linux-gpib and libgpib.
+
+commit b4ed33a7765a3e08756c18695bcd885b330bc83a
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 2 21:31:11 2014 +0100
+
+    C++: Implement Deleter pattern in UserOwned template.
+
+commit 90e89c2a42ec085658f6c99656195ee4866e34c8
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 2 21:05:42 2014 +0100
+
+    C++: Add UserOwned base template for objects with resources owned by user.
+
+commit 541c855e1d05a007e996f52639698a7d3881c957
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 2 20:38:44 2014 +0100
+
+    C++: Rename StructureWrapper to ParentOwned.
+
+commit bf52cc8cf278ff73a0ace5b4472a276d76651232
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 2 20:30:50 2014 +0100
+
+    C++: Make StructureWrapper a recurring template, eliminating lots of casts.
+
+commit ba4eac48d4c7277d8b7cbe4036fb64610b8e6c61
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 2 20:01:35 2014 +0100
+
+    C++: Don't spell out arguments to StructureWrapper when not required.
+
+commit e8779db70cfe53db1d9f1f6ad00c4888ccf09b23
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 2 22:09:17 2014 +0200
+
+    input/wav: Now really correctly check supported sample size.
+
+commit 110fe1b4a2cac30140b7f5483e256f4c2fdf068c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 2 21:55:52 2014 +0200
+
+    agilent-dmm: U124xx/U125xx: Add current loop sensor support.
+    
+    Also fixes frequency support.
+
+commit 0d0170ae9e4743e3918a3c9709c026f93d714a4f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Tue Sep 2 19:21:11 2014 +0100
+
+    C++: Fix duplicated shared_ptr creation.
+
+commit 3bc172a61f1518a2a9cd4c795e6467c80d8f703d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Sep 1 16:41:21 2014 +0100
+
+    C++: Remove erroneous stray method.
+
+commit e82d34a9c230afb06c429f09fbff404299837e75
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Jul 17 01:06:13 2014 +0200
+
+    scpi/usbtmc: Minor debug output changes.
+
+commit 73145219c01dd8a1a00f54c7ac4c2c537ff02518
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 2 21:26:15 2014 +0200
+
+    input/wav: Correctly check supported sample size.
+
+commit 28d9df729254d998e152d7cec665a9db47c79c4d
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Tue Sep 2 19:14:25 2014 +0200
+
+    input/wav: Fix broken handling of float32 samples on big endian
+    
+    Also, make sure that floats are 32 bit even in the case of an
+    extensible header.
+
+commit c7f5219e627d9aa880a7547ddab3abf73420807c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 2 01:01:56 2014 +0200
+
+    agilent-dmm: Add temperature regex to U125xx parser.
+
+commit a965748ae2c880b2628888695198573f7c38eace
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Sep 2 00:54:23 2014 +0200
+
+    agilent-dmm: U124xx/U125xx: support 5 more modes.
+    
+    This adds support for resistance, capacitance, frequency, continuity,
+    and temperature.
+
+commit 5791bdf6048e7151e24e5bb0cdf592781fc3d03a
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Mon Sep 1 14:58:26 2014 +0200
+
+    gmc-mh-1x-2x-rs232: Completed energy measurement ranges (V, A, W) for Metrahit 29S.
+
+commit ee2bcdfc44f285c5a7b93fcba625409fdd25dcc3
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Sep 1 21:44:08 2014 +0200
+
+    agilent-dmm: U124x/U125x fixes.
+
+commit 95bc7725949836f8fb9851a51fcc755b25e4fc6f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Aug 29 21:32:34 2014 +0200
+
+    unit tests: Update to recent API changes.
+
+commit 129d5bc961fb3589a08b391092d9a7e5eef522f6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Sep 1 00:08:23 2014 +0200
+
+    error.c: Add missing entries.
+
+commit 78132e2a138f707b9522c41110079f92b39afc88
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Aug 30 12:16:51 2014 +0100
+
+    C++: Add sanity checks in StructureWrapper.
+
+commit 2fe07d3fb521ac1aac6c1e8a6a8fc8d651b28a29
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 31 13:46:45 2014 +0200
+
+    build: Silence java-clean.
+
+commit d92de05ad1d8004643ec5cd81227e456be5a261f
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sun Aug 31 10:56:00 2014 +0200
+
+    C++: Provide fallbacks for missing stoi/stod functions
+    
+    Notably, Android does not provide these functions.  The fallback
+    implementation is based on the one in the GNU ISO C++ Library.
+
+commit d1a5f737816cd9bc8d4ddfeb6d8831a1c1c58bf9
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sun Aug 31 10:49:02 2014 +0200
+
+    Makefile.am: Use $(CXXCOMPILE) to compile C++ code
+    
+    When compiling the SWIG wrappers for the Java bindings, use the correct
+    automake macro so that all compiler flags are honoured.
+
+commit ac10a927b5141f0f1e163d05a5c43e397c6ce9d2
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Aug 31 00:40:11 2014 +0200
+
+    yokogawa-dlm: Replace g_try_malloc() calls and fix coding style
+
+commit 0028d5a1eeca4620a21a6f7374edd43ca1370aab
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Wed Aug 27 14:22:57 2014 +0200
+
+    yokogawa-dlm: Lower timeout to increase throughput and fix data acq bug
+
+commit af3487ec288c9eb7042932140fcc8d53abf8c382
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Wed Aug 27 13:02:20 2014 +0200
+
+    yokogawa-dlm: Do not block when receiving and save frame length in scope state
+
+commit 8ab929d614262dc446ee2e948ca583d31110b14b
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Tue Aug 26 16:09:28 2014 +0200
+
+    yokogawa-dlm: Flesh out driver with current state of development
+
+commit 107639373df3d592961eb70111e6f4ccb36f74fa
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Mon Aug 25 13:09:42 2014 +0200
+
+    yokogawa-dlm: Integrate driver skeleton
+
+commit 6e8d95a50ccd041a0698ef84b39b3f70a3f570b5
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Aug 30 20:16:45 2014 +0200
+
+    input/csv: Use uint64 for samplerate option.
+
+commit edd28877cc74f9f0c063910d3cc24729db67f3f4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Aug 30 20:15:30 2014 +0200
+
+    input/chronovu_la8: Use uint64 for samplerate option.
+
+commit 10288172bed925d1e50270a62d323576b578ef7f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Aug 30 20:13:13 2014 +0200
+
+    input/binary: Use uint64 for samplerate option.
+
+commit 4edba404b0754ddea7ed9f2e0b851f067a8cb9f0
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Aug 30 19:32:15 2014 +0200
+
+    agilent-dmm: Add U124xx device IDs.
+
+commit 173378f0f5e323f9f73004a2d6a0dabb69810180
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 29 23:41:41 2014 +0200
+
+    agilent-dmm: Add provisional support for the U124xx.
+
+commit f857bd928da912b0b451893dd720ef909b2577ab
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 29 23:39:00 2014 +0200
+
+    agilent-dmm: Assume all A and B models are identical on the wire.
+
+commit 51b92b7da41c30c0dfe31c824ace6d9e7245ab92
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 29 21:35:07 2014 +0200
+
+    agilent-dmm: Add RMS flag to AC voltage modes.
+
+commit f216eb869967d354a0cc546ed72b93cbd5015c04
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 29 19:35:01 2014 +0200
+
+    agilent-dmm: Correctly parse negative overload.
+
+commit e6284bf09fa80a611b84fdc2f370fb10e1df7207
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Fri Aug 29 23:02:22 2014 +0200
+
+    Remove unneeded subdriver debug messages.
+
+commit 4cd883a7f36136ba9fbac4ed824dee4c6ce2b044
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 29 20:06:49 2014 +0100
+
+    C++: Fix shared pointer handling for PacketPayload base class.
+
+commit b31581f8a8fab97004603ba61c16c3758c14c777
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 29 17:35:46 2014 +0100
+
+    C++: Fix hash table initialisation in map_to_hash_variant.
+
+commit d01d2314879988e9d9ab80872889340e89ec8cc8
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 29 14:01:51 2014 +0100
+
+    C++: Fix shared pointer handling for Device base class.
+
+commit f1830b225db53a29fdb7ea5d0de328119d9b9e79
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 29 13:56:40 2014 +0100
+
+    Clean Java files on any change that needs re-running SWIG.
+
+commit 7c03b56443a514ce35b6be4fdfe2e9a3fac74e29
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Aug 28 17:44:24 2014 +0200
+
+    agilent-dmm: Fix value parser.
+    
+    This considered 0.0 to be an invalid result for no good reason.
+
+commit 6f479a0a72492b2a28344de04776733b19fdbaa9
+Author: Peter Zotov <whitequark at whitequark.org>
+Date:   Fri Aug 22 22:27:56 2014 +0400
+
+    Add support for mcupro Logic16, a Saleae Logic16 clone.
+    
+    From sigrok's point of view, this analyzer has two differences:
+    
+      * It does not require uploading the firmware.
+      * It returns garbage in some registers used for sanity checks.
+        Saleae's software ignores that garbage; sigrok only does if it
+        specifically detects the mcupro clone.
+
+commit f88c73732cbef5dad58788d1555bd66742001192
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Aug 28 01:49:48 2014 +0200
+
+    C++: Use sr_input_scan_*() API changes.
+
+commit 4f979115a4206bc1ff72c0319a340cbcad0f4b06
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Aug 28 00:22:13 2014 +0200
+
+    input: sr_input_scan_*() now return a status code.
+    
+    SR_OK: a match was found.
+    SR_ERR: no match.
+    SR_ERR_DATA: a match was found but the module cannot handle the input.
+    SR_OK_CONTINUE: some module didn't have enough data to be sure.
+
+commit aad21bd8666e7937b3d8b83b1fc0c57865dfc1ec
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Aug 28 00:19:36 2014 +0200
+
+    Add error string for SR_OK_CONTINUE.
+
+commit b7f446051c67f16c360350c6191d7d181ffd2356
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Aug 28 00:18:29 2014 +0200
+
+    Add SR_ERR_DATA.
+
+commit cbd9e6e987a4427a3ef11c1d12a967d4e6b92943
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 27 15:55:52 2014 +0200
+
+    input/wav: Support for WAVE_FORMAT_EXTENSIBLE.
+    
+    This is needed for supporting > 16-bit PCM samples.
+
+commit 962d43440a95f67d365f7e8174b2af89c34bae9a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 26 22:48:12 2014 +0200
+
+    input/wav: use our own endian macros.
+    
+    These should work better on non-aligned memory locations.
+
+commit 5bf0dd6aff6cc0ef1f18362faa0c6185a50996a4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 26 22:45:38 2014 +0200
+
+    Add RB16S and RB32S (signed) macros.
+    
+    Also note signedness of all the endian macros.
+
+commit 06ad20bebf658f97d55cda3ac058f9c336be09df
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 26 12:38:41 2014 +0200
+
+    input: Avoid warnings on all-zero static struct entries.
+
+commit 7f5a036750d30db466f23bb5fdaa28173bafe28d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Aug 24 20:11:35 2014 +0100
+
+    C++: Update InputFormat::get_options for latest version of input API.
+
+commit 43942280bb42a1dd82957aa582fd43d6e2e5dc96
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Aug 24 14:22:03 2014 +0100
+
+    C++: Implement InputFormat::get_options()
+
+commit 6e5240f418a8e021a8d1272b6255a8c0f10b5af6
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Aug 24 02:00:14 2014 +0100
+
+    C++: Correct ownership of InputDevice objects.
+
+commit ca3291e3ee7415c4c4505164ec931b91ea15cefe
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Aug 24 01:40:19 2014 +0100
+
+    Update bindings for new input API.
+
+commit b84cba4dbf2a78806848f9dbaf10238a18daf735
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 20 01:29:37 2014 +0200
+
+    input: Convert binary module.
+
+commit 75cb428f9f5f11aaa42499e45679b12cc806b1ce
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 20 01:27:39 2014 +0200
+
+    input: format_match() is not required in an input module.
+
+commit 02e24c0ce0d8677039f1ba9b10322c7967ee1a13
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 20 00:43:13 2014 +0200
+
+    input: Convert chronovu-la8 module.
+
+commit 5e83cd741f01551e1a529b2da703f1c91c25d520
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 20 00:42:16 2014 +0200
+
+    input/vcd: Minor code cleanup.
+
+commit 33e4303baa3a33e5321bae9f3769deb6afd9b7a3
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 20 00:41:22 2014 +0200
+
+    input: Only feed scanned header to streams, not scanned files.
+
+commit 115fbe9410c898c137071021ed37444e30524198
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 20 00:40:25 2014 +0200
+
+    input: Only use header buffer for modules that need it.
+    
+    The buffer was leaking into modules that didn't ask for it.
+
+commit 0a4d68f74b720f089966525055d91b0b026b7fa1
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 19 16:49:27 2014 +0200
+
+    input/wav: Send END packet on cleanup.
+
+commit c10ef17c23f9493e70227d8514254facf74cabd6
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 19 16:49:18 2014 +0200
+
+    input/vcd: Send END packet on cleanup.
+
+commit 41d214f61a1683fca49b9dc687045afc2ce42007
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 19 00:14:47 2014 +0200
+
+    input: Convert CSV module.
+
+commit 3e2cd21115a22689393f9d154b988792d18ae21a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 19 00:12:32 2014 +0200
+
+    Add second positive status code: SR_OK_CONTINUE.
+    
+    Errors are thus always < 0, SR_OK == 0, and "OK but..." status
+    codes are > 0.
+
+commit 57486a7528f8a646bbc6d22994a8f5effd3cacf9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 19 00:09:09 2014 +0200
+
+    input: sr_input_new() always allocates the instance buffer.
+
+commit fe4fe25bf5afdcab336e2fc10625c77720733ded
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 19 00:07:36 2014 +0200
+
+    input: Fix option enumeration.
+
+commit d65fcbcd4101c276e509746e1af73b3a95aa03fb
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 15 23:31:39 2014 +0200
+
+    input: Fix internal API docs.
+
+commit bd0bfaafd863ef1eb0cd7d1711c28d66bca3359b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 15 23:31:20 2014 +0200
+
+    input: Actually return a NULL-terminated array.
+
+commit 20e8882106bcd100414573f53b1a2e9a720cae9b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 15 19:59:38 2014 +0200
+
+    input: Frontends don't need to see SR_INPUT_META_*.
+
+commit 7db0639495cb8d96aadcd17678a535c97449a677
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 13 14:18:12 2014 +0200
+
+    input: Fixes and VCD.
+
+commit 0f3dbc9530b94a1cdefa97aee820e9de461cdb48
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 10 18:44:39 2014 +0200
+
+    input: Fix up API documentation.
+
+commit 17bfaca62aaacec71c6da4bd927af051727593b6
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 10 16:57:04 2014 +0200
+
+    input: Introduce new input module API.
+    
+    This is a work in progress.
+
+commit d514d35dab8b831264c8cb01444cf0bb5abbbf8f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 3 15:12:49 2014 +0200
+
+    input: make sr_input{_module} opaque to clients.
+
+commit d4c937749a92ce6defa2f0095b34692181afe597
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 3 15:11:24 2014 +0200
+
+    input: s/format/module in all naming.
+
+commit 81a34976553e9c9d2c61563de939c20d1cca118f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 26 01:49:00 2014 +0200
+
+    fx2lafw: Code cleanup.
+
+commit 1685c27619b7c8c9041476d0db437081ac892d4f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Aug 26 01:48:27 2014 +0200
+
+    Avoid warnings on all-zero static struct entries.
+
+commit 84cbaf77b4a74f55bd6e9a5dcfbb2c4d5bcf7c27
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Aug 25 01:00:00 2014 +0200
+
+    pipistrello-ols: Disable unused trigger stages.
+    
+    Thanks to Magnus Karlsson for this fix.
+
+commit acc885c7553d12ce18d45fc603ebf7d0c188db09
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 22 14:59:04 2014 +0200
+
+    pipistrello-ols: Preliminary port to current API.
+
+commit b94cff407f6a260e4ae916137da54f7602f3d050
+Author: magnuskarlsson <magnus at saanlima.com>
+Date:   Thu Jun 5 14:28:50 2014 -0700
+
+    DEMUX and RLE fixes
+
+commit 1e0de84608580b90cd19e13abff876cbc6a262fa
+Author: magnuskarlsson <magnus at saanlima.com>
+Date:   Wed May 28 18:53:22 2014 -0700
+
+    added edge triggers
+
+commit 1f9bcd0f946a0a55275357143e259ff402619444
+Author: magnuskarlsson <magnus at saanlima.com>
+Date:   Sat May 10 16:15:19 2014 -0700
+
+    fixed typos
+
+commit 72716f15bebb583a17ed47646724105b11bef4de
+Author: magnuskarlsson <magnus at saanlima.com>
+Date:   Sat May 10 16:04:58 2014 -0700
+
+    fixed a problem with USB defines
+
+commit 4bd80e12287dbc056f1431e42a17a0cb60010abc
+Author: magnuskarlsson <magnus at saanlima.com>
+Date:   Sat May 10 15:44:13 2014 -0700
+
+    added pipistrello-ols
+    
+    Conflicts:
+    	configure.ac
+    	src/hwdriver.c
+
+commit 562b7ae513ed755ee93f233d6a460259cea535de
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Sun Aug 24 16:54:38 2014 +0200
+
+    hameg-hmo / rigol-ds: Restore compatibility with std_dev_clear()
+
+commit 14e1aa6df0c760d6dacc0d3b225276cf32972ca5
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 22 20:15:32 2014 +0100
+
+    rigol-ds: Fix duplicated vendor string for Agilent devices.
+
+commit 81a69107de387a1f8d7c3b2ee9081386d2efc248
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 20 23:39:27 2014 +0200
+
+    build: Require libserialport version 0.2.0.
+
+commit 01f6e330f8a49a80f2c7fdf94af79bffbe279cba
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Jul 1 22:33:03 2014 +0200
+
+    serial: re-implement sr_serial_find_usb() using new libsp APIs
+
+commit fdefc40aec016e0395a6f46d96fd4931a0ec9f45
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 20 17:33:09 2014 +0200
+
+    output: Fix options enumeration. Again.
+
+commit aba57f3550b8d377e96dd47f5d555148851ab639
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Aug 20 12:58:33 2014 +0100
+
+    Close zip file at end of sr_session_file_check.
+    
+    From patch sent to sigrok-devel by jerryxjtu.
+
+commit 3e27b177c413063cfbdbe682504d11c03059b851
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Wed Aug 20 14:15:24 2014 +0200
+
+    configure.ac: Fix jni.h check when cross compiling
+    
+    Since AX_JNI_INCLUDE_DIR does not work for cross compilation, don't
+    invoke it when cross compiling.  Also, add a configure option to
+    set the jni.h include path manually if needed.
+
+commit 879dd50fb6d5f810d3c5635c3264b2c08ad22a70
+Author: Marc Schink <sigrok-dev at marcschink.de>
+Date:   Wed Aug 20 13:54:53 2014 +0200
+
+    output: Allocate additional memory for NULL terminator.
+    
+    Best regards,
+    Marc
+    
+    >From a7228150fdea91a65b5d70359bf51b87d2572edf Mon Sep 17 00:00:00 2001
+    From: Marc Schink <sigrok-dev at marcschink.de>
+    Date: Wed, 20 Aug 2014 05:34:57 -0400
+    Subject: [PATCH] output: Allocate additional memory for NULL terminator.
+
+commit f817f05aaca16d0bb95ca1ade7b7e1941178fa68
+Author: Marc Schink <sigrok-dev at marcschink.de>
+Date:   Wed Aug 20 13:55:04 2014 +0200
+
+    ikalogic-scanaplus: Free device context to fix memory leak.
+    
+    Best regards,
+    Marc
+    
+    >From 779ef3a1150b3679ab357ceb0e2f50785ad90d28 Mon Sep 17 00:00:00 2001
+    From: Marc Schink <sigrok-dev at marcschink.de>
+    Date: Wed, 20 Aug 2014 05:42:07 -0400
+    Subject: [PATCH] ikalogic-scanaplus: Free device context to fix memory leak.
+
+commit 9fc318d9ef1b3f2438e185d5a466dca03bcbcbb4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Aug 18 23:55:39 2014 +0200
+
+    output: Fix double free.
+
+commit 499c85dce538b5bb270bce62ba6c6911254f58a4
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Aug 18 23:54:46 2014 +0200
+
+    output: Fix output option enumeration.
+
+commit 34f4e3b4e4a4984d024e8757ee8b49c099d8baf9
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Aug 17 17:32:46 2014 +0200
+
+    unitests: Adapt to recent API changes.
+
+commit 7754fb4d931013f520f85aa8cbef3bcd491e2599
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sun Aug 17 14:20:01 2014 +0200
+
+    saleae-logic16: Recognize FPGA FIFO overflow status
+
+commit 0e1a7fe91a9132ad586337bdd29d93eff4344edd
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Sat Aug 16 23:44:19 2014 +0200
+
+    Makefile.am: Fix out-of-tree build for Python bindings
+
+commit 2ba308ecc69118287f12986b5d6814963f1d5da4
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Fri Aug 15 15:55:22 2014 +0200
+
+    configure.ac: Look for python-2.7.pc as well when making bindings
+
+commit 33c84e81975c75b73002b038a0076f384ca13d63
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Thu Aug 14 23:07:45 2014 +0200
+
+    Makefile.am: Fix out-of-tree build for C++ and Java bindings
+
+commit 7f82ec4d724c5ddb46bf8dbed6dce881bc21230a
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Fri Aug 15 14:42:18 2014 +0200
+
+    Makefile.am: Fix doxygen invocation in out-of-tree build
+
+commit 55e55a3f928460c9e20a14857bd27d312b4aadb3
+Author: Marcus Comstedt <marcus at mc.pp.se>
+Date:   Thu Aug 14 23:04:06 2014 +0200
+
+    Fix include paths for out-of-tree builds
+
+commit 70d3b20ba6189093c3a18473b3376644683f729d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Aug 17 11:44:05 2014 +0100
+
+    bindings: update for sr_output_options_{get,free} API change.
+
+commit af7d656d37de4b8b7fc344fd557f9ae8d3238705
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 15 20:21:50 2014 +0200
+
+    output: Constify module options.
+
+commit fc74643098877824b7a3cf16cc24de624b191615
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 15 20:16:31 2014 +0200
+
+    output: Actually return a NULL-terminated array.
+    
+    This returned an array of structs with an NULL-ed element at the end.
+    The drivers still do this, but the wrappers now make and free a NULL-
+    terminated array around it.
+    
+    sr_output_options_free() now takes the pointer returned by
+    sr_output_options_get(), instead of the module owning it.
+
+commit db81fbb58261825e03aadaf20cff794e4c65401a
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Wed Aug 13 16:33:38 2014 +0200
+
+    hameg-hmo: Make sure the enabled_channels list is empty before populating it
+    
+    Previous runs of dev_acquisition_start() keep the enabled_channels list
+    populated if they fail. This means that once an invalid channel
+    configuration was detected, it will be detected again even if the channel
+    configuration was changed. With this change, the list will be cleared
+    before being populated so that any stale entries are removed.
+
+commit 3c1cafebb3773739866ab16f878eca47e0ede785
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 13 14:34:24 2014 +0200
+
+    session: Remove irrelevant logging.
+
+commit 441e9eae58c2b1864b1982d09c4c2dc9c8f7e4bf
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 13 14:30:30 2014 +0200
+
+    output/wav: Minor code cleanup.
+
+commit 63f6df68c67099a207910f38386e0fe77ece127f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Aug 13 14:22:41 2014 +0200
+
+    output: Deal properly with NULL (no) options on a new output instance.
+
+commit 98de0c78743e27be185bce724fd847d20640ee22
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Aug 11 13:20:50 2014 +0200
+
+    tests: Factor out srtest_setup() and srtest_teardown().
+
+commit 41de54ffb1d36cbf5021705403a85b171662ec96
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Aug 11 13:15:43 2014 +0200
+
+    sr_session_new(): Return SR_ERR_ARG upon invalid argument.
+    
+    (instead of segfaulting)
+
+commit 4172352914138a17340cbd2dde74d095cb61a247
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Aug 11 12:37:54 2014 +0200
+
+    Add a small set of unit tests for session handling.
+
+commit 508ddda2a31b57aae2353345fd34072a4b80b292
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Aug 11 12:31:01 2014 +0200
+
+    session_file.c: Drop left-over extern.
+
+commit 15aa3b0d0ca6429b93206f79402a7d61064f15cb
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Aug 10 18:56:54 2014 +0200
+
+    Doxygen: Exclude more non-public files and directories.
+
+commit 9fcc286604df23b2447fc6ee29bbb44a85d0329f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Aug 10 18:56:24 2014 +0200
+
+    trigger.c: Add Doxygen @file and @defgroup tags.
+
+commit 071b93d9edc9dd1c49093cfd184ad6ecf6e7155f
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Aug 10 18:18:30 2014 +0200
+
+    Makefile.am: Silence some more Doxygen output.
+    
+    This silences stuff like this (among others), depending on the
+    Doxygen version used:
+    
+      Warning: Tag `XML_SCHEMA' at line 1814 of file `Doxyfile' has become
+      obsolete. To avoid this warning please remove this line from your
+      configuration file or upgrade it using "doxygen -u"
+
+commit ac0db24ad4b4e13c2bd9b0b5030dd31d4d8a4c55
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Aug 10 17:59:34 2014 +0200
+
+    Doxygen: Silence Doxygen warnings by default.
+    
+    This allows for a clean build per default. Developers can enable
+    those switches while writing documentation to get a few useful
+    warnings.
+
+commit 7efe889e7aa5849ae3e4d09b8f50779992502b79
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Aug 10 17:27:43 2014 +0200
+
+    Doxygen: Add a few missing @param lines for sessions.
+    
+    This fixes a bunch of Doxygen warnings.
+
+commit 5cad31c708b50329a27f31c7df0f67dddea6ce5a
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 1 20:08:44 2014 +0100
+
+    bindings: Transfer C++ method parameter documentation to Python/Java bindings.
+
+commit b6f411ac4eace7e3c793444b54be7456686d71a5
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Jul 27 12:05:44 2014 +0100
+
+    C++: Add parameter documentation and additional method descriptions.
+
+commit 8a314e90ffd20157340d6326d5a2424e7dbca050
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 1 19:29:48 2014 +0100
+
+    java: Add documentation generation.
+
+commit 6a8c1d68797cbb51cb94436b29ef451a1eab5293
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 1 19:22:24 2014 +0100
+
+    python: Add documentation generation.
+
+commit bd4fda24074ce175d23a59cb15cceb19707fe6f1
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 1 18:17:33 2014 +0100
+
+    bindings: Transfer C++ documentation strings to Python and Java wrappers.
+
+commit 84c870852af95aebf9c1c2fd3e30350b8c2c029a
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 1 17:40:56 2014 +0100
+
+    C++ bindings: Attach documentation to enum wrapper classes.
+
+commit 3532ed01256b986ecaf37ea5bb29d81465e2aa89
+Author: Martin Ling <martin-git at earth.li>
+Date:   Fri Aug 1 17:18:09 2014 +0100
+
+    C++ bindings: Reimplement enums.py using doxygen XML output instead of gccxml.
+
+commit b4e31d2aac02fb530326aaabedd3742772c52a2e
+Author: Soeren Apel <soeren at apelpie.net>
+Date:   Wed Aug 6 16:06:09 2014 +0200
+
+    Fix typo in the Hameg HMO driver and add some error message for when the float comparison breaks
+
+commit 375340a7245cdbfe1c769108fee77d52c45c963a
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Aug 4 22:00:17 2014 +0200
+
+    configure.ac: Don't build BeagleLogic where a build would fail.
+    
+    The BeagleLogic driver needs sys/mman.h and sys/ioctl.h in order to
+    build, so disable the driver if those headers aren't available.
+    
+    This is the case (for example) on MinGW.
+
+commit e336c0413c50fec1b5678957d182c045847bfc63
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 3 14:09:04 2014 +0200
+
+    output/vcd: Drop obsolete string.
+
+commit 226363c4e8247cae249f1c6a28e48e5a0163162c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 3 14:00:07 2014 +0200
+
+    hameg-hmo: Implement SR_CONF_SCAN_OPTIONS.
+
+commit 706f482a95f669f722a40fc30488d67431553d1e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 3 01:24:30 2014 +0200
+
+    output: Check options hash before destroying.
+
+commit dcc55fe91aadf4396ce4a3b5db2898da39e1a6be
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Aug 3 01:16:03 2014 +0200
+
+    output: Move option checks to the wrapper.
+    
+    Output modules are now guaranteed:
+      - Every option is always given, with the default value if not supplied
+        by the user, and is the right GVariantType.
+      - No invalid options are ever passed.
+
+commit 950043c30ecd2a0d1d15a14f0d07f29b06157fc6
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Aug 2 19:20:00 2014 +0200
+
+    output: Modules can keep track of option resources without wrapper help.
+
+commit 7ea75009d1977874efb686b000516c4ce1343474
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Aug 2 03:48:55 2014 +0200
+
+    output/wav: Add 'scale' option.
+    
+    Audio tools processing WAV failes generally need the samples to be in
+    the range -1 to +1. The scale option adds postprocessing to any samples
+    going into a WAV file, by dividing the sample values by the given factor.
+
+commit a24c3f4a899d61a6bfc837a1969dbacf42e2ea72
+Author: Janne Huttunen <jahuttun at gmail.com>
+Date:   Wed Jul 30 10:28:58 2014 +0300
+
+    Implement Brymen BM25x series as a serial DMM.
+    
+    The Brymen BM25x series supports the BC-20X that is an opto-isolated
+    serial cable. The link seems to be unidirectional i.e. when activated
+    the DMM periodically sends updates to the host while the host cannot
+    control the DMM in any way.
+    
+    The protocol is documented in "6000-count-digital-multimeters-r1.pdf"
+    that is available from the manufacturer. Every 15 byte packet consists
+    of a bitmap where the bits correspond to segments or symbols on the
+    LCD display i.e. the DMM essentially sends the contents of its screen
+    to the host in every update. This driver then decodes the measured
+    quantity, unit and its value from the bitmap.
+
+commit a01eab4a08e6da4db6fb90a607e98e16e2e5b24f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 1 16:31:13 2014 +0200
+
+    Doxygen: Update paths.
+
+commit 2f6f0e9187cc8e7f70da9e1107fa0cb6f6598b0c
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 1 12:04:20 2014 +0200
+
+    input/vcd: Make less noise in format match failure.
+
+commit b866fc095d8f80621551af5b97102d630591f73b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 1 12:03:42 2014 +0200
+
+    input/csv: Match format on .csv extension in filename.
+
+commit cb41a838a7da85f897b7ddd401b35a243a81bf1f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 1 12:02:09 2014 +0200
+
+    input/wav: Deal with IEEE float samples in WAV files.
+    
+    This also skips chunks before the 'data' chunk in WAV files, as
+    this is quite common.
+
+commit 364859ac732fadddada2d1622dfec7f2ba2fef76
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 1 12:00:51 2014 +0200
+
+    output/wav: Fix channel deinterleaving.
+
+commit 6e6babb9d5f2293b3ebc1b7d90f94fa7038e0602
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Aug 1 11:57:59 2014 +0200
+
+    output/analog: Fix channel deinterleaving.
+
+commit 0605f874754be5b6c8eb8b8bca481c0d47ea3f48
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 26 17:19:52 2014 +0200
+
+    output/wav: Initial module implementation.
+
+commit afaa75b98c8beca03d67d28dacbffee819c2f70b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 26 11:00:51 2014 +0200
+
+    output/wav: Initial module skeleton.
+
+commit 49224c285351e1d868731f13b3ef3c7906518706
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Jul 29 01:56:19 2014 +0200
+
+    demo: Attach analog generator to channel, not channel group.
+    
+    This also adds a new channel group "Analog", which has all analog
+    channels in it.
+
+commit d686c5ec462a4044e049931e57d60e9d08df8cde
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 28 15:56:17 2014 +0200
+
+    output: Rename instance private storage pointer to priv.
+    
+    This makes it consistent with other libsigrok fields used for this purpose.
+
+commit dddabe37052d0d8c7fbd2ac3e15861e556c4814f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 28 00:01:03 2014 +0200
+
+    demo: Support changing the amplitude of analog channels.
+
+commit cff7d8d60d1ae12aa38bc97c41506cc9067fe980
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Jul 27 23:59:49 2014 +0200
+
+    Add config key for amplitude.
+    
+    This is intended for setting (or getting) the amplitude of a source
+    which doesn't really have an MQ associated with it, such as the demo
+    driver's analog channels.
+
+commit 7a958e2a0703ba1ddc4c27a3f597efe1873eec69
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 26 21:28:05 2014 +0100
+
+    Java: correct input map type in create_output() wrapper.
+
+commit 90bd7656401dd178756597d133581e08615093d7
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 26 19:11:11 2014 +0100
+
+    Java: Move interfaces into org.sigrok.core.interfaces, tidy build system.
+
+commit 9455b6df6f7e727b9f089fea9271f619333d1bc0
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 26 20:37:53 2014 +0100
+
+    Java: ignore Meta::get_config() due to SWIG typemap issues.
+
+commit ca5fdd96ba672c7dec9474f1b4ca6c553559f1fe
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 26 18:34:40 2014 +0100
+
+    Add bindings/swig/classes.i to Python binding dependencies.
+
+commit 4711ec660d3c46aefa8d63b12c9bc8d5796acade
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 26 18:34:20 2014 +0100
+
+    Fix python-quietclean Makefile target.
+
+commit 58aa1f8359007804f48a4f881e6782a06e1b729a
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 26 16:03:25 2014 +0100
+
+    Update bindings to use new output API.
+
+commit a755b0e122105d934c4e7b97435420eda6df6e8e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Jul 25 05:56:52 2014 +0200
+
+    output: Finish output module API wrappers.
+    
+    The sr_output and sr_output_module structs are now no longer accessible
+    from the client.
+
+commit 06bd935e161dd273788ce9f1b455732fb91efd84
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 15:01:26 2014 +0100
+
+    bindings: Add Channel::get_index() method.
+
+commit f36ca8893d575d42eae0b103768ac7d8a4320322
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 14:45:58 2014 +0100
+
+    bindings: Add Device::get_description() method.
+
+commit e194c0119709d03db52f4287a62146c41739341d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 13:17:19 2014 +0100
+
+    bindings: Change return type of config_list to Glib::VariantContainerBase.
+
+commit 1d67cfb4eaa3884ded5d78d3ac53d19c9ee45ecb
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 12:43:27 2014 +0100
+
+    bindings: Revise Session::append() API.
+
+commit 6be7a7f287a5567361f1dadf41a9d11b828a9101
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 12:41:49 2014 +0100
+
+    bindings: Support get_channel_groups() on base Device class.
+
+commit 2928f47d6404e51c2dda1842c43db578cb1d6cdd
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Jul 20 02:20:13 2014 +0100
+
+    bindings: Overhaul packet & payload classes.
+
+commit 7009a3921a0394ec5e89c2f0009eae4d7398a560
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 18:26:19 2014 +0100
+
+    C++: Fix leak of GVariant in ConfigKey::parse_string().
+
+commit 1797a887d7672caa1e0fc1ff87a26de2370a8381
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 18:10:33 2014 +0100
+
+    C++: Fix Packet destructor for packets without payload.
+
+commit 4178d9712f3e67f83aa1eb49a5f5299e9ce1515b
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 03:22:44 2014 +0100
+
+    C++: Add internal lookup to find Channel object from sr_channel *.
+
+commit 7649683c2a5ee9fd859227fcd4cb875b9f47a2b1
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 24 03:12:40 2014 +0100
+
+    C++: Centralise code for preparing shared pointers.
+
+commit ed0b7fed106881941faa4d9b3f1bfdc17295d58d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Jul 24 21:01:39 2014 +0200
+
+    demo: Free analog channel groups when done with them.
+    
+    This fixes a memory leak.
+
+commit 886413b6d2e26513bd73f5d89f8b1816eda800a8
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Jul 24 21:01:08 2014 +0200
+
+    std: Free channel groups from device instances as well.
+
+commit 07443fd204f25639d38c87a42aa5eee137380fe9
+Author: Martin Ling <martin-git at earth.li>
+Date:   Wed Jul 23 22:40:17 2014 +0100
+
+    Add Doxyfile for C++ bindings and adaptations to C++ header file.
+
+commit 90ba83f21dae08fdafa26f9db513b25c1aeea92d
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Jul 20 01:38:31 2014 +0100
+
+    bindings: Add Packet.get_type() method.
+
+commit 6fa0eb86af061e1eddbc46b6d6fbe8f5d2186e3e
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 19 22:45:35 2014 +0100
+
+    Add bindings for getting/setting session trigger.
+
+commit de44e114fcaa0c8802aeeeba9bcc75e8a4b7366b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 23 16:33:42 2014 +0200
+
+    build: More dependency fixes.
+
+commit 1029d384d149fd287dae029d780b1ff5eea924be
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 23 14:36:11 2014 +0200
+
+    build: If language binding dependency checks fail, summarize the reason.
+
+commit 0afa91b765542c7efaf745b218e118823dccd052
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 23 12:57:37 2014 +0200
+
+    build: Add more dependency checks for building bindings.
+
+commit 57d62c4c7192f5b1f882c13de1abcde7527c156a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 23 12:45:41 2014 +0200
+
+    build: We only need python 2.x for generating the bindings.
+
+commit 4d7b36a0b59ec1c9d2aad42d1859dbbd7a88ffc6
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 23 12:32:35 2014 +0200
+
+    fx2lafw: Check for valid samplerate before setting.
+
+commit b62bb97afb6c09c75cf06be917ee40fe9330b34d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 23 04:53:25 2014 +0200
+
+    demo: Support continuous acquisition.
+
+commit 2f663c826af929b203eb1dfe6f72928d216fb062
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 23 00:24:23 2014 +0200
+
+    fx2lafw: Fix continuous mode.
+
+commit b88c3e492364eae674df9dddf4ca58b7a3d1aadf
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Jul 22 23:54:18 2014 +0200
+
+    Drop references to obsolete sigrok-commits mailing list.
+
+commit f4d2042d3c4b5aa63bb13e932b4f4d3cc4e651ab
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Jul 22 23:25:24 2014 +0200
+
+    session*.c: Remove some unneeded lines.
+
+commit 271cdfd267d027f9b31b8be07a964d4bdb0c7165
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Jul 22 23:15:11 2014 +0200
+
+    configure: Show glibmm and gobject versions in the summary.
+
+commit 022a7fe09fec0c58bd5f4e92f74610f46fb53d2b
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Jul 22 23:11:44 2014 +0200
+
+    configure.ac: Add versioned dependency on pygobject.
+    
+    Roughly around 3.0.0 usage changed from "import gobject" to the new
+    "from gi.repository import GObject" etc. (which we use).
+
+commit 3db4b08b2d2536e86cf0f120100f0334ae25b336
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Tue Jul 22 22:50:06 2014 +0200
+
+    Makefile.am: Add beaglelogic.h so it ends up in the tarball.
+
+commit c6036f755af6a292b63cc7552caa46e61d6a5789
+Author: Jens Steinhauser <jens.steinhauser at gmail.com>
+Date:   Tue Jul 22 12:40:57 2014 +0200
+
+    C++ bindings: Make enums.py work with newer versions of pygccxml.
+
+commit 155b680da482cea2381becb73c51cfb838bff31e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 21 21:27:33 2014 +0200
+
+    Reorganize project tree.
+
+commit 43cd4637285833706f8a404ca027bcf0ee75b9ae
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Jul 22 18:17:34 2014 +0200
+
+    build: Portability fixes.
+    
+    This also defers reporting on enabled drivers until it's really decided.
+
+commit c26162ffe0d08ba6b40125bd581046a7b16dfcbe
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Jul 22 04:31:54 2014 +0200
+
+    python: Fix build dependency.
+    
+    This also makes it more portable, notably to non-bash shells.
+
+commit ad9dbc1cdb4818954ee2954787feefc896b4c5c4
+Author: Kumar Abhishek <abhishek at theembeddedkitchen.net>
+Date:   Tue Jul 22 01:41:53 2014 +0530
+
+    beaglelogic: Implementation with soft triggers
+    
+    Signed-off-by: Kumar Abhishek <abhishek at theembeddedkitchen.net>
+
+commit bb993797611dc5dfcba339238bdfcdad0af0c0aa
+Author: Kumar Abhishek <abhishek at theembeddedkitchen.net>
+Date:   Tue Jul 22 01:41:52 2014 +0530
+
+    beaglelogic: Initial driver skeleton.
+
+commit 64668fae1936bf57ff0da768c58646283773f549
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 21 15:08:53 2014 +0200
+
+    Remove unnecessary level of indirection for source manipulation.
+
+commit 3337e9a1c9e853a57beed80084d1f5ca93e0a3db
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 21 15:08:17 2014 +0200
+
+    Get rid of global session.
+
+commit 102f12396660e0784134bccce5cc0679db325751
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 21 14:35:27 2014 +0200
+
+    Add struct sr_session parameter to all session source backends.
+
+commit 85b69c2b64fc3f0b0478063a983c1920bf0e30a7
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 19 15:31:08 2014 +0200
+
+    scpi: sr_scpi_scan_resource() never returns more than one sdi.
+    
+    No need to always make it a one-entry GSList.
+
+commit 8eb10b625e77a0c4eedb865eb03ada9694ce532a
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sun Jul 20 22:37:32 2014 +0200
+
+    python: Silence irrelevant build warnings.
+
+commit dfa4cd0e574e36b1e09f80170de40c5d16a91ed6
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jul 20 12:09:10 2014 +0200
+
+    make check: Update to new session API.
+
+commit 6592c3699509213395fa80af5b37f2e4db25ac38
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Sun Jul 20 12:03:29 2014 +0200
+
+    Fix 'make check' invocation wrt new libsigrok.h location.
+
+commit 9ae3eb121f6ec9cdc0067e0bc00b31590d28660d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 19 22:22:12 2014 +0200
+
+    python: Don't install as a zip file.
+
+commit f2a189f0a7737d6abc1140b61a554c4a9f1efb70
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 19 20:21:19 2014 +0200
+
+    build: Silence language bindings building.
+
+commit 6223feeb8ed6f6e525c85f3023195d99dfa1589f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 19 20:20:47 2014 +0200
+
+    configure: Add --enable-bindings switch.
+
+commit 6884b52bdefb7ed7169d557e1125f5b36276e2bf
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 19 19:48:01 2014 +0200
+
+    bindings: Filter out various pygccxml noise.
+
+commit 580ed4005bf0a95e8d7e11b527b0b1ad259aecca
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 19 17:05:29 2014 +0100
+
+    Additional build system fixes for C++/Python/Java bindings.
+
+commit d1785122c71c550e6068e84a3a8f0a30a3a2bf8c
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 19 16:23:45 2014 +0100
+
+    Assorted build system fixes for C++/Python/Java bindings.
+
+commit 815a6b95f437b7fcc5d90a305f667b01d731feae
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 19 15:43:03 2014 +0100
+
+    Pass configured prefix to python setup.py install step.
+
+commit d615a3962a2e8243aa36c8bd2feabe1d1db51374
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 19 15:27:41 2014 +0100
+
+    Suppress some SWIG warnings that don't matter.
+
+commit abc7146d032d41af41e8bd20ea64554021e86656
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Jul 19 15:10:25 2014 +0100
+
+    Fix building Python bindings with pygobject >= 3.7.91.
+
+commit 762aee1e918e19962a333073f1966ce26584fa11
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Jul 18 21:20:40 2014 +0200
+
+    configure: Fix java --enable check.
+
+commit bcacb8633ec3b4789801b92c73e72ca964948872
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Jul 18 21:12:31 2014 +0200
+
+    configure: Add C++ bindings dependency checks.
+
+commit f8158d56554688c9ff38731dbfd234fac46b6d9d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Jul 18 19:55:38 2014 +0200
+
+    configure: Deal with missing macro a bit better.
+    
+    The AX_CXX_COMPILE_STDCXX_11 macro is found in the autoconf-archive
+    package. If this is not installed, invoking the macro spits out an
+    error message that makes it look like a syntax error. This wraps it
+    in a check.
+
+commit f0f1d90d9c88d9369a8441618d348ce8aedf974b
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun May 4 21:13:52 2014 +0100
+
+    Remove obsolete low-level language bindings.
+
+commit 9fcf4d0b5b9969b0b7770e71994b960f15f7757f
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun Apr 27 09:48:18 2014 +0100
+
+    Add Java bindings.
+
+commit f774095496a5ab9b68ce79503ae7d45f717c0006
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 17 18:45:29 2014 +0100
+
+    Reimplement high-level Python bindings on top of SWIG/C++ bindings.
+
+commit 608b848d8be3488c2a38fd8a2fd78fce07d99b21
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 17 18:45:05 2014 +0100
+
+    Add new generic SWIG bindings based on C++ classes.
+
+commit c23c8659b8f8c4ca60bf59f6afd12bde7a0b2383
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 17 18:39:49 2014 +0100
+
+    Add C++ bindings.
+
+commit 00ebcbf48a775e56b6e73a39e69af390db547865
+Author: Martin Ling <martin-git at earth.li>
+Date:   Thu Jul 17 18:39:17 2014 +0100
+
+    Change prototype of sr_trigger_new to take const char *.
+
+commit 5a7e62211c4714ea2aac35baac10cf448373d4f5
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Apr 21 01:30:41 2014 +0100
+
+    SWIG: Rename libsigrok.i to lowlevel.i.
+
+commit fb336bf9e509693f460cfcdf16d106ba690734c8
+Author: Martin Ling <martin-git at earth.li>
+Date:   Mon Apr 21 02:13:10 2014 +0100
+
+    SWIG: Remove obsolete lowlevel wrapper methods for sr_output_format.
+
+commit 63d0fb752830fa8ea225ed9a9776e44a0ba66928
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sat Apr 26 03:03:11 2014 +0100
+
+    Move public headers into include/libsigrok.
+    
+    This is needed so that the C++ bindings, the header for which
+    references "libsigrok/libsigrok.h", can have a valid include
+    directory passed to build them before the headers are installed.
+
+commit 0812c40e361c9a75f3b4ef318a57ce8ba0479fa1
+Author: Martin Ling <martin-git at earth.li>
+Date:   Sun May 4 23:07:00 2014 +0100
+
+    Revise session API to allow for multiple sessions in future.
+
+commit 2c1a012ed243c39dc4ec1d24eede97e046224877
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Thu Jul 17 12:40:02 2014 +0200
+
+    gmc-mh-1x-2x-rs232: Support for Metrahit 16T and similiar models.
+
+commit 5842817e45d40fea99b698e162ba3b086c9bf3e2
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 14 19:22:58 2014 +0200
+
+    swig: Use new output API.
+
+commit 5d8c55f9eecaea4d5516f9b61f6f6abaf859dd82
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri Jul 11 02:14:14 2014 +0200
+
+    Move conditional driver cruft to separate source file.
+
+commit d4b387482850b094c908ee88f3538ee906afdc15
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu Jul 10 17:26:22 2014 +0200
+
+    autoconf: Use DRIVER macro to declare drivers.
+
+commit b5bbc3f1b00d5f3096c6800af4069fb07704d3a9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 9 20:06:19 2014 +0200
+
+    Minor code cleanup.
+
+commit 3747d8a21ea684800fb83f2fdc70794e037f8ecd
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed Jul 9 18:09:01 2014 +0200
+
+    testo: Remove unused dependency on libserialport.
+
+commit e7fef37bf89e762260bbdb46c4ad2de5bca45f77
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Jul 8 15:20:52 2014 +0200
+
+    saleae-logic16: Drop unused variable.
+
+commit 88b1d4e573480757f08ea4312b15bdccc264d444
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 7 17:37:36 2014 +0200
+
+    testo: Detach kernel driver as needed, and code optimization.
+    
+    Thanks to Aurelien Jacobs for the CRC check code improvement.
+
+commit 8789564070383e058cf5c9511c80c2398ff221e2
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Mon Jul 7 01:14:06 2014 +0200
+
+    testo: More robust probing and packet checking.
+    
+    The CRC in every packet is now also checked. Thanks to Aurelien
+    Jacobs for the CRC function.
+
+commit 6dcb97230eac000a4033aef0d5ec1099e5f8143d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 5 21:49:39 2014 +0200
+
+    testo: Initial driver implementation.
+    
+    This adds support for the Testo 435-4, with differential pressure
+    measurement built in, and probes for wind speed and temperature/humidity
+    at 293 kelvin.
+    
+    Support for other probe types will have to be added by people with
+    access to those probes.
+    
+    Models other than the 435-4 may well work unchanged, but this is
+    difficult to predict. Most likely new unit types will need to be added,
+    and possibly the protocol handling may need to be more flexible and
+    model-dependent to cope with 5-byte values and other minor changes
+    in the protocol.
+
+commit 0acdd79357633e90bb1a398e246ad96b39dceda9
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue Jul 1 01:34:29 2014 +0200
+
+    testo: Initial driver skeleton.
+
+commit 543d041dc16c134db18f34764cfd134335d04be2
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 5 21:47:21 2014 +0200
+
+    Add printers for wind speed, pressure and humidity at 293 kelvin.
+
+commit 31801362100e33af942ff652b266108a31cc0645
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 5 21:46:30 2014 +0200
+
+    Add MQ/units for wind speed, pressure and humidity at 293 kelvin.
+
+commit 7079c078df97af1ee6f4f47f46504a341a812865
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat Jul 5 21:45:54 2014 +0200
+
+    Add VID/PID for Testo 435.
+
+commit 5437a0adae74091bb4cd2243a39bd94f7ae73e1f
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Thu Jun 26 17:56:54 2014 +0200
+
+    manson-hcs-3xxx: Use maximum voltage and current read from device.
+
+commit a9cf203593b1a8f28624d4dcb5eed2bd7f152a00
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Thu Jun 26 14:22:50 2014 +0200
+
+    serial: docs.
+
+commit 811d6255bbed786183616867196029b639fd7891
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Thu Jun 26 14:14:31 2014 +0200
+
+    manson-hcs-3xxx: Implemented setting voltage, current and output.
+
+commit 9740d9bf8c257e714d937602a10b8d81a7762d4e
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Thu Jun 26 11:23:29 2014 +0200
+
+    manson-hcs-3xxx: Cleanup, improved error handling, docs.
+
+commit 25abc8dd135f9948e7ef52da9443b53f260dd83d
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Wed Jun 25 23:22:50 2014 +0200
+
+    manson-hcs-3xxx: Added missing models of series, fixed current resolution.
+
+commit adeae78f90a54fd7ca773f5e1cf433ccb1858e23
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Fri Jun 27 23:31:50 2014 +0200
+
+    manson-hcs-3xxx: Fixed build without libserialport.
+
+commit b5e926479019bbd08b555102c3112f654bfd69a8
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Jun 23 00:40:58 2014 +0200
+
+    manson-hcs-3xxx: Initial driver implementation.
+    
+    Currently supports only few HCS-3xxx models (tested on HCS-3202), and
+    not all commands/operations yet.
+
+commit 8f4e922f43ca7d6c9b25d593c7c25abc3efa7e17
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Jun 23 00:31:22 2014 +0200
+
+    manson-hcs-3xxx: Initial driver skeleton.
+
+commit 2baac44db78c9d0a5c1cc18ea53dbe7df60f7e8f
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Fri May 23 22:26:34 2014 +0200
+
+    serial-dmm: Implement request timeout mechanism.
+    
+    (fixes request flooding for Voltcraft ME-42 and M-3650CR)
+    
+    This fixes bug #345.
+
+commit ab4458df160ced42aa997617807c6a59c4d9a620
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Jun 16 13:20:56 2014 +0200
+
+    README: Document that libftdi1 is also an option.
+
+commit c78bcc6f5d3d5dd93c63e4ef438e41807c127d25
+Author: Uwe Hermann <uwe at hermann-uwe.de>
+Date:   Mon Jun 16 13:19:34 2014 +0200
+
+    Lower libftdi1 requirement to 1.0 (builds fine too).
+
+commit 92506359d333b25255e7b79db47af9ae32d1b4e3
+Author: Dan Horák <dan at danny.cz>
+Date:   Sun Jun 15 11:08:37 2014 +0200
+
+    detect version 1.x of libftdi library
+    
+    The FDTI library changed version, module name and also soname, so add an option to detect it
+    when the 0.x version is not found. The 1.x API is compatible enough for libsigrok to build.
+
+commit 82bfb646aee7741eb366ffc5c4054ea284ae380b
+Author: Mike Frysinger <vapier at gentoo.org>
+Date:   Fri Jun 13 22:44:39 2014 -0400
+
+    do not add check to common cflags/libs
+    
+    Only the unittests use these flags, so don't go linking them in for
+    the main library too.
+
+commit e15e5873b34f1a06ea2490ad7782870daa63f311
+Author: Mike Frysinger <vapier at gentoo.org>
+Date:   Fri Jun 13 22:44:40 2014 -0400
+
+    asix-sigma: fix build time warnings
+    
+    We need to include unistd.h for usleep():
+    hardware/asix-sigma/asix-sigma.c: In function 'sigma_fpga_init_bitbang':
+    hardware/asix-sigma/asix-sigma.c:450:3: warning: implicit declaration of function 'usleep'
+       usleep(10000);
+    
+    And we need to tweak the type of chunks_per_read to match dl_lines_total:
+    hardware/asix-sigma/asix-sigma.c: In function 'download_capture':
+    hardware/asix-sigma/asix-sigma.c:1161:39: warning: comparison between signed and unsigned integer expressions
+       dl_lines_curr = MIN(chunks_per_read, dl_lines_total);
+
+commit a4737997a2dcedaa616272013b2580246c3d32eb
+Author: Mike Frysinger <vapier at gentoo.org>
+Date:   Fri Jun 13 22:44:38 2014 -0400
+
+    add explicit configure flags for ftdi/serial/usb libraries
+    
+    This makes it a lot easier for distros to control this functionality.
+
+commit a4e435eb49c1fa30c31d5851b404001324cafe33
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Fri Jun 13 21:22:11 2014 +0200
+
+    norma-dmm: Added request timeout mechanism; docs.
+
+commit 49c06128d777a4d422a96e713b1490fa7a708da4
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Sat Jun 7 19:45:41 2014 +0200
+
+    norma-dmm: Added separate driver siemens-b102x for Siemens B1024-B1028 DMMs (just for cosmetic reasons).
+
+commit 13dd25bdb807217addc76e9c446485e28e6ec00a
+Author: Aurelien Jacobs <aurel at gnuage.org>
+Date:   Tue Jun 3 16:43:04 2014 +0200
+
+    chronovu-la: re-add return that was inadvertently removed in commit aeff7fa2
+
+commit a153d6b8fffa6e1e1a88c8d52bee0aec86add91e
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat May 31 10:34:56 2014 +0200
+
+    trigger: Rename sr_trigger_stage_new() to sr_trigger_stage_add().
+
+commit d292f767bd031979edbcbf54b7daa39b0aad7812
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Sat May 31 10:17:17 2014 +0200
+
+    session: Expose sr_session_trigger_get() as a public API call.
+
+commit 649e9e1677b9fefa790289feb66da9acd8579589
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu May 29 22:58:58 2014 +0200
+
+    fx2lafw: Fix sample count.
+
+commit a989cdbe03d8b5afae779dc97f14a76d7184638f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu May 29 22:55:52 2014 +0200
+
+    saleae-logic16: Fix acquisition with fewer than nine channels enabled.
+
+commit 863357fb1091489d5ecb4257bcb879b3beb79734
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu May 29 22:16:58 2014 +0200
+
+    saleae-logic16: Use new library software trigger.
+
+commit 335122f07d3768cc0fdfa5541030c3a546b8c054
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 15:54:46 2014 +0200
+
+    fx2lafw: Use new library software trigger.
+
+commit ac136b574ac289efffe4273fd7f1e29c70e19208
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 15:53:27 2014 +0200
+
+    Add new software trigger as a library-wide facility.
+    
+    This is strictly an internal feature, for use by drivers for hardware
+    that doesn't have its own trigger mechanism.
+
+commit 3d68b6126d4fd670c317db497ade19314f7f8b56
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 15:46:43 2014 +0200
+
+    session: Free session-wide trigger if set.
+
+commit 2fe80494471a601484b58dd30043665c0dd84774
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 15:46:21 2014 +0200
+
+    trigger: Fix memory leak.
+
+commit 28731bab29640ee3d68b60c1ebdb471a0758e41b
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:22:06 2014 +0200
+
+    zeroplus-logic-cube: Use new trigger API.
+
+commit bbe7e48a8856c3cab7e2dcef34da8111e23a3bc3
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:21:56 2014 +0200
+
+    sysclk-lwla: Use new trigger API.
+
+commit 91fd0f7246b2cfe7adbdcd8c8cdefe89b6607afb
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:21:45 2014 +0200
+
+    ols: Use new trigger API.
+
+commit 02d5c0d8ea5b398962b51952928d31e75c4a8f8d
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:21:23 2014 +0200
+
+    ikalogic-scanalogic2: Use new trigger API.
+
+commit 9615eeb572be5db9e770c0e343f80ab212335e3f
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:21:10 2014 +0200
+
+    fx2lafw: Use new trigger API.
+
+commit aeff7fa28622367115c3c8bdbd965c07dd536458
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:20:51 2014 +0200
+
+    chronovu-la: Use new trigger API.
+
+commit 39c64c6a4ffd11fded83770510fcbea78a658d87
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:19:58 2014 +0200
+
+    asix-sigma: Use new trigger API.
+
+commit 9f00e5aefb492a4bed34ece62177b95c794dbe33
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:05:51 2014 +0200
+
+    Remove dead code from session read.
+    
+    The "trigger" keyword was supported in theory, but in practice nothing
+    ever wrote it, so it was never used.
+
+commit 6db3b6a4d1cff174853206092afa8d191183b6fc
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:05:00 2014 +0200
+
+    Remove obsolete API call sr_parse_triggerstring().
+    
+    Since triggers are now passed to libsigrok with an API, this moved
+    to sigrok-cli.
+
+commit 3b0ff21c8cac7e8db5ed1c00d36d12c8fd41c7ec
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Wed May 28 00:01:54 2014 +0200
+
+    Remove obsolete API call sr_dev_trigger_set().
+    
+    Triggers are now set on the session, not on a channel.
+
+commit 795c9de35e90515e18a107f5f55f1956c7c785ee
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Tue May 27 23:55:26 2014 +0200
+
+    Rename SR_CONF_TRIGGER_TYPE to SR_CONF_TRIGGER_MATCH.
+    
+    Drivers supporting triggering need to implement this in config_list()
+    and return an array of int32_t consisting of SR_TRIGGER_*.
+
+commit 7b5e6d2978b9fe7afa952b7fa9f8837c87e8ed26
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Fri May 23 01:21:58 2014 +0200
+
+    Add new triggering framework.
+    
+    The new triggers consist of a set of structs and an API to manipulate
+    them.  Both logic and analog triggers are supported, in an unlimited
+    number of stages.
+    
+    A single struct sr_trigger containing its stages and triggers is then
+    added to the session.
+    
+    In case of a driver where the hardware supports triggering, the struct
+    is then converted and used to arm the hardware trigger. Drivers without
+    hardware trigger support, such as fx2lafw or multimeter drivers, use it
+    as the basis for a software-based trigger implementation instead.
+
+commit f66d45806fb6f9757343db72664ab2b058c75985
+Author: Bert Vermeulen <bert at biot.com>
+Date:   Thu May 22 23:22:37 2014 +0200
+
+    fx2lafw: Rename trigger constant.
+
+commit 419bfb50098a33007099837d9ae7900deb1568e2
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Mon May 19 21:38:23 2014 +0200
+
+    motech-lps-30x: Cleanup.
+
+commit 1f7da0c2bbc096cdc5750f47e084ced8c7ce6e48
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Mon May 19 20:47:15 2014 +0200
+
+    motech-lps-30x: Cleanup.
+
+commit 1c3d002b68d9fe6fe511c84e4da7f164f32f35ff
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Mon May 19 19:48:28 2014 +0200
+
+    motech-lps-30x: Implemented driver.
+
+commit d0a92abd543345beb5bcc4d2ddf87936a35d1fb3
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Mon May 19 19:44:04 2014 +0200
+
+    Whitespace and comment improvements, no semantical changes.
+
+commit 1ba4a1cf33080d6e9e7453372ddcf94cab277624
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Mon May 19 19:40:53 2014 +0200
+
+    strutil: Now using base 10 in sr_atol(), sr_atoi() for compatibility to atoi(), atol(); docs.
+
+commit 41b7bd01c1f58f0214b16949a89ed0d283aa4220
+Author: Matthias Heidbrink <m-sigrok at heidbrink.biz>
+Date:   Mon May 19 19:23:30 2014 +0200
+
+    motech-lps-30x: Initial driver framework.
 
 commit 4133caab1d2467a582850a3713ee083827364c8b
 Author: Uwe Hermann <uwe at hermann-uwe.de>
diff --git a/Doxyfile b/Doxyfile
index a42748b..b659cc8 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME           = "libsigrok"
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = "0.3.0"
+PROJECT_NUMBER         = "0.4.0"
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -58,7 +58,7 @@ PROJECT_LOGO           = contrib/sigrok-logo-notext.png
 # entered, it will be relative to the location where doxygen was started. If
 # left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = doxy
+OUTPUT_DIRECTORY       = $(BUILDDIR)doxy
 
 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
 # directories (in 2 levels) under the output directory of each output format and
@@ -144,7 +144,7 @@ FULL_PATH_NAMES        = YES
 # will be relative from the directory where doxygen is started.
 # This tag requires that the tag FULL_PATH_NAMES is set to YES.
 
-STRIP_FROM_PATH        =
+STRIP_FROM_PATH        = . $(BUILDDIR)
 
 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
 # path mentioned in the documentation of a class, which tells the reader which
@@ -683,7 +683,7 @@ CITE_BIB_FILES         =
 # messages are off.
 # The default value is: NO.
 
-QUIET                  = NO
+QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
 # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
@@ -743,7 +743,7 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = .
+INPUT                  = src include $(BUILDDIR)include/libsigrok
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -778,7 +778,9 @@ RECURSIVE              = YES
 # Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE                = config.h libsigrok-internal.h session_driver.c std.c
+EXCLUDE                = config.h src/libsigrok-internal.h src/session_driver.c
+EXCLUDE               += src/std.c src/drivers.c src/ezusb.c src/fallback.c
+EXCLUDE               += src/soft-trigger.c src/usb.c
 
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
@@ -797,20 +799,30 @@ EXCLUDE_SYMLINKS       = NO
 #
 # Ignore the following files and directories (see also EXCLUDE above):
 #  - config.h: Non-public stuff, the file doesn't get installed.
-#  - libsigrok-internal.h: Non-public stuff, the file doesn't get installed.
-#  - session_driver.c: Special driver for "virtual" devices, non-public.
-#  - std.c: Non-public helpers, no public API stuff in there.
-#  - hardware/*: Only driver-specific stuff, no public API stuff in there.
-#  - input/*: Only input.c contains public API, everything else doesn't.
-#  - output/*: Only output.c contains public API, everything else doesn't.
+#  - src/libsigrok-internal.h: Non-public stuff, the file doesn't get installed.
+#  - src/session_driver.c: Special driver for "virtual" devices, non-public.
+#  - src/std.c: Non-public helpers, no public API stuff in there.
+#  - src/drivers.c: Non-public helpers, no public API stuff in there.
+#  - src/ezusb.c: Non-public helpers, no public API stuff in there.
+#  - src/fallback.c: Fallback functions to API calls from serial.c.
+#  - src/soft-trigger.c: Non-public helpers, no public API stuff in there.
+#  - src/usb.c: Non-public helpers, no public API stuff in there.
+#  - src/hardware/*: Only driver-specific stuff, no public API stuff in there.
+#  - src/input/*: Only input.c contains public API, everything else doesn't.
+#  - src/output/*: Only output.c contains public API, everything else doesn't.
+#  - src/transform/*: Only transform.c contains public API, everything else doesn't.
+#  - src/scpi/*: Non-public helpers, no public API stuff in there.
+#  - src/dmm/*: Non-public helpers, no public API stuff in there.
+#  - src/lcr/*: Non-public helpers, no public API stuff in there.
 #  - tests/*: Unit tests, no public API stuff in there.
 #  - bindings/*: Language bindings, no public API stuff in there.
 #  - doxy/*: Potentially already generated docs, should not be scanned.
 #
-EXCLUDE_PATTERNS       = */hardware/* */input/* */output/* */tests/*
-EXCLUDE_PATTERNS      += */bindings/*
-EXCLUDE_PATTERNS      += */doxy/*
-INPUT                 += input/input.c output/output.c
+EXCLUDE_PATTERNS       = */src/hardware/* */src/input/* */src/output/* */src/transform/*
+EXCLUDE_PATTERNS      += */src/scpi/* */src/dmm/* */src/lcr/*
+EXCLUDE_PATTERNS      += */src/tests/* */src/bindings/* */src/doxy/*
+INPUT                 += src/input/input.c src/output/output.c
+INPUT                 += src/transform/transform.c
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
@@ -1795,7 +1807,7 @@ MAN_LINKS              = NO
 # captures the structure of the code including all documentation.
 # The default value is: NO.
 
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 
 # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -1824,7 +1836,7 @@ XML_DTD                =
 # The default value is: YES.
 # This tag requires that the tag GENERATE_XML is set to YES.
 
-XML_PROGRAMLISTING     = YES
+XML_PROGRAMLISTING     = NO
 
 #---------------------------------------------------------------------------
 # Configuration options related to the DOCBOOK output
diff --git a/HACKING b/HACKING
index 0e818c2..aa408ff 100644
--- a/HACKING
+++ b/HACKING
@@ -45,7 +45,7 @@ You can apply it like this:
  $ cd libsigrok
  $ git am 0001-tondaj-sl-814-Initial-driver-skeleton.patch
 
-You can now edit the files in the hardware/tondaj-sl-814 directory as needed
+You can now edit the files in src/hardware/tondaj-sl-814 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
@@ -59,15 +59,10 @@ 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
 'new-driver' script (see above) does for you:
 
- - configure.ac:
-   - Add an --enable-tondaj-sl-814 option.
-   - Add "hardware/tondaj-sl-814/Makefile" to the AC_CONFIG_FILES list.
-   - Add and entry for the device in the "Enabled hardware drivers" list
-     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
+ - Makefile.am: Add HW_TONDAJ_SL_814 and add to libsigrok_la_SOURCES.
+ - configure.ac: Add a DRIVER() and DRIVER2() call.
+ - src/drivers.c: Add a tondaj_sl_814_driver_info entry in two places.
+ - src/hardware/tondaj-sl-814/ directory: Add api.c, protocol.c, protocol.h.
 
 See existing drivers or the 'new-driver' output for the details.
 
@@ -81,18 +76,21 @@ Random notes
  - Generally avoid assigning values to variables at declaration time,
    especially so for complex and/or run-time dependent values.
 
- - Consistently use g_try_malloc() / g_try_malloc0(). Do not use standard
+ - Consistently use g_*malloc() / g_*malloc0(). Do not use standard
    malloc()/calloc() if it can be avoided (sometimes other libs such
    as libftdi can return malloc()'d memory, for example).
 
  - Always properly match allocations with the proper *free() functions. If
-   glib's g_try_malloc()/g_try_malloc0() was used, use g_free() to free the
+   glib's g_*malloc()/g_*malloc0() was used, use g_free() to free the
    memory. Otherwise use standard free(). Never use the wrong function!
 
- - Never use g_malloc() or g_malloc0(). These functions do not return NULL
-   if not enough memory is available but rather lead to an exit() or segfault
-   instead. This behaviour is not acceptable for libraries.
-   Use g_try_malloc()/g_try_malloc0() instead and check the return value.
+ - We assume that "small" memory allocations (< 1MB) will always succeed.
+   Thus, it's fine to use g_malloc() or g_malloc0() for allocations of
+   simple/small structs and such (instead of using g_try_malloc()), and
+   there's no need to 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
    elsewhere) "manually" via e.g. printf() or g_log() or similar functions.
@@ -105,7 +103,7 @@ Random notes
  - Consistently use the same naming convention for #include guards in headers:
    <PROJECTNAME>_<PATH_TO_FILE>_<FILE>
    This ensures that all #include guards are always unique and consistent.
-   Examples: LIBSIGROK_LIBSIGROK_H, LIBSIGROK_HARDWARE_MIC_985XX_PROTOCOL_H
+   Example: LIBSIGROK_HARDWARE_MIC_985XX_PROTOCOL_H
 
  - Consistently use the same naming convention for API functions:
    <libprefix>_<groupname>_<action>().
diff --git a/Makefile.am b/Makefile.am
index 0bda213..9ce6760 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,302 +18,473 @@
 ## along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ##
 
-ACLOCAL_AMFLAGS = -I autostuff
+ACLOCAL_AMFLAGS = -I m4
+AM_LIBTOOLFLAGS = --silent
+GNUMAKEFLAGS = --no-print-directory
 
-AM_CPPFLAGS = -DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
+# distutils/setuptools cause trouble on distcheck. Disable for now.
+DISTCHECK_CONFIGURE_FLAGS = --disable-python
+
+FIRMWARE_DIR = $(datadir)/sigrok-firmware
+
+local_includes = -Iinclude -I$(srcdir)/include -I$(srcdir)/src -I.
+if BINDINGS_CXX
+local_includes += -Ibindings/cxx/include -I$(srcdir)/bindings/cxx/include -Ibindings/cxx
+endif
+# Do not hard-code the firmware location on Windows.
+if WIN32
+global_defs =
+else
+global_defs = -DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
+endif
+# Ensure that local include directories are always searched first.
+AM_CPPFLAGS = $(local_includes) $(global_defs)
+
+# The tests CFLAGS are a superset of the libsigrok CFLAGS, and the
+# python bindings CFLAGS are a superset of the C++ bindings CFLAGS.
+AM_CFLAGS = $(SR_EXTRA_CFLAGS) $(SR_WFLAGS) $(TESTS_CFLAGS)
+AM_CXXFLAGS = $(SR_WXXFLAGS) $(LIBSIGROKCXX_CFLAGS)
 
 lib_LTLIBRARIES = libsigrok.la
 
 # Backend files
 libsigrok_la_SOURCES = \
-	backend.c \
-	device.c \
-	session.c \
-	session_file.c \
-	session_driver.c \
-	hwdriver.c \
-	strutil.c \
-	log.c \
-	version.c \
-	error.c \
-	std.c
-
-# Input formats
-libsigrok_la_SOURCES += \
-	input/binary.c \
-	input/chronovu_la8.c \
-	input/csv.c \
-	input/input.c \
-	input/vcd.c \
-	input/wav.c
-
-# Output formats
-libsigrok_la_SOURCES += \
-	output/output.c \
-	output/analog.c \
-	output/ascii.c \
-	output/bits.c \
-	output/binary.c \
-	output/csv.c \
-	output/chronovu_la8.c \
-	output/gnuplot.c \
-	output/hex.c \
-	output/ols.c \
-	output/vcd.c
-
-# Hardware (common files)
-libsigrok_la_SOURCES += \
-	hardware/common/scpi.c \
-	hardware/common/scpi_tcp.c
+	src/backend.c \
+	src/device.c \
+	src/session.c \
+	src/session_file.c \
+	src/session_driver.c \
+	src/drivers.c \
+	src/hwdriver.c \
+	src/trigger.c \
+	src/soft-trigger.c \
+	src/analog.c \
+	src/fallback.c \
+	src/resource.c \
+	src/strutil.c \
+	src/log.c \
+	src/version.c \
+	src/error.c \
+	src/std.c
+
+# Input modules
+libsigrok_la_SOURCES += \
+	src/input/input.c \
+	src/input/binary.c \
+	src/input/chronovu_la8.c \
+	src/input/csv.c \
+	src/input/raw_analog.c \
+	src/input/trace32_ad.c \
+	src/input/vcd.c \
+	src/input/wav.c
+
+# Output modules
+libsigrok_la_SOURCES += \
+	src/output/output.c \
+	src/output/analog.c \
+	src/output/ascii.c \
+	src/output/bits.c \
+	src/output/binary.c \
+	src/output/csv.c \
+	src/output/chronovu_la8.c \
+	src/output/wav.c \
+	src/output/gnuplot.c \
+	src/output/hex.c \
+	src/output/ols.c \
+	src/output/srzip.c \
+	src/output/vcd.c
+
+# Transform modules
+libsigrok_la_SOURCES += \
+	src/transform/transform.c \
+	src/transform/nop.c \
+	src/transform/scale.c \
+	src/transform/invert.c
+
+# SCPI support
+libsigrok_la_SOURCES += \
+	src/scpi.h \
+	src/scpi/scpi.c \
+	src/scpi/helpers.c \
+	src/scpi/scpi_tcp.c
 if NEED_RPC
 libsigrok_la_SOURCES += \
-	hardware/common/scpi_vxi.c \
-	hardware/common/vxi_clnt.c \
-	hardware/common/vxi_xdr.c \
-	hardware/common/vxi.h
+	src/scpi/scpi_vxi.c \
+	src/scpi/vxi_clnt.c \
+	src/scpi/vxi_xdr.c \
+	src/scpi/vxi.h
 endif
 if NEED_SERIAL
 libsigrok_la_SOURCES += \
-	hardware/common/serial.c \
-	hardware/common/scpi_serial.c
+	src/serial.c \
+	src/scpi/scpi_serial.c
 endif
 if NEED_USB
 libsigrok_la_SOURCES += \
-	hardware/common/ezusb.c \
-	hardware/common/usb.c \
-	hardware/common/scpi_usbtmc_libusb.c
+	src/ezusb.c \
+	src/usb.c \
+	src/scpi/scpi_usbtmc_libusb.c
 endif
 if NEED_VISA
 libsigrok_la_SOURCES += \
-	hardware/common/scpi_visa.c
+	src/scpi/scpi_visa.c
+endif
+if NEED_GPIB
+libsigrok_la_SOURCES += \
+	src/scpi/scpi_libgpib.c
+endif
+
+# Modbus support
+libsigrok_la_SOURCES += \
+	src/modbus/modbus.c
+if NEED_SERIAL
+libsigrok_la_SOURCES += \
+	src/modbus/modbus_serial_rtu.c
+endif
+
+# Hardware (DMM chip parsers)
+libsigrok_la_SOURCES += \
+	src/dmm/es519xx.c \
+	src/dmm/fs9721.c \
+	src/dmm/fs9922.c \
+	src/dmm/m2110.c \
+	src/dmm/metex14.c \
+	src/dmm/rs9lcd.c \
+	src/dmm/bm25x.c \
+	src/dmm/ut71x.c \
+	src/dmm/ut372.c \
+	src/dmm/vc870.c \
+	src/dmm/dtm0660.c
+
+# Hardware (LCR chip parsers)
+if NEED_SERIAL
+libsigrok_la_SOURCES += \
+	src/lcr/es51919.c
 endif
 
-# Hardware (DMM parsers)
+# Hardware (Scale protocol parsers)
 libsigrok_la_SOURCES += \
-	hardware/common/dmm/es519xx.c \
-	hardware/common/dmm/fs9721.c \
-	hardware/common/dmm/fs9922.c \
-	hardware/common/dmm/m2110.c \
-	hardware/common/dmm/metex14.c \
-	hardware/common/dmm/rs9lcd.c
+	src/scale/kern.c
 
 # Hardware drivers
 if HW_AGILENT_DMM
 libsigrok_la_SOURCES += \
-	hardware/agilent-dmm/api.c \
-	hardware/agilent-dmm/agilent-dmm.h \
-	hardware/agilent-dmm/sched.c
+	src/hardware/agilent-dmm/api.c \
+	src/hardware/agilent-dmm/agilent-dmm.h \
+	src/hardware/agilent-dmm/sched.c
 endif
 if HW_APPA_55II
 libsigrok_la_SOURCES += \
-	hardware/appa-55ii/protocol.h \
-	hardware/appa-55ii/protocol.c \
-	hardware/appa-55ii/api.c
+	src/hardware/appa-55ii/protocol.h \
+	src/hardware/appa-55ii/protocol.c \
+	src/hardware/appa-55ii/api.c
 endif
 if HW_ASIX_SIGMA
 libsigrok_la_SOURCES += \
-	hardware/asix-sigma/asix-sigma.h \
-	hardware/asix-sigma/asix-sigma.c
+	src/hardware/asix-sigma/protocol.h \
+	src/hardware/asix-sigma/protocol.c \
+	src/hardware/asix-sigma/api.c
 endif
 if HW_ATTEN_PPS3XXX
 libsigrok_la_SOURCES += \
-	hardware/atten-pps3xxx/protocol.h \
-	hardware/atten-pps3xxx/protocol.c \
-	hardware/atten-pps3xxx/api.c
+	src/hardware/atten-pps3xxx/protocol.h \
+	src/hardware/atten-pps3xxx/protocol.c \
+	src/hardware/atten-pps3xxx/api.c
+endif
+if HW_BAYLIBRE_ACME
+libsigrok_la_SOURCES += \
+	src/hardware/baylibre-acme/protocol.h \
+	src/hardware/baylibre-acme/protocol.c \
+	src/hardware/baylibre-acme/api.c \
+	src/hardware/baylibre-acme/gpio.h \
+	src/hardware/baylibre-acme/gpio.c
+endif
+if HW_BEAGLELOGIC
+libsigrok_la_SOURCES += \
+	src/hardware/beaglelogic/beaglelogic.h \
+	src/hardware/beaglelogic/protocol.h \
+	src/hardware/beaglelogic/protocol.c \
+	src/hardware/beaglelogic/api.c
 endif
 if HW_BRYMEN_BM86X
 libsigrok_la_SOURCES += \
-	hardware/brymen-bm86x/protocol.h \
-	hardware/brymen-bm86x/protocol.c \
-	hardware/brymen-bm86x/api.c
+	src/hardware/brymen-bm86x/protocol.h \
+	src/hardware/brymen-bm86x/protocol.c \
+	src/hardware/brymen-bm86x/api.c
 endif
 if HW_BRYMEN_DMM
 libsigrok_la_SOURCES += \
-	hardware/brymen-dmm/parser.c \
-	hardware/brymen-dmm/protocol.h \
-	hardware/brymen-dmm/protocol.c \
-	hardware/brymen-dmm/api.c
+	src/hardware/brymen-dmm/parser.c \
+	src/hardware/brymen-dmm/protocol.h \
+	src/hardware/brymen-dmm/protocol.c \
+	src/hardware/brymen-dmm/api.c
 endif
 if HW_CEM_DT_885X
 libsigrok_la_SOURCES += \
-	hardware/cem-dt-885x/protocol.h \
-	hardware/cem-dt-885x/protocol.c \
-	hardware/cem-dt-885x/api.c
+	src/hardware/cem-dt-885x/protocol.h \
+	src/hardware/cem-dt-885x/protocol.c \
+	src/hardware/cem-dt-885x/api.c
 endif
 if HW_CENTER_3XX
 libsigrok_la_SOURCES += \
-	hardware/center-3xx/protocol.h \
-	hardware/center-3xx/protocol.c \
-	hardware/center-3xx/api.c
+	src/hardware/center-3xx/protocol.h \
+	src/hardware/center-3xx/protocol.c \
+	src/hardware/center-3xx/api.c
 endif
 if HW_CHRONOVU_LA
 libsigrok_la_SOURCES += \
-	hardware/chronovu-la/protocol.h \
-	hardware/chronovu-la/protocol.c \
-	hardware/chronovu-la/api.c
+	src/hardware/chronovu-la/protocol.h \
+	src/hardware/chronovu-la/protocol.c \
+	src/hardware/chronovu-la/api.c
 endif
 if HW_COLEAD_SLM
 libsigrok_la_SOURCES += \
-	hardware/colead-slm/protocol.h \
-	hardware/colead-slm/protocol.c \
-	hardware/colead-slm/api.c
+	src/hardware/colead-slm/protocol.h \
+	src/hardware/colead-slm/protocol.c \
+	src/hardware/colead-slm/api.c
 endif
 if HW_CONRAD_DIGI_35_CPU
 libsigrok_la_SOURCES += \
-	hardware/conrad-digi-35-cpu/protocol.h \
-	hardware/conrad-digi-35-cpu/protocol.c \
-	hardware/conrad-digi-35-cpu/api.c
+	src/hardware/conrad-digi-35-cpu/protocol.h \
+	src/hardware/conrad-digi-35-cpu/protocol.c \
+	src/hardware/conrad-digi-35-cpu/api.c
 endif
 if HW_DEMO
 libsigrok_la_SOURCES += \
-	hardware/demo/demo.c
+	src/hardware/demo/demo.c
+endif
+if HW_DEREE_DE5000
+libsigrok_la_SOURCES += \
+	src/hardware/deree-de5000/api.c
 endif
 if HW_FLUKE_DMM
 libsigrok_la_SOURCES += \
-	hardware/fluke-dmm/fluke-dmm.h \
-	hardware/fluke-dmm/fluke.c \
-	hardware/fluke-dmm/api.c
+	src/hardware/fluke-dmm/fluke-dmm.h \
+	src/hardware/fluke-dmm/fluke.c \
+	src/hardware/fluke-dmm/api.c
 endif
 if HW_FX2LAFW
 libsigrok_la_SOURCES += \
-	hardware/fx2lafw/protocol.h \
-	hardware/fx2lafw/protocol.c \
-	hardware/fx2lafw/api.c
+	src/hardware/fx2lafw/protocol.h \
+	src/hardware/fx2lafw/protocol.c \
+	src/hardware/fx2lafw/api.c \
+	src/hardware/fx2lafw/dslogic.c \
+	src/hardware/fx2lafw/dslogic.h
 endif
 if HW_GMC_MH_1X_2X
 libsigrok_la_SOURCES += \
-	hardware/gmc-mh-1x-2x/protocol.h \
-	hardware/gmc-mh-1x-2x/protocol.c \
-	hardware/gmc-mh-1x-2x/api.c
+	src/hardware/gmc-mh-1x-2x/protocol.h \
+	src/hardware/gmc-mh-1x-2x/protocol.c \
+	src/hardware/gmc-mh-1x-2x/api.c
+endif
+if HW_GWINSTEK_GDS_800
+libsigrok_la_SOURCES += \
+	src/hardware/gwinstek-gds-800/protocol.h \
+	src/hardware/gwinstek-gds-800/protocol.c \
+	src/hardware/gwinstek-gds-800/api.c
 endif
 if HW_HAMEG_HMO
 libsigrok_la_SOURCES += \
-	hardware/hameg-hmo/protocol.h \
-	hardware/hameg-hmo/protocol.c \
-	hardware/hameg-hmo/api.c
+	src/hardware/hameg-hmo/protocol.h \
+	src/hardware/hameg-hmo/protocol.c \
+	src/hardware/hameg-hmo/api.c
 endif
 if HW_HANTEK_DSO
 libsigrok_la_SOURCES += \
-	hardware/hantek-dso/dso.h \
-	hardware/hantek-dso/dso.c \
-	hardware/hantek-dso/api.c
+	src/hardware/hantek-dso/dso.h \
+	src/hardware/hantek-dso/dso.c \
+	src/hardware/hantek-dso/api.c
+endif
+if HW_HUNG_CHANG_DSO_2100
+libsigrok_la_SOURCES += \
+	src/hardware/hung-chang-dso-2100/protocol.h \
+	src/hardware/hung-chang-dso-2100/protocol.c \
+	src/hardware/hung-chang-dso-2100/api.c
 endif
 if HW_IKALOGIC_SCANALOGIC2
 libsigrok_la_SOURCES += \
-	hardware/ikalogic-scanalogic2/protocol.h \
-	hardware/ikalogic-scanalogic2/protocol.c \
-	hardware/ikalogic-scanalogic2/api.c
+	src/hardware/ikalogic-scanalogic2/protocol.h \
+	src/hardware/ikalogic-scanalogic2/protocol.c \
+	src/hardware/ikalogic-scanalogic2/api.c
 endif
 if HW_IKALOGIC_SCANAPLUS
 libsigrok_la_SOURCES += \
-	hardware/ikalogic-scanaplus/protocol.h \
-	hardware/ikalogic-scanaplus/protocol.c \
-	hardware/ikalogic-scanaplus/api.c
+	src/hardware/ikalogic-scanaplus/protocol.h \
+	src/hardware/ikalogic-scanaplus/protocol.c \
+	src/hardware/ikalogic-scanaplus/api.c
 endif
 if HW_KECHENG_KC_330B
 libsigrok_la_SOURCES += \
-	hardware/kecheng-kc-330b/protocol.h \
-	hardware/kecheng-kc-330b/protocol.c \
-	hardware/kecheng-kc-330b/api.c
+	src/hardware/kecheng-kc-330b/protocol.h \
+	src/hardware/kecheng-kc-330b/protocol.c \
+	src/hardware/kecheng-kc-330b/api.c
+endif
+if HW_KERN_SCALE
+libsigrok_la_SOURCES += \
+	src/hardware/kern-scale/protocol.h \
+	src/hardware/kern-scale/protocol.c \
+	src/hardware/kern-scale/api.c
+endif
+if HW_KORAD_KAXXXXP
+libsigrok_la_SOURCES += \
+	src/hardware/korad-kaxxxxp/protocol.h \
+	src/hardware/korad-kaxxxxp/protocol.c \
+	src/hardware/korad-kaxxxxp/api.c
 endif
 if HW_LASCAR_EL_USB
 libsigrok_la_SOURCES += \
-	hardware/lascar-el-usb/protocol.h \
-	hardware/lascar-el-usb/protocol.c \
-	hardware/lascar-el-usb/api.c
+	src/hardware/lascar-el-usb/protocol.h \
+	src/hardware/lascar-el-usb/protocol.c \
+	src/hardware/lascar-el-usb/api.c
+endif
+if HW_LECROY_LOGICSTUDIO
+libsigrok_la_SOURCES += \
+	src/hardware/lecroy-logicstudio/protocol.h \
+	src/hardware/lecroy-logicstudio/protocol.c \
+	src/hardware/lecroy-logicstudio/api.c
+endif
+if HW_MANSON_HCS_3XXX
+libsigrok_la_SOURCES += \
+	src/hardware/manson-hcs-3xxx/protocol.h \
+	src/hardware/manson-hcs-3xxx/protocol.c \
+	src/hardware/manson-hcs-3xxx/api.c
+endif
+if HW_MAYNUO_M97
+libsigrok_la_SOURCES += \
+	src/hardware/maynuo-m97/protocol.h \
+	src/hardware/maynuo-m97/protocol.c \
+	src/hardware/maynuo-m97/api.c
 endif
 if HW_MIC_985XX
 libsigrok_la_SOURCES += \
-	hardware/mic-985xx/protocol.h \
-	hardware/mic-985xx/protocol.c \
-	hardware/mic-985xx/api.c
+	src/hardware/mic-985xx/protocol.h \
+	src/hardware/mic-985xx/protocol.c \
+	src/hardware/mic-985xx/api.c
+endif
+if HW_MOTECH_LPS_30X
+libsigrok_la_SOURCES += \
+	src/hardware/motech-lps-30x/protocol.h \
+	src/hardware/motech-lps-30x/protocol.c \
+	src/hardware/motech-lps-30x/api.c
 endif
 if HW_NORMA_DMM
 libsigrok_la_SOURCES += \
-	hardware/norma-dmm/protocol.h \
-	hardware/norma-dmm/protocol.c \
-	hardware/norma-dmm/api.c
+	src/hardware/norma-dmm/protocol.h \
+	src/hardware/norma-dmm/protocol.c \
+	src/hardware/norma-dmm/api.c
+endif
+if HW_OPENBENCH_LOGIC_SNIFFER
+libsigrok_la_SOURCES += \
+	src/hardware/openbench-logic-sniffer/protocol.h \
+	src/hardware/openbench-logic-sniffer/protocol.c \
+	src/hardware/openbench-logic-sniffer/api.c
 endif
-if HW_OLS
+if HW_PIPISTRELLO_OLS
 libsigrok_la_SOURCES += \
-	hardware/openbench-logic-sniffer/protocol.h \
-	hardware/openbench-logic-sniffer/protocol.c \
-	hardware/openbench-logic-sniffer/api.c
+	src/hardware/pipistrello-ols/protocol.h \
+	src/hardware/pipistrello-ols/protocol.c \
+	src/hardware/pipistrello-ols/api.c
 endif
 if HW_RIGOL_DS
 libsigrok_la_SOURCES += \
-	hardware/rigol-ds/protocol.h \
-	hardware/rigol-ds/protocol.c \
-	hardware/rigol-ds/api.c
+	src/hardware/rigol-ds/protocol.h \
+	src/hardware/rigol-ds/protocol.c \
+	src/hardware/rigol-ds/api.c
 endif
 if HW_SALEAE_LOGIC16
 libsigrok_la_SOURCES += \
-	hardware/saleae-logic16/protocol.h \
-	hardware/saleae-logic16/protocol.c \
-	hardware/saleae-logic16/api.c
+	src/hardware/saleae-logic16/protocol.h \
+	src/hardware/saleae-logic16/protocol.c \
+	src/hardware/saleae-logic16/api.c
+endif
+if HW_SCPI_PPS
+libsigrok_la_SOURCES += \
+	src/hardware/scpi-pps/protocol.h \
+	src/hardware/scpi-pps/protocol.c \
+	src/hardware/scpi-pps/profiles.c \
+	src/hardware/scpi-pps/api.c
 endif
 if HW_SERIAL_DMM
 libsigrok_la_SOURCES += \
-	hardware/serial-dmm/protocol.h \
-	hardware/serial-dmm/protocol.c \
-	hardware/serial-dmm/api.c
+	src/hardware/serial-dmm/protocol.h \
+	src/hardware/serial-dmm/protocol.c \
+	src/hardware/serial-dmm/api.c
 endif
 if HW_SYSCLK_LWLA
 libsigrok_la_SOURCES += \
-	hardware/sysclk-lwla/lwla.h \
-	hardware/sysclk-lwla/lwla.c \
-	hardware/sysclk-lwla/protocol.h \
-	hardware/sysclk-lwla/protocol.c \
-	hardware/sysclk-lwla/api.c
+	src/hardware/sysclk-lwla/lwla.h \
+	src/hardware/sysclk-lwla/lwla.c \
+	src/hardware/sysclk-lwla/lwla1016.c \
+	src/hardware/sysclk-lwla/lwla1034.c \
+	src/hardware/sysclk-lwla/protocol.h \
+	src/hardware/sysclk-lwla/protocol.c \
+	src/hardware/sysclk-lwla/api.c
 endif
 if HW_TELEINFO
 libsigrok_la_SOURCES += \
-	hardware/teleinfo/protocol.h \
-	hardware/teleinfo/protocol.c \
-	hardware/teleinfo/api.c
+	src/hardware/teleinfo/protocol.h \
+	src/hardware/teleinfo/protocol.c \
+	src/hardware/teleinfo/api.c
+endif
+if HW_TESTO
+libsigrok_la_SOURCES += \
+	src/hardware/testo/protocol.h \
+	src/hardware/testo/protocol.c \
+	src/hardware/testo/api.c
 endif
 if HW_TONDAJ_SL_814
 libsigrok_la_SOURCES += \
-	hardware/tondaj-sl-814/protocol.h \
-	hardware/tondaj-sl-814/protocol.c \
-	hardware/tondaj-sl-814/api.c
+	src/hardware/tondaj-sl-814/protocol.h \
+	src/hardware/tondaj-sl-814/protocol.c \
+	src/hardware/tondaj-sl-814/api.c
 endif
 if HW_UNI_T_DMM
 libsigrok_la_SOURCES += \
-	hardware/uni-t-dmm/protocol.h \
-	hardware/uni-t-dmm/protocol.c \
-	hardware/uni-t-dmm/api.c
+	src/hardware/uni-t-dmm/protocol.h \
+	src/hardware/uni-t-dmm/protocol.c \
+	src/hardware/uni-t-dmm/api.c
 endif
 if HW_UNI_T_UT32X
 libsigrok_la_SOURCES += \
-	hardware/uni-t-ut32x/protocol.h \
-	hardware/uni-t-ut32x/protocol.c \
-	hardware/uni-t-ut32x/api.c
+	src/hardware/uni-t-ut32x/protocol.h \
+	src/hardware/uni-t-ut32x/protocol.c \
+	src/hardware/uni-t-ut32x/api.c
 endif
 if HW_VICTOR_DMM
 libsigrok_la_SOURCES += \
-	hardware/victor-dmm/protocol.h \
-	hardware/victor-dmm/protocol.c \
-	hardware/victor-dmm/api.c
+	src/hardware/victor-dmm/protocol.h \
+	src/hardware/victor-dmm/protocol.c \
+	src/hardware/victor-dmm/api.c
+endif
+if HW_YOKOGAWA_DLM
+libsigrok_la_SOURCES += \
+	src/hardware/yokogawa-dlm/protocol.h \
+	src/hardware/yokogawa-dlm/protocol.c \
+	src/hardware/yokogawa-dlm/protocol_wrappers.h \
+	src/hardware/yokogawa-dlm/protocol_wrappers.c \
+	src/hardware/yokogawa-dlm/api.c
 endif
 if HW_ZEROPLUS_LOGIC_CUBE
 libsigrok_la_SOURCES += \
-	hardware/zeroplus-logic-cube/analyzer.c \
-	hardware/zeroplus-logic-cube/analyzer.h \
-	hardware/zeroplus-logic-cube/gl_usb.h \
-	hardware/zeroplus-logic-cube/gl_usb.c \
-	hardware/zeroplus-logic-cube/protocol.h \
-	hardware/zeroplus-logic-cube/protocol.c \
-	hardware/zeroplus-logic-cube/api.c
+	src/hardware/zeroplus-logic-cube/analyzer.c \
+	src/hardware/zeroplus-logic-cube/analyzer.h \
+	src/hardware/zeroplus-logic-cube/gl_usb.h \
+	src/hardware/zeroplus-logic-cube/gl_usb.c \
+	src/hardware/zeroplus-logic-cube/protocol.h \
+	src/hardware/zeroplus-logic-cube/protocol.c \
+	src/hardware/zeroplus-logic-cube/api.c
 endif
 
-libsigrok_la_LIBADD = $(LIBOBJS)
-
-libsigrok_la_LDFLAGS = $(SR_LIB_LDFLAGS)
+libsigrok_la_LIBADD = $(SR_EXTRA_LIBS) $(LIBSIGROK_LIBS)
+libsigrok_la_LDFLAGS = -version-info $(SR_LIB_VERSION) -no-undefined
 
 library_includedir = $(includedir)/libsigrok
-library_include_HEADERS = libsigrok.h proto.h version.h
-noinst_HEADERS = libsigrok-internal.h
+library_include_HEADERS = \
+	include/libsigrok/libsigrok.h \
+	include/libsigrok/proto.h
+nodist_library_include_HEADERS = \
+	include/libsigrok/version.h
+noinst_HEADERS = src/libsigrok-internal.h
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libsigrok.pc
@@ -322,6 +493,25 @@ EXTRA_DIST = \
 	Doxyfile \
 	HACKING \
 	README.devices \
+	bindings/cxx/ConfigKey_methods.cpp \
+	bindings/cxx/ConfigKey_methods.hpp \
+	bindings/cxx/ConfigKey_methods.i \
+	bindings/cxx/Doxyfile \
+	bindings/cxx/QuantityFlag_methods.cpp \
+	bindings/cxx/QuantityFlag_methods.hpp \
+	bindings/cxx/enums.py \
+	bindings/python/Doxyfile \
+	bindings/python/setup.py \
+	bindings/python/sigrok/__init__.py \
+	bindings/python/sigrok/core/__init__.py \
+	bindings/python/sigrok/core/classes.i \
+	bindings/java/Doxyfile \
+	bindings/java/org/sigrok/core/classes/classes.i \
+	bindings/java/org/sigrok/core/interfaces/DatafeedCallback.java \
+	bindings/java/org/sigrok/core/interfaces/LogCallback.java \
+	bindings/swig/classes.i \
+	bindings/swig/doc.py \
+	bindings/swig/templates.i \
 	contrib/gnuplot_chronovu_la8.gpi \
 	contrib/gnuplot_rigol_ds1xx2.gpi \
 	contrib/gnuplot_usbeesx.gpi \
@@ -331,35 +521,267 @@ EXTRA_DIST = \
 	contrib/z60_libsigrok.rules
 
 if HAVE_CHECK
-
-TESTS = tests/check_main
-
+TESTS = tests/main
 check_PROGRAMS = ${TESTS}
+endif
 
-tests_check_main_SOURCES = \
-	libsigrok.h \
+tests_main_SOURCES = \
+	include/libsigrok/libsigrok.h \
 	tests/lib.c \
 	tests/lib.h \
-	tests/check_main.c \
-	tests/check_core.c \
-	tests/check_input_all.c \
-	tests/check_input_binary.c \
-	tests/check_output_all.c \
-	tests/check_strutil.c \
-	tests/check_version.c \
-	tests/check_driver_all.c
+	tests/main.c \
+	tests/core.c \
+	tests/input_all.c \
+	tests/input_binary.c \
+	tests/output_all.c \
+	tests/transform_all.c \
+	tests/session.c \
+	tests/strutil.c \
+	tests/version.c \
+	tests/driver_all.c \
+	tests/device.c \
+	tests/trigger.c \
+	tests/analog.c
+
+tests_main_LDADD = libsigrok.la $(SR_EXTRA_LIBS) $(TESTS_LIBS)
 
-tests_check_main_CFLAGS = @check_CFLAGS@
+BUILD_EXTRA =
+INSTALL_EXTRA =
+UNINSTALL_EXTRA =
+CLEAN_EXTRA =
 
-tests_check_main_LDADD = $(top_builddir)/libsigrok.la @check_LIBS@
+if BINDINGS_CXX
+
+lib_LTLIBRARIES += bindings/cxx/libsigrokcxx.la
+
+bindings_cxx_libsigrokcxx_la_SOURCES = bindings/cxx/classes.cpp
+
+bindings_cxx_libsigrokcxx_la_LIBADD = libsigrok.la $(SR_EXTRA_LIBS) $(LIBSIGROKCXX_LIBS)
+bindings_cxx_libsigrokcxx_la_LDFLAGS = -version-info $(SR_LIB_VERSION) -no-undefined
+
+bindings_cxx_libsigrokcxx_la_includedir = $(includedir)/libsigrokcxx
+bindings_cxx_libsigrokcxx_la_include_HEADERS = \
+	bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp
+nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS = \
+	bindings/cxx/include/libsigrokcxx/enums.hpp
+
+pkgconfig_DATA += bindings/cxx/libsigrokcxx.pc
+
+doxy/xml/index.xml: include/libsigrok/libsigrok.h
+	$(AM_V_GEN)cd $(srcdir) && BUILDDIR=$(abs_builddir)/ doxygen Doxyfile 2>/dev/null
+
+bindings/swig/enums.i: bindings/cxx/enums.timestamp
+bindings/cxx/enums.cpp: bindings/cxx/enums.timestamp
+bindings/cxx/include/libsigrokcxx/enums.hpp: bindings/cxx/enums.timestamp
+
+bindings/cxx/enums.timestamp: $(srcdir)/bindings/cxx/enums.py doxy/xml/index.xml \
+		bindings/cxx/ConfigKey_methods.cpp bindings/cxx/QuantityFlag_methods.cpp
+	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/cxx/enums.py doxy/xml/index.xml
+	$(AM_V_at)touch $@
+
+bindings/cxx/classes.lo: bindings/cxx/classes.cpp bindings/cxx/enums.cpp \
+		$(library_include_HEADERS) $(nodist_library_include_HEADERS)
+
+cxx-clean:
+	rm -rf doxy/
+	rm -rf bindings/cxx/doxy/
+	rm -f bindings/swig/enums.i
+	rm -f bindings/cxx/enums.cpp
+	rm -f bindings/cxx/include/libsigrokcxx/enums.hpp
+	rm -f bindings/cxx/enums.timestamp
+
+CLEAN_EXTRA += cxx-clean
+
+endif
+
+CPPXMLDOC = bindings/cxx/doxy/xml/index.xml
+
+$(CPPXMLDOC): bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp \
+		bindings/cxx/enums.timestamp
+	$(AM_V_GEN)cd $(srcdir)/bindings/cxx && BUILDDIR=$(abs_builddir)/bindings/cxx/ doxygen Doxyfile 2>/dev/null
+
+# Macro definitions to be used by the SWIG parser.
+swig_defs = -Dnoexcept= -Dprivate=protected -DG_GNUC_BEGIN_IGNORE_DEPRECATIONS= -DG_GNUC_END_IGNORE_DEPRECATIONS=
+
+if BINDINGS_PYTHON
+
+PDIR = bindings/python
+PDOC_START = bindings/python/sigrok/core/doc_start.i
+PDOC_END = bindings/python/sigrok/core/doc_end.i
+
+setup_vars = VERSION='$(PACKAGE_VERSION)' CC='$(CXX)' CFLAGS='$(CXXFLAGS) $(SR_WXXFLAGS) $(PYSIGROK_CFLAGS)' CXXFLAGS='$(CXXFLAGS) $(SR_WXXFLAGS) $(PYSIGROK_CFLAGS)' LDADD='$(PYSIGROK_LIBS)'
+setup_quiet = --quiet
+setup_py = $(PYTHON) $(srcdir)/$(PDIR)/setup.py $(setup_vars) $(setup_quiet)
+
+$(PDOC_START): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC)
+	$(AM_V_at)test -d $(PDIR)/sigrok/core || $(MKDIR_P) $(PDIR)/sigrok/core
+	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py python $(CPPXMLDOC) start > $@
+
+$(PDOC_END): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC)
+	$(AM_V_at)test -d $(PDIR)/sigrok/core || $(MKDIR_P) $(PDIR)/sigrok/core
+	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py python $(CPPXMLDOC) end > $@
+
+python-build: $(PDIR)/timestamp
+
+$(PDIR)/timestamp: $(PDIR)/sigrok/core/classes.i \
+		bindings/swig/classes.i bindings/swig/templates.i \
+		bindings/swig/enums.i $(PDOC_START) $(PDOC_END) \
+		$(library_include_HEADERS) \
+		$(nodist_library_include_HEADERS) \
+		$(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+		$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+		@ORDER@ bindings/cxx/libsigrokcxx.la
+	$(AM_V_at)$(setup_py) clean --all 2>/dev/null
+	$(AM_V_GEN)$(setup_py) build_ext --swig "$(SWIG)" --swig-opts '$(swig_defs)' build_py
+	$(AM_V_at): >$@
+
+python-install:
+	$(AM_V_at)$(MKDIR_P) "$(DESTDIR)$(prefix)" "$(DESTDIR)$(exec_prefix)"
+	destdir='$(DESTDIR)'; $(setup_py) install $${destdir:+"--root=$$destdir"} \
+		--prefix "$(prefix)" --exec-prefix "$(exec_prefix)"
+
+python-clean:
+	-$(AM_V_at)rm -f $(PDIR)/timestamp
+	-$(AM_V_at)rm -fr $(PDIR)/doxy
+	-$(AM_V_at)$(setup_py) clean --all 2>/dev/null
+
+python-doc:
+	$(AM_V_at)cd $(srcdir)/$(PDIR) && BUILDDIR="$(abs_builddir)/$(PDIR)/" doxygen Doxyfile 2>/dev/null
+
+BUILD_EXTRA += python-build
+INSTALL_EXTRA += python-install
+CLEAN_EXTRA += python-clean
+
+endif
+
+if BINDINGS_RUBY
+
+RDIR = bindings/ruby
+RDOC = $(RDIR)/doc.i
+RWRAP = $(RDIR)/classes_wrap.cpp
+ROBJ = $(RWRAP:.cpp=.o)
+REXT = $(RDIR)/sigrok.$(RUBY_DLEXT)
+
+$(RDOC): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC)
+	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py ruby $(CPPXMLDOC) > $@
+
+$(RWRAP): $(RDIR)/classes.i $(RDOC) \
+		bindings/swig/classes.i bindings/swig/templates.i \
+		bindings/swig/enums.i
+	$(AM_V_GEN)$(SWIG) -ruby -c++ -Ibindings -Ibindings/cxx/include $(swig_defs) -o $@ $<
+
+$(ROBJ): $(RWRAP) \
+		$(library_include_HEADERS) \
+		$(nodist_library_include_HEADERS) \
+		$(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+		$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS)
+	$(AM_V_CXX)$(CXX) $(RBSIGROK_CFLAGS) -I. -Iinclude -Ibindings/cxx/include -fPIC -o $@ -c $<
+
+$(REXT): $(ROBJ) @ORDER@ bindings/cxx/libsigrokcxx.la
+	$(AM_V_CXXLD)$(CXX) -shared -std=c++11 -o $@ $< -lsigrokcxx -Lbindings/cxx/.libs $(RBSIGROK_LIBS)
+
+ruby-build: $(REXT)
+
+ruby-install: $(REXT)
+	$(INSTALL) -d $(DESTDIR)$(prefix)/$(RBSIGROK_EXTDIR)
+	$(INSTALL) $< $(DESTDIR)$(prefix)/$(RBSIGROK_EXTDIR)
+
+ruby-clean:
+	-$(AM_V_at)rm -fr $(RDIR)/doc
+	-$(AM_V_at)rm $(REXT) $(ROBJ) $(RWRAP) $(RDOC)
+
+ruby-doc: $(RWRAP)
+	$(AM_V_at)yard doc -o $(RDIR)/doc $<
+
+BUILD_EXTRA += ruby-build
+INSTALL_EXTRA += ruby-install
+CLEAN_EXTRA += ruby-clean
+
+endif
+
+if BINDINGS_JAVA
+
+JDIR = bindings/java
+JPKG = org/sigrok/core
+JCLS = $(JDIR)/$(JPKG)/classes
+JINT = $(JDIR)/$(JPKG)/interfaces
+JSRC = $(JCLS)/*.java $(srcdir)/$(JINT)/*.java
+JSWG = $(JCLS)/classes.i
+JDOC = $(JCLS)/doc.i
+JCXX = $(JCLS)/classes_wrap.cxx
+JLIB = $(JDIR)/libsigrok_java_core_classes.so
+JJAR = $(JDIR)/sigrok-core.jar
+
+java_cleanfiles = $(JCXX) $(JCLS)/*.java $(JCLS)/*.class $(JINT)/*.class $(JJAR) $(JLIB)
+
+java-build: $(JJAR) $(JLIB)
+
+$(JDOC): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC)
+	$(AM_V_at)test -d $(JCLS) || $(MKDIR_P) $(JCLS)
+	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py java $(CPPXMLDOC) > $@
+
+$(JCXX): $(srcdir)/$(JSWG) $(JDOC) bindings/swig/classes.i \
+		bindings/swig/templates.i bindings/swig/enums.i \
+		$(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+		$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS)
+	-$(AM_V_at)rm -f $(java_cleanfiles)
+	$(AM_V_GEN)$(SWIG) -c++ $(swig_defs) \
+		-java -package org.sigrok.core.classes \
+		-Ibindings -I$(JCLS) $(local_includes) -I$(srcdir) $(JNI_CPPFLAGS) \
+		-outdir $(JCLS) -o $@ $(srcdir)/$(JSWG)
+
+$(JJAR): $(JCXX)
+	$(AM_V_GEN)$(JAVAC) -d $(JDIR) $(JSRC)
+	$(AM_V_at)jar cf $(JJAR) -C $(JDIR) $(JPKG)
+
+$(JLIB): $(JCXX) \
+		$(library_include_HEADERS) $(nodist_library_include_HEADERS) \
+		$(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+		$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+		@ORDER@ bindings/cxx/libsigrokcxx.la
+	$(AM_V_GEN)$(CXXCOMPILE) $(JNI_CPPFLAGS) -L.libs -Lbindings/cxx/.libs \
+		-fno-strict-aliasing -fPIC -shared $(JCLS)/classes_wrap.cxx \
+		-lsigrokcxx $(LIBSIGROKCXX_LIBS) -o $(JLIB)
+
+java-install:
+	$(INSTALL) -d $(DESTDIR)$(libdir)/jni
+	$(INSTALL) $(JLIB) $(DESTDIR)$(libdir)/jni
+	$(INSTALL) -d $(DESTDIR)$(datadir)/java
+	$(INSTALL) $(JJAR) $(DESTDIR)$(datadir)/java
+
+java-uninstall:
+	-rm -f $(DESTDIR)$(datadir)/java/sigrok-core.jar
+	-rm -f $(DESTDIR)$(libdir)/jni/libsigrok_java_core_classes.so
+
+java-clean:
+	-$(AM_V_at)rm -f $(java_cleanfiles) $(JDOC)
+	-$(AM_V_at)rm -fr $(JDIR)/doxy
+
+java-doc:
+	$(AM_V_at)cd $(srcdir)/$(JDIR) && BUILDDIR="$(abs_builddir)/$(JDIR)/" doxygen Doxyfile
+
+BUILD_EXTRA += java-build
+INSTALL_EXTRA += java-install
+UNINSTALL_EXTRA += java-uninstall
+CLEAN_EXTRA += java-clean
 
 endif
 
-MAINTAINERCLEANFILES = ChangeLog
+all-local: $(BUILD_EXTRA)
+install-exec-local: $(INSTALL_EXTRA)
+uninstall-local: $(UNINSTALL_EXTRA)
+clean-local: $(CLEAN_EXTRA)
+
+.PHONY: dist-changelog
 
-.PHONY: ChangeLog
-ChangeLog:
-	git --git-dir $(top_srcdir)/.git log > ChangeLog || touch ChangeLog
+dist-hook: dist-changelog
 
-dist-hook: ChangeLog
+dist-changelog:
+	$(AM_V_at)if test ! -d '$(top_srcdir)/.git'; then \
+		cp -f '$(top_srcdir)/ChangeLog' "$(top_distdir)/ChangeLog"; \
+	elif git -C '$(top_srcdir)' log >.ChangeLog.tmp; then \
+		mv -f .ChangeLog.tmp "$(top_distdir)/ChangeLog"; \
+	else \
+		rm -f .ChangeLog.tmp; exit 1; \
+	fi
 
diff --git a/Makefile.in b/Makefile.in
index 2bb50f2..2919625 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -17,7 +17,17 @@
 
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -80,233 +90,330 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
- at NEED_RPC_TRUE@am__append_1 = \
- at NEED_RPC_TRUE@	hardware/common/scpi_vxi.c \
- at NEED_RPC_TRUE@	hardware/common/vxi_clnt.c \
- at NEED_RPC_TRUE@	hardware/common/vxi_xdr.c \
- at NEED_RPC_TRUE@	hardware/common/vxi.h
+ at BINDINGS_CXX_TRUE@am__append_1 = -Ibindings/cxx/include -I$(srcdir)/bindings/cxx/include -Ibindings/cxx
+ at NEED_RPC_TRUE@am__append_2 = \
+ at NEED_RPC_TRUE@	src/scpi/scpi_vxi.c \
+ at NEED_RPC_TRUE@	src/scpi/vxi_clnt.c \
+ at NEED_RPC_TRUE@	src/scpi/vxi_xdr.c \
+ at NEED_RPC_TRUE@	src/scpi/vxi.h
+
+ at NEED_SERIAL_TRUE@am__append_3 = \
+ at NEED_SERIAL_TRUE@	src/serial.c \
+ at NEED_SERIAL_TRUE@	src/scpi/scpi_serial.c
+
+ at NEED_USB_TRUE@am__append_4 = \
+ at NEED_USB_TRUE@	src/ezusb.c \
+ at NEED_USB_TRUE@	src/usb.c \
+ at NEED_USB_TRUE@	src/scpi/scpi_usbtmc_libusb.c
 
- at NEED_SERIAL_TRUE@am__append_2 = \
- at NEED_SERIAL_TRUE@	hardware/common/serial.c \
- at NEED_SERIAL_TRUE@	hardware/common/scpi_serial.c
+ at NEED_VISA_TRUE@am__append_5 = \
+ at NEED_VISA_TRUE@	src/scpi/scpi_visa.c
 
- at NEED_USB_TRUE@am__append_3 = \
- at NEED_USB_TRUE@	hardware/common/ezusb.c \
- at NEED_USB_TRUE@	hardware/common/usb.c \
- at NEED_USB_TRUE@	hardware/common/scpi_usbtmc_libusb.c
+ at NEED_GPIB_TRUE@am__append_6 = \
+ at NEED_GPIB_TRUE@	src/scpi/scpi_libgpib.c
 
- at NEED_VISA_TRUE@am__append_4 = \
- at NEED_VISA_TRUE@	hardware/common/scpi_visa.c
+ at NEED_SERIAL_TRUE@am__append_7 = \
+ at NEED_SERIAL_TRUE@	src/modbus/modbus_serial_rtu.c
+
+
+# Hardware (LCR chip parsers)
+ at NEED_SERIAL_TRUE@am__append_8 = \
+ at NEED_SERIAL_TRUE@	src/lcr/es51919.c
 
 
 # Hardware drivers
- at HW_AGILENT_DMM_TRUE@am__append_5 = \
- at HW_AGILENT_DMM_TRUE@	hardware/agilent-dmm/api.c \
- at HW_AGILENT_DMM_TRUE@	hardware/agilent-dmm/agilent-dmm.h \
- at HW_AGILENT_DMM_TRUE@	hardware/agilent-dmm/sched.c
-
- at HW_APPA_55II_TRUE@am__append_6 = \
- at HW_APPA_55II_TRUE@	hardware/appa-55ii/protocol.h \
- at HW_APPA_55II_TRUE@	hardware/appa-55ii/protocol.c \
- at HW_APPA_55II_TRUE@	hardware/appa-55ii/api.c
-
- at HW_ASIX_SIGMA_TRUE@am__append_7 = \
- at HW_ASIX_SIGMA_TRUE@	hardware/asix-sigma/asix-sigma.h \
- at HW_ASIX_SIGMA_TRUE@	hardware/asix-sigma/asix-sigma.c
-
- at HW_ATTEN_PPS3XXX_TRUE@am__append_8 = \
- at HW_ATTEN_PPS3XXX_TRUE@	hardware/atten-pps3xxx/protocol.h \
- at HW_ATTEN_PPS3XXX_TRUE@	hardware/atten-pps3xxx/protocol.c \
- at HW_ATTEN_PPS3XXX_TRUE@	hardware/atten-pps3xxx/api.c
-
- at HW_BRYMEN_BM86X_TRUE@am__append_9 = \
- at HW_BRYMEN_BM86X_TRUE@	hardware/brymen-bm86x/protocol.h \
- at HW_BRYMEN_BM86X_TRUE@	hardware/brymen-bm86x/protocol.c \
- at HW_BRYMEN_BM86X_TRUE@	hardware/brymen-bm86x/api.c
-
- at HW_BRYMEN_DMM_TRUE@am__append_10 = \
- at HW_BRYMEN_DMM_TRUE@	hardware/brymen-dmm/parser.c \
- at HW_BRYMEN_DMM_TRUE@	hardware/brymen-dmm/protocol.h \
- at HW_BRYMEN_DMM_TRUE@	hardware/brymen-dmm/protocol.c \
- at HW_BRYMEN_DMM_TRUE@	hardware/brymen-dmm/api.c
-
- at HW_CEM_DT_885X_TRUE@am__append_11 = \
- at HW_CEM_DT_885X_TRUE@	hardware/cem-dt-885x/protocol.h \
- at HW_CEM_DT_885X_TRUE@	hardware/cem-dt-885x/protocol.c \
- at HW_CEM_DT_885X_TRUE@	hardware/cem-dt-885x/api.c
-
- at HW_CENTER_3XX_TRUE@am__append_12 = \
- at HW_CENTER_3XX_TRUE@	hardware/center-3xx/protocol.h \
- at HW_CENTER_3XX_TRUE@	hardware/center-3xx/protocol.c \
- at HW_CENTER_3XX_TRUE@	hardware/center-3xx/api.c
-
- at HW_CHRONOVU_LA_TRUE@am__append_13 = \
- at HW_CHRONOVU_LA_TRUE@	hardware/chronovu-la/protocol.h \
- at HW_CHRONOVU_LA_TRUE@	hardware/chronovu-la/protocol.c \
- at HW_CHRONOVU_LA_TRUE@	hardware/chronovu-la/api.c
-
- at HW_COLEAD_SLM_TRUE@am__append_14 = \
- at HW_COLEAD_SLM_TRUE@	hardware/colead-slm/protocol.h \
- at HW_COLEAD_SLM_TRUE@	hardware/colead-slm/protocol.c \
- at HW_COLEAD_SLM_TRUE@	hardware/colead-slm/api.c
-
- at HW_CONRAD_DIGI_35_CPU_TRUE@am__append_15 = \
- at HW_CONRAD_DIGI_35_CPU_TRUE@	hardware/conrad-digi-35-cpu/protocol.h \
- at HW_CONRAD_DIGI_35_CPU_TRUE@	hardware/conrad-digi-35-cpu/protocol.c \
- at HW_CONRAD_DIGI_35_CPU_TRUE@	hardware/conrad-digi-35-cpu/api.c
-
- at HW_DEMO_TRUE@am__append_16 = \
- at HW_DEMO_TRUE@	hardware/demo/demo.c
-
- at HW_FLUKE_DMM_TRUE@am__append_17 = \
- at HW_FLUKE_DMM_TRUE@	hardware/fluke-dmm/fluke-dmm.h \
- at HW_FLUKE_DMM_TRUE@	hardware/fluke-dmm/fluke.c \
- at HW_FLUKE_DMM_TRUE@	hardware/fluke-dmm/api.c
-
- at HW_FX2LAFW_TRUE@am__append_18 = \
- at HW_FX2LAFW_TRUE@	hardware/fx2lafw/protocol.h \
- at HW_FX2LAFW_TRUE@	hardware/fx2lafw/protocol.c \
- at HW_FX2LAFW_TRUE@	hardware/fx2lafw/api.c
-
- at HW_GMC_MH_1X_2X_TRUE@am__append_19 = \
- at HW_GMC_MH_1X_2X_TRUE@	hardware/gmc-mh-1x-2x/protocol.h \
- at HW_GMC_MH_1X_2X_TRUE@	hardware/gmc-mh-1x-2x/protocol.c \
- at HW_GMC_MH_1X_2X_TRUE@	hardware/gmc-mh-1x-2x/api.c
-
- at HW_HAMEG_HMO_TRUE@am__append_20 = \
- at HW_HAMEG_HMO_TRUE@	hardware/hameg-hmo/protocol.h \
- at HW_HAMEG_HMO_TRUE@	hardware/hameg-hmo/protocol.c \
- at HW_HAMEG_HMO_TRUE@	hardware/hameg-hmo/api.c
-
- at HW_HANTEK_DSO_TRUE@am__append_21 = \
- at HW_HANTEK_DSO_TRUE@	hardware/hantek-dso/dso.h \
- at HW_HANTEK_DSO_TRUE@	hardware/hantek-dso/dso.c \
- at HW_HANTEK_DSO_TRUE@	hardware/hantek-dso/api.c
-
- at HW_IKALOGIC_SCANALOGIC2_TRUE@am__append_22 = \
- at HW_IKALOGIC_SCANALOGIC2_TRUE@	hardware/ikalogic-scanalogic2/protocol.h \
- at HW_IKALOGIC_SCANALOGIC2_TRUE@	hardware/ikalogic-scanalogic2/protocol.c \
- at HW_IKALOGIC_SCANALOGIC2_TRUE@	hardware/ikalogic-scanalogic2/api.c
-
- at HW_IKALOGIC_SCANAPLUS_TRUE@am__append_23 = \
- at HW_IKALOGIC_SCANAPLUS_TRUE@	hardware/ikalogic-scanaplus/protocol.h \
- at HW_IKALOGIC_SCANAPLUS_TRUE@	hardware/ikalogic-scanaplus/protocol.c \
- at HW_IKALOGIC_SCANAPLUS_TRUE@	hardware/ikalogic-scanaplus/api.c
-
- at HW_KECHENG_KC_330B_TRUE@am__append_24 = \
- at HW_KECHENG_KC_330B_TRUE@	hardware/kecheng-kc-330b/protocol.h \
- at HW_KECHENG_KC_330B_TRUE@	hardware/kecheng-kc-330b/protocol.c \
- at HW_KECHENG_KC_330B_TRUE@	hardware/kecheng-kc-330b/api.c
-
- at HW_LASCAR_EL_USB_TRUE@am__append_25 = \
- at HW_LASCAR_EL_USB_TRUE@	hardware/lascar-el-usb/protocol.h \
- at HW_LASCAR_EL_USB_TRUE@	hardware/lascar-el-usb/protocol.c \
- at HW_LASCAR_EL_USB_TRUE@	hardware/lascar-el-usb/api.c
-
- at HW_MIC_985XX_TRUE@am__append_26 = \
- at HW_MIC_985XX_TRUE@	hardware/mic-985xx/protocol.h \
- at HW_MIC_985XX_TRUE@	hardware/mic-985xx/protocol.c \
- at HW_MIC_985XX_TRUE@	hardware/mic-985xx/api.c
-
- at HW_NORMA_DMM_TRUE@am__append_27 = \
- at HW_NORMA_DMM_TRUE@	hardware/norma-dmm/protocol.h \
- at HW_NORMA_DMM_TRUE@	hardware/norma-dmm/protocol.c \
- at HW_NORMA_DMM_TRUE@	hardware/norma-dmm/api.c
-
- at HW_OLS_TRUE@am__append_28 = \
- at HW_OLS_TRUE@	hardware/openbench-logic-sniffer/protocol.h \
- at HW_OLS_TRUE@	hardware/openbench-logic-sniffer/protocol.c \
- at HW_OLS_TRUE@	hardware/openbench-logic-sniffer/api.c
-
- at HW_RIGOL_DS_TRUE@am__append_29 = \
- at HW_RIGOL_DS_TRUE@	hardware/rigol-ds/protocol.h \
- at HW_RIGOL_DS_TRUE@	hardware/rigol-ds/protocol.c \
- at HW_RIGOL_DS_TRUE@	hardware/rigol-ds/api.c
-
- at HW_SALEAE_LOGIC16_TRUE@am__append_30 = \
- at HW_SALEAE_LOGIC16_TRUE@	hardware/saleae-logic16/protocol.h \
- at HW_SALEAE_LOGIC16_TRUE@	hardware/saleae-logic16/protocol.c \
- at HW_SALEAE_LOGIC16_TRUE@	hardware/saleae-logic16/api.c
-
- at HW_SERIAL_DMM_TRUE@am__append_31 = \
- at HW_SERIAL_DMM_TRUE@	hardware/serial-dmm/protocol.h \
- at HW_SERIAL_DMM_TRUE@	hardware/serial-dmm/protocol.c \
- at HW_SERIAL_DMM_TRUE@	hardware/serial-dmm/api.c
-
- at HW_SYSCLK_LWLA_TRUE@am__append_32 = \
- at HW_SYSCLK_LWLA_TRUE@	hardware/sysclk-lwla/lwla.h \
- at HW_SYSCLK_LWLA_TRUE@	hardware/sysclk-lwla/lwla.c \
- at HW_SYSCLK_LWLA_TRUE@	hardware/sysclk-lwla/protocol.h \
- at HW_SYSCLK_LWLA_TRUE@	hardware/sysclk-lwla/protocol.c \
- at HW_SYSCLK_LWLA_TRUE@	hardware/sysclk-lwla/api.c
-
- at HW_TELEINFO_TRUE@am__append_33 = \
- at HW_TELEINFO_TRUE@	hardware/teleinfo/protocol.h \
- at HW_TELEINFO_TRUE@	hardware/teleinfo/protocol.c \
- at HW_TELEINFO_TRUE@	hardware/teleinfo/api.c
-
- at HW_TONDAJ_SL_814_TRUE@am__append_34 = \
- at HW_TONDAJ_SL_814_TRUE@	hardware/tondaj-sl-814/protocol.h \
- at HW_TONDAJ_SL_814_TRUE@	hardware/tondaj-sl-814/protocol.c \
- at HW_TONDAJ_SL_814_TRUE@	hardware/tondaj-sl-814/api.c
-
- at HW_UNI_T_DMM_TRUE@am__append_35 = \
- at HW_UNI_T_DMM_TRUE@	hardware/uni-t-dmm/protocol.h \
- at HW_UNI_T_DMM_TRUE@	hardware/uni-t-dmm/protocol.c \
- at HW_UNI_T_DMM_TRUE@	hardware/uni-t-dmm/api.c
-
- at HW_UNI_T_UT32X_TRUE@am__append_36 = \
- at HW_UNI_T_UT32X_TRUE@	hardware/uni-t-ut32x/protocol.h \
- at HW_UNI_T_UT32X_TRUE@	hardware/uni-t-ut32x/protocol.c \
- at HW_UNI_T_UT32X_TRUE@	hardware/uni-t-ut32x/api.c
-
- at HW_VICTOR_DMM_TRUE@am__append_37 = \
- at HW_VICTOR_DMM_TRUE@	hardware/victor-dmm/protocol.h \
- at HW_VICTOR_DMM_TRUE@	hardware/victor-dmm/protocol.c \
- at HW_VICTOR_DMM_TRUE@	hardware/victor-dmm/api.c
-
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@am__append_38 = \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/analyzer.c \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/analyzer.h \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/gl_usb.h \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/gl_usb.c \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/protocol.h \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/protocol.c \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/api.c
-
- at HAVE_CHECK_TRUE@TESTS = tests/check_main$(EXEEXT)
+ at HW_AGILENT_DMM_TRUE@am__append_9 = \
+ at HW_AGILENT_DMM_TRUE@	src/hardware/agilent-dmm/api.c \
+ at HW_AGILENT_DMM_TRUE@	src/hardware/agilent-dmm/agilent-dmm.h \
+ at HW_AGILENT_DMM_TRUE@	src/hardware/agilent-dmm/sched.c
+
+ at HW_APPA_55II_TRUE@am__append_10 = \
+ at HW_APPA_55II_TRUE@	src/hardware/appa-55ii/protocol.h \
+ at HW_APPA_55II_TRUE@	src/hardware/appa-55ii/protocol.c \
+ at HW_APPA_55II_TRUE@	src/hardware/appa-55ii/api.c
+
+ at HW_ASIX_SIGMA_TRUE@am__append_11 = \
+ at HW_ASIX_SIGMA_TRUE@	src/hardware/asix-sigma/protocol.h \
+ at HW_ASIX_SIGMA_TRUE@	src/hardware/asix-sigma/protocol.c \
+ at HW_ASIX_SIGMA_TRUE@	src/hardware/asix-sigma/api.c
+
+ at HW_ATTEN_PPS3XXX_TRUE@am__append_12 = \
+ at HW_ATTEN_PPS3XXX_TRUE@	src/hardware/atten-pps3xxx/protocol.h \
+ at HW_ATTEN_PPS3XXX_TRUE@	src/hardware/atten-pps3xxx/protocol.c \
+ at HW_ATTEN_PPS3XXX_TRUE@	src/hardware/atten-pps3xxx/api.c
+
+ at HW_BAYLIBRE_ACME_TRUE@am__append_13 = \
+ at HW_BAYLIBRE_ACME_TRUE@	src/hardware/baylibre-acme/protocol.h \
+ at HW_BAYLIBRE_ACME_TRUE@	src/hardware/baylibre-acme/protocol.c \
+ at HW_BAYLIBRE_ACME_TRUE@	src/hardware/baylibre-acme/api.c \
+ at HW_BAYLIBRE_ACME_TRUE@	src/hardware/baylibre-acme/gpio.h \
+ at HW_BAYLIBRE_ACME_TRUE@	src/hardware/baylibre-acme/gpio.c
+
+ at HW_BEAGLELOGIC_TRUE@am__append_14 = \
+ at HW_BEAGLELOGIC_TRUE@	src/hardware/beaglelogic/beaglelogic.h \
+ at HW_BEAGLELOGIC_TRUE@	src/hardware/beaglelogic/protocol.h \
+ at HW_BEAGLELOGIC_TRUE@	src/hardware/beaglelogic/protocol.c \
+ at HW_BEAGLELOGIC_TRUE@	src/hardware/beaglelogic/api.c
+
+ at HW_BRYMEN_BM86X_TRUE@am__append_15 = \
+ at HW_BRYMEN_BM86X_TRUE@	src/hardware/brymen-bm86x/protocol.h \
+ at HW_BRYMEN_BM86X_TRUE@	src/hardware/brymen-bm86x/protocol.c \
+ at HW_BRYMEN_BM86X_TRUE@	src/hardware/brymen-bm86x/api.c
+
+ at HW_BRYMEN_DMM_TRUE@am__append_16 = \
+ at HW_BRYMEN_DMM_TRUE@	src/hardware/brymen-dmm/parser.c \
+ at HW_BRYMEN_DMM_TRUE@	src/hardware/brymen-dmm/protocol.h \
+ at HW_BRYMEN_DMM_TRUE@	src/hardware/brymen-dmm/protocol.c \
+ at HW_BRYMEN_DMM_TRUE@	src/hardware/brymen-dmm/api.c
+
+ at HW_CEM_DT_885X_TRUE@am__append_17 = \
+ at HW_CEM_DT_885X_TRUE@	src/hardware/cem-dt-885x/protocol.h \
+ at HW_CEM_DT_885X_TRUE@	src/hardware/cem-dt-885x/protocol.c \
+ at HW_CEM_DT_885X_TRUE@	src/hardware/cem-dt-885x/api.c
+
+ at HW_CENTER_3XX_TRUE@am__append_18 = \
+ at HW_CENTER_3XX_TRUE@	src/hardware/center-3xx/protocol.h \
+ at HW_CENTER_3XX_TRUE@	src/hardware/center-3xx/protocol.c \
+ at HW_CENTER_3XX_TRUE@	src/hardware/center-3xx/api.c
+
+ at HW_CHRONOVU_LA_TRUE@am__append_19 = \
+ at HW_CHRONOVU_LA_TRUE@	src/hardware/chronovu-la/protocol.h \
+ at HW_CHRONOVU_LA_TRUE@	src/hardware/chronovu-la/protocol.c \
+ at HW_CHRONOVU_LA_TRUE@	src/hardware/chronovu-la/api.c
+
+ at HW_COLEAD_SLM_TRUE@am__append_20 = \
+ at HW_COLEAD_SLM_TRUE@	src/hardware/colead-slm/protocol.h \
+ at HW_COLEAD_SLM_TRUE@	src/hardware/colead-slm/protocol.c \
+ at HW_COLEAD_SLM_TRUE@	src/hardware/colead-slm/api.c
+
+ at HW_CONRAD_DIGI_35_CPU_TRUE@am__append_21 = \
+ at HW_CONRAD_DIGI_35_CPU_TRUE@	src/hardware/conrad-digi-35-cpu/protocol.h \
+ at HW_CONRAD_DIGI_35_CPU_TRUE@	src/hardware/conrad-digi-35-cpu/protocol.c \
+ at HW_CONRAD_DIGI_35_CPU_TRUE@	src/hardware/conrad-digi-35-cpu/api.c
+
+ at HW_DEMO_TRUE@am__append_22 = \
+ at HW_DEMO_TRUE@	src/hardware/demo/demo.c
+
+ at HW_DEREE_DE5000_TRUE@am__append_23 = \
+ at HW_DEREE_DE5000_TRUE@	src/hardware/deree-de5000/api.c
+
+ at HW_FLUKE_DMM_TRUE@am__append_24 = \
+ at HW_FLUKE_DMM_TRUE@	src/hardware/fluke-dmm/fluke-dmm.h \
+ at HW_FLUKE_DMM_TRUE@	src/hardware/fluke-dmm/fluke.c \
+ at HW_FLUKE_DMM_TRUE@	src/hardware/fluke-dmm/api.c
+
+ at HW_FX2LAFW_TRUE@am__append_25 = \
+ at HW_FX2LAFW_TRUE@	src/hardware/fx2lafw/protocol.h \
+ at HW_FX2LAFW_TRUE@	src/hardware/fx2lafw/protocol.c \
+ at HW_FX2LAFW_TRUE@	src/hardware/fx2lafw/api.c \
+ at HW_FX2LAFW_TRUE@	src/hardware/fx2lafw/dslogic.c \
+ at HW_FX2LAFW_TRUE@	src/hardware/fx2lafw/dslogic.h
+
+ at HW_GMC_MH_1X_2X_TRUE@am__append_26 = \
+ at HW_GMC_MH_1X_2X_TRUE@	src/hardware/gmc-mh-1x-2x/protocol.h \
+ at HW_GMC_MH_1X_2X_TRUE@	src/hardware/gmc-mh-1x-2x/protocol.c \
+ at HW_GMC_MH_1X_2X_TRUE@	src/hardware/gmc-mh-1x-2x/api.c
+
+ at HW_GWINSTEK_GDS_800_TRUE@am__append_27 = \
+ at HW_GWINSTEK_GDS_800_TRUE@	src/hardware/gwinstek-gds-800/protocol.h \
+ at HW_GWINSTEK_GDS_800_TRUE@	src/hardware/gwinstek-gds-800/protocol.c \
+ at HW_GWINSTEK_GDS_800_TRUE@	src/hardware/gwinstek-gds-800/api.c
+
+ at HW_HAMEG_HMO_TRUE@am__append_28 = \
+ at HW_HAMEG_HMO_TRUE@	src/hardware/hameg-hmo/protocol.h \
+ at HW_HAMEG_HMO_TRUE@	src/hardware/hameg-hmo/protocol.c \
+ at HW_HAMEG_HMO_TRUE@	src/hardware/hameg-hmo/api.c
+
+ at HW_HANTEK_DSO_TRUE@am__append_29 = \
+ at HW_HANTEK_DSO_TRUE@	src/hardware/hantek-dso/dso.h \
+ at HW_HANTEK_DSO_TRUE@	src/hardware/hantek-dso/dso.c \
+ at HW_HANTEK_DSO_TRUE@	src/hardware/hantek-dso/api.c
+
+ at HW_HUNG_CHANG_DSO_2100_TRUE@am__append_30 = \
+ at HW_HUNG_CHANG_DSO_2100_TRUE@	src/hardware/hung-chang-dso-2100/protocol.h \
+ at HW_HUNG_CHANG_DSO_2100_TRUE@	src/hardware/hung-chang-dso-2100/protocol.c \
+ at HW_HUNG_CHANG_DSO_2100_TRUE@	src/hardware/hung-chang-dso-2100/api.c
+
+ at HW_IKALOGIC_SCANALOGIC2_TRUE@am__append_31 = \
+ at HW_IKALOGIC_SCANALOGIC2_TRUE@	src/hardware/ikalogic-scanalogic2/protocol.h \
+ at HW_IKALOGIC_SCANALOGIC2_TRUE@	src/hardware/ikalogic-scanalogic2/protocol.c \
+ at HW_IKALOGIC_SCANALOGIC2_TRUE@	src/hardware/ikalogic-scanalogic2/api.c
+
+ at HW_IKALOGIC_SCANAPLUS_TRUE@am__append_32 = \
+ at HW_IKALOGIC_SCANAPLUS_TRUE@	src/hardware/ikalogic-scanaplus/protocol.h \
+ at HW_IKALOGIC_SCANAPLUS_TRUE@	src/hardware/ikalogic-scanaplus/protocol.c \
+ at HW_IKALOGIC_SCANAPLUS_TRUE@	src/hardware/ikalogic-scanaplus/api.c
+
+ at HW_KECHENG_KC_330B_TRUE@am__append_33 = \
+ at HW_KECHENG_KC_330B_TRUE@	src/hardware/kecheng-kc-330b/protocol.h \
+ at HW_KECHENG_KC_330B_TRUE@	src/hardware/kecheng-kc-330b/protocol.c \
+ at HW_KECHENG_KC_330B_TRUE@	src/hardware/kecheng-kc-330b/api.c
+
+ at HW_KERN_SCALE_TRUE@am__append_34 = \
+ at HW_KERN_SCALE_TRUE@	src/hardware/kern-scale/protocol.h \
+ at HW_KERN_SCALE_TRUE@	src/hardware/kern-scale/protocol.c \
+ at HW_KERN_SCALE_TRUE@	src/hardware/kern-scale/api.c
+
+ at HW_KORAD_KAXXXXP_TRUE@am__append_35 = \
+ at HW_KORAD_KAXXXXP_TRUE@	src/hardware/korad-kaxxxxp/protocol.h \
+ at HW_KORAD_KAXXXXP_TRUE@	src/hardware/korad-kaxxxxp/protocol.c \
+ at HW_KORAD_KAXXXXP_TRUE@	src/hardware/korad-kaxxxxp/api.c
+
+ at HW_LASCAR_EL_USB_TRUE@am__append_36 = \
+ at HW_LASCAR_EL_USB_TRUE@	src/hardware/lascar-el-usb/protocol.h \
+ at HW_LASCAR_EL_USB_TRUE@	src/hardware/lascar-el-usb/protocol.c \
+ at HW_LASCAR_EL_USB_TRUE@	src/hardware/lascar-el-usb/api.c
+
+ at HW_LECROY_LOGICSTUDIO_TRUE@am__append_37 = \
+ at HW_LECROY_LOGICSTUDIO_TRUE@	src/hardware/lecroy-logicstudio/protocol.h \
+ at HW_LECROY_LOGICSTUDIO_TRUE@	src/hardware/lecroy-logicstudio/protocol.c \
+ at HW_LECROY_LOGICSTUDIO_TRUE@	src/hardware/lecroy-logicstudio/api.c
+
+ at HW_MANSON_HCS_3XXX_TRUE@am__append_38 = \
+ at HW_MANSON_HCS_3XXX_TRUE@	src/hardware/manson-hcs-3xxx/protocol.h \
+ at HW_MANSON_HCS_3XXX_TRUE@	src/hardware/manson-hcs-3xxx/protocol.c \
+ at HW_MANSON_HCS_3XXX_TRUE@	src/hardware/manson-hcs-3xxx/api.c
+
+ at HW_MAYNUO_M97_TRUE@am__append_39 = \
+ at HW_MAYNUO_M97_TRUE@	src/hardware/maynuo-m97/protocol.h \
+ at HW_MAYNUO_M97_TRUE@	src/hardware/maynuo-m97/protocol.c \
+ at HW_MAYNUO_M97_TRUE@	src/hardware/maynuo-m97/api.c
+
+ at HW_MIC_985XX_TRUE@am__append_40 = \
+ at HW_MIC_985XX_TRUE@	src/hardware/mic-985xx/protocol.h \
+ at HW_MIC_985XX_TRUE@	src/hardware/mic-985xx/protocol.c \
+ at HW_MIC_985XX_TRUE@	src/hardware/mic-985xx/api.c
+
+ at HW_MOTECH_LPS_30X_TRUE@am__append_41 = \
+ at HW_MOTECH_LPS_30X_TRUE@	src/hardware/motech-lps-30x/protocol.h \
+ at HW_MOTECH_LPS_30X_TRUE@	src/hardware/motech-lps-30x/protocol.c \
+ at HW_MOTECH_LPS_30X_TRUE@	src/hardware/motech-lps-30x/api.c
+
+ at HW_NORMA_DMM_TRUE@am__append_42 = \
+ at HW_NORMA_DMM_TRUE@	src/hardware/norma-dmm/protocol.h \
+ at HW_NORMA_DMM_TRUE@	src/hardware/norma-dmm/protocol.c \
+ at HW_NORMA_DMM_TRUE@	src/hardware/norma-dmm/api.c
+
+ at HW_OPENBENCH_LOGIC_SNIFFER_TRUE@am__append_43 = \
+ at HW_OPENBENCH_LOGIC_SNIFFER_TRUE@	src/hardware/openbench-logic-sniffer/protocol.h \
+ at HW_OPENBENCH_LOGIC_SNIFFER_TRUE@	src/hardware/openbench-logic-sniffer/protocol.c \
+ at HW_OPENBENCH_LOGIC_SNIFFER_TRUE@	src/hardware/openbench-logic-sniffer/api.c
+
+ at HW_PIPISTRELLO_OLS_TRUE@am__append_44 = \
+ at HW_PIPISTRELLO_OLS_TRUE@	src/hardware/pipistrello-ols/protocol.h \
+ at HW_PIPISTRELLO_OLS_TRUE@	src/hardware/pipistrello-ols/protocol.c \
+ at HW_PIPISTRELLO_OLS_TRUE@	src/hardware/pipistrello-ols/api.c
+
+ at HW_RIGOL_DS_TRUE@am__append_45 = \
+ at HW_RIGOL_DS_TRUE@	src/hardware/rigol-ds/protocol.h \
+ at HW_RIGOL_DS_TRUE@	src/hardware/rigol-ds/protocol.c \
+ at HW_RIGOL_DS_TRUE@	src/hardware/rigol-ds/api.c
+
+ at HW_SALEAE_LOGIC16_TRUE@am__append_46 = \
+ at HW_SALEAE_LOGIC16_TRUE@	src/hardware/saleae-logic16/protocol.h \
+ at HW_SALEAE_LOGIC16_TRUE@	src/hardware/saleae-logic16/protocol.c \
+ at HW_SALEAE_LOGIC16_TRUE@	src/hardware/saleae-logic16/api.c
+
+ at HW_SCPI_PPS_TRUE@am__append_47 = \
+ at HW_SCPI_PPS_TRUE@	src/hardware/scpi-pps/protocol.h \
+ at HW_SCPI_PPS_TRUE@	src/hardware/scpi-pps/protocol.c \
+ at HW_SCPI_PPS_TRUE@	src/hardware/scpi-pps/profiles.c \
+ at HW_SCPI_PPS_TRUE@	src/hardware/scpi-pps/api.c
+
+ at HW_SERIAL_DMM_TRUE@am__append_48 = \
+ at HW_SERIAL_DMM_TRUE@	src/hardware/serial-dmm/protocol.h \
+ at HW_SERIAL_DMM_TRUE@	src/hardware/serial-dmm/protocol.c \
+ at HW_SERIAL_DMM_TRUE@	src/hardware/serial-dmm/api.c
+
+ at HW_SYSCLK_LWLA_TRUE@am__append_49 = \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/lwla.h \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/lwla.c \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/lwla1016.c \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/lwla1034.c \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/protocol.h \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/protocol.c \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/api.c
+
+ at HW_TELEINFO_TRUE@am__append_50 = \
+ at HW_TELEINFO_TRUE@	src/hardware/teleinfo/protocol.h \
+ at HW_TELEINFO_TRUE@	src/hardware/teleinfo/protocol.c \
+ at HW_TELEINFO_TRUE@	src/hardware/teleinfo/api.c
+
+ at HW_TESTO_TRUE@am__append_51 = \
+ at HW_TESTO_TRUE@	src/hardware/testo/protocol.h \
+ at HW_TESTO_TRUE@	src/hardware/testo/protocol.c \
+ at HW_TESTO_TRUE@	src/hardware/testo/api.c
+
+ at HW_TONDAJ_SL_814_TRUE@am__append_52 = \
+ at HW_TONDAJ_SL_814_TRUE@	src/hardware/tondaj-sl-814/protocol.h \
+ at HW_TONDAJ_SL_814_TRUE@	src/hardware/tondaj-sl-814/protocol.c \
+ at HW_TONDAJ_SL_814_TRUE@	src/hardware/tondaj-sl-814/api.c
+
+ at HW_UNI_T_DMM_TRUE@am__append_53 = \
+ at HW_UNI_T_DMM_TRUE@	src/hardware/uni-t-dmm/protocol.h \
+ at HW_UNI_T_DMM_TRUE@	src/hardware/uni-t-dmm/protocol.c \
+ at HW_UNI_T_DMM_TRUE@	src/hardware/uni-t-dmm/api.c
+
+ at HW_UNI_T_UT32X_TRUE@am__append_54 = \
+ at HW_UNI_T_UT32X_TRUE@	src/hardware/uni-t-ut32x/protocol.h \
+ at HW_UNI_T_UT32X_TRUE@	src/hardware/uni-t-ut32x/protocol.c \
+ at HW_UNI_T_UT32X_TRUE@	src/hardware/uni-t-ut32x/api.c
+
+ at HW_VICTOR_DMM_TRUE@am__append_55 = \
+ at HW_VICTOR_DMM_TRUE@	src/hardware/victor-dmm/protocol.h \
+ at HW_VICTOR_DMM_TRUE@	src/hardware/victor-dmm/protocol.c \
+ at HW_VICTOR_DMM_TRUE@	src/hardware/victor-dmm/api.c
+
+ at HW_YOKOGAWA_DLM_TRUE@am__append_56 = \
+ at HW_YOKOGAWA_DLM_TRUE@	src/hardware/yokogawa-dlm/protocol.h \
+ at HW_YOKOGAWA_DLM_TRUE@	src/hardware/yokogawa-dlm/protocol.c \
+ at HW_YOKOGAWA_DLM_TRUE@	src/hardware/yokogawa-dlm/protocol_wrappers.h \
+ at HW_YOKOGAWA_DLM_TRUE@	src/hardware/yokogawa-dlm/protocol_wrappers.c \
+ at HW_YOKOGAWA_DLM_TRUE@	src/hardware/yokogawa-dlm/api.c
+
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@am__append_57 = \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/analyzer.c \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/analyzer.h \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/gl_usb.h \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/gl_usb.c \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/protocol.h \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/protocol.c \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/api.c
+
+ at HAVE_CHECK_TRUE@TESTS = tests/main$(EXEEXT)
 @HAVE_CHECK_TRUE at check_PROGRAMS = $(am__EXEEXT_1)
+ at BINDINGS_CXX_TRUE@am__append_58 = bindings/cxx/libsigrokcxx.la
+ at BINDINGS_CXX_TRUE@am__append_59 = bindings/cxx/libsigrokcxx.pc
+ at BINDINGS_CXX_TRUE@am__append_60 = cxx-clean
+ at BINDINGS_PYTHON_TRUE@am__append_61 = python-build
+ at BINDINGS_PYTHON_TRUE@am__append_62 = python-install
+ at BINDINGS_PYTHON_TRUE@am__append_63 = python-clean
+ at BINDINGS_RUBY_TRUE@am__append_64 = ruby-build
+ at BINDINGS_RUBY_TRUE@am__append_65 = ruby-install
+ at BINDINGS_RUBY_TRUE@am__append_66 = ruby-clean
+ at BINDINGS_JAVA_TRUE@am__append_67 = java-build
+ at BINDINGS_JAVA_TRUE@am__append_68 = java-install
+ at BINDINGS_JAVA_TRUE@am__append_69 = java-uninstall
+ at BINDINGS_JAVA_TRUE@am__append_70 = java-clean
 subdir = .
-DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \
-	$(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/configure $(am__configure_deps) \
-	$(srcdir)/config.h.in $(srcdir)/version.h.in \
-	$(srcdir)/libsigrok.pc.in $(top_srcdir)/autostuff/depcomp \
-	$(library_include_HEADERS) $(noinst_HEADERS) \
-	$(top_srcdir)/autostuff/test-driver COPYING autostuff/ar-lib \
-	autostuff/compile autostuff/config.guess autostuff/config.sub \
-	autostuff/depcomp autostuff/install-sh autostuff/missing \
-	autostuff/ltmain.sh $(top_srcdir)/autostuff/ar-lib \
-	$(top_srcdir)/autostuff/compile \
-	$(top_srcdir)/autostuff/config.guess \
-	$(top_srcdir)/autostuff/config.sub \
-	$(top_srcdir)/autostuff/install-sh \
-	$(top_srcdir)/autostuff/ltmain.sh \
-	$(top_srcdir)/autostuff/missing
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/autostuff/libtool.m4 \
-	$(top_srcdir)/autostuff/ltoptions.m4 \
-	$(top_srcdir)/autostuff/ltsugar.m4 \
-	$(top_srcdir)/autostuff/ltversion.m4 \
-	$(top_srcdir)/autostuff/lt~obsolete.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/sigrok.m4 \
 	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
+	$(am__configure_deps) \
+	$(am__bindings_cxx_libsigrokcxx_la_include_HEADERS_DIST) \
+	$(library_include_HEADERS) $(noinst_HEADERS) \
+	$(am__DIST_COMMON)
 am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
  configure.lineno config.status.lineno
 mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = config.h
-CONFIG_CLEAN_FILES = version.h libsigrok.pc
+CONFIG_HEADER = config.h $(top_builddir)/include/libsigrok/version.h
+CONFIG_CLEAN_FILES = libsigrok.pc bindings/cxx/libsigrokcxx.pc
 CONFIG_CLEAN_VPATH_FILES =
 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
 am__vpath_adj = case $$p in \
@@ -336,242 +443,399 @@ am__uninstall_files_from_dir = { \
          $(am__cd) "$$dir" && rm -f $$files; }; \
   }
 am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \
+	"$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)" \
+	"$(DESTDIR)$(library_includedir)" \
+	"$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)" \
 	"$(DESTDIR)$(library_includedir)"
 LTLIBRARIES = $(lib_LTLIBRARIES)
-libsigrok_la_DEPENDENCIES = $(LIBOBJS)
-am__libsigrok_la_SOURCES_DIST = backend.c device.c session.c \
-	session_file.c session_driver.c hwdriver.c strutil.c log.c \
-	version.c error.c std.c input/binary.c input/chronovu_la8.c \
-	input/csv.c input/input.c input/vcd.c input/wav.c \
-	output/output.c output/analog.c output/ascii.c output/bits.c \
-	output/binary.c output/csv.c output/chronovu_la8.c \
-	output/gnuplot.c output/hex.c output/ols.c output/vcd.c \
-	hardware/common/scpi.c hardware/common/scpi_tcp.c \
-	hardware/common/scpi_vxi.c hardware/common/vxi_clnt.c \
-	hardware/common/vxi_xdr.c hardware/common/vxi.h \
-	hardware/common/serial.c hardware/common/scpi_serial.c \
-	hardware/common/ezusb.c hardware/common/usb.c \
-	hardware/common/scpi_usbtmc_libusb.c \
-	hardware/common/scpi_visa.c hardware/common/dmm/es519xx.c \
-	hardware/common/dmm/fs9721.c hardware/common/dmm/fs9922.c \
-	hardware/common/dmm/m2110.c hardware/common/dmm/metex14.c \
-	hardware/common/dmm/rs9lcd.c hardware/agilent-dmm/api.c \
-	hardware/agilent-dmm/agilent-dmm.h \
-	hardware/agilent-dmm/sched.c hardware/appa-55ii/protocol.h \
-	hardware/appa-55ii/protocol.c hardware/appa-55ii/api.c \
-	hardware/asix-sigma/asix-sigma.h \
-	hardware/asix-sigma/asix-sigma.c \
-	hardware/atten-pps3xxx/protocol.h \
-	hardware/atten-pps3xxx/protocol.c hardware/atten-pps3xxx/api.c \
-	hardware/brymen-bm86x/protocol.h \
-	hardware/brymen-bm86x/protocol.c hardware/brymen-bm86x/api.c \
-	hardware/brymen-dmm/parser.c hardware/brymen-dmm/protocol.h \
-	hardware/brymen-dmm/protocol.c hardware/brymen-dmm/api.c \
-	hardware/cem-dt-885x/protocol.h \
-	hardware/cem-dt-885x/protocol.c hardware/cem-dt-885x/api.c \
-	hardware/center-3xx/protocol.h hardware/center-3xx/protocol.c \
-	hardware/center-3xx/api.c hardware/chronovu-la/protocol.h \
-	hardware/chronovu-la/protocol.c hardware/chronovu-la/api.c \
-	hardware/colead-slm/protocol.h hardware/colead-slm/protocol.c \
-	hardware/colead-slm/api.c \
-	hardware/conrad-digi-35-cpu/protocol.h \
-	hardware/conrad-digi-35-cpu/protocol.c \
-	hardware/conrad-digi-35-cpu/api.c hardware/demo/demo.c \
-	hardware/fluke-dmm/fluke-dmm.h hardware/fluke-dmm/fluke.c \
-	hardware/fluke-dmm/api.c hardware/fx2lafw/protocol.h \
-	hardware/fx2lafw/protocol.c hardware/fx2lafw/api.c \
-	hardware/gmc-mh-1x-2x/protocol.h \
-	hardware/gmc-mh-1x-2x/protocol.c hardware/gmc-mh-1x-2x/api.c \
-	hardware/hameg-hmo/protocol.h hardware/hameg-hmo/protocol.c \
-	hardware/hameg-hmo/api.c hardware/hantek-dso/dso.h \
-	hardware/hantek-dso/dso.c hardware/hantek-dso/api.c \
-	hardware/ikalogic-scanalogic2/protocol.h \
-	hardware/ikalogic-scanalogic2/protocol.c \
-	hardware/ikalogic-scanalogic2/api.c \
-	hardware/ikalogic-scanaplus/protocol.h \
-	hardware/ikalogic-scanaplus/protocol.c \
-	hardware/ikalogic-scanaplus/api.c \
-	hardware/kecheng-kc-330b/protocol.h \
-	hardware/kecheng-kc-330b/protocol.c \
-	hardware/kecheng-kc-330b/api.c \
-	hardware/lascar-el-usb/protocol.h \
-	hardware/lascar-el-usb/protocol.c hardware/lascar-el-usb/api.c \
-	hardware/mic-985xx/protocol.h hardware/mic-985xx/protocol.c \
-	hardware/mic-985xx/api.c hardware/norma-dmm/protocol.h \
-	hardware/norma-dmm/protocol.c hardware/norma-dmm/api.c \
-	hardware/openbench-logic-sniffer/protocol.h \
-	hardware/openbench-logic-sniffer/protocol.c \
-	hardware/openbench-logic-sniffer/api.c \
-	hardware/rigol-ds/protocol.h hardware/rigol-ds/protocol.c \
-	hardware/rigol-ds/api.c hardware/saleae-logic16/protocol.h \
-	hardware/saleae-logic16/protocol.c \
-	hardware/saleae-logic16/api.c hardware/serial-dmm/protocol.h \
-	hardware/serial-dmm/protocol.c hardware/serial-dmm/api.c \
-	hardware/sysclk-lwla/lwla.h hardware/sysclk-lwla/lwla.c \
-	hardware/sysclk-lwla/protocol.h \
-	hardware/sysclk-lwla/protocol.c hardware/sysclk-lwla/api.c \
-	hardware/teleinfo/protocol.h hardware/teleinfo/protocol.c \
-	hardware/teleinfo/api.c hardware/tondaj-sl-814/protocol.h \
-	hardware/tondaj-sl-814/protocol.c hardware/tondaj-sl-814/api.c \
-	hardware/uni-t-dmm/protocol.h hardware/uni-t-dmm/protocol.c \
-	hardware/uni-t-dmm/api.c hardware/uni-t-ut32x/protocol.h \
-	hardware/uni-t-ut32x/protocol.c hardware/uni-t-ut32x/api.c \
-	hardware/victor-dmm/protocol.h hardware/victor-dmm/protocol.c \
-	hardware/victor-dmm/api.c \
-	hardware/zeroplus-logic-cube/analyzer.c \
-	hardware/zeroplus-logic-cube/analyzer.h \
-	hardware/zeroplus-logic-cube/gl_usb.h \
-	hardware/zeroplus-logic-cube/gl_usb.c \
-	hardware/zeroplus-logic-cube/protocol.h \
-	hardware/zeroplus-logic-cube/protocol.c \
-	hardware/zeroplus-logic-cube/api.c
+am__DEPENDENCIES_1 =
+ at BINDINGS_CXX_TRUE@bindings_cxx_libsigrokcxx_la_DEPENDENCIES =  \
+ at BINDINGS_CXX_TRUE@	libsigrok.la $(am__DEPENDENCIES_1) \
+ at BINDINGS_CXX_TRUE@	$(am__DEPENDENCIES_1)
+am__bindings_cxx_libsigrokcxx_la_SOURCES_DIST =  \
+	bindings/cxx/classes.cpp
 am__dirstamp = $(am__leading_dot)dirstamp
- at NEED_RPC_TRUE@am__objects_1 = hardware/common/scpi_vxi.lo \
- at NEED_RPC_TRUE@	hardware/common/vxi_clnt.lo \
- at NEED_RPC_TRUE@	hardware/common/vxi_xdr.lo
- at NEED_SERIAL_TRUE@am__objects_2 = hardware/common/serial.lo \
- at NEED_SERIAL_TRUE@	hardware/common/scpi_serial.lo
- at NEED_USB_TRUE@am__objects_3 = hardware/common/ezusb.lo \
- at NEED_USB_TRUE@	hardware/common/usb.lo \
- at NEED_USB_TRUE@	hardware/common/scpi_usbtmc_libusb.lo
- at NEED_VISA_TRUE@am__objects_4 = hardware/common/scpi_visa.lo
- at HW_AGILENT_DMM_TRUE@am__objects_5 = hardware/agilent-dmm/api.lo \
- at HW_AGILENT_DMM_TRUE@	hardware/agilent-dmm/sched.lo
- at HW_APPA_55II_TRUE@am__objects_6 = hardware/appa-55ii/protocol.lo \
- at HW_APPA_55II_TRUE@	hardware/appa-55ii/api.lo
- at HW_ASIX_SIGMA_TRUE@am__objects_7 = hardware/asix-sigma/asix-sigma.lo
- at HW_ATTEN_PPS3XXX_TRUE@am__objects_8 =  \
- at HW_ATTEN_PPS3XXX_TRUE@	hardware/atten-pps3xxx/protocol.lo \
- at HW_ATTEN_PPS3XXX_TRUE@	hardware/atten-pps3xxx/api.lo
- at HW_BRYMEN_BM86X_TRUE@am__objects_9 =  \
- at HW_BRYMEN_BM86X_TRUE@	hardware/brymen-bm86x/protocol.lo \
- at HW_BRYMEN_BM86X_TRUE@	hardware/brymen-bm86x/api.lo
- at HW_BRYMEN_DMM_TRUE@am__objects_10 = hardware/brymen-dmm/parser.lo \
- at HW_BRYMEN_DMM_TRUE@	hardware/brymen-dmm/protocol.lo \
- at HW_BRYMEN_DMM_TRUE@	hardware/brymen-dmm/api.lo
- at HW_CEM_DT_885X_TRUE@am__objects_11 =  \
- at HW_CEM_DT_885X_TRUE@	hardware/cem-dt-885x/protocol.lo \
- at HW_CEM_DT_885X_TRUE@	hardware/cem-dt-885x/api.lo
- at HW_CENTER_3XX_TRUE@am__objects_12 = hardware/center-3xx/protocol.lo \
- at HW_CENTER_3XX_TRUE@	hardware/center-3xx/api.lo
- at HW_CHRONOVU_LA_TRUE@am__objects_13 =  \
- at HW_CHRONOVU_LA_TRUE@	hardware/chronovu-la/protocol.lo \
- at HW_CHRONOVU_LA_TRUE@	hardware/chronovu-la/api.lo
- at HW_COLEAD_SLM_TRUE@am__objects_14 = hardware/colead-slm/protocol.lo \
- at HW_COLEAD_SLM_TRUE@	hardware/colead-slm/api.lo
- at HW_CONRAD_DIGI_35_CPU_TRUE@am__objects_15 = hardware/conrad-digi-35-cpu/protocol.lo \
- at HW_CONRAD_DIGI_35_CPU_TRUE@	hardware/conrad-digi-35-cpu/api.lo
- at HW_DEMO_TRUE@am__objects_16 = hardware/demo/demo.lo
- at HW_FLUKE_DMM_TRUE@am__objects_17 = hardware/fluke-dmm/fluke.lo \
- at HW_FLUKE_DMM_TRUE@	hardware/fluke-dmm/api.lo
- at HW_FX2LAFW_TRUE@am__objects_18 = hardware/fx2lafw/protocol.lo \
- at HW_FX2LAFW_TRUE@	hardware/fx2lafw/api.lo
- at HW_GMC_MH_1X_2X_TRUE@am__objects_19 =  \
- at HW_GMC_MH_1X_2X_TRUE@	hardware/gmc-mh-1x-2x/protocol.lo \
- at HW_GMC_MH_1X_2X_TRUE@	hardware/gmc-mh-1x-2x/api.lo
- at HW_HAMEG_HMO_TRUE@am__objects_20 = hardware/hameg-hmo/protocol.lo \
- at HW_HAMEG_HMO_TRUE@	hardware/hameg-hmo/api.lo
- at HW_HANTEK_DSO_TRUE@am__objects_21 = hardware/hantek-dso/dso.lo \
- at HW_HANTEK_DSO_TRUE@	hardware/hantek-dso/api.lo
- at HW_IKALOGIC_SCANALOGIC2_TRUE@am__objects_22 = hardware/ikalogic-scanalogic2/protocol.lo \
- at HW_IKALOGIC_SCANALOGIC2_TRUE@	hardware/ikalogic-scanalogic2/api.lo
- at HW_IKALOGIC_SCANAPLUS_TRUE@am__objects_23 = hardware/ikalogic-scanaplus/protocol.lo \
- at HW_IKALOGIC_SCANAPLUS_TRUE@	hardware/ikalogic-scanaplus/api.lo
- at HW_KECHENG_KC_330B_TRUE@am__objects_24 =  \
- at HW_KECHENG_KC_330B_TRUE@	hardware/kecheng-kc-330b/protocol.lo \
- at HW_KECHENG_KC_330B_TRUE@	hardware/kecheng-kc-330b/api.lo
- at HW_LASCAR_EL_USB_TRUE@am__objects_25 =  \
- at HW_LASCAR_EL_USB_TRUE@	hardware/lascar-el-usb/protocol.lo \
- at HW_LASCAR_EL_USB_TRUE@	hardware/lascar-el-usb/api.lo
- at HW_MIC_985XX_TRUE@am__objects_26 = hardware/mic-985xx/protocol.lo \
- at HW_MIC_985XX_TRUE@	hardware/mic-985xx/api.lo
- at HW_NORMA_DMM_TRUE@am__objects_27 = hardware/norma-dmm/protocol.lo \
- at HW_NORMA_DMM_TRUE@	hardware/norma-dmm/api.lo
- at HW_OLS_TRUE@am__objects_28 =  \
- at HW_OLS_TRUE@	hardware/openbench-logic-sniffer/protocol.lo \
- at HW_OLS_TRUE@	hardware/openbench-logic-sniffer/api.lo
- at HW_RIGOL_DS_TRUE@am__objects_29 = hardware/rigol-ds/protocol.lo \
- at HW_RIGOL_DS_TRUE@	hardware/rigol-ds/api.lo
- at HW_SALEAE_LOGIC16_TRUE@am__objects_30 =  \
- at HW_SALEAE_LOGIC16_TRUE@	hardware/saleae-logic16/protocol.lo \
- at HW_SALEAE_LOGIC16_TRUE@	hardware/saleae-logic16/api.lo
- at HW_SERIAL_DMM_TRUE@am__objects_31 = hardware/serial-dmm/protocol.lo \
- at HW_SERIAL_DMM_TRUE@	hardware/serial-dmm/api.lo
- at HW_SYSCLK_LWLA_TRUE@am__objects_32 = hardware/sysclk-lwla/lwla.lo \
- at HW_SYSCLK_LWLA_TRUE@	hardware/sysclk-lwla/protocol.lo \
- at HW_SYSCLK_LWLA_TRUE@	hardware/sysclk-lwla/api.lo
- at HW_TELEINFO_TRUE@am__objects_33 = hardware/teleinfo/protocol.lo \
- at HW_TELEINFO_TRUE@	hardware/teleinfo/api.lo
- at HW_TONDAJ_SL_814_TRUE@am__objects_34 =  \
- at HW_TONDAJ_SL_814_TRUE@	hardware/tondaj-sl-814/protocol.lo \
- at HW_TONDAJ_SL_814_TRUE@	hardware/tondaj-sl-814/api.lo
- at HW_UNI_T_DMM_TRUE@am__objects_35 = hardware/uni-t-dmm/protocol.lo \
- at HW_UNI_T_DMM_TRUE@	hardware/uni-t-dmm/api.lo
- at HW_UNI_T_UT32X_TRUE@am__objects_36 =  \
- at HW_UNI_T_UT32X_TRUE@	hardware/uni-t-ut32x/protocol.lo \
- at HW_UNI_T_UT32X_TRUE@	hardware/uni-t-ut32x/api.lo
- at HW_VICTOR_DMM_TRUE@am__objects_37 = hardware/victor-dmm/protocol.lo \
- at HW_VICTOR_DMM_TRUE@	hardware/victor-dmm/api.lo
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@am__objects_38 = hardware/zeroplus-logic-cube/analyzer.lo \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/gl_usb.lo \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/protocol.lo \
- at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	hardware/zeroplus-logic-cube/api.lo
-am_libsigrok_la_OBJECTS = backend.lo device.lo session.lo \
-	session_file.lo session_driver.lo hwdriver.lo strutil.lo \
-	log.lo version.lo error.lo std.lo input/binary.lo \
-	input/chronovu_la8.lo input/csv.lo input/input.lo input/vcd.lo \
-	input/wav.lo output/output.lo output/analog.lo output/ascii.lo \
-	output/bits.lo output/binary.lo output/csv.lo \
-	output/chronovu_la8.lo output/gnuplot.lo output/hex.lo \
-	output/ols.lo output/vcd.lo hardware/common/scpi.lo \
-	hardware/common/scpi_tcp.lo $(am__objects_1) $(am__objects_2) \
-	$(am__objects_3) $(am__objects_4) \
-	hardware/common/dmm/es519xx.lo hardware/common/dmm/fs9721.lo \
-	hardware/common/dmm/fs9922.lo hardware/common/dmm/m2110.lo \
-	hardware/common/dmm/metex14.lo hardware/common/dmm/rs9lcd.lo \
-	$(am__objects_5) $(am__objects_6) $(am__objects_7) \
-	$(am__objects_8) $(am__objects_9) $(am__objects_10) \
-	$(am__objects_11) $(am__objects_12) $(am__objects_13) \
-	$(am__objects_14) $(am__objects_15) $(am__objects_16) \
-	$(am__objects_17) $(am__objects_18) $(am__objects_19) \
-	$(am__objects_20) $(am__objects_21) $(am__objects_22) \
-	$(am__objects_23) $(am__objects_24) $(am__objects_25) \
-	$(am__objects_26) $(am__objects_27) $(am__objects_28) \
-	$(am__objects_29) $(am__objects_30) $(am__objects_31) \
-	$(am__objects_32) $(am__objects_33) $(am__objects_34) \
-	$(am__objects_35) $(am__objects_36) $(am__objects_37) \
-	$(am__objects_38)
-libsigrok_la_OBJECTS = $(am_libsigrok_la_OBJECTS)
+ at BINDINGS_CXX_TRUE@am_bindings_cxx_libsigrokcxx_la_OBJECTS =  \
+ at BINDINGS_CXX_TRUE@	bindings/cxx/classes.lo
+bindings_cxx_libsigrokcxx_la_OBJECTS =  \
+	$(am_bindings_cxx_libsigrokcxx_la_OBJECTS)
 AM_V_lt = $(am__v_lt_ at AM_V@)
 am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
 am__v_lt_0 = --silent
 am__v_lt_1 = 
+bindings_cxx_libsigrokcxx_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+	$(AM_CXXFLAGS) $(CXXFLAGS) \
+	$(bindings_cxx_libsigrokcxx_la_LDFLAGS) $(LDFLAGS) -o $@
+ at BINDINGS_CXX_TRUE@am_bindings_cxx_libsigrokcxx_la_rpath = -rpath \
+ at BINDINGS_CXX_TRUE@	$(libdir)
+libsigrok_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am__libsigrok_la_SOURCES_DIST = src/backend.c src/device.c \
+	src/session.c src/session_file.c src/session_driver.c \
+	src/drivers.c src/hwdriver.c src/trigger.c src/soft-trigger.c \
+	src/analog.c src/fallback.c src/resource.c src/strutil.c \
+	src/log.c src/version.c src/error.c src/std.c \
+	src/input/input.c src/input/binary.c src/input/chronovu_la8.c \
+	src/input/csv.c src/input/raw_analog.c src/input/trace32_ad.c \
+	src/input/vcd.c src/input/wav.c src/output/output.c \
+	src/output/analog.c src/output/ascii.c src/output/bits.c \
+	src/output/binary.c src/output/csv.c src/output/chronovu_la8.c \
+	src/output/wav.c src/output/gnuplot.c src/output/hex.c \
+	src/output/ols.c src/output/srzip.c src/output/vcd.c \
+	src/transform/transform.c src/transform/nop.c \
+	src/transform/scale.c src/transform/invert.c src/scpi.h \
+	src/scpi/scpi.c src/scpi/helpers.c src/scpi/scpi_tcp.c \
+	src/scpi/scpi_vxi.c src/scpi/vxi_clnt.c src/scpi/vxi_xdr.c \
+	src/scpi/vxi.h src/serial.c src/scpi/scpi_serial.c src/ezusb.c \
+	src/usb.c src/scpi/scpi_usbtmc_libusb.c src/scpi/scpi_visa.c \
+	src/scpi/scpi_libgpib.c src/modbus/modbus.c \
+	src/modbus/modbus_serial_rtu.c src/dmm/es519xx.c \
+	src/dmm/fs9721.c src/dmm/fs9922.c src/dmm/m2110.c \
+	src/dmm/metex14.c src/dmm/rs9lcd.c src/dmm/bm25x.c \
+	src/dmm/ut71x.c src/dmm/ut372.c src/dmm/vc870.c \
+	src/dmm/dtm0660.c src/lcr/es51919.c src/scale/kern.c \
+	src/hardware/agilent-dmm/api.c \
+	src/hardware/agilent-dmm/agilent-dmm.h \
+	src/hardware/agilent-dmm/sched.c \
+	src/hardware/appa-55ii/protocol.h \
+	src/hardware/appa-55ii/protocol.c src/hardware/appa-55ii/api.c \
+	src/hardware/asix-sigma/protocol.h \
+	src/hardware/asix-sigma/protocol.c \
+	src/hardware/asix-sigma/api.c \
+	src/hardware/atten-pps3xxx/protocol.h \
+	src/hardware/atten-pps3xxx/protocol.c \
+	src/hardware/atten-pps3xxx/api.c \
+	src/hardware/baylibre-acme/protocol.h \
+	src/hardware/baylibre-acme/protocol.c \
+	src/hardware/baylibre-acme/api.c \
+	src/hardware/baylibre-acme/gpio.h \
+	src/hardware/baylibre-acme/gpio.c \
+	src/hardware/beaglelogic/beaglelogic.h \
+	src/hardware/beaglelogic/protocol.h \
+	src/hardware/beaglelogic/protocol.c \
+	src/hardware/beaglelogic/api.c \
+	src/hardware/brymen-bm86x/protocol.h \
+	src/hardware/brymen-bm86x/protocol.c \
+	src/hardware/brymen-bm86x/api.c \
+	src/hardware/brymen-dmm/parser.c \
+	src/hardware/brymen-dmm/protocol.h \
+	src/hardware/brymen-dmm/protocol.c \
+	src/hardware/brymen-dmm/api.c \
+	src/hardware/cem-dt-885x/protocol.h \
+	src/hardware/cem-dt-885x/protocol.c \
+	src/hardware/cem-dt-885x/api.c \
+	src/hardware/center-3xx/protocol.h \
+	src/hardware/center-3xx/protocol.c \
+	src/hardware/center-3xx/api.c \
+	src/hardware/chronovu-la/protocol.h \
+	src/hardware/chronovu-la/protocol.c \
+	src/hardware/chronovu-la/api.c \
+	src/hardware/colead-slm/protocol.h \
+	src/hardware/colead-slm/protocol.c \
+	src/hardware/colead-slm/api.c \
+	src/hardware/conrad-digi-35-cpu/protocol.h \
+	src/hardware/conrad-digi-35-cpu/protocol.c \
+	src/hardware/conrad-digi-35-cpu/api.c src/hardware/demo/demo.c \
+	src/hardware/deree-de5000/api.c \
+	src/hardware/fluke-dmm/fluke-dmm.h \
+	src/hardware/fluke-dmm/fluke.c src/hardware/fluke-dmm/api.c \
+	src/hardware/fx2lafw/protocol.h \
+	src/hardware/fx2lafw/protocol.c src/hardware/fx2lafw/api.c \
+	src/hardware/fx2lafw/dslogic.c src/hardware/fx2lafw/dslogic.h \
+	src/hardware/gmc-mh-1x-2x/protocol.h \
+	src/hardware/gmc-mh-1x-2x/protocol.c \
+	src/hardware/gmc-mh-1x-2x/api.c \
+	src/hardware/gwinstek-gds-800/protocol.h \
+	src/hardware/gwinstek-gds-800/protocol.c \
+	src/hardware/gwinstek-gds-800/api.c \
+	src/hardware/hameg-hmo/protocol.h \
+	src/hardware/hameg-hmo/protocol.c src/hardware/hameg-hmo/api.c \
+	src/hardware/hantek-dso/dso.h src/hardware/hantek-dso/dso.c \
+	src/hardware/hantek-dso/api.c \
+	src/hardware/hung-chang-dso-2100/protocol.h \
+	src/hardware/hung-chang-dso-2100/protocol.c \
+	src/hardware/hung-chang-dso-2100/api.c \
+	src/hardware/ikalogic-scanalogic2/protocol.h \
+	src/hardware/ikalogic-scanalogic2/protocol.c \
+	src/hardware/ikalogic-scanalogic2/api.c \
+	src/hardware/ikalogic-scanaplus/protocol.h \
+	src/hardware/ikalogic-scanaplus/protocol.c \
+	src/hardware/ikalogic-scanaplus/api.c \
+	src/hardware/kecheng-kc-330b/protocol.h \
+	src/hardware/kecheng-kc-330b/protocol.c \
+	src/hardware/kecheng-kc-330b/api.c \
+	src/hardware/kern-scale/protocol.h \
+	src/hardware/kern-scale/protocol.c \
+	src/hardware/kern-scale/api.c \
+	src/hardware/korad-kaxxxxp/protocol.h \
+	src/hardware/korad-kaxxxxp/protocol.c \
+	src/hardware/korad-kaxxxxp/api.c \
+	src/hardware/lascar-el-usb/protocol.h \
+	src/hardware/lascar-el-usb/protocol.c \
+	src/hardware/lascar-el-usb/api.c \
+	src/hardware/lecroy-logicstudio/protocol.h \
+	src/hardware/lecroy-logicstudio/protocol.c \
+	src/hardware/lecroy-logicstudio/api.c \
+	src/hardware/manson-hcs-3xxx/protocol.h \
+	src/hardware/manson-hcs-3xxx/protocol.c \
+	src/hardware/manson-hcs-3xxx/api.c \
+	src/hardware/maynuo-m97/protocol.h \
+	src/hardware/maynuo-m97/protocol.c \
+	src/hardware/maynuo-m97/api.c \
+	src/hardware/mic-985xx/protocol.h \
+	src/hardware/mic-985xx/protocol.c src/hardware/mic-985xx/api.c \
+	src/hardware/motech-lps-30x/protocol.h \
+	src/hardware/motech-lps-30x/protocol.c \
+	src/hardware/motech-lps-30x/api.c \
+	src/hardware/norma-dmm/protocol.h \
+	src/hardware/norma-dmm/protocol.c src/hardware/norma-dmm/api.c \
+	src/hardware/openbench-logic-sniffer/protocol.h \
+	src/hardware/openbench-logic-sniffer/protocol.c \
+	src/hardware/openbench-logic-sniffer/api.c \
+	src/hardware/pipistrello-ols/protocol.h \
+	src/hardware/pipistrello-ols/protocol.c \
+	src/hardware/pipistrello-ols/api.c \
+	src/hardware/rigol-ds/protocol.h \
+	src/hardware/rigol-ds/protocol.c src/hardware/rigol-ds/api.c \
+	src/hardware/saleae-logic16/protocol.h \
+	src/hardware/saleae-logic16/protocol.c \
+	src/hardware/saleae-logic16/api.c \
+	src/hardware/scpi-pps/protocol.h \
+	src/hardware/scpi-pps/protocol.c \
+	src/hardware/scpi-pps/profiles.c src/hardware/scpi-pps/api.c \
+	src/hardware/serial-dmm/protocol.h \
+	src/hardware/serial-dmm/protocol.c \
+	src/hardware/serial-dmm/api.c src/hardware/sysclk-lwla/lwla.h \
+	src/hardware/sysclk-lwla/lwla.c \
+	src/hardware/sysclk-lwla/lwla1016.c \
+	src/hardware/sysclk-lwla/lwla1034.c \
+	src/hardware/sysclk-lwla/protocol.h \
+	src/hardware/sysclk-lwla/protocol.c \
+	src/hardware/sysclk-lwla/api.c \
+	src/hardware/teleinfo/protocol.h \
+	src/hardware/teleinfo/protocol.c src/hardware/teleinfo/api.c \
+	src/hardware/testo/protocol.h src/hardware/testo/protocol.c \
+	src/hardware/testo/api.c src/hardware/tondaj-sl-814/protocol.h \
+	src/hardware/tondaj-sl-814/protocol.c \
+	src/hardware/tondaj-sl-814/api.c \
+	src/hardware/uni-t-dmm/protocol.h \
+	src/hardware/uni-t-dmm/protocol.c src/hardware/uni-t-dmm/api.c \
+	src/hardware/uni-t-ut32x/protocol.h \
+	src/hardware/uni-t-ut32x/protocol.c \
+	src/hardware/uni-t-ut32x/api.c \
+	src/hardware/victor-dmm/protocol.h \
+	src/hardware/victor-dmm/protocol.c \
+	src/hardware/victor-dmm/api.c \
+	src/hardware/yokogawa-dlm/protocol.h \
+	src/hardware/yokogawa-dlm/protocol.c \
+	src/hardware/yokogawa-dlm/protocol_wrappers.h \
+	src/hardware/yokogawa-dlm/protocol_wrappers.c \
+	src/hardware/yokogawa-dlm/api.c \
+	src/hardware/zeroplus-logic-cube/analyzer.c \
+	src/hardware/zeroplus-logic-cube/analyzer.h \
+	src/hardware/zeroplus-logic-cube/gl_usb.h \
+	src/hardware/zeroplus-logic-cube/gl_usb.c \
+	src/hardware/zeroplus-logic-cube/protocol.h \
+	src/hardware/zeroplus-logic-cube/protocol.c \
+	src/hardware/zeroplus-logic-cube/api.c
+ at NEED_RPC_TRUE@am__objects_1 = src/scpi/scpi_vxi.lo \
+ at NEED_RPC_TRUE@	src/scpi/vxi_clnt.lo src/scpi/vxi_xdr.lo
+ at NEED_SERIAL_TRUE@am__objects_2 = src/serial.lo \
+ at NEED_SERIAL_TRUE@	src/scpi/scpi_serial.lo
+ at NEED_USB_TRUE@am__objects_3 = src/ezusb.lo src/usb.lo \
+ at NEED_USB_TRUE@	src/scpi/scpi_usbtmc_libusb.lo
+ at NEED_VISA_TRUE@am__objects_4 = src/scpi/scpi_visa.lo
+ at NEED_GPIB_TRUE@am__objects_5 = src/scpi/scpi_libgpib.lo
+ at NEED_SERIAL_TRUE@am__objects_6 = src/modbus/modbus_serial_rtu.lo
+ at NEED_SERIAL_TRUE@am__objects_7 = src/lcr/es51919.lo
+ at HW_AGILENT_DMM_TRUE@am__objects_8 = src/hardware/agilent-dmm/api.lo \
+ at HW_AGILENT_DMM_TRUE@	src/hardware/agilent-dmm/sched.lo
+ at HW_APPA_55II_TRUE@am__objects_9 = src/hardware/appa-55ii/protocol.lo \
+ at HW_APPA_55II_TRUE@	src/hardware/appa-55ii/api.lo
+ at HW_ASIX_SIGMA_TRUE@am__objects_10 =  \
+ at HW_ASIX_SIGMA_TRUE@	src/hardware/asix-sigma/protocol.lo \
+ at HW_ASIX_SIGMA_TRUE@	src/hardware/asix-sigma/api.lo
+ at HW_ATTEN_PPS3XXX_TRUE@am__objects_11 =  \
+ at HW_ATTEN_PPS3XXX_TRUE@	src/hardware/atten-pps3xxx/protocol.lo \
+ at HW_ATTEN_PPS3XXX_TRUE@	src/hardware/atten-pps3xxx/api.lo
+ at HW_BAYLIBRE_ACME_TRUE@am__objects_12 =  \
+ at HW_BAYLIBRE_ACME_TRUE@	src/hardware/baylibre-acme/protocol.lo \
+ at HW_BAYLIBRE_ACME_TRUE@	src/hardware/baylibre-acme/api.lo \
+ at HW_BAYLIBRE_ACME_TRUE@	src/hardware/baylibre-acme/gpio.lo
+ at HW_BEAGLELOGIC_TRUE@am__objects_13 =  \
+ at HW_BEAGLELOGIC_TRUE@	src/hardware/beaglelogic/protocol.lo \
+ at HW_BEAGLELOGIC_TRUE@	src/hardware/beaglelogic/api.lo
+ at HW_BRYMEN_BM86X_TRUE@am__objects_14 =  \
+ at HW_BRYMEN_BM86X_TRUE@	src/hardware/brymen-bm86x/protocol.lo \
+ at HW_BRYMEN_BM86X_TRUE@	src/hardware/brymen-bm86x/api.lo
+ at HW_BRYMEN_DMM_TRUE@am__objects_15 =  \
+ at HW_BRYMEN_DMM_TRUE@	src/hardware/brymen-dmm/parser.lo \
+ at HW_BRYMEN_DMM_TRUE@	src/hardware/brymen-dmm/protocol.lo \
+ at HW_BRYMEN_DMM_TRUE@	src/hardware/brymen-dmm/api.lo
+ at HW_CEM_DT_885X_TRUE@am__objects_16 =  \
+ at HW_CEM_DT_885X_TRUE@	src/hardware/cem-dt-885x/protocol.lo \
+ at HW_CEM_DT_885X_TRUE@	src/hardware/cem-dt-885x/api.lo
+ at HW_CENTER_3XX_TRUE@am__objects_17 =  \
+ at HW_CENTER_3XX_TRUE@	src/hardware/center-3xx/protocol.lo \
+ at HW_CENTER_3XX_TRUE@	src/hardware/center-3xx/api.lo
+ at HW_CHRONOVU_LA_TRUE@am__objects_18 =  \
+ at HW_CHRONOVU_LA_TRUE@	src/hardware/chronovu-la/protocol.lo \
+ at HW_CHRONOVU_LA_TRUE@	src/hardware/chronovu-la/api.lo
+ at HW_COLEAD_SLM_TRUE@am__objects_19 =  \
+ at HW_COLEAD_SLM_TRUE@	src/hardware/colead-slm/protocol.lo \
+ at HW_COLEAD_SLM_TRUE@	src/hardware/colead-slm/api.lo
+ at HW_CONRAD_DIGI_35_CPU_TRUE@am__objects_20 = src/hardware/conrad-digi-35-cpu/protocol.lo \
+ at HW_CONRAD_DIGI_35_CPU_TRUE@	src/hardware/conrad-digi-35-cpu/api.lo
+ at HW_DEMO_TRUE@am__objects_21 = src/hardware/demo/demo.lo
+ at HW_DEREE_DE5000_TRUE@am__objects_22 =  \
+ at HW_DEREE_DE5000_TRUE@	src/hardware/deree-de5000/api.lo
+ at HW_FLUKE_DMM_TRUE@am__objects_23 = src/hardware/fluke-dmm/fluke.lo \
+ at HW_FLUKE_DMM_TRUE@	src/hardware/fluke-dmm/api.lo
+ at HW_FX2LAFW_TRUE@am__objects_24 = src/hardware/fx2lafw/protocol.lo \
+ at HW_FX2LAFW_TRUE@	src/hardware/fx2lafw/api.lo \
+ at HW_FX2LAFW_TRUE@	src/hardware/fx2lafw/dslogic.lo
+ at HW_GMC_MH_1X_2X_TRUE@am__objects_25 =  \
+ at HW_GMC_MH_1X_2X_TRUE@	src/hardware/gmc-mh-1x-2x/protocol.lo \
+ at HW_GMC_MH_1X_2X_TRUE@	src/hardware/gmc-mh-1x-2x/api.lo
+ at HW_GWINSTEK_GDS_800_TRUE@am__objects_26 = src/hardware/gwinstek-gds-800/protocol.lo \
+ at HW_GWINSTEK_GDS_800_TRUE@	src/hardware/gwinstek-gds-800/api.lo
+ at HW_HAMEG_HMO_TRUE@am__objects_27 =  \
+ at HW_HAMEG_HMO_TRUE@	src/hardware/hameg-hmo/protocol.lo \
+ at HW_HAMEG_HMO_TRUE@	src/hardware/hameg-hmo/api.lo
+ at HW_HANTEK_DSO_TRUE@am__objects_28 = src/hardware/hantek-dso/dso.lo \
+ at HW_HANTEK_DSO_TRUE@	src/hardware/hantek-dso/api.lo
+ at HW_HUNG_CHANG_DSO_2100_TRUE@am__objects_29 = src/hardware/hung-chang-dso-2100/protocol.lo \
+ at HW_HUNG_CHANG_DSO_2100_TRUE@	src/hardware/hung-chang-dso-2100/api.lo
+ at HW_IKALOGIC_SCANALOGIC2_TRUE@am__objects_30 = src/hardware/ikalogic-scanalogic2/protocol.lo \
+ at HW_IKALOGIC_SCANALOGIC2_TRUE@	src/hardware/ikalogic-scanalogic2/api.lo
+ at HW_IKALOGIC_SCANAPLUS_TRUE@am__objects_31 = src/hardware/ikalogic-scanaplus/protocol.lo \
+ at HW_IKALOGIC_SCANAPLUS_TRUE@	src/hardware/ikalogic-scanaplus/api.lo
+ at HW_KECHENG_KC_330B_TRUE@am__objects_32 = src/hardware/kecheng-kc-330b/protocol.lo \
+ at HW_KECHENG_KC_330B_TRUE@	src/hardware/kecheng-kc-330b/api.lo
+ at HW_KERN_SCALE_TRUE@am__objects_33 =  \
+ at HW_KERN_SCALE_TRUE@	src/hardware/kern-scale/protocol.lo \
+ at HW_KERN_SCALE_TRUE@	src/hardware/kern-scale/api.lo
+ at HW_KORAD_KAXXXXP_TRUE@am__objects_34 =  \
+ at HW_KORAD_KAXXXXP_TRUE@	src/hardware/korad-kaxxxxp/protocol.lo \
+ at HW_KORAD_KAXXXXP_TRUE@	src/hardware/korad-kaxxxxp/api.lo
+ at HW_LASCAR_EL_USB_TRUE@am__objects_35 =  \
+ at HW_LASCAR_EL_USB_TRUE@	src/hardware/lascar-el-usb/protocol.lo \
+ at HW_LASCAR_EL_USB_TRUE@	src/hardware/lascar-el-usb/api.lo
+ at HW_LECROY_LOGICSTUDIO_TRUE@am__objects_36 = src/hardware/lecroy-logicstudio/protocol.lo \
+ at HW_LECROY_LOGICSTUDIO_TRUE@	src/hardware/lecroy-logicstudio/api.lo
+ at HW_MANSON_HCS_3XXX_TRUE@am__objects_37 = src/hardware/manson-hcs-3xxx/protocol.lo \
+ at HW_MANSON_HCS_3XXX_TRUE@	src/hardware/manson-hcs-3xxx/api.lo
+ at HW_MAYNUO_M97_TRUE@am__objects_38 =  \
+ at HW_MAYNUO_M97_TRUE@	src/hardware/maynuo-m97/protocol.lo \
+ at HW_MAYNUO_M97_TRUE@	src/hardware/maynuo-m97/api.lo
+ at HW_MIC_985XX_TRUE@am__objects_39 =  \
+ at HW_MIC_985XX_TRUE@	src/hardware/mic-985xx/protocol.lo \
+ at HW_MIC_985XX_TRUE@	src/hardware/mic-985xx/api.lo
+ at HW_MOTECH_LPS_30X_TRUE@am__objects_40 = src/hardware/motech-lps-30x/protocol.lo \
+ at HW_MOTECH_LPS_30X_TRUE@	src/hardware/motech-lps-30x/api.lo
+ at HW_NORMA_DMM_TRUE@am__objects_41 =  \
+ at HW_NORMA_DMM_TRUE@	src/hardware/norma-dmm/protocol.lo \
+ at HW_NORMA_DMM_TRUE@	src/hardware/norma-dmm/api.lo
+ at HW_OPENBENCH_LOGIC_SNIFFER_TRUE@am__objects_42 = src/hardware/openbench-logic-sniffer/protocol.lo \
+ at HW_OPENBENCH_LOGIC_SNIFFER_TRUE@	src/hardware/openbench-logic-sniffer/api.lo
+ at HW_PIPISTRELLO_OLS_TRUE@am__objects_43 = src/hardware/pipistrello-ols/protocol.lo \
+ at HW_PIPISTRELLO_OLS_TRUE@	src/hardware/pipistrello-ols/api.lo
+ at HW_RIGOL_DS_TRUE@am__objects_44 = src/hardware/rigol-ds/protocol.lo \
+ at HW_RIGOL_DS_TRUE@	src/hardware/rigol-ds/api.lo
+ at HW_SALEAE_LOGIC16_TRUE@am__objects_45 = src/hardware/saleae-logic16/protocol.lo \
+ at HW_SALEAE_LOGIC16_TRUE@	src/hardware/saleae-logic16/api.lo
+ at HW_SCPI_PPS_TRUE@am__objects_46 = src/hardware/scpi-pps/protocol.lo \
+ at HW_SCPI_PPS_TRUE@	src/hardware/scpi-pps/profiles.lo \
+ at HW_SCPI_PPS_TRUE@	src/hardware/scpi-pps/api.lo
+ at HW_SERIAL_DMM_TRUE@am__objects_47 =  \
+ at HW_SERIAL_DMM_TRUE@	src/hardware/serial-dmm/protocol.lo \
+ at HW_SERIAL_DMM_TRUE@	src/hardware/serial-dmm/api.lo
+ at HW_SYSCLK_LWLA_TRUE@am__objects_48 =  \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/lwla.lo \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/lwla1016.lo \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/lwla1034.lo \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/protocol.lo \
+ at HW_SYSCLK_LWLA_TRUE@	src/hardware/sysclk-lwla/api.lo
+ at HW_TELEINFO_TRUE@am__objects_49 = src/hardware/teleinfo/protocol.lo \
+ at HW_TELEINFO_TRUE@	src/hardware/teleinfo/api.lo
+ at HW_TESTO_TRUE@am__objects_50 = src/hardware/testo/protocol.lo \
+ at HW_TESTO_TRUE@	src/hardware/testo/api.lo
+ at HW_TONDAJ_SL_814_TRUE@am__objects_51 =  \
+ at HW_TONDAJ_SL_814_TRUE@	src/hardware/tondaj-sl-814/protocol.lo \
+ at HW_TONDAJ_SL_814_TRUE@	src/hardware/tondaj-sl-814/api.lo
+ at HW_UNI_T_DMM_TRUE@am__objects_52 =  \
+ at HW_UNI_T_DMM_TRUE@	src/hardware/uni-t-dmm/protocol.lo \
+ at HW_UNI_T_DMM_TRUE@	src/hardware/uni-t-dmm/api.lo
+ at HW_UNI_T_UT32X_TRUE@am__objects_53 =  \
+ at HW_UNI_T_UT32X_TRUE@	src/hardware/uni-t-ut32x/protocol.lo \
+ at HW_UNI_T_UT32X_TRUE@	src/hardware/uni-t-ut32x/api.lo
+ at HW_VICTOR_DMM_TRUE@am__objects_54 =  \
+ at HW_VICTOR_DMM_TRUE@	src/hardware/victor-dmm/protocol.lo \
+ at HW_VICTOR_DMM_TRUE@	src/hardware/victor-dmm/api.lo
+ at HW_YOKOGAWA_DLM_TRUE@am__objects_55 =  \
+ at HW_YOKOGAWA_DLM_TRUE@	src/hardware/yokogawa-dlm/protocol.lo \
+ at HW_YOKOGAWA_DLM_TRUE@	src/hardware/yokogawa-dlm/protocol_wrappers.lo \
+ at HW_YOKOGAWA_DLM_TRUE@	src/hardware/yokogawa-dlm/api.lo
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@am__objects_56 = src/hardware/zeroplus-logic-cube/analyzer.lo \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/gl_usb.lo \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/protocol.lo \
+ at HW_ZEROPLUS_LOGIC_CUBE_TRUE@	src/hardware/zeroplus-logic-cube/api.lo
+am_libsigrok_la_OBJECTS = src/backend.lo src/device.lo src/session.lo \
+	src/session_file.lo src/session_driver.lo src/drivers.lo \
+	src/hwdriver.lo src/trigger.lo src/soft-trigger.lo \
+	src/analog.lo src/fallback.lo src/resource.lo src/strutil.lo \
+	src/log.lo src/version.lo src/error.lo src/std.lo \
+	src/input/input.lo src/input/binary.lo \
+	src/input/chronovu_la8.lo src/input/csv.lo \
+	src/input/raw_analog.lo src/input/trace32_ad.lo \
+	src/input/vcd.lo src/input/wav.lo src/output/output.lo \
+	src/output/analog.lo src/output/ascii.lo src/output/bits.lo \
+	src/output/binary.lo src/output/csv.lo \
+	src/output/chronovu_la8.lo src/output/wav.lo \
+	src/output/gnuplot.lo src/output/hex.lo src/output/ols.lo \
+	src/output/srzip.lo src/output/vcd.lo \
+	src/transform/transform.lo src/transform/nop.lo \
+	src/transform/scale.lo src/transform/invert.lo \
+	src/scpi/scpi.lo src/scpi/helpers.lo src/scpi/scpi_tcp.lo \
+	$(am__objects_1) $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4) $(am__objects_5) src/modbus/modbus.lo \
+	$(am__objects_6) src/dmm/es519xx.lo src/dmm/fs9721.lo \
+	src/dmm/fs9922.lo src/dmm/m2110.lo src/dmm/metex14.lo \
+	src/dmm/rs9lcd.lo src/dmm/bm25x.lo src/dmm/ut71x.lo \
+	src/dmm/ut372.lo src/dmm/vc870.lo src/dmm/dtm0660.lo \
+	$(am__objects_7) src/scale/kern.lo $(am__objects_8) \
+	$(am__objects_9) $(am__objects_10) $(am__objects_11) \
+	$(am__objects_12) $(am__objects_13) $(am__objects_14) \
+	$(am__objects_15) $(am__objects_16) $(am__objects_17) \
+	$(am__objects_18) $(am__objects_19) $(am__objects_20) \
+	$(am__objects_21) $(am__objects_22) $(am__objects_23) \
+	$(am__objects_24) $(am__objects_25) $(am__objects_26) \
+	$(am__objects_27) $(am__objects_28) $(am__objects_29) \
+	$(am__objects_30) $(am__objects_31) $(am__objects_32) \
+	$(am__objects_33) $(am__objects_34) $(am__objects_35) \
+	$(am__objects_36) $(am__objects_37) $(am__objects_38) \
+	$(am__objects_39) $(am__objects_40) $(am__objects_41) \
+	$(am__objects_42) $(am__objects_43) $(am__objects_44) \
+	$(am__objects_45) $(am__objects_46) $(am__objects_47) \
+	$(am__objects_48) $(am__objects_49) $(am__objects_50) \
+	$(am__objects_51) $(am__objects_52) $(am__objects_53) \
+	$(am__objects_54) $(am__objects_55) $(am__objects_56)
+libsigrok_la_OBJECTS = $(am_libsigrok_la_OBJECTS)
 libsigrok_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
 	$(libsigrok_la_LDFLAGS) $(LDFLAGS) -o $@
- at HAVE_CHECK_TRUE@am__EXEEXT_1 = tests/check_main$(EXEEXT)
-am__tests_check_main_SOURCES_DIST = libsigrok.h tests/lib.c \
-	tests/lib.h tests/check_main.c tests/check_core.c \
-	tests/check_input_all.c tests/check_input_binary.c \
-	tests/check_output_all.c tests/check_strutil.c \
-	tests/check_version.c tests/check_driver_all.c
- at HAVE_CHECK_TRUE@am_tests_check_main_OBJECTS =  \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-lib.$(OBJEXT) \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-check_main.$(OBJEXT) \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-check_core.$(OBJEXT) \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-check_input_all.$(OBJEXT) \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-check_input_binary.$(OBJEXT) \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-check_output_all.$(OBJEXT) \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-check_strutil.$(OBJEXT) \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-check_version.$(OBJEXT) \
- at HAVE_CHECK_TRUE@	tests/tests_check_main-check_driver_all.$(OBJEXT)
-tests_check_main_OBJECTS = $(am_tests_check_main_OBJECTS)
- at HAVE_CHECK_TRUE@tests_check_main_DEPENDENCIES =  \
- at HAVE_CHECK_TRUE@	$(top_builddir)/libsigrok.la
-tests_check_main_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
-	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
-	$(tests_check_main_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
-	-o $@
+ at HAVE_CHECK_TRUE@am__EXEEXT_1 = tests/main$(EXEEXT)
+am_tests_main_OBJECTS = tests/lib.$(OBJEXT) tests/main.$(OBJEXT) \
+	tests/core.$(OBJEXT) tests/input_all.$(OBJEXT) \
+	tests/input_binary.$(OBJEXT) tests/output_all.$(OBJEXT) \
+	tests/transform_all.$(OBJEXT) tests/session.$(OBJEXT) \
+	tests/strutil.$(OBJEXT) tests/version.$(OBJEXT) \
+	tests/driver_all.$(OBJEXT) tests/device.$(OBJEXT) \
+	tests/trigger.$(OBJEXT) tests/analog.$(OBJEXT)
+tests_main_OBJECTS = $(am_tests_main_OBJECTS)
+tests_main_DEPENDENCIES = libsigrok.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
 AM_V_P = $(am__v_P_ at AM_V@)
 am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -584,7 +848,7 @@ AM_V_at = $(am__v_at_ at AM_V@)
 am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
 am__v_at_0 = @
 am__v_at_1 = 
-DEFAULT_INCLUDES = -I. at am__isrc@
+DEFAULT_INCLUDES = 
 depcomp = $(SHELL) $(top_srcdir)/autostuff/depcomp
 am__depfiles_maybe = depfiles
 am__mv = mv -f
@@ -606,16 +870,40 @@ AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libsigrok_la_SOURCES) $(tests_check_main_SOURCES)
-DIST_SOURCES = $(am__libsigrok_la_SOURCES_DIST) \
-	$(am__tests_check_main_SOURCES_DIST)
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_ at AM_V@)
+am__v_CXX_ = $(am__v_CXX_ at AM_DEFAULT_V@)
+am__v_CXX_0 = @echo "  CXX     " $@;
+am__v_CXX_1 = 
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+	$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_ at AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_ at AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo "  CXXLD   " $@;
+am__v_CXXLD_1 = 
+SOURCES = $(bindings_cxx_libsigrokcxx_la_SOURCES) \
+	$(libsigrok_la_SOURCES) $(tests_main_SOURCES)
+DIST_SOURCES = $(am__bindings_cxx_libsigrokcxx_la_SOURCES_DIST) \
+	$(am__libsigrok_la_SOURCES_DIST) $(tests_main_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
 DATA = $(pkgconfig_DATA)
-HEADERS = $(library_include_HEADERS) $(noinst_HEADERS)
+am__bindings_cxx_libsigrokcxx_la_include_HEADERS_DIST =  \
+	bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp
+HEADERS = $(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+	$(library_include_HEADERS) \
+	$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+	$(nodist_library_include_HEADERS) $(noinst_HEADERS)
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
 	$(LISP)config.h.in
 # Read a list of newline-separated strings from the standard input,
@@ -814,6 +1102,22 @@ TEST_LOGS = $(am__test_logs2:.test.log=.log)
 TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/autostuff/test-driver
 TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
 	$(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
+	$(srcdir)/libsigrok.pc.in $(top_srcdir)/autostuff/ar-lib \
+	$(top_srcdir)/autostuff/compile \
+	$(top_srcdir)/autostuff/config.guess \
+	$(top_srcdir)/autostuff/config.sub \
+	$(top_srcdir)/autostuff/depcomp \
+	$(top_srcdir)/autostuff/install-sh \
+	$(top_srcdir)/autostuff/ltmain.sh \
+	$(top_srcdir)/autostuff/missing \
+	$(top_srcdir)/autostuff/test-driver \
+	$(top_srcdir)/bindings/cxx/libsigrokcxx.pc.in \
+	$(top_srcdir)/include/libsigrok/version.h.in AUTHORS COPYING \
+	ChangeLog INSTALL NEWS README autostuff/ar-lib \
+	autostuff/compile autostuff/config.guess autostuff/config.sub \
+	autostuff/depcomp autostuff/install-sh autostuff/ltmain.sh \
+	autostuff/missing
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
@@ -834,7 +1138,6 @@ distcleancheck_listfiles = find . -type f -print
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
-AM_LIBTOOLFLAGS = @AM_LIBTOOLFLAGS@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -843,8 +1146,13 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CONFIG_STATUS_DEPENDENCIES = @CONFIG_STATUS_DEPENDENCIES@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
@@ -857,28 +1165,29 @@ ECHO_T = @ECHO_T@
 EGREP = @EGREP@
 EXEEXT = @EXEEXT@
 FGREP = @FGREP@
-FIRMWARE_DIR = @FIRMWARE_DIR@
-GLIB_CFLAGS = @GLIB_CFLAGS@
-GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
-GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
-GLIB_LIBS = @GLIB_LIBS@
-GLIB_MKENUMS = @GLIB_MKENUMS@
-GOBJECT_QUERY = @GOBJECT_QUERY@
 GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
+HAVE_DOXYGEN = @HAVE_DOXYGEN@
+HAVE_JAVAC = @HAVE_JAVAC@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JAVAC = @JAVAC@
+JNI_CPPFLAGS = @JNI_CPPFLAGS@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
+LIBSIGROKCXX_CFLAGS = @LIBSIGROKCXX_CFLAGS@
+LIBSIGROKCXX_LIBS = @LIBSIGROKCXX_LIBS@
+LIBSIGROK_CFLAGS = @LIBSIGROK_CFLAGS@
+LIBSIGROK_LIBS = @LIBSIGROK_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
-MAKEFLAGS = @MAKEFLAGS@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
@@ -899,28 +1208,42 @@ PATH_SEPARATOR = @PATH_SEPARATOR@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PYSIGROK_CFLAGS = @PYSIGROK_CFLAGS@
+PYSIGROK_LIBS = @PYSIGROK_LIBS@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
 RANLIB = @RANLIB@
+RBSIGROK_CFLAGS = @RBSIGROK_CFLAGS@
+RBSIGROK_EXTDIR = @RBSIGROK_EXTDIR@
+RBSIGROK_LIBS = @RBSIGROK_LIBS@
+RUBY = @RUBY@
+RUBY_DLEXT = @RUBY_DLEXT@
 SED = @SED@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
-SR_LIB_LDFLAGS = @SR_LIB_LDFLAGS@
+SR_EXTRA_CFLAGS = @SR_EXTRA_CFLAGS@
+SR_EXTRA_LIBS = @SR_EXTRA_LIBS@
 SR_LIB_VERSION = @SR_LIB_VERSION@
-SR_LIB_VERSION_AGE = @SR_LIB_VERSION_AGE@
-SR_LIB_VERSION_CURRENT = @SR_LIB_VERSION_CURRENT@
-SR_LIB_VERSION_REVISION = @SR_LIB_VERSION_REVISION@
 SR_PACKAGE_VERSION = @SR_PACKAGE_VERSION@
-SR_PACKAGE_VERSION_MAJOR = @SR_PACKAGE_VERSION_MAJOR@
-SR_PACKAGE_VERSION_MICRO = @SR_PACKAGE_VERSION_MICRO@
-SR_PACKAGE_VERSION_MINOR = @SR_PACKAGE_VERSION_MINOR@
 SR_PKGLIBS = @SR_PKGLIBS@
+SR_WFLAGS = @SR_WFLAGS@
+SR_WXXFLAGS = @SR_WXXFLAGS@
 STRIP = @STRIP@
+SWIG = @SWIG@
+TESTS_CFLAGS = @TESTS_CFLAGS@
+TESTS_LIBS = @TESTS_LIBS@
 VERSION = @VERSION@
+_ACJNI_JAVAC = @_ACJNI_JAVAC@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
 abs_top_srcdir = @abs_top_srcdir@
 ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
@@ -934,8 +1257,6 @@ build_cpu = @build_cpu@
 build_os = @build_os@
 build_vendor = @build_vendor@
 builddir = @builddir@
-check_CFLAGS = @check_CFLAGS@
-check_LIBS = @check_LIBS@
 datadir = @datadir@
 datarootdir = @datarootdir@
 docdir = @docdir@
@@ -952,25 +1273,20 @@ infodir = @infodir@
 install_sh = @install_sh@
 libdir = @libdir@
 libexecdir = @libexecdir@
-libftdi_CFLAGS = @libftdi_CFLAGS@
-libftdi_LIBS = @libftdi_LIBS@
-librevisa_CFLAGS = @librevisa_CFLAGS@
-librevisa_LIBS = @librevisa_LIBS@
-libserialport_CFLAGS = @libserialport_CFLAGS@
-libserialport_LIBS = @libserialport_LIBS@
-libusb_CFLAGS = @libusb_CFLAGS@
-libusb_LIBS = @libusb_LIBS@
-libzip_CFLAGS = @libzip_CFLAGS@
-libzip_LIBS = @libzip_LIBS@
 localedir = @localedir@
 localstatedir = @localstatedir@
 mandir = @mandir@
 mkdir_p = @mkdir_p@
 oldincludedir = @oldincludedir@
 pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -979,53 +1295,116 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-ACLOCAL_AMFLAGS = -I autostuff
-AM_CPPFLAGS = -DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
-lib_LTLIBRARIES = libsigrok.la
+ACLOCAL_AMFLAGS = -I m4
+AM_LIBTOOLFLAGS = --silent
+GNUMAKEFLAGS = --no-print-directory
+
+# distutils/setuptools cause trouble on distcheck. Disable for now.
+DISTCHECK_CONFIGURE_FLAGS = --disable-python
+FIRMWARE_DIR = $(datadir)/sigrok-firmware
+local_includes = -Iinclude -I$(srcdir)/include -I$(srcdir)/src -I. \
+	$(am__append_1)
+ at WIN32_FALSE@global_defs = -DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
+# Do not hard-code the firmware location on Windows.
+ at WIN32_TRUE@global_defs = 
+# Ensure that local include directories are always searched first.
+AM_CPPFLAGS = $(local_includes) $(global_defs)
+
+# The tests CFLAGS are a superset of the libsigrok CFLAGS, and the
+# python bindings CFLAGS are a superset of the C++ bindings CFLAGS.
+AM_CFLAGS = $(SR_EXTRA_CFLAGS) $(SR_WFLAGS) $(TESTS_CFLAGS)
+AM_CXXFLAGS = $(SR_WXXFLAGS) $(LIBSIGROKCXX_CFLAGS)
+lib_LTLIBRARIES = libsigrok.la $(am__append_58)
 
 # Backend files
 
-# Input formats
-
-# Output formats
-
-# Hardware (common files)
-
-# Hardware (DMM parsers)
-libsigrok_la_SOURCES = backend.c device.c session.c session_file.c \
-	session_driver.c hwdriver.c strutil.c log.c version.c error.c \
-	std.c input/binary.c input/chronovu_la8.c input/csv.c \
-	input/input.c input/vcd.c input/wav.c output/output.c \
-	output/analog.c output/ascii.c output/bits.c output/binary.c \
-	output/csv.c output/chronovu_la8.c output/gnuplot.c \
-	output/hex.c output/ols.c output/vcd.c hardware/common/scpi.c \
-	hardware/common/scpi_tcp.c $(am__append_1) $(am__append_2) \
-	$(am__append_3) $(am__append_4) hardware/common/dmm/es519xx.c \
-	hardware/common/dmm/fs9721.c hardware/common/dmm/fs9922.c \
-	hardware/common/dmm/m2110.c hardware/common/dmm/metex14.c \
-	hardware/common/dmm/rs9lcd.c $(am__append_5) $(am__append_6) \
-	$(am__append_7) $(am__append_8) $(am__append_9) \
-	$(am__append_10) $(am__append_11) $(am__append_12) \
-	$(am__append_13) $(am__append_14) $(am__append_15) \
-	$(am__append_16) $(am__append_17) $(am__append_18) \
-	$(am__append_19) $(am__append_20) $(am__append_21) \
-	$(am__append_22) $(am__append_23) $(am__append_24) \
-	$(am__append_25) $(am__append_26) $(am__append_27) \
-	$(am__append_28) $(am__append_29) $(am__append_30) \
-	$(am__append_31) $(am__append_32) $(am__append_33) \
-	$(am__append_34) $(am__append_35) $(am__append_36) \
-	$(am__append_37) $(am__append_38)
-libsigrok_la_LIBADD = $(LIBOBJS)
-libsigrok_la_LDFLAGS = $(SR_LIB_LDFLAGS)
+# Input modules
+
+# Output modules
+
+# Transform modules
+
+# SCPI support
+
+# Modbus support
+
+# Hardware (DMM chip parsers)
+
+# Hardware (Scale protocol parsers)
+libsigrok_la_SOURCES = src/backend.c src/device.c src/session.c \
+	src/session_file.c src/session_driver.c src/drivers.c \
+	src/hwdriver.c src/trigger.c src/soft-trigger.c src/analog.c \
+	src/fallback.c src/resource.c src/strutil.c src/log.c \
+	src/version.c src/error.c src/std.c src/input/input.c \
+	src/input/binary.c src/input/chronovu_la8.c src/input/csv.c \
+	src/input/raw_analog.c src/input/trace32_ad.c src/input/vcd.c \
+	src/input/wav.c src/output/output.c src/output/analog.c \
+	src/output/ascii.c src/output/bits.c src/output/binary.c \
+	src/output/csv.c src/output/chronovu_la8.c src/output/wav.c \
+	src/output/gnuplot.c src/output/hex.c src/output/ols.c \
+	src/output/srzip.c src/output/vcd.c src/transform/transform.c \
+	src/transform/nop.c src/transform/scale.c \
+	src/transform/invert.c src/scpi.h src/scpi/scpi.c \
+	src/scpi/helpers.c src/scpi/scpi_tcp.c $(am__append_2) \
+	$(am__append_3) $(am__append_4) $(am__append_5) \
+	$(am__append_6) src/modbus/modbus.c $(am__append_7) \
+	src/dmm/es519xx.c src/dmm/fs9721.c src/dmm/fs9922.c \
+	src/dmm/m2110.c src/dmm/metex14.c src/dmm/rs9lcd.c \
+	src/dmm/bm25x.c src/dmm/ut71x.c src/dmm/ut372.c \
+	src/dmm/vc870.c src/dmm/dtm0660.c $(am__append_8) \
+	src/scale/kern.c $(am__append_9) $(am__append_10) \
+	$(am__append_11) $(am__append_12) $(am__append_13) \
+	$(am__append_14) $(am__append_15) $(am__append_16) \
+	$(am__append_17) $(am__append_18) $(am__append_19) \
+	$(am__append_20) $(am__append_21) $(am__append_22) \
+	$(am__append_23) $(am__append_24) $(am__append_25) \
+	$(am__append_26) $(am__append_27) $(am__append_28) \
+	$(am__append_29) $(am__append_30) $(am__append_31) \
+	$(am__append_32) $(am__append_33) $(am__append_34) \
+	$(am__append_35) $(am__append_36) $(am__append_37) \
+	$(am__append_38) $(am__append_39) $(am__append_40) \
+	$(am__append_41) $(am__append_42) $(am__append_43) \
+	$(am__append_44) $(am__append_45) $(am__append_46) \
+	$(am__append_47) $(am__append_48) $(am__append_49) \
+	$(am__append_50) $(am__append_51) $(am__append_52) \
+	$(am__append_53) $(am__append_54) $(am__append_55) \
+	$(am__append_56) $(am__append_57)
+libsigrok_la_LIBADD = $(SR_EXTRA_LIBS) $(LIBSIGROK_LIBS)
+libsigrok_la_LDFLAGS = -version-info $(SR_LIB_VERSION) -no-undefined
 library_includedir = $(includedir)/libsigrok
-library_include_HEADERS = libsigrok.h proto.h version.h
-noinst_HEADERS = libsigrok-internal.h
+library_include_HEADERS = \
+	include/libsigrok/libsigrok.h \
+	include/libsigrok/proto.h
+
+nodist_library_include_HEADERS = \
+	include/libsigrok/version.h
+
+noinst_HEADERS = src/libsigrok-internal.h
 pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libsigrok.pc
+pkgconfig_DATA = libsigrok.pc $(am__append_59)
 EXTRA_DIST = \
 	Doxyfile \
 	HACKING \
 	README.devices \
+	bindings/cxx/ConfigKey_methods.cpp \
+	bindings/cxx/ConfigKey_methods.hpp \
+	bindings/cxx/ConfigKey_methods.i \
+	bindings/cxx/Doxyfile \
+	bindings/cxx/QuantityFlag_methods.cpp \
+	bindings/cxx/QuantityFlag_methods.hpp \
+	bindings/cxx/enums.py \
+	bindings/python/Doxyfile \
+	bindings/python/setup.py \
+	bindings/python/sigrok/__init__.py \
+	bindings/python/sigrok/core/__init__.py \
+	bindings/python/sigrok/core/classes.i \
+	bindings/java/Doxyfile \
+	bindings/java/org/sigrok/core/classes/classes.i \
+	bindings/java/org/sigrok/core/interfaces/DatafeedCallback.java \
+	bindings/java/org/sigrok/core/interfaces/LogCallback.java \
+	bindings/swig/classes.i \
+	bindings/swig/doc.py \
+	bindings/swig/templates.i \
 	contrib/gnuplot_chronovu_la8.gpi \
 	contrib/gnuplot_rigol_ds1xx2.gpi \
 	contrib/gnuplot_usbeesx.gpi \
@@ -1034,27 +1413,71 @@ EXTRA_DIST = \
 	contrib/sigrok-logo-notext.png \
 	contrib/z60_libsigrok.rules
 
- at HAVE_CHECK_TRUE@tests_check_main_SOURCES = \
- at HAVE_CHECK_TRUE@	libsigrok.h \
- at HAVE_CHECK_TRUE@	tests/lib.c \
- at HAVE_CHECK_TRUE@	tests/lib.h \
- at HAVE_CHECK_TRUE@	tests/check_main.c \
- at HAVE_CHECK_TRUE@	tests/check_core.c \
- at HAVE_CHECK_TRUE@	tests/check_input_all.c \
- at HAVE_CHECK_TRUE@	tests/check_input_binary.c \
- at HAVE_CHECK_TRUE@	tests/check_output_all.c \
- at HAVE_CHECK_TRUE@	tests/check_strutil.c \
- at HAVE_CHECK_TRUE@	tests/check_version.c \
- at HAVE_CHECK_TRUE@	tests/check_driver_all.c
-
- at HAVE_CHECK_TRUE@tests_check_main_CFLAGS = @check_CFLAGS@
- at HAVE_CHECK_TRUE@tests_check_main_LDADD = $(top_builddir)/libsigrok.la @check_LIBS@
-MAINTAINERCLEANFILES = ChangeLog
+tests_main_SOURCES = \
+	include/libsigrok/libsigrok.h \
+	tests/lib.c \
+	tests/lib.h \
+	tests/main.c \
+	tests/core.c \
+	tests/input_all.c \
+	tests/input_binary.c \
+	tests/output_all.c \
+	tests/transform_all.c \
+	tests/session.c \
+	tests/strutil.c \
+	tests/version.c \
+	tests/driver_all.c \
+	tests/device.c \
+	tests/trigger.c \
+	tests/analog.c
+
+tests_main_LDADD = libsigrok.la $(SR_EXTRA_LIBS) $(TESTS_LIBS)
+BUILD_EXTRA = $(am__append_61) $(am__append_64) $(am__append_67)
+INSTALL_EXTRA = $(am__append_62) $(am__append_65) $(am__append_68)
+UNINSTALL_EXTRA = $(am__append_69)
+CLEAN_EXTRA = $(am__append_60) $(am__append_63) $(am__append_66) \
+	$(am__append_70)
+ at BINDINGS_CXX_TRUE@bindings_cxx_libsigrokcxx_la_SOURCES = bindings/cxx/classes.cpp
+ at BINDINGS_CXX_TRUE@bindings_cxx_libsigrokcxx_la_LIBADD = libsigrok.la $(SR_EXTRA_LIBS) $(LIBSIGROKCXX_LIBS)
+ at BINDINGS_CXX_TRUE@bindings_cxx_libsigrokcxx_la_LDFLAGS = -version-info $(SR_LIB_VERSION) -no-undefined
+ at BINDINGS_CXX_TRUE@bindings_cxx_libsigrokcxx_la_includedir = $(includedir)/libsigrokcxx
+ at BINDINGS_CXX_TRUE@bindings_cxx_libsigrokcxx_la_include_HEADERS = \
+ at BINDINGS_CXX_TRUE@	bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp
+
+ at BINDINGS_CXX_TRUE@nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS = \
+ at BINDINGS_CXX_TRUE@	bindings/cxx/include/libsigrokcxx/enums.hpp
+
+CPPXMLDOC = bindings/cxx/doxy/xml/index.xml
+
+# Macro definitions to be used by the SWIG parser.
+swig_defs = -Dnoexcept= -Dprivate=protected -DG_GNUC_BEGIN_IGNORE_DEPRECATIONS= -DG_GNUC_END_IGNORE_DEPRECATIONS=
+ at BINDINGS_PYTHON_TRUE@PDIR = bindings/python
+ at BINDINGS_PYTHON_TRUE@PDOC_START = bindings/python/sigrok/core/doc_start.i
+ at BINDINGS_PYTHON_TRUE@PDOC_END = bindings/python/sigrok/core/doc_end.i
+ at BINDINGS_PYTHON_TRUE@setup_vars = VERSION='$(PACKAGE_VERSION)' CC='$(CXX)' CFLAGS='$(CXXFLAGS) $(SR_WXXFLAGS) $(PYSIGROK_CFLAGS)' CXXFLAGS='$(CXXFLAGS) $(SR_WXXFLAGS) $(PYSIGROK_CFLAGS)' LDADD='$(PYSIGROK_LIBS)'
+ at BINDINGS_PYTHON_TRUE@setup_quiet = --quiet
+ at BINDINGS_PYTHON_TRUE@setup_py = $(PYTHON) $(srcdir)/$(PDIR)/setup.py $(setup_vars) $(setup_quiet)
+ at BINDINGS_RUBY_TRUE@RDIR = bindings/ruby
+ at BINDINGS_RUBY_TRUE@RDOC = $(RDIR)/doc.i
+ at BINDINGS_RUBY_TRUE@RWRAP = $(RDIR)/classes_wrap.cpp
+ at BINDINGS_RUBY_TRUE@ROBJ = $(RWRAP:.cpp=.o)
+ at BINDINGS_RUBY_TRUE@REXT = $(RDIR)/sigrok.$(RUBY_DLEXT)
+ at BINDINGS_JAVA_TRUE@JDIR = bindings/java
+ at BINDINGS_JAVA_TRUE@JPKG = org/sigrok/core
+ at BINDINGS_JAVA_TRUE@JCLS = $(JDIR)/$(JPKG)/classes
+ at BINDINGS_JAVA_TRUE@JINT = $(JDIR)/$(JPKG)/interfaces
+ at BINDINGS_JAVA_TRUE@JSRC = $(JCLS)/*.java $(srcdir)/$(JINT)/*.java
+ at BINDINGS_JAVA_TRUE@JSWG = $(JCLS)/classes.i
+ at BINDINGS_JAVA_TRUE@JDOC = $(JCLS)/doc.i
+ at BINDINGS_JAVA_TRUE@JCXX = $(JCLS)/classes_wrap.cxx
+ at BINDINGS_JAVA_TRUE@JLIB = $(JDIR)/libsigrok_java_core_classes.so
+ at BINDINGS_JAVA_TRUE@JJAR = $(JDIR)/sigrok-core.jar
+ at BINDINGS_JAVA_TRUE@java_cleanfiles = $(JCXX) $(JCLS)/*.java $(JCLS)/*.class $(JINT)/*.class $(JJAR) $(JLIB)
 all: config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-am
 
 .SUFFIXES:
-.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+.SUFFIXES: .c .cpp .lo .log .o .obj .test .test$(EXEEXT) .trs
 am--refresh: Makefile
 	@:
 $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
@@ -1070,7 +1493,6 @@ $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -1102,12 +1524,20 @@ $(srcdir)/config.h.in:  $(am__configure_deps)
 	rm -f stamp-h1
 	touch $@
 
+include/libsigrok/version.h: include/libsigrok/stamp-h2
+	@test -f $@ || rm -f include/libsigrok/stamp-h2
+	@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) include/libsigrok/stamp-h2
+
+include/libsigrok/stamp-h2: $(top_srcdir)/include/libsigrok/version.h.in $(top_builddir)/config.status
+	@rm -f include/libsigrok/stamp-h2
+	cd $(top_builddir) && $(SHELL) ./config.status include/libsigrok/version.h
+
 distclean-hdr:
-	-rm -f config.h stamp-h1
-version.h: $(top_builddir)/config.status $(srcdir)/version.h.in
-	cd $(top_builddir) && $(SHELL) ./config.status $@
+	-rm -f config.h stamp-h1 include/libsigrok/version.h include/libsigrok/stamp-h2
 libsigrok.pc: $(top_builddir)/config.status $(srcdir)/libsigrok.pc.in
 	cd $(top_builddir) && $(SHELL) ./config.status $@
+bindings/cxx/libsigrokcxx.pc: $(top_builddir)/config.status $(top_srcdir)/bindings/cxx/libsigrokcxx.pc.in
+	cd $(top_builddir) && $(SHELL) ./config.status $@
 
 install-libLTLIBRARIES: $(lib_LTLIBRARIES)
 	@$(NORMAL_INSTALL)
@@ -1143,460 +1573,790 @@ clean-libLTLIBRARIES:
 	  echo rm -f $${locs}; \
 	  rm -f $${locs}; \
 	}
-input/$(am__dirstamp):
-	@$(MKDIR_P) input
-	@: > input/$(am__dirstamp)
-input/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) input/$(DEPDIR)
-	@: > input/$(DEPDIR)/$(am__dirstamp)
-input/binary.lo: input/$(am__dirstamp) input/$(DEPDIR)/$(am__dirstamp)
-input/chronovu_la8.lo: input/$(am__dirstamp) \
-	input/$(DEPDIR)/$(am__dirstamp)
-input/csv.lo: input/$(am__dirstamp) input/$(DEPDIR)/$(am__dirstamp)
-input/input.lo: input/$(am__dirstamp) input/$(DEPDIR)/$(am__dirstamp)
-input/vcd.lo: input/$(am__dirstamp) input/$(DEPDIR)/$(am__dirstamp)
-input/wav.lo: input/$(am__dirstamp) input/$(DEPDIR)/$(am__dirstamp)
-output/$(am__dirstamp):
-	@$(MKDIR_P) output
-	@: > output/$(am__dirstamp)
-output/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) output/$(DEPDIR)
-	@: > output/$(DEPDIR)/$(am__dirstamp)
-output/output.lo: output/$(am__dirstamp) \
-	output/$(DEPDIR)/$(am__dirstamp)
-output/analog.lo: output/$(am__dirstamp) \
-	output/$(DEPDIR)/$(am__dirstamp)
-output/ascii.lo: output/$(am__dirstamp) \
-	output/$(DEPDIR)/$(am__dirstamp)
-output/bits.lo: output/$(am__dirstamp) \
-	output/$(DEPDIR)/$(am__dirstamp)
-output/binary.lo: output/$(am__dirstamp) \
-	output/$(DEPDIR)/$(am__dirstamp)
-output/csv.lo: output/$(am__dirstamp) output/$(DEPDIR)/$(am__dirstamp)
-output/chronovu_la8.lo: output/$(am__dirstamp) \
-	output/$(DEPDIR)/$(am__dirstamp)
-output/gnuplot.lo: output/$(am__dirstamp) \
-	output/$(DEPDIR)/$(am__dirstamp)
-output/hex.lo: output/$(am__dirstamp) output/$(DEPDIR)/$(am__dirstamp)
-output/ols.lo: output/$(am__dirstamp) output/$(DEPDIR)/$(am__dirstamp)
-output/vcd.lo: output/$(am__dirstamp) output/$(DEPDIR)/$(am__dirstamp)
-hardware/common/$(am__dirstamp):
-	@$(MKDIR_P) hardware/common
-	@: > hardware/common/$(am__dirstamp)
-hardware/common/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/common/$(DEPDIR)
-	@: > hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/scpi.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/scpi_tcp.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/scpi_vxi.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/vxi_clnt.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/vxi_xdr.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/serial.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/scpi_serial.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/ezusb.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/usb.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/scpi_usbtmc_libusb.lo:  \
-	hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/scpi_visa.lo: hardware/common/$(am__dirstamp) \
-	hardware/common/$(DEPDIR)/$(am__dirstamp)
-hardware/common/dmm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/common/dmm
-	@: > hardware/common/dmm/$(am__dirstamp)
-hardware/common/dmm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/common/dmm/$(DEPDIR)
-	@: > hardware/common/dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/common/dmm/es519xx.lo: hardware/common/dmm/$(am__dirstamp) \
-	hardware/common/dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/common/dmm/fs9721.lo: hardware/common/dmm/$(am__dirstamp) \
-	hardware/common/dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/common/dmm/fs9922.lo: hardware/common/dmm/$(am__dirstamp) \
-	hardware/common/dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/common/dmm/m2110.lo: hardware/common/dmm/$(am__dirstamp) \
-	hardware/common/dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/common/dmm/metex14.lo: hardware/common/dmm/$(am__dirstamp) \
-	hardware/common/dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/common/dmm/rs9lcd.lo: hardware/common/dmm/$(am__dirstamp) \
-	hardware/common/dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/agilent-dmm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/agilent-dmm
-	@: > hardware/agilent-dmm/$(am__dirstamp)
-hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/agilent-dmm/$(DEPDIR)
-	@: > hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/agilent-dmm/api.lo: hardware/agilent-dmm/$(am__dirstamp) \
-	hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/agilent-dmm/sched.lo: hardware/agilent-dmm/$(am__dirstamp) \
-	hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/appa-55ii/$(am__dirstamp):
-	@$(MKDIR_P) hardware/appa-55ii
-	@: > hardware/appa-55ii/$(am__dirstamp)
-hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/appa-55ii/$(DEPDIR)
-	@: > hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp)
-hardware/appa-55ii/protocol.lo: hardware/appa-55ii/$(am__dirstamp) \
-	hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp)
-hardware/appa-55ii/api.lo: hardware/appa-55ii/$(am__dirstamp) \
-	hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp)
-hardware/asix-sigma/$(am__dirstamp):
-	@$(MKDIR_P) hardware/asix-sigma
-	@: > hardware/asix-sigma/$(am__dirstamp)
-hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/asix-sigma/$(DEPDIR)
-	@: > hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp)
-hardware/asix-sigma/asix-sigma.lo:  \
-	hardware/asix-sigma/$(am__dirstamp) \
-	hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp)
-hardware/atten-pps3xxx/$(am__dirstamp):
-	@$(MKDIR_P) hardware/atten-pps3xxx
-	@: > hardware/atten-pps3xxx/$(am__dirstamp)
-hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/atten-pps3xxx/$(DEPDIR)
-	@: > hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp)
-hardware/atten-pps3xxx/protocol.lo:  \
-	hardware/atten-pps3xxx/$(am__dirstamp) \
-	hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp)
-hardware/atten-pps3xxx/api.lo: hardware/atten-pps3xxx/$(am__dirstamp) \
-	hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp)
-hardware/brymen-bm86x/$(am__dirstamp):
-	@$(MKDIR_P) hardware/brymen-bm86x
-	@: > hardware/brymen-bm86x/$(am__dirstamp)
-hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/brymen-bm86x/$(DEPDIR)
-	@: > hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp)
-hardware/brymen-bm86x/protocol.lo:  \
-	hardware/brymen-bm86x/$(am__dirstamp) \
-	hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp)
-hardware/brymen-bm86x/api.lo: hardware/brymen-bm86x/$(am__dirstamp) \
-	hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp)
-hardware/brymen-dmm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/brymen-dmm
-	@: > hardware/brymen-dmm/$(am__dirstamp)
-hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/brymen-dmm/$(DEPDIR)
-	@: > hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/brymen-dmm/parser.lo: hardware/brymen-dmm/$(am__dirstamp) \
-	hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/brymen-dmm/protocol.lo: hardware/brymen-dmm/$(am__dirstamp) \
-	hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/brymen-dmm/api.lo: hardware/brymen-dmm/$(am__dirstamp) \
-	hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/cem-dt-885x/$(am__dirstamp):
-	@$(MKDIR_P) hardware/cem-dt-885x
-	@: > hardware/cem-dt-885x/$(am__dirstamp)
-hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/cem-dt-885x/$(DEPDIR)
-	@: > hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp)
-hardware/cem-dt-885x/protocol.lo:  \
-	hardware/cem-dt-885x/$(am__dirstamp) \
-	hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp)
-hardware/cem-dt-885x/api.lo: hardware/cem-dt-885x/$(am__dirstamp) \
-	hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp)
-hardware/center-3xx/$(am__dirstamp):
-	@$(MKDIR_P) hardware/center-3xx
-	@: > hardware/center-3xx/$(am__dirstamp)
-hardware/center-3xx/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/center-3xx/$(DEPDIR)
-	@: > hardware/center-3xx/$(DEPDIR)/$(am__dirstamp)
-hardware/center-3xx/protocol.lo: hardware/center-3xx/$(am__dirstamp) \
-	hardware/center-3xx/$(DEPDIR)/$(am__dirstamp)
-hardware/center-3xx/api.lo: hardware/center-3xx/$(am__dirstamp) \
-	hardware/center-3xx/$(DEPDIR)/$(am__dirstamp)
-hardware/chronovu-la/$(am__dirstamp):
-	@$(MKDIR_P) hardware/chronovu-la
-	@: > hardware/chronovu-la/$(am__dirstamp)
-hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/chronovu-la/$(DEPDIR)
-	@: > hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp)
-hardware/chronovu-la/protocol.lo:  \
-	hardware/chronovu-la/$(am__dirstamp) \
-	hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp)
-hardware/chronovu-la/api.lo: hardware/chronovu-la/$(am__dirstamp) \
-	hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp)
-hardware/colead-slm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/colead-slm
-	@: > hardware/colead-slm/$(am__dirstamp)
-hardware/colead-slm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/colead-slm/$(DEPDIR)
-	@: > hardware/colead-slm/$(DEPDIR)/$(am__dirstamp)
-hardware/colead-slm/protocol.lo: hardware/colead-slm/$(am__dirstamp) \
-	hardware/colead-slm/$(DEPDIR)/$(am__dirstamp)
-hardware/colead-slm/api.lo: hardware/colead-slm/$(am__dirstamp) \
-	hardware/colead-slm/$(DEPDIR)/$(am__dirstamp)
-hardware/conrad-digi-35-cpu/$(am__dirstamp):
-	@$(MKDIR_P) hardware/conrad-digi-35-cpu
-	@: > hardware/conrad-digi-35-cpu/$(am__dirstamp)
-hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/conrad-digi-35-cpu/$(DEPDIR)
-	@: > hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp)
-hardware/conrad-digi-35-cpu/protocol.lo:  \
-	hardware/conrad-digi-35-cpu/$(am__dirstamp) \
-	hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp)
-hardware/conrad-digi-35-cpu/api.lo:  \
-	hardware/conrad-digi-35-cpu/$(am__dirstamp) \
-	hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp)
-hardware/demo/$(am__dirstamp):
-	@$(MKDIR_P) hardware/demo
-	@: > hardware/demo/$(am__dirstamp)
-hardware/demo/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/demo/$(DEPDIR)
-	@: > hardware/demo/$(DEPDIR)/$(am__dirstamp)
-hardware/demo/demo.lo: hardware/demo/$(am__dirstamp) \
-	hardware/demo/$(DEPDIR)/$(am__dirstamp)
-hardware/fluke-dmm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/fluke-dmm
-	@: > hardware/fluke-dmm/$(am__dirstamp)
-hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/fluke-dmm/$(DEPDIR)
-	@: > hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/fluke-dmm/fluke.lo: hardware/fluke-dmm/$(am__dirstamp) \
-	hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/fluke-dmm/api.lo: hardware/fluke-dmm/$(am__dirstamp) \
-	hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/fx2lafw/$(am__dirstamp):
-	@$(MKDIR_P) hardware/fx2lafw
-	@: > hardware/fx2lafw/$(am__dirstamp)
-hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/fx2lafw/$(DEPDIR)
-	@: > hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
-hardware/fx2lafw/protocol.lo: hardware/fx2lafw/$(am__dirstamp) \
-	hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
-hardware/fx2lafw/api.lo: hardware/fx2lafw/$(am__dirstamp) \
-	hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
-hardware/gmc-mh-1x-2x/$(am__dirstamp):
-	@$(MKDIR_P) hardware/gmc-mh-1x-2x
-	@: > hardware/gmc-mh-1x-2x/$(am__dirstamp)
-hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/gmc-mh-1x-2x/$(DEPDIR)
-	@: > hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp)
-hardware/gmc-mh-1x-2x/protocol.lo:  \
-	hardware/gmc-mh-1x-2x/$(am__dirstamp) \
-	hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp)
-hardware/gmc-mh-1x-2x/api.lo: hardware/gmc-mh-1x-2x/$(am__dirstamp) \
-	hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp)
-hardware/hameg-hmo/$(am__dirstamp):
-	@$(MKDIR_P) hardware/hameg-hmo
-	@: > hardware/hameg-hmo/$(am__dirstamp)
-hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/hameg-hmo/$(DEPDIR)
-	@: > hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp)
-hardware/hameg-hmo/protocol.lo: hardware/hameg-hmo/$(am__dirstamp) \
-	hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp)
-hardware/hameg-hmo/api.lo: hardware/hameg-hmo/$(am__dirstamp) \
-	hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp)
-hardware/hantek-dso/$(am__dirstamp):
-	@$(MKDIR_P) hardware/hantek-dso
-	@: > hardware/hantek-dso/$(am__dirstamp)
-hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/hantek-dso/$(DEPDIR)
-	@: > hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp)
-hardware/hantek-dso/dso.lo: hardware/hantek-dso/$(am__dirstamp) \
-	hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp)
-hardware/hantek-dso/api.lo: hardware/hantek-dso/$(am__dirstamp) \
-	hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp)
-hardware/ikalogic-scanalogic2/$(am__dirstamp):
-	@$(MKDIR_P) hardware/ikalogic-scanalogic2
-	@: > hardware/ikalogic-scanalogic2/$(am__dirstamp)
-hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/ikalogic-scanalogic2/$(DEPDIR)
-	@: > hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp)
-hardware/ikalogic-scanalogic2/protocol.lo:  \
-	hardware/ikalogic-scanalogic2/$(am__dirstamp) \
-	hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp)
-hardware/ikalogic-scanalogic2/api.lo:  \
-	hardware/ikalogic-scanalogic2/$(am__dirstamp) \
-	hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp)
-hardware/ikalogic-scanaplus/$(am__dirstamp):
-	@$(MKDIR_P) hardware/ikalogic-scanaplus
-	@: > hardware/ikalogic-scanaplus/$(am__dirstamp)
-hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/ikalogic-scanaplus/$(DEPDIR)
-	@: > hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp)
-hardware/ikalogic-scanaplus/protocol.lo:  \
-	hardware/ikalogic-scanaplus/$(am__dirstamp) \
-	hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp)
-hardware/ikalogic-scanaplus/api.lo:  \
-	hardware/ikalogic-scanaplus/$(am__dirstamp) \
-	hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp)
-hardware/kecheng-kc-330b/$(am__dirstamp):
-	@$(MKDIR_P) hardware/kecheng-kc-330b
-	@: > hardware/kecheng-kc-330b/$(am__dirstamp)
-hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/kecheng-kc-330b/$(DEPDIR)
-	@: > hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp)
-hardware/kecheng-kc-330b/protocol.lo:  \
-	hardware/kecheng-kc-330b/$(am__dirstamp) \
-	hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp)
-hardware/kecheng-kc-330b/api.lo:  \
-	hardware/kecheng-kc-330b/$(am__dirstamp) \
-	hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp)
-hardware/lascar-el-usb/$(am__dirstamp):
-	@$(MKDIR_P) hardware/lascar-el-usb
-	@: > hardware/lascar-el-usb/$(am__dirstamp)
-hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/lascar-el-usb/$(DEPDIR)
-	@: > hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp)
-hardware/lascar-el-usb/protocol.lo:  \
-	hardware/lascar-el-usb/$(am__dirstamp) \
-	hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp)
-hardware/lascar-el-usb/api.lo: hardware/lascar-el-usb/$(am__dirstamp) \
-	hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp)
-hardware/mic-985xx/$(am__dirstamp):
-	@$(MKDIR_P) hardware/mic-985xx
-	@: > hardware/mic-985xx/$(am__dirstamp)
-hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/mic-985xx/$(DEPDIR)
-	@: > hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp)
-hardware/mic-985xx/protocol.lo: hardware/mic-985xx/$(am__dirstamp) \
-	hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp)
-hardware/mic-985xx/api.lo: hardware/mic-985xx/$(am__dirstamp) \
-	hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp)
-hardware/norma-dmm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/norma-dmm
-	@: > hardware/norma-dmm/$(am__dirstamp)
-hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/norma-dmm/$(DEPDIR)
-	@: > hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/norma-dmm/protocol.lo: hardware/norma-dmm/$(am__dirstamp) \
-	hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/norma-dmm/api.lo: hardware/norma-dmm/$(am__dirstamp) \
-	hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/openbench-logic-sniffer/$(am__dirstamp):
-	@$(MKDIR_P) hardware/openbench-logic-sniffer
-	@: > hardware/openbench-logic-sniffer/$(am__dirstamp)
-hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/openbench-logic-sniffer/$(DEPDIR)
-	@: > hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp)
-hardware/openbench-logic-sniffer/protocol.lo:  \
-	hardware/openbench-logic-sniffer/$(am__dirstamp) \
-	hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp)
-hardware/openbench-logic-sniffer/api.lo:  \
-	hardware/openbench-logic-sniffer/$(am__dirstamp) \
-	hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp)
-hardware/rigol-ds/$(am__dirstamp):
-	@$(MKDIR_P) hardware/rigol-ds
-	@: > hardware/rigol-ds/$(am__dirstamp)
-hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/rigol-ds/$(DEPDIR)
-	@: > hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp)
-hardware/rigol-ds/protocol.lo: hardware/rigol-ds/$(am__dirstamp) \
-	hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp)
-hardware/rigol-ds/api.lo: hardware/rigol-ds/$(am__dirstamp) \
-	hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp)
-hardware/saleae-logic16/$(am__dirstamp):
-	@$(MKDIR_P) hardware/saleae-logic16
-	@: > hardware/saleae-logic16/$(am__dirstamp)
-hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/saleae-logic16/$(DEPDIR)
-	@: > hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp)
-hardware/saleae-logic16/protocol.lo:  \
-	hardware/saleae-logic16/$(am__dirstamp) \
-	hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp)
-hardware/saleae-logic16/api.lo:  \
-	hardware/saleae-logic16/$(am__dirstamp) \
-	hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp)
-hardware/serial-dmm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/serial-dmm
-	@: > hardware/serial-dmm/$(am__dirstamp)
-hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/serial-dmm/$(DEPDIR)
-	@: > hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/serial-dmm/protocol.lo: hardware/serial-dmm/$(am__dirstamp) \
-	hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/serial-dmm/api.lo: hardware/serial-dmm/$(am__dirstamp) \
-	hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/sysclk-lwla/$(am__dirstamp):
-	@$(MKDIR_P) hardware/sysclk-lwla
-	@: > hardware/sysclk-lwla/$(am__dirstamp)
-hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/sysclk-lwla/$(DEPDIR)
-	@: > hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
-hardware/sysclk-lwla/lwla.lo: hardware/sysclk-lwla/$(am__dirstamp) \
-	hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
-hardware/sysclk-lwla/protocol.lo:  \
-	hardware/sysclk-lwla/$(am__dirstamp) \
-	hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
-hardware/sysclk-lwla/api.lo: hardware/sysclk-lwla/$(am__dirstamp) \
-	hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
-hardware/teleinfo/$(am__dirstamp):
-	@$(MKDIR_P) hardware/teleinfo
-	@: > hardware/teleinfo/$(am__dirstamp)
-hardware/teleinfo/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/teleinfo/$(DEPDIR)
-	@: > hardware/teleinfo/$(DEPDIR)/$(am__dirstamp)
-hardware/teleinfo/protocol.lo: hardware/teleinfo/$(am__dirstamp) \
-	hardware/teleinfo/$(DEPDIR)/$(am__dirstamp)
-hardware/teleinfo/api.lo: hardware/teleinfo/$(am__dirstamp) \
-	hardware/teleinfo/$(DEPDIR)/$(am__dirstamp)
-hardware/tondaj-sl-814/$(am__dirstamp):
-	@$(MKDIR_P) hardware/tondaj-sl-814
-	@: > hardware/tondaj-sl-814/$(am__dirstamp)
-hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/tondaj-sl-814/$(DEPDIR)
-	@: > hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp)
-hardware/tondaj-sl-814/protocol.lo:  \
-	hardware/tondaj-sl-814/$(am__dirstamp) \
-	hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp)
-hardware/tondaj-sl-814/api.lo: hardware/tondaj-sl-814/$(am__dirstamp) \
-	hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp)
-hardware/uni-t-dmm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/uni-t-dmm
-	@: > hardware/uni-t-dmm/$(am__dirstamp)
-hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/uni-t-dmm/$(DEPDIR)
-	@: > hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/uni-t-dmm/protocol.lo: hardware/uni-t-dmm/$(am__dirstamp) \
-	hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/uni-t-dmm/api.lo: hardware/uni-t-dmm/$(am__dirstamp) \
-	hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/uni-t-ut32x/$(am__dirstamp):
-	@$(MKDIR_P) hardware/uni-t-ut32x
-	@: > hardware/uni-t-ut32x/$(am__dirstamp)
-hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/uni-t-ut32x/$(DEPDIR)
-	@: > hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp)
-hardware/uni-t-ut32x/protocol.lo:  \
-	hardware/uni-t-ut32x/$(am__dirstamp) \
-	hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp)
-hardware/uni-t-ut32x/api.lo: hardware/uni-t-ut32x/$(am__dirstamp) \
-	hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp)
-hardware/victor-dmm/$(am__dirstamp):
-	@$(MKDIR_P) hardware/victor-dmm
-	@: > hardware/victor-dmm/$(am__dirstamp)
-hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/victor-dmm/$(DEPDIR)
-	@: > hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/victor-dmm/protocol.lo: hardware/victor-dmm/$(am__dirstamp) \
-	hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/victor-dmm/api.lo: hardware/victor-dmm/$(am__dirstamp) \
-	hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp)
-hardware/zeroplus-logic-cube/$(am__dirstamp):
-	@$(MKDIR_P) hardware/zeroplus-logic-cube
-	@: > hardware/zeroplus-logic-cube/$(am__dirstamp)
-hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) hardware/zeroplus-logic-cube/$(DEPDIR)
-	@: > hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
-hardware/zeroplus-logic-cube/analyzer.lo:  \
-	hardware/zeroplus-logic-cube/$(am__dirstamp) \
-	hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
-hardware/zeroplus-logic-cube/gl_usb.lo:  \
-	hardware/zeroplus-logic-cube/$(am__dirstamp) \
-	hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
-hardware/zeroplus-logic-cube/protocol.lo:  \
-	hardware/zeroplus-logic-cube/$(am__dirstamp) \
-	hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
-hardware/zeroplus-logic-cube/api.lo:  \
-	hardware/zeroplus-logic-cube/$(am__dirstamp) \
-	hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
+bindings/cxx/$(am__dirstamp):
+	@$(MKDIR_P) bindings/cxx
+	@: > bindings/cxx/$(am__dirstamp)
+bindings/cxx/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) bindings/cxx/$(DEPDIR)
+	@: > bindings/cxx/$(DEPDIR)/$(am__dirstamp)
+bindings/cxx/classes.lo: bindings/cxx/$(am__dirstamp) \
+	bindings/cxx/$(DEPDIR)/$(am__dirstamp)
+
+bindings/cxx/libsigrokcxx.la: $(bindings_cxx_libsigrokcxx_la_OBJECTS) $(bindings_cxx_libsigrokcxx_la_DEPENDENCIES) $(EXTRA_bindings_cxx_libsigrokcxx_la_DEPENDENCIES) bindings/cxx/$(am__dirstamp)
+	$(AM_V_CXXLD)$(bindings_cxx_libsigrokcxx_la_LINK) $(am_bindings_cxx_libsigrokcxx_la_rpath) $(bindings_cxx_libsigrokcxx_la_OBJECTS) $(bindings_cxx_libsigrokcxx_la_LIBADD) $(LIBS)
+src/$(am__dirstamp):
+	@$(MKDIR_P) src
+	@: > src/$(am__dirstamp)
+src/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/$(DEPDIR)
+	@: > src/$(DEPDIR)/$(am__dirstamp)
+src/backend.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/device.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/session.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/session_file.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/session_driver.lo: src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
+src/drivers.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/hwdriver.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/trigger.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/soft-trigger.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/analog.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/fallback.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/resource.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/strutil.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/log.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/version.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/error.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/std.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/input/$(am__dirstamp):
+	@$(MKDIR_P) src/input
+	@: > src/input/$(am__dirstamp)
+src/input/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/input/$(DEPDIR)
+	@: > src/input/$(DEPDIR)/$(am__dirstamp)
+src/input/input.lo: src/input/$(am__dirstamp) \
+	src/input/$(DEPDIR)/$(am__dirstamp)
+src/input/binary.lo: src/input/$(am__dirstamp) \
+	src/input/$(DEPDIR)/$(am__dirstamp)
+src/input/chronovu_la8.lo: src/input/$(am__dirstamp) \
+	src/input/$(DEPDIR)/$(am__dirstamp)
+src/input/csv.lo: src/input/$(am__dirstamp) \
+	src/input/$(DEPDIR)/$(am__dirstamp)
+src/input/raw_analog.lo: src/input/$(am__dirstamp) \
+	src/input/$(DEPDIR)/$(am__dirstamp)
+src/input/trace32_ad.lo: src/input/$(am__dirstamp) \
+	src/input/$(DEPDIR)/$(am__dirstamp)
+src/input/vcd.lo: src/input/$(am__dirstamp) \
+	src/input/$(DEPDIR)/$(am__dirstamp)
+src/input/wav.lo: src/input/$(am__dirstamp) \
+	src/input/$(DEPDIR)/$(am__dirstamp)
+src/output/$(am__dirstamp):
+	@$(MKDIR_P) src/output
+	@: > src/output/$(am__dirstamp)
+src/output/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/output/$(DEPDIR)
+	@: > src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/output.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/analog.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/ascii.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/bits.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/binary.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/csv.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/chronovu_la8.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/wav.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/gnuplot.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/hex.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/ols.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/srzip.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/output/vcd.lo: src/output/$(am__dirstamp) \
+	src/output/$(DEPDIR)/$(am__dirstamp)
+src/transform/$(am__dirstamp):
+	@$(MKDIR_P) src/transform
+	@: > src/transform/$(am__dirstamp)
+src/transform/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/transform/$(DEPDIR)
+	@: > src/transform/$(DEPDIR)/$(am__dirstamp)
+src/transform/transform.lo: src/transform/$(am__dirstamp) \
+	src/transform/$(DEPDIR)/$(am__dirstamp)
+src/transform/nop.lo: src/transform/$(am__dirstamp) \
+	src/transform/$(DEPDIR)/$(am__dirstamp)
+src/transform/scale.lo: src/transform/$(am__dirstamp) \
+	src/transform/$(DEPDIR)/$(am__dirstamp)
+src/transform/invert.lo: src/transform/$(am__dirstamp) \
+	src/transform/$(DEPDIR)/$(am__dirstamp)
+src/scpi/$(am__dirstamp):
+	@$(MKDIR_P) src/scpi
+	@: > src/scpi/$(am__dirstamp)
+src/scpi/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/scpi/$(DEPDIR)
+	@: > src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/scpi/scpi.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/scpi/helpers.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/scpi/scpi_tcp.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/scpi/scpi_vxi.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/scpi/vxi_clnt.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/scpi/vxi_xdr.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/serial.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/scpi/scpi_serial.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/ezusb.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/usb.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/scpi/scpi_usbtmc_libusb.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/scpi/scpi_visa.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/scpi/scpi_libgpib.lo: src/scpi/$(am__dirstamp) \
+	src/scpi/$(DEPDIR)/$(am__dirstamp)
+src/modbus/$(am__dirstamp):
+	@$(MKDIR_P) src/modbus
+	@: > src/modbus/$(am__dirstamp)
+src/modbus/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/modbus/$(DEPDIR)
+	@: > src/modbus/$(DEPDIR)/$(am__dirstamp)
+src/modbus/modbus.lo: src/modbus/$(am__dirstamp) \
+	src/modbus/$(DEPDIR)/$(am__dirstamp)
+src/modbus/modbus_serial_rtu.lo: src/modbus/$(am__dirstamp) \
+	src/modbus/$(DEPDIR)/$(am__dirstamp)
+src/dmm/$(am__dirstamp):
+	@$(MKDIR_P) src/dmm
+	@: > src/dmm/$(am__dirstamp)
+src/dmm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/dmm/$(DEPDIR)
+	@: > src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/es519xx.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/fs9721.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/fs9922.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/m2110.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/metex14.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/rs9lcd.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/bm25x.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/ut71x.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/ut372.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/vc870.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/dmm/dtm0660.lo: src/dmm/$(am__dirstamp) \
+	src/dmm/$(DEPDIR)/$(am__dirstamp)
+src/lcr/$(am__dirstamp):
+	@$(MKDIR_P) src/lcr
+	@: > src/lcr/$(am__dirstamp)
+src/lcr/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/lcr/$(DEPDIR)
+	@: > src/lcr/$(DEPDIR)/$(am__dirstamp)
+src/lcr/es51919.lo: src/lcr/$(am__dirstamp) \
+	src/lcr/$(DEPDIR)/$(am__dirstamp)
+src/scale/$(am__dirstamp):
+	@$(MKDIR_P) src/scale
+	@: > src/scale/$(am__dirstamp)
+src/scale/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/scale/$(DEPDIR)
+	@: > src/scale/$(DEPDIR)/$(am__dirstamp)
+src/scale/kern.lo: src/scale/$(am__dirstamp) \
+	src/scale/$(DEPDIR)/$(am__dirstamp)
+src/hardware/agilent-dmm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/agilent-dmm
+	@: > src/hardware/agilent-dmm/$(am__dirstamp)
+src/hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/agilent-dmm/$(DEPDIR)
+	@: > src/hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/agilent-dmm/api.lo:  \
+	src/hardware/agilent-dmm/$(am__dirstamp) \
+	src/hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/agilent-dmm/sched.lo:  \
+	src/hardware/agilent-dmm/$(am__dirstamp) \
+	src/hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/appa-55ii/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/appa-55ii
+	@: > src/hardware/appa-55ii/$(am__dirstamp)
+src/hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/appa-55ii/$(DEPDIR)
+	@: > src/hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp)
+src/hardware/appa-55ii/protocol.lo:  \
+	src/hardware/appa-55ii/$(am__dirstamp) \
+	src/hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp)
+src/hardware/appa-55ii/api.lo: src/hardware/appa-55ii/$(am__dirstamp) \
+	src/hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp)
+src/hardware/asix-sigma/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/asix-sigma
+	@: > src/hardware/asix-sigma/$(am__dirstamp)
+src/hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/asix-sigma/$(DEPDIR)
+	@: > src/hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp)
+src/hardware/asix-sigma/protocol.lo:  \
+	src/hardware/asix-sigma/$(am__dirstamp) \
+	src/hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp)
+src/hardware/asix-sigma/api.lo:  \
+	src/hardware/asix-sigma/$(am__dirstamp) \
+	src/hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp)
+src/hardware/atten-pps3xxx/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/atten-pps3xxx
+	@: > src/hardware/atten-pps3xxx/$(am__dirstamp)
+src/hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/atten-pps3xxx/$(DEPDIR)
+	@: > src/hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/atten-pps3xxx/protocol.lo:  \
+	src/hardware/atten-pps3xxx/$(am__dirstamp) \
+	src/hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/atten-pps3xxx/api.lo:  \
+	src/hardware/atten-pps3xxx/$(am__dirstamp) \
+	src/hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/baylibre-acme/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/baylibre-acme
+	@: > src/hardware/baylibre-acme/$(am__dirstamp)
+src/hardware/baylibre-acme/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/baylibre-acme/$(DEPDIR)
+	@: > src/hardware/baylibre-acme/$(DEPDIR)/$(am__dirstamp)
+src/hardware/baylibre-acme/protocol.lo:  \
+	src/hardware/baylibre-acme/$(am__dirstamp) \
+	src/hardware/baylibre-acme/$(DEPDIR)/$(am__dirstamp)
+src/hardware/baylibre-acme/api.lo:  \
+	src/hardware/baylibre-acme/$(am__dirstamp) \
+	src/hardware/baylibre-acme/$(DEPDIR)/$(am__dirstamp)
+src/hardware/baylibre-acme/gpio.lo:  \
+	src/hardware/baylibre-acme/$(am__dirstamp) \
+	src/hardware/baylibre-acme/$(DEPDIR)/$(am__dirstamp)
+src/hardware/beaglelogic/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/beaglelogic
+	@: > src/hardware/beaglelogic/$(am__dirstamp)
+src/hardware/beaglelogic/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/beaglelogic/$(DEPDIR)
+	@: > src/hardware/beaglelogic/$(DEPDIR)/$(am__dirstamp)
+src/hardware/beaglelogic/protocol.lo:  \
+	src/hardware/beaglelogic/$(am__dirstamp) \
+	src/hardware/beaglelogic/$(DEPDIR)/$(am__dirstamp)
+src/hardware/beaglelogic/api.lo:  \
+	src/hardware/beaglelogic/$(am__dirstamp) \
+	src/hardware/beaglelogic/$(DEPDIR)/$(am__dirstamp)
+src/hardware/brymen-bm86x/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/brymen-bm86x
+	@: > src/hardware/brymen-bm86x/$(am__dirstamp)
+src/hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/brymen-bm86x/$(DEPDIR)
+	@: > src/hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/brymen-bm86x/protocol.lo:  \
+	src/hardware/brymen-bm86x/$(am__dirstamp) \
+	src/hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/brymen-bm86x/api.lo:  \
+	src/hardware/brymen-bm86x/$(am__dirstamp) \
+	src/hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/brymen-dmm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/brymen-dmm
+	@: > src/hardware/brymen-dmm/$(am__dirstamp)
+src/hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/brymen-dmm/$(DEPDIR)
+	@: > src/hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/brymen-dmm/parser.lo:  \
+	src/hardware/brymen-dmm/$(am__dirstamp) \
+	src/hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/brymen-dmm/protocol.lo:  \
+	src/hardware/brymen-dmm/$(am__dirstamp) \
+	src/hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/brymen-dmm/api.lo:  \
+	src/hardware/brymen-dmm/$(am__dirstamp) \
+	src/hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/cem-dt-885x/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/cem-dt-885x
+	@: > src/hardware/cem-dt-885x/$(am__dirstamp)
+src/hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/cem-dt-885x/$(DEPDIR)
+	@: > src/hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/cem-dt-885x/protocol.lo:  \
+	src/hardware/cem-dt-885x/$(am__dirstamp) \
+	src/hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/cem-dt-885x/api.lo:  \
+	src/hardware/cem-dt-885x/$(am__dirstamp) \
+	src/hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/center-3xx/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/center-3xx
+	@: > src/hardware/center-3xx/$(am__dirstamp)
+src/hardware/center-3xx/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/center-3xx/$(DEPDIR)
+	@: > src/hardware/center-3xx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/center-3xx/protocol.lo:  \
+	src/hardware/center-3xx/$(am__dirstamp) \
+	src/hardware/center-3xx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/center-3xx/api.lo:  \
+	src/hardware/center-3xx/$(am__dirstamp) \
+	src/hardware/center-3xx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/chronovu-la/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/chronovu-la
+	@: > src/hardware/chronovu-la/$(am__dirstamp)
+src/hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/chronovu-la/$(DEPDIR)
+	@: > src/hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp)
+src/hardware/chronovu-la/protocol.lo:  \
+	src/hardware/chronovu-la/$(am__dirstamp) \
+	src/hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp)
+src/hardware/chronovu-la/api.lo:  \
+	src/hardware/chronovu-la/$(am__dirstamp) \
+	src/hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp)
+src/hardware/colead-slm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/colead-slm
+	@: > src/hardware/colead-slm/$(am__dirstamp)
+src/hardware/colead-slm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/colead-slm/$(DEPDIR)
+	@: > src/hardware/colead-slm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/colead-slm/protocol.lo:  \
+	src/hardware/colead-slm/$(am__dirstamp) \
+	src/hardware/colead-slm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/colead-slm/api.lo:  \
+	src/hardware/colead-slm/$(am__dirstamp) \
+	src/hardware/colead-slm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/conrad-digi-35-cpu/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/conrad-digi-35-cpu
+	@: > src/hardware/conrad-digi-35-cpu/$(am__dirstamp)
+src/hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/conrad-digi-35-cpu/$(DEPDIR)
+	@: > src/hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp)
+src/hardware/conrad-digi-35-cpu/protocol.lo:  \
+	src/hardware/conrad-digi-35-cpu/$(am__dirstamp) \
+	src/hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp)
+src/hardware/conrad-digi-35-cpu/api.lo:  \
+	src/hardware/conrad-digi-35-cpu/$(am__dirstamp) \
+	src/hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp)
+src/hardware/demo/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/demo
+	@: > src/hardware/demo/$(am__dirstamp)
+src/hardware/demo/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/demo/$(DEPDIR)
+	@: > src/hardware/demo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/demo/demo.lo: src/hardware/demo/$(am__dirstamp) \
+	src/hardware/demo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/deree-de5000/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/deree-de5000
+	@: > src/hardware/deree-de5000/$(am__dirstamp)
+src/hardware/deree-de5000/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/deree-de5000/$(DEPDIR)
+	@: > src/hardware/deree-de5000/$(DEPDIR)/$(am__dirstamp)
+src/hardware/deree-de5000/api.lo:  \
+	src/hardware/deree-de5000/$(am__dirstamp) \
+	src/hardware/deree-de5000/$(DEPDIR)/$(am__dirstamp)
+src/hardware/fluke-dmm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/fluke-dmm
+	@: > src/hardware/fluke-dmm/$(am__dirstamp)
+src/hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/fluke-dmm/$(DEPDIR)
+	@: > src/hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/fluke-dmm/fluke.lo:  \
+	src/hardware/fluke-dmm/$(am__dirstamp) \
+	src/hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/fluke-dmm/api.lo: src/hardware/fluke-dmm/$(am__dirstamp) \
+	src/hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/fx2lafw/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/fx2lafw
+	@: > src/hardware/fx2lafw/$(am__dirstamp)
+src/hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/fx2lafw/$(DEPDIR)
+	@: > src/hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
+src/hardware/fx2lafw/protocol.lo:  \
+	src/hardware/fx2lafw/$(am__dirstamp) \
+	src/hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
+src/hardware/fx2lafw/api.lo: src/hardware/fx2lafw/$(am__dirstamp) \
+	src/hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
+src/hardware/fx2lafw/dslogic.lo: src/hardware/fx2lafw/$(am__dirstamp) \
+	src/hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
+src/hardware/gmc-mh-1x-2x/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/gmc-mh-1x-2x
+	@: > src/hardware/gmc-mh-1x-2x/$(am__dirstamp)
+src/hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/gmc-mh-1x-2x/$(DEPDIR)
+	@: > src/hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/gmc-mh-1x-2x/protocol.lo:  \
+	src/hardware/gmc-mh-1x-2x/$(am__dirstamp) \
+	src/hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/gmc-mh-1x-2x/api.lo:  \
+	src/hardware/gmc-mh-1x-2x/$(am__dirstamp) \
+	src/hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/gwinstek-gds-800/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/gwinstek-gds-800
+	@: > src/hardware/gwinstek-gds-800/$(am__dirstamp)
+src/hardware/gwinstek-gds-800/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/gwinstek-gds-800/$(DEPDIR)
+	@: > src/hardware/gwinstek-gds-800/$(DEPDIR)/$(am__dirstamp)
+src/hardware/gwinstek-gds-800/protocol.lo:  \
+	src/hardware/gwinstek-gds-800/$(am__dirstamp) \
+	src/hardware/gwinstek-gds-800/$(DEPDIR)/$(am__dirstamp)
+src/hardware/gwinstek-gds-800/api.lo:  \
+	src/hardware/gwinstek-gds-800/$(am__dirstamp) \
+	src/hardware/gwinstek-gds-800/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hameg-hmo/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/hameg-hmo
+	@: > src/hardware/hameg-hmo/$(am__dirstamp)
+src/hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/hameg-hmo/$(DEPDIR)
+	@: > src/hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hameg-hmo/protocol.lo:  \
+	src/hardware/hameg-hmo/$(am__dirstamp) \
+	src/hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hameg-hmo/api.lo: src/hardware/hameg-hmo/$(am__dirstamp) \
+	src/hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hantek-dso/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/hantek-dso
+	@: > src/hardware/hantek-dso/$(am__dirstamp)
+src/hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/hantek-dso/$(DEPDIR)
+	@: > src/hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hantek-dso/dso.lo:  \
+	src/hardware/hantek-dso/$(am__dirstamp) \
+	src/hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hantek-dso/api.lo:  \
+	src/hardware/hantek-dso/$(am__dirstamp) \
+	src/hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hung-chang-dso-2100/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/hung-chang-dso-2100
+	@: > src/hardware/hung-chang-dso-2100/$(am__dirstamp)
+src/hardware/hung-chang-dso-2100/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/hung-chang-dso-2100/$(DEPDIR)
+	@: > src/hardware/hung-chang-dso-2100/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hung-chang-dso-2100/protocol.lo:  \
+	src/hardware/hung-chang-dso-2100/$(am__dirstamp) \
+	src/hardware/hung-chang-dso-2100/$(DEPDIR)/$(am__dirstamp)
+src/hardware/hung-chang-dso-2100/api.lo:  \
+	src/hardware/hung-chang-dso-2100/$(am__dirstamp) \
+	src/hardware/hung-chang-dso-2100/$(DEPDIR)/$(am__dirstamp)
+src/hardware/ikalogic-scanalogic2/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/ikalogic-scanalogic2
+	@: > src/hardware/ikalogic-scanalogic2/$(am__dirstamp)
+src/hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/ikalogic-scanalogic2/$(DEPDIR)
+	@: > src/hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp)
+src/hardware/ikalogic-scanalogic2/protocol.lo:  \
+	src/hardware/ikalogic-scanalogic2/$(am__dirstamp) \
+	src/hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp)
+src/hardware/ikalogic-scanalogic2/api.lo:  \
+	src/hardware/ikalogic-scanalogic2/$(am__dirstamp) \
+	src/hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp)
+src/hardware/ikalogic-scanaplus/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/ikalogic-scanaplus
+	@: > src/hardware/ikalogic-scanaplus/$(am__dirstamp)
+src/hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/ikalogic-scanaplus/$(DEPDIR)
+	@: > src/hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp)
+src/hardware/ikalogic-scanaplus/protocol.lo:  \
+	src/hardware/ikalogic-scanaplus/$(am__dirstamp) \
+	src/hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp)
+src/hardware/ikalogic-scanaplus/api.lo:  \
+	src/hardware/ikalogic-scanaplus/$(am__dirstamp) \
+	src/hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp)
+src/hardware/kecheng-kc-330b/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/kecheng-kc-330b
+	@: > src/hardware/kecheng-kc-330b/$(am__dirstamp)
+src/hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/kecheng-kc-330b/$(DEPDIR)
+	@: > src/hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp)
+src/hardware/kecheng-kc-330b/protocol.lo:  \
+	src/hardware/kecheng-kc-330b/$(am__dirstamp) \
+	src/hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp)
+src/hardware/kecheng-kc-330b/api.lo:  \
+	src/hardware/kecheng-kc-330b/$(am__dirstamp) \
+	src/hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp)
+src/hardware/kern-scale/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/kern-scale
+	@: > src/hardware/kern-scale/$(am__dirstamp)
+src/hardware/kern-scale/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/kern-scale/$(DEPDIR)
+	@: > src/hardware/kern-scale/$(DEPDIR)/$(am__dirstamp)
+src/hardware/kern-scale/protocol.lo:  \
+	src/hardware/kern-scale/$(am__dirstamp) \
+	src/hardware/kern-scale/$(DEPDIR)/$(am__dirstamp)
+src/hardware/kern-scale/api.lo:  \
+	src/hardware/kern-scale/$(am__dirstamp) \
+	src/hardware/kern-scale/$(DEPDIR)/$(am__dirstamp)
+src/hardware/korad-kaxxxxp/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/korad-kaxxxxp
+	@: > src/hardware/korad-kaxxxxp/$(am__dirstamp)
+src/hardware/korad-kaxxxxp/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/korad-kaxxxxp/$(DEPDIR)
+	@: > src/hardware/korad-kaxxxxp/$(DEPDIR)/$(am__dirstamp)
+src/hardware/korad-kaxxxxp/protocol.lo:  \
+	src/hardware/korad-kaxxxxp/$(am__dirstamp) \
+	src/hardware/korad-kaxxxxp/$(DEPDIR)/$(am__dirstamp)
+src/hardware/korad-kaxxxxp/api.lo:  \
+	src/hardware/korad-kaxxxxp/$(am__dirstamp) \
+	src/hardware/korad-kaxxxxp/$(DEPDIR)/$(am__dirstamp)
+src/hardware/lascar-el-usb/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/lascar-el-usb
+	@: > src/hardware/lascar-el-usb/$(am__dirstamp)
+src/hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/lascar-el-usb/$(DEPDIR)
+	@: > src/hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp)
+src/hardware/lascar-el-usb/protocol.lo:  \
+	src/hardware/lascar-el-usb/$(am__dirstamp) \
+	src/hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp)
+src/hardware/lascar-el-usb/api.lo:  \
+	src/hardware/lascar-el-usb/$(am__dirstamp) \
+	src/hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp)
+src/hardware/lecroy-logicstudio/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/lecroy-logicstudio
+	@: > src/hardware/lecroy-logicstudio/$(am__dirstamp)
+src/hardware/lecroy-logicstudio/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/lecroy-logicstudio/$(DEPDIR)
+	@: > src/hardware/lecroy-logicstudio/$(DEPDIR)/$(am__dirstamp)
+src/hardware/lecroy-logicstudio/protocol.lo:  \
+	src/hardware/lecroy-logicstudio/$(am__dirstamp) \
+	src/hardware/lecroy-logicstudio/$(DEPDIR)/$(am__dirstamp)
+src/hardware/lecroy-logicstudio/api.lo:  \
+	src/hardware/lecroy-logicstudio/$(am__dirstamp) \
+	src/hardware/lecroy-logicstudio/$(DEPDIR)/$(am__dirstamp)
+src/hardware/manson-hcs-3xxx/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/manson-hcs-3xxx
+	@: > src/hardware/manson-hcs-3xxx/$(am__dirstamp)
+src/hardware/manson-hcs-3xxx/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/manson-hcs-3xxx/$(DEPDIR)
+	@: > src/hardware/manson-hcs-3xxx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/manson-hcs-3xxx/protocol.lo:  \
+	src/hardware/manson-hcs-3xxx/$(am__dirstamp) \
+	src/hardware/manson-hcs-3xxx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/manson-hcs-3xxx/api.lo:  \
+	src/hardware/manson-hcs-3xxx/$(am__dirstamp) \
+	src/hardware/manson-hcs-3xxx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/maynuo-m97/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/maynuo-m97
+	@: > src/hardware/maynuo-m97/$(am__dirstamp)
+src/hardware/maynuo-m97/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/maynuo-m97/$(DEPDIR)
+	@: > src/hardware/maynuo-m97/$(DEPDIR)/$(am__dirstamp)
+src/hardware/maynuo-m97/protocol.lo:  \
+	src/hardware/maynuo-m97/$(am__dirstamp) \
+	src/hardware/maynuo-m97/$(DEPDIR)/$(am__dirstamp)
+src/hardware/maynuo-m97/api.lo:  \
+	src/hardware/maynuo-m97/$(am__dirstamp) \
+	src/hardware/maynuo-m97/$(DEPDIR)/$(am__dirstamp)
+src/hardware/mic-985xx/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/mic-985xx
+	@: > src/hardware/mic-985xx/$(am__dirstamp)
+src/hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/mic-985xx/$(DEPDIR)
+	@: > src/hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/mic-985xx/protocol.lo:  \
+	src/hardware/mic-985xx/$(am__dirstamp) \
+	src/hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/mic-985xx/api.lo: src/hardware/mic-985xx/$(am__dirstamp) \
+	src/hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp)
+src/hardware/motech-lps-30x/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/motech-lps-30x
+	@: > src/hardware/motech-lps-30x/$(am__dirstamp)
+src/hardware/motech-lps-30x/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/motech-lps-30x/$(DEPDIR)
+	@: > src/hardware/motech-lps-30x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/motech-lps-30x/protocol.lo:  \
+	src/hardware/motech-lps-30x/$(am__dirstamp) \
+	src/hardware/motech-lps-30x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/motech-lps-30x/api.lo:  \
+	src/hardware/motech-lps-30x/$(am__dirstamp) \
+	src/hardware/motech-lps-30x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/norma-dmm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/norma-dmm
+	@: > src/hardware/norma-dmm/$(am__dirstamp)
+src/hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/norma-dmm/$(DEPDIR)
+	@: > src/hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/norma-dmm/protocol.lo:  \
+	src/hardware/norma-dmm/$(am__dirstamp) \
+	src/hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/norma-dmm/api.lo: src/hardware/norma-dmm/$(am__dirstamp) \
+	src/hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/openbench-logic-sniffer/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/openbench-logic-sniffer
+	@: > src/hardware/openbench-logic-sniffer/$(am__dirstamp)
+src/hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/openbench-logic-sniffer/$(DEPDIR)
+	@: > src/hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp)
+src/hardware/openbench-logic-sniffer/protocol.lo:  \
+	src/hardware/openbench-logic-sniffer/$(am__dirstamp) \
+	src/hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp)
+src/hardware/openbench-logic-sniffer/api.lo:  \
+	src/hardware/openbench-logic-sniffer/$(am__dirstamp) \
+	src/hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp)
+src/hardware/pipistrello-ols/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/pipistrello-ols
+	@: > src/hardware/pipistrello-ols/$(am__dirstamp)
+src/hardware/pipistrello-ols/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/pipistrello-ols/$(DEPDIR)
+	@: > src/hardware/pipistrello-ols/$(DEPDIR)/$(am__dirstamp)
+src/hardware/pipistrello-ols/protocol.lo:  \
+	src/hardware/pipistrello-ols/$(am__dirstamp) \
+	src/hardware/pipistrello-ols/$(DEPDIR)/$(am__dirstamp)
+src/hardware/pipistrello-ols/api.lo:  \
+	src/hardware/pipistrello-ols/$(am__dirstamp) \
+	src/hardware/pipistrello-ols/$(DEPDIR)/$(am__dirstamp)
+src/hardware/rigol-ds/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/rigol-ds
+	@: > src/hardware/rigol-ds/$(am__dirstamp)
+src/hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/rigol-ds/$(DEPDIR)
+	@: > src/hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp)
+src/hardware/rigol-ds/protocol.lo:  \
+	src/hardware/rigol-ds/$(am__dirstamp) \
+	src/hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp)
+src/hardware/rigol-ds/api.lo: src/hardware/rigol-ds/$(am__dirstamp) \
+	src/hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp)
+src/hardware/saleae-logic16/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/saleae-logic16
+	@: > src/hardware/saleae-logic16/$(am__dirstamp)
+src/hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/saleae-logic16/$(DEPDIR)
+	@: > src/hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp)
+src/hardware/saleae-logic16/protocol.lo:  \
+	src/hardware/saleae-logic16/$(am__dirstamp) \
+	src/hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp)
+src/hardware/saleae-logic16/api.lo:  \
+	src/hardware/saleae-logic16/$(am__dirstamp) \
+	src/hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp)
+src/hardware/scpi-pps/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/scpi-pps
+	@: > src/hardware/scpi-pps/$(am__dirstamp)
+src/hardware/scpi-pps/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/scpi-pps/$(DEPDIR)
+	@: > src/hardware/scpi-pps/$(DEPDIR)/$(am__dirstamp)
+src/hardware/scpi-pps/protocol.lo:  \
+	src/hardware/scpi-pps/$(am__dirstamp) \
+	src/hardware/scpi-pps/$(DEPDIR)/$(am__dirstamp)
+src/hardware/scpi-pps/profiles.lo:  \
+	src/hardware/scpi-pps/$(am__dirstamp) \
+	src/hardware/scpi-pps/$(DEPDIR)/$(am__dirstamp)
+src/hardware/scpi-pps/api.lo: src/hardware/scpi-pps/$(am__dirstamp) \
+	src/hardware/scpi-pps/$(DEPDIR)/$(am__dirstamp)
+src/hardware/serial-dmm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/serial-dmm
+	@: > src/hardware/serial-dmm/$(am__dirstamp)
+src/hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/serial-dmm/$(DEPDIR)
+	@: > src/hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/serial-dmm/protocol.lo:  \
+	src/hardware/serial-dmm/$(am__dirstamp) \
+	src/hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/serial-dmm/api.lo:  \
+	src/hardware/serial-dmm/$(am__dirstamp) \
+	src/hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/sysclk-lwla/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/sysclk-lwla
+	@: > src/hardware/sysclk-lwla/$(am__dirstamp)
+src/hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/sysclk-lwla/$(DEPDIR)
+	@: > src/hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
+src/hardware/sysclk-lwla/lwla.lo:  \
+	src/hardware/sysclk-lwla/$(am__dirstamp) \
+	src/hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
+src/hardware/sysclk-lwla/lwla1016.lo:  \
+	src/hardware/sysclk-lwla/$(am__dirstamp) \
+	src/hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
+src/hardware/sysclk-lwla/lwla1034.lo:  \
+	src/hardware/sysclk-lwla/$(am__dirstamp) \
+	src/hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
+src/hardware/sysclk-lwla/protocol.lo:  \
+	src/hardware/sysclk-lwla/$(am__dirstamp) \
+	src/hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
+src/hardware/sysclk-lwla/api.lo:  \
+	src/hardware/sysclk-lwla/$(am__dirstamp) \
+	src/hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
+src/hardware/teleinfo/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/teleinfo
+	@: > src/hardware/teleinfo/$(am__dirstamp)
+src/hardware/teleinfo/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/teleinfo/$(DEPDIR)
+	@: > src/hardware/teleinfo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/teleinfo/protocol.lo:  \
+	src/hardware/teleinfo/$(am__dirstamp) \
+	src/hardware/teleinfo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/teleinfo/api.lo: src/hardware/teleinfo/$(am__dirstamp) \
+	src/hardware/teleinfo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/testo/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/testo
+	@: > src/hardware/testo/$(am__dirstamp)
+src/hardware/testo/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/testo/$(DEPDIR)
+	@: > src/hardware/testo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/testo/protocol.lo: src/hardware/testo/$(am__dirstamp) \
+	src/hardware/testo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/testo/api.lo: src/hardware/testo/$(am__dirstamp) \
+	src/hardware/testo/$(DEPDIR)/$(am__dirstamp)
+src/hardware/tondaj-sl-814/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/tondaj-sl-814
+	@: > src/hardware/tondaj-sl-814/$(am__dirstamp)
+src/hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/tondaj-sl-814/$(DEPDIR)
+	@: > src/hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp)
+src/hardware/tondaj-sl-814/protocol.lo:  \
+	src/hardware/tondaj-sl-814/$(am__dirstamp) \
+	src/hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp)
+src/hardware/tondaj-sl-814/api.lo:  \
+	src/hardware/tondaj-sl-814/$(am__dirstamp) \
+	src/hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp)
+src/hardware/uni-t-dmm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/uni-t-dmm
+	@: > src/hardware/uni-t-dmm/$(am__dirstamp)
+src/hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/uni-t-dmm/$(DEPDIR)
+	@: > src/hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/uni-t-dmm/protocol.lo:  \
+	src/hardware/uni-t-dmm/$(am__dirstamp) \
+	src/hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/uni-t-dmm/api.lo: src/hardware/uni-t-dmm/$(am__dirstamp) \
+	src/hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/uni-t-ut32x/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/uni-t-ut32x
+	@: > src/hardware/uni-t-ut32x/$(am__dirstamp)
+src/hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/uni-t-ut32x/$(DEPDIR)
+	@: > src/hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/uni-t-ut32x/protocol.lo:  \
+	src/hardware/uni-t-ut32x/$(am__dirstamp) \
+	src/hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/uni-t-ut32x/api.lo:  \
+	src/hardware/uni-t-ut32x/$(am__dirstamp) \
+	src/hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp)
+src/hardware/victor-dmm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/victor-dmm
+	@: > src/hardware/victor-dmm/$(am__dirstamp)
+src/hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/victor-dmm/$(DEPDIR)
+	@: > src/hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/victor-dmm/protocol.lo:  \
+	src/hardware/victor-dmm/$(am__dirstamp) \
+	src/hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/victor-dmm/api.lo:  \
+	src/hardware/victor-dmm/$(am__dirstamp) \
+	src/hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/yokogawa-dlm/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/yokogawa-dlm
+	@: > src/hardware/yokogawa-dlm/$(am__dirstamp)
+src/hardware/yokogawa-dlm/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/yokogawa-dlm/$(DEPDIR)
+	@: > src/hardware/yokogawa-dlm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/yokogawa-dlm/protocol.lo:  \
+	src/hardware/yokogawa-dlm/$(am__dirstamp) \
+	src/hardware/yokogawa-dlm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/yokogawa-dlm/protocol_wrappers.lo:  \
+	src/hardware/yokogawa-dlm/$(am__dirstamp) \
+	src/hardware/yokogawa-dlm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/yokogawa-dlm/api.lo:  \
+	src/hardware/yokogawa-dlm/$(am__dirstamp) \
+	src/hardware/yokogawa-dlm/$(DEPDIR)/$(am__dirstamp)
+src/hardware/zeroplus-logic-cube/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/zeroplus-logic-cube
+	@: > src/hardware/zeroplus-logic-cube/$(am__dirstamp)
+src/hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) src/hardware/zeroplus-logic-cube/$(DEPDIR)
+	@: > src/hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
+src/hardware/zeroplus-logic-cube/analyzer.lo:  \
+	src/hardware/zeroplus-logic-cube/$(am__dirstamp) \
+	src/hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
+src/hardware/zeroplus-logic-cube/gl_usb.lo:  \
+	src/hardware/zeroplus-logic-cube/$(am__dirstamp) \
+	src/hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
+src/hardware/zeroplus-logic-cube/protocol.lo:  \
+	src/hardware/zeroplus-logic-cube/$(am__dirstamp) \
+	src/hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
+src/hardware/zeroplus-logic-cube/api.lo:  \
+	src/hardware/zeroplus-logic-cube/$(am__dirstamp) \
+	src/hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
 
 libsigrok.la: $(libsigrok_la_OBJECTS) $(libsigrok_la_DEPENDENCIES) $(EXTRA_libsigrok_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(libsigrok_la_LINK) -rpath $(libdir) $(libsigrok_la_OBJECTS) $(libsigrok_la_LIBADD) $(LIBS)
@@ -1615,236 +2375,355 @@ tests/$(am__dirstamp):
 tests/$(DEPDIR)/$(am__dirstamp):
 	@$(MKDIR_P) tests/$(DEPDIR)
 	@: > tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-lib.$(OBJEXT): tests/$(am__dirstamp) \
+tests/lib.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/main.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/core.$(OBJEXT): tests/$(am__dirstamp) \
 	tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-check_main.$(OBJEXT): tests/$(am__dirstamp) \
+tests/input_all.$(OBJEXT): tests/$(am__dirstamp) \
 	tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-check_core.$(OBJEXT): tests/$(am__dirstamp) \
+tests/input_binary.$(OBJEXT): tests/$(am__dirstamp) \
 	tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-check_input_all.$(OBJEXT):  \
-	tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-check_input_binary.$(OBJEXT):  \
-	tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-check_output_all.$(OBJEXT):  \
-	tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-check_strutil.$(OBJEXT): tests/$(am__dirstamp) \
+tests/output_all.$(OBJEXT): tests/$(am__dirstamp) \
 	tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-check_version.$(OBJEXT): tests/$(am__dirstamp) \
+tests/transform_all.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/session.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/strutil.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/version.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/driver_all.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/device.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/trigger.$(OBJEXT): tests/$(am__dirstamp) \
+	tests/$(DEPDIR)/$(am__dirstamp)
+tests/analog.$(OBJEXT): tests/$(am__dirstamp) \
 	tests/$(DEPDIR)/$(am__dirstamp)
-tests/tests_check_main-check_driver_all.$(OBJEXT):  \
-	tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp)
 
-tests/check_main$(EXEEXT): $(tests_check_main_OBJECTS) $(tests_check_main_DEPENDENCIES) $(EXTRA_tests_check_main_DEPENDENCIES) tests/$(am__dirstamp)
-	@rm -f tests/check_main$(EXEEXT)
-	$(AM_V_CCLD)$(tests_check_main_LINK) $(tests_check_main_OBJECTS) $(tests_check_main_LDADD) $(LIBS)
+tests/main$(EXEEXT): $(tests_main_OBJECTS) $(tests_main_DEPENDENCIES) $(EXTRA_tests_main_DEPENDENCIES) tests/$(am__dirstamp)
+	@rm -f tests/main$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(tests_main_OBJECTS) $(tests_main_LDADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
-	-rm -f hardware/agilent-dmm/*.$(OBJEXT)
-	-rm -f hardware/agilent-dmm/*.lo
-	-rm -f hardware/appa-55ii/*.$(OBJEXT)
-	-rm -f hardware/appa-55ii/*.lo
-	-rm -f hardware/asix-sigma/*.$(OBJEXT)
-	-rm -f hardware/asix-sigma/*.lo
-	-rm -f hardware/atten-pps3xxx/*.$(OBJEXT)
-	-rm -f hardware/atten-pps3xxx/*.lo
-	-rm -f hardware/brymen-bm86x/*.$(OBJEXT)
-	-rm -f hardware/brymen-bm86x/*.lo
-	-rm -f hardware/brymen-dmm/*.$(OBJEXT)
-	-rm -f hardware/brymen-dmm/*.lo
-	-rm -f hardware/cem-dt-885x/*.$(OBJEXT)
-	-rm -f hardware/cem-dt-885x/*.lo
-	-rm -f hardware/center-3xx/*.$(OBJEXT)
-	-rm -f hardware/center-3xx/*.lo
-	-rm -f hardware/chronovu-la/*.$(OBJEXT)
-	-rm -f hardware/chronovu-la/*.lo
-	-rm -f hardware/colead-slm/*.$(OBJEXT)
-	-rm -f hardware/colead-slm/*.lo
-	-rm -f hardware/common/*.$(OBJEXT)
-	-rm -f hardware/common/*.lo
-	-rm -f hardware/common/dmm/*.$(OBJEXT)
-	-rm -f hardware/common/dmm/*.lo
-	-rm -f hardware/conrad-digi-35-cpu/*.$(OBJEXT)
-	-rm -f hardware/conrad-digi-35-cpu/*.lo
-	-rm -f hardware/demo/*.$(OBJEXT)
-	-rm -f hardware/demo/*.lo
-	-rm -f hardware/fluke-dmm/*.$(OBJEXT)
-	-rm -f hardware/fluke-dmm/*.lo
-	-rm -f hardware/fx2lafw/*.$(OBJEXT)
-	-rm -f hardware/fx2lafw/*.lo
-	-rm -f hardware/gmc-mh-1x-2x/*.$(OBJEXT)
-	-rm -f hardware/gmc-mh-1x-2x/*.lo
-	-rm -f hardware/hameg-hmo/*.$(OBJEXT)
-	-rm -f hardware/hameg-hmo/*.lo
-	-rm -f hardware/hantek-dso/*.$(OBJEXT)
-	-rm -f hardware/hantek-dso/*.lo
-	-rm -f hardware/ikalogic-scanalogic2/*.$(OBJEXT)
-	-rm -f hardware/ikalogic-scanalogic2/*.lo
-	-rm -f hardware/ikalogic-scanaplus/*.$(OBJEXT)
-	-rm -f hardware/ikalogic-scanaplus/*.lo
-	-rm -f hardware/kecheng-kc-330b/*.$(OBJEXT)
-	-rm -f hardware/kecheng-kc-330b/*.lo
-	-rm -f hardware/lascar-el-usb/*.$(OBJEXT)
-	-rm -f hardware/lascar-el-usb/*.lo
-	-rm -f hardware/mic-985xx/*.$(OBJEXT)
-	-rm -f hardware/mic-985xx/*.lo
-	-rm -f hardware/norma-dmm/*.$(OBJEXT)
-	-rm -f hardware/norma-dmm/*.lo
-	-rm -f hardware/openbench-logic-sniffer/*.$(OBJEXT)
-	-rm -f hardware/openbench-logic-sniffer/*.lo
-	-rm -f hardware/rigol-ds/*.$(OBJEXT)
-	-rm -f hardware/rigol-ds/*.lo
-	-rm -f hardware/saleae-logic16/*.$(OBJEXT)
-	-rm -f hardware/saleae-logic16/*.lo
-	-rm -f hardware/serial-dmm/*.$(OBJEXT)
-	-rm -f hardware/serial-dmm/*.lo
-	-rm -f hardware/sysclk-lwla/*.$(OBJEXT)
-	-rm -f hardware/sysclk-lwla/*.lo
-	-rm -f hardware/teleinfo/*.$(OBJEXT)
-	-rm -f hardware/teleinfo/*.lo
-	-rm -f hardware/tondaj-sl-814/*.$(OBJEXT)
-	-rm -f hardware/tondaj-sl-814/*.lo
-	-rm -f hardware/uni-t-dmm/*.$(OBJEXT)
-	-rm -f hardware/uni-t-dmm/*.lo
-	-rm -f hardware/uni-t-ut32x/*.$(OBJEXT)
-	-rm -f hardware/uni-t-ut32x/*.lo
-	-rm -f hardware/victor-dmm/*.$(OBJEXT)
-	-rm -f hardware/victor-dmm/*.lo
-	-rm -f hardware/zeroplus-logic-cube/*.$(OBJEXT)
-	-rm -f hardware/zeroplus-logic-cube/*.lo
-	-rm -f input/*.$(OBJEXT)
-	-rm -f input/*.lo
-	-rm -f output/*.$(OBJEXT)
-	-rm -f output/*.lo
+	-rm -f bindings/cxx/*.$(OBJEXT)
+	-rm -f bindings/cxx/*.lo
+	-rm -f src/*.$(OBJEXT)
+	-rm -f src/*.lo
+	-rm -f src/dmm/*.$(OBJEXT)
+	-rm -f src/dmm/*.lo
+	-rm -f src/hardware/agilent-dmm/*.$(OBJEXT)
+	-rm -f src/hardware/agilent-dmm/*.lo
+	-rm -f src/hardware/appa-55ii/*.$(OBJEXT)
+	-rm -f src/hardware/appa-55ii/*.lo
+	-rm -f src/hardware/asix-sigma/*.$(OBJEXT)
+	-rm -f src/hardware/asix-sigma/*.lo
+	-rm -f src/hardware/atten-pps3xxx/*.$(OBJEXT)
+	-rm -f src/hardware/atten-pps3xxx/*.lo
+	-rm -f src/hardware/baylibre-acme/*.$(OBJEXT)
+	-rm -f src/hardware/baylibre-acme/*.lo
+	-rm -f src/hardware/beaglelogic/*.$(OBJEXT)
+	-rm -f src/hardware/beaglelogic/*.lo
+	-rm -f src/hardware/brymen-bm86x/*.$(OBJEXT)
+	-rm -f src/hardware/brymen-bm86x/*.lo
+	-rm -f src/hardware/brymen-dmm/*.$(OBJEXT)
+	-rm -f src/hardware/brymen-dmm/*.lo
+	-rm -f src/hardware/cem-dt-885x/*.$(OBJEXT)
+	-rm -f src/hardware/cem-dt-885x/*.lo
+	-rm -f src/hardware/center-3xx/*.$(OBJEXT)
+	-rm -f src/hardware/center-3xx/*.lo
+	-rm -f src/hardware/chronovu-la/*.$(OBJEXT)
+	-rm -f src/hardware/chronovu-la/*.lo
+	-rm -f src/hardware/colead-slm/*.$(OBJEXT)
+	-rm -f src/hardware/colead-slm/*.lo
+	-rm -f src/hardware/conrad-digi-35-cpu/*.$(OBJEXT)
+	-rm -f src/hardware/conrad-digi-35-cpu/*.lo
+	-rm -f src/hardware/demo/*.$(OBJEXT)
+	-rm -f src/hardware/demo/*.lo
+	-rm -f src/hardware/deree-de5000/*.$(OBJEXT)
+	-rm -f src/hardware/deree-de5000/*.lo
+	-rm -f src/hardware/fluke-dmm/*.$(OBJEXT)
+	-rm -f src/hardware/fluke-dmm/*.lo
+	-rm -f src/hardware/fx2lafw/*.$(OBJEXT)
+	-rm -f src/hardware/fx2lafw/*.lo
+	-rm -f src/hardware/gmc-mh-1x-2x/*.$(OBJEXT)
+	-rm -f src/hardware/gmc-mh-1x-2x/*.lo
+	-rm -f src/hardware/gwinstek-gds-800/*.$(OBJEXT)
+	-rm -f src/hardware/gwinstek-gds-800/*.lo
+	-rm -f src/hardware/hameg-hmo/*.$(OBJEXT)
+	-rm -f src/hardware/hameg-hmo/*.lo
+	-rm -f src/hardware/hantek-dso/*.$(OBJEXT)
+	-rm -f src/hardware/hantek-dso/*.lo
+	-rm -f src/hardware/hung-chang-dso-2100/*.$(OBJEXT)
+	-rm -f src/hardware/hung-chang-dso-2100/*.lo
+	-rm -f src/hardware/ikalogic-scanalogic2/*.$(OBJEXT)
+	-rm -f src/hardware/ikalogic-scanalogic2/*.lo
+	-rm -f src/hardware/ikalogic-scanaplus/*.$(OBJEXT)
+	-rm -f src/hardware/ikalogic-scanaplus/*.lo
+	-rm -f src/hardware/kecheng-kc-330b/*.$(OBJEXT)
+	-rm -f src/hardware/kecheng-kc-330b/*.lo
+	-rm -f src/hardware/kern-scale/*.$(OBJEXT)
+	-rm -f src/hardware/kern-scale/*.lo
+	-rm -f src/hardware/korad-kaxxxxp/*.$(OBJEXT)
+	-rm -f src/hardware/korad-kaxxxxp/*.lo
+	-rm -f src/hardware/lascar-el-usb/*.$(OBJEXT)
+	-rm -f src/hardware/lascar-el-usb/*.lo
+	-rm -f src/hardware/lecroy-logicstudio/*.$(OBJEXT)
+	-rm -f src/hardware/lecroy-logicstudio/*.lo
+	-rm -f src/hardware/manson-hcs-3xxx/*.$(OBJEXT)
+	-rm -f src/hardware/manson-hcs-3xxx/*.lo
+	-rm -f src/hardware/maynuo-m97/*.$(OBJEXT)
+	-rm -f src/hardware/maynuo-m97/*.lo
+	-rm -f src/hardware/mic-985xx/*.$(OBJEXT)
+	-rm -f src/hardware/mic-985xx/*.lo
+	-rm -f src/hardware/motech-lps-30x/*.$(OBJEXT)
+	-rm -f src/hardware/motech-lps-30x/*.lo
+	-rm -f src/hardware/norma-dmm/*.$(OBJEXT)
+	-rm -f src/hardware/norma-dmm/*.lo
+	-rm -f src/hardware/openbench-logic-sniffer/*.$(OBJEXT)
+	-rm -f src/hardware/openbench-logic-sniffer/*.lo
+	-rm -f src/hardware/pipistrello-ols/*.$(OBJEXT)
+	-rm -f src/hardware/pipistrello-ols/*.lo
+	-rm -f src/hardware/rigol-ds/*.$(OBJEXT)
+	-rm -f src/hardware/rigol-ds/*.lo
+	-rm -f src/hardware/saleae-logic16/*.$(OBJEXT)
+	-rm -f src/hardware/saleae-logic16/*.lo
+	-rm -f src/hardware/scpi-pps/*.$(OBJEXT)
+	-rm -f src/hardware/scpi-pps/*.lo
+	-rm -f src/hardware/serial-dmm/*.$(OBJEXT)
+	-rm -f src/hardware/serial-dmm/*.lo
+	-rm -f src/hardware/sysclk-lwla/*.$(OBJEXT)
+	-rm -f src/hardware/sysclk-lwla/*.lo
+	-rm -f src/hardware/teleinfo/*.$(OBJEXT)
+	-rm -f src/hardware/teleinfo/*.lo
+	-rm -f src/hardware/testo/*.$(OBJEXT)
+	-rm -f src/hardware/testo/*.lo
+	-rm -f src/hardware/tondaj-sl-814/*.$(OBJEXT)
+	-rm -f src/hardware/tondaj-sl-814/*.lo
+	-rm -f src/hardware/uni-t-dmm/*.$(OBJEXT)
+	-rm -f src/hardware/uni-t-dmm/*.lo
+	-rm -f src/hardware/uni-t-ut32x/*.$(OBJEXT)
+	-rm -f src/hardware/uni-t-ut32x/*.lo
+	-rm -f src/hardware/victor-dmm/*.$(OBJEXT)
+	-rm -f src/hardware/victor-dmm/*.lo
+	-rm -f src/hardware/yokogawa-dlm/*.$(OBJEXT)
+	-rm -f src/hardware/yokogawa-dlm/*.lo
+	-rm -f src/hardware/zeroplus-logic-cube/*.$(OBJEXT)
+	-rm -f src/hardware/zeroplus-logic-cube/*.lo
+	-rm -f src/input/*.$(OBJEXT)
+	-rm -f src/input/*.lo
+	-rm -f src/lcr/*.$(OBJEXT)
+	-rm -f src/lcr/*.lo
+	-rm -f src/modbus/*.$(OBJEXT)
+	-rm -f src/modbus/*.lo
+	-rm -f src/output/*.$(OBJEXT)
+	-rm -f src/output/*.lo
+	-rm -f src/scale/*.$(OBJEXT)
+	-rm -f src/scale/*.lo
+	-rm -f src/scpi/*.$(OBJEXT)
+	-rm -f src/scpi/*.lo
+	-rm -f src/transform/*.$(OBJEXT)
+	-rm -f src/transform/*.lo
 	-rm -f tests/*.$(OBJEXT)
 
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/backend.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/device.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/error.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/hwdriver.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/log.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/session.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/session_driver.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/session_file.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/std.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/strutil.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/version.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/agilent-dmm/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/agilent-dmm/$(DEPDIR)/sched.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/appa-55ii/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/appa-55ii/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/asix-sigma/$(DEPDIR)/asix-sigma.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/atten-pps3xxx/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/atten-pps3xxx/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/brymen-bm86x/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/brymen-bm86x/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/brymen-dmm/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/brymen-dmm/$(DEPDIR)/parser.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/brymen-dmm/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/cem-dt-885x/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/cem-dt-885x/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/center-3xx/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/center-3xx/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/chronovu-la/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/chronovu-la/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/colead-slm/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/colead-slm/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/ezusb.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/scpi.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/scpi_serial.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/scpi_tcp.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/scpi_usbtmc_libusb.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/scpi_visa.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/scpi_vxi.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/serial.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/usb.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/vxi_clnt.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/$(DEPDIR)/vxi_xdr.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/dmm/$(DEPDIR)/es519xx.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/dmm/$(DEPDIR)/fs9721.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/dmm/$(DEPDIR)/fs9922.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/dmm/$(DEPDIR)/m2110.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/dmm/$(DEPDIR)/metex14.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/common/dmm/$(DEPDIR)/rs9lcd.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/conrad-digi-35-cpu/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/conrad-digi-35-cpu/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/demo/$(DEPDIR)/demo.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/fluke-dmm/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/fluke-dmm/$(DEPDIR)/fluke.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/fx2lafw/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/fx2lafw/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/gmc-mh-1x-2x/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/gmc-mh-1x-2x/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/hameg-hmo/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/hameg-hmo/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/hantek-dso/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/hantek-dso/$(DEPDIR)/dso.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/ikalogic-scanalogic2/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/ikalogic-scanalogic2/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/ikalogic-scanaplus/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/ikalogic-scanaplus/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/kecheng-kc-330b/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/kecheng-kc-330b/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/lascar-el-usb/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/lascar-el-usb/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/mic-985xx/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/mic-985xx/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/norma-dmm/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/norma-dmm/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/openbench-logic-sniffer/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/openbench-logic-sniffer/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/rigol-ds/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/rigol-ds/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/saleae-logic16/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/saleae-logic16/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/serial-dmm/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/serial-dmm/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/sysclk-lwla/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/sysclk-lwla/$(DEPDIR)/lwla.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/sysclk-lwla/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/teleinfo/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/teleinfo/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/tondaj-sl-814/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/tondaj-sl-814/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/uni-t-dmm/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/uni-t-dmm/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/uni-t-ut32x/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/uni-t-ut32x/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/victor-dmm/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/victor-dmm/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/zeroplus-logic-cube/$(DEPDIR)/analyzer.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/zeroplus-logic-cube/$(DEPDIR)/api.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/zeroplus-logic-cube/$(DEPDIR)/gl_usb.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at hardware/zeroplus-logic-cube/$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at input/$(DEPDIR)/binary.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at input/$(DEPDIR)/chronovu_la8.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at input/$(DEPDIR)/csv.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at input/$(DEPDIR)/input.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at input/$(DEPDIR)/vcd.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at input/$(DEPDIR)/wav.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/analog.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/ascii.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/binary.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/bits.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/chronovu_la8.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/csv.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/gnuplot.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/hex.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/ols.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/output.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at output/$(DEPDIR)/vcd.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-check_core.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-check_driver_all.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-check_input_all.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-check_input_binary.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-check_main.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-check_output_all.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-check_strutil.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-check_version.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/tests_check_main-lib.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at bindings/cxx/$(DEPDIR)/classes.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/analog.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/backend.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/device.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/drivers.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/error.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/ezusb.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/fallback.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/hwdriver.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/log.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/resource.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/serial.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/session.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/session_driver.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/session_file.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/soft-trigger.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/std.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/strutil.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/trigger.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/usb.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/$(DEPDIR)/version.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/bm25x.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/dtm0660.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/es519xx.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/fs9721.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/fs9922.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/m2110.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/metex14.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/rs9lcd.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/ut372.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/ut71x.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/dmm/$(DEPDIR)/vc870.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/agilent-dmm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/agilent-dmm/$(DEPDIR)/sched.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/appa-55ii/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/appa-55ii/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/asix-sigma/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/asix-sigma/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/atten-pps3xxx/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/atten-pps3xxx/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/baylibre-acme/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/baylibre-acme/$(DEPDIR)/gpio.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/baylibre-acme/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/beaglelogic/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/beaglelogic/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/brymen-bm86x/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/brymen-bm86x/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/brymen-dmm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/brymen-dmm/$(DEPDIR)/parser.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/brymen-dmm/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/cem-dt-885x/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/cem-dt-885x/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/center-3xx/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/center-3xx/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/chronovu-la/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/chronovu-la/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/colead-slm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/colead-slm/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/conrad-digi-35-cpu/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/conrad-digi-35-cpu/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/demo/$(DEPDIR)/demo.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/deree-de5000/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/fluke-dmm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/fluke-dmm/$(DEPDIR)/fluke.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/fx2lafw/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/fx2lafw/$(DEPDIR)/dslogic.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/fx2lafw/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/gmc-mh-1x-2x/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/gmc-mh-1x-2x/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/gwinstek-gds-800/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/gwinstek-gds-800/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/hameg-hmo/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/hameg-hmo/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/hantek-dso/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/hantek-dso/$(DEPDIR)/dso.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/hung-chang-dso-2100/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/hung-chang-dso-2100/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/ikalogic-scanalogic2/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/ikalogic-scanalogic2/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/ikalogic-scanaplus/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/ikalogic-scanaplus/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/kecheng-kc-330b/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/kecheng-kc-330b/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/kern-scale/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/kern-scale/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/korad-kaxxxxp/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/korad-kaxxxxp/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/lascar-el-usb/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/lascar-el-usb/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/lecroy-logicstudio/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/lecroy-logicstudio/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/manson-hcs-3xxx/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/manson-hcs-3xxx/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/maynuo-m97/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/maynuo-m97/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/mic-985xx/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/mic-985xx/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/motech-lps-30x/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/motech-lps-30x/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/norma-dmm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/norma-dmm/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/openbench-logic-sniffer/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/openbench-logic-sniffer/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/pipistrello-ols/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/pipistrello-ols/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/rigol-ds/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/rigol-ds/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/saleae-logic16/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/saleae-logic16/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/scpi-pps/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/scpi-pps/$(DEPDIR)/profiles.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/scpi-pps/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/serial-dmm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/serial-dmm/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/sysclk-lwla/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/sysclk-lwla/$(DEPDIR)/lwla.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/sysclk-lwla/$(DEPDIR)/lwla1016.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/sysclk-lwla/$(DEPDIR)/lwla1034.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/sysclk-lwla/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/teleinfo/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/teleinfo/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/testo/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/testo/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/tondaj-sl-814/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/tondaj-sl-814/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/uni-t-dmm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/uni-t-dmm/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/uni-t-ut32x/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/uni-t-ut32x/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/victor-dmm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/victor-dmm/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/yokogawa-dlm/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/yokogawa-dlm/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/yokogawa-dlm/$(DEPDIR)/protocol_wrappers.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/zeroplus-logic-cube/$(DEPDIR)/analyzer.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/zeroplus-logic-cube/$(DEPDIR)/api.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/zeroplus-logic-cube/$(DEPDIR)/gl_usb.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/hardware/zeroplus-logic-cube/$(DEPDIR)/protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/input/$(DEPDIR)/binary.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/input/$(DEPDIR)/chronovu_la8.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/input/$(DEPDIR)/csv.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/input/$(DEPDIR)/input.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/input/$(DEPDIR)/raw_analog.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/input/$(DEPDIR)/trace32_ad.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/input/$(DEPDIR)/vcd.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/input/$(DEPDIR)/wav.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/lcr/$(DEPDIR)/es51919.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/modbus/$(DEPDIR)/modbus.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/modbus/$(DEPDIR)/modbus_serial_rtu.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/analog.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/ascii.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/binary.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/bits.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/chronovu_la8.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/csv.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/gnuplot.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/hex.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/ols.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/output.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/srzip.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/vcd.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/output/$(DEPDIR)/wav.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scale/$(DEPDIR)/kern.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/helpers.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/scpi.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/scpi_libgpib.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/scpi_serial.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/scpi_tcp.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/scpi_usbtmc_libusb.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/scpi_visa.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/scpi_vxi.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/vxi_clnt.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/scpi/$(DEPDIR)/vxi_xdr.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/transform/$(DEPDIR)/invert.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/transform/$(DEPDIR)/nop.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/transform/$(DEPDIR)/scale.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/transform/$(DEPDIR)/transform.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/analog.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/core.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/device.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/driver_all.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/input_all.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/input_binary.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/lib.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/main.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/output_all.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/session.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/strutil.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/transform_all.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/trigger.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at tests/$(DEPDIR)/version.Po at am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -1870,175 +2749,94 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
-tests/tests_check_main-lib.o: tests/lib.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-lib.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-lib.Tpo -c -o tests/tests_check_main-lib.o `test -f 'tests/lib.c' || echo '$(srcdir)/'`tests/lib.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-lib.Tpo tests/$(DEPDIR)/tests_check_main-lib.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/lib.c' object='tests/tests_check_main-lib.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-lib.o `test -f 'tests/lib.c' || echo '$(srcdir)/'`tests/lib.c
-
-tests/tests_check_main-lib.obj: tests/lib.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-lib.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-lib.Tpo -c -o tests/tests_check_main-lib.obj `if test -f 'tests/lib.c'; then $(CYGPATH_W) 'tests/lib.c'; else $(CYGPATH_W) '$(srcdir)/tests/lib.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-lib.Tpo tests/$(DEPDIR)/tests_check_main-lib.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/lib.c' object='tests/tests_check_main-lib.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-lib.obj `if test -f 'tests/lib.c'; then $(CYGPATH_W) 'tests/lib.c'; else $(CYGPATH_W) '$(srcdir)/tests/lib.c'; fi`
-
-tests/tests_check_main-check_main.o: tests/check_main.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_main.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_main.Tpo -c -o tests/tests_check_main-check_main.o `test -f 'tests/check_main.c' || echo '$(srcdir)/'`tests/check_main.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_main.Tpo tests/$(DEPDIR)/tests_check_main-check_main.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_main.c' object='tests/tests_check_main-check_main.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_main.o `test -f 'tests/check_main.c' || echo '$(srcdir)/'`tests/check_main.c
-
-tests/tests_check_main-check_main.obj: tests/check_main.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_main.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_main.Tpo -c -o tests/tests_check_main-check_main.obj `if test -f 'tests/check_main.c'; then $(CYGPATH_W) 'tests/check_main.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_main.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_main.Tpo tests/$(DEPDIR)/tests_check_main-check_main.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_main.c' object='tests/tests_check_main-check_main.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_main.obj `if test -f 'tests/check_main.c'; then $(CYGPATH_W) 'tests/check_main.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_main.c'; fi`
-
-tests/tests_check_main-check_core.o: tests/check_core.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_core.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_core.Tpo -c -o tests/tests_check_main-check_core.o `test -f 'tests/check_core.c' || echo '$(srcdir)/'`tests/check_core.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_core.Tpo tests/$(DEPDIR)/tests_check_main-check_core.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_core.c' object='tests/tests_check_main-check_core.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_core.o `test -f 'tests/check_core.c' || echo '$(srcdir)/'`tests/check_core.c
-
-tests/tests_check_main-check_core.obj: tests/check_core.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_core.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_core.Tpo -c -o tests/tests_check_main-check_core.obj `if test -f 'tests/check_core.c'; then $(CYGPATH_W) 'tests/check_core.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_core.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_core.Tpo tests/$(DEPDIR)/tests_check_main-check_core.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_core.c' object='tests/tests_check_main-check_core.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_core.obj `if test -f 'tests/check_core.c'; then $(CYGPATH_W) 'tests/check_core.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_core.c'; fi`
-
-tests/tests_check_main-check_input_all.o: tests/check_input_all.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_input_all.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_input_all.Tpo -c -o tests/tests_check_main-check_input_all.o `test -f 'tests/check_input_all.c' || echo '$(srcdir)/'`tests/check_input_all.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_input_all.Tpo tests/$(DEPDIR)/tests_check_main-check_input_all.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_input_all.c' object='tests/tests_check_main-check_input_all.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_input_all.o `test -f 'tests/check_input_all.c' || echo '$(srcdir)/'`tests/check_input_all.c
-
-tests/tests_check_main-check_input_all.obj: tests/check_input_all.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_input_all.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_input_all.Tpo -c -o tests/tests_check_main-check_input_all.obj `if test -f 'tests/check_input_all.c'; then $(CYGPATH_W) 'tests/check_input_all.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_input_all.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_input_all.Tpo tests/$(DEPDIR)/tests_check_main-check_input_all.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_input_all.c' object='tests/tests_check_main-check_input_all.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_input_all.obj `if test -f 'tests/check_input_all.c'; then $(CYGPATH_W) 'tests/check_input_all.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_input_all.c'; fi`
-
-tests/tests_check_main-check_input_binary.o: tests/check_input_binary.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_input_binary.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_input_binary.Tpo -c -o tests/tests_check_main-check_input_binary.o `test -f 'tests/check_input_binary.c' || echo '$(srcdir)/'`tests/check_input_binary.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_input_binary.Tpo tests/$(DEPDIR)/tests_check_main-check_input_binary.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_input_binary.c' object='tests/tests_check_main-check_input_binary.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_input_binary.o `test -f 'tests/check_input_binary.c' || echo '$(srcdir)/'`tests/check_input_binary.c
-
-tests/tests_check_main-check_input_binary.obj: tests/check_input_binary.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_input_binary.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_input_binary.Tpo -c -o tests/tests_check_main-check_input_binary.obj `if test -f 'tests/check_input_binary.c'; then $(CYGPATH_W) 'tests/check_input_binary.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_input_binary.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_input_binary.Tpo tests/$(DEPDIR)/tests_check_main-check_input_binary.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_input_binary.c' object='tests/tests_check_main-check_input_binary.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_input_binary.obj `if test -f 'tests/check_input_binary.c'; then $(CYGPATH_W) 'tests/check_input_binary.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_input_binary.c'; fi`
-
-tests/tests_check_main-check_output_all.o: tests/check_output_all.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_output_all.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_output_all.Tpo -c -o tests/tests_check_main-check_output_all.o `test -f 'tests/check_output_all.c' || echo '$(srcdir)/'`tests/check_output_all.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_output_all.Tpo tests/$(DEPDIR)/tests_check_main-check_output_all.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_output_all.c' object='tests/tests_check_main-check_output_all.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_output_all.o `test -f 'tests/check_output_all.c' || echo '$(srcdir)/'`tests/check_output_all.c
-
-tests/tests_check_main-check_output_all.obj: tests/check_output_all.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_output_all.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_output_all.Tpo -c -o tests/tests_check_main-check_output_all.obj `if test -f 'tests/check_output_all.c'; then $(CYGPATH_W) 'tests/check_output_all.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_output_all.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_output_all.Tpo tests/$(DEPDIR)/tests_check_main-check_output_all.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_output_all.c' object='tests/tests_check_main-check_output_all.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_output_all.obj `if test -f 'tests/check_output_all.c'; then $(CYGPATH_W) 'tests/check_output_all.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_output_all.c'; fi`
-
-tests/tests_check_main-check_strutil.o: tests/check_strutil.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_strutil.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_strutil.Tpo -c -o tests/tests_check_main-check_strutil.o `test -f 'tests/check_strutil.c' || echo '$(srcdir)/'`tests/check_strutil.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_strutil.Tpo tests/$(DEPDIR)/tests_check_main-check_strutil.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_strutil.c' object='tests/tests_check_main-check_strutil.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_strutil.o `test -f 'tests/check_strutil.c' || echo '$(srcdir)/'`tests/check_strutil.c
-
-tests/tests_check_main-check_strutil.obj: tests/check_strutil.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_strutil.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_strutil.Tpo -c -o tests/tests_check_main-check_strutil.obj `if test -f 'tests/check_strutil.c'; then $(CYGPATH_W) 'tests/check_strutil.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_strutil.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_strutil.Tpo tests/$(DEPDIR)/tests_check_main-check_strutil.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_strutil.c' object='tests/tests_check_main-check_strutil.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_strutil.obj `if test -f 'tests/check_strutil.c'; then $(CYGPATH_W) 'tests/check_strutil.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_strutil.c'; fi`
-
-tests/tests_check_main-check_version.o: tests/check_version.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_version.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_version.Tpo -c -o tests/tests_check_main-check_version.o `test -f 'tests/check_version.c' || echo '$(srcdir)/'`tests/check_version.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_version.Tpo tests/$(DEPDIR)/tests_check_main-check_version.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_version.c' object='tests/tests_check_main-check_version.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_version.o `test -f 'tests/check_version.c' || echo '$(srcdir)/'`tests/check_version.c
-
-tests/tests_check_main-check_version.obj: tests/check_version.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_version.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_version.Tpo -c -o tests/tests_check_main-check_version.obj `if test -f 'tests/check_version.c'; then $(CYGPATH_W) 'tests/check_version.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_version.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_version.Tpo tests/$(DEPDIR)/tests_check_main-check_version.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_version.c' object='tests/tests_check_main-check_version.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_version.obj `if test -f 'tests/check_version.c'; then $(CYGPATH_W) 'tests/check_version.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_version.c'; fi`
-
-tests/tests_check_main-check_driver_all.o: tests/check_driver_all.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_driver_all.o -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_driver_all.Tpo -c -o tests/tests_check_main-check_driver_all.o `test -f 'tests/check_driver_all.c' || echo '$(srcdir)/'`tests/check_driver_all.c
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_driver_all.Tpo tests/$(DEPDIR)/tests_check_main-check_driver_all.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_driver_all.c' object='tests/tests_check_main-check_driver_all.o' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_driver_all.o `test -f 'tests/check_driver_all.c' || echo '$(srcdir)/'`tests/check_driver_all.c
-
-tests/tests_check_main-check_driver_all.obj: tests/check_driver_all.c
- at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -MT tests/tests_check_main-check_driver_all.obj -MD -MP -MF tests/$(DEPDIR)/tests_check_main-check_driver_all.Tpo -c -o tests/tests_check_main-check_driver_all.obj `if test -f 'tests/check_driver_all.c'; then $(CYGPATH_W) 'tests/check_driver_all.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_driver_all.c'; fi`
- at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/tests_check_main-check_driver_all.Tpo tests/$(DEPDIR)/tests_check_main-check_driver_all.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/check_driver_all.c' object='tests/tests_check_main-check_driver_all.obj' libtool=no @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_check_main_CFLAGS) $(CFLAGS) -c -o tests/tests_check_main-check_driver_all.obj `if test -f 'tests/check_driver_all.c'; then $(CYGPATH_W) 'tests/check_driver_all.c'; else $(CYGPATH_W) '$(srcdir)/tests/check_driver_all.c'; fi`
+.cpp.o:
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCXX_TRUE@	$(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCXX_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCXX_TRUE@	$(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCXX_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+ at am__fastdepCXX_TRUE@	$(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCXX_TRUE@	$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCXX_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(AM_V_CXX at am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
 
 mostlyclean-libtool:
 	-rm -f *.lo
 
 clean-libtool:
 	-rm -rf .libs _libs
-	-rm -rf hardware/agilent-dmm/.libs hardware/agilent-dmm/_libs
-	-rm -rf hardware/appa-55ii/.libs hardware/appa-55ii/_libs
-	-rm -rf hardware/asix-sigma/.libs hardware/asix-sigma/_libs
-	-rm -rf hardware/atten-pps3xxx/.libs hardware/atten-pps3xxx/_libs
-	-rm -rf hardware/brymen-bm86x/.libs hardware/brymen-bm86x/_libs
-	-rm -rf hardware/brymen-dmm/.libs hardware/brymen-dmm/_libs
-	-rm -rf hardware/cem-dt-885x/.libs hardware/cem-dt-885x/_libs
-	-rm -rf hardware/center-3xx/.libs hardware/center-3xx/_libs
-	-rm -rf hardware/chronovu-la/.libs hardware/chronovu-la/_libs
-	-rm -rf hardware/colead-slm/.libs hardware/colead-slm/_libs
-	-rm -rf hardware/common/.libs hardware/common/_libs
-	-rm -rf hardware/common/dmm/.libs hardware/common/dmm/_libs
-	-rm -rf hardware/conrad-digi-35-cpu/.libs hardware/conrad-digi-35-cpu/_libs
-	-rm -rf hardware/demo/.libs hardware/demo/_libs
-	-rm -rf hardware/fluke-dmm/.libs hardware/fluke-dmm/_libs
-	-rm -rf hardware/fx2lafw/.libs hardware/fx2lafw/_libs
-	-rm -rf hardware/gmc-mh-1x-2x/.libs hardware/gmc-mh-1x-2x/_libs
-	-rm -rf hardware/hameg-hmo/.libs hardware/hameg-hmo/_libs
-	-rm -rf hardware/hantek-dso/.libs hardware/hantek-dso/_libs
-	-rm -rf hardware/ikalogic-scanalogic2/.libs hardware/ikalogic-scanalogic2/_libs
-	-rm -rf hardware/ikalogic-scanaplus/.libs hardware/ikalogic-scanaplus/_libs
-	-rm -rf hardware/kecheng-kc-330b/.libs hardware/kecheng-kc-330b/_libs
-	-rm -rf hardware/lascar-el-usb/.libs hardware/lascar-el-usb/_libs
-	-rm -rf hardware/mic-985xx/.libs hardware/mic-985xx/_libs
-	-rm -rf hardware/norma-dmm/.libs hardware/norma-dmm/_libs
-	-rm -rf hardware/openbench-logic-sniffer/.libs hardware/openbench-logic-sniffer/_libs
-	-rm -rf hardware/rigol-ds/.libs hardware/rigol-ds/_libs
-	-rm -rf hardware/saleae-logic16/.libs hardware/saleae-logic16/_libs
-	-rm -rf hardware/serial-dmm/.libs hardware/serial-dmm/_libs
-	-rm -rf hardware/sysclk-lwla/.libs hardware/sysclk-lwla/_libs
-	-rm -rf hardware/teleinfo/.libs hardware/teleinfo/_libs
-	-rm -rf hardware/tondaj-sl-814/.libs hardware/tondaj-sl-814/_libs
-	-rm -rf hardware/uni-t-dmm/.libs hardware/uni-t-dmm/_libs
-	-rm -rf hardware/uni-t-ut32x/.libs hardware/uni-t-ut32x/_libs
-	-rm -rf hardware/victor-dmm/.libs hardware/victor-dmm/_libs
-	-rm -rf hardware/zeroplus-logic-cube/.libs hardware/zeroplus-logic-cube/_libs
-	-rm -rf input/.libs input/_libs
-	-rm -rf output/.libs output/_libs
+	-rm -rf bindings/cxx/.libs bindings/cxx/_libs
+	-rm -rf src/.libs src/_libs
+	-rm -rf src/dmm/.libs src/dmm/_libs
+	-rm -rf src/hardware/agilent-dmm/.libs src/hardware/agilent-dmm/_libs
+	-rm -rf src/hardware/appa-55ii/.libs src/hardware/appa-55ii/_libs
+	-rm -rf src/hardware/asix-sigma/.libs src/hardware/asix-sigma/_libs
+	-rm -rf src/hardware/atten-pps3xxx/.libs src/hardware/atten-pps3xxx/_libs
+	-rm -rf src/hardware/baylibre-acme/.libs src/hardware/baylibre-acme/_libs
+	-rm -rf src/hardware/beaglelogic/.libs src/hardware/beaglelogic/_libs
+	-rm -rf src/hardware/brymen-bm86x/.libs src/hardware/brymen-bm86x/_libs
+	-rm -rf src/hardware/brymen-dmm/.libs src/hardware/brymen-dmm/_libs
+	-rm -rf src/hardware/cem-dt-885x/.libs src/hardware/cem-dt-885x/_libs
+	-rm -rf src/hardware/center-3xx/.libs src/hardware/center-3xx/_libs
+	-rm -rf src/hardware/chronovu-la/.libs src/hardware/chronovu-la/_libs
+	-rm -rf src/hardware/colead-slm/.libs src/hardware/colead-slm/_libs
+	-rm -rf src/hardware/conrad-digi-35-cpu/.libs src/hardware/conrad-digi-35-cpu/_libs
+	-rm -rf src/hardware/demo/.libs src/hardware/demo/_libs
+	-rm -rf src/hardware/deree-de5000/.libs src/hardware/deree-de5000/_libs
+	-rm -rf src/hardware/fluke-dmm/.libs src/hardware/fluke-dmm/_libs
+	-rm -rf src/hardware/fx2lafw/.libs src/hardware/fx2lafw/_libs
+	-rm -rf src/hardware/gmc-mh-1x-2x/.libs src/hardware/gmc-mh-1x-2x/_libs
+	-rm -rf src/hardware/gwinstek-gds-800/.libs src/hardware/gwinstek-gds-800/_libs
+	-rm -rf src/hardware/hameg-hmo/.libs src/hardware/hameg-hmo/_libs
+	-rm -rf src/hardware/hantek-dso/.libs src/hardware/hantek-dso/_libs
+	-rm -rf src/hardware/hung-chang-dso-2100/.libs src/hardware/hung-chang-dso-2100/_libs
+	-rm -rf src/hardware/ikalogic-scanalogic2/.libs src/hardware/ikalogic-scanalogic2/_libs
+	-rm -rf src/hardware/ikalogic-scanaplus/.libs src/hardware/ikalogic-scanaplus/_libs
+	-rm -rf src/hardware/kecheng-kc-330b/.libs src/hardware/kecheng-kc-330b/_libs
+	-rm -rf src/hardware/kern-scale/.libs src/hardware/kern-scale/_libs
+	-rm -rf src/hardware/korad-kaxxxxp/.libs src/hardware/korad-kaxxxxp/_libs
+	-rm -rf src/hardware/lascar-el-usb/.libs src/hardware/lascar-el-usb/_libs
+	-rm -rf src/hardware/lecroy-logicstudio/.libs src/hardware/lecroy-logicstudio/_libs
+	-rm -rf src/hardware/manson-hcs-3xxx/.libs src/hardware/manson-hcs-3xxx/_libs
+	-rm -rf src/hardware/maynuo-m97/.libs src/hardware/maynuo-m97/_libs
+	-rm -rf src/hardware/mic-985xx/.libs src/hardware/mic-985xx/_libs
+	-rm -rf src/hardware/motech-lps-30x/.libs src/hardware/motech-lps-30x/_libs
+	-rm -rf src/hardware/norma-dmm/.libs src/hardware/norma-dmm/_libs
+	-rm -rf src/hardware/openbench-logic-sniffer/.libs src/hardware/openbench-logic-sniffer/_libs
+	-rm -rf src/hardware/pipistrello-ols/.libs src/hardware/pipistrello-ols/_libs
+	-rm -rf src/hardware/rigol-ds/.libs src/hardware/rigol-ds/_libs
+	-rm -rf src/hardware/saleae-logic16/.libs src/hardware/saleae-logic16/_libs
+	-rm -rf src/hardware/scpi-pps/.libs src/hardware/scpi-pps/_libs
+	-rm -rf src/hardware/serial-dmm/.libs src/hardware/serial-dmm/_libs
+	-rm -rf src/hardware/sysclk-lwla/.libs src/hardware/sysclk-lwla/_libs
+	-rm -rf src/hardware/teleinfo/.libs src/hardware/teleinfo/_libs
+	-rm -rf src/hardware/testo/.libs src/hardware/testo/_libs
+	-rm -rf src/hardware/tondaj-sl-814/.libs src/hardware/tondaj-sl-814/_libs
+	-rm -rf src/hardware/uni-t-dmm/.libs src/hardware/uni-t-dmm/_libs
+	-rm -rf src/hardware/uni-t-ut32x/.libs src/hardware/uni-t-ut32x/_libs
+	-rm -rf src/hardware/victor-dmm/.libs src/hardware/victor-dmm/_libs
+	-rm -rf src/hardware/yokogawa-dlm/.libs src/hardware/yokogawa-dlm/_libs
+	-rm -rf src/hardware/zeroplus-logic-cube/.libs src/hardware/zeroplus-logic-cube/_libs
+	-rm -rf src/input/.libs src/input/_libs
+	-rm -rf src/lcr/.libs src/lcr/_libs
+	-rm -rf src/modbus/.libs src/modbus/_libs
+	-rm -rf src/output/.libs src/output/_libs
+	-rm -rf src/scale/.libs src/scale/_libs
+	-rm -rf src/scpi/.libs src/scpi/_libs
+	-rm -rf src/transform/.libs src/transform/_libs
 	-rm -rf tests/.libs tests/_libs
 
 distclean-libtool:
@@ -2064,6 +2862,27 @@ uninstall-pkgconfigDATA:
 	@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
 	dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
+install-bindings_cxx_libsigrokcxx_la_includeHEADERS: $(bindings_cxx_libsigrokcxx_la_include_HEADERS)
+	@$(NORMAL_INSTALL)
+	@list='$(bindings_cxx_libsigrokcxx_la_include_HEADERS)'; test -n "$(bindings_cxx_libsigrokcxx_la_includedir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)" || exit $$?; \
+	done
+
+uninstall-bindings_cxx_libsigrokcxx_la_includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(bindings_cxx_libsigrokcxx_la_include_HEADERS)'; test -n "$(bindings_cxx_libsigrokcxx_la_includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)'; $(am__uninstall_files_from_dir)
 install-library_includeHEADERS: $(library_include_HEADERS)
 	@$(NORMAL_INSTALL)
 	@list='$(library_include_HEADERS)'; test -n "$(library_includedir)" || list=; \
@@ -2085,6 +2904,48 @@ uninstall-library_includeHEADERS:
 	@list='$(library_include_HEADERS)'; test -n "$(library_includedir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
 	dir='$(DESTDIR)$(library_includedir)'; $(am__uninstall_files_from_dir)
+install-nodist_bindings_cxx_libsigrokcxx_la_includeHEADERS: $(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS)
+	@$(NORMAL_INSTALL)
+	@list='$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS)'; test -n "$(bindings_cxx_libsigrokcxx_la_includedir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)" || exit $$?; \
+	done
+
+uninstall-nodist_bindings_cxx_libsigrokcxx_la_includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS)'; test -n "$(bindings_cxx_libsigrokcxx_la_includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)'; $(am__uninstall_files_from_dir)
+install-nodist_library_includeHEADERS: $(nodist_library_include_HEADERS)
+	@$(NORMAL_INSTALL)
+	@list='$(nodist_library_include_HEADERS)'; test -n "$(library_includedir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(library_includedir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(library_includedir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(library_includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(library_includedir)" || exit $$?; \
+	done
+
+uninstall-nodist_library_includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(nodist_library_include_HEADERS)'; test -n "$(library_includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(library_includedir)'; $(am__uninstall_files_from_dir)
 
 ID: $(am__tagged_files)
 	$(am__define_uniq_tagged_files); mkid -fID $$unique
@@ -2175,7 +3036,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
 	if test -n "$$am__remaking_logs"; then \
 	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
 	       "recursion detected" >&2; \
-	else \
+	elif test -n "$$redo_logs"; then \
 	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
 	fi; \
 	if $(am__make_dryrun); then :; else \
@@ -2286,9 +3147,9 @@ recheck: all $(check_PROGRAMS)
 	        am__force_recheck=am--force-recheck \
 	        TEST_LOGS="$$log_list"; \
 	exit $$?
-tests/check_main.log: tests/check_main$(EXEEXT)
-	@p='tests/check_main$(EXEEXT)'; \
-	b='tests/check_main'; \
+tests/main.log: tests/main$(EXEEXT)
+	@p='tests/main$(EXEEXT)'; \
+	b='tests/main'; \
 	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
 	--log-file $$b.log --trs-file $$b.trs \
 	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
@@ -2373,15 +3234,15 @@ dist-xz: distdir
 	$(am__post_remove_distdir)
 
 dist-tarZ: distdir
-	@echo WARNING: "Support for shar distribution archives is" \
-	               "deprecated." >&2
+	@echo WARNING: "Support for distribution archives compressed with" \
+		       "legacy program 'compress' is deprecated." >&2
 	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
 	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
 	$(am__post_remove_distdir)
 
 dist-shar: distdir
-	@echo WARNING: "Support for distribution archives compressed with" \
-		       "legacy program 'compress' is deprecated." >&2
+	@echo WARNING: "Support for shar distribution archives is" \
+	               "deprecated." >&2
 	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
 	shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
 	$(am__post_remove_distdir)
@@ -2417,17 +3278,17 @@ distcheck: dist
 	esac
 	chmod -R a-w $(distdir)
 	chmod u+w $(distdir)
-	mkdir $(distdir)/_build $(distdir)/_inst
+	mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
 	chmod a-w $(distdir)
 	test -d $(distdir)/_build || exit 0; \
 	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
 	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
 	  && am__cwd=`pwd` \
-	  && $(am__cd) $(distdir)/_build \
-	  && ../configure \
+	  && $(am__cd) $(distdir)/_build/sub \
+	  && ../../configure \
 	    $(AM_DISTCHECK_CONFIGURE_FLAGS) \
 	    $(DISTCHECK_CONFIGURE_FLAGS) \
-	    --srcdir=.. --prefix="$$dc_install_base" \
+	    --srcdir=../.. --prefix="$$dc_install_base" \
 	  && $(MAKE) $(AM_MAKEFLAGS) \
 	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
 	  && $(MAKE) $(AM_MAKEFLAGS) check \
@@ -2484,9 +3345,9 @@ check-am: all-am
 	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
 	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
 check: check-am
-all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) config.h
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) config.h all-local
 installdirs:
-	for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(library_includedir)"; do \
+	for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)" "$(DESTDIR)$(library_includedir)" "$(DESTDIR)$(bindings_cxx_libsigrokcxx_la_includedir)" "$(DESTDIR)$(library_includedir)"; do \
 	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
 	done
 install: install-am
@@ -2518,97 +3379,138 @@ clean-generic:
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
 	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-	-rm -f hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/agilent-dmm/$(am__dirstamp)
-	-rm -f hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/appa-55ii/$(am__dirstamp)
-	-rm -f hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/asix-sigma/$(am__dirstamp)
-	-rm -f hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/atten-pps3xxx/$(am__dirstamp)
-	-rm -f hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/brymen-bm86x/$(am__dirstamp)
-	-rm -f hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/brymen-dmm/$(am__dirstamp)
-	-rm -f hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/cem-dt-885x/$(am__dirstamp)
-	-rm -f hardware/center-3xx/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/center-3xx/$(am__dirstamp)
-	-rm -f hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/chronovu-la/$(am__dirstamp)
-	-rm -f hardware/colead-slm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/colead-slm/$(am__dirstamp)
-	-rm -f hardware/common/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/common/$(am__dirstamp)
-	-rm -f hardware/common/dmm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/common/dmm/$(am__dirstamp)
-	-rm -f hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/conrad-digi-35-cpu/$(am__dirstamp)
-	-rm -f hardware/demo/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/demo/$(am__dirstamp)
-	-rm -f hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/fluke-dmm/$(am__dirstamp)
-	-rm -f hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/fx2lafw/$(am__dirstamp)
-	-rm -f hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/gmc-mh-1x-2x/$(am__dirstamp)
-	-rm -f hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/hameg-hmo/$(am__dirstamp)
-	-rm -f hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/hantek-dso/$(am__dirstamp)
-	-rm -f hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/ikalogic-scanalogic2/$(am__dirstamp)
-	-rm -f hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/ikalogic-scanaplus/$(am__dirstamp)
-	-rm -f hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/kecheng-kc-330b/$(am__dirstamp)
-	-rm -f hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/lascar-el-usb/$(am__dirstamp)
-	-rm -f hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/mic-985xx/$(am__dirstamp)
-	-rm -f hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/norma-dmm/$(am__dirstamp)
-	-rm -f hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/openbench-logic-sniffer/$(am__dirstamp)
-	-rm -f hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/rigol-ds/$(am__dirstamp)
-	-rm -f hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/saleae-logic16/$(am__dirstamp)
-	-rm -f hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/serial-dmm/$(am__dirstamp)
-	-rm -f hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/sysclk-lwla/$(am__dirstamp)
-	-rm -f hardware/teleinfo/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/teleinfo/$(am__dirstamp)
-	-rm -f hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/tondaj-sl-814/$(am__dirstamp)
-	-rm -f hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/uni-t-dmm/$(am__dirstamp)
-	-rm -f hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/uni-t-ut32x/$(am__dirstamp)
-	-rm -f hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/victor-dmm/$(am__dirstamp)
-	-rm -f hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
-	-rm -f hardware/zeroplus-logic-cube/$(am__dirstamp)
-	-rm -f input/$(DEPDIR)/$(am__dirstamp)
-	-rm -f input/$(am__dirstamp)
-	-rm -f output/$(DEPDIR)/$(am__dirstamp)
-	-rm -f output/$(am__dirstamp)
+	-rm -f bindings/cxx/$(DEPDIR)/$(am__dirstamp)
+	-rm -f bindings/cxx/$(am__dirstamp)
+	-rm -f src/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/$(am__dirstamp)
+	-rm -f src/dmm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/dmm/$(am__dirstamp)
+	-rm -f src/hardware/agilent-dmm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/agilent-dmm/$(am__dirstamp)
+	-rm -f src/hardware/appa-55ii/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/appa-55ii/$(am__dirstamp)
+	-rm -f src/hardware/asix-sigma/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/asix-sigma/$(am__dirstamp)
+	-rm -f src/hardware/atten-pps3xxx/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/atten-pps3xxx/$(am__dirstamp)
+	-rm -f src/hardware/baylibre-acme/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/baylibre-acme/$(am__dirstamp)
+	-rm -f src/hardware/beaglelogic/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/beaglelogic/$(am__dirstamp)
+	-rm -f src/hardware/brymen-bm86x/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/brymen-bm86x/$(am__dirstamp)
+	-rm -f src/hardware/brymen-dmm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/brymen-dmm/$(am__dirstamp)
+	-rm -f src/hardware/cem-dt-885x/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/cem-dt-885x/$(am__dirstamp)
+	-rm -f src/hardware/center-3xx/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/center-3xx/$(am__dirstamp)
+	-rm -f src/hardware/chronovu-la/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/chronovu-la/$(am__dirstamp)
+	-rm -f src/hardware/colead-slm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/colead-slm/$(am__dirstamp)
+	-rm -f src/hardware/conrad-digi-35-cpu/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/conrad-digi-35-cpu/$(am__dirstamp)
+	-rm -f src/hardware/demo/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/demo/$(am__dirstamp)
+	-rm -f src/hardware/deree-de5000/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/deree-de5000/$(am__dirstamp)
+	-rm -f src/hardware/fluke-dmm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/fluke-dmm/$(am__dirstamp)
+	-rm -f src/hardware/fx2lafw/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/fx2lafw/$(am__dirstamp)
+	-rm -f src/hardware/gmc-mh-1x-2x/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/gmc-mh-1x-2x/$(am__dirstamp)
+	-rm -f src/hardware/gwinstek-gds-800/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/gwinstek-gds-800/$(am__dirstamp)
+	-rm -f src/hardware/hameg-hmo/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/hameg-hmo/$(am__dirstamp)
+	-rm -f src/hardware/hantek-dso/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/hantek-dso/$(am__dirstamp)
+	-rm -f src/hardware/hung-chang-dso-2100/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/hung-chang-dso-2100/$(am__dirstamp)
+	-rm -f src/hardware/ikalogic-scanalogic2/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/ikalogic-scanalogic2/$(am__dirstamp)
+	-rm -f src/hardware/ikalogic-scanaplus/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/ikalogic-scanaplus/$(am__dirstamp)
+	-rm -f src/hardware/kecheng-kc-330b/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/kecheng-kc-330b/$(am__dirstamp)
+	-rm -f src/hardware/kern-scale/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/kern-scale/$(am__dirstamp)
+	-rm -f src/hardware/korad-kaxxxxp/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/korad-kaxxxxp/$(am__dirstamp)
+	-rm -f src/hardware/lascar-el-usb/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/lascar-el-usb/$(am__dirstamp)
+	-rm -f src/hardware/lecroy-logicstudio/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/lecroy-logicstudio/$(am__dirstamp)
+	-rm -f src/hardware/manson-hcs-3xxx/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/manson-hcs-3xxx/$(am__dirstamp)
+	-rm -f src/hardware/maynuo-m97/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/maynuo-m97/$(am__dirstamp)
+	-rm -f src/hardware/mic-985xx/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/mic-985xx/$(am__dirstamp)
+	-rm -f src/hardware/motech-lps-30x/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/motech-lps-30x/$(am__dirstamp)
+	-rm -f src/hardware/norma-dmm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/norma-dmm/$(am__dirstamp)
+	-rm -f src/hardware/openbench-logic-sniffer/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/openbench-logic-sniffer/$(am__dirstamp)
+	-rm -f src/hardware/pipistrello-ols/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/pipistrello-ols/$(am__dirstamp)
+	-rm -f src/hardware/rigol-ds/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/rigol-ds/$(am__dirstamp)
+	-rm -f src/hardware/saleae-logic16/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/saleae-logic16/$(am__dirstamp)
+	-rm -f src/hardware/scpi-pps/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/scpi-pps/$(am__dirstamp)
+	-rm -f src/hardware/serial-dmm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/serial-dmm/$(am__dirstamp)
+	-rm -f src/hardware/sysclk-lwla/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/sysclk-lwla/$(am__dirstamp)
+	-rm -f src/hardware/teleinfo/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/teleinfo/$(am__dirstamp)
+	-rm -f src/hardware/testo/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/testo/$(am__dirstamp)
+	-rm -f src/hardware/tondaj-sl-814/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/tondaj-sl-814/$(am__dirstamp)
+	-rm -f src/hardware/uni-t-dmm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/uni-t-dmm/$(am__dirstamp)
+	-rm -f src/hardware/uni-t-ut32x/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/uni-t-ut32x/$(am__dirstamp)
+	-rm -f src/hardware/victor-dmm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/victor-dmm/$(am__dirstamp)
+	-rm -f src/hardware/yokogawa-dlm/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/yokogawa-dlm/$(am__dirstamp)
+	-rm -f src/hardware/zeroplus-logic-cube/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/hardware/zeroplus-logic-cube/$(am__dirstamp)
+	-rm -f src/input/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/input/$(am__dirstamp)
+	-rm -f src/lcr/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/lcr/$(am__dirstamp)
+	-rm -f src/modbus/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/modbus/$(am__dirstamp)
+	-rm -f src/output/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/output/$(am__dirstamp)
+	-rm -f src/scale/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/scale/$(am__dirstamp)
+	-rm -f src/scpi/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/scpi/$(am__dirstamp)
+	-rm -f src/transform/$(DEPDIR)/$(am__dirstamp)
+	-rm -f src/transform/$(am__dirstamp)
 	-rm -f tests/$(DEPDIR)/$(am__dirstamp)
 	-rm -f tests/$(am__dirstamp)
 
 maintainer-clean-generic:
 	@echo "This command is intended for maintainers to use"
 	@echo "it deletes files that may require special tools to rebuild."
-	-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
 clean: clean-am
 
 clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
-	clean-libtool mostlyclean-am
+	clean-libtool clean-local mostlyclean-am
 
 distclean: distclean-am
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-	-rm -rf ./$(DEPDIR) hardware/agilent-dmm/$(DEPDIR) hardware/appa-55ii/$(DEPDIR) hardware/asix-sigma/$(DEPDIR) hardware/atten-pps3xxx/$(DEPDIR) hardware/brymen-bm86x/$(DEPDIR) hardware/brymen-dmm/$(DEPDIR) hardware/cem-dt-885x/$(DEPDIR) hardware/center-3xx/$(DEPDIR) hardware/chronovu-la/$(DEPDIR) hardware/colead-slm/$(DEPDIR) hardware/common/$(DEPDIR) hardware/common/dmm/$(DEPDIR) hardware/conrad-digi-35-cpu/$(DEPDIR) hardware/demo/$(DEPDIR) hardware/fluke-dmm/$(DEPDIR) hardware/fx2lafw/ [...]
+	-rm -rf bindings/cxx/$(DEPDIR) src/$(DEPDIR) src/dmm/$(DEPDIR) src/hardware/agilent-dmm/$(DEPDIR) src/hardware/appa-55ii/$(DEPDIR) src/hardware/asix-sigma/$(DEPDIR) src/hardware/atten-pps3xxx/$(DEPDIR) src/hardware/baylibre-acme/$(DEPDIR) src/hardware/beaglelogic/$(DEPDIR) src/hardware/brymen-bm86x/$(DEPDIR) src/hardware/brymen-dmm/$(DEPDIR) src/hardware/cem-dt-885x/$(DEPDIR) src/hardware/center-3xx/$(DEPDIR) src/hardware/chronovu-la/$(DEPDIR) src/hardware/colead-slm/$(DEPDIR) src/hardw [...]
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-hdr distclean-libtool distclean-tags
@@ -2625,13 +3527,16 @@ info: info-am
 
 info-am:
 
-install-data-am: install-library_includeHEADERS install-pkgconfigDATA
+install-data-am: install-bindings_cxx_libsigrokcxx_la_includeHEADERS \
+	install-library_includeHEADERS \
+	install-nodist_bindings_cxx_libsigrokcxx_la_includeHEADERS \
+	install-nodist_library_includeHEADERS install-pkgconfigDATA
 
 install-dvi: install-dvi-am
 
 install-dvi-am:
 
-install-exec-am: install-libLTLIBRARIES
+install-exec-am: install-exec-local install-libLTLIBRARIES
 
 install-html: install-html-am
 
@@ -2656,7 +3561,7 @@ installcheck-am:
 maintainer-clean: maintainer-clean-am
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
 	-rm -rf $(top_srcdir)/autom4te.cache
-	-rm -rf ./$(DEPDIR) hardware/agilent-dmm/$(DEPDIR) hardware/appa-55ii/$(DEPDIR) hardware/asix-sigma/$(DEPDIR) hardware/atten-pps3xxx/$(DEPDIR) hardware/brymen-bm86x/$(DEPDIR) hardware/brymen-dmm/$(DEPDIR) hardware/cem-dt-885x/$(DEPDIR) hardware/center-3xx/$(DEPDIR) hardware/chronovu-la/$(DEPDIR) hardware/colead-slm/$(DEPDIR) hardware/common/$(DEPDIR) hardware/common/dmm/$(DEPDIR) hardware/conrad-digi-35-cpu/$(DEPDIR) hardware/demo/$(DEPDIR) hardware/fluke-dmm/$(DEPDIR) hardware/fx2lafw/ [...]
+	-rm -rf bindings/cxx/$(DEPDIR) src/$(DEPDIR) src/dmm/$(DEPDIR) src/hardware/agilent-dmm/$(DEPDIR) src/hardware/appa-55ii/$(DEPDIR) src/hardware/asix-sigma/$(DEPDIR) src/hardware/atten-pps3xxx/$(DEPDIR) src/hardware/baylibre-acme/$(DEPDIR) src/hardware/beaglelogic/$(DEPDIR) src/hardware/brymen-bm86x/$(DEPDIR) src/hardware/brymen-dmm/$(DEPDIR) src/hardware/cem-dt-885x/$(DEPDIR) src/hardware/center-3xx/$(DEPDIR) src/hardware/chronovu-la/$(DEPDIR) src/hardware/colead-slm/$(DEPDIR) src/hardw [...]
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -2673,38 +3578,204 @@ ps: ps-am
 
 ps-am:
 
-uninstall-am: uninstall-libLTLIBRARIES \
-	uninstall-library_includeHEADERS uninstall-pkgconfigDATA
+uninstall-am: uninstall-bindings_cxx_libsigrokcxx_la_includeHEADERS \
+	uninstall-libLTLIBRARIES uninstall-library_includeHEADERS \
+	uninstall-local \
+	uninstall-nodist_bindings_cxx_libsigrokcxx_la_includeHEADERS \
+	uninstall-nodist_library_includeHEADERS \
+	uninstall-pkgconfigDATA
 
 .MAKE: all check-am install-am install-strip
 
-.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-TESTS \
-	check-am clean clean-checkPROGRAMS clean-cscope clean-generic \
-	clean-libLTLIBRARIES clean-libtool cscope cscopelist-am ctags \
-	ctags-am dist dist-all dist-bzip2 dist-gzip dist-hook \
-	dist-lzip dist-shar dist-tarZ dist-xz dist-zip distcheck \
-	distclean distclean-compile distclean-generic distclean-hdr \
-	distclean-libtool distclean-tags distcleancheck distdir \
-	distuninstallcheck dvi dvi-am html html-am info info-am \
-	install install-am install-data install-data-am install-dvi \
-	install-dvi-am install-exec install-exec-am install-html \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local am--refresh check \
+	check-TESTS check-am clean clean-checkPROGRAMS clean-cscope \
+	clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
+	cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \
+	dist-gzip dist-hook dist-lzip dist-shar dist-tarZ dist-xz \
+	dist-zip distcheck distclean distclean-compile \
+	distclean-generic distclean-hdr distclean-libtool \
+	distclean-tags distcleancheck distdir distuninstallcheck dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-bindings_cxx_libsigrokcxx_la_includeHEADERS \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-exec-local install-html \
 	install-html-am install-info install-info-am \
 	install-libLTLIBRARIES install-library_includeHEADERS \
-	install-man install-pdf install-pdf-am install-pkgconfigDATA \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-compile \
-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	recheck tags tags-am uninstall uninstall-am \
+	install-man \
+	install-nodist_bindings_cxx_libsigrokcxx_la_includeHEADERS \
+	install-nodist_library_includeHEADERS install-pdf \
+	install-pdf-am install-pkgconfigDATA install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am recheck tags tags-am uninstall \
+	uninstall-am \
+	uninstall-bindings_cxx_libsigrokcxx_la_includeHEADERS \
 	uninstall-libLTLIBRARIES uninstall-library_includeHEADERS \
+	uninstall-local \
+	uninstall-nodist_bindings_cxx_libsigrokcxx_la_includeHEADERS \
+	uninstall-nodist_library_includeHEADERS \
 	uninstall-pkgconfigDATA
 
+.PRECIOUS: Makefile
 
-.PHONY: ChangeLog
-ChangeLog:
-	git --git-dir $(top_srcdir)/.git log > ChangeLog || touch ChangeLog
 
-dist-hook: ChangeLog
+ at BINDINGS_CXX_TRUE@doxy/xml/index.xml: include/libsigrok/libsigrok.h
+ at BINDINGS_CXX_TRUE@	$(AM_V_GEN)cd $(srcdir) && BUILDDIR=$(abs_builddir)/ doxygen Doxyfile 2>/dev/null
+
+ at BINDINGS_CXX_TRUE@bindings/swig/enums.i: bindings/cxx/enums.timestamp
+ at BINDINGS_CXX_TRUE@bindings/cxx/enums.cpp: bindings/cxx/enums.timestamp
+ at BINDINGS_CXX_TRUE@bindings/cxx/include/libsigrokcxx/enums.hpp: bindings/cxx/enums.timestamp
+
+ at BINDINGS_CXX_TRUE@bindings/cxx/enums.timestamp: $(srcdir)/bindings/cxx/enums.py doxy/xml/index.xml \
+ at BINDINGS_CXX_TRUE@		bindings/cxx/ConfigKey_methods.cpp bindings/cxx/QuantityFlag_methods.cpp
+ at BINDINGS_CXX_TRUE@	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/cxx/enums.py doxy/xml/index.xml
+ at BINDINGS_CXX_TRUE@	$(AM_V_at)touch $@
+
+ at BINDINGS_CXX_TRUE@bindings/cxx/classes.lo: bindings/cxx/classes.cpp bindings/cxx/enums.cpp \
+ at BINDINGS_CXX_TRUE@		$(library_include_HEADERS) $(nodist_library_include_HEADERS)
+
+ at BINDINGS_CXX_TRUE@cxx-clean:
+ at BINDINGS_CXX_TRUE@	rm -rf doxy/
+ at BINDINGS_CXX_TRUE@	rm -rf bindings/cxx/doxy/
+ at BINDINGS_CXX_TRUE@	rm -f bindings/swig/enums.i
+ at BINDINGS_CXX_TRUE@	rm -f bindings/cxx/enums.cpp
+ at BINDINGS_CXX_TRUE@	rm -f bindings/cxx/include/libsigrokcxx/enums.hpp
+ at BINDINGS_CXX_TRUE@	rm -f bindings/cxx/enums.timestamp
+
+$(CPPXMLDOC): bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp \
+		bindings/cxx/enums.timestamp
+	$(AM_V_GEN)cd $(srcdir)/bindings/cxx && BUILDDIR=$(abs_builddir)/bindings/cxx/ doxygen Doxyfile 2>/dev/null
+
+ at BINDINGS_PYTHON_TRUE@$(PDOC_START): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC)
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_at)test -d $(PDIR)/sigrok/core || $(MKDIR_P) $(PDIR)/sigrok/core
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py python $(CPPXMLDOC) start > $@
+
+ at BINDINGS_PYTHON_TRUE@$(PDOC_END): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC)
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_at)test -d $(PDIR)/sigrok/core || $(MKDIR_P) $(PDIR)/sigrok/core
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py python $(CPPXMLDOC) end > $@
+
+ at BINDINGS_PYTHON_TRUE@python-build: $(PDIR)/timestamp
+
+ at BINDINGS_PYTHON_TRUE@$(PDIR)/timestamp: $(PDIR)/sigrok/core/classes.i \
+ at BINDINGS_PYTHON_TRUE@		bindings/swig/classes.i bindings/swig/templates.i \
+ at BINDINGS_PYTHON_TRUE@		bindings/swig/enums.i $(PDOC_START) $(PDOC_END) \
+ at BINDINGS_PYTHON_TRUE@		$(library_include_HEADERS) \
+ at BINDINGS_PYTHON_TRUE@		$(nodist_library_include_HEADERS) \
+ at BINDINGS_PYTHON_TRUE@		$(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+ at BINDINGS_PYTHON_TRUE@		$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+ at BINDINGS_PYTHON_TRUE@		@ORDER@ bindings/cxx/libsigrokcxx.la
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_at)$(setup_py) clean --all 2>/dev/null
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_GEN)$(setup_py) build_ext --swig "$(SWIG)" --swig-opts '$(swig_defs)' build_py
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_at): >$@
+
+ at BINDINGS_PYTHON_TRUE@python-install:
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_at)$(MKDIR_P) "$(DESTDIR)$(prefix)" "$(DESTDIR)$(exec_prefix)"
+ at BINDINGS_PYTHON_TRUE@	destdir='$(DESTDIR)'; $(setup_py) install $${destdir:+"--root=$$destdir"} \
+ at BINDINGS_PYTHON_TRUE@		--prefix "$(prefix)" --exec-prefix "$(exec_prefix)"
+
+ at BINDINGS_PYTHON_TRUE@python-clean:
+ at BINDINGS_PYTHON_TRUE@	-$(AM_V_at)rm -f $(PDIR)/timestamp
+ at BINDINGS_PYTHON_TRUE@	-$(AM_V_at)rm -fr $(PDIR)/doxy
+ at BINDINGS_PYTHON_TRUE@	-$(AM_V_at)$(setup_py) clean --all 2>/dev/null
+
+ at BINDINGS_PYTHON_TRUE@python-doc:
+ at BINDINGS_PYTHON_TRUE@	$(AM_V_at)cd $(srcdir)/$(PDIR) && BUILDDIR="$(abs_builddir)/$(PDIR)/" doxygen Doxyfile 2>/dev/null
+
+ at BINDINGS_RUBY_TRUE@$(RDOC): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC)
+ at BINDINGS_RUBY_TRUE@	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py ruby $(CPPXMLDOC) > $@
+
+ at BINDINGS_RUBY_TRUE@$(RWRAP): $(RDIR)/classes.i $(RDOC) \
+ at BINDINGS_RUBY_TRUE@		bindings/swig/classes.i bindings/swig/templates.i \
+ at BINDINGS_RUBY_TRUE@		bindings/swig/enums.i
+ at BINDINGS_RUBY_TRUE@	$(AM_V_GEN)$(SWIG) -ruby -c++ -Ibindings -Ibindings/cxx/include $(swig_defs) -o $@ $<
+
+ at BINDINGS_RUBY_TRUE@$(ROBJ): $(RWRAP) \
+ at BINDINGS_RUBY_TRUE@		$(library_include_HEADERS) \
+ at BINDINGS_RUBY_TRUE@		$(nodist_library_include_HEADERS) \
+ at BINDINGS_RUBY_TRUE@		$(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+ at BINDINGS_RUBY_TRUE@		$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS)
+ at BINDINGS_RUBY_TRUE@	$(AM_V_CXX)$(CXX) $(RBSIGROK_CFLAGS) -I. -Iinclude -Ibindings/cxx/include -fPIC -o $@ -c $<
+
+ at BINDINGS_RUBY_TRUE@$(REXT): $(ROBJ) @ORDER@ bindings/cxx/libsigrokcxx.la
+ at BINDINGS_RUBY_TRUE@	$(AM_V_CXXLD)$(CXX) -shared -std=c++11 -o $@ $< -lsigrokcxx -Lbindings/cxx/.libs $(RBSIGROK_LIBS)
+
+ at BINDINGS_RUBY_TRUE@ruby-build: $(REXT)
+
+ at BINDINGS_RUBY_TRUE@ruby-install: $(REXT)
+ at BINDINGS_RUBY_TRUE@	$(INSTALL) -d $(DESTDIR)$(prefix)/$(RBSIGROK_EXTDIR)
+ at BINDINGS_RUBY_TRUE@	$(INSTALL) $< $(DESTDIR)$(prefix)/$(RBSIGROK_EXTDIR)
+
+ at BINDINGS_RUBY_TRUE@ruby-clean:
+ at BINDINGS_RUBY_TRUE@	-$(AM_V_at)rm -fr $(RDIR)/doc
+ at BINDINGS_RUBY_TRUE@	-$(AM_V_at)rm $(REXT) $(ROBJ) $(RWRAP) $(RDOC)
+
+ at BINDINGS_RUBY_TRUE@ruby-doc: $(RWRAP)
+ at BINDINGS_RUBY_TRUE@	$(AM_V_at)yard doc -o $(RDIR)/doc $<
+
+ at BINDINGS_JAVA_TRUE@java-build: $(JJAR) $(JLIB)
+
+ at BINDINGS_JAVA_TRUE@$(JDOC): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC)
+ at BINDINGS_JAVA_TRUE@	$(AM_V_at)test -d $(JCLS) || $(MKDIR_P) $(JCLS)
+ at BINDINGS_JAVA_TRUE@	$(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py java $(CPPXMLDOC) > $@
+
+ at BINDINGS_JAVA_TRUE@$(JCXX): $(srcdir)/$(JSWG) $(JDOC) bindings/swig/classes.i \
+ at BINDINGS_JAVA_TRUE@		bindings/swig/templates.i bindings/swig/enums.i \
+ at BINDINGS_JAVA_TRUE@		$(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+ at BINDINGS_JAVA_TRUE@		$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS)
+ at BINDINGS_JAVA_TRUE@	-$(AM_V_at)rm -f $(java_cleanfiles)
+ at BINDINGS_JAVA_TRUE@	$(AM_V_GEN)$(SWIG) -c++ $(swig_defs) \
+ at BINDINGS_JAVA_TRUE@		-java -package org.sigrok.core.classes \
+ at BINDINGS_JAVA_TRUE@		-Ibindings -I$(JCLS) $(local_includes) -I$(srcdir) $(JNI_CPPFLAGS) \
+ at BINDINGS_JAVA_TRUE@		-outdir $(JCLS) -o $@ $(srcdir)/$(JSWG)
+
+ at BINDINGS_JAVA_TRUE@$(JJAR): $(JCXX)
+ at BINDINGS_JAVA_TRUE@	$(AM_V_GEN)$(JAVAC) -d $(JDIR) $(JSRC)
+ at BINDINGS_JAVA_TRUE@	$(AM_V_at)jar cf $(JJAR) -C $(JDIR) $(JPKG)
+
+ at BINDINGS_JAVA_TRUE@$(JLIB): $(JCXX) \
+ at BINDINGS_JAVA_TRUE@		$(library_include_HEADERS) $(nodist_library_include_HEADERS) \
+ at BINDINGS_JAVA_TRUE@		$(bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+ at BINDINGS_JAVA_TRUE@		$(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS) \
+ at BINDINGS_JAVA_TRUE@		@ORDER@ bindings/cxx/libsigrokcxx.la
+ at BINDINGS_JAVA_TRUE@	$(AM_V_GEN)$(CXXCOMPILE) $(JNI_CPPFLAGS) -L.libs -Lbindings/cxx/.libs \
+ at BINDINGS_JAVA_TRUE@		-fno-strict-aliasing -fPIC -shared $(JCLS)/classes_wrap.cxx \
+ at BINDINGS_JAVA_TRUE@		-lsigrokcxx $(LIBSIGROKCXX_LIBS) -o $(JLIB)
+
+ at BINDINGS_JAVA_TRUE@java-install:
+ at BINDINGS_JAVA_TRUE@	$(INSTALL) -d $(DESTDIR)$(libdir)/jni
+ at BINDINGS_JAVA_TRUE@	$(INSTALL) $(JLIB) $(DESTDIR)$(libdir)/jni
+ at BINDINGS_JAVA_TRUE@	$(INSTALL) -d $(DESTDIR)$(datadir)/java
+ at BINDINGS_JAVA_TRUE@	$(INSTALL) $(JJAR) $(DESTDIR)$(datadir)/java
+
+ at BINDINGS_JAVA_TRUE@java-uninstall:
+ at BINDINGS_JAVA_TRUE@	-rm -f $(DESTDIR)$(datadir)/java/sigrok-core.jar
+ at BINDINGS_JAVA_TRUE@	-rm -f $(DESTDIR)$(libdir)/jni/libsigrok_java_core_classes.so
+
+ at BINDINGS_JAVA_TRUE@java-clean:
+ at BINDINGS_JAVA_TRUE@	-$(AM_V_at)rm -f $(java_cleanfiles) $(JDOC)
+ at BINDINGS_JAVA_TRUE@	-$(AM_V_at)rm -fr $(JDIR)/doxy
+
+ at BINDINGS_JAVA_TRUE@java-doc:
+ at BINDINGS_JAVA_TRUE@	$(AM_V_at)cd $(srcdir)/$(JDIR) && BUILDDIR="$(abs_builddir)/$(JDIR)/" doxygen Doxyfile
+
+all-local: $(BUILD_EXTRA)
+install-exec-local: $(INSTALL_EXTRA)
+uninstall-local: $(UNINSTALL_EXTRA)
+clean-local: $(CLEAN_EXTRA)
+
+.PHONY: dist-changelog
+
+dist-hook: dist-changelog
+
+dist-changelog:
+	$(AM_V_at)if test ! -d '$(top_srcdir)/.git'; then \
+		cp -f '$(top_srcdir)/ChangeLog' "$(top_distdir)/ChangeLog"; \
+	elif git -C '$(top_srcdir)' log >.ChangeLog.tmp; then \
+		mv -f .ChangeLog.tmp "$(top_distdir)/ChangeLog"; \
+	else \
+		rm -f .ChangeLog.tmp; exit 1; \
+	fi
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/NEWS b/NEWS
index b9aa277..57fac4d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,325 @@
+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)
 ------------------
 
@@ -74,7 +396,7 @@ Note: This release DOES change the libsigrok API. That means it is NOT
      - 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 dependecies:
+ * 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:
@@ -119,7 +441,7 @@ Note: This release DOES change the libsigrok API. That means it is NOT
    - 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 occuring when all channels were disabled (bugs #316, #347).
+   - 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'.
@@ -154,7 +476,7 @@ Note: This release DOES change the libsigrok API. That means it is NOT
    - Report max. possible number of samples (bugs #197, #258, #263).
  * Output modules:
    - Skip analog channels in logic-only output formats.
-   - Remove the obolete output module API, add wrapper calls for the new one.
+   - 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).
@@ -197,7 +519,7 @@ Note: This release DOES change the libsigrok API. That means it is NOT
    - 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 defineѕ names with _t suffix (POSIX reserved).
+   - The lib no longer defines names with _t suffix (POSIX reserved).
    - New API calls:
       - sr_session_dev_list()
       - sr_session_save_init()
diff --git a/README b/README
index 556ab3f..977c53a 100644
--- a/README
+++ b/README
@@ -27,20 +27,65 @@ Distro packagers should only use released tarballs (no git snapshots).
 Requirements
 ------------
 
- - git
- - gcc (>= 4.0)
+Requirements for the C library:
+
+ - git (only needed when building from git)
+ - gcc (>= 4.0) or clang
  - make
- - autoconf >= 2.63
- - automake >= 1.11
- - libtool
+ - autoconf >= 2.63 (only needed when building from git)
+ - automake >= 1.11 (only needed when building from git)
+ - libtool (only needed when building from git)
  - pkg-config >= 0.22
  - libglib >= 2.32.0
  - libzip >= 0.10
- - libserialport >= 0.1.0 (optional, used by some drivers)
- - librevisa >= 0.0.20130812 (optional, used by some drivers)
+ - libserialport >= 0.1.1 (optional, used by some drivers)
+ - librevisa >= 0.0.20130412 (optional, used by some drivers)
  - libusb-1.0 >= 1.0.16 (optional, used by some drivers)
- - libftdi >= 0.16 (optional, used by some drivers)
+ - libftdi >= 0.16 or libftdi1 >= 1.0 (optional, used by some drivers)
+ - libgpib (optional, used by some drivers)
+ - libieee1284 (optional, used by some drivers)
  - check >= 0.9.4 (optional, only needed to run unit tests)
+ - 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.7)
+   - clang++ (>= 3.1)
+ - 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 >= 1.9.3 (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
@@ -62,6 +107,10 @@ See INSTALL or the following wiki page for more (OS-specific) instructions:
 
  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
 ----------------------
@@ -90,13 +139,10 @@ 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.
 
 
-Mailing lists
--------------
-
-There are two mailing lists for sigrok/libsigrok:
+Mailing list
+------------
 
  https://lists.sourceforge.net/lists/listinfo/sigrok-devel
- https://lists.sourceforge.net/lists/listinfo/sigrok-commits
 
 
 IRC
diff --git a/README.devices b/README.devices
index db651fa..935f797 100644
--- a/README.devices
+++ b/README.devices
@@ -75,6 +75,7 @@ The following drivers/devices do not need any firmware upload:
  - mic-985xx (including all subdrivers)
  - norma-dmm
  - openbench-logic-sniffer
+ - pipistrello-ols
  - rigol-ds
  - serial-dmm (including all subdrivers)
  - teleinfo
@@ -82,6 +83,7 @@ The following drivers/devices do not need any firmware upload:
  - uni-t-dmm (including all subdrivers)
  - uni-t-ut32x
  - victor-dmm
+ - yokogawa-dlm
  - zeroplus-logic-cube
 
 
@@ -133,17 +135,19 @@ The following drivers/devices do not require a serial port specification:
  - ikalogic-scanaplus
  - kecheng-kc-330b
  - lascar-el-usb
+ - pipistrello-ols
  - rigol-ds (USBTMC or TCP)
  - saleae-logic16
  - sysclk-lwla
  - uni-t-dmm (including all subdrivers)
  - uni-t-ut32x
  - victor-dmm
+ - yokogawa-dlm (USBTMC or TCP)
  - zeroplus-logic-cube
 
 
-Specifiying serial port parameters
-----------------------------------
+Specifying serial port parameters
+---------------------------------
 
 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
@@ -315,12 +319,15 @@ a short list for convenience:
  - Tenma 72-7750: Briefly press the "RS232C" button.
  - UNI-T UT60G: Briefly press the "RS232C" button.
  - UNI-T UT61B/C/D: Press the "REL/RS232/USB" button for roughly 1 second.
+ - UNI-T UT71x: Press the "SEND/-/MAXMIN" button for roughly 1 second.
+   Briefly pressing the "EXIT" button leaves this mode again.
  - 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
diff --git a/aclocal.m4 b/aclocal.m4
index 9a85aac..5cc771a 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.14.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.15 -*- Autoconf -*-
 
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
 
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -20,244 +20,406 @@ You have another version of autoconf.  It may work, but is not guaranteed to.
 If you have problems, you may need to regenerate the build system entirely.
 To do so, use the procedure documented by the package, typically 'autoreconf'.])])
 
-# Configure paths for GLIB
-# Owen Taylor     1997-2001
-
-dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
-dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject,
-dnl gthread, or gio is specified in MODULES, pass to pkg-config
-dnl
-AC_DEFUN([AM_PATH_GLIB_2_0],
-[dnl 
-dnl Get the cflags and libraries from pkg-config
-dnl
-AC_ARG_ENABLE(glibtest, [  --disable-glibtest      do not try to compile and run a test GLIB program],
-		    , enable_glibtest=yes)
+# ===========================================================================
+#    http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_JNI_INCLUDE_DIR
+#
+# DESCRIPTION
+#
+#   AX_JNI_INCLUDE_DIR finds include directories needed for compiling
+#   programs using the JNI interface.
+#
+#   JNI include directories are usually in the Java distribution. This is
+#   deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
+#   that order. When this macro completes, a list of directories is left in
+#   the variable JNI_INCLUDE_DIRS.
+#
+#   Example usage follows:
+#
+#     AX_JNI_INCLUDE_DIR
+#
+#     for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
+#     do
+#             CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
+#     done
+#
+#   If you want to force a specific compiler:
+#
+#   - at the configure.in level, set JAVAC=yourcompiler before calling
+#   AX_JNI_INCLUDE_DIR
+#
+#   - at the configure level, setenv JAVAC
+#
+#   Note: This macro can work with the autoconf M4 macros for Java programs.
+#   This particular macro is not part of the original set of macros.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Don Anderson <dda at sleepycat.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
 
-  pkg_config_args=glib-2.0
-  for module in . $4
-  do
-      case "$module" in
-         gmodule) 
-             pkg_config_args="$pkg_config_args gmodule-2.0"
-         ;;
-         gmodule-no-export) 
-             pkg_config_args="$pkg_config_args gmodule-no-export-2.0"
-         ;;
-         gobject) 
-             pkg_config_args="$pkg_config_args gobject-2.0"
-         ;;
-         gthread) 
-             pkg_config_args="$pkg_config_args gthread-2.0"
-         ;;
-         gio*) 
-             pkg_config_args="$pkg_config_args $module-2.0"
-         ;;
-      esac
-  done
+#serial 11
 
-  PKG_PROG_PKG_CONFIG([0.16])
+AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
+AC_DEFUN([AX_JNI_INCLUDE_DIR],[
 
-  no_glib=""
+JNI_INCLUDE_DIRS=""
 
-  if test "x$PKG_CONFIG" = x ; then
-    no_glib=yes
-    PKG_CONFIG=no
-  fi
+if test "x$JAVA_HOME" != x; then
+	_JTOPDIR="$JAVA_HOME"
+else
+	if test "x$JAVAC" = x; then
+		JAVAC=javac
+	fi
+	AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
+	if test "x$_ACJNI_JAVAC" = xno; then
+		AC_MSG_ERROR([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
+	fi
+	_ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
+	_JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
+fi
 
-  min_glib_version=ifelse([$1], ,2.0.0,$1)
-  AC_MSG_CHECKING(for GLIB - version >= $min_glib_version)
+case "$host_os" in
+        darwin*)        # Apple JDK is at /System location and has headers symlinked elsewhere
+                        case "$_JTOPDIR" in
+                        /System/Library/Frameworks/JavaVM.framework/*)
+				_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
+				_JINC="$_JTOPDIR/Headers";;
+			*)      _JINC="$_JTOPDIR/include";;
+                        esac;;
+        *)              _JINC="$_JTOPDIR/include";;
+esac
+_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
+_AS_ECHO_LOG([_JINC=$_JINC])
+
+# On Mac OS X 10.6.4, jni.h is a symlink:
+# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
+# -> ../../CurrentJDK/Headers/jni.h.
+AC_CHECK_FILE([$_JINC/jni.h],
+	[JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JINC"],
+	[_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
+	 AC_CHECK_FILE([$_JTOPDIR/include/jni.h],
+		[JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include"],
+                AC_MSG_ERROR([cannot find JDK header files]))
+	])
+
+# get the likely subdirectories for system specific java includes
+case "$host_os" in
+bsdi*)          _JNI_INC_SUBDIRS="bsdos";;
+freebsd*)       _JNI_INC_SUBDIRS="freebsd";;
+darwin*)        _JNI_INC_SUBDIRS="darwin";;
+linux*)         _JNI_INC_SUBDIRS="linux genunix";;
+osf*)           _JNI_INC_SUBDIRS="alpha";;
+solaris*)       _JNI_INC_SUBDIRS="solaris";;
+mingw*)		_JNI_INC_SUBDIRS="win32";;
+cygwin*)	_JNI_INC_SUBDIRS="win32";;
+*)              _JNI_INC_SUBDIRS="genunix";;
+esac
 
-  if test x$PKG_CONFIG != xno ; then
-    ## don't try to run the test against uninstalled libtool libs
-    if $PKG_CONFIG --uninstalled $pkg_config_args; then
-	  echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH"
-	  enable_glibtest=no
+# add any subdirectories that are present
+for JINCSUBDIR in $_JNI_INC_SUBDIRS
+do
+    if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
+         JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
     fi
+done
+])
 
-    if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then
-	  :
-    else
-	  no_glib=yes
-    fi
-  fi
+# _ACJNI_FOLLOW_SYMLINKS <path>
+# Follows symbolic links on <path>,
+# finally setting variable _ACJNI_FOLLOWED
+# ----------------------------------------
+AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
+# find the include directory relative to the javac executable
+_cur="$1"
+while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
+        AC_MSG_CHECKING([symlink for $_cur])
+        _slink=`ls -ld "$_cur" | sed 's/.* -> //'`
+        case "$_slink" in
+        /*) _cur="$_slink";;
+        # 'X' avoids triggering unwanted echo options.
+        *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
+        esac
+        AC_MSG_RESULT([$_cur])
+done
+_ACJNI_FOLLOWED="$_cur"
+])# _ACJNI
 
-  if test x"$no_glib" = x ; then
-    GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
-    GOBJECT_QUERY=`$PKG_CONFIG --variable=gobject_query glib-2.0`
-    GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
-    GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable=glib_compile_resources gio-2.0`
-
-    GLIB_CFLAGS=`$PKG_CONFIG --cflags $pkg_config_args`
-    GLIB_LIBS=`$PKG_CONFIG --libs $pkg_config_args`
-    glib_config_major_version=`$PKG_CONFIG --modversion glib-2.0 | \
-           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
-    glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.0 | \
-           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
-    glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.0 | \
-           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
-    if test "x$enable_glibtest" = "xyes" ; then
-      ac_save_CFLAGS="$CFLAGS"
-      ac_save_LIBS="$LIBS"
-      CFLAGS="$CFLAGS $GLIB_CFLAGS"
-      LIBS="$GLIB_LIBS $LIBS"
-dnl
-dnl Now check if the installed GLIB is sufficiently new. (Also sanity
-dnl checks the results of pkg-config to some extent)
-dnl
-      rm -f conf.glibtest
-      AC_TRY_RUN([
-#include <glib.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-int 
-main ()
-{
-  unsigned int major, minor, micro;
-
-  fclose (fopen ("conf.glibtest", "w"));
-
-  if (sscanf("$min_glib_version", "%u.%u.%u", &major, &minor, &micro) != 3) {
-     printf("%s, bad version string\n", "$min_glib_version");
-     exit(1);
-   }
-
-  if ((glib_major_version != $glib_config_major_version) ||
-      (glib_minor_version != $glib_config_minor_version) ||
-      (glib_micro_version != $glib_config_micro_version))
-    {
-      printf("\n*** 'pkg-config --modversion glib-2.0' returned %d.%d.%d, but GLIB (%d.%d.%d)\n", 
-             $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
-             glib_major_version, glib_minor_version, glib_micro_version);
-      printf ("*** was found! If pkg-config was correct, then it is best\n");
-      printf ("*** to remove the old version of GLib. You may also be able to fix the error\n");
-      printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
-      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
-      printf("*** required on your system.\n");
-      printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
-      printf("*** to point to the correct configuration files\n");
-    } 
-  else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
-	   (glib_minor_version != GLIB_MINOR_VERSION) ||
-           (glib_micro_version != GLIB_MICRO_VERSION))
-    {
-      printf("*** GLIB header files (version %d.%d.%d) do not match\n",
-	     GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
-      printf("*** library (version %d.%d.%d)\n",
-	     glib_major_version, glib_minor_version, glib_micro_version);
-    }
-  else
-    {
-      if ((glib_major_version > major) ||
-        ((glib_major_version == major) && (glib_minor_version > minor)) ||
-        ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
-      {
-        return 0;
-       }
-     else
-      {
-        printf("\n*** An old version of GLIB (%u.%u.%u) was found.\n",
-               glib_major_version, glib_minor_version, glib_micro_version);
-        printf("*** You need a version of GLIB newer than %u.%u.%u. The latest version of\n",
-	       major, minor, micro);
-        printf("*** GLIB is always available from ftp://ftp.gtk.org.\n");
-        printf("***\n");
-        printf("*** If you have already installed a sufficiently new version, this error\n");
-        printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
-        printf("*** being found. The easiest way to fix this is to remove the old version\n");
-        printf("*** of GLIB, but you can also set the PKG_CONFIG environment to point to the\n");
-        printf("*** correct copy of pkg-config. (In this case, you will have to\n");
-        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
-        printf("*** so that the correct libraries are found at run-time))\n");
-      }
-    }
-  return 1;
+# ===========================================================================
+#       http://www.gnu.org/software/autoconf-archive/ax_prog_javac.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PROG_JAVAC
+#
+# DESCRIPTION
+#
+#   AX_PROG_JAVAC tests an existing Java compiler. It uses the environment
+#   variable JAVAC then tests in sequence various common Java compilers. For
+#   political reasons, it starts with the free ones.
+#
+#   If you want to force a specific compiler:
+#
+#   - at the configure.in level, set JAVAC=yourcompiler before calling
+#   AX_PROG_JAVAC
+#
+#   - at the configure level, setenv JAVAC
+#
+#   You can use the JAVAC variable in your Makefile.in, with @JAVAC at .
+#
+#   *Warning*: its success or failure can depend on a proper setting of the
+#   CLASSPATH env. variable.
+#
+#   TODO: allow to exclude compilers (rationale: most Java programs cannot
+#   compile with some compilers like guavac).
+#
+#   Note: This is part of the set of autoconf M4 macros for Java programs.
+#   It is VERY IMPORTANT that you download the whole set, some macros depend
+#   on other. Unfortunately, the autoconf archive does not support the
+#   concept of set of macros, so I had to break it for submission. The
+#   general documentation, as well as the sample configure.in, is included
+#   in the AX_PROG_JAVA macro.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Stephane Bortzmeyer <bortzmeyer at pasteur.fr>
+#
+#   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/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 7
+
+AU_ALIAS([AC_PROG_JAVAC], [AX_PROG_JAVAC])
+AC_DEFUN([AX_PROG_JAVAC],[
+m4_define([m4_ax_prog_javac_list],["gcj -C" guavac jikes javac])dnl
+AS_IF([test "x$JAVAPREFIX" = x],
+      [test "x$JAVAC" = x && AC_CHECK_PROGS([JAVAC], [m4_ax_prog_javac_list])],
+      [test "x$JAVAC" = x && AC_CHECK_PROGS([JAVAC], [m4_ax_prog_javac_list], [], [$JAVAPREFIX/bin])])
+m4_undefine([m4_ax_prog_javac_list])dnl
+test "x$JAVAC" = x && AC_MSG_ERROR([no acceptable Java compiler found in \$PATH])
+AX_PROG_JAVAC_WORKS
+AC_PROVIDE([$0])dnl
+])
+
+# ===========================================================================
+#    http://www.gnu.org/software/autoconf-archive/ax_prog_javac_works.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PROG_JAVAC_WORKS
+#
+# DESCRIPTION
+#
+#   Internal use ONLY.
+#
+#   Note: This is part of the set of autoconf M4 macros for Java programs.
+#   It is VERY IMPORTANT that you download the whole set, some macros depend
+#   on other. Unfortunately, the autoconf archive does not support the
+#   concept of set of macros, so I had to break it for submission. The
+#   general documentation, as well as the sample configure.in, is included
+#   in the AX_PROG_JAVA macro.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Stephane Bortzmeyer <bortzmeyer at pasteur.fr>
+#
+#   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/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 6
+
+AU_ALIAS([AC_PROG_JAVAC_WORKS], [AX_PROG_JAVAC_WORKS])
+AC_DEFUN([AX_PROG_JAVAC_WORKS],[
+AC_CACHE_CHECK([if $JAVAC works], ac_cv_prog_javac_works, [
+JAVA_TEST=Test.java
+CLASS_TEST=Test.class
+cat << \EOF > $JAVA_TEST
+/* [#]line __oline__ "configure" */
+public class Test {
 }
-],, no_glib=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
-       CFLAGS="$ac_save_CFLAGS"
-       LIBS="$ac_save_LIBS"
-     fi
-  fi
-  if test "x$no_glib" = x ; then
-     AC_MSG_RESULT(yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version))
-     ifelse([$2], , :, [$2])     
-  else
-     AC_MSG_RESULT(no)
-     if test "$PKG_CONFIG" = "no" ; then
-       echo "*** A new enough version of pkg-config was not found."
-       echo "*** See http://www.freedesktop.org/software/pkgconfig/"
-     else
-       if test -f conf.glibtest ; then
-        :
-       else
-          echo "*** Could not run GLIB test program, checking why..."
-          ac_save_CFLAGS="$CFLAGS"
-          ac_save_LIBS="$LIBS"
-          CFLAGS="$CFLAGS $GLIB_CFLAGS"
-          LIBS="$LIBS $GLIB_LIBS"
-          AC_TRY_LINK([
-#include <glib.h>
-#include <stdio.h>
-],      [ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ],
-        [ echo "*** The test program compiled, but did not run. This usually means"
-          echo "*** that the run-time linker is not finding GLIB or finding the wrong"
-          echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your"
-          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
-          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
-          echo "*** is required on your system"
-	  echo "***"
-          echo "*** If you have an old version installed, it is best to remove it, although"
-          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
-        [ echo "*** The test program failed to compile or link. See the file config.log for the"
-          echo "*** exact error that occured. This usually means GLIB is incorrectly installed."])
-          CFLAGS="$ac_save_CFLAGS"
-          LIBS="$ac_save_LIBS"
-       fi
-     fi
-     GLIB_CFLAGS=""
-     GLIB_LIBS=""
-     GLIB_GENMARSHAL=""
-     GOBJECT_QUERY=""
-     GLIB_MKENUMS=""
-     GLIB_COMPILE_RESOURCES=""
-     ifelse([$3], , :, [$3])
-  fi
-  AC_SUBST(GLIB_CFLAGS)
-  AC_SUBST(GLIB_LIBS)
-  AC_SUBST(GLIB_GENMARSHAL)
-  AC_SUBST(GOBJECT_QUERY)
-  AC_SUBST(GLIB_MKENUMS)
-  AC_SUBST(GLIB_COMPILE_RESOURCES)
-  rm -f conf.glibtest
+EOF
+if AC_TRY_COMMAND($JAVAC $JAVACFLAGS $JAVA_TEST) >/dev/null 2>&1; then
+  ac_cv_prog_javac_works=yes
+else
+  AC_MSG_ERROR([The Java compiler $JAVAC failed (see config.log, check the CLASSPATH?)])
+  echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD
+  cat $JAVA_TEST >&AS_MESSAGE_LOG_FD
+fi
+rm -f $JAVA_TEST $CLASS_TEST
+])
+AC_PROVIDE([$0])dnl
 ])
 
-# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
-# serial 1 (pkg-config-0.24)
-# 
-# Copyright © 2004 Scott James Remnant <scott at netsplit.com>.
+# ===========================================================================
+#     http://www.gnu.org/software/autoconf-archive/ax_python_module.html
+# ===========================================================================
+#
+# SYNOPSIS
 #
-# 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.
+#   AX_PYTHON_MODULE(modname[, fatal, python])
 #
-# 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.
+# DESCRIPTION
 #
-# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#   Checks for Python module.
 #
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
+#   If fatal is non-empty then absence of a module will trigger an error.
+#   The third parameter can either be "python" for Python 2 or "python3" for
+#   Python 3; defaults to Python 3.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Andrew Collier
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 8
+
+AU_ALIAS([AC_PYTHON_MODULE], [AX_PYTHON_MODULE])
+AC_DEFUN([AX_PYTHON_MODULE],[
+    if test -z $PYTHON;
+    then
+        if test -z "$3";
+        then
+            PYTHON="python3"
+        else
+            PYTHON="$3"
+        fi
+    fi
+    PYTHON_NAME=`basename $PYTHON`
+    AC_MSG_CHECKING($PYTHON_NAME module: $1)
+    $PYTHON -c "import $1" 2>/dev/null
+    if test $? -eq 0;
+    then
+        AC_MSG_RESULT(yes)
+        eval AS_TR_CPP(HAVE_PYMOD_$1)=yes
+    else
+        AC_MSG_RESULT(no)
+        eval AS_TR_CPP(HAVE_PYMOD_$1)=no
+        #
+        if test -n "$2"
+        then
+            AC_MSG_ERROR(failed to find required module $1)
+            exit 1
+        fi
+    fi
+])
 
-# PKG_PROG_PKG_CONFIG([MIN-VERSION])
-# ----------------------------------
+dnl pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
+dnl serial 11 (pkg-config-0.29)
+dnl
+dnl Copyright © 2004 Scott James Remnant <scott at netsplit.com>.
+dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists at gmail.com>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+dnl 02111-1307, USA.
+dnl
+dnl As a special exception to the GNU General Public License, if you
+dnl distribute this file as part of a program that contains a
+dnl configuration script generated by Autoconf, you may include it under
+dnl the same distribution terms that you use for the rest of that
+dnl program.
+
+dnl PKG_PREREQ(MIN-VERSION)
+dnl -----------------------
+dnl Since: 0.29
+dnl
+dnl Verify that the version of the pkg-config macros are at least
+dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
+dnl installed version of pkg-config, this checks the developer's version
+dnl of pkg.m4 when generating configure.
+dnl
+dnl To ensure that this macro is defined, also add:
+dnl m4_ifndef([PKG_PREREQ],
+dnl     [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
+dnl
+dnl See the "Since" comment for each macro you use to see what version
+dnl of the macros you require.
+m4_defun([PKG_PREREQ],
+[m4_define([PKG_MACROS_VERSION], [0.29])
+m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
+    [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
+])dnl PKG_PREREQ
+
+dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
+dnl ----------------------------------
+dnl Since: 0.16
+dnl
+dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
+dnl first found in the path. Checks that the version of pkg-config found
+dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
+dnl used since that's the first version where most current features of
+dnl pkg-config existed.
 AC_DEFUN([PKG_PROG_PKG_CONFIG],
 [m4_pattern_forbid([^_?PKG_[A-Z_]+$])
 m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
@@ -279,18 +441,19 @@ if test -n "$PKG_CONFIG"; then
 		PKG_CONFIG=""
 	fi
 fi[]dnl
-])# PKG_PROG_PKG_CONFIG
+])dnl PKG_PROG_PKG_CONFIG
 
-# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
-#
-# Check to see whether a particular set of modules exists.  Similar
-# to PKG_CHECK_MODULES(), but does not set variables or print errors.
-#
-# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
-# only at the first occurence in configure.ac, so if the first place
-# it's called might be skipped (such as if it is within an "if", you
-# have to call PKG_CHECK_EXISTS manually
-# --------------------------------------------------------------
+dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------------------------------
+dnl Since: 0.18
+dnl
+dnl Check to see whether a particular set of modules exists. Similar to
+dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
+dnl
+dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+dnl only at the first occurence in configure.ac, so if the first place
+dnl it's called might be skipped (such as if it is within an "if", you
+dnl have to call PKG_CHECK_EXISTS manually
 AC_DEFUN([PKG_CHECK_EXISTS],
 [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
 if test -n "$PKG_CONFIG" && \
@@ -300,8 +463,10 @@ m4_ifvaln([$3], [else
   $3])dnl
 fi])
 
-# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
-# ---------------------------------------------
+dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+dnl ---------------------------------------------
+dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
+dnl pkg_failed based on the result.
 m4_define([_PKG_CONFIG],
 [if test -n "$$1"; then
     pkg_cv_[]$1="$$1"
@@ -313,10 +478,11 @@ m4_define([_PKG_CONFIG],
  else
     pkg_failed=untried
 fi[]dnl
-])# _PKG_CONFIG
+])dnl _PKG_CONFIG
 
-# _PKG_SHORT_ERRORS_SUPPORTED
-# -----------------------------
+dnl _PKG_SHORT_ERRORS_SUPPORTED
+dnl ---------------------------
+dnl Internal check to see if pkg-config supports short errors.
 AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
 [AC_REQUIRE([PKG_PROG_PKG_CONFIG])
 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -324,19 +490,17 @@ if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
 else
         _pkg_short_errors_supported=no
 fi[]dnl
-])# _PKG_SHORT_ERRORS_SUPPORTED
+])dnl _PKG_SHORT_ERRORS_SUPPORTED
 
 
-# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
-# [ACTION-IF-NOT-FOUND])
-#
-#
-# Note that if there is a possibility the first call to
-# PKG_CHECK_MODULES might not happen, you should be sure to include an
-# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
-#
-#
-# --------------------------------------------------------------
+dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl   [ACTION-IF-NOT-FOUND])
+dnl --------------------------------------------------------------
+dnl Since: 0.4.0
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
+dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
 AC_DEFUN([PKG_CHECK_MODULES],
 [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
 AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
@@ -390,9 +554,92 @@ else
         AC_MSG_RESULT([yes])
 	$3
 fi[]dnl
-])# PKG_CHECK_MODULES
+])dnl PKG_CHECK_MODULES
+
+
+dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl   [ACTION-IF-NOT-FOUND])
+dnl ---------------------------------------------------------------------
+dnl Since: 0.29
+dnl
+dnl Checks for existence of MODULES and gathers its build flags with
+dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
+dnl and VARIABLE-PREFIX_LIBS from --libs.
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
+dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
+dnl configure.ac.
+AC_DEFUN([PKG_CHECK_MODULES_STATIC],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+_save_PKG_CONFIG=$PKG_CONFIG
+PKG_CONFIG="$PKG_CONFIG --static"
+PKG_CHECK_MODULES($@)
+PKG_CONFIG=$_save_PKG_CONFIG[]dnl
+])dnl PKG_CHECK_MODULES_STATIC
+
+
+dnl PKG_INSTALLDIR([DIRECTORY])
+dnl -------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable pkgconfigdir as the location where a module
+dnl should install pkg-config .pc files. By default the directory is
+dnl $libdir/pkgconfig, but the default can be changed by passing
+dnl DIRECTORY. The user can override through the --with-pkgconfigdir
+dnl parameter.
+AC_DEFUN([PKG_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([pkgconfigdir],
+    [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
+    [with_pkgconfigdir=]pkg_default)
+AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_INSTALLDIR
+
+
+dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
+dnl --------------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable noarch_pkgconfigdir as the location where a
+dnl module should install arch-independent pkg-config .pc files. By
+dnl default the directory is $datadir/pkgconfig, but the default can be
+dnl changed by passing DIRECTORY. The user can override through the
+dnl --with-noarch-pkgconfigdir parameter.
+AC_DEFUN([PKG_NOARCH_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([noarch-pkgconfigdir],
+    [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
+    [with_noarch_pkgconfigdir=]pkg_default)
+AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_NOARCH_INSTALLDIR
+
+
+dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------
+dnl Since: 0.28
+dnl
+dnl Retrieves the value of the pkg-config variable for the given module.
+AC_DEFUN([PKG_CHECK_VAR],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
+
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
+AS_VAR_COPY([$1], [pkg_cv_][$1])
+
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
+])dnl PKG_CHECK_VAR
 
-# Copyright (C) 2002-2013 Free Software Foundation, Inc.
+# Copyright (C) 2002-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -404,10 +651,10 @@ fi[]dnl
 # generated from the m4 files accompanying Automake X.Y.
 # (This private macro should not be called outside this file.)
 AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.14'
+[am__api_version='1.15'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.14.1], [],
+m4_if([$1], [1.15], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -423,12 +670,12 @@ m4_define([_AM_AUTOCONF_VERSION], [])
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.14.1])dnl
+[AM_AUTOMAKE_VERSION([1.15])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 
-# Copyright (C) 2011-2013 Free Software Foundation, Inc.
+# Copyright (C) 2011-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -490,7 +737,7 @@ AC_SUBST([AR])dnl
 
 # AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
 
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -535,15 +782,51 @@ AC_SUBST([AR])dnl
 # configured tree to be moved without reconfiguration.
 
 AC_DEFUN([AM_AUX_DIR_EXPAND],
-[dnl Rely on autoconf to set up CDPATH properly.
-AC_PREREQ([2.50])dnl
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+])
+
+# AM_COND_IF                                            -*- Autoconf -*-
+
+# Copyright (C) 2008-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_COND_IF
+# _AM_COND_ELSE
+# _AM_COND_ENDIF
+# --------------
+# These macros are only used for tracing.
+m4_define([_AM_COND_IF])
+m4_define([_AM_COND_ELSE])
+m4_define([_AM_COND_ENDIF])
+
+# AM_COND_IF(COND, [IF-TRUE], [IF-FALSE])
+# ---------------------------------------
+# If the shell condition COND is true, execute IF-TRUE, otherwise execute
+# IF-FALSE.  Allow automake to learn about conditional instantiating macros
+# (the AC_CONFIG_FOOS).
+AC_DEFUN([AM_COND_IF],
+[m4_ifndef([_AM_COND_VALUE_$1],
+	   [m4_fatal([$0: no such condition "$1"])])dnl
+_AM_COND_IF([$1])dnl
+if test -z "$$1_TRUE"; then :
+  m4_n([$2])[]dnl
+m4_ifval([$3],
+[_AM_COND_ELSE([$1])dnl
+else
+  $3
+])dnl
+_AM_COND_ENDIF([$1])dnl
+fi[]dnl
 ])
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
 
-# Copyright (C) 1997-2013 Free Software Foundation, Inc.
+# Copyright (C) 1997-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -574,7 +857,7 @@ AC_CONFIG_COMMANDS_PRE(
 Usually this means the macro was only invoked conditionally.]])
 fi])])
 
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -765,7 +1048,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
 
 # Generate code to set up dependency tracking.              -*- Autoconf -*-
 
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -841,7 +1124,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
 
 # Do all the work for Automake.                             -*- Autoconf -*-
 
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -931,8 +1214,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl
 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
 AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
-# We need awk for the "check" target.  The system "awk" is bad on
-# some platforms.
+# We need awk for the "check" target (and possibly the TAP driver).  The
+# system "awk" is bad on some platforms.
 AC_REQUIRE([AC_PROG_AWK])dnl
 AC_REQUIRE([AC_PROG_MAKE_SET])dnl
 AC_REQUIRE([AM_SET_LEADING_DOT])dnl
@@ -1006,6 +1289,9 @@ END
     AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
   fi
 fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
 ])
 
 dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
@@ -1035,7 +1321,7 @@ for _am_header in $config_headers :; do
 done
 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
 
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1046,7 +1332,7 @@ echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_co
 # Define $install_sh.
 AC_DEFUN([AM_PROG_INSTALL_SH],
 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-if test x"${install_sh}" != xset; then
+if test x"${install_sh+set}" != xset; then
   case $am_aux_dir in
   *\ * | *\	*)
     install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -1056,7 +1342,7 @@ if test x"${install_sh}" != xset; then
 fi
 AC_SUBST([install_sh])])
 
-# Copyright (C) 2003-2013 Free Software Foundation, Inc.
+# Copyright (C) 2003-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1077,7 +1363,7 @@ AC_SUBST([am__leading_dot])])
 
 # Check to see how 'make' treats includes.	            -*- Autoconf -*-
 
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1127,7 +1413,7 @@ rm -f confinc confmf
 
 # Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
 
-# Copyright (C) 1997-2013 Free Software Foundation, Inc.
+# Copyright (C) 1997-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1166,7 +1452,7 @@ fi
 
 # Helper functions for option handling.                     -*- Autoconf -*-
 
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1195,7 +1481,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1242,7 +1528,242 @@ AC_LANG_POP([C])])
 # For backward compatibility.
 AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
 
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# ---------------------------------------------------------------------------
+# Adds support for distributing Python modules and packages.  To
+# install modules, copy them to $(pythondir), using the python_PYTHON
+# automake variable.  To install a package with the same name as the
+# automake package, install to $(pkgpythondir), or use the
+# pkgpython_PYTHON automake variable.
+#
+# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as
+# locations to install python extension modules (shared libraries).
+# Another macro is required to find the appropriate flags to compile
+# extension modules.
+#
+# If your package is configured with a different prefix to python,
+# users will have to add the install directory to the PYTHONPATH
+# environment variable, or create a .pth file (see the python
+# documentation for details).
+#
+# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will
+# cause an error if the version of python installed on the system
+# doesn't meet the requirement.  MINIMUM-VERSION should consist of
+# numbers and dots only.
+AC_DEFUN([AM_PATH_PYTHON],
+ [
+  dnl Find a Python interpreter.  Python versions prior to 2.0 are not
+  dnl supported. (2.0 was released on October 16, 2000).
+  m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
+[python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl
+ python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0])
+
+  AC_ARG_VAR([PYTHON], [the Python interpreter])
+
+  m4_if([$1],[],[
+    dnl No version check is needed.
+    # Find any Python interpreter.
+    if test -z "$PYTHON"; then
+      AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :)
+    fi
+    am_display_PYTHON=python
+  ], [
+    dnl A version check is needed.
+    if test -n "$PYTHON"; then
+      # If the user set $PYTHON, use it and don't search something else.
+      AC_MSG_CHECKING([whether $PYTHON version is >= $1])
+      AM_PYTHON_CHECK_VERSION([$PYTHON], [$1],
+			      [AC_MSG_RESULT([yes])],
+			      [AC_MSG_RESULT([no])
+			       AC_MSG_ERROR([Python interpreter is too old])])
+      am_display_PYTHON=$PYTHON
+    else
+      # Otherwise, try each interpreter until we find one that satisfies
+      # VERSION.
+      AC_CACHE_CHECK([for a Python interpreter with version >= $1],
+	[am_cv_pathless_PYTHON],[
+	for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do
+	  test "$am_cv_pathless_PYTHON" = none && break
+	  AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break])
+	done])
+      # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
+      if test "$am_cv_pathless_PYTHON" = none; then
+	PYTHON=:
+      else
+        AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON])
+      fi
+      am_display_PYTHON=$am_cv_pathless_PYTHON
+    fi
+  ])
+
+  if test "$PYTHON" = :; then
+  dnl Run any user-specified action, or abort.
+    m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])])
+  else
+
+  dnl Query Python for its version number.  Getting [:3] seems to be
+  dnl the best way to do this; it's what "site.py" does in the standard
+  dnl library.
+
+  AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version],
+    [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`])
+  AC_SUBST([PYTHON_VERSION], [$am_cv_python_version])
+
+  dnl Use the values of $prefix and $exec_prefix for the corresponding
+  dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX.  These are made
+  dnl distinct variables so they can be overridden if need be.  However,
+  dnl general consensus is that you shouldn't need this ability.
+
+  AC_SUBST([PYTHON_PREFIX], ['${prefix}'])
+  AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}'])
+
+  dnl At times (like when building shared libraries) you may want
+  dnl to know which OS platform Python thinks this is.
+
+  AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform],
+    [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`])
+  AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform])
+
+  # Just factor out some code duplication.
+  am_python_setup_sysconfig="\
+import sys
+# Prefer sysconfig over distutils.sysconfig, for better compatibility
+# with python 3.x.  See automake bug#10227.
+try:
+    import sysconfig
+except ImportError:
+    can_use_sysconfig = 0
+else:
+    can_use_sysconfig = 1
+# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs:
+# <https://github.com/pypa/virtualenv/issues/118>
+try:
+    from platform import python_implementation
+    if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7':
+        can_use_sysconfig = 0
+except ImportError:
+    pass"
+
+  dnl Set up 4 directories:
+
+  dnl pythondir -- where to install python scripts.  This is the
+  dnl   site-packages directory, not the python standard library
+  dnl   directory like in previous automake betas.  This behavior
+  dnl   is more consistent with lispdir.m4 for example.
+  dnl Query distutils for this directory.
+  AC_CACHE_CHECK([for $am_display_PYTHON script directory],
+    [am_cv_python_pythondir],
+    [if test "x$prefix" = xNONE
+     then
+       am_py_prefix=$ac_default_prefix
+     else
+       am_py_prefix=$prefix
+     fi
+     am_cv_python_pythondir=`$PYTHON -c "
+$am_python_setup_sysconfig
+if can_use_sysconfig:
+    sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'})
+else:
+    from distutils import sysconfig
+    sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix')
+sys.stdout.write(sitedir)"`
+     case $am_cv_python_pythondir in
+     $am_py_prefix*)
+       am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'`
+       am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"`
+       ;;
+     *)
+       case $am_py_prefix in
+         /usr|/System*) ;;
+         *)
+	  am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages
+	  ;;
+       esac
+       ;;
+     esac
+    ])
+  AC_SUBST([pythondir], [$am_cv_python_pythondir])
+
+  dnl pkgpythondir -- $PACKAGE directory under pythondir.  Was
+  dnl   PYTHON_SITE_PACKAGE in previous betas, but this naming is
+  dnl   more consistent with the rest of automake.
+
+  AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE])
+
+  dnl pyexecdir -- directory for installing python extension modules
+  dnl   (shared libraries)
+  dnl Query distutils for this directory.
+  AC_CACHE_CHECK([for $am_display_PYTHON extension module directory],
+    [am_cv_python_pyexecdir],
+    [if test "x$exec_prefix" = xNONE
+     then
+       am_py_exec_prefix=$am_py_prefix
+     else
+       am_py_exec_prefix=$exec_prefix
+     fi
+     am_cv_python_pyexecdir=`$PYTHON -c "
+$am_python_setup_sysconfig
+if can_use_sysconfig:
+    sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'})
+else:
+    from distutils import sysconfig
+    sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix')
+sys.stdout.write(sitedir)"`
+     case $am_cv_python_pyexecdir in
+     $am_py_exec_prefix*)
+       am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'`
+       am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"`
+       ;;
+     *)
+       case $am_py_exec_prefix in
+         /usr|/System*) ;;
+         *)
+	   am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages
+	   ;;
+       esac
+       ;;
+     esac
+    ])
+  AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir])
+
+  dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE)
+
+  AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE])
+
+  dnl Run any user-specified action.
+  $2
+  fi
+
+])
+
+
+# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+# ---------------------------------------------------------------------------
+# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION.
+# Run ACTION-IF-FALSE otherwise.
+# This test uses sys.hexversion instead of the string equivalent (first
+# word of sys.version), in order to cope with versions such as 2.2c1.
+# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000).
+AC_DEFUN([AM_PYTHON_CHECK_VERSION],
+ [prog="import sys
+# split strings by '.' and convert to numeric.  Append some zeros
+# because we need at least 4 digits for the hex conversion.
+# map returns an iterator in Python 3.0 and a list in 2.x
+minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]]
+minverhex = 0
+# xrange is not present in Python 3.0 and range returns an iterator
+for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]]
+sys.exit(sys.hexversion < minverhex)"
+  AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])])
+
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1261,7 +1782,7 @@ AC_DEFUN([AM_RUN_LOG],
 
 # Check to make sure that the build environment is sane.    -*- Autoconf -*-
 
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1342,7 +1863,7 @@ AC_CONFIG_COMMANDS_PRE(
 rm -f conftest.file
 ])
 
-# Copyright (C) 2009-2013 Free Software Foundation, Inc.
+# Copyright (C) 2009-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1402,7 +1923,7 @@ AC_SUBST([AM_BACKSLASH])dnl
 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
 ])
 
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1430,7 +1951,7 @@ fi
 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 AC_SUBST([INSTALL_STRIP_PROGRAM])])
 
-# Copyright (C) 2006-2013 Free Software Foundation, Inc.
+# Copyright (C) 2006-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1449,7 +1970,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004-2013 Free Software Foundation, Inc.
+# Copyright (C) 2004-2014 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1580,8 +2101,10 @@ AC_SUBST([am__tar])
 AC_SUBST([am__untar])
 ]) # _AM_PROG_TAR
 
-m4_include([autostuff/libtool.m4])
-m4_include([autostuff/ltoptions.m4])
-m4_include([autostuff/ltsugar.m4])
-m4_include([autostuff/ltversion.m4])
-m4_include([autostuff/lt~obsolete.m4])
+m4_include([m4/ax_cxx_compile_stdcxx_11.m4])
+m4_include([m4/libtool.m4])
+m4_include([m4/ltoptions.m4])
+m4_include([m4/ltsugar.m4])
+m4_include([m4/ltversion.m4])
+m4_include([m4/lt~obsolete.m4])
+m4_include([m4/sigrok.m4])
diff --git a/autostuff/ar-lib b/autostuff/ar-lib
index fe2301e..463b9ec 100755
--- a/autostuff/ar-lib
+++ b/autostuff/ar-lib
@@ -4,7 +4,7 @@
 me=ar-lib
 scriptversion=2012-03-01.08; # UTC
 
-# Copyright (C) 2010-2013 Free Software Foundation, Inc.
+# Copyright (C) 2010-2014 Free Software Foundation, Inc.
 # Written by Peter Rosin <peda at lysator.liu.se>.
 #
 # This program is free software; you can redistribute it and/or modify
diff --git a/autostuff/compile b/autostuff/compile
index 531136b..a85b723 100755
--- a/autostuff/compile
+++ b/autostuff/compile
@@ -3,7 +3,7 @@
 
 scriptversion=2012-10-14.11; # UTC
 
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 # Written by Tom Tromey <tromey at cygnus.com>.
 #
 # This program is free software; you can redistribute it and/or modify
diff --git a/autostuff/config.guess b/autostuff/config.guess
index b79252d..1659250 100755
--- a/autostuff/config.guess
+++ b/autostuff/config.guess
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2013 Free Software Foundation, Inc.
+#   Copyright 1992-2015 Free Software Foundation, Inc.
 
-timestamp='2013-06-10'
+timestamp='2015-08-20'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -24,12 +24,12 @@ timestamp='2013-06-10'
 # program.  This Exception is an additional permission under section 7
 # of the GNU General Public License, version 3 ("GPLv3").
 #
-# Originally written by Per Bothner.
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
 #
 # You can get the latest version of this script from:
 # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
 #
-# Please send patches with a ChangeLog entry to config-patches at gnu.org.
+# Please send patches to <config-patches at gnu.org>.
 
 
 me=`echo "$0" | sed -e 's,.*/,,'`
@@ -50,7 +50,7 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2015 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -149,7 +149,7 @@ Linux|GNU|GNU/*)
 	LIBC=gnu
 	#endif
 	EOF
-	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
 	;;
 esac
 
@@ -168,20 +168,27 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 	# Note: NetBSD doesn't particularly care about the vendor
 	# portion of the name.  We always set it to "unknown".
 	sysctl="sysctl -n hw.machine_arch"
-	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
-	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+	    /sbin/$sysctl 2>/dev/null || \
+	    /usr/sbin/$sysctl 2>/dev/null || \
+	    echo unknown)`
 	case "${UNAME_MACHINE_ARCH}" in
 	    armeb) machine=armeb-unknown ;;
 	    arm*) machine=arm-unknown ;;
 	    sh3el) machine=shl-unknown ;;
 	    sh3eb) machine=sh-unknown ;;
 	    sh5el) machine=sh5le-unknown ;;
+	    earmv*)
+		arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+		endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'`
+		machine=${arch}${endian}-unknown
+		;;
 	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
 	esac
 	# The Operating System including object format, if it has switched
 	# to ELF recently, or will in the future.
 	case "${UNAME_MACHINE_ARCH}" in
-	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+	    arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax)
 		eval $set_cc_for_build
 		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
 			| grep -q __ELF__
@@ -197,6 +204,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 		os=netbsd
 		;;
 	esac
+	# Determine ABI tags.
+	case "${UNAME_MACHINE_ARCH}" in
+	    earm*)
+		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+		abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
+		;;
+	esac
 	# The OS release
 	# Debian GNU/NetBSD machines have a different userland, and
 	# thus, need a distinct triplet. However, they do not need
@@ -207,13 +221,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 		release='-gnu'
 		;;
 	    *)
-		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+		release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`
 		;;
 	esac
 	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
 	# contains redundant information, the shorter form:
 	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
-	echo "${machine}-${os}${release}"
+	echo "${machine}-${os}${release}${abi}"
 	exit ;;
     *:Bitrig:*:*)
 	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
@@ -235,6 +249,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
     *:MirBSD:*:*)
 	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
 	exit ;;
+    *:Sortix:*:*)
+	echo ${UNAME_MACHINE}-unknown-sortix
+	exit ;;
     alpha:OSF1:*:*)
 	case $UNAME_RELEASE in
 	*4.0)
@@ -579,8 +596,9 @@ EOF
 	else
 		IBM_ARCH=powerpc
 	fi
-	if [ -x /usr/bin/oslevel ] ; then
-		IBM_REV=`/usr/bin/oslevel`
+	if [ -x /usr/bin/lslpp ] ; then
+		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
 	else
 		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
 	fi
@@ -826,7 +844,7 @@ EOF
     *:MINGW*:*)
 	echo ${UNAME_MACHINE}-pc-mingw32
 	exit ;;
-    i*:MSYS*:*)
+    *:MSYS*:*)
 	echo ${UNAME_MACHINE}-pc-msys
 	exit ;;
     i*:windows32*:*)
@@ -932,6 +950,9 @@ EOF
     crisv32:Linux:*:*)
 	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
 	exit ;;
+    e2k:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
     frv:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
@@ -969,10 +990,10 @@ EOF
 	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
 	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
 	;;
-    or1k:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+    openrisc*:Linux:*:*)
+	echo or1k-unknown-linux-${LIBC}
 	exit ;;
-    or32:Linux:*:*)
+    or32:Linux:*:* | or1k*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     padre:Linux:*:*)
@@ -1020,7 +1041,7 @@ EOF
 	echo ${UNAME_MACHINE}-dec-linux-${LIBC}
 	exit ;;
     x86_64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	echo ${UNAME_MACHINE}-pc-linux-${LIBC}
 	exit ;;
     xtensa*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
@@ -1260,16 +1281,26 @@ EOF
 	if test "$UNAME_PROCESSOR" = unknown ; then
 	    UNAME_PROCESSOR=powerpc
 	fi
-	if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
-	    if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
-		(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
-		grep IS_64BIT_ARCH >/dev/null
-	    then
-		case $UNAME_PROCESSOR in
-		    i386) UNAME_PROCESSOR=x86_64 ;;
-		    powerpc) UNAME_PROCESSOR=powerpc64 ;;
-		esac
+	if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
+	    if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		    (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		    grep IS_64BIT_ARCH >/dev/null
+		then
+		    case $UNAME_PROCESSOR in
+			i386) UNAME_PROCESSOR=x86_64 ;;
+			powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		    esac
+		fi
 	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # Avoid executing cc on OS X 10.9, as it ships with a stub
+	    # that puts up a graphical alert prompting to install
+	    # developer tools.  Any system running Mac OS X 10.7 or
+	    # later (Darwin 11 and later) is required to have a 64-bit
+	    # processor. This is not true of the ARM version of Darwin
+	    # that Apple uses in portable devices.
+	    UNAME_PROCESSOR=x86_64
 	fi
 	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
 	exit ;;
@@ -1361,154 +1392,6 @@ EOF
 	exit ;;
 esac
 
-eval $set_cc_for_build
-cat >$dummy.c <<EOF
-#ifdef _SEQUENT_
-# include <sys/types.h>
-# include <sys/utsname.h>
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
-  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
-     I don't know....  */
-  printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include <sys/param.h>
-  printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
-	"4"
-#else
-	""
-#endif
-	); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
-  printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
-  printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
-  int version;
-  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
-  if (version < 4)
-    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
-  else
-    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
-  exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
-  printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
-  printf ("ns32k-encore-mach\n"); exit (0);
-#else
-  printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
-  printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
-  printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
-  printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
-    struct utsname un;
-
-    uname(&un);
-
-    if (strncmp(un.version, "V2", 2) == 0) {
-	printf ("i386-sequent-ptx2\n"); exit (0);
-    }
-    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
-	printf ("i386-sequent-ptx1\n"); exit (0);
-    }
-    printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-#  include <sys/param.h>
-#  if defined (BSD)
-#   if BSD == 43
-      printf ("vax-dec-bsd4.3\n"); exit (0);
-#   else
-#    if BSD == 199006
-      printf ("vax-dec-bsd4.3reno\n"); exit (0);
-#    else
-      printf ("vax-dec-bsd\n"); exit (0);
-#    endif
-#   endif
-#  else
-    printf ("vax-dec-bsd\n"); exit (0);
-#  endif
-# else
-    printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
-  printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
-  exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
-	{ echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
-    case `getsysinfo -f cpu_type` in
-    c1*)
-	echo c1-convex-bsd
-	exit ;;
-    c2*)
-	if getsysinfo -f scalar_acc
-	then echo c32-convex-bsd
-	else echo c2-convex-bsd
-	fi
-	exit ;;
-    c34*)
-	echo c34-convex-bsd
-	exit ;;
-    c38*)
-	echo c38-convex-bsd
-	exit ;;
-    c4*)
-	echo c4-convex-bsd
-	exit ;;
-    esac
-fi
-
 cat >&2 <<EOF
 $0: unable to guess system type
 
diff --git a/autostuff/config.sub b/autostuff/config.sub
index 9633db7..1acc966 100755
--- a/autostuff/config.sub
+++ b/autostuff/config.sub
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2013 Free Software Foundation, Inc.
+#   Copyright 1992-2015 Free Software Foundation, Inc.
 
-timestamp='2013-08-10'
+timestamp='2015-08-20'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@ timestamp='2013-08-10'
 # of the GNU General Public License, version 3 ("GPLv3").
 
 
-# Please send patches with a ChangeLog entry to config-patches at gnu.org.
+# Please send patches to <config-patches at gnu.org>.
 #
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Supply the specified configuration type as an argument.
@@ -68,7 +68,7 @@ Report bugs and patches to <config-patches at gnu.org>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2015 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -117,7 +117,7 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
   nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
   linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
-  knetbsd*-gnu* | netbsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
   kopensolaris*-gnu* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
@@ -255,16 +255,18 @@ case $basic_machine in
 	| arc | arceb \
 	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
 	| avr | avr32 \
+	| ba \
 	| be32 | be64 \
 	| bfin \
 	| c4x | c8051 | clipper \
 	| d10v | d30v | dlx | dsp16xx \
-	| epiphany \
-	| fido | fr30 | frv \
+	| e2k | epiphany \
+	| fido | fr30 | frv | ft32 \
 	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
 	| hexagon \
 	| i370 | i860 | i960 | ia64 \
 	| ip2k | iq2000 \
+	| k1om \
 	| le32 | le64 \
 	| lm32 \
 	| m32c | m32r | m32rle | m68000 | m68k | m88k \
@@ -282,8 +284,10 @@ case $basic_machine in
 	| mips64vr5900 | mips64vr5900el \
 	| mipsisa32 | mipsisa32el \
 	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa32r6 | mipsisa32r6el \
 	| mipsisa64 | mipsisa64el \
 	| mipsisa64r2 | mipsisa64r2el \
+	| mipsisa64r6 | mipsisa64r6el \
 	| mipsisa64sb1 | mipsisa64sb1el \
 	| mipsisa64sr71k | mipsisa64sr71kel \
 	| mipsr5900 | mipsr5900el \
@@ -295,14 +299,14 @@ case $basic_machine in
 	| nds32 | nds32le | nds32be \
 	| nios | nios2 | nios2eb | nios2el \
 	| ns16k | ns32k \
-	| open8 \
-	| or1k | or32 \
+	| open8 | or1k | or1knd | or32 \
 	| pdp10 | pdp11 | pj | pjl \
 	| powerpc | powerpc64 | powerpc64le | powerpcle \
 	| pyramid \
+	| riscv32 | riscv64 \
 	| rl78 | rx \
 	| score \
-	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
 	| sh64 | sh64le \
 	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
 	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
@@ -310,6 +314,7 @@ case $basic_machine in
 	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
 	| ubicom32 \
 	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+	| visium \
 	| we32k \
 	| x86 | xc16x | xstormy16 | xtensa \
 	| z8k | z80)
@@ -324,7 +329,10 @@ case $basic_machine in
 	c6x)
 		basic_machine=tic6x-unknown
 		;;
-	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+	leon|leon[3-9])
+		basic_machine=sparc-$basic_machine
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
 		basic_machine=$basic_machine-unknown
 		os=-none
 		;;
@@ -369,18 +377,20 @@ case $basic_machine in
 	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
 	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
 	| avr-* | avr32-* \
+	| ba-* \
 	| be32-* | be64-* \
 	| bfin-* | bs2000-* \
 	| c[123]* | c30-* | [cjt]90-* | c4x-* \
 	| c8051-* | clipper-* | craynv-* | cydra-* \
 	| d10v-* | d30v-* | dlx-* \
-	| elxsi-* \
+	| e2k-* | elxsi-* \
 	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
 	| h8300-* | h8500-* \
 	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
 	| hexagon-* \
 	| i*86-* | i860-* | i960-* | ia64-* \
 	| ip2k-* | iq2000-* \
+	| k1om-* \
 	| le32-* | le64-* \
 	| lm32-* \
 	| m32c-* | m32r-* | m32rle-* \
@@ -400,8 +410,10 @@ case $basic_machine in
 	| mips64vr5900-* | mips64vr5900el-* \
 	| mipsisa32-* | mipsisa32el-* \
 	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa32r6-* | mipsisa32r6el-* \
 	| mipsisa64-* | mipsisa64el-* \
 	| mipsisa64r2-* | mipsisa64r2el-* \
+	| mipsisa64r6-* | mipsisa64r6el-* \
 	| mipsisa64sb1-* | mipsisa64sb1el-* \
 	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
 	| mipsr5900-* | mipsr5900el-* \
@@ -413,16 +425,18 @@ case $basic_machine in
 	| nios-* | nios2-* | nios2eb-* | nios2el-* \
 	| none-* | np1-* | ns16k-* | ns32k-* \
 	| open8-* \
+	| or1k*-* \
 	| orion-* \
 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
 	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
 	| pyramid-* \
+	| riscv32-* | riscv64-* \
 	| rl78-* | romp-* | rs6000-* | rx-* \
 	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
 	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
 	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
 	| sparclite-* \
-	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
 	| tahoe-* \
 	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
 	| tile*-* \
@@ -430,6 +444,7 @@ case $basic_machine in
 	| ubicom32-* \
 	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
 	| vax-* \
+	| visium-* \
 	| we32k-* \
 	| x86-* | x86_64-* | xc16x-* | xps100-* \
 	| xstormy16-* | xtensa*-* \
@@ -506,6 +521,9 @@ case $basic_machine in
 		basic_machine=i386-pc
 		os=-aros
 		;;
+        asmjs)
+		basic_machine=asmjs-unknown
+		;;
 	aux)
 		basic_machine=m68k-apple
 		os=-aux
@@ -767,6 +785,9 @@ case $basic_machine in
 		basic_machine=m68k-isi
 		os=-sysv
 		;;
+	leon-*|leon[3-9]-*)
+		basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+		;;
 	m68knommu)
 		basic_machine=m68k-unknown
 		os=-linux
@@ -822,6 +843,10 @@ case $basic_machine in
 		basic_machine=powerpc-unknown
 		os=-morphos
 		;;
+	moxiebox)
+		basic_machine=moxie-unknown
+		os=-moxiebox
+		;;
 	msdos)
 		basic_machine=i386-pc
 		os=-msdos
@@ -1354,7 +1379,7 @@ case $os in
 	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
 	      | -sym* | -kopensolaris* | -plan9* \
 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
-	      | -aos* | -aros* \
+	      | -aos* | -aros* | -cloudabi* | -sortix* \
 	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
 	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
 	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
@@ -1367,14 +1392,14 @@ case $os in
 	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
 	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
 	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
-	      | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
 	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
 	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
 	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
 	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
-	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
 	# Remember, each alternative MUST END IN *, to match a version number.
 		;;
 	-qnx*)
@@ -1592,9 +1617,6 @@ case $basic_machine in
 	mips*-*)
 		os=-elf
 		;;
-	or1k-*)
-		os=-elf
-		;;
 	or32-*)
 		os=-coff
 		;;
diff --git a/autostuff/depcomp b/autostuff/depcomp
index 4ebd5b3..fc98710 100755
--- a/autostuff/depcomp
+++ b/autostuff/depcomp
@@ -3,7 +3,7 @@
 
 scriptversion=2013-05-30.07; # UTC
 
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
 
 # 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
diff --git a/autostuff/install-sh b/autostuff/install-sh
index 377bb86..59990a1 100755
--- a/autostuff/install-sh
+++ b/autostuff/install-sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # install - install a program, script, or datafile
 
-scriptversion=2011-11-20.07; # UTC
+scriptversion=2014-09-12.12; # UTC
 
 # This originates from X11R5 (mit/util/scripts/install.sh), which was
 # later released in X11R6 (xc/config/util/install.sh) with the
@@ -41,19 +41,15 @@ scriptversion=2011-11-20.07; # UTC
 # This script is compatible with the BSD install script, but was written
 # from scratch.
 
+tab='	'
 nl='
 '
-IFS=" ""	$nl"
+IFS=" $tab$nl"
 
-# set DOITPROG to echo to test this script
+# Set DOITPROG to "echo" to test this script.
 
-# Don't use :- since 4.3BSD and earlier shells don't like it.
 doit=${DOITPROG-}
-if test -z "$doit"; then
-  doit_exec=exec
-else
-  doit_exec=$doit
-fi
+doit_exec=${doit:-exec}
 
 # Put in absolute file names if you don't have them in your path;
 # or use environment vars.
@@ -68,17 +64,6 @@ mvprog=${MVPROG-mv}
 rmprog=${RMPROG-rm}
 stripprog=${STRIPPROG-strip}
 
-posix_glob='?'
-initialize_posix_glob='
-  test "$posix_glob" != "?" || {
-    if (set -f) 2>/dev/null; then
-      posix_glob=
-    else
-      posix_glob=:
-    fi
-  }
-'
-
 posix_mkdir=
 
 # Desired mode of installed file.
@@ -97,7 +82,7 @@ dir_arg=
 dst_arg=
 
 copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
 
 usage="\
 Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -137,46 +122,57 @@ while test $# -ne 0; do
     -d) dir_arg=true;;
 
     -g) chgrpcmd="$chgrpprog $2"
-	shift;;
+        shift;;
 
     --help) echo "$usage"; exit $?;;
 
     -m) mode=$2
-	case $mode in
-	  *' '* | *'	'* | *'
-'*	  | *'*'* | *'?'* | *'['*)
-	    echo "$0: invalid mode: $mode" >&2
-	    exit 1;;
-	esac
-	shift;;
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
 
     -o) chowncmd="$chownprog $2"
-	shift;;
+        shift;;
 
     -s) stripcmd=$stripprog;;
 
-    -t) dst_arg=$2
-	# Protect names problematic for 'test' and other utilities.
-	case $dst_arg in
-	  -* | [=\(\)!]) dst_arg=./$dst_arg;;
-	esac
-	shift;;
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
 
-    -T) no_target_directory=true;;
+    -T) is_target_a_directory=never;;
 
     --version) echo "$0 $scriptversion"; exit $?;;
 
-    --)	shift
-	break;;
+    --) shift
+        break;;
 
-    -*)	echo "$0: invalid option: $1" >&2
-	exit 1;;
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
 
     *)  break;;
   esac
   shift
 done
 
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
 if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
   # When -d is used, all remaining arguments are directories to create.
   # When -t is used, the destination is already specified.
@@ -208,6 +204,15 @@ if test $# -eq 0; then
 fi
 
 if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
   do_exit='(exit $ret); exit $ret'
   trap "ret=129; $do_exit" 1
   trap "ret=130; $do_exit" 2
@@ -223,16 +228,16 @@ if test -z "$dir_arg"; then
 
     *[0-7])
       if test -z "$stripcmd"; then
-	u_plus_rw=
+        u_plus_rw=
       else
-	u_plus_rw='% 200'
+        u_plus_rw='% 200'
       fi
       cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
     *)
       if test -z "$stripcmd"; then
-	u_plus_rw=
+        u_plus_rw=
       else
-	u_plus_rw=,u+rw
+        u_plus_rw=,u+rw
       fi
       cp_umask=$mode$u_plus_rw;;
   esac
@@ -269,41 +274,15 @@ do
     # If destination is a directory, append the input filename; won't work
     # if double slashes aren't ignored.
     if test -d "$dst"; then
-      if test -n "$no_target_directory"; then
-	echo "$0: $dst_arg: Is a directory" >&2
-	exit 1
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
       fi
       dstdir=$dst
       dst=$dstdir/`basename "$src"`
       dstdir_status=0
     else
-      # Prefer dirname, but fall back on a substitute if dirname fails.
-      dstdir=`
-	(dirname "$dst") 2>/dev/null ||
-	expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-	     X"$dst" : 'X\(//\)[^/]' \| \
-	     X"$dst" : 'X\(//\)$' \| \
-	     X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
-	echo X"$dst" |
-	    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\/\)[^/].*/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\/\)$/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\).*/{
-		   s//\1/
-		   q
-		 }
-		 s/.*/./; q'
-      `
-
+      dstdir=`dirname "$dst"`
       test -d "$dstdir"
       dstdir_status=$?
     fi
@@ -314,74 +293,81 @@ do
   if test $dstdir_status != 0; then
     case $posix_mkdir in
       '')
-	# Create intermediate dirs using mode 755 as modified by the umask.
-	# This is like FreeBSD 'install' as of 1997-10-28.
-	umask=`umask`
-	case $stripcmd.$umask in
-	  # Optimize common cases.
-	  *[2367][2367]) mkdir_umask=$umask;;
-	  .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
-	  *[0-7])
-	    mkdir_umask=`expr $umask + 22 \
-	      - $umask % 100 % 40 + $umask % 20 \
-	      - $umask % 10 % 4 + $umask % 2
-	    `;;
-	  *) mkdir_umask=$umask,go-w;;
-	esac
-
-	# With -d, create the new directory with the user-specified mode.
-	# Otherwise, rely on $mkdir_umask.
-	if test -n "$dir_arg"; then
-	  mkdir_mode=-m$mode
-	else
-	  mkdir_mode=
-	fi
-
-	posix_mkdir=false
-	case $umask in
-	  *[123567][0-7][0-7])
-	    # POSIX mkdir -p sets u+wx bits regardless of umask, which
-	    # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
-	    ;;
-	  *)
-	    tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
-	    trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
-	    if (umask $mkdir_umask &&
-		exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
-	    then
-	      if test -z "$dir_arg" || {
-		   # Check for POSIX incompatibilities with -m.
-		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
-		   # other-writable bit of parent directory when it shouldn't.
-		   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
-		   ls_ld_tmpdir=`ls -ld "$tmpdir"`
-		   case $ls_ld_tmpdir in
-		     d????-?r-*) different_mode=700;;
-		     d????-?--*) different_mode=755;;
-		     *) false;;
-		   esac &&
-		   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
-		     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
-		     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
-		   }
-		 }
-	      then posix_mkdir=:
-	      fi
-	      rmdir "$tmpdir/d" "$tmpdir"
-	    else
-	      # Remove any dirs left behind by ancient mkdir implementations.
-	      rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
-	    fi
-	    trap '' 0;;
-	esac;;
+        # Create intermediate dirs using mode 755 as modified by the umask.
+        # This is like FreeBSD 'install' as of 1997-10-28.
+        umask=`umask`
+        case $stripcmd.$umask in
+          # Optimize common cases.
+          *[2367][2367]) mkdir_umask=$umask;;
+          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+          *[0-7])
+            mkdir_umask=`expr $umask + 22 \
+              - $umask % 100 % 40 + $umask % 20 \
+              - $umask % 10 % 4 + $umask % 2
+            `;;
+          *) mkdir_umask=$umask,go-w;;
+        esac
+
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+        case $umask in
+          *[123567][0-7][0-7])
+            # POSIX mkdir -p sets u+wx bits regardless of umask, which
+            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+            ;;
+          *)
+            # $RANDOM is not portable (e.g. dash);  use it when possible to
+            # lower collision chance
+            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+            trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+            # As "mkdir -p" follows symlinks and we work in /tmp possibly;  so
+            # create the $tmpdir first (and fail if unsuccessful) to make sure
+            # that nobody tries to guess the $tmpdir name.
+            if (umask $mkdir_umask &&
+                $mkdirprog $mkdir_mode "$tmpdir" &&
+                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+            then
+              if test -z "$dir_arg" || {
+                   # Check for POSIX incompatibilities with -m.
+                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                   # other-writable bit of parent directory when it shouldn't.
+                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                   test_tmpdir="$tmpdir/a"
+                   ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+                   case $ls_ld_tmpdir in
+                     d????-?r-*) different_mode=700;;
+                     d????-?--*) different_mode=755;;
+                     *) false;;
+                   esac &&
+                   $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+                     ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                   }
+                 }
+              then posix_mkdir=:
+              fi
+              rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+            else
+              # Remove any dirs left behind by ancient mkdir implementations.
+              rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+            fi
+            trap '' 0;;
+        esac;;
     esac
 
     if
       $posix_mkdir && (
-	umask $mkdir_umask &&
-	$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
       )
     then :
     else
@@ -391,53 +377,51 @@ do
       # directory the slow way, step by step, checking for races as we go.
 
       case $dstdir in
-	/*) prefix='/';;
-	[-=\(\)!]*) prefix='./';;
-	*)  prefix='';;
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
       esac
 
-      eval "$initialize_posix_glob"
-
       oIFS=$IFS
       IFS=/
-      $posix_glob set -f
+      set -f
       set fnord $dstdir
       shift
-      $posix_glob set +f
+      set +f
       IFS=$oIFS
 
       prefixes=
 
       for d
       do
-	test X"$d" = X && continue
-
-	prefix=$prefix$d
-	if test -d "$prefix"; then
-	  prefixes=
-	else
-	  if $posix_mkdir; then
-	    (umask=$mkdir_umask &&
-	     $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
-	    # Don't fail if two instances are running concurrently.
-	    test -d "$prefix" || exit 1
-	  else
-	    case $prefix in
-	      *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
-	      *) qprefix=$prefix;;
-	    esac
-	    prefixes="$prefixes '$qprefix'"
-	  fi
-	fi
-	prefix=$prefix/
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask=$mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
       done
 
       if test -n "$prefixes"; then
-	# Don't fail if two instances are running concurrently.
-	(umask $mkdir_umask &&
-	 eval "\$doit_exec \$mkdirprog $prefixes") ||
-	  test -d "$dstdir" || exit 1
-	obsolete_mkdir_used=true
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
       fi
     fi
   fi
@@ -472,15 +456,12 @@ do
 
     # If -C, don't bother to copy if it wouldn't change the file.
     if $copy_on_change &&
-       old=`LC_ALL=C ls -dlL "$dst"	2>/dev/null` &&
-       new=`LC_ALL=C ls -dlL "$dsttmp"	2>/dev/null` &&
-
-       eval "$initialize_posix_glob" &&
-       $posix_glob set -f &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
        set X $old && old=:$2:$4:$5:$6 &&
        set X $new && new=:$2:$4:$5:$6 &&
-       $posix_glob set +f &&
-
+       set +f &&
        test "$old" = "$new" &&
        $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
     then
@@ -493,24 +474,24 @@ do
       # to itself, or perhaps because mv is so ancient that it does not
       # support -f.
       {
-	# Now remove or move aside any old file at destination location.
-	# We try this two ways since rm can't unlink itself on some
-	# systems and the destination file might be busy for other
-	# reasons.  In this case, the final cleanup might fail but the new
-	# file should still install successfully.
-	{
-	  test ! -f "$dst" ||
-	  $doit $rmcmd -f "$dst" 2>/dev/null ||
-	  { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
-	    { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
-	  } ||
-	  { echo "$0: cannot unlink or rename $dst" >&2
-	    (exit 1); exit 1
-	  }
-	} &&
-
-	# Now rename the file to the real destination.
-	$doit $mvcmd "$dsttmp" "$dst"
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd -f "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
       }
     fi || exit 1
 
diff --git a/autostuff/ltmain.sh b/autostuff/ltmain.sh
index bb5fa02..bffda54 100644
--- a/autostuff/ltmain.sh
+++ b/autostuff/ltmain.sh
@@ -70,7 +70,7 @@
 #         compiler:		$LTCC
 #         compiler flags:		$LTCFLAGS
 #         linker:		$LD (gnu? $with_gnu_ld)
-#         $progname:	(GNU libtool) 2.4.2 Debian-2.4.2-1.7
+#         $progname:	(GNU libtool) 2.4.2 Debian-2.4.2-1.11
 #         automake:	$automake_version
 #         autoconf:	$autoconf_version
 #
@@ -80,7 +80,7 @@
 
 PROGRAM=libtool
 PACKAGE=libtool
-VERSION="2.4.2 Debian-2.4.2-1.7"
+VERSION="2.4.2 Debian-2.4.2-1.11"
 TIMESTAMP=""
 package_revision=1.3337
 
diff --git a/autostuff/missing b/autostuff/missing
index db98974..f62bbae 100755
--- a/autostuff/missing
+++ b/autostuff/missing
@@ -3,7 +3,7 @@
 
 scriptversion=2013-10-28.13; # UTC
 
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
 # Originally written by Fran,cois Pinard <pinard at iro.umontreal.ca>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
diff --git a/autostuff/test-driver b/autostuff/test-driver
index d306056..8e575b0 100755
--- a/autostuff/test-driver
+++ b/autostuff/test-driver
@@ -3,7 +3,7 @@
 
 scriptversion=2013-07-13.22; # UTC
 
-# Copyright (C) 2011-2013 Free Software Foundation, Inc.
+# Copyright (C) 2011-2014 Free Software Foundation, Inc.
 #
 # 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
@@ -106,11 +106,14 @@ trap "st=143; $do_exit" 15
 # Test script is run here.
 "$@" >$log_file 2>&1
 estatus=$?
+
 if test $enable_hard_errors = no && test $estatus -eq 99; then
-  estatus=1
+  tweaked_estatus=1
+else
+  tweaked_estatus=$estatus
 fi
 
-case $estatus:$expect_failure in
+case $tweaked_estatus:$expect_failure in
   0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
   0:*)   col=$grn res=PASS  recheck=no  gcopy=no;;
   77:*)  col=$blu res=SKIP  recheck=no  gcopy=yes;;
@@ -119,6 +122,12 @@ case $estatus:$expect_failure in
   *:*)   col=$red res=FAIL  recheck=yes gcopy=yes;;
 esac
 
+# Report the test outcome and exit status in the logs, so that one can
+# know whether the test passed or failed simply by looking at the '.log'
+# file, without the need of also peaking into the corresponding '.trs'
+# file (automake bug#11814).
+echo "$res $test_name (exit status: $estatus)" >>$log_file
+
 # Report outcome to console.
 echo "${col}${res}${std}: $test_name"
 
diff --git a/bindings/cxx/ConfigKey_methods.cpp b/bindings/cxx/ConfigKey_methods.cpp
new file mode 100644
index 0000000..54af62e
--- /dev/null
+++ b/bindings/cxx/ConfigKey_methods.cpp
@@ -0,0 +1,118 @@
+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);
+}
+
+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);
+}
+
+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(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);
+}
+
+#include <config.h>
+
+#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(string value) const
+{
+	GVariant *variant;
+	uint64_t p, q;
+
+	switch (data_type()->id())
+	{
+		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);
+}
+
diff --git a/bindings/cxx/ConfigKey_methods.hpp b/bindings/cxx/ConfigKey_methods.hpp
new file mode 100644
index 0000000..f759cc4
--- /dev/null
+++ b/bindings/cxx/ConfigKey_methods.hpp
@@ -0,0 +1,10 @@
+    /** Data type used for this configuration key. */
+    const DataType *data_type() const;
+    /** String identifier for this configuration key, suitable for CLI use. */
+    string identifier() const;
+    /** Description of this configuration key. */
+    string description() const;
+    /** Get configuration key by string identifier. */
+    static const ConfigKey *get_by_identifier(string identifier);
+    /** Parse a string argument into the appropriate type for this key. */
+    Glib::VariantBase parse_string(string value) const;
diff --git a/bindings/cxx/ConfigKey_methods.i b/bindings/cxx/ConfigKey_methods.i
new file mode 100644
index 0000000..eaa2ab6
--- /dev/null
+++ b/bindings/cxx/ConfigKey_methods.i
@@ -0,0 +1 @@
+%attributestring(sigrok::ConfigKey, std::string, identifier, identifier);
diff --git a/Doxyfile b/bindings/cxx/Doxyfile
similarity index 98%
copy from Doxyfile
copy to bindings/cxx/Doxyfile
index a42748b..7fb0854 100644
--- a/Doxyfile
+++ b/bindings/cxx/Doxyfile
@@ -32,33 +32,33 @@ DOXYFILE_ENCODING      = UTF-8
 # title of most generated pages and in a few other places.
 # The default value is: My Project.
 
-PROJECT_NAME           = "libsigrok"
+PROJECT_NAME           = "libsigrokcxx"
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = "0.3.0"
+PROJECT_NUMBER         = "unreleased development snapshot"
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
 # quick idea about the purpose of the project. Keep the description short.
 
-PROJECT_BRIEF          = "sigrok hardware access and backend library"
+PROJECT_BRIEF          = "C++ bindings for libsigrok"
 
 # With the PROJECT_LOGO tag one can specify an logo or icon that is included in
 # the documentation. The maximum height of the logo should not exceed 55 pixels
 # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
 # to the output directory.
 
-PROJECT_LOGO           = contrib/sigrok-logo-notext.png
+PROJECT_LOGO           = ../../contrib/sigrok-logo-notext.png
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
 # into which the generated documentation will be written. If a relative path is
 # entered, it will be relative to the location where doxygen was started. If
 # left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = doxy
+OUTPUT_DIRECTORY       = $(BUILDDIR)doxy
 
 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
 # directories (in 2 levels) under the output directory of each output format and
@@ -153,7 +153,7 @@ STRIP_FROM_PATH        =
 # specify the list of include paths that are normally passed to the compiler
 # using the -I flag.
 
-STRIP_FROM_INC_PATH    =
+STRIP_FROM_INC_PATH    = include/
 
 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
 # less readable) file names. This can be useful is your file systems doesn't
@@ -234,7 +234,7 @@ TCL_SUBST              =
 # members will be omitted, etc.
 # The default value is: NO.
 
-OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_FOR_C  = NO
 
 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
 # Python sources only. Doxygen will then generate output that is more tailored
@@ -298,7 +298,7 @@ AUTOLINK_SUPPORT       = YES
 # diagrams that involve STL classes more complete and accurate.
 # The default value is: NO.
 
-BUILTIN_STL_SUPPORT    = NO
+BUILTIN_STL_SUPPORT    = YES
 
 # If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
@@ -398,7 +398,7 @@ LOOKUP_CACHE_SIZE      = 0
 # normally produced when WARNINGS is set to YES.
 # The default value is: NO.
 
-EXTRACT_ALL            = YES
+EXTRACT_ALL            = NO
 
 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
 # be included in the documentation.
@@ -457,14 +457,14 @@ HIDE_UNDOC_MEMBERS     = NO
 # no effect if EXTRACT_ALL is enabled.
 # The default value is: NO.
 
-HIDE_UNDOC_CLASSES     = NO
+HIDE_UNDOC_CLASSES     = YES
 
 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
 # (class|struct|union) declarations. If set to NO these declarations will be
 # included in the documentation.
 # The default value is: NO.
 
-HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_FRIEND_COMPOUNDS  = YES
 
 # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
 # documentation blocks found inside the body of a function. If set to NO these
@@ -526,7 +526,7 @@ INLINE_INFO            = YES
 # name. If set to NO the members will appear in declaration order.
 # The default value is: YES.
 
-SORT_MEMBER_DOCS       = YES
+SORT_MEMBER_DOCS       = NO
 
 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
 # descriptions of file, namespace and class members alphabetically by member
@@ -631,7 +631,7 @@ SHOW_USED_FILES        = YES
 # (if specified).
 # The default value is: YES.
 
-SHOW_FILES             = YES
+SHOW_FILES             = NO
 
 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
 # page. This will remove the Namespaces entry from the Quick Index and from the
@@ -683,7 +683,7 @@ CITE_BIB_FILES         =
 # messages are off.
 # The default value is: NO.
 
-QUIET                  = NO
+QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
 # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
@@ -692,14 +692,14 @@ QUIET                  = NO
 # Tip: Turn warnings on while writing the documentation.
 # The default value is: YES.
 
-WARNINGS               = YES
+WARNINGS               = NO
 
 # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
 # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
 # will automatically be disabled.
 # The default value is: YES.
 
-WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_UNDOCUMENTED   = NO
 
 # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
 # potential errors in the documentation, such as not documenting some parameters
@@ -707,7 +707,7 @@ WARN_IF_UNDOCUMENTED   = YES
 # markup commands wrongly.
 # The default value is: YES.
 
-WARN_IF_DOC_ERROR      = YES
+WARN_IF_DOC_ERROR      = NO
 
 # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
 # are documented, but have no documentation for their parameters or return
@@ -715,7 +715,7 @@ WARN_IF_DOC_ERROR      = YES
 # documentation, but not about the absence of documentation.
 # The default value is: NO.
 
-WARN_NO_PARAMDOC       = YES
+WARN_NO_PARAMDOC       = NO
 
 # The WARN_FORMAT tag determines the format of the warning messages that doxygen
 # can produce. The string should contain the $file, $line, and $text tags, which
@@ -743,7 +743,8 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = .
+INPUT                  = include/libsigrokcxx/libsigrokcxx.hpp \
+                         $(BUILDDIR)include/libsigrokcxx/enums.hpp
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -763,7 +764,7 @@ INPUT_ENCODING         = UTF-8
 # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
 # *.qsf, *.as and *.js.
 
-FILE_PATTERNS          =
+FILE_PATTERNS          = *.hpp
 
 # The RECURSIVE tag can be used to specify whether or not subdirectories should
 # be searched for input files as well.
@@ -778,7 +779,7 @@ RECURSIVE              = YES
 # Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE                = config.h libsigrok-internal.h session_driver.c std.c
+EXCLUDE                =
 
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
@@ -794,23 +795,7 @@ EXCLUDE_SYMLINKS       = NO
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories for example use the pattern */test/*
 
-#
-# Ignore the following files and directories (see also EXCLUDE above):
-#  - config.h: Non-public stuff, the file doesn't get installed.
-#  - libsigrok-internal.h: Non-public stuff, the file doesn't get installed.
-#  - session_driver.c: Special driver for "virtual" devices, non-public.
-#  - std.c: Non-public helpers, no public API stuff in there.
-#  - hardware/*: Only driver-specific stuff, no public API stuff in there.
-#  - input/*: Only input.c contains public API, everything else doesn't.
-#  - output/*: Only output.c contains public API, everything else doesn't.
-#  - tests/*: Unit tests, no public API stuff in there.
-#  - bindings/*: Language bindings, no public API stuff in there.
-#  - doxy/*: Potentially already generated docs, should not be scanned.
-#
-EXCLUDE_PATTERNS       = */hardware/* */input/* */output/* */tests/*
-EXCLUDE_PATTERNS      += */bindings/*
-EXCLUDE_PATTERNS      += */doxy/*
-INPUT                 += input/input.c output/output.c
+EXCLUDE_PATTERNS       =
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
@@ -821,7 +806,7 @@ INPUT                 += input/input.c output/output.c
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories use the pattern */test/*
 
-EXCLUDE_SYMBOLS        =
+EXCLUDE_SYMBOLS        = StructureWrapper enable_shared_from_this Deleter
 
 # The EXAMPLE_PATH tag can be used to specify one or more files or directories
 # that contain example code fragments that are included (see the \include
@@ -992,7 +977,7 @@ VERBATIM_HEADERS       = YES
 # classes, structs, unions or interfaces.
 # The default value is: YES.
 
-ALPHABETICAL_INDEX     = YES
+ALPHABETICAL_INDEX     = NO
 
 # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
 # which the alphabetical index list will be split.
@@ -1795,7 +1780,7 @@ MAN_LINKS              = NO
 # captures the structure of the code including all documentation.
 # The default value is: NO.
 
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 
 # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -1824,7 +1809,7 @@ XML_DTD                =
 # The default value is: YES.
 # This tag requires that the tag GENERATE_XML is set to YES.
 
-XML_PROGRAMLISTING     = YES
+XML_PROGRAMLISTING     = NO
 
 #---------------------------------------------------------------------------
 # Configuration options related to the DOCBOOK output
@@ -1933,7 +1918,7 @@ SEARCH_INCLUDES        = YES
 # preprocessor.
 # This tag requires that the tag SEARCH_INCLUDES is set to YES.
 
-INCLUDE_PATH           =
+INCLUDE_PATH           = $(BUILDDIR)include/libsigrok
 
 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
 # patterns (like *.h and *.hpp) to filter out the header-files in the
@@ -1954,6 +1939,9 @@ INCLUDE_FILE_PATTERNS  =
 # This gets rid of the SR_API function prefix in the Doxygen output.
 PREDEFINED             = SR_API=
 
+# This treats all protected members as private.
+PREDEFINED             = protected=private
+
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
 # macro definition that is found in the sources will be used. Use the PREDEFINED
@@ -2057,7 +2045,7 @@ DIA_PATH               =
 # and usage relations if the target is undocumented or is not a class.
 # The default value is: YES.
 
-HIDE_UNDOC_RELATIONS   = NO
+HIDE_UNDOC_RELATIONS   = YES
 
 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
 # available from the path. This tool is part of Graphviz (see:
@@ -2117,7 +2105,7 @@ CLASS_GRAPH            = YES
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-COLLABORATION_GRAPH    = YES
+COLLABORATION_GRAPH    = NO
 
 # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
 # groups, showing the direct groups dependencies.
@@ -2153,7 +2141,7 @@ UML_LIMIT_NUM_FIELDS   = 10
 # The default value is: NO.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-TEMPLATE_RELATIONS     = NO
+TEMPLATE_RELATIONS     = YES
 
 # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
 # YES then doxygen will generate a graph for each documented file showing the
diff --git a/bindings/cxx/QuantityFlag_methods.cpp b/bindings/cxx/QuantityFlag_methods.cpp
new file mode 100644
index 0000000..e822b80
--- /dev/null
+++ b/bindings/cxx/QuantityFlag_methods.cpp
@@ -0,0 +1,21 @@
+vector<const QuantityFlag *>
+    QuantityFlag::flags_from_mask(unsigned int mask)
+{
+    auto result = 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(vector<const QuantityFlag *> flags)
+{
+    unsigned int result = 0;
+    for (auto flag : flags)
+        result |= flag->id();
+    return result;
+}
diff --git a/bindings/cxx/QuantityFlag_methods.hpp b/bindings/cxx/QuantityFlag_methods.hpp
new file mode 100644
index 0000000..d07c37a
--- /dev/null
+++ b/bindings/cxx/QuantityFlag_methods.hpp
@@ -0,0 +1,7 @@
+	/** Get flags corresponding to a bitmask. */
+	static vector<const QuantityFlag *>
+		flags_from_mask(unsigned int mask);
+
+	/** Get bitmask corresponding to a set of flags. */
+	static unsigned int mask_from_flags(
+		vector<const QuantityFlag *> flags);
diff --git a/bindings/cxx/classes.cpp b/bindings/cxx/classes.cpp
new file mode 100644
index 0000000..4b4de2d
--- /dev/null
+++ b/bindings/cxx/classes.cpp
@@ -0,0 +1,1477 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013-2014 Martin Ling <martin-sigrok at 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/>.
+ */
+
+/* Needed for isascii(), as used in the GNU libstdc++ headers */
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 600
+#endif
+
+#include <config.h>
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include <sstream>
+#include <cmath>
+
+namespace sigrok
+{
+
+/** Helper function to translate C errors to C++ exceptions. */
+static void check(int result)
+{
+	if (result != SR_OK)
+		throw Error(result);
+}
+
+/** Helper function to obtain valid strings from possibly null input. */
+static inline const char *valid_string(const char *input)
+{
+	return (input) ? input : "";
+}
+
+/** Helper function to convert between map<string, VariantBase> and GHashTable */
+static GHashTable *map_to_hash_variant(const map<string, Glib::VariantBase> &input)
+{
+	auto *const output = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+			reinterpret_cast<GDestroyNotify>(&g_variant_unref));
+	for (const auto &entry : input)
+		g_hash_table_insert(output,
+			g_strdup(entry.first.c_str()),
+			entry.second.gobj_copy());
+	return output;
+}
+
+Error::Error(int result) : result(result)
+{
+}
+
+const char *Error::what() const noexcept
+{
+	return sr_strerror(result);
+}
+
+Error::~Error() noexcept
+{
+}
+
+ResourceReader::~ResourceReader()
+{
+}
+
+SR_PRIV int ResourceReader::open_callback(struct sr_resource *res,
+		const char *name, void *cb_data) noexcept
+{
+	try {
+		auto *const reader = static_cast<ResourceReader*>(cb_data);
+		reader->open(res, name);
+	} catch (const Error &err) {
+		return err.result;
+	} catch (...) {
+		return SR_ERR;
+	}
+	return SR_OK;
+}
+
+SR_PRIV int ResourceReader::close_callback(struct sr_resource *res,
+		void *cb_data) noexcept
+{
+	try {
+		auto *const reader = static_cast<ResourceReader*>(cb_data);
+		reader->close(res);
+	} catch (const Error &err) {
+		return err.result;
+	} catch (...) {
+		return SR_ERR;
+	}
+	return SR_OK;
+}
+
+SR_PRIV gssize ResourceReader::read_callback(const struct sr_resource *res,
+		void *buf, size_t count, void *cb_data) noexcept
+{
+	try {
+		auto *const reader = static_cast<ResourceReader*>(cb_data);
+		return reader->read(res, buf, count);
+	} catch (const Error &err) {
+		return err.result;
+	} catch (...) {
+		return SR_ERR;
+	}
+}
+
+shared_ptr<Context> Context::create()
+{
+	return shared_ptr<Context>{new Context{}, default_delete<Context>{}};
+}
+
+Context::Context() :
+	_structure(nullptr),
+	_session(nullptr)
+{
+	check(sr_init(&_structure));
+
+	if (struct sr_dev_driver **driver_list = sr_driver_list(_structure))
+		for (int i = 0; driver_list[i]; i++) {
+			unique_ptr<Driver> driver {new Driver{driver_list[i]}};
+			_drivers.insert(make_pair(driver->name(), move(driver)));
+		}
+
+	if (const struct sr_input_module **input_list = sr_input_list())
+		for (int i = 0; input_list[i]; i++) {
+			unique_ptr<InputFormat> input {new InputFormat{input_list[i]}};
+			_input_formats.insert(make_pair(input->name(), move(input)));
+		}
+
+	if (const struct sr_output_module **output_list = sr_output_list())
+		for (int i = 0; output_list[i]; i++) {
+			unique_ptr<OutputFormat> output {new OutputFormat{output_list[i]}};
+			_output_formats.insert(make_pair(output->name(), move(output)));
+		}
+}
+
+string Context::package_version()
+{
+	return sr_package_version_string_get();
+}
+
+string Context::lib_version()
+{
+	return sr_lib_version_string_get();
+}
+
+map<string, shared_ptr<Driver>> Context::drivers()
+{
+	map<string, shared_ptr<Driver>> result;
+	for (const auto &entry: _drivers)
+	{
+		const auto &name = entry.first;
+		const auto &driver = entry.second;
+		result.insert({name, driver->share_owned_by(shared_from_this())});
+	}
+	return result;
+}
+
+map<string, shared_ptr<InputFormat>> Context::input_formats()
+{
+	map<string, shared_ptr<InputFormat>> result;
+	for (const auto &entry: _input_formats)
+	{
+		const auto &name = entry.first;
+		const auto &input_format = entry.second;
+		result.insert({name, input_format->share_owned_by(shared_from_this())});
+	}
+	return result;
+}
+
+map<string, shared_ptr<OutputFormat>> Context::output_formats()
+{
+	map<string, shared_ptr<OutputFormat>> result;
+	for (const auto &entry: _output_formats)
+	{
+		const auto &name = entry.first;
+		const auto &output_format = entry.second;
+		result.insert({name, output_format->share_owned_by(shared_from_this())});
+	}
+	return result;
+}
+
+Context::~Context()
+{
+	check(sr_exit(_structure));
+}
+
+const LogLevel *Context::log_level() const
+{
+	return LogLevel::get(sr_log_loglevel_get());
+}
+
+void Context::set_log_level(const LogLevel *level)
+{
+	check(sr_log_loglevel_set(level->id()));
+}
+
+static int call_log_callback(void *cb_data, int loglevel,
+		const char *format, va_list args) noexcept
+{
+	const unique_ptr<char, decltype(&g_free)>
+		message {g_strdup_vprintf(format, args), &g_free};
+
+	auto *const callback = static_cast<LogCallbackFunction *>(cb_data);
+
+	try
+	{
+		(*callback)(LogLevel::get(loglevel), message.get());
+	}
+	catch (Error e)
+	{
+		return e.result;
+	}
+
+	return SR_OK;
+}
+
+void Context::set_log_callback(LogCallbackFunction callback)
+{
+	_log_callback = move(callback);
+	check(sr_log_callback_set(call_log_callback, &_log_callback));
+}
+
+void Context::set_log_callback_default()
+{
+	check(sr_log_callback_set_default());
+	_log_callback = nullptr;
+}
+
+void Context::set_resource_reader(ResourceReader *reader)
+{
+	if (reader) {
+		check(sr_resource_set_hooks(_structure,
+				&ResourceReader::open_callback,
+				&ResourceReader::close_callback,
+				&ResourceReader::read_callback, reader));
+	} else {
+		check(sr_resource_set_hooks(_structure,
+				nullptr, nullptr, nullptr, nullptr));
+	}
+}
+
+shared_ptr<Session> Context::create_session()
+{
+	return shared_ptr<Session>{new Session{shared_from_this()},
+		default_delete<Session>{}};
+}
+
+shared_ptr<UserDevice> Context::create_user_device(
+		string vendor, string model, string version)
+{
+	return shared_ptr<UserDevice>{
+		new UserDevice{move(vendor), move(model), move(version)},
+		default_delete<UserDevice>{}};
+}
+
+shared_ptr<Packet> Context::create_header_packet(Glib::TimeVal start_time)
+{
+	auto header = g_new(struct sr_datafeed_header, 1);
+	header->feed_version = 1;
+	header->starttime.tv_sec = start_time.tv_sec;
+	header->starttime.tv_usec = start_time.tv_usec;
+	auto packet = g_new(struct sr_datafeed_packet, 1);
+	packet->type = SR_DF_HEADER;
+	packet->payload = header;
+	return shared_ptr<Packet>{new Packet{nullptr, packet},
+		default_delete<Packet>{}};
+}
+
+shared_ptr<Packet> Context::create_meta_packet(
+	map<const ConfigKey *, Glib::VariantBase> config)
+{
+	auto meta = g_new0(struct sr_datafeed_meta, 1);
+	for (const auto &input : config)
+	{
+		const auto &key = input.first;
+		const auto &value = input.second;
+		auto *const output = g_new(struct sr_config, 1);
+		output->key = key->id();
+		output->data = value.gobj_copy();
+		meta->config = g_slist_append(meta->config, output);
+	}
+	auto packet = g_new(struct sr_datafeed_packet, 1);
+	packet->type = SR_DF_META;
+	packet->payload = meta;
+	return shared_ptr<Packet>{new Packet{nullptr, packet},
+		default_delete<Packet>{}};
+}
+
+shared_ptr<Packet> Context::create_logic_packet(
+	void *data_pointer, size_t data_length, unsigned int unit_size)
+{
+	auto logic = g_new(struct sr_datafeed_logic, 1);
+	logic->length = data_length;
+	logic->unitsize = unit_size;
+	logic->data = data_pointer;
+	auto packet = g_new(struct sr_datafeed_packet, 1);
+	packet->type = SR_DF_LOGIC;
+	packet->payload = logic;
+	return shared_ptr<Packet>{new Packet{nullptr, packet}, default_delete<Packet>{}};
+}
+
+shared_ptr<Packet> Context::create_analog_packet(
+	vector<shared_ptr<Channel> > channels,
+	float *data_pointer, unsigned int num_samples, const Quantity *mq,
+	const Unit *unit, vector<const QuantityFlag *> mqflags)
+{
+	auto analog = g_new0(struct sr_datafeed_analog, 1);
+	auto meaning = g_new0(struct sr_analog_meaning, 1);
+
+	analog->meaning = meaning;
+
+	for (const auto &channel : channels)
+		meaning->channels = g_slist_append(meaning->channels, channel->_structure);
+	analog->num_samples = num_samples;
+	meaning->mq = static_cast<sr_mq>(mq->id());
+	meaning->unit = static_cast<sr_unit>(unit->id());
+	meaning->mqflags = static_cast<sr_mqflag>(QuantityFlag::mask_from_flags(move(mqflags)));
+	analog->data = data_pointer;
+	auto packet = g_new(struct sr_datafeed_packet, 1);
+	packet->type = SR_DF_ANALOG;
+	packet->payload = analog;
+	return shared_ptr<Packet>{new Packet{nullptr, packet}, default_delete<Packet>{}};
+}
+
+shared_ptr<Session> Context::load_session(string filename)
+{
+	return shared_ptr<Session>{
+		new Session{shared_from_this(), move(filename)},
+		default_delete<Session>{}};
+}
+
+shared_ptr<Trigger> Context::create_trigger(string name)
+{
+	return shared_ptr<Trigger>{
+		new Trigger{shared_from_this(), move(name)},
+		default_delete<Trigger>{}};
+}
+
+shared_ptr<Input> Context::open_file(string filename)
+{
+	const struct sr_input *input;
+
+	check(sr_input_scan_file(filename.c_str(), &input));
+	return shared_ptr<Input>{
+		new Input{shared_from_this(), input},
+		default_delete<Input>{}};
+}
+
+shared_ptr<Input> Context::open_stream(string header)
+{
+	const struct sr_input *input;
+
+	auto gstr = g_string_new(header.c_str());
+	auto ret = sr_input_scan_buffer(gstr, &input);
+	g_string_free(gstr, true);
+	check(ret);
+	return shared_ptr<Input>{
+		new Input{shared_from_this(), input},
+		default_delete<Input>{}};
+}
+
+map<string, string> Context::serials(shared_ptr<Driver> driver) const
+{
+	GSList *serial_list = sr_serial_list(driver ? driver->_structure : nullptr);
+	map<string, string> serials;
+
+	for (GSList *serial = serial_list; serial; serial = serial->next) {
+		auto *const port = static_cast<sr_serial_port *>(serial->data);
+		serials[string(port->name)] = string(port->description);
+	}
+
+	g_slist_free_full(serial_list,
+		reinterpret_cast<GDestroyNotify>(&sr_serial_free));
+	return serials;
+}
+
+Driver::Driver(struct sr_dev_driver *structure) :
+	Configurable(structure, nullptr, nullptr),
+	_structure(structure),
+	_initialized(false)
+{
+}
+
+Driver::~Driver()
+{
+}
+
+string Driver::name() const
+{
+	return valid_string(_structure->name);
+}
+
+string Driver::long_name() const
+{
+	return valid_string(_structure->longname);
+}
+
+set<const ConfigKey *> Driver::scan_options() const
+{
+	GArray *opts = sr_driver_scan_options_list(_structure);
+	set<const ConfigKey *> result;
+	for (guint i = 0; i < opts->len; i++)
+		result.insert(ConfigKey::get(g_array_index(opts, uint32_t, i)));
+	g_array_free(opts, TRUE);
+	return result;
+}
+
+vector<shared_ptr<HardwareDevice>> Driver::scan(
+	map<const ConfigKey *, Glib::VariantBase> options)
+{
+	/* Initialise the driver if not yet done. */
+	if (!_initialized)
+	{
+		check(sr_driver_init(_parent->_structure, _structure));
+		_initialized = true;
+	}
+
+	/* Translate scan options to GSList of struct sr_config pointers. */
+	GSList *option_list = nullptr;
+	for (const auto &entry : options)
+	{
+		const auto &key = entry.first;
+		const auto &value = entry.second;
+		auto *const config = g_new(struct sr_config, 1);
+		config->key = key->id();
+		config->data = const_cast<GVariant*>(value.gobj());
+		option_list = g_slist_append(option_list, config);
+	}
+
+	/* Run scan. */
+	GSList *device_list = sr_driver_scan(_structure, option_list);
+
+	/* Free option list. */
+	g_slist_free_full(option_list, g_free);
+
+
+	/* Create device objects. */
+	vector<shared_ptr<HardwareDevice>> result;
+	for (GSList *device = device_list; device; device = device->next)
+	{
+		auto *const sdi = static_cast<struct sr_dev_inst *>(device->data);
+		shared_ptr<HardwareDevice> hwdev {
+			new HardwareDevice{shared_from_this(), sdi},
+			default_delete<HardwareDevice>{}};
+		result.push_back(move(hwdev));
+	}
+
+	/* Free GSList returned from scan. */
+	g_slist_free(device_list);
+
+	return result;
+}
+
+Configurable::Configurable(
+		struct sr_dev_driver *driver,
+		struct sr_dev_inst *sdi,
+		struct sr_channel_group *cg) :
+	config_driver(driver),
+	config_sdi(sdi),
+	config_channel_group(cg)
+{
+}
+
+Configurable::~Configurable()
+{
+}
+
+set<const ConfigKey *> Configurable::config_keys() const
+{
+	GArray *opts;
+	set<const ConfigKey *> result;
+
+	opts = sr_dev_options(config_driver, config_sdi, config_channel_group);
+
+	for (guint i = 0; i < opts->len; i++)
+		result.insert(ConfigKey::get(g_array_index(opts, uint32_t, i)));
+
+	g_array_free(opts, TRUE);
+
+	return result;
+}
+
+Glib::VariantBase Configurable::config_get(const ConfigKey *key) const
+{
+	GVariant *data;
+	check(sr_config_get(
+		config_driver, config_sdi, config_channel_group,
+		key->id(), &data));
+	return Glib::VariantBase(data);
+}
+
+void Configurable::config_set(const ConfigKey *key, const Glib::VariantBase &value)
+{
+	check(sr_config_set(
+		config_sdi, config_channel_group,
+		key->id(), const_cast<GVariant*>(value.gobj())));
+}
+
+set<const Capability *> Configurable::config_capabilities(const ConfigKey *key) const
+{
+	int caps = sr_dev_config_capabilities_list(config_sdi,
+				config_channel_group, key->id());
+
+	set<const Capability *> result;
+
+	for (auto cap: Capability::values())
+		if (caps & cap->id())
+			result.insert(cap);
+
+	return result;
+}
+
+bool Configurable::config_check(const ConfigKey *key,
+	const Capability *capability) const
+{
+	int caps = sr_dev_config_capabilities_list(config_sdi,
+				config_channel_group, key->id());
+
+	return (caps & capability->id());
+}
+
+Glib::VariantContainerBase Configurable::config_list(const ConfigKey *key) const
+{
+	GVariant *data;
+	check(sr_config_list(
+		config_driver, config_sdi, config_channel_group,
+		key->id(), &data));
+	return Glib::VariantContainerBase(data);
+}
+
+Device::Device(struct sr_dev_inst *structure) :
+	Configurable(sr_dev_inst_driver_get(structure), structure, nullptr),
+	_structure(structure)
+{
+	for (GSList *entry = sr_dev_inst_channels_get(structure); entry; entry = entry->next)
+	{
+		auto *const ch = static_cast<struct sr_channel *>(entry->data);
+		unique_ptr<Channel> channel {new Channel{ch}};
+		_channels.insert(make_pair(ch, move(channel)));
+	}
+
+	for (GSList *entry = sr_dev_inst_channel_groups_get(structure); entry; entry = entry->next)
+	{
+		auto *const cg = static_cast<struct sr_channel_group *>(entry->data);
+		unique_ptr<ChannelGroup> group {new ChannelGroup{this, cg}};
+		_channel_groups.insert(make_pair(group->name(), move(group)));
+	}
+}
+
+Device::~Device()
+{}
+
+string Device::vendor() const
+{
+	return valid_string(sr_dev_inst_vendor_get(_structure));
+}
+
+string Device::model() const
+{
+	return valid_string(sr_dev_inst_model_get(_structure));
+}
+
+string Device::version() const
+{
+	return valid_string(sr_dev_inst_version_get(_structure));
+}
+
+string Device::serial_number() const
+{
+	return valid_string(sr_dev_inst_sernum_get(_structure));
+}
+
+string Device::connection_id() const
+{
+	return valid_string(sr_dev_inst_connid_get(_structure));
+}
+
+vector<shared_ptr<Channel>> Device::channels()
+{
+	vector<shared_ptr<Channel>> result;
+	for (auto channel = sr_dev_inst_channels_get(_structure); channel; channel = channel->next) {
+		auto *const ch = static_cast<struct sr_channel *>(channel->data);
+		result.push_back(_channels[ch]->share_owned_by(get_shared_from_this()));
+	}
+	return result;
+}
+
+shared_ptr<Channel> Device::get_channel(struct sr_channel *ptr)
+{
+	return _channels[ptr]->share_owned_by(get_shared_from_this());
+}
+
+map<string, shared_ptr<ChannelGroup>>
+Device::channel_groups()
+{
+	map<string, shared_ptr<ChannelGroup>> result;
+	for (const auto &entry: _channel_groups)
+	{
+		const auto &name = entry.first;
+		const auto &channel_group = entry.second;
+		result.insert({name, channel_group->share_owned_by(get_shared_from_this())});
+	}
+	return result;
+}
+
+void Device::open()
+{
+	check(sr_dev_open(_structure));
+}
+
+void Device::close()
+{
+	check(sr_dev_close(_structure));
+}
+
+HardwareDevice::HardwareDevice(shared_ptr<Driver> driver,
+		struct sr_dev_inst *structure) :
+	Device(structure),
+	_driver(move(driver))
+{
+}
+
+HardwareDevice::~HardwareDevice()
+{
+}
+
+shared_ptr<Device> HardwareDevice::get_shared_from_this()
+{
+	return static_pointer_cast<Device>(shared_from_this());
+}
+
+shared_ptr<Driver> HardwareDevice::driver()
+{
+	return _driver;
+}
+
+UserDevice::UserDevice(string vendor, string model, string version) :
+	Device(sr_dev_inst_user_new(
+		vendor.c_str(), model.c_str(), version.c_str()))
+{
+}
+
+UserDevice::~UserDevice()
+{
+}
+
+shared_ptr<Device> UserDevice::get_shared_from_this()
+{
+	return static_pointer_cast<Device>(shared_from_this());
+}
+
+shared_ptr<Channel> UserDevice::add_channel(unsigned int index,
+	const ChannelType *type, string name)
+{
+	check(sr_dev_inst_channel_add(Device::_structure,
+		index, type->id(), name.c_str()));
+	GSList *const last = g_slist_last(sr_dev_inst_channels_get(Device::_structure));
+	auto *const ch = static_cast<struct sr_channel *>(last->data);
+	unique_ptr<Channel> channel {new Channel{ch}};
+	_channels.insert(make_pair(ch, move(channel)));
+	return get_channel(ch);
+}
+
+Channel::Channel(struct sr_channel *structure) :
+	_structure(structure),
+	_type(ChannelType::get(_structure->type))
+{
+}
+
+Channel::~Channel()
+{
+}
+
+string Channel::name() const
+{
+	return valid_string(_structure->name);
+}
+
+void Channel::set_name(string name)
+{
+	check(sr_dev_channel_name_set(_structure, name.c_str()));
+}
+
+const ChannelType *Channel::type() const
+{
+	return ChannelType::get(_structure->type);
+}
+
+bool Channel::enabled() const
+{
+	return _structure->enabled;
+}
+
+void Channel::set_enabled(bool value)
+{
+	check(sr_dev_channel_enable(_structure, value));
+}
+
+unsigned int Channel::index() const
+{
+	return _structure->index;
+}
+
+ChannelGroup::ChannelGroup(const Device *device,
+		struct sr_channel_group *structure) :
+	Configurable(sr_dev_inst_driver_get(device->_structure), device->_structure, structure)
+{
+	for (GSList *entry = config_channel_group->channels; entry; entry = entry->next) {
+		auto *const ch = static_cast<struct sr_channel *>(entry->data);
+		/* Note: This relies on Device::_channels to keep the Channel
+		 * objects around over the lifetime of the ChannelGroup. */
+		_channels.push_back(device->_channels.find(ch)->second.get());
+	}
+}
+
+ChannelGroup::~ChannelGroup()
+{
+}
+
+string ChannelGroup::name() const
+{
+	return valid_string(config_channel_group->name);
+}
+
+vector<shared_ptr<Channel>> ChannelGroup::channels()
+{
+	vector<shared_ptr<Channel>> result;
+	for (const auto &channel : _channels)
+		result.push_back(channel->share_owned_by(_parent));
+	return result;
+}
+
+Trigger::Trigger(shared_ptr<Context> context, string name) : 
+	_structure(sr_trigger_new(name.c_str())),
+	_context(move(context))
+{
+	for (auto *stage = _structure->stages; stage; stage = stage->next) {
+		unique_ptr<TriggerStage> ts {new TriggerStage{
+				static_cast<struct sr_trigger_stage *>(stage->data)}};
+		_stages.push_back(move(ts));
+	}
+}
+
+Trigger::~Trigger()
+{
+	sr_trigger_free(_structure);
+}
+
+string Trigger::name() const
+{
+	return _structure->name;
+}
+
+vector<shared_ptr<TriggerStage>> Trigger::stages()
+{
+	vector<shared_ptr<TriggerStage>> result;
+	for (const auto &stage : _stages)
+		result.push_back(stage->share_owned_by(shared_from_this()));
+	return result;
+}
+
+shared_ptr<TriggerStage> Trigger::add_stage()
+{
+	unique_ptr<TriggerStage> stage {new TriggerStage{sr_trigger_stage_add(_structure)}};
+	_stages.push_back(move(stage));
+	return _stages.back()->share_owned_by(shared_from_this());
+}
+
+TriggerStage::TriggerStage(struct sr_trigger_stage *structure) :
+	_structure(structure)
+{
+}
+
+TriggerStage::~TriggerStage()
+{
+}
+	
+int TriggerStage::number() const
+{
+	return _structure->stage;
+}
+
+vector<shared_ptr<TriggerMatch>> TriggerStage::matches()
+{
+	vector<shared_ptr<TriggerMatch>> result;
+	for (const auto &match : _matches)
+		result.push_back(match->share_owned_by(shared_from_this()));
+	return result;
+}
+
+void TriggerStage::add_match(shared_ptr<Channel> channel,
+	const TriggerMatchType *type, float value)
+{
+	check(sr_trigger_match_add(_structure,
+		channel->_structure, type->id(), value));
+	GSList *const last = g_slist_last(_structure->matches);
+	unique_ptr<TriggerMatch> match {new TriggerMatch{
+			static_cast<struct sr_trigger_match *>(last->data),
+			move(channel)}};
+	_matches.push_back(move(match));
+}
+
+void TriggerStage::add_match(shared_ptr<Channel> channel,
+	const TriggerMatchType *type)
+{
+	add_match(move(channel), type, NAN);
+}
+
+TriggerMatch::TriggerMatch(struct sr_trigger_match *structure,
+		shared_ptr<Channel> channel) :
+	_structure(structure),
+	_channel(move(channel))
+{
+}
+
+TriggerMatch::~TriggerMatch()
+{
+}
+
+shared_ptr<Channel> TriggerMatch::channel()
+{
+	return _channel;
+}
+
+const TriggerMatchType *TriggerMatch::type() const
+{
+	return TriggerMatchType::get(_structure->match);
+}
+
+float TriggerMatch::value() const
+{
+	return _structure->value;
+}
+
+DatafeedCallbackData::DatafeedCallbackData(Session *session,
+		DatafeedCallbackFunction callback) :
+	_callback(move(callback)),
+	_session(session)
+{
+}
+
+void DatafeedCallbackData::run(const struct sr_dev_inst *sdi,
+	const struct sr_datafeed_packet *pkt)
+{
+	auto device = _session->get_device(sdi);
+	shared_ptr<Packet> packet {new Packet{device, pkt}, default_delete<Packet>{}};
+	_callback(move(device), move(packet));
+}
+
+SessionDevice::SessionDevice(struct sr_dev_inst *structure) :
+	Device(structure)
+{
+}
+
+SessionDevice::~SessionDevice()
+{
+}
+
+shared_ptr<Device> SessionDevice::get_shared_from_this()
+{
+	return static_pointer_cast<Device>(shared_from_this());
+}
+
+Session::Session(shared_ptr<Context> context) :
+	_structure(nullptr),
+	_context(move(context))
+{
+	check(sr_session_new(_context->_structure, &_structure));
+	_context->_session = this;
+}
+
+Session::Session(shared_ptr<Context> context, string filename) :
+	_structure(nullptr),
+	_context(move(context)),
+	_filename(move(filename))
+{
+	check(sr_session_load(_context->_structure, _filename.c_str(), &_structure));
+	GSList *dev_list;
+	check(sr_session_dev_list(_structure, &dev_list));
+	for (GSList *dev = dev_list; dev; dev = dev->next) {
+		auto *const sdi = static_cast<struct sr_dev_inst *>(dev->data);
+		unique_ptr<SessionDevice> device {new SessionDevice{sdi}};
+		_owned_devices.insert(make_pair(sdi, move(device)));
+	}
+	_context->_session = this;
+}
+
+Session::~Session()
+{
+	check(sr_session_destroy(_structure));
+}
+
+shared_ptr<Device> Session::get_device(const struct sr_dev_inst *sdi)
+{
+	if (_owned_devices.count(sdi))
+		return static_pointer_cast<Device>(
+			_owned_devices[sdi]->share_owned_by(shared_from_this()));
+	else if (_other_devices.count(sdi))
+		return _other_devices[sdi];
+	else
+		throw Error(SR_ERR_BUG);
+}
+
+void Session::add_device(shared_ptr<Device> device)
+{
+	const auto dev_struct = device->_structure;
+	check(sr_session_dev_add(_structure, dev_struct));
+	_other_devices[dev_struct] = move(device);
+}
+
+vector<shared_ptr<Device>> Session::devices()
+{
+	GSList *dev_list;
+	check(sr_session_dev_list(_structure, &dev_list));
+	vector<shared_ptr<Device>> result;
+	for (GSList *dev = dev_list; dev; dev = dev->next) {
+		auto *const sdi = static_cast<struct sr_dev_inst *>(dev->data);
+		result.push_back(get_device(sdi));
+	}
+	return result;
+}
+
+void Session::remove_devices()
+{
+	_other_devices.clear();
+	check(sr_session_dev_remove_all(_structure));
+}
+
+void Session::start()
+{
+	check(sr_session_start(_structure));
+}
+
+void Session::run()
+{
+	check(sr_session_run(_structure));
+}
+
+void Session::stop()
+{
+	check(sr_session_stop(_structure));
+}
+
+bool Session::is_running() const
+{
+	const int ret = sr_session_is_running(_structure);
+	if (ret < 0)
+		throw Error{ret};
+	return (ret != 0);
+}
+
+static void session_stopped_callback(void *data) noexcept
+{
+	auto *const callback = static_cast<SessionStoppedCallback*>(data);
+	(*callback)();
+}
+
+void Session::set_stopped_callback(SessionStoppedCallback callback)
+{
+	_stopped_callback = move(callback);
+	if (_stopped_callback)
+		check(sr_session_stopped_callback_set(_structure,
+				&session_stopped_callback, &_stopped_callback));
+	else
+		check(sr_session_stopped_callback_set(_structure,
+				nullptr, nullptr));
+}
+
+static void datafeed_callback(const struct sr_dev_inst *sdi,
+	const struct sr_datafeed_packet *pkt, void *cb_data) noexcept
+{
+	auto callback = static_cast<DatafeedCallbackData *>(cb_data);
+	callback->run(sdi, pkt);
+}
+
+void Session::add_datafeed_callback(DatafeedCallbackFunction callback)
+{
+	unique_ptr<DatafeedCallbackData> cb_data
+		{new DatafeedCallbackData{this, move(callback)}};
+	check(sr_session_datafeed_callback_add(_structure,
+			&datafeed_callback, cb_data.get()));
+	_datafeed_callbacks.push_back(move(cb_data));
+}
+
+void Session::remove_datafeed_callbacks()
+{
+	check(sr_session_datafeed_callback_remove_all(_structure));
+	_datafeed_callbacks.clear();
+}
+
+shared_ptr<Trigger> Session::trigger()
+{
+	return _trigger;
+}
+
+void Session::set_trigger(shared_ptr<Trigger> trigger)
+{
+	if (!trigger)
+		// Set NULL trigger, i.e. remove any trigger from the session.
+		check(sr_session_trigger_set(_structure, nullptr));
+	else
+		check(sr_session_trigger_set(_structure, trigger->_structure));
+	_trigger = move(trigger);
+}
+
+string Session::filename() const
+{
+	return _filename;
+}
+
+shared_ptr<Context> Session::context()
+{
+	return _context;
+}
+
+Packet::Packet(shared_ptr<Device> device,
+	const struct sr_datafeed_packet *structure) :
+	_structure(structure),
+	_device(move(device))
+{
+	switch (structure->type)
+	{
+		case SR_DF_HEADER:
+			_payload.reset(new Header{
+				static_cast<const struct sr_datafeed_header *>(
+					structure->payload)});
+			break;
+		case SR_DF_META:
+			_payload.reset(new Meta{
+				static_cast<const struct sr_datafeed_meta *>(
+					structure->payload)});
+			break;
+		case SR_DF_LOGIC:
+			_payload.reset(new Logic{
+				static_cast<const struct sr_datafeed_logic *>(
+					structure->payload)});
+			break;
+		case SR_DF_ANALOG:
+			_payload.reset(new Analog{
+				static_cast<const struct sr_datafeed_analog *>(
+					structure->payload)});
+			break;
+	}
+}
+
+Packet::~Packet()
+{
+}
+
+const PacketType *Packet::type() const
+{
+	return PacketType::get(_structure->type);
+}
+
+shared_ptr<PacketPayload> Packet::payload()
+{
+	if (_payload)
+		return _payload->share_owned_by(shared_from_this());
+	else
+		throw Error(SR_ERR_NA);
+}
+
+PacketPayload::PacketPayload()
+{
+}
+
+PacketPayload::~PacketPayload()
+{
+}
+
+Header::Header(const struct sr_datafeed_header *structure) :
+	PacketPayload(),
+	_structure(structure)
+{
+}
+
+Header::~Header()
+{
+}
+
+shared_ptr<PacketPayload> Header::share_owned_by(shared_ptr<Packet> _parent)
+{
+	return static_pointer_cast<PacketPayload>(
+		ParentOwned::share_owned_by(_parent));
+}
+
+int Header::feed_version() const
+{
+	return _structure->feed_version;
+}
+
+Glib::TimeVal Header::start_time() const
+{
+	return Glib::TimeVal(
+		_structure->starttime.tv_sec,
+		_structure->starttime.tv_usec);
+}
+
+Meta::Meta(const struct sr_datafeed_meta *structure) :
+	PacketPayload(),
+	_structure(structure)
+{
+}
+
+Meta::~Meta()
+{
+}
+
+shared_ptr<PacketPayload> Meta::share_owned_by(shared_ptr<Packet> _parent)
+{
+	return static_pointer_cast<PacketPayload>(
+		ParentOwned::share_owned_by(_parent));
+}
+
+map<const ConfigKey *, Glib::VariantBase> Meta::config() const
+{
+	map<const ConfigKey *, Glib::VariantBase> result;
+	for (auto l = _structure->config; l; l = l->next) {
+		auto *const config = static_cast<struct sr_config *>(l->data);
+		result[ConfigKey::get(config->key)] = Glib::VariantBase(config->data, true);
+	}
+	return result;
+}
+
+Logic::Logic(const struct sr_datafeed_logic *structure) :
+	PacketPayload(),
+	_structure(structure)
+{
+}
+
+Logic::~Logic()
+{
+}
+
+shared_ptr<PacketPayload> Logic::share_owned_by(shared_ptr<Packet> _parent)
+{
+	return static_pointer_cast<PacketPayload>(
+		ParentOwned::share_owned_by(_parent));
+}
+
+void *Logic::data_pointer()
+{
+	return _structure->data;
+}
+
+size_t Logic::data_length() const
+{
+	return _structure->length;
+}
+
+unsigned int Logic::unit_size() const
+{
+	return _structure->unitsize;
+}
+
+Analog::Analog(const struct sr_datafeed_analog *structure) :
+	PacketPayload(),
+	_structure(structure)
+{
+}
+
+Analog::~Analog()
+{
+}
+
+shared_ptr<PacketPayload> Analog::share_owned_by(shared_ptr<Packet> _parent)
+{
+	return static_pointer_cast<PacketPayload>(
+		ParentOwned::share_owned_by(_parent));
+}
+
+void *Analog::data_pointer()
+{
+	return _structure->data;
+}
+
+unsigned int Analog::num_samples() const
+{
+	return _structure->num_samples;
+}
+
+vector<shared_ptr<Channel>> Analog::channels()
+{
+	vector<shared_ptr<Channel>> result;
+	for (auto l = _structure->meaning->channels; l; l = l->next) {
+		auto *const ch = static_cast<struct sr_channel *>(l->data);
+		result.push_back(_parent->_device->get_channel(ch));
+	}
+	return result;
+}
+
+const Quantity *Analog::mq() const
+{
+	return Quantity::get(_structure->meaning->mq);
+}
+
+const Unit *Analog::unit() const
+{
+	return Unit::get(_structure->meaning->unit);
+}
+
+vector<const QuantityFlag *> Analog::mq_flags() const
+{
+	return QuantityFlag::flags_from_mask(_structure->meaning->mqflags);
+}
+
+InputFormat::InputFormat(const struct sr_input_module *structure) :
+	_structure(structure)
+{
+}
+
+InputFormat::~InputFormat()
+{
+}
+
+string InputFormat::name() const
+{
+	return valid_string(sr_input_id_get(_structure));
+}
+
+string InputFormat::description() const
+{
+	return valid_string(sr_input_description_get(_structure));
+}
+
+vector<string> InputFormat::extensions() const
+{
+	vector<string> exts;
+	for (const char *const *e = sr_input_extensions_get(_structure);
+		e && *e; e++)
+		exts.push_back(*e);
+	return exts;
+}
+
+map<string, shared_ptr<Option>> InputFormat::options()
+{
+	map<string, shared_ptr<Option>> result;
+
+	if (const struct sr_option **options = sr_input_options_get(_structure))
+	{
+		shared_ptr<const struct sr_option *> option_array
+			{options, &sr_input_options_free};
+		for (int i = 0; options[i]; i++) {
+			shared_ptr<Option> opt {
+				new Option{options[i], option_array},
+				default_delete<Option>{}};
+			result.insert({opt->id(), move(opt)});
+		}
+	}
+	return result;
+}
+
+shared_ptr<Input> InputFormat::create_input(
+	map<string, Glib::VariantBase> options)
+{
+	auto input = sr_input_new(_structure, map_to_hash_variant(options));
+	if (!input)
+		throw Error(SR_ERR_ARG);
+	return shared_ptr<Input>{new Input{_parent, input}, default_delete<Input>{}};
+}
+
+Input::Input(shared_ptr<Context> context, const struct sr_input *structure) :
+	_structure(structure),
+	_context(move(context))
+{
+}
+
+shared_ptr<InputDevice> Input::device()
+{
+	if (!_device)
+	{
+		auto sdi = sr_input_dev_inst_get(_structure);
+		if (!sdi)
+			throw Error(SR_ERR_NA);
+		_device.reset(new InputDevice{shared_from_this(), sdi});
+	}
+
+	return _device->share_owned_by(shared_from_this());
+}
+
+void Input::send(void *data, size_t length)
+{
+	auto gstr = g_string_new_len(static_cast<char *>(data), length);
+	auto ret = sr_input_send(_structure, gstr);
+	g_string_free(gstr, false);
+	check(ret);
+}
+
+void Input::end()
+{
+	check(sr_input_end(_structure));
+}
+
+Input::~Input()
+{
+	sr_input_free(_structure);
+}
+
+InputDevice::InputDevice(shared_ptr<Input> input,
+		struct sr_dev_inst *structure) :
+	Device(structure),
+	_input(move(input))
+{
+}
+
+InputDevice::~InputDevice()
+{
+}
+
+shared_ptr<Device> InputDevice::get_shared_from_this()
+{
+	return static_pointer_cast<Device>(shared_from_this());
+}
+
+Option::Option(const struct sr_option *structure,
+		shared_ptr<const struct sr_option *> structure_array) :
+	_structure(structure),
+	_structure_array(move(structure_array))
+{
+}
+
+Option::~Option()
+{
+}
+
+string Option::id() const
+{
+	return valid_string(_structure->id);
+}
+
+string Option::name() const
+{
+	return valid_string(_structure->name);
+}
+
+string Option::description() const
+{
+	return valid_string(_structure->desc);
+}
+
+Glib::VariantBase Option::default_value() const
+{
+	return Glib::VariantBase(_structure->def, true);
+}
+
+vector<Glib::VariantBase> Option::values() const
+{
+	vector<Glib::VariantBase> result;
+	for (auto l = _structure->values; l; l = l->next) {
+		auto *const var = static_cast<GVariant *>(l->data);
+		result.push_back(Glib::VariantBase(var, true));
+	}
+	return result;
+}
+
+OutputFormat::OutputFormat(const struct sr_output_module *structure) :
+	_structure(structure)
+{
+}
+
+OutputFormat::~OutputFormat()
+{
+}
+
+string OutputFormat::name() const
+{
+	return valid_string(sr_output_id_get(_structure));
+}
+
+string OutputFormat::description() const
+{
+	return valid_string(sr_output_description_get(_structure));
+}
+
+vector<string> OutputFormat::extensions() const
+{
+	vector<string> exts;
+	for (const char *const *e = sr_output_extensions_get(_structure);
+		e && *e; e++)
+		exts.push_back(*e);
+	return exts;
+}
+
+map<string, shared_ptr<Option>> OutputFormat::options()
+{
+	map<string, shared_ptr<Option>> result;
+
+	if (const struct sr_option **options = sr_output_options_get(_structure))
+	{
+		shared_ptr<const struct sr_option *> option_array
+			{options, &sr_output_options_free};
+		for (int i = 0; options[i]; i++) {
+			shared_ptr<Option> opt {
+				new Option{options[i], option_array},
+				default_delete<Option>{}};
+			result.insert({opt->id(), move(opt)});
+		}
+	}
+	return result;
+}
+
+shared_ptr<Output> OutputFormat::create_output(
+	shared_ptr<Device> device, map<string, Glib::VariantBase> options)
+{
+	return shared_ptr<Output>{
+		new Output{shared_from_this(), move(device), move(options)},
+		default_delete<Output>{}};
+}
+
+shared_ptr<Output> OutputFormat::create_output(string filename,
+	shared_ptr<Device> device, map<string, Glib::VariantBase> options)
+{
+	return shared_ptr<Output>{
+		new Output{move(filename), shared_from_this(), move(device), move(options)},
+		default_delete<Output>{}};
+}
+
+bool OutputFormat::test_flag(const OutputFlag *flag) const
+{
+	return sr_output_test_flag(_structure, flag->id());
+}
+
+Output::Output(shared_ptr<OutputFormat> format,
+		shared_ptr<Device> device, map<string, Glib::VariantBase> options) :
+	_structure(sr_output_new(format->_structure,
+		map_to_hash_variant(options), device->_structure, nullptr)),
+	_format(move(format)),
+	_device(move(device)),
+	_options(move(options))
+{
+}
+
+Output::Output(string filename, shared_ptr<OutputFormat> format,
+		shared_ptr<Device> device, map<string, Glib::VariantBase> options) :
+	_structure(sr_output_new(format->_structure,
+		map_to_hash_variant(options), device->_structure, filename.c_str())),
+	_format(move(format)),
+	_device(move(device)),
+	_options(move(options))
+{
+}
+
+Output::~Output()
+{
+	check(sr_output_free(_structure));
+}
+
+string Output::receive(shared_ptr<Packet> packet)
+{
+	GString *out;
+	check(sr_output_send(_structure, packet->_structure, &out));
+	if (out)
+	{
+		auto result = string(out->str, out->str + out->len);
+		g_string_free(out, true);
+		return result;
+	}
+	else
+	{
+		return string();
+	}
+}
+
+#include <enums.cpp>
+
+}
diff --git a/bindings/cxx/enums.py b/bindings/cxx/enums.py
new file mode 100644
index 0000000..a1577a0
--- /dev/null
+++ b/bindings/cxx/enums.py
@@ -0,0 +1,175 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2014 Martin Ling <martin-sigrok at 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 = """
+/** {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)
diff --git a/bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp b/bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp
new file mode 100644
index 0000000..eb7957e
--- /dev/null
+++ b/bindings/cxx/include/libsigrokcxx/libsigrokcxx.hpp
@@ -0,0 +1,998 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013-2014 Martin Ling <martin-sigrok at 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/>.
+ */
+
+/**
+
+ at mainpage API Reference
+
+Introduction
+------------
+
+The libsigrokcxx API provides an object-oriented C++ interface to the
+functionality in libsigrok, including automatic memory and resource management.
+
+It is built on top of the public libsigrok C API, and is designed to be used as
+a standalone alternative API. Programs should not mix usage of the C and C++
+APIs; the C++ interface code needs to have full control of all C API calls for
+resources to be managed correctly.
+
+Memory management
+-----------------
+
+All runtime objects created through the C++ API are passed and accessed via
+shared pointers, using the C++11 std::shared_ptr implementation. This means
+that a reference count is kept for each object.
+
+Shared pointers can be copied and assigned in a user's program, automatically
+updating their reference count and deleting objects when they are no longer in
+use. The C++ interface code also keeps track of internal dependencies between
+libsigrok resources, and ensures that objects are not prematurely deleted when
+their resources are in use by other objects.
+
+This means that management of libsigrokcxx objects and their underlying
+libsigrok resources can be treated as fully automatic. As long as all shared
+pointers to objects are deleted or reassigned when no longer in use, all
+underlying resources will be released at the right time.
+
+Getting started
+---------------
+
+Usage of the C++ API needs to begin with a call to sigrok::Context::create().
+This will create the global libsigrok context and returns a shared pointer to
+the sigrok::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, a sigrok::Error exception is
+raised, which provides access to the error code and description.
+
+*/
+
+#ifndef LIBSIGROKCXX_HPP
+#define LIBSIGROKCXX_HPP
+
+#include <libsigrok/libsigrok.h>
+
+/* Suppress warnings due to glibmm's use of std::auto_ptr<> in a public
+ * header file. To be removed once glibmm is fixed. */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#include <glibmm.h>
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+#include <stdexcept>
+#include <memory>
+#include <vector>
+#include <map>
+#include <set>
+
+namespace sigrok
+{
+
+using namespace std;
+
+/* Forward declarations */
+class SR_API Error;
+class SR_API Context;
+class SR_API Driver;
+class SR_API Device;
+class SR_API HardwareDevice;
+class SR_API Channel;
+class SR_API Session;
+class SR_API ConfigKey;
+class SR_API Capability;
+class SR_API InputFormat;
+class SR_API OutputFormat;
+class SR_API OutputFlag;
+class SR_API LogLevel;
+class SR_API ChannelGroup;
+class SR_API Trigger;
+class SR_API TriggerStage;
+class SR_API TriggerMatch;
+class SR_API TriggerMatchType;
+class SR_API ChannelType;
+class SR_API Packet;
+class SR_API PacketPayload;
+class SR_API PacketType;
+class SR_API Quantity;
+class SR_API Unit;
+class SR_API QuantityFlag;
+class SR_API Input;
+class SR_API InputDevice;
+class SR_API Output;
+class SR_API DataType;
+class SR_API Option;
+class SR_API UserDevice;
+
+/** Exception thrown when an error code is returned by any libsigrok call. */
+class SR_API Error: public exception
+{
+public:
+	explicit Error(int result);
+	~Error() noexcept;
+	const int result;
+	const char *what() const noexcept;
+};
+
+/* Base template for classes whose resources are owned by a parent object. */
+template <class Class, class Parent>
+class SR_API ParentOwned
+{
+private:
+	/* Weak pointer for shared_from_this() implementation. */
+	weak_ptr<Class> _weak_this;
+
+	static void reset_parent(Class *object)
+	{
+		if (!object->_parent)
+			throw Error(SR_ERR_BUG);
+		object->_parent.reset();
+	}
+
+protected:
+	/*  Parent object which owns this child object's underlying structure.
+
+		This shared pointer will be null when this child is unused, but
+		will be assigned to point to the parent before any shared pointer
+		to this child is handed out to the user.
+
+		When the reference count of this child falls to zero, this shared
+		pointer to its parent is reset by a custom deleter on the child's
+		shared pointer.
+
+		This strategy ensures that the destructors for both the child and
+		the parent are called at the correct time, i.e. only when all
+		references to both the parent and all its children are gone. */
+	shared_ptr<Parent> _parent;
+
+	ParentOwned() {}
+
+	/* Note, this implementation will create a new smart_ptr if none exists. */
+	shared_ptr<Class> shared_from_this()
+	{
+		shared_ptr<Class> shared = _weak_this.lock();
+
+		if (!shared)
+		{
+			shared.reset(static_cast<Class *>(this), &reset_parent);
+			_weak_this = shared;
+		}
+
+		return shared;
+	}
+
+	shared_ptr<Class> share_owned_by(shared_ptr<Parent> parent)
+	{
+		if (!parent)
+			throw Error(SR_ERR_BUG);
+		this->_parent = parent;
+		return shared_from_this();
+	}
+
+public:
+	/* Get parent object that owns this object. */
+	shared_ptr<Parent> parent()
+	{
+		return _parent;
+	}
+};
+
+/* Base template for classes whose resources are owned by the user. */
+template <class Class>
+class SR_API UserOwned : public enable_shared_from_this<Class>
+{
+protected:
+	UserOwned() {}
+
+	shared_ptr<Class> shared_from_this()
+	{
+		auto shared = enable_shared_from_this<Class>::shared_from_this();
+		if (!shared)
+			throw Error(SR_ERR_BUG);
+		return shared;
+	}
+};
+
+/** Type of log callback */
+typedef function<void(const LogLevel *, string message)> LogCallbackFunction;
+
+/** Resource reader delegate. */
+class SR_API ResourceReader
+{
+public:
+	ResourceReader() {}
+	virtual ~ResourceReader();
+private:
+	/** Resource open hook. */
+	virtual void open(struct sr_resource *res, string name) = 0;
+	/** Resource close hook. */
+	virtual void close(struct sr_resource *res) = 0;
+	/** Resource read hook. */
+	virtual size_t read(const struct sr_resource *res, void *buf, size_t count) = 0;
+
+	static SR_PRIV int open_callback(struct sr_resource *res,
+			const char *name, void *cb_data) noexcept;
+	static SR_PRIV int close_callback(struct sr_resource *res,
+			void *cb_data) noexcept;
+	static SR_PRIV gssize read_callback(const struct sr_resource *res,
+			void *buf, size_t count, void *cb_data) noexcept;
+	friend class Context;
+};
+
+/** The global libsigrok context */
+class SR_API Context : public UserOwned<Context>
+{
+public:
+	/** Create new context */
+	static shared_ptr<Context> create();
+	/** libsigrok package version. */
+	static string package_version();
+	/** libsigrok library version. */
+	static string lib_version();
+	/** Available hardware drivers, indexed by name. */
+	map<string, shared_ptr<Driver> > drivers();
+	/** Available input formats, indexed by name. */
+	map<string, shared_ptr<InputFormat> > input_formats();
+	/** Available output formats, indexed by name. */
+	map<string, shared_ptr<OutputFormat> > output_formats();
+	/** Current log level. */
+	const LogLevel *log_level() const;
+	/** Set the log level.
+	 * @param level LogLevel to use. */
+	void set_log_level(const LogLevel *level);
+	/** Set the log callback.
+	 * @param callback Callback of the form callback(LogLevel, string). */
+	void set_log_callback(LogCallbackFunction callback);
+	/** Set the log callback to the default handler. */
+	void set_log_callback_default();
+	/** Install a delegate for reading resource files.
+	 * @param reader The resource reader delegate, or nullptr to unset. */
+	void set_resource_reader(ResourceReader *reader);
+	/** Create a new session. */
+	shared_ptr<Session> create_session();
+	/** Create a new user device. */
+	shared_ptr<UserDevice> create_user_device(
+		string vendor, string model, string version);
+	/** Create a header packet. */
+	shared_ptr<Packet> create_header_packet(Glib::TimeVal start_time);
+	/** Create a meta packet. */
+	shared_ptr<Packet> create_meta_packet(
+		map<const ConfigKey *, Glib::VariantBase> config);
+	/** Create a logic packet. */
+	shared_ptr<Packet> create_logic_packet(
+		void *data_pointer, size_t data_length, unsigned int unit_size);
+	/** Create an analog packet. */
+	shared_ptr<Packet> create_analog_packet(
+		vector<shared_ptr<Channel> > channels,
+		float *data_pointer, unsigned int num_samples, const Quantity *mq,
+		const Unit *unit, vector<const QuantityFlag *> mqflags);
+	/** Load a saved session.
+	 * @param filename File name string. */
+	shared_ptr<Session> load_session(string filename);
+	/** Create a new trigger.
+	 * @param name Name string for new trigger. */
+	shared_ptr<Trigger> create_trigger(string name);
+	/** Open an input file.
+	 * @param filename File name string. */
+	shared_ptr<Input> open_file(string filename);
+	/** Open an input stream based on header data.
+	 * @param header Initial data from stream. */
+	shared_ptr<Input> open_stream(string header);
+	map<string, string> serials(shared_ptr<Driver> driver) const;
+private:
+	struct sr_context *_structure;
+	map<string, unique_ptr<Driver> > _drivers;
+	map<string, unique_ptr<InputFormat> > _input_formats;
+	map<string, unique_ptr<OutputFormat> > _output_formats;
+	Session *_session;
+	LogCallbackFunction _log_callback;
+	Context();
+	~Context();
+	friend class Session;
+	friend class Driver;
+	friend struct std::default_delete<Context>;
+};
+
+/** An object that can be configured. */
+class SR_API Configurable
+{
+public:
+	/** Supported configuration keys. */
+	set<const ConfigKey *> config_keys() const;
+	/** Read configuration for the given key.
+	 * @param key ConfigKey to read. */
+	Glib::VariantBase config_get(const ConfigKey *key) const;
+	/** Set configuration for the given key to a specified value.
+	 * @param key ConfigKey to set.
+	 * @param value Value to set. */
+	void config_set(const ConfigKey *key, const Glib::VariantBase &value);
+	/** Enumerate available values for the given configuration key.
+	 * @param key ConfigKey to enumerate values for. */
+	Glib::VariantContainerBase config_list(const ConfigKey *key) const;
+	/** Enumerate configuration capabilities for the given configuration key.
+	 * @param key ConfigKey to enumerate capabilities for. */
+	set<const Capability *> config_capabilities(const ConfigKey *key) const;
+	/** Check whether a configuration capability is supported for a given key.
+	 * @param key ConfigKey to check.
+     * @param capability Capability to check for. */
+	bool config_check(const ConfigKey *key, const Capability *capability) const;
+protected:
+	Configurable(
+		struct sr_dev_driver *driver,
+		struct sr_dev_inst *sdi,
+		struct sr_channel_group *channel_group);
+	virtual ~Configurable();
+	struct sr_dev_driver *config_driver;
+	struct sr_dev_inst *config_sdi;
+	struct sr_channel_group *config_channel_group;
+};
+
+/** A hardware driver provided by the library */
+class SR_API Driver : public ParentOwned<Driver, Context>, public Configurable
+{
+public:
+	/** Name of this driver. */
+	string name() const;
+	/** Long name for this driver. */
+	string long_name() const;
+	/** Scan options supported by this driver. */
+	set<const ConfigKey *> scan_options() const;
+	/** Scan for devices and return a list of devices found.
+	 * @param options Mapping of (ConfigKey, value) pairs. */
+	vector<shared_ptr<HardwareDevice> > scan(map<const ConfigKey *, Glib::VariantBase>
+			options = map<const ConfigKey *, Glib::VariantBase>());
+private:
+	struct sr_dev_driver *_structure;
+	bool _initialized;
+	vector<HardwareDevice *> _devices;
+	explicit Driver(struct sr_dev_driver *structure);
+	~Driver();
+	friend class Context;
+	friend class HardwareDevice;
+	friend class ChannelGroup;
+	friend struct std::default_delete<Driver>;
+};
+
+/** A generic device, either hardware or virtual */
+class SR_API Device : public Configurable
+{
+public:
+	/** Vendor name for this device. */
+	string vendor() const;
+	/** Model name for this device. */
+	string model() const;
+	/** Version string for this device. */
+	string version() const;
+	/** Serial number for this device. */
+	string serial_number() const;
+	/** Connection ID for this device. */
+	string connection_id() const;
+	/** List of the channels available on this device. */
+	vector<shared_ptr<Channel> > channels();
+	/** Channel groups available on this device, indexed by name. */
+	map<string, shared_ptr<ChannelGroup> > channel_groups();
+	/** Open device. */
+	void open();
+	/** Close device. */
+	void close();
+protected:
+	explicit Device(struct sr_dev_inst *structure);
+	~Device();
+	virtual shared_ptr<Device> get_shared_from_this() = 0;
+	shared_ptr<Channel> get_channel(struct sr_channel *ptr);
+
+	struct sr_dev_inst *_structure;
+	map<struct sr_channel *, unique_ptr<Channel> > _channels;
+private:
+	map<string, unique_ptr<ChannelGroup> > _channel_groups;
+
+	friend class Session;
+	friend class Channel;
+	friend class ChannelGroup;
+	friend class Output;
+	friend class Analog;
+	friend struct std::default_delete<Device>;
+};
+
+/** A real hardware device, connected via a driver */
+class SR_API HardwareDevice :
+	public UserOwned<HardwareDevice>,
+	public Device
+{
+public:
+	/** Driver providing this device. */
+	shared_ptr<Driver> driver();
+private:
+	HardwareDevice(shared_ptr<Driver> driver, struct sr_dev_inst *structure);
+	~HardwareDevice();
+	shared_ptr<Device> get_shared_from_this();
+	shared_ptr<Driver> _driver;
+
+	friend class Driver;
+	friend class ChannelGroup;
+	friend struct std::default_delete<HardwareDevice>;
+};
+
+/** A virtual device, created by the user */
+class SR_API UserDevice :
+	public UserOwned<UserDevice>,
+	public Device
+{
+public:
+	/** Add a new channel to this device. */
+	shared_ptr<Channel> add_channel(unsigned int index, const ChannelType *type, string name);
+private:
+	UserDevice(string vendor, string model, string version);
+	~UserDevice();
+	shared_ptr<Device> get_shared_from_this();
+
+	friend class Context;
+	friend struct std::default_delete<UserDevice>;
+};
+
+/** A channel on a device */
+class SR_API Channel :
+	public ParentOwned<Channel, Device>
+{
+public:
+	/** Current name of this channel. */
+	string name() const;
+	/** Set the name of this channel. *
+	 * @param name Name string to set. */
+	void set_name(string name);
+	/** Type of this channel. */
+	const ChannelType *type() const;
+	/** Enabled status of this channel. */
+	bool enabled() const;
+	/** Set the enabled status of this channel.
+	 * @param value Boolean value to set. */
+	void set_enabled(bool value);
+	/** Get the index number of this channel. */
+	unsigned int index() const;
+private:
+	explicit Channel(struct sr_channel *structure);
+	~Channel();
+	struct sr_channel *_structure;
+	const ChannelType * const _type;
+	friend class Device;
+	friend class UserDevice;
+	friend class ChannelGroup;
+	friend class Session;
+	friend class TriggerStage;
+	friend class Context;
+	friend struct std::default_delete<Channel>;
+};
+
+/** A group of channels on a device, which share some configuration */
+class SR_API ChannelGroup :
+	public ParentOwned<ChannelGroup, Device>,
+	public Configurable
+{
+public:
+	/** Name of this channel group. */
+	string name() const;
+	/** List of the channels in this group. */
+	vector<shared_ptr<Channel> > channels();
+private:
+	ChannelGroup(const Device *device, struct sr_channel_group *structure);
+	~ChannelGroup();
+	vector<Channel *> _channels;
+	friend class Device;
+	friend struct std::default_delete<ChannelGroup>;
+};
+
+/** A trigger configuration */
+class SR_API Trigger : public UserOwned<Trigger>
+{
+public:
+	/** Name of this trigger configuration. */
+	string name() const;
+	/** List of the stages in this trigger. */
+	vector<shared_ptr<TriggerStage> > stages();
+	/** Add a new stage to this trigger. */
+	shared_ptr<TriggerStage> add_stage();
+private:
+	Trigger(shared_ptr<Context> context, string name);
+	~Trigger();
+	struct sr_trigger *_structure;
+	shared_ptr<Context> _context;
+	vector<unique_ptr<TriggerStage> > _stages;
+	friend class Context;
+	friend class Session;
+	friend struct std::default_delete<Trigger>;
+};
+
+/** A stage in a trigger configuration */
+class SR_API TriggerStage :
+	public ParentOwned<TriggerStage, Trigger>
+{
+public:
+	/** Index number of this stage. */
+	int number() const;
+	/** List of match conditions on this stage. */
+	vector<shared_ptr<TriggerMatch> > matches();
+	/** Add a new match condition to this stage.
+	 * @param channel Channel to match on.
+	 * @param type TriggerMatchType to apply. */
+	void add_match(shared_ptr<Channel> channel, const TriggerMatchType *type);
+	/** Add a new match condition to this stage.
+	 * @param channel Channel to match on.
+	 * @param type TriggerMatchType to apply.
+	 * @param value Threshold value. */
+	void add_match(shared_ptr<Channel> channel, const TriggerMatchType *type, float value);
+private:
+	struct sr_trigger_stage *_structure;
+	vector<unique_ptr<TriggerMatch> > _matches;
+	explicit TriggerStage(struct sr_trigger_stage *structure);
+	~TriggerStage();
+	friend class Trigger;
+	friend struct std::default_delete<TriggerStage>;
+};
+
+/** A match condition in a trigger configuration  */
+class SR_API TriggerMatch :
+	public ParentOwned<TriggerMatch, TriggerStage>
+{
+public:
+	/** Channel this condition matches on. */
+	shared_ptr<Channel> channel();
+	/** Type of match. */
+	const TriggerMatchType *type() const;
+	/** Threshold value. */
+	float value() const;
+private:
+	TriggerMatch(struct sr_trigger_match *structure, shared_ptr<Channel> channel);
+	~TriggerMatch();
+	struct sr_trigger_match *_structure;
+	shared_ptr<Channel> _channel;
+	friend class TriggerStage;
+	friend struct std::default_delete<TriggerMatch>;
+};
+
+/** Type of session stopped callback */
+typedef function<void()> SessionStoppedCallback;
+
+/** Type of datafeed callback */
+typedef function<void(shared_ptr<Device>, shared_ptr<Packet>)>
+	DatafeedCallbackFunction;
+
+/* Data required for C callback function to call a C++ datafeed callback */
+class SR_PRIV DatafeedCallbackData
+{
+public:
+	void run(const struct sr_dev_inst *sdi,
+		const struct sr_datafeed_packet *pkt);
+private:
+	DatafeedCallbackFunction _callback;
+	DatafeedCallbackData(Session *session,
+		DatafeedCallbackFunction callback);
+	Session *_session;
+	friend class Session;
+};
+
+/** A virtual device associated with a stored session */
+class SR_API SessionDevice :
+	public ParentOwned<SessionDevice, Session>,
+	public Device
+{
+private:
+	explicit SessionDevice(struct sr_dev_inst *sdi);
+	~SessionDevice();
+	shared_ptr<Device> get_shared_from_this();
+
+	friend class Session;
+	friend struct std::default_delete<SessionDevice>;
+};
+
+/** A sigrok session */
+class SR_API Session : public UserOwned<Session>
+{
+public:
+	/** Add a device to this session.
+	 * @param device Device to add. */
+	void add_device(shared_ptr<Device> device);
+	/** List devices attached to this session. */
+	vector<shared_ptr<Device> > devices();
+	/** Remove all devices from this session. */
+	void remove_devices();
+	/** Add a datafeed callback to this session.
+	 * @param callback Callback of the form callback(Device, Packet). */
+	void add_datafeed_callback(DatafeedCallbackFunction callback);
+	/** Remove all datafeed callbacks from this session. */
+	void remove_datafeed_callbacks();
+	/** Start the session. */
+	void start();
+	/** Run the session event loop. */
+	void run();
+	/** Stop the session. */
+	void stop();
+	/** Return whether the session is running. */
+	bool is_running() const;
+	/** Set callback to be invoked on session stop. */
+	void set_stopped_callback(SessionStoppedCallback callback);
+	/** Get current trigger setting. */
+	shared_ptr<Trigger> trigger();
+	/** Get the context. */
+	shared_ptr<Context> context();
+	/** Set trigger setting.
+	 * @param trigger Trigger object to use. */
+	void set_trigger(shared_ptr<Trigger> trigger);
+	/** Get filename this session was loaded from. */
+	string filename() const;
+private:
+	explicit Session(shared_ptr<Context> context);
+	Session(shared_ptr<Context> context, string filename);
+	~Session();
+	shared_ptr<Device> get_device(const struct sr_dev_inst *sdi);
+	struct sr_session *_structure;
+	const shared_ptr<Context> _context;
+	map<const struct sr_dev_inst *, unique_ptr<SessionDevice> > _owned_devices;
+	map<const struct sr_dev_inst *, shared_ptr<Device> > _other_devices;
+	vector<unique_ptr<DatafeedCallbackData> > _datafeed_callbacks;
+	SessionStoppedCallback _stopped_callback;
+	string _filename;
+	shared_ptr<Trigger> _trigger;
+
+	friend class Context;
+	friend class DatafeedCallbackData;
+	friend class SessionDevice;
+	friend struct std::default_delete<Session>;
+};
+
+/** A packet on the session datafeed */
+class SR_API Packet : public UserOwned<Packet>
+{
+public:
+	/** Type of this packet. */
+	const PacketType *type() const;
+	/** Payload of this packet. */
+	shared_ptr<PacketPayload> payload();
+private:
+	Packet(shared_ptr<Device> device,
+		const struct sr_datafeed_packet *structure);
+	~Packet();
+	const struct sr_datafeed_packet *_structure;
+	shared_ptr<Device> _device;
+	unique_ptr<PacketPayload> _payload;
+
+	friend class Session;
+	friend class Output;
+	friend class DatafeedCallbackData;
+	friend class Header;
+	friend class Meta;
+	friend class Logic;
+	friend class Analog;
+	friend class Context;
+	friend struct std::default_delete<Packet>;
+};
+
+/** Abstract base class for datafeed packet payloads */
+class SR_API PacketPayload
+{
+protected:
+	PacketPayload();
+	virtual ~PacketPayload() = 0;
+private:
+	virtual shared_ptr<PacketPayload> share_owned_by(shared_ptr<Packet> parent) = 0;
+
+	friend class Packet;
+	friend class Output;
+	friend struct std::default_delete<PacketPayload>;
+};
+
+/** Payload of a datafeed header packet */
+class SR_API Header :
+	public ParentOwned<Header, Packet>,
+	public PacketPayload
+{
+public:
+	/* Feed version number. */
+	int feed_version() const;
+	/* Start time of this session. */
+	Glib::TimeVal start_time() const;
+private:
+	explicit Header(const struct sr_datafeed_header *structure);
+	~Header();
+	shared_ptr<PacketPayload> share_owned_by(shared_ptr<Packet> parent);
+
+	const struct sr_datafeed_header *_structure;
+
+	friend class Packet;
+};
+
+/** Payload of a datafeed metadata packet */
+class SR_API Meta :
+	public ParentOwned<Meta, Packet>,
+	public PacketPayload
+{
+public:
+	/* Mapping of (ConfigKey, value) pairs. */
+	map<const ConfigKey *, Glib::VariantBase> config() const;
+private:
+	explicit Meta(const struct sr_datafeed_meta *structure);
+	~Meta();
+	shared_ptr<PacketPayload> share_owned_by(shared_ptr<Packet> parent);
+
+	const struct sr_datafeed_meta *_structure;
+	map<const ConfigKey *, Glib::VariantBase> _config;
+
+	friend class Packet;
+};
+
+/** Payload of a datafeed packet with logic data */
+class SR_API Logic :
+	public ParentOwned<Logic, Packet>,
+	public PacketPayload
+{
+public:
+	/* Pointer to data. */
+	void *data_pointer();
+	/* Data length in bytes. */
+	size_t data_length() const;
+	/* Size of each sample in bytes. */
+	unsigned int unit_size() const;
+private:
+	explicit Logic(const struct sr_datafeed_logic *structure);
+	~Logic();
+	shared_ptr<PacketPayload> share_owned_by(shared_ptr<Packet> parent);
+
+	const struct sr_datafeed_logic *_structure;
+
+	friend class Packet;
+};
+
+/** Payload of a datafeed packet with analog data */
+class SR_API Analog :
+	public ParentOwned<Analog, Packet>,
+	public PacketPayload
+{
+public:
+	/** Pointer to data. */
+	void *data_pointer();
+	/** Number of samples in this packet. */
+	unsigned int num_samples() const;
+	/** Channels for which this packet contains data. */
+	vector<shared_ptr<Channel> > channels();
+	/** Measured quantity of the samples in this packet. */
+	const Quantity *mq() const;
+	/** Unit of the samples in this packet. */
+	const Unit *unit() const;
+	/** Measurement flags associated with the samples in this packet. */
+	vector<const QuantityFlag *> mq_flags() const;
+private:
+	explicit Analog(const struct sr_datafeed_analog *structure);
+	~Analog();
+	shared_ptr<PacketPayload> share_owned_by(shared_ptr<Packet> parent);
+
+	const struct sr_datafeed_analog *_structure;
+
+	friend class Packet;
+};
+
+/** An input format supported by the library */
+class SR_API InputFormat :
+	public ParentOwned<InputFormat, Context>
+{
+public:
+	/** Name of this input format. */
+	string name() const;
+	/** Description of this input format. */
+	string description() const;
+	/** A list of preferred file name extensions for this file format.
+         * @note This list is a recommendation only. */
+	vector<string> extensions() const;
+	/** Options supported by this input format. */
+	map<string, shared_ptr<Option> > options();
+	/** Create an input using this input format.
+	 * @param options Mapping of (option name, value) pairs. */
+	shared_ptr<Input> create_input(map<string, Glib::VariantBase>
+			options = map<string, Glib::VariantBase>());
+private:
+	explicit InputFormat(const struct sr_input_module *structure);
+	~InputFormat();
+
+	const struct sr_input_module *_structure;
+
+	friend class Context;
+	friend class InputDevice;
+	friend struct std::default_delete<InputFormat>;
+};
+
+/** An input instance (an input format applied to a file or stream) */
+class SR_API Input : public UserOwned<Input>
+{
+public:
+	/** Virtual device associated with this input. */
+	shared_ptr<InputDevice> device();
+	/** Send next stream data.
+	 * @param data Next stream data.
+	 * @param length Length of data. */
+	void send(void *data, size_t length);
+	/** Signal end of input data. */
+	void end();
+private:
+	Input(shared_ptr<Context> context, const struct sr_input *structure);
+	~Input();
+	const struct sr_input *_structure;
+	shared_ptr<Context> _context;
+	unique_ptr<InputDevice> _device;
+
+	friend class Context;
+	friend class InputFormat;
+	friend struct std::default_delete<Input>;
+};
+
+/** A virtual device associated with an input */
+class SR_API InputDevice :
+	public ParentOwned<InputDevice, Input>,
+	public Device
+{
+private:
+	InputDevice(shared_ptr<Input> input, struct sr_dev_inst *sdi);
+	~InputDevice();
+	shared_ptr<Device> get_shared_from_this();
+	shared_ptr<Input> _input;
+	friend class Input;
+	friend struct std::default_delete<InputDevice>;
+};
+
+/** An option used by an output format */
+class SR_API Option : public UserOwned<Option>
+{
+public:
+	/** Short name of this option suitable for command line usage. */
+	string id() const;
+	/** Short name of this option suitable for GUI usage. */
+	string name() const;
+	/** Description of this option in a sentence. */
+	string description() const;
+	/** Default value for this option. */
+	Glib::VariantBase default_value() const;
+	/** Possible values for this option, if a limited set. */
+	vector<Glib::VariantBase> values() const;
+private:
+	Option(const struct sr_option *structure,
+		shared_ptr<const struct sr_option *> structure_array);
+	~Option();
+	const struct sr_option *_structure;
+	shared_ptr<const struct sr_option *> _structure_array;
+
+	friend class InputFormat;
+	friend class OutputFormat;
+	friend struct std::default_delete<Option>;
+};
+
+/** An output format supported by the library */
+class SR_API OutputFormat :
+	public ParentOwned<OutputFormat, Context>
+{
+public:
+	/** Name of this output format. */
+	string name() const;
+	/** Description of this output format. */
+	string description() const;
+	/** A list of preferred file name extensions for this file format.
+         * @note This list is a recommendation only. */
+	vector<string> extensions() const;
+	/** Options supported by this output format. */
+	map<string, shared_ptr<Option> > options();
+	/** Create an output using this format.
+	 * @param device Device to output for.
+	 * @param options Mapping of (option name, value) pairs. */
+	shared_ptr<Output> create_output(shared_ptr<Device> device,
+		map<string, Glib::VariantBase> options = map<string, Glib::VariantBase>());
+	/** Create an output using this format.
+	 * @param filename Name of destination file.
+	 * @param device Device to output for.
+	 * @param options Mapping of (option name, value) pairs. */
+	shared_ptr<Output> create_output(string filename,
+		shared_ptr<Device> device,
+		map<string, Glib::VariantBase> options = map<string, Glib::VariantBase>());
+	/**
+	 * Checks whether a given flag is set.
+	 * @param flag Flag to check
+	 * @return true if flag is set for this module
+	 * @see sr_output_flags
+	 */
+	bool test_flag(const OutputFlag *flag) const;
+private:
+	explicit OutputFormat(const struct sr_output_module *structure);
+	~OutputFormat();
+
+	const struct sr_output_module *_structure;
+
+	friend class Context;
+	friend class Output;
+	friend struct std::default_delete<OutputFormat>;
+};
+
+/** An output instance (an output format applied to a device) */
+class SR_API Output : public UserOwned<Output>
+{
+public:
+	/** Update output with data from the given packet.
+	 * @param packet Packet to handle. */
+	string receive(shared_ptr<Packet> packet);
+private:
+	Output(shared_ptr<OutputFormat> format, shared_ptr<Device> device);
+	Output(shared_ptr<OutputFormat> format,
+		shared_ptr<Device> device, map<string, Glib::VariantBase> options);
+	Output(string filename, shared_ptr<OutputFormat> format,
+		shared_ptr<Device> device, map<string, Glib::VariantBase> options);
+	~Output();
+
+	const struct sr_output *_structure;
+	const shared_ptr<OutputFormat> _format;
+	const shared_ptr<Device> _device;
+	const map<string, Glib::VariantBase> _options;
+
+	friend class OutputFormat;
+	friend struct std::default_delete<Output>;
+};
+
+/** Base class for objects which wrap an enumeration value from libsigrok */
+template <class Class, typename Enum> class SR_API EnumValue
+{
+public:
+	/** The integer constant associated with this value. */
+	int id() const
+	{
+		return static_cast<int>(_id);
+	}
+	/** The name associated with this value. */
+	string name() const
+	{
+		return _name;
+	}
+	/** Get value associated with a given integer constant. */
+	static const Class *get(int id)
+	{
+		const auto pos = _values.find(static_cast<Enum>(id));
+		if (pos == _values.end())
+			throw Error(SR_ERR_ARG);
+		return pos->second;
+	}
+	/** Get possible values. */
+	static std::vector<const Class *> values()
+	{
+		std::vector<const Class *> result;
+		for (auto entry : _values)
+			result.push_back(entry.second);
+		return result;
+	}
+protected:
+	EnumValue(Enum id, const char name[]) : _id(id), _name(name)
+	{
+	}
+	~EnumValue()
+	{
+	}
+private:
+	static const std::map<const Enum, const Class * const> _values;
+	const Enum _id;
+	const string _name;
+};
+
+}
+
+#include <libsigrokcxx/enums.hpp>
+
+#endif
diff --git a/bindings/cxx/libsigrokcxx.pc.in b/bindings/cxx/libsigrokcxx.pc.in
new file mode 100644
index 0000000..10a92f2
--- /dev/null
+++ b/bindings/cxx/libsigrokcxx.pc.in
@@ -0,0 +1,13 @@
+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}
diff --git a/Doxyfile b/bindings/java/Doxyfile
similarity index 97%
copy from Doxyfile
copy to bindings/java/Doxyfile
index a42748b..381136a 100644
--- a/Doxyfile
+++ b/bindings/java/Doxyfile
@@ -32,33 +32,33 @@ DOXYFILE_ENCODING      = UTF-8
 # title of most generated pages and in a few other places.
 # The default value is: My Project.
 
-PROJECT_NAME           = "libsigrok"
+PROJECT_NAME           = "sigrok-java"
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = "0.3.0"
+PROJECT_NUMBER         = "unreleased development snapshot"
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
 # quick idea about the purpose of the project. Keep the description short.
 
-PROJECT_BRIEF          = "sigrok hardware access and backend library"
+PROJECT_BRIEF          = "Java bindings for libsigrok"
 
 # With the PROJECT_LOGO tag one can specify an logo or icon that is included in
 # the documentation. The maximum height of the logo should not exceed 55 pixels
 # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
 # to the output directory.
 
-PROJECT_LOGO           = contrib/sigrok-logo-notext.png
+PROJECT_LOGO           = ../../contrib/sigrok-logo-notext.png
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
 # into which the generated documentation will be written. If a relative path is
 # entered, it will be relative to the location where doxygen was started. If
 # left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = doxy
+OUTPUT_DIRECTORY       = $(BUILDDIR)doxy
 
 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
 # directories (in 2 levels) under the output directory of each output format and
@@ -117,7 +117,7 @@ ABBREVIATE_BRIEF       =
 # description.
 # The default value is: NO.
 
-ALWAYS_DETAILED_SEC    = NO
+ALWAYS_DETAILED_SEC    = YES
 
 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
 # inherited members of a class in the documentation of that class as if those
@@ -234,7 +234,7 @@ TCL_SUBST              =
 # members will be omitted, etc.
 # The default value is: NO.
 
-OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_FOR_C  = NO
 
 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
 # Python sources only. Doxygen will then generate output that is more tailored
@@ -242,7 +242,7 @@ OPTIMIZE_OUTPUT_FOR_C  = YES
 # qualified scopes will look different, etc.
 # The default value is: NO.
 
-OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_OUTPUT_JAVA   = YES
 
 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
 # sources. Doxygen will then generate output that is tailored for Fortran.
@@ -398,7 +398,7 @@ LOOKUP_CACHE_SIZE      = 0
 # normally produced when WARNINGS is set to YES.
 # The default value is: NO.
 
-EXTRACT_ALL            = YES
+EXTRACT_ALL            = NO
 
 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
 # be included in the documentation.
@@ -449,7 +449,7 @@ EXTRACT_ANON_NSPACES   = NO
 # section is generated. This option has no effect if EXTRACT_ALL is enabled.
 # The default value is: NO.
 
-HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_MEMBERS     = YES
 
 # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
 # undocumented classes that are normally visible in the class hierarchy. If set
@@ -457,14 +457,14 @@ HIDE_UNDOC_MEMBERS     = NO
 # no effect if EXTRACT_ALL is enabled.
 # The default value is: NO.
 
-HIDE_UNDOC_CLASSES     = NO
+HIDE_UNDOC_CLASSES     = YES
 
 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
 # (class|struct|union) declarations. If set to NO these declarations will be
 # included in the documentation.
 # The default value is: NO.
 
-HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_FRIEND_COMPOUNDS  = YES
 
 # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
 # documentation blocks found inside the body of a function. If set to NO these
@@ -526,7 +526,7 @@ INLINE_INFO            = YES
 # name. If set to NO the members will appear in declaration order.
 # The default value is: YES.
 
-SORT_MEMBER_DOCS       = YES
+SORT_MEMBER_DOCS       = NO
 
 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
 # descriptions of file, namespace and class members alphabetically by member
@@ -617,21 +617,21 @@ ENABLED_SECTIONS       =
 # documentation regardless of this setting.
 # Minimum value: 0, maximum value: 10000, default value: 30.
 
-MAX_INITIALIZER_LINES  = 30
+MAX_INITIALIZER_LINES  = 0
 
 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
 # the bottom of the documentation of classes and structs. If set to YES the list
 # will mention the files that were used to generate the documentation.
 # The default value is: YES.
 
-SHOW_USED_FILES        = YES
+SHOW_USED_FILES        = NO
 
 # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
 # will remove the Files entry from the Quick Index and from the Folder Tree View
 # (if specified).
 # The default value is: YES.
 
-SHOW_FILES             = YES
+SHOW_FILES             = NO
 
 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
 # page. This will remove the Namespaces entry from the Quick Index and from the
@@ -683,7 +683,7 @@ CITE_BIB_FILES         =
 # messages are off.
 # The default value is: NO.
 
-QUIET                  = NO
+QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
 # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
@@ -692,14 +692,14 @@ QUIET                  = NO
 # Tip: Turn warnings on while writing the documentation.
 # The default value is: YES.
 
-WARNINGS               = YES
+WARNINGS               = NO
 
 # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
 # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
 # will automatically be disabled.
 # The default value is: YES.
 
-WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_UNDOCUMENTED   = NO
 
 # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
 # potential errors in the documentation, such as not documenting some parameters
@@ -707,7 +707,7 @@ WARN_IF_UNDOCUMENTED   = YES
 # markup commands wrongly.
 # The default value is: YES.
 
-WARN_IF_DOC_ERROR      = YES
+WARN_IF_DOC_ERROR      = NO
 
 # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
 # are documented, but have no documentation for their parameters or return
@@ -715,7 +715,7 @@ WARN_IF_DOC_ERROR      = YES
 # documentation, but not about the absence of documentation.
 # The default value is: NO.
 
-WARN_NO_PARAMDOC       = YES
+WARN_NO_PARAMDOC       = NO
 
 # The WARN_FORMAT tag determines the format of the warning messages that doxygen
 # can produce. The string should contain the $file, $line, and $text tags, which
@@ -743,7 +743,7 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = .
+INPUT                  = org/sigrok/core $(BUILDDIR)org/sigrok/core
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -763,7 +763,7 @@ INPUT_ENCODING         = UTF-8
 # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
 # *.qsf, *.as and *.js.
 
-FILE_PATTERNS          =
+FILE_PATTERNS          = *.java
 
 # The RECURSIVE tag can be used to specify whether or not subdirectories should
 # be searched for input files as well.
@@ -778,7 +778,7 @@ RECURSIVE              = YES
 # Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE                = config.h libsigrok-internal.h session_driver.c std.c
+EXCLUDE                =
 
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
@@ -794,23 +794,7 @@ EXCLUDE_SYMLINKS       = NO
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories for example use the pattern */test/*
 
-#
-# Ignore the following files and directories (see also EXCLUDE above):
-#  - config.h: Non-public stuff, the file doesn't get installed.
-#  - libsigrok-internal.h: Non-public stuff, the file doesn't get installed.
-#  - session_driver.c: Special driver for "virtual" devices, non-public.
-#  - std.c: Non-public helpers, no public API stuff in there.
-#  - hardware/*: Only driver-specific stuff, no public API stuff in there.
-#  - input/*: Only input.c contains public API, everything else doesn't.
-#  - output/*: Only output.c contains public API, everything else doesn't.
-#  - tests/*: Unit tests, no public API stuff in there.
-#  - bindings/*: Language bindings, no public API stuff in there.
-#  - doxy/*: Potentially already generated docs, should not be scanned.
-#
-EXCLUDE_PATTERNS       = */hardware/* */input/* */output/* */tests/*
-EXCLUDE_PATTERNS      += */bindings/*
-EXCLUDE_PATTERNS      += */doxy/*
-INPUT                 += input/input.c output/output.c
+EXCLUDE_PATTERNS       =
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
@@ -821,7 +805,7 @@ INPUT                 += input/input.c output/output.c
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories use the pattern */test/*
 
-EXCLUDE_SYMBOLS        =
+EXCLUDE_SYMBOLS        = *Map *Vector
 
 # The EXAMPLE_PATH tag can be used to specify one or more files or directories
 # that contain example code fragments that are included (see the \include
@@ -908,7 +892,7 @@ USE_MDFILE_AS_MAINPAGE =
 # also VERBATIM_HEADERS is set to NO.
 # The default value is: NO.
 
-SOURCE_BROWSER         = YES
+SOURCE_BROWSER         = NO
 
 # Setting the INLINE_SOURCES tag to YES will include the body of functions,
 # classes and enums directly into the documentation.
@@ -981,7 +965,7 @@ USE_HTAGS              = NO
 # See also: Section \class.
 # The default value is: YES.
 
-VERBATIM_HEADERS       = YES
+VERBATIM_HEADERS       = NO
 
 #---------------------------------------------------------------------------
 # Configuration options related to the alphabetical class index
@@ -992,7 +976,7 @@ VERBATIM_HEADERS       = YES
 # classes, structs, unions or interfaces.
 # The default value is: YES.
 
-ALPHABETICAL_INDEX     = YES
+ALPHABETICAL_INDEX     = NO
 
 # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
 # which the alphabetical index list will be split.
@@ -1824,7 +1808,7 @@ XML_DTD                =
 # The default value is: YES.
 # This tag requires that the tag GENERATE_XML is set to YES.
 
-XML_PROGRAMLISTING     = YES
+XML_PROGRAMLISTING     = NO
 
 #---------------------------------------------------------------------------
 # Configuration options related to the DOCBOOK output
@@ -1902,7 +1886,7 @@ PERLMOD_MAKEVAR_PREFIX =
 # C-preprocessor directives found in the sources and include files.
 # The default value is: YES.
 
-ENABLE_PREPROCESSING   = YES
+ENABLE_PREPROCESSING   = NO
 
 # If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
 # in the source code. If set to NO only conditional compilation will be
@@ -1951,8 +1935,7 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-# This gets rid of the SR_API function prefix in the Doxygen output.
-PREDEFINED             = SR_API=
+PREDEFINED             =
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
@@ -2057,7 +2040,7 @@ DIA_PATH               =
 # and usage relations if the target is undocumented or is not a class.
 # The default value is: YES.
 
-HIDE_UNDOC_RELATIONS   = NO
+HIDE_UNDOC_RELATIONS   = YES
 
 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
 # available from the path. This tool is part of Graphviz (see:
@@ -2117,7 +2100,7 @@ CLASS_GRAPH            = YES
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-COLLABORATION_GRAPH    = YES
+COLLABORATION_GRAPH    = NO
 
 # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
 # groups, showing the direct groups dependencies.
@@ -2153,7 +2136,7 @@ UML_LIMIT_NUM_FIELDS   = 10
 # The default value is: NO.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-TEMPLATE_RELATIONS     = NO
+TEMPLATE_RELATIONS     = YES
 
 # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
 # YES then doxygen will generate a graph for each documented file showing the
diff --git a/bindings/java/org/sigrok/core/classes/classes.i b/bindings/java/org/sigrok/core/classes/classes.i
new file mode 100644
index 0000000..129654d
--- /dev/null
+++ b/bindings/java/org/sigrok/core/classes/classes.i
@@ -0,0 +1,378 @@
+%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->CallObjectMethod($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,
+    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")
+  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"
+
diff --git a/bindings/java/org/sigrok/core/interfaces/DatafeedCallback.java b/bindings/java/org/sigrok/core/interfaces/DatafeedCallback.java
new file mode 100644
index 0000000..2c6036a
--- /dev/null
+++ b/bindings/java/org/sigrok/core/interfaces/DatafeedCallback.java
@@ -0,0 +1,9 @@
+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);
+}
diff --git a/bindings/java/org/sigrok/core/interfaces/LogCallback.java b/bindings/java/org/sigrok/core/interfaces/LogCallback.java
new file mode 100644
index 0000000..24ef4ed
--- /dev/null
+++ b/bindings/java/org/sigrok/core/interfaces/LogCallback.java
@@ -0,0 +1,8 @@
+package org.sigrok.core.interfaces;
+
+import org.sigrok.core.classes.LogLevel;
+
+public interface LogCallback 
+{
+    public void run(LogLevel loglevel, String message);
+}
diff --git a/Doxyfile b/bindings/python/Doxyfile
similarity index 97%
copy from Doxyfile
copy to bindings/python/Doxyfile
index a42748b..3c45847 100644
--- a/Doxyfile
+++ b/bindings/python/Doxyfile
@@ -32,33 +32,33 @@ DOXYFILE_ENCODING      = UTF-8
 # title of most generated pages and in a few other places.
 # The default value is: My Project.
 
-PROJECT_NAME           = "libsigrok"
+PROJECT_NAME           = "pysigrok"
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = "0.3.0"
+PROJECT_NUMBER         = "unreleased development snapshot"
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
 # quick idea about the purpose of the project. Keep the description short.
 
-PROJECT_BRIEF          = "sigrok hardware access and backend library"
+PROJECT_BRIEF          = "Python bindings for libsigrok"
 
 # With the PROJECT_LOGO tag one can specify an logo or icon that is included in
 # the documentation. The maximum height of the logo should not exceed 55 pixels
 # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
 # to the output directory.
 
-PROJECT_LOGO           = contrib/sigrok-logo-notext.png
+PROJECT_LOGO           = ../../contrib/sigrok-logo-notext.png
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
 # into which the generated documentation will be written. If a relative path is
 # entered, it will be relative to the location where doxygen was started. If
 # left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = doxy
+OUTPUT_DIRECTORY       = $(BUILDDIR)doxy
 
 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
 # directories (in 2 levels) under the output directory of each output format and
@@ -117,7 +117,7 @@ ABBREVIATE_BRIEF       =
 # description.
 # The default value is: NO.
 
-ALWAYS_DETAILED_SEC    = NO
+ALWAYS_DETAILED_SEC    = YES
 
 # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
 # inherited members of a class in the documentation of that class as if those
@@ -153,7 +153,7 @@ STRIP_FROM_PATH        =
 # specify the list of include paths that are normally passed to the compiler
 # using the -I flag.
 
-STRIP_FROM_INC_PATH    =
+STRIP_FROM_INC_PATH    = include/
 
 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
 # less readable) file names. This can be useful is your file systems doesn't
@@ -234,7 +234,7 @@ TCL_SUBST              =
 # members will be omitted, etc.
 # The default value is: NO.
 
-OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_FOR_C  = NO
 
 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
 # Python sources only. Doxygen will then generate output that is more tailored
@@ -242,7 +242,7 @@ OPTIMIZE_OUTPUT_FOR_C  = YES
 # qualified scopes will look different, etc.
 # The default value is: NO.
 
-OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_OUTPUT_JAVA   = YES
 
 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
 # sources. Doxygen will then generate output that is tailored for Fortran.
@@ -298,7 +298,7 @@ AUTOLINK_SUPPORT       = YES
 # diagrams that involve STL classes more complete and accurate.
 # The default value is: NO.
 
-BUILTIN_STL_SUPPORT    = NO
+BUILTIN_STL_SUPPORT    = YES
 
 # If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
@@ -398,7 +398,7 @@ LOOKUP_CACHE_SIZE      = 0
 # normally produced when WARNINGS is set to YES.
 # The default value is: NO.
 
-EXTRACT_ALL            = YES
+EXTRACT_ALL            = NO
 
 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
 # be included in the documentation.
@@ -457,14 +457,14 @@ HIDE_UNDOC_MEMBERS     = NO
 # no effect if EXTRACT_ALL is enabled.
 # The default value is: NO.
 
-HIDE_UNDOC_CLASSES     = NO
+HIDE_UNDOC_CLASSES     = YES
 
 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
 # (class|struct|union) declarations. If set to NO these declarations will be
 # included in the documentation.
 # The default value is: NO.
 
-HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_FRIEND_COMPOUNDS  = YES
 
 # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
 # documentation blocks found inside the body of a function. If set to NO these
@@ -526,7 +526,7 @@ INLINE_INFO            = YES
 # name. If set to NO the members will appear in declaration order.
 # The default value is: YES.
 
-SORT_MEMBER_DOCS       = YES
+SORT_MEMBER_DOCS       = NO
 
 # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
 # descriptions of file, namespace and class members alphabetically by member
@@ -617,7 +617,7 @@ ENABLED_SECTIONS       =
 # documentation regardless of this setting.
 # Minimum value: 0, maximum value: 10000, default value: 30.
 
-MAX_INITIALIZER_LINES  = 30
+MAX_INITIALIZER_LINES  = 0
 
 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
 # the bottom of the documentation of classes and structs. If set to YES the list
@@ -631,7 +631,7 @@ SHOW_USED_FILES        = YES
 # (if specified).
 # The default value is: YES.
 
-SHOW_FILES             = YES
+SHOW_FILES             = NO
 
 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
 # page. This will remove the Namespaces entry from the Quick Index and from the
@@ -683,7 +683,7 @@ CITE_BIB_FILES         =
 # messages are off.
 # The default value is: NO.
 
-QUIET                  = NO
+QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
 # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
@@ -692,14 +692,14 @@ QUIET                  = NO
 # Tip: Turn warnings on while writing the documentation.
 # The default value is: YES.
 
-WARNINGS               = YES
+WARNINGS               = NO
 
 # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
 # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
 # will automatically be disabled.
 # The default value is: YES.
 
-WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_UNDOCUMENTED   = NO
 
 # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
 # potential errors in the documentation, such as not documenting some parameters
@@ -707,7 +707,7 @@ WARN_IF_UNDOCUMENTED   = YES
 # markup commands wrongly.
 # The default value is: YES.
 
-WARN_IF_DOC_ERROR      = YES
+WARN_IF_DOC_ERROR      = NO
 
 # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
 # are documented, but have no documentation for their parameters or return
@@ -715,7 +715,7 @@ WARN_IF_DOC_ERROR      = YES
 # documentation, but not about the absence of documentation.
 # The default value is: NO.
 
-WARN_NO_PARAMDOC       = YES
+WARN_NO_PARAMDOC       = NO
 
 # The WARN_FORMAT tag determines the format of the warning messages that doxygen
 # can produce. The string should contain the $file, $line, and $text tags, which
@@ -743,7 +743,7 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = .
+INPUT                  = $(BUILDDIR)sigrok/core/classes.py
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -769,7 +769,7 @@ FILE_PATTERNS          =
 # be searched for input files as well.
 # The default value is: NO.
 
-RECURSIVE              = YES
+RECURSIVE              = NO
 
 # The EXCLUDE tag can be used to specify files and/or directories that should be
 # excluded from the INPUT source files. This way you can easily exclude a
@@ -778,7 +778,7 @@ RECURSIVE              = YES
 # Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE                = config.h libsigrok-internal.h session_driver.c std.c
+EXCLUDE                =
 
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
@@ -794,23 +794,7 @@ EXCLUDE_SYMLINKS       = NO
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories for example use the pattern */test/*
 
-#
-# Ignore the following files and directories (see also EXCLUDE above):
-#  - config.h: Non-public stuff, the file doesn't get installed.
-#  - libsigrok-internal.h: Non-public stuff, the file doesn't get installed.
-#  - session_driver.c: Special driver for "virtual" devices, non-public.
-#  - std.c: Non-public helpers, no public API stuff in there.
-#  - hardware/*: Only driver-specific stuff, no public API stuff in there.
-#  - input/*: Only input.c contains public API, everything else doesn't.
-#  - output/*: Only output.c contains public API, everything else doesn't.
-#  - tests/*: Unit tests, no public API stuff in there.
-#  - bindings/*: Language bindings, no public API stuff in there.
-#  - doxy/*: Potentially already generated docs, should not be scanned.
-#
-EXCLUDE_PATTERNS       = */hardware/* */input/* */output/* */tests/*
-EXCLUDE_PATTERNS      += */bindings/*
-EXCLUDE_PATTERNS      += */doxy/*
-INPUT                 += input/input.c output/output.c
+EXCLUDE_PATTERNS       =
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
@@ -821,7 +805,7 @@ INPUT                 += input/input.c output/output.c
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories use the pattern */test/*
 
-EXCLUDE_SYMBOLS        =
+EXCLUDE_SYMBOLS        = *Map *Vector _*
 
 # The EXAMPLE_PATH tag can be used to specify one or more files or directories
 # that contain example code fragments that are included (see the \include
@@ -864,7 +848,7 @@ IMAGE_PATH             =
 # code is scanned, but not when the output code is generated. If lines are added
 # or removed, the anchors will not be placed correctly.
 
-INPUT_FILTER           =
+INPUT_FILTER           = doxypy
 
 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
 # basis. Doxygen will compare the file name with each pattern and apply the
@@ -908,7 +892,7 @@ USE_MDFILE_AS_MAINPAGE =
 # also VERBATIM_HEADERS is set to NO.
 # The default value is: NO.
 
-SOURCE_BROWSER         = YES
+SOURCE_BROWSER         = NO
 
 # Setting the INLINE_SOURCES tag to YES will include the body of functions,
 # classes and enums directly into the documentation.
@@ -981,7 +965,7 @@ USE_HTAGS              = NO
 # See also: Section \class.
 # The default value is: YES.
 
-VERBATIM_HEADERS       = YES
+VERBATIM_HEADERS       = NO
 
 #---------------------------------------------------------------------------
 # Configuration options related to the alphabetical class index
@@ -992,7 +976,7 @@ VERBATIM_HEADERS       = YES
 # classes, structs, unions or interfaces.
 # The default value is: YES.
 
-ALPHABETICAL_INDEX     = YES
+ALPHABETICAL_INDEX     = NO
 
 # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
 # which the alphabetical index list will be split.
@@ -1824,7 +1808,7 @@ XML_DTD                =
 # The default value is: YES.
 # This tag requires that the tag GENERATE_XML is set to YES.
 
-XML_PROGRAMLISTING     = YES
+XML_PROGRAMLISTING     = NO
 
 #---------------------------------------------------------------------------
 # Configuration options related to the DOCBOOK output
@@ -1902,7 +1886,7 @@ PERLMOD_MAKEVAR_PREFIX =
 # C-preprocessor directives found in the sources and include files.
 # The default value is: YES.
 
-ENABLE_PREPROCESSING   = YES
+ENABLE_PREPROCESSING   = NO
 
 # If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
 # in the source code. If set to NO only conditional compilation will be
@@ -1951,8 +1935,7 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-# This gets rid of the SR_API function prefix in the Doxygen output.
-PREDEFINED             = SR_API=
+PREDEFINED             =
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
@@ -2057,7 +2040,7 @@ DIA_PATH               =
 # and usage relations if the target is undocumented or is not a class.
 # The default value is: YES.
 
-HIDE_UNDOC_RELATIONS   = NO
+HIDE_UNDOC_RELATIONS   = YES
 
 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
 # available from the path. This tool is part of Graphviz (see:
@@ -2117,7 +2100,7 @@ CLASS_GRAPH            = YES
 # The default value is: YES.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-COLLABORATION_GRAPH    = YES
+COLLABORATION_GRAPH    = NO
 
 # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
 # groups, showing the direct groups dependencies.
@@ -2153,7 +2136,7 @@ UML_LIMIT_NUM_FIELDS   = 10
 # The default value is: NO.
 # This tag requires that the tag HAVE_DOT is set to YES.
 
-TEMPLATE_RELATIONS     = NO
+TEMPLATE_RELATIONS     = YES
 
 # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
 # YES then doxygen will generate a graph for each documented file showing the
diff --git a/bindings/python/setup.py b/bindings/python/setup.py
new file mode 100644
index 0000000..dd8a3d7
--- /dev/null
+++ b/bindings/python/setup.py
@@ -0,0 +1,100 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2013 Martin Ling <martin-sigrok at 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 setuptools import setup, find_packages, Extension
+from distutils.command.build_py import build_py as _build_py
+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__))
+os.chdir('bindings/python')
+srcdir = os.path.relpath(srcdir)
+srcdir_parent = os.path.normpath(os.path.join(srcdir, '..'))
+
+# 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(
+    name = 'libsigrok',
+    namespace_packages = ['sigrok'],
+    packages = find_packages(srcdir),
+    version = os.environ.get('VERSION'),
+    description = "libsigrok API wrapper",
+    zip_safe = False,
+    ext_modules = [
+        Extension('sigrok.core._classes',
+            sources = [vpath('sigrok/core/classes.i')],
+            extra_compile_args = ['-Wno-uninitialized'],
+            include_dirs = includes,
+            library_dirs = libdirs,
+            libraries = libs)
+    ],
+    cmdclass = {'build_py': build_py, 'build_ext': build_ext},
+)
diff --git a/bindings/python/sigrok/__init__.py b/bindings/python/sigrok/__init__.py
new file mode 100644
index 0000000..b1f900a
--- /dev/null
+++ b/bindings/python/sigrok/__init__.py
@@ -0,0 +1,20 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2013 Martin Ling <martin-sigrok at 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/>.
+##
+
+__import__("pkg_resources").declare_namespace(__name__)
diff --git a/bindings/python/sigrok/core/__init__.py b/bindings/python/sigrok/core/__init__.py
new file mode 100644
index 0000000..20752b8
--- /dev/null
+++ b/bindings/python/sigrok/core/__init__.py
@@ -0,0 +1,21 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2013 Martin Ling <martin-sigrok at 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 . import classes
+from .classes import *
diff --git a/bindings/python/sigrok/core/classes.i b/bindings/python/sigrok/core/classes.i
new file mode 100644
index 0000000..2afd9cf
--- /dev/null
+++ b/bindings/python/sigrok/core/classes.i
@@ -0,0 +1,551 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Martin Ling <martin-sigrok at 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 <stdio.h>
+#include <pygobject.h>
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <numpy/arrayobject.h>
+
+PyObject *PyGObject_lib;
+PyObject *GLib;
+
+#include "config.h"
+
+#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);
+}
+
+/* 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
+        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::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)
+}
+}
+
+%include "doc_end.i"
diff --git a/bindings/swig/classes.i b/bindings/swig/classes.i
new file mode 100644
index 0000000..523995d
--- /dev/null
+++ b/bindings/swig/classes.i
@@ -0,0 +1,220 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Martin Ling <martin-sigrok at 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>
diff --git a/bindings/swig/doc.py b/bindings/swig/doc.py
new file mode 100644
index 0000000..e8767af
--- /dev/null
+++ b/bindings/swig/doc.py
@@ -0,0 +1,114 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2014 Martin Ling <martin-sigrok at 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))
diff --git a/bindings/swig/templates.i b/bindings/swig/templates.i
new file mode 100644
index 0000000..1baaf0f
--- /dev/null
+++ b/bindings/swig/templates.i
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Martin Ling <martin-sigrok at 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> >;
diff --git a/config.h.in b/config.h.in
index 79530cd..8dd116b 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1,128 +1,206 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
-#ifndef SR_CONFIG_H
-#define SR_CONFIG_H    /* To stop multiple inclusions. */
-
 /* Define if building universal (internal helper macro) */
 #undef AC_APPLE_UNIVERSAL_BUILD
 
+/* The canonical host libsigrok will run on. */
+#undef CONF_HOST
+
+/* Build-time version of libftdi. */
+#undef CONF_LIBFTDI_VERSION
+
+/* Build-time version of libgpib. */
+#undef CONF_LIBGPIB_VERSION
+
+/* Build-time version of librevisa. */
+#undef CONF_LIBREVISA_VERSION
+
+/* Build-time version of libserialport. */
+#undef CONF_LIBSERIALPORT_VERSION
+
+/* Build-time version of libusb. */
+#undef CONF_LIBUSB_1_0_VERSION
+
+/* Build-time version of libzip. */
+#undef CONF_LIBZIP_VERSION
+
+/* define if the compiler supports basic C++11 syntax */
+#undef HAVE_CXX11
+
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #undef HAVE_DLFCN_H
 
-/* Agilent DMM support */
+/* Whether to support Agilent DMM device. */
 #undef HAVE_HW_AGILENT_DMM
 
-/* APPA 55II support */
+/* Whether to support Appa 55II device. */
 #undef HAVE_HW_APPA_55II
 
-/* ASIX SIGMA/SIGMA2 support */
+/* Whether to support ASIX SIGMA/SIGMA2 device. */
 #undef HAVE_HW_ASIX_SIGMA
 
-/* Atten PPS3xxx support */
+/* Whether to support Atten PPS3xxx device. */
 #undef HAVE_HW_ATTEN_PPS3XXX
 
-/* Brymen BM86X support */
+/* Whether to support BayLibre ACME device. */
+#undef HAVE_HW_BAYLIBRE_ACME
+
+/* Whether to support BeagleLogic device. */
+#undef HAVE_HW_BEAGLELOGIC
+
+/* Whether to support Brymen BM86x device. */
 #undef HAVE_HW_BRYMEN_BM86X
 
-/* Brymen DMM support */
+/* Whether to support Brymen DMM device. */
 #undef HAVE_HW_BRYMEN_DMM
 
-/* CEM DT-885x support */
+/* Whether to support CEM DT-885x device. */
 #undef HAVE_HW_CEM_DT_885X
 
-/* Center 3xx support */
+/* Whether to support Center 3xx device. */
 #undef HAVE_HW_CENTER_3XX
 
-/* ChronoVu LA support */
+/* Whether to support ChronoVu LA device. */
 #undef HAVE_HW_CHRONOVU_LA
 
-/* Colead SLM support */
+/* Whether to support Colead SLM device. */
 #undef HAVE_HW_COLEAD_SLM
 
-/* Conrad DIGI 35 CPU support */
+/* Whether to support Conrad DIGI 35 CPU device. */
 #undef HAVE_HW_CONRAD_DIGI_35_CPU
 
-/* Demo driver support */
+/* Whether to support demo device. */
 #undef HAVE_HW_DEMO
 
-/* Fluke DMM support */
+/* Whether to support DER EE DE-5000 device. */
+#undef HAVE_HW_DEREE_DE5000
+
+/* Whether to support Fluke DMM device. */
 #undef HAVE_HW_FLUKE_DMM
 
-/* fx2lafw support */
+/* Whether to support fx2lafw device. */
 #undef HAVE_HW_FX2LAFW
 
-/* gmc-mh-1x-2x support */
+/* Whether to support GMC MH 1x/2x device. */
 #undef HAVE_HW_GMC_MH_1X_2X
 
-/* Hameg HMO support */
+/* Whether to support GW Instek GDS-800 device. */
+#undef HAVE_HW_GWINSTEK_GDS_800
+
+/* Whether to support Hameg HMO device. */
 #undef HAVE_HW_HAMEG_HMO
 
-/* Hantek DSO support */
+/* Whether to support Hantek DSO device. */
 #undef HAVE_HW_HANTEK_DSO
 
-/* IKALOGIC Scanalogic-2 support */
+/* Whether to support Hung-Chang DSO-2100 device. */
+#undef HAVE_HW_HUNG_CHANG_DSO_2100
+
+/* Whether to support Ikalogic Scanalogic-2 device. */
 #undef HAVE_HW_IKALOGIC_SCANALOGIC2
 
-/* IKALOGIC ScanaPLUS support */
+/* Whether to support Ikalogic Scanaplus device. */
 #undef HAVE_HW_IKALOGIC_SCANAPLUS
 
-/* Kecheng KC-330B support */
+/* Whether to support Kecheng KC-330B device. */
 #undef HAVE_HW_KECHENG_KC_330B
 
-/* Lascar EL-USB support */
+/* Whether to support KERN scale device. */
+#undef HAVE_HW_KERN_SCALE
+
+/* Whether to support Korad KAxxxxP device. */
+#undef HAVE_HW_KORAD_KAXXXXP
+
+/* Whether to support Lascar EL-USB device. */
 #undef HAVE_HW_LASCAR_EL_USB
 
-/* MIC 985xx support */
+/* Whether to support LeCroy LogicStudio device. */
+#undef HAVE_HW_LECROY_LOGICSTUDIO
+
+/* Whether to support Manson HCS-3xxx device. */
+#undef HAVE_HW_MANSON_HCS_3XXX
+
+/* Whether to support maynuo-m97 device. */
+#undef HAVE_HW_MAYNUO_M97
+
+/* Whether to support MIC 985xx device. */
 #undef HAVE_HW_MIC_985XX
 
-/* Norma DMM support */
+/* Whether to support Motech LPS 30x device. */
+#undef HAVE_HW_MOTECH_LPS_30X
+
+/* Whether to support Norma DMM device. */
 #undef HAVE_HW_NORMA_DMM
 
-/* OpenBench Logic Sniffer (OLS) support */
-#undef HAVE_HW_OLS
+/* Whether to support OpenBench Logic Sniffer device. */
+#undef HAVE_HW_OPENBENCH_LOGIC_SNIFFER
+
+/* Whether to support Pipistrello-OLS device. */
+#undef HAVE_HW_PIPISTRELLO_OLS
 
-/* Rigol DS support */
+/* Whether to support Rigol DS device. */
 #undef HAVE_HW_RIGOL_DS
 
-/* Saleae Logic16 support */
+/* Whether to support Saleae Logic16 device. */
 #undef HAVE_HW_SALEAE_LOGIC16
 
-/* Serial DMM support */
+/* Whether to support SCPI PPS device. */
+#undef HAVE_HW_SCPI_PPS
+
+/* Whether to support serial DMM device. */
 #undef HAVE_HW_SERIAL_DMM
 
-/* Sysclk LWLA support */
+/* Whether to support Sysclk LWLA device. */
 #undef HAVE_HW_SYSCLK_LWLA
 
-/* Teleinfo support */
+/* Whether to support Teleinfo device. */
 #undef HAVE_HW_TELEINFO
 
-/* Tondaj SL-814 support */
+/* Whether to support Testo device. */
+#undef HAVE_HW_TESTO
+
+/* Whether to support Tondaj SL-814 device. */
 #undef HAVE_HW_TONDAJ_SL_814
 
-/* UNI-T DMM support */
+/* Whether to support UNI-T DMM device. */
 #undef HAVE_HW_UNI_T_DMM
 
-/* UNI-T UT32x support */
+/* Whether to support UNI-T UT32x device. */
 #undef HAVE_HW_UNI_T_UT32X
 
-/* Victor DMM support */
+/* Whether to support Victor DMM device. */
 #undef HAVE_HW_VICTOR_DMM
 
-/* ZEROPLUS Logic Cube support */
+/* Whether to support Yokogawa DL/DLM device. */
+#undef HAVE_HW_YOKOGAWA_DLM
+
+/* Whether to support ZEROPLUS Logic Cube device. */
 #undef HAVE_HW_ZEROPLUS_LOGIC_CUBE
 
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
-/* Specifies whether we have librevisa. */
+/* Whether libftdi is available. */
+#undef HAVE_LIBFTDI
+
+/* Whether libgpib is available. */
+#undef HAVE_LIBGPIB
+
+/* Whether libieee1284 is available. */
+#undef HAVE_LIBIEEE1284
+
+/* Whether librevisa is available. */
 #undef HAVE_LIBREVISA
 
-/* Specifies whether we have libserialport. */
+/* Whether libserialport is available. */
 #undef HAVE_LIBSERIALPORT
 
-/* Specifies whether we have a libusb.h header. */
+/* Whether libusb is available. */
 #undef HAVE_LIBUSB_1_0
 
+/* Define to 1 if the system has the type `libusb_os_handle'. */
+#undef HAVE_LIBUSB_OS_HANDLE
+
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
@@ -135,28 +213,40 @@
 /* Define to 1 if you have the <stdlib.h> header file. */
 #undef HAVE_STDLIB_H
 
+/* Specifies whether we have the stoi and stod functions. */
+#undef HAVE_STOI_STOD
+
 /* Define to 1 if you have the <strings.h> header file. */
 #undef HAVE_STRINGS_H
 
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#undef HAVE_SYS_MMAN_H
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
+/* Define to 1 if you have the <sys/timerfd.h> header file. */
+#undef HAVE_SYS_TIMERFD_H
+
 /* Define to 1 if you have the <sys/types.h> header file. */
 #undef HAVE_SYS_TYPES_H
 
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the `zip_discard' function. */
+#undef HAVE_ZIP_DISCARD
+
 /* Define to the sub-directory in which libtool stores uninstalled libraries.
    */
 #undef LT_OBJDIR
 
-/* Name of package */
-#undef PACKAGE
-
 /* Define to the address where bug reports for this package should be sent. */
 #undef PACKAGE_BUGREPORT
 
@@ -175,12 +265,36 @@
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
+/* Whether last argument to pyg_flags_get_value() is signed. */
+#undef PYGOBJECT_FLAGS_SIGNED
+
+/* Binary age of libsigrok. */
+#undef SR_LIB_VERSION_AGE
+
+/* Binary version of libsigrok. */
+#undef SR_LIB_VERSION_CURRENT
+
+/* Binary revision of libsigrok. */
+#undef SR_LIB_VERSION_REVISION
+
+/* Binary version triple of libsigrok. */
+#undef SR_LIB_VERSION_STRING
+
+/* Major version number of libsigrok. */
+#undef SR_PACKAGE_VERSION_MAJOR
+
+/* Micro version number of libsigrok. */
+#undef SR_PACKAGE_VERSION_MICRO
+
+/* Minor version number of libsigrok. */
+#undef SR_PACKAGE_VERSION_MINOR
+
+/* Version of libsigrok. */
+#undef SR_PACKAGE_VERSION_STRING
+
 /* Define to 1 if you have the ANSI C header files. */
 #undef STDC_HEADERS
 
-/* Version number of package */
-#undef VERSION
-
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */
 #if defined AC_APPLE_UNIVERSAL_BUILD
@@ -193,4 +307,18 @@
 # endif
 #endif
 
-#endif /* SR_CONFIG_H */
+/* Enable large inode numbers on Mac OS X 10.5.  */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* The targeted POSIX standard. */
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200112L
+#endif
diff --git a/configure b/configure
index f3a3310..c67a570 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for libsigrok 0.3.0.
+# Generated by GNU Autoconf 2.69 for libsigrok 0.4.0.
 #
 # Report bugs to <sigrok-devel at lists.sourceforge.net>.
 #
@@ -566,6 +566,66 @@ as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
 
 SHELL=${CONFIG_SHELL-/bin/sh}
 
+as_awk_strverscmp='
+  # Use only awk features that work with 7th edition Unix awk (1978).
+  # My, what an old awk you have, Mr. Solaris!
+  END {
+    while (length(v1) && length(v2)) {
+      # Set d1 to be the next thing to compare from v1, and likewise for d2.
+      # Normally this is a single character, but if v1 and v2 contain digits,
+      # compare them as integers and fractions as strverscmp does.
+      if (v1 ~ /^[0-9]/ && v2 ~ /^[0-9]/) {
+	# Split v1 and v2 into their leading digit string components d1 and d2,
+	# and advance v1 and v2 past the leading digit strings.
+	for (len1 = 1; substr(v1, len1 + 1) ~ /^[0-9]/; len1++) continue
+	for (len2 = 1; substr(v2, len2 + 1) ~ /^[0-9]/; len2++) continue
+	d1 = substr(v1, 1, len1); v1 = substr(v1, len1 + 1)
+	d2 = substr(v2, 1, len2); v2 = substr(v2, len2 + 1)
+	if (d1 ~ /^0/) {
+	  if (d2 ~ /^0/) {
+	    # Compare two fractions.
+	    while (d1 ~ /^0/ && d2 ~ /^0/) {
+	      d1 = substr(d1, 2); len1--
+	      d2 = substr(d2, 2); len2--
+	    }
+	    if (len1 != len2 && ! (len1 && len2 && substr(d1, 1, 1) == substr(d2, 1, 1))) {
+	      # The two components differ in length, and the common prefix
+	      # contains only leading zeros.  Consider the longer to be less.
+	      d1 = -len1
+	      d2 = -len2
+	    } else {
+	      # Otherwise, compare as strings.
+	      d1 = "x" d1
+	      d2 = "x" d2
+	    }
+	  } else {
+	    # A fraction is less than an integer.
+	    exit 1
+	  }
+	} else {
+	  if (d2 ~ /^0/) {
+	    # An integer is greater than a fraction.
+	    exit 2
+	  } else {
+	    # Compare two integers.
+	    d1 += 0
+	    d2 += 0
+	  }
+	}
+      } else {
+	# The normal case, without worrying about digits.
+	d1 = substr(v1, 1, 1); v1 = substr(v1, 2)
+	d2 = substr(v2, 1, 1); v2 = substr(v2, 2)
+      }
+      if (d1 < d2) exit 1
+      if (d1 > d2) exit 2
+    }
+    # Beware Solaris /usr/xgp4/bin/awk (at least through Solaris 10),
+    # which mishandles some comparisons of empty strings to integers.
+    if (length(v2)) exit 1
+    if (length(v1)) exit 2
+  }
+'
 
 test -n "$DJDIR" || exec 7<&0 </dev/null
 exec 6>&1
@@ -590,8 +650,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='libsigrok'
 PACKAGE_TARNAME='libsigrok'
-PACKAGE_VERSION='0.3.0'
-PACKAGE_STRING='libsigrok 0.3.0'
+PACKAGE_VERSION='0.4.0'
+PACKAGE_STRING='libsigrok 0.4.0'
 PACKAGE_BUGREPORT='sigrok-devel at lists.sourceforge.net'
 PACKAGE_URL='http://www.sigrok.org'
 
@@ -635,15 +695,49 @@ ac_subst_vars='am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
-SR_PACKAGE_VERSION
-SR_PACKAGE_VERSION_MICRO
-SR_PACKAGE_VERSION_MINOR
-SR_PACKAGE_VERSION_MAJOR
-AM_LIBTOOLFLAGS
-MAKEFLAGS
-FIRMWARE_DIR
+RBSIGROK_EXTDIR
+RBSIGROK_LIBS
+RBSIGROK_CFLAGS
+PYSIGROK_LIBS
+PYSIGROK_CFLAGS
+LIBSIGROKCXX_LIBS
+LIBSIGROKCXX_CFLAGS
+TESTS_LIBS
+TESTS_CFLAGS
+LIBSIGROK_LIBS
+LIBSIGROK_CFLAGS
+SR_PKGLIBS
+BINDINGS_JAVA_FALSE
+BINDINGS_JAVA_TRUE
+JNI_CPPFLAGS
+_ACJNI_JAVAC
+JAVAC
+HAVE_JAVAC
+BINDINGS_RUBY_FALSE
+BINDINGS_RUBY_TRUE
+RUBY_DLEXT
+RUBY
+BINDINGS_PYTHON_FALSE
+BINDINGS_PYTHON_TRUE
+SWIG
+BINDINGS_CXX_FALSE
+BINDINGS_CXX_TRUE
+pkgpyexecdir
+pyexecdir
+pkgpythondir
+pythondir
+PYTHON_PLATFORM
+PYTHON_EXEC_PREFIX
+PYTHON_PREFIX
+PYTHON_VERSION
+PYTHON
+HAVE_DOXYGEN
+HAVE_CXX11
+SR_WXXFLAGS
 HW_ZEROPLUS_LOGIC_CUBE_FALSE
 HW_ZEROPLUS_LOGIC_CUBE_TRUE
+HW_YOKOGAWA_DLM_FALSE
+HW_YOKOGAWA_DLM_TRUE
 HW_VICTOR_DMM_FALSE
 HW_VICTOR_DMM_TRUE
 HW_UNI_T_UT32X_FALSE
@@ -652,34 +746,56 @@ HW_UNI_T_DMM_FALSE
 HW_UNI_T_DMM_TRUE
 HW_TONDAJ_SL_814_FALSE
 HW_TONDAJ_SL_814_TRUE
+HW_TESTO_FALSE
+HW_TESTO_TRUE
 HW_TELEINFO_FALSE
 HW_TELEINFO_TRUE
 HW_SYSCLK_LWLA_FALSE
 HW_SYSCLK_LWLA_TRUE
 HW_SERIAL_DMM_FALSE
 HW_SERIAL_DMM_TRUE
+HW_SCPI_PPS_FALSE
+HW_SCPI_PPS_TRUE
 HW_SALEAE_LOGIC16_FALSE
 HW_SALEAE_LOGIC16_TRUE
 HW_RIGOL_DS_FALSE
 HW_RIGOL_DS_TRUE
-HW_OLS_FALSE
-HW_OLS_TRUE
+HW_PIPISTRELLO_OLS_FALSE
+HW_PIPISTRELLO_OLS_TRUE
+HW_OPENBENCH_LOGIC_SNIFFER_FALSE
+HW_OPENBENCH_LOGIC_SNIFFER_TRUE
 HW_NORMA_DMM_FALSE
 HW_NORMA_DMM_TRUE
+HW_MOTECH_LPS_30X_FALSE
+HW_MOTECH_LPS_30X_TRUE
 HW_MIC_985XX_FALSE
 HW_MIC_985XX_TRUE
+HW_MAYNUO_M97_FALSE
+HW_MAYNUO_M97_TRUE
+HW_MANSON_HCS_3XXX_FALSE
+HW_MANSON_HCS_3XXX_TRUE
+HW_LECROY_LOGICSTUDIO_FALSE
+HW_LECROY_LOGICSTUDIO_TRUE
 HW_LASCAR_EL_USB_FALSE
 HW_LASCAR_EL_USB_TRUE
+HW_KORAD_KAXXXXP_FALSE
+HW_KORAD_KAXXXXP_TRUE
+HW_KERN_SCALE_FALSE
+HW_KERN_SCALE_TRUE
 HW_KECHENG_KC_330B_FALSE
 HW_KECHENG_KC_330B_TRUE
 HW_IKALOGIC_SCANAPLUS_FALSE
 HW_IKALOGIC_SCANAPLUS_TRUE
 HW_IKALOGIC_SCANALOGIC2_FALSE
 HW_IKALOGIC_SCANALOGIC2_TRUE
-HW_HAMEG_HMO_FALSE
-HW_HAMEG_HMO_TRUE
+HW_HUNG_CHANG_DSO_2100_FALSE
+HW_HUNG_CHANG_DSO_2100_TRUE
 HW_HANTEK_DSO_FALSE
 HW_HANTEK_DSO_TRUE
+HW_HAMEG_HMO_FALSE
+HW_HAMEG_HMO_TRUE
+HW_GWINSTEK_GDS_800_FALSE
+HW_GWINSTEK_GDS_800_TRUE
 HW_GMC_MH_1X_2X_FALSE
 HW_GMC_MH_1X_2X_TRUE
 HW_FX2LAFW_FALSE
@@ -688,6 +804,8 @@ HW_FLUKE_DMM_FALSE
 HW_FLUKE_DMM_TRUE
 HW_DEMO_FALSE
 HW_DEMO_TRUE
+HW_DEREE_DE5000_FALSE
+HW_DEREE_DE5000_TRUE
 HW_CONRAD_DIGI_35_CPU_FALSE
 HW_CONRAD_DIGI_35_CPU_TRUE
 HW_COLEAD_SLM_FALSE
@@ -702,6 +820,10 @@ HW_BRYMEN_DMM_FALSE
 HW_BRYMEN_DMM_TRUE
 HW_BRYMEN_BM86X_FALSE
 HW_BRYMEN_BM86X_TRUE
+HW_BEAGLELOGIC_FALSE
+HW_BEAGLELOGIC_TRUE
+HW_BAYLIBRE_ACME_FALSE
+HW_BAYLIBRE_ACME_TRUE
 HW_ATTEN_PPS3XXX_FALSE
 HW_ATTEN_PPS3XXX_TRUE
 HW_ASIX_SIGMA_FALSE
@@ -710,43 +832,31 @@ HW_APPA_55II_FALSE
 HW_APPA_55II_TRUE
 HW_AGILENT_DMM_FALSE
 HW_AGILENT_DMM_TRUE
-SR_PKGLIBS
+NEED_RPC_FALSE
+NEED_RPC_TRUE
+SR_EXTRA_LIBS
+SR_WFLAGS
+SR_EXTRA_CFLAGS
 HAVE_CHECK_FALSE
 HAVE_CHECK_TRUE
-check_LIBS
-check_CFLAGS
-libftdi_LIBS
-libftdi_CFLAGS
-NEED_USB_FALSE
-NEED_USB_TRUE
-libusb_LIBS
-libusb_CFLAGS
+NEED_GPIB_FALSE
+NEED_GPIB_TRUE
 NEED_VISA_FALSE
 NEED_VISA_TRUE
-librevisa_LIBS
-librevisa_CFLAGS
+NEED_USB_FALSE
+NEED_USB_TRUE
 NEED_SERIAL_FALSE
 NEED_SERIAL_TRUE
-libserialport_LIBS
-libserialport_CFLAGS
-libzip_LIBS
-libzip_CFLAGS
-GLIB_COMPILE_RESOURCES
-GLIB_MKENUMS
-GOBJECT_QUERY
-GLIB_GENMARSHAL
-GLIB_LIBS
-GLIB_CFLAGS
-NEED_RPC_FALSE
-NEED_RPC_TRUE
-SR_LIB_LDFLAGS
-SR_LIB_VERSION
-SR_LIB_VERSION_AGE
-SR_LIB_VERSION_REVISION
-SR_LIB_VERSION_CURRENT
 PKG_CONFIG_LIBDIR
 PKG_CONFIG_PATH
 PKG_CONFIG
+WIN32_FALSE
+WIN32_TRUE
+SR_LIB_VERSION
+SR_PACKAGE_VERSION
+CONFIG_STATUS_DEPENDENCIES
+CXXCPP
+CPP
 OTOOL64
 OTOOL
 LIPO
@@ -764,6 +874,15 @@ FGREP
 EGREP
 GREP
 SED
+LIBTOOL
+ORDER
+LN_S
+am__fastdepCXX_FALSE
+am__fastdepCXX_TRUE
+CXXDEPMODE
+ac_ct_CXX
+CXXFLAGS
+CXX
 host_os
 host_vendor
 host_cpu
@@ -772,9 +891,6 @@ build_os
 build_vendor
 build_cpu
 build
-LIBTOOL
-LN_S
-CPP
 am__fastdepCC_FALSE
 am__fastdepCC_TRUE
 CCDEPMODE
@@ -840,6 +956,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -871,11 +988,21 @@ enable_fast_install
 with_gnu_ld
 with_sysroot
 enable_libtool_lock
+with_libserialport
+with_libftdi
+with_libusb
+with_librevisa
+with_libgpib
+with_libieee1284
+enable_warnings
+enable_largefile
 enable_all_drivers
 enable_agilent_dmm
 enable_appa_55ii
 enable_asix_sigma
 enable_atten_pps3xxx
+enable_baylibre_acme
+enable_beaglelogic
 enable_brymen_bm86x
 enable_brymen_dmm
 enable_cem_dt_885x
@@ -883,30 +1010,48 @@ enable_center_3xx
 enable_chronovu_la
 enable_colead_slm
 enable_conrad_digi_35_cpu
+enable_deree_de5000
 enable_demo
 enable_fluke_dmm
 enable_fx2lafw
 enable_gmc_mh_1x_2x
+enable_gwinstek_gds_800
 enable_hameg_hmo
 enable_hantek_dso
+enable_hung_chang_dso_2100
 enable_ikalogic_scanalogic2
 enable_ikalogic_scanaplus
 enable_kecheng_kc_330b
+enable_kern_scale
+enable_korad_kaxxxxp
 enable_lascar_el_usb
+enable_lecroy_logicstudio
+enable_manson_hcs_3xxx
+enable_maynuo_m97
 enable_mic_985xx
+enable_motech_lps_30x
 enable_norma_dmm
-enable_ols
+enable_openbench_logic_sniffer
+enable_pipistrello_ols
 enable_rigol_ds
 enable_saleae_logic16
+enable_scpi_pps
 enable_serial_dmm
 enable_sysclk_lwla
 enable_teleinfo
+enable_testo
 enable_tondaj_sl_814
 enable_uni_t_dmm
 enable_uni_t_ut32x
 enable_victor_dmm
+enable_yokogawa_dlm
 enable_zeroplus_logic_cube
-enable_glibtest
+enable_bindings
+enable_cxx
+enable_python
+enable_ruby
+enable_java
+with_jni_include_path
 '
       ac_precious_vars='build_alias
 host_alias
@@ -916,22 +1061,25 @@ CFLAGS
 LDFLAGS
 LIBS
 CPPFLAGS
+CXX
+CXXFLAGS
+CCC
 CPP
+CXXCPP
 PKG_CONFIG
 PKG_CONFIG_PATH
 PKG_CONFIG_LIBDIR
-libzip_CFLAGS
-libzip_LIBS
-libserialport_CFLAGS
-libserialport_LIBS
-librevisa_CFLAGS
-librevisa_LIBS
-libusb_CFLAGS
-libusb_LIBS
-libftdi_CFLAGS
-libftdi_LIBS
-check_CFLAGS
-check_LIBS'
+PYTHON
+LIBSIGROK_CFLAGS
+LIBSIGROK_LIBS
+TESTS_CFLAGS
+TESTS_LIBS
+LIBSIGROKCXX_CFLAGS
+LIBSIGROKCXX_LIBS
+PYSIGROK_CFLAGS
+PYSIGROK_LIBS
+RBSIGROK_CFLAGS
+RBSIGROK_LIBS'
 
 
 # Initialize some variables set by options.
@@ -970,6 +1118,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1222,6 +1371,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1359,7 +1517,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1472,7 +1630,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures libsigrok 0.3.0 to adapt to many kinds of systems.
+\`configure' configures libsigrok 0.4.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1512,6 +1670,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -1542,7 +1701,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of libsigrok 0.3.0:";;
+     short | recursive ) echo "Configuration of libsigrok 0.4.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1561,48 +1720,75 @@ Optional Features:
   --enable-fast-install[=PKGS]
                           optimize for fast installation [default=yes]
   --disable-libtool-lock  avoid locking (might break parallel builds)
+  --enable-warnings[=min|max|fatal|no]
+                          set compile pedantry level [default=max]
+  --disable-largefile     omit support for large files
   --enable-all-drivers    enable all drivers by default [default=yes]
-  --enable-agilent-dmm    enable Agilent DMM support [default=yes]
-  --enable-appa-55ii      enable APPA 55II support [default=yes]
-  --enable-asix-sigma     enable ASIX SIGMA/SIGMA2 support [default=yes]
-  --enable-atten-pps3xxx  enable Atten PPS3xxx support [default=yes]
-  --enable-brymen-bm86x   enable Brymen BM86X support [default=yes]
-  --enable-brymen-dmm     enable Brymen DMM support [default=yes]
-  --enable-cem-dt-885x    enable CEM DT-885x support [default=yes]
-  --enable-center-3xx     enable Center 3xx support [default=yes]
-  --enable-chronovu-la    enable ChronoVu LA support [default=yes]
-  --enable-colead-slm     enable Colead SLM support [default=yes]
+  --enable-agilent-dmm    enable Agilent DMM support
+  --enable-appa-55ii      enable Appa 55II support
+  --enable-asix-sigma     enable ASIX SIGMA/SIGMA2 support
+  --enable-atten-pps3xxx  enable Atten PPS3xxx support
+  --enable-baylibre-acme  enable BayLibre ACME support
+  --enable-beaglelogic    enable BeagleLogic support
+  --enable-brymen-bm86x   enable Brymen BM86x support
+  --enable-brymen-dmm     enable Brymen DMM support
+  --enable-cem-dt-885x    enable CEM DT-885x support
+  --enable-center-3xx     enable Center 3xx support
+  --enable-chronovu-la    enable ChronoVu LA support
+  --enable-colead-slm     enable Colead SLM support
   --enable-conrad-digi-35-cpu
-                          enable Conrad DIGI 35 CPU support [default=yes]
-  --enable-demo           enable demo driver support [default=yes]
-  --enable-fluke-dmm      enable Fluke DMM support [default=yes]
-  --enable-fx2lafw        enable fx2lafw support (for FX2 LAs). [default=yes]
-  --enable-gmc-mh-1x-2x   enable gmc-mh-1x-2x support [default=yes]
-  --enable-hameg-hmo      enable Hameg HMO support [default=yes]
-  --enable-hantek-dso     enable Hantek DSO support [default=yes]
+                          enable Conrad DIGI 35 CPU support
+  --enable-deree-de5000   enable DER EE DE-5000 support
+  --enable-demo           enable demo support
+  --enable-fluke-dmm      enable Fluke DMM support
+  --enable-fx2lafw        enable fx2lafw support
+  --enable-gmc-mh-1x-2x   enable GMC MH 1x/2x support
+  --enable-gwinstek-gds-800
+                          enable GW Instek GDS-800 support
+  --enable-hameg-hmo      enable Hameg HMO support
+  --enable-hantek-dso     enable Hantek DSO support
+  --enable-hung-chang-dso-2100
+                          enable Hung-Chang DSO-2100 support
   --enable-ikalogic-scanalogic2
-                          enable IKALOGIC Scanalogic-2 support [default=yes]
+                          enable Ikalogic Scanalogic-2 support
   --enable-ikalogic-scanaplus
-                          enable IKALOGIC ScanaPLUS support [default=yes]
+                          enable Ikalogic Scanaplus support
   --enable-kecheng-kc-330b
-                          enable Kecheng KC-330B support [default=yes]
-  --enable-lascar-el-usb  enable Lascar EL-USB support [default=yes]
-  --enable-mic-985xx      enable MIC 985xx support [default=yes]
-  --enable-norma-dmm      enable Norma DMM support [default=yes]
-  --enable-ols            enable OpenBench Logic Sniffer (OLS) support
-                          [default=yes]
-  --enable-rigol-ds       enable Rigol DS support [default=yes]
-  --enable-saleae-logic16 enable Saleae Logic16 support [default=yes]
-  --enable-serial-dmm     enable serial DMM support [default=yes]
-  --enable-sysclk-lwla    enable Sysclk LWLA support [default=yes]
-  --enable-teleinfo       enable Teleinfo support [default=yes]
-  --enable-tondaj-sl-814  enable Tondaj SL-814 support [default=yes]
-  --enable-uni-t-dmm      enable UNI-T DMM support [default=yes]
-  --enable-uni-t-ut32x    enable UNI-T UT32x support [default=yes]
-  --enable-victor-dmm     enable victor-dmm support [default=yes]
+                          enable Kecheng KC-330B support
+  --enable-kern-scale     enable KERN scale support
+  --enable-korad-kaxxxxp  enable Korad KAxxxxP support
+  --enable-lascar-el-usb  enable Lascar EL-USB support
+  --enable-lecroy-logicstudio
+                          enable LeCroy LogicStudio support
+  --enable-manson-hcs-3xxx
+                          enable Manson HCS-3xxx support
+  --enable-maynuo-m97     enable maynuo-m97 support
+  --enable-mic-985xx      enable MIC 985xx support
+  --enable-motech-lps-30x enable Motech LPS 30x support
+  --enable-norma-dmm      enable Norma DMM support
+  --enable-openbench-logic-sniffer
+                          enable OpenBench Logic Sniffer support
+  --enable-pipistrello-ols
+                          enable Pipistrello-OLS support
+  --enable-rigol-ds       enable Rigol DS support
+  --enable-saleae-logic16 enable Saleae Logic16 support
+  --enable-scpi-pps       enable SCPI PPS support
+  --enable-serial-dmm     enable serial DMM support
+  --enable-sysclk-lwla    enable Sysclk LWLA support
+  --enable-teleinfo       enable Teleinfo support
+  --enable-testo          enable Testo support
+  --enable-tondaj-sl-814  enable Tondaj SL-814 support
+  --enable-uni-t-dmm      enable UNI-T DMM support
+  --enable-uni-t-ut32x    enable UNI-T UT32x support
+  --enable-victor-dmm     enable Victor DMM support
+  --enable-yokogawa-dlm   enable Yokogawa DL/DLM support
   --enable-zeroplus-logic-cube
-                          enable ZEROPLUS Logic Cube support [default=yes]
-  --disable-glibtest      do not try to compile and run a test GLIB program
+                          enable ZEROPLUS Logic Cube support
+  --enable-bindings       build language bindings [default=yes]
+  --enable-cxx            build C++ bindings [default=yes]
+  --enable-python         build Python bindings [default=yes]
+  --enable-ruby           build Ruby bindings [default=yes]
+  --enable-java           build Java bindings [default=yes]
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -1612,6 +1798,14 @@ Optional Packages:
   --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
   --with-sysroot=DIR Search for dependent libraries within DIR
                         (or the compiler's sysroot if not specified).
+  --without-libserialport disable libserialport support [default=detect]
+  --without-libftdi       disable libftdi support [default=detect]
+  --without-libusb        disable libusb support [default=detect]
+  --without-librevisa     disable librevisa support [default=detect]
+  --without-libgpib       disable libgpib support [default=detect]
+  --without-libieee1284   disable libieee1284 support [default=detect]
+  --with-jni-include-path=DIR-LIST (space-separated)
+                          specify JNI include directories [default=detect]
 
 Some influential environment variables:
   CC          C compiler command
@@ -1621,33 +1815,35 @@ Some influential environment variables:
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
               you have headers in a nonstandard directory <include dir>
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
   CPP         C preprocessor
+  CXXCPP      C++ preprocessor
   PKG_CONFIG  path to pkg-config utility
   PKG_CONFIG_PATH
               directories to add to pkg-config's search path
   PKG_CONFIG_LIBDIR
               path overriding pkg-config's built-in search path
-  libzip_CFLAGS
-              C compiler flags for libzip, overriding pkg-config
-  libzip_LIBS linker flags for libzip, overriding pkg-config
-  libserialport_CFLAGS
-              C compiler flags for libserialport, overriding pkg-config
-  libserialport_LIBS
-              linker flags for libserialport, overriding pkg-config
-  librevisa_CFLAGS
-              C compiler flags for librevisa, overriding pkg-config
-  librevisa_LIBS
-              linker flags for librevisa, overriding pkg-config
-  libusb_CFLAGS
-              C compiler flags for libusb, overriding pkg-config
-  libusb_LIBS linker flags for libusb, overriding pkg-config
-  libftdi_CFLAGS
-              C compiler flags for libftdi, overriding pkg-config
-  libftdi_LIBS
-              linker flags for libftdi, overriding pkg-config
-  check_CFLAGS
-              C compiler flags for check, overriding pkg-config
-  check_LIBS  linker flags for check, overriding pkg-config
+  PYTHON      the Python interpreter
+  LIBSIGROK_CFLAGS
+              C compiler flags for LIBSIGROK, overriding pkg-config
+  LIBSIGROK_LIBS
+              linker flags for LIBSIGROK, overriding pkg-config
+  TESTS_CFLAGS
+              C compiler flags for TESTS, overriding pkg-config
+  TESTS_LIBS  linker flags for TESTS, overriding pkg-config
+  LIBSIGROKCXX_CFLAGS
+              C compiler flags for LIBSIGROKCXX, overriding pkg-config
+  LIBSIGROKCXX_LIBS
+              linker flags for LIBSIGROKCXX, overriding pkg-config
+  PYSIGROK_CFLAGS
+              C compiler flags for PYSIGROK, overriding pkg-config
+  PYSIGROK_LIBS
+              linker flags for PYSIGROK, overriding pkg-config
+  RBSIGROK_CFLAGS
+              C compiler flags for RBSIGROK, overriding pkg-config
+  RBSIGROK_LIBS
+              linker flags for RBSIGROK, overriding pkg-config
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1716,7 +1912,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-libsigrok configure 0.3.0
+libsigrok configure 0.4.0
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1768,20 +1964,21 @@ fi
 
 } # ac_fn_c_try_compile
 
-# ac_fn_c_try_cpp LINENO
-# ----------------------
-# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
-ac_fn_c_try_cpp ()
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
 {
   as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  if { { ac_try="$ac_cpp conftest.$ac_ext"
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
 case "(($ac_try" in
   *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
   *) ac_try_echo=$ac_try;;
 esac
 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
 $as_echo "$ac_try_echo"; } >&5
-  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  (eval "$ac_compile") 2>conftest.err
   ac_status=$?
   if test -s conftest.err; then
     grep -v '^ *+' conftest.err >conftest.er1
@@ -1789,21 +1986,21 @@ $as_echo "$ac_try_echo"; } >&5
     mv -f conftest.er1 conftest.err
   fi
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; } > conftest.i && {
-	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+  test $ac_status = 0; } && {
+	 test -z "$ac_cxx_werror_flag" ||
 	 test ! -s conftest.err
-       }; then :
+       } && test -s conftest.$ac_objext; then :
   ac_retval=0
 else
   $as_echo "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
-    ac_retval=1
+	ac_retval=1
 fi
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
   as_fn_set_status $ac_retval
 
-} # ac_fn_c_try_cpp
+} # ac_fn_cxx_try_compile
 
 # ac_fn_c_try_link LINENO
 # -----------------------
@@ -1882,6 +2079,43 @@ $as_echo "$ac_res" >&6; }
 
 } # ac_fn_c_check_header_compile
 
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
 # ac_fn_c_try_run LINENO
 # ----------------------
 # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
@@ -1990,79 +2224,398 @@ $as_echo "$ac_res" >&6; }
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
 
 } # ac_fn_c_check_func
-cat >config.log <<_ACEOF
-This file contains any messages produced by compilers while
-running configure, to aid debugging if configure makes a mistake.
-
-It was created by libsigrok $as_me 0.3.0, which was
-generated by GNU Autoconf 2.69.  Invocation command line was
-
-  $ $0 $@
 
-_ACEOF
-exec 5>>config.log
+# ac_fn_cxx_try_cpp LINENO
+# ------------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_cpp ()
 {
-cat <<_ASUNAME
-## --------- ##
-## Platform. ##
-## --------- ##
-
-hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
-uname -m = `(uname -m) 2>/dev/null || echo unknown`
-uname -r = `(uname -r) 2>/dev/null || echo unknown`
-uname -s = `(uname -s) 2>/dev/null || echo unknown`
-uname -v = `(uname -v) 2>/dev/null || echo unknown`
-
-/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
-/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
-
-/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
-/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
-/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
-/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
-/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
-/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
-/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+	 test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
 
-_ASUNAME
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
 
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    $as_echo "PATH: $as_dir"
-  done
-IFS=$as_save_IFS
+} # ac_fn_cxx_try_cpp
 
-} >&5
+# ac_fn_cxx_try_link LINENO
+# -------------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
 
-cat >&5 <<_ACEOF
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
 
+} # ac_fn_cxx_try_link
 
-## ----------- ##
-## Core tests. ##
-## ----------- ##
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
 
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
 _ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ------------------------------------------------- ##
+## Report this to sigrok-devel at lists.sourceforge.net ##
+## ------------------------------------------------- ##"
+     ) | sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
 
+} # ac_fn_c_check_header_mongrel
 
-# Keep a trace of the command line.
-# Strip out --no-create and --no-recursion so they do not pile up.
-# Strip out --silent because we don't want to record it for future runs.
-# Also quote any args containing shell meta-characters.
-# Make two passes to allow for proper duplicate-argument suppression.
-ac_configure_args=
-ac_configure_args0=
-ac_configure_args1=
-ac_must_keep_next=false
-for ac_pass in 1 2
-do
-  for ac_arg
-  do
-    case $ac_arg in
-    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
-    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+# ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES
+# ---------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_cxx_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ------------------------------------------------- ##
+## Report this to sigrok-devel at lists.sourceforge.net ##
+## ------------------------------------------------- ##"
+     ) | sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_header_mongrel
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+	 return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+	    return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by libsigrok $as_me 0.4.0, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
     | -silent | --silent | --silen | --sile | --sil)
       continue ;;
     *\'*)
@@ -2342,8 +2895,6 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
-ac_config_headers="$ac_config_headers config.h"
-
 
 ac_aux_dir=
 for ac_dir in autostuff "$srcdir"/autostuff; do
@@ -2374,9 +2925,11 @@ ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
 ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
 
 
+ac_config_headers="$ac_config_headers config.h include/libsigrok/version.h"
+
 
 # We require at least automake 1.11 (needed for 'silent rules').
-am__api_version='1.14'
+am__api_version='1.15'
 
 # Find a good install program.  We prefer a C program (faster),
 # so one script is as good as another.  But avoid the broken or
@@ -2548,8 +3101,8 @@ test "$program_suffix" != NONE &&
 ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
 program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
 
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
 
 if test x"${MISSING+set}" != xset; then
   case $am_aux_dir in
@@ -2568,7 +3121,7 @@ else
 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
 fi
 
-if test x"${install_sh}" != xset; then
+if test x"${install_sh+set}" != xset; then
   case $am_aux_dir in
   *\ * | *\	*)
     install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -2862,18 +3415,9 @@ fi
 
 # Define the identity of the package.
  PACKAGE='libsigrok'
- VERSION='0.3.0'
+ VERSION='0.4.0'
 
 
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE "$PACKAGE"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define VERSION "$VERSION"
-_ACEOF
-
 # Some tools Automake needs.
 
 ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
@@ -2896,8 +3440,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
 mkdir_p='$(MKDIR_P)'
 
-# We need awk for the "check" target.  The system "awk" is bad on
-# some platforms.
+# We need awk for the "check" target (and possibly the TAP driver).  The
+# system "awk" is bad on some platforms.
 # Always define AMTAR for backward compatibility.  Yes, it's still used
 # in the wild :-(  We should find a proper way to deprecate it ...
 AMTAR='$${TAR-tar}'
@@ -4206,13 +4750,77 @@ unknown)
 esac
 
 
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
 
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
 
 
-# Enable more compiler warnings via -Wall and -Wextra. Add -fvisibility=hidden
-# and enforce use of SR_API to explicitly mark all public API functions.
-COMMON_FLAGS="$CFLAGS -Wall -Wextra -fvisibility=hidden"
-CFLAGS="$COMMON_FLAGS -Wmissing-prototypes"
 
 # Checks for programs.
 ac_ext=c
@@ -4941,256 +5549,482 @@ else
 fi
 
 
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
-$as_echo_n "checking how to run the C preprocessor... " >&6; }
-# On Suns, sometimes $CPP names a directory.
-if test -n "$CPP" && test -d "$CPP"; then
-  CPP=
-fi
-if test -z "$CPP"; then
-  if ${ac_cv_prog_CPP+:} false; then :
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-      # Double quotes because CPP needs to be expanded
-    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
-    do
-      ac_preproc_ok=false
-for ac_c_preproc_warn_flag in '' yes
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
 do
-  # Use a header file that comes with gcc, so configuring glibc
-  # with a fresh cross-compiler works.
-  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-  # <limits.h> exists even on freestanding compilers.
-  # On the NeXT, cc -E runs the code through the compiler's parser,
-  # not just through cpp. "Syntax error" is here to catch this case.
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-		     Syntax error
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
 
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
 else
-  # Broken: fails on valid input.
-continue
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
-rm -f conftest.err conftest.i conftest.$ac_ext
 
-  # OK, works on sane cases.  Now check whether nonexistent headers
-  # can be detected and how.
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <ac_nonexistent.h>
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
-  # Broken: success on invalid input.
-continue
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  # Passes both tests.
-ac_preproc_ok=:
-break
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
-rm -f conftest.err conftest.i conftest.$ac_ext
 
+
+  test -n "$ac_ct_CXX" && break
 done
-# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.i conftest.err conftest.$ac_ext
-if $ac_preproc_ok; then :
-  break
-fi
 
-    done
-    ac_cv_prog_CPP=$CPP
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
 
+  fi
 fi
-  CPP=$ac_cv_prog_CPP
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  ac_cv_prog_CPP=$CPP
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
-$as_echo "$CPP" >&6; }
-ac_preproc_ok=false
-for ac_c_preproc_warn_flag in '' yes
-do
-  # Use a header file that comes with gcc, so configuring glibc
-  # with a fresh cross-compiler works.
-  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-  # <limits.h> exists even on freestanding compilers.
-  # On the NeXT, cc -E runs the code through the compiler's parser,
-  # not just through cpp. "Syntax error" is here to catch this case.
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
 #endif
-		     Syntax error
+
+  ;
+  return 0;
+}
 _ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
 
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
 else
-  # Broken: fails on valid input.
-continue
+  GXX=
 fi
-rm -f conftest.err conftest.i conftest.$ac_ext
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
-  # OK, works on sane cases.  Now check whether nonexistent headers
-  # can be detected and how.
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <ac_nonexistent.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
 _ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
-  # Broken: success on invalid input.
-continue
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
 else
-  # Passes both tests.
-ac_preproc_ok=:
-break
-fi
-rm -f conftest.err conftest.i conftest.$ac_ext
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+	 CXXFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
-done
-# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.i conftest.err conftest.$ac_ext
-if $ac_preproc_ok; then :
+int
+main ()
+{
 
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
 else
-  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
-See \`config.log' for more details" "$LINENO" 5; }
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
 fi
-
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
+depcc="$CXX"  am_compiler_list=
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
-$as_echo_n "checking whether ln -s works... " >&6; }
-LN_S=$as_ln_s
-if test "$LN_S" = "ln -s"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CXX_dependencies_compiler_type+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
-$as_echo "no, using $LN_S" >&6; }
-fi
-
-
-# Required for per-target flags or subdir-objects with C sources.
-
-
-# Initialize libtool.
-case `pwd` in
-  *\ * | *\	*)
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
-$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
-esac
-
-
-
-macro_version='2.4.2'
-macro_revision='1.3337'
-
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
 
+  am_cv_CXX_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
 
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
 
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CXX_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
 
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CXX_dependencies_compiler_type=none
+fi
 
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; }
+CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type
 
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then
+  am__fastdepCXX_TRUE=
+  am__fastdepCXX_FALSE='#'
+else
+  am__fastdepCXX_TRUE='#'
+  am__fastdepCXX_FALSE=
+fi
 
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
 
 
+# Required for per-target flags or subdir-objects with C sources.
 
-ltmain="$ac_aux_dir/ltmain.sh"
 
-# Make sure we can run config.sub.
-$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
-  as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+# Set the standard the C library headers should conform to.
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
-$as_echo_n "checking build system type... " >&6; }
-if ${ac_cv_build+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_build_alias=$build_alias
-test "x$ac_build_alias" = x &&
-  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
-test "x$ac_build_alias" = x &&
-  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
-ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
-  as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
 
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
-$as_echo "$ac_cv_build" >&6; }
-case $ac_cv_build in
-*-*-*) ;;
-*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+# Get compiler versions.
+sr_prog_ver=`$CC --version 2>&5 | sed 1q 2>&5`
+case $?:$sr_prog_ver in #(
+  0:*[0-9].[0-9]*) :
+    sr_cc_version=$sr_prog_ver ;; #(
+  *) :
+    sr_cc_version=unknown ;;
+esac
+sr_prog_ver=`$CXX --version 2>&5 | sed 1q 2>&5`
+case $?:$sr_prog_ver in #(
+  0:*[0-9].[0-9]*) :
+    sr_cxx_version=$sr_prog_ver ;; #(
+  *) :
+    sr_cxx_version=unknown ;;
 esac
-build=$ac_cv_build
-ac_save_IFS=$IFS; IFS='-'
-set x $ac_cv_build
-shift
-build_cpu=$1
-build_vendor=$2
-shift; shift
-# Remember, the first character of IFS is used to create $*,
-# except with old shells:
-build_os=$*
-IFS=$ac_save_IFS
-case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
-
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
-$as_echo_n "checking host system type... " >&6; }
-if ${ac_cv_host+:} false; then :
+# Check for optional make features.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE:-make} supports order-only prerequisites" >&5
+$as_echo_n "checking whether ${MAKE:-make} supports order-only prerequisites... " >&6; }
+if ${sr_cv_prog_make_order_only+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-  if test "x$host_alias" = x; then
-  ac_cv_host=$ac_cv_build
+
+cat >conftest.mk <<'_SREOF'
+a: b | c
+a b c: ; @:
+.PHONY: a b c
+_SREOF
+if ${MAKE:-make} -f conftest.mk >&5 2>&5; then :
+  sr_cv_prog_make_order_only=yes
 else
-  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
-    as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+  sr_cv_prog_make_order_only=no
 fi
+rm -f conftest.mk
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
-$as_echo "$ac_cv_host" >&6; }
-case $ac_cv_host in
-*-*-*) ;;
-*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_cv_prog_make_order_only" >&5
+$as_echo "$sr_cv_prog_make_order_only" >&6; }
+if test "x$sr_cv_prog_make_order_only" = xyes; then :
+  ORDER='|'
+else
+  ORDER=
+fi
+
+
+
+# Initialize libtool.
+case `pwd` in
+  *\ * | *\	*)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
 esac
-host=$ac_cv_host
-ac_save_IFS=$IFS; IFS='-'
-set x $ac_cv_host
-shift
-host_cpu=$1
-host_vendor=$2
-shift; shift
-# Remember, the first character of IFS is used to create $*,
-# except with old shells:
-host_os=$*
-IFS=$ac_save_IFS
-case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
 
 
+
+macro_version='2.4.2'
+macro_revision='1.3337'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
 # Backslashify metacharacters that are still active within
 # double-quoted strings.
 sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
@@ -8387,29 +9221,166 @@ $as_echo "$lt_cv_ld_force_load" >&6; }
     ;;
   esac
 
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
-$as_echo_n "checking for ANSI C header files... " >&6; }
-if ${ac_cv_header_stdc+:} false; then :
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
   $as_echo_n "(cached) " >&6
 else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <float.h>
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
 _ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_header_stdc=yes
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
 else
   ac_cv_header_stdc=no
 fi
@@ -8533,6 +9504,17 @@ done
 
 
 
+func_stripname_cnf ()
+{
+  case ${2} in
+  .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+  *)  func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+  esac
+} # func_stripname_cnf
+
+
+
+
 
 # Set options
 
@@ -12521,2221 +13503,8122 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 CC="$lt_save_CC"
 
+      if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
+    ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
+    (test "X$CXX" != "Xg++"))) ; then
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5
+$as_echo_n "checking how to run the C++ preprocessor... " >&6; }
+if test -z "$CXXCPP"; then
+  if ${ac_cv_prog_CXXCPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CXXCPP needs to be expanded
+    for CXXCPP in "$CXX -E" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
 
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
 
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
 
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
 
+    done
+    ac_cv_prog_CXXCPP=$CXXCPP
 
+fi
+  CXXCPP=$ac_cv_prog_CXXCPP
+else
+  ac_cv_prog_CXXCPP=$CXXCPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5
+$as_echo "$CXXCPP" >&6; }
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
 
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
 
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
 
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
 
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
 
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
+else
+  _lt_caught_CXX_error=yes
+fi
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+archive_cmds_need_lc_CXX=no
+allow_undefined_flag_CXX=
+always_export_symbols_CXX=no
+archive_expsym_cmds_CXX=
+compiler_needs_object_CXX=no
+export_dynamic_flag_spec_CXX=
+hardcode_direct_CXX=no
+hardcode_direct_absolute_CXX=no
+hardcode_libdir_flag_spec_CXX=
+hardcode_libdir_separator_CXX=
+hardcode_minus_L_CXX=no
+hardcode_shlibpath_var_CXX=unsupported
+hardcode_automatic_CXX=no
+inherit_rpath_CXX=no
+module_cmds_CXX=
+module_expsym_cmds_CXX=
+link_all_deplibs_CXX=unknown
+old_archive_cmds_CXX=$old_archive_cmds
+reload_flag_CXX=$reload_flag
+reload_cmds_CXX=$reload_cmds
+no_undefined_flag_CXX=
+whole_archive_flag_spec_CXX=
+enable_shared_with_static_runtimes_CXX=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+objext_CXX=$objext
 
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_caught_CXX_error" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="int some_variable = 0;"
 
+  # Code to be used in simple link tests
+  lt_simple_link_test_code='int main(int, char *[]) { return(0); }'
 
-        ac_config_commands="$ac_config_commands libtool"
-
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
 
 
 
-# Only expand once:
 
 
 
-# Initialize pkg-config.
-# We require at least 0.22, as "Requires.private" behaviour changed there.
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
 
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
 
+# Allow CC to be a program name with arguments.
+compiler=$CC
 
 
+  # save warnings/boilerplate of simple test code
+  ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
 
+  ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
 
 
-if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
-	if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
-set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_PKG_CONFIG+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  case $PKG_CONFIG in
-  [\\/]* | ?:[\\/]*)
-  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
-  ;;
-  *)
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
+  # Allow CC to be a program name with arguments.
+  lt_save_CC=$CC
+  lt_save_CFLAGS=$CFLAGS
+  lt_save_LD=$LD
+  lt_save_GCC=$GCC
+  GCC=$GXX
+  lt_save_with_gnu_ld=$with_gnu_ld
+  lt_save_path_LD=$lt_cv_path_LD
+  if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+    lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+  else
+    $as_unset lt_cv_prog_gnu_ld
+  fi
+  if test -n "${lt_cv_path_LDCXX+set}"; then
+    lt_cv_path_LD=$lt_cv_path_LDCXX
+  else
+    $as_unset lt_cv_path_LD
   fi
+  test -z "${LDCXX+set}" || LD=$LDCXX
+  CC=${CXX-"c++"}
+  CFLAGS=$CXXFLAGS
+  compiler=$CC
+  compiler_CXX=$CC
+  for cc_temp in $compiler""; do
+  case $cc_temp in
+    compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+    distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
 done
-  done
-IFS=$as_save_IFS
+cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
 
-  ;;
-esac
-fi
-PKG_CONFIG=$ac_cv_path_PKG_CONFIG
-if test -n "$PKG_CONFIG"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
-$as_echo "$PKG_CONFIG" >&6; }
+
+  if test -n "$compiler"; then
+    # We don't want -fno-exception when compiling C++ code, so set the
+    # no_builtin_flag separately
+    if test "$GXX" = yes; then
+      lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin'
+    else
+      lt_prog_compiler_no_builtin_flag_CXX=
+    fi
+
+    if test "$GXX" = yes; then
+      # Set up default GNU C++ configuration
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+  withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
 else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+  with_gnu_ld=no
 fi
 
-
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | ?:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
 fi
-if test -z "$ac_cv_path_PKG_CONFIG"; then
-  ac_pt_PKG_CONFIG=$PKG_CONFIG
-  # Extract the first word of "pkg-config", so it can be a program name with args.
-set dummy pkg-config; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+if ${lt_cv_path_LD+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-  case $ac_pt_PKG_CONFIG in
-  [\\/]* | ?:[\\/]*)
-  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
-  ;;
-  *)
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
+  if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test "$with_gnu_ld" != no && break
+	;;
+      *)
+	test "$with_gnu_ld" != yes && break
+	;;
+      esac
+    fi
   done
-IFS=$as_save_IFS
-
-  ;;
-esac
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
 fi
-ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
-if test -n "$ac_pt_PKG_CONFIG"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
-$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
 else
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 fi
-
-  if test "x$ac_pt_PKG_CONFIG" = x; then
-    PKG_CONFIG=""
-  else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
-    PKG_CONFIG=$ac_pt_PKG_CONFIG
-  fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
-fi
-
-fi
-if test -n "$PKG_CONFIG"; then
-	_pkg_min_version=0.22
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
-$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
-	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
-		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	else
-		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-		PKG_CONFIG=""
-	fi
+  # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
 
-# Library version for libsigrok (NOT the same as the package version).
-# Carefully read the libtool docs before updating these numbers!
-# The algorithm for determining which number to change (and how) is nontrivial!
-# http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
-SR_LIB_VERSION_CURRENT=2
-SR_LIB_VERSION_REVISION=0
-SR_LIB_VERSION_AGE=0
-SR_LIB_VERSION="$SR_LIB_VERSION_CURRENT:$SR_LIB_VERSION_REVISION:$SR_LIB_VERSION_AGE"
-SR_LIB_LDFLAGS="-version-info $SR_LIB_VERSION"
 
 
 
 
 
 
-# Hardware support '--enable' options.
+      # Check if GNU C++ uses GNU ld as the underlying linker, since the
+      # archiving commands below assume that GNU ld is being used.
+      if test "$with_gnu_ld" = yes; then
+        archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
 
-# Check whether --enable-all-drivers was given.
-if test "${enable_all_drivers+set}" = set; then :
-  enableval=$enable_all_drivers; HW_ENABLED_DEFAULT="$enableval"
-else
-  HW_ENABLED_DEFAULT="yes"
-fi
+        hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+        export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
 
+        # If archive_cmds runs LD, not CC, wlarc should be empty
+        # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+        #     investigate it a little bit more. (MM)
+        wlarc='${wl}'
 
-# Check whether --enable-agilent-dmm was given.
-if test "${enable_agilent_dmm+set}" = set; then :
-  enableval=$enable_agilent_dmm; HW_AGILENT_DMM="$enableval"
-else
-  HW_AGILENT_DMM=$HW_ENABLED_DEFAULT
-fi
+        # ancient GNU ld didn't support --whole-archive et. al.
+        if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+	  $GREP 'no-whole-archive' > /dev/null; then
+          whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+        else
+          whole_archive_flag_spec_CXX=
+        fi
+      else
+        with_gnu_ld=no
+        wlarc=
+
+        # A generic and very simple default shared library creation
+        # command for GNU C++ for the case where it uses the native
+        # linker, instead of GNU ld.  If possible, this setting should
+        # overridden to take advantage of the native linker features on
+        # the platform it is being used on.
+        archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+      fi
 
+      # Commands to make compiler produce verbose output that lists
+      # what "hidden" libraries, object files and flags are used when
+      # linking a shared library.
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
 
-# Check whether --enable-appa-55ii was given.
-if test "${enable_appa_55ii+set}" = set; then :
-  enableval=$enable_appa_55ii; HW_APPA_55II="$enableval"
-else
-  HW_APPA_55II=$HW_ENABLED_DEFAULT
-fi
+    else
+      GXX=no
+      with_gnu_ld=no
+      wlarc=
+    fi
 
+    # PORTME: fill in a description of your system's C++ link characteristics
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+    ld_shlibs_CXX=yes
+    case $host_os in
+      aix3*)
+        # FIXME: insert proper C++ library support
+        ld_shlibs_CXX=no
+        ;;
+      aix[4-9]*)
+        if test "$host_cpu" = ia64; then
+          # On IA64, the linker does run time linking by default, so we don't
+          # have to do anything special.
+          aix_use_runtimelinking=no
+          exp_sym_flag='-Bexport'
+          no_entry_flag=""
+        else
+          aix_use_runtimelinking=no
+
+          # Test if we are trying to use run time linking or normal
+          # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+          # need to do runtime linking.
+          case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+	    for ld_flag in $LDFLAGS; do
+	      case $ld_flag in
+	      *-brtl*)
+	        aix_use_runtimelinking=yes
+	        break
+	        ;;
+	      esac
+	    done
+	    ;;
+          esac
 
-# Check whether --enable-asix-sigma was given.
-if test "${enable_asix_sigma+set}" = set; then :
-  enableval=$enable_asix_sigma; HW_ASIX_SIGMA="$enableval"
-else
-  HW_ASIX_SIGMA=$HW_ENABLED_DEFAULT
-fi
+          exp_sym_flag='-bexport'
+          no_entry_flag='-bnoentry'
+        fi
 
+        # When large executables or shared objects are built, AIX ld can
+        # have problems creating the table of contents.  If linking a library
+        # or program results in "error TOC overflow" add -mminimal-toc to
+        # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+        # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+        archive_cmds_CXX=''
+        hardcode_direct_CXX=yes
+        hardcode_direct_absolute_CXX=yes
+        hardcode_libdir_separator_CXX=':'
+        link_all_deplibs_CXX=yes
+        file_list_spec_CXX='${wl}-f,'
+
+        if test "$GXX" = yes; then
+          case $host_os in aix4.[012]|aix4.[012].*)
+          # We only want to do this on AIX 4.2 and lower, the check
+          # below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" &&
+	     strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+	  then
+	    # We have reworked collect2
+	    :
+	  else
+	    # We have old collect2
+	    hardcode_direct_CXX=unsupported
+	    # It fails to find uninstalled libraries when the uninstalled
+	    # path is not listed in the libpath.  Setting hardcode_minus_L
+	    # to unsupported forces relinking
+	    hardcode_minus_L_CXX=yes
+	    hardcode_libdir_flag_spec_CXX='-L$libdir'
+	    hardcode_libdir_separator_CXX=
+	  fi
+          esac
+          shared_flag='-shared'
+	  if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag="$shared_flag "'${wl}-G'
+	  fi
+        else
+          # not using gcc
+          if test "$host_cpu" = ia64; then
+	  # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	  # chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+          else
+	    if test "$aix_use_runtimelinking" = yes; then
+	      shared_flag='${wl}-G'
+	    else
+	      shared_flag='${wl}-bM:SRE'
+	    fi
+          fi
+        fi
 
-# Check whether --enable-atten-pps3xxx was given.
-if test "${enable_atten_pps3xxx+set}" = set; then :
-  enableval=$enable_atten_pps3xxx; HW_ATTEN_PPS3XXX="$enableval"
+        export_dynamic_flag_spec_CXX='${wl}-bexpall'
+        # It seems that -bexpall does not export symbols beginning with
+        # underscore (_), so it is better to generate a list of symbols to
+	# export.
+        always_export_symbols_CXX=yes
+        if test "$aix_use_runtimelinking" = yes; then
+          # Warning - without using the other runtime loading flags (-brtl),
+          # -berok will link without error, but may produce a broken library.
+          allow_undefined_flag_CXX='-berok'
+          # Determine the default libpath from the value encoded in an empty
+          # executable.
+          if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
 else
-  HW_ATTEN_PPS3XXX=$HW_ENABLED_DEFAULT
-fi
-
-
-# Check whether --enable-brymen-bm86x was given.
-if test "${enable_brymen_bm86x+set}" = set; then :
-  enableval=$enable_brymen_bm86x; HW_BRYMEN_BM86X="$enableval"
+  if ${lt_cv_aix_libpath__CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  HW_BRYMEN_BM86X=$HW_ENABLED_DEFAULT
-fi
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
+int
+main ()
+{
 
-# Check whether --enable-brymen-dmm was given.
-if test "${enable_brymen_dmm+set}" = set; then :
-  enableval=$enable_brymen_dmm; HW_BRYMEN_DMM="$enableval"
-else
-  HW_BRYMEN_DMM=$HW_ENABLED_DEFAULT
-fi
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
 
-
-# Check whether --enable-cem-dt-885x was given.
-if test "${enable_cem_dt_885x+set}" = set; then :
-  enableval=$enable_cem_dt_885x; HW_CEM_DT_885X="$enableval"
-else
-  HW_CEM_DT_885X=$HW_ENABLED_DEFAULT
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }'
+  lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath__CXX"; then
+    lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
 fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath__CXX"; then
+    lt_cv_aix_libpath__CXX="/usr/lib:/lib"
+  fi
 
-
-# Check whether --enable-center-3xx was given.
-if test "${enable_center_3xx+set}" = set; then :
-  enableval=$enable_center_3xx; HW_CENTER_3XX="$enableval"
-else
-  HW_CENTER_3XX=$HW_ENABLED_DEFAULT
 fi
 
-
-# Check whether --enable-chronovu-la was given.
-if test "${enable_chronovu_la+set}" = set; then :
-  enableval=$enable_chronovu_la; HW_CHRONOVU_LA="$enableval"
-else
-  HW_CHRONOVU_LA=$HW_ENABLED_DEFAULT
+  aix_libpath=$lt_cv_aix_libpath__CXX
 fi
 
+          hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath"
 
-# Check whether --enable-colead-slm was given.
-if test "${enable_colead_slm+set}" = set; then :
-  enableval=$enable_colead_slm; HW_COLEAD_SLM="$enableval"
+          archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+        else
+          if test "$host_cpu" = ia64; then
+	    hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib'
+	    allow_undefined_flag_CXX="-z nodefs"
+	    archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+          else
+	    # Determine the default libpath from the value encoded in an
+	    # empty executable.
+	    if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
 else
-  HW_COLEAD_SLM=$HW_ENABLED_DEFAULT
-fi
-
-
-# Check whether --enable-conrad-digi-35-cpu was given.
-if test "${enable_conrad_digi_35_cpu+set}" = set; then :
-  enableval=$enable_conrad_digi_35_cpu; HW_CONRAD_DIGI_35_CPU="$enableval"
+  if ${lt_cv_aix_libpath__CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  HW_CONRAD_DIGI_35_CPU=$HW_ENABLED_DEFAULT
-fi
-
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
-# Check whether --enable-demo was given.
-if test "${enable_demo+set}" = set; then :
-  enableval=$enable_demo; HW_DEMO="$enableval"
-else
-  HW_DEMO=$HW_ENABLED_DEFAULT
-fi
+int
+main ()
+{
 
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
 
-# Check whether --enable-fluke-dmm was given.
-if test "${enable_fluke_dmm+set}" = set; then :
-  enableval=$enable_fluke_dmm; HW_FLUKE_DMM="$enableval"
-else
-  HW_FLUKE_DMM=$HW_ENABLED_DEFAULT
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }'
+  lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath__CXX"; then
+    lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
 fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath__CXX"; then
+    lt_cv_aix_libpath__CXX="/usr/lib:/lib"
+  fi
 
-
-# Check whether --enable-fx2lafw was given.
-if test "${enable_fx2lafw+set}" = set; then :
-  enableval=$enable_fx2lafw; HW_FX2LAFW="$enableval"
-else
-  HW_FX2LAFW=$HW_ENABLED_DEFAULT
 fi
 
-
-# Check whether --enable-gmc-mh-1x-2x was given.
-if test "${enable_gmc_mh_1x_2x+set}" = set; then :
-  enableval=$enable_gmc_mh_1x_2x; HW_GMC_MH_1X_2X="$enableval"
-else
-  HW_GMC_MH_1X_2X=$HW_ENABLED_DEFAULT
+  aix_libpath=$lt_cv_aix_libpath__CXX
 fi
 
+	    hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath"
+	    # Warning - without using the other run time loading flags,
+	    # -berok will link without error, but may produce a broken library.
+	    no_undefined_flag_CXX=' ${wl}-bernotok'
+	    allow_undefined_flag_CXX=' ${wl}-berok'
+	    if test "$with_gnu_ld" = yes; then
+	      # We only use this code for GNU lds that support --whole-archive.
+	      whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	    else
+	      # Exported symbols can be pulled into shared objects from archives
+	      whole_archive_flag_spec_CXX='$convenience'
+	    fi
+	    archive_cmds_need_lc_CXX=yes
+	    # This is similar to how AIX traditionally builds its shared
+	    # libraries.
+	    archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+          fi
+        fi
+        ;;
 
-# Check whether --enable-hameg-hmo was given.
-if test "${enable_hameg_hmo+set}" = set; then :
-  enableval=$enable_hameg_hmo; HW_HAMEG_HMO="$enableval"
-else
-  HW_HAMEG_HMO=$HW_ENABLED_DEFAULT
-fi
+      beos*)
+	if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	  allow_undefined_flag_CXX=unsupported
+	  # Joseph Beckenbach <jrb3 at best.com> says some releases of gcc
+	  # support --undefined.  This deserves some investigation.  FIXME
+	  archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	else
+	  ld_shlibs_CXX=no
+	fi
+	;;
 
+      chorus*)
+        case $cc_basename in
+          *)
+	  # FIXME: insert proper C++ library support
+	  ld_shlibs_CXX=no
+	  ;;
+        esac
+        ;;
 
-# Check whether --enable-hantek-dso was given.
-if test "${enable_hantek_dso+set}" = set; then :
-  enableval=$enable_hantek_dso; HW_HANTEK_DSO="$enableval"
-else
-  HW_HANTEK_DSO=$HW_ENABLED_DEFAULT
-fi
+      cygwin* | mingw* | pw32* | cegcc*)
+	case $GXX,$cc_basename in
+	,cl* | no,cl*)
+	  # Native MSVC
+	  # hardcode_libdir_flag_spec is actually meaningless, as there is
+	  # no search path for DLLs.
+	  hardcode_libdir_flag_spec_CXX=' '
+	  allow_undefined_flag_CXX=unsupported
+	  always_export_symbols_CXX=yes
+	  file_list_spec_CXX='@'
+	  # Tell ltmain to make .lib files, not .a files.
+	  libext=lib
+	  # Tell ltmain to make .dll files, not .so files.
+	  shrext_cmds=".dll"
+	  # FIXME: Setting linknames here is a bad hack.
+	  archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+	  archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	      $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+	    else
+	      $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+	    fi~
+	    $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+	    linknames='
+	  # The linker will not automatically build a static lib if we build a DLL.
+	  # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true'
+	  enable_shared_with_static_runtimes_CXX=yes
+	  # Don't use ranlib
+	  old_postinstall_cmds_CXX='chmod 644 $oldlib'
+	  postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~
+	    lt_tool_outputfile="@TOOL_OUTPUT@"~
+	    case $lt_outputfile in
+	      *.exe|*.EXE) ;;
+	      *)
+		lt_outputfile="$lt_outputfile.exe"
+		lt_tool_outputfile="$lt_tool_outputfile.exe"
+		;;
+	    esac~
+	    func_to_tool_file "$lt_outputfile"~
+	    if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+	      $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+	      $RM "$lt_outputfile.manifest";
+	    fi'
+	  ;;
+	*)
+	  # g++
+	  # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless,
+	  # as there is no search path for DLLs.
+	  hardcode_libdir_flag_spec_CXX='-L$libdir'
+	  export_dynamic_flag_spec_CXX='${wl}--export-all-symbols'
+	  allow_undefined_flag_CXX=unsupported
+	  always_export_symbols_CXX=no
+	  enable_shared_with_static_runtimes_CXX=yes
+
+	  if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+	    archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	    # If the export-symbols file already is a .def file (1st line
+	    # is EXPORTS), use it as is; otherwise, prepend...
+	    archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	      cp $export_symbols $output_objdir/$soname.def;
+	    else
+	      echo EXPORTS > $output_objdir/$soname.def;
+	      cat $export_symbols >> $output_objdir/$soname.def;
+	    fi~
+	    $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	  else
+	    ld_shlibs_CXX=no
+	  fi
+	  ;;
+	esac
+	;;
+      darwin* | rhapsody*)
 
 
-# Check whether --enable-ikalogic-scanalogic2 was given.
-if test "${enable_ikalogic_scanalogic2+set}" = set; then :
-  enableval=$enable_ikalogic_scanalogic2; HW_IKALOGIC_SCANALOGIC2="$enableval"
-else
-  HW_IKALOGIC_SCANALOGIC2=$HW_ENABLED_DEFAULT
-fi
+  archive_cmds_need_lc_CXX=no
+  hardcode_direct_CXX=no
+  hardcode_automatic_CXX=yes
+  hardcode_shlibpath_var_CXX=unsupported
+  if test "$lt_cv_ld_force_load" = "yes"; then
+    whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
 
+  else
+    whole_archive_flag_spec_CXX=''
+  fi
+  link_all_deplibs_CXX=yes
+  allow_undefined_flag_CXX="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=func_echo_all
+    archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+       if test "$lt_cv_apple_cc_single_mod" != "yes"; then
+      archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
+      archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+    fi
 
-# Check whether --enable-ikalogic-scanaplus was given.
-if test "${enable_ikalogic_scanaplus+set}" = set; then :
-  enableval=$enable_ikalogic_scanaplus; HW_IKALOGIC_SCANAPLUS="$enableval"
-else
-  HW_IKALOGIC_SCANAPLUS=$HW_ENABLED_DEFAULT
-fi
+  else
+  ld_shlibs_CXX=no
+  fi
 
+	;;
 
-# Check whether --enable-kecheng-kc-330b was given.
-if test "${enable_kecheng_kc_330b+set}" = set; then :
-  enableval=$enable_kecheng_kc_330b; HW_KECHENG_KC_330B="$enableval"
-else
-  HW_KECHENG_KC_330B=$HW_ENABLED_DEFAULT
-fi
+      dgux*)
+        case $cc_basename in
+          ec++*)
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+          ghcx*)
+	    # Green Hills C++ Compiler
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+        esac
+        ;;
 
+      freebsd2.*)
+        # C++ shared libraries reported to be fairly broken before
+	# switch to ELF
+        ld_shlibs_CXX=no
+        ;;
 
-# Check whether --enable-lascar-el-usb was given.
-if test "${enable_lascar_el_usb+set}" = set; then :
-  enableval=$enable_lascar_el_usb; HW_LASCAR_EL_USB="$enableval"
-else
-  HW_LASCAR_EL_USB=$HW_ENABLED_DEFAULT
-fi
+      freebsd-elf*)
+        archive_cmds_need_lc_CXX=no
+        ;;
 
+      freebsd* | dragonfly*)
+        # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+        # conventions
+        ld_shlibs_CXX=yes
+        ;;
 
-# Check whether --enable-mic-985xx was given.
-if test "${enable_mic_985xx+set}" = set; then :
-  enableval=$enable_mic_985xx; HW_MIC_985XX="$enableval"
-else
-  HW_MIC_985XX=$HW_ENABLED_DEFAULT
-fi
+      haiku*)
+        archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        link_all_deplibs_CXX=yes
+        ;;
 
+      hpux9*)
+        hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir'
+        hardcode_libdir_separator_CXX=:
+        export_dynamic_flag_spec_CXX='${wl}-E'
+        hardcode_direct_CXX=yes
+        hardcode_minus_L_CXX=yes # Not in the search PATH,
+				             # but as the default
+				             # location of the library.
+
+        case $cc_basename in
+          CC*)
+            # FIXME: insert proper C++ library support
+            ld_shlibs_CXX=no
+            ;;
+          aCC*)
+            archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            # Commands to make compiler produce verbose output that lists
+            # what "hidden" libraries, object files and flags are used when
+            # linking a shared library.
+            #
+            # There doesn't appear to be a way to prevent this compiler from
+            # explicitly linking system object files so we need to strip them
+            # from the output so that they don't get included in the library
+            # dependencies.
+            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+            ;;
+          *)
+            if test "$GXX" = yes; then
+              archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            else
+              # FIXME: insert proper C++ library support
+              ld_shlibs_CXX=no
+            fi
+            ;;
+        esac
+        ;;
 
-# Check whether --enable-norma-dmm was given.
-if test "${enable_norma_dmm+set}" = set; then :
-  enableval=$enable_norma_dmm; HW_NORMA_DMM="$enableval"
-else
-  HW_NORMA_DMM=$HW_ENABLED_DEFAULT
-fi
+      hpux10*|hpux11*)
+        if test $with_gnu_ld = no; then
+	  hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir'
+	  hardcode_libdir_separator_CXX=:
+
+          case $host_cpu in
+            hppa*64*|ia64*)
+              ;;
+            *)
+	      export_dynamic_flag_spec_CXX='${wl}-E'
+              ;;
+          esac
+        fi
+        case $host_cpu in
+          hppa*64*|ia64*)
+            hardcode_direct_CXX=no
+            hardcode_shlibpath_var_CXX=no
+            ;;
+          *)
+            hardcode_direct_CXX=yes
+            hardcode_direct_absolute_CXX=yes
+            hardcode_minus_L_CXX=yes # Not in the search PATH,
+					         # but as the default
+					         # location of the library.
+            ;;
+        esac
 
+        case $cc_basename in
+          CC*)
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+          aCC*)
+	    case $host_cpu in
+	      hppa*64*)
+	        archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	      ia64*)
+	        archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	      *)
+	        archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	    esac
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+	    ;;
+          *)
+	    if test "$GXX" = yes; then
+	      if test $with_gnu_ld = no; then
+	        case $host_cpu in
+	          hppa*64*)
+	            archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	          ia64*)
+	            archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	          *)
+	            archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	        esac
+	      fi
+	    else
+	      # FIXME: insert proper C++ library support
+	      ld_shlibs_CXX=no
+	    fi
+	    ;;
+        esac
+        ;;
 
-# Check whether --enable-ols was given.
-if test "${enable_ols+set}" = set; then :
-  enableval=$enable_ols; HW_OLS="$enableval"
-else
-  HW_OLS=$HW_ENABLED_DEFAULT
-fi
+      interix[3-9]*)
+	hardcode_direct_CXX=no
+	hardcode_shlibpath_var_CXX=no
+	hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+	export_dynamic_flag_spec_CXX='${wl}-E'
+	# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+	# Instead, shared libraries are loaded at an image base (0x10000000 by
+	# default) and relocated if they conflict, which is a slow very memory
+	# consuming and fragmenting process.  To avoid this, we pick a random,
+	# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+	# time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+	archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+	archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+	;;
+      irix5* | irix6*)
+        case $cc_basename in
+          CC*)
+	    # SGI C++
+	    archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+
+	    # Archives containing C++ object files must be created using
+	    # "CC -ar", where "CC" is the IRIX C++ compiler.  This is
+	    # necessary to make sure instantiated templates are included
+	    # in the archive.
+	    old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs'
+	    ;;
+          *)
+	    if test "$GXX" = yes; then
+	      if test "$with_gnu_ld" = no; then
+	        archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	      else
+	        archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib'
+	      fi
+	    fi
+	    link_all_deplibs_CXX=yes
+	    ;;
+        esac
+        hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+        hardcode_libdir_separator_CXX=:
+        inherit_rpath_CXX=yes
+        ;;
 
+      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+        case $cc_basename in
+          KCC*)
+	    # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	    # KCC will only create a shared library if the output file
+	    # ends with ".so" (or ".sl" for HP-UX), so rename the library
+	    # to its proper name (with version) after linking.
+	    archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+	    archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+	    hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+	    export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+
+	    # Archives containing C++ object files must be created using
+	    # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+	    old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs'
+	    ;;
+	  icpc* | ecpc* )
+	    # Intel C++
+	    with_gnu_ld=yes
+	    # version 8.0 and above of icpc choke on multiply defined symbols
+	    # if we add $predep_objects and $postdep_objects, however 7.1 and
+	    # earlier do not add the objects themselves.
+	    case `$CC -V 2>&1` in
+	      *"Version 7."*)
+	        archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+		archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+		;;
+	      *)  # Version 8.0 or newer
+	        tmp_idyn=
+	        case $host_cpu in
+		  ia64*) tmp_idyn=' -i_dynamic';;
+		esac
+	        archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+		archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+		;;
+	    esac
+	    archive_cmds_need_lc_CXX=no
+	    hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+	    export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+	    whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	    ;;
+          pgCC* | pgcpp*)
+            # Portland Group C++ compiler
+	    case `$CC -V` in
+	    *pgCC\ [1-5].* | *pgcpp\ [1-5].*)
+	      prelink_cmds_CXX='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+		compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+	      old_archive_cmds_CXX='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+		$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+		$RANLIB $oldlib'
+	      archive_cmds_CXX='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+		$CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+	      archive_expsym_cmds_CXX='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+		$CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+	      ;;
+	    *) # Version 6 and above use weak symbols
+	      archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+	      archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+	      ;;
+	    esac
 
-# Check whether --enable-rigol-ds was given.
-if test "${enable_rigol_ds+set}" = set; then :
-  enableval=$enable_rigol_ds; HW_RIGOL_DS="$enableval"
-else
-  HW_RIGOL_DS=$HW_ENABLED_DEFAULT
-fi
+	    hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir'
+	    export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+	    whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+            ;;
+	  cxx*)
+	    # Compaq C++
+	    archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+	    runpath_var=LD_RUN_PATH
+	    hardcode_libdir_flag_spec_CXX='-rpath $libdir'
+	    hardcode_libdir_separator_CXX=:
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+	    ;;
+	  xl* | mpixl* | bgxl*)
+	    # IBM XL 8.0 on PPC, with GNU ld
+	    hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+	    export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+	    archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    if test "x$supports_anon_versioning" = xyes; then
+	      archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~
+		cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+		echo "local: *; };" >> $output_objdir/$libname.ver~
+		$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+	    fi
+	    ;;
+	  *)
+	    case `$CC -V 2>&1 | sed 5q` in
+	    *Sun\ C*)
+	      # Sun C++ 5.9
+	      no_undefined_flag_CXX=' -zdefs'
+	      archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	      archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+	      hardcode_libdir_flag_spec_CXX='-R$libdir'
+	      whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	      compiler_needs_object_CXX=yes
+
+	      # Not sure whether something based on
+	      # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+	      # would be better.
+	      output_verbose_link_cmd='func_echo_all'
+
+	      # Archives containing C++ object files must be created using
+	      # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+	      # necessary to make sure instantiated templates are included
+	      # in the archive.
+	      old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'
+	      ;;
+	    esac
+	    ;;
+	esac
+	;;
 
+      lynxos*)
+        # FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
 
-# Check whether --enable-saleae-logic16 was given.
-if test "${enable_saleae_logic16+set}" = set; then :
-  enableval=$enable_saleae_logic16; HW_SALEAE_LOGIC16="$enableval"
-else
-  HW_SALEAE_LOGIC16=$HW_ENABLED_DEFAULT
-fi
+      m88k*)
+        # FIXME: insert proper C++ library support
+        ld_shlibs_CXX=no
+	;;
 
+      mvs*)
+        case $cc_basename in
+          cxx*)
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+	  *)
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+	esac
+	;;
 
-# Check whether --enable-serial-dmm was given.
-if test "${enable_serial_dmm+set}" = set; then :
-  enableval=$enable_serial_dmm; HW_SERIAL_DMM="$enableval"
-else
-  HW_SERIAL_DMM=$HW_ENABLED_DEFAULT
-fi
+      netbsd*)
+        if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	  archive_cmds_CXX='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+	  wlarc=
+	  hardcode_libdir_flag_spec_CXX='-R$libdir'
+	  hardcode_direct_CXX=yes
+	  hardcode_shlibpath_var_CXX=no
+	fi
+	# Workaround some broken pre-1.5 toolchains
+	output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+	;;
 
+      *nto* | *qnx*)
+        ld_shlibs_CXX=yes
+	;;
 
-# Check whether --enable-sysclk-lwla was given.
-if test "${enable_sysclk_lwla+set}" = set; then :
-  enableval=$enable_sysclk_lwla; HW_SYSCLK_LWLA="$enableval"
-else
-  HW_SYSCLK_LWLA=$HW_ENABLED_DEFAULT
-fi
+      openbsd2*)
+        # C++ shared libraries are fairly broken
+	ld_shlibs_CXX=no
+	;;
 
+      openbsd*)
+	if test -f /usr/libexec/ld.so; then
+	  hardcode_direct_CXX=yes
+	  hardcode_shlibpath_var_CXX=no
+	  hardcode_direct_absolute_CXX=yes
+	  archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+	  hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+	  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	    archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
+	    export_dynamic_flag_spec_CXX='${wl}-E'
+	    whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+	  fi
+	  output_verbose_link_cmd=func_echo_all
+	else
+	  ld_shlibs_CXX=no
+	fi
+	;;
 
-# Check whether --enable-teleinfo was given.
-if test "${enable_teleinfo+set}" = set; then :
-  enableval=$enable_teleinfo; HW_TELEINFO="$enableval"
-else
-  HW_TELEINFO=$HW_ENABLED_DEFAULT
-fi
+      osf3* | osf4* | osf5*)
+        case $cc_basename in
+          KCC*)
+	    # Kuck and Associates, Inc. (KAI) C++ Compiler
 
+	    # KCC will only create a shared library if the output file
+	    # ends with ".so" (or ".sl" for HP-UX), so rename the library
+	    # to its proper name (with version) after linking.
+	    archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
 
-# Check whether --enable-tondaj-sl-814 was given.
-if test "${enable_tondaj_sl_814+set}" = set; then :
-  enableval=$enable_tondaj_sl_814; HW_TONDAJ_SL_814="$enableval"
-else
-  HW_TONDAJ_SL_814=$HW_ENABLED_DEFAULT
-fi
+	    hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+	    hardcode_libdir_separator_CXX=:
 
+	    # Archives containing C++ object files must be created using
+	    # the KAI C++ compiler.
+	    case $host in
+	      osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;;
+	      *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;;
+	    esac
+	    ;;
+          RCC*)
+	    # Rational C++ 2.4.1
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+          cxx*)
+	    case $host in
+	      osf3*)
+	        allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*'
+	        archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	        hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+		;;
+	      *)
+	        allow_undefined_flag_CXX=' -expect_unresolved \*'
+	        archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	        archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+	          echo "-hidden">> $lib.exp~
+	          $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp  `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~
+	          $RM $lib.exp'
+	        hardcode_libdir_flag_spec_CXX='-rpath $libdir'
+		;;
+	    esac
 
-# Check whether --enable-uni-t-dmm was given.
-if test "${enable_uni_t_dmm+set}" = set; then :
-  enableval=$enable_uni_t_dmm; HW_UNI_T_DMM="$enableval"
-else
-  HW_UNI_T_DMM=$HW_ENABLED_DEFAULT
-fi
+	    hardcode_libdir_separator_CXX=:
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+	    ;;
+	  *)
+	    if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	      allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*'
+	      case $host in
+	        osf3*)
+	          archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+		  ;;
+	        *)
+	          archive_cmds_CXX='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+		  ;;
+	      esac
+
+	      hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+	      hardcode_libdir_separator_CXX=:
+
+	      # Commands to make compiler produce verbose output that lists
+	      # what "hidden" libraries, object files and flags are used when
+	      # linking a shared library.
+	      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+	    else
+	      # FIXME: insert proper C++ library support
+	      ld_shlibs_CXX=no
+	    fi
+	    ;;
+        esac
+        ;;
 
+      psos*)
+        # FIXME: insert proper C++ library support
+        ld_shlibs_CXX=no
+        ;;
 
-# Check whether --enable-uni-t-ut32x was given.
-if test "${enable_uni_t_ut32x+set}" = set; then :
-  enableval=$enable_uni_t_ut32x; HW_UNI_T_UT32X="$enableval"
-else
-  HW_UNI_T_UT32X=$HW_ENABLED_DEFAULT
-fi
+      sunos4*)
+        case $cc_basename in
+          CC*)
+	    # Sun C++ 4.x
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+          lcc*)
+	    # Lucid
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+        esac
+        ;;
 
+      solaris*)
+        case $cc_basename in
+          CC* | sunCC*)
+	    # Sun C++ 4.2, 5.x and Centerline C++
+            archive_cmds_need_lc_CXX=yes
+	    no_undefined_flag_CXX=' -zdefs'
+	    archive_cmds_CXX='$CC -G${allow_undefined_flag}  -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	    archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	      $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	    hardcode_libdir_flag_spec_CXX='-R$libdir'
+	    hardcode_shlibpath_var_CXX=no
+	    case $host_os in
+	      solaris2.[0-5] | solaris2.[0-5].*) ;;
+	      *)
+		# The compiler driver will combine and reorder linker options,
+		# but understands `-z linker_flag'.
+	        # Supported since Solaris 2.6 (maybe 2.5.1?)
+		whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract'
+	        ;;
+	    esac
+	    link_all_deplibs_CXX=yes
 
-# Check whether --enable-victor-dmm was given.
-if test "${enable_victor_dmm+set}" = set; then :
-  enableval=$enable_victor_dmm; HW_VICTOR_DMM="$enableval"
-else
-  HW_VICTOR_DMM=$HW_ENABLED_DEFAULT
-fi
+	    output_verbose_link_cmd='func_echo_all'
 
+	    # Archives containing C++ object files must be created using
+	    # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+	    # necessary to make sure instantiated templates are included
+	    # in the archive.
+	    old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'
+	    ;;
+          gcx*)
+	    # Green Hills C++ Compiler
+	    archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
 
-# Check whether --enable-zeroplus-logic-cube was given.
-if test "${enable_zeroplus_logic_cube+set}" = set; then :
-  enableval=$enable_zeroplus_logic_cube; HW_ZEROPLUS_LOGIC_CUBE="$enableval"
-else
-  HW_ZEROPLUS_LOGIC_CUBE=$HW_ENABLED_DEFAULT
-fi
+	    # The C++ compiler must be used to create the archive.
+	    old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+	    ;;
+          *)
+	    # GNU C++ compiler with Solaris linker
+	    if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	      no_undefined_flag_CXX=' ${wl}-z ${wl}defs'
+	      if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+	        archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	        archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+		  $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	        # Commands to make compiler produce verbose output that lists
+	        # what "hidden" libraries, object files and flags are used when
+	        # linking a shared library.
+	        output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+	      else
+	        # g++ 2.7 appears to require `-G' NOT `-shared' on this
+	        # platform.
+	        archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	        archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+		  $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	        # Commands to make compiler produce verbose output that lists
+	        # what "hidden" libraries, object files and flags are used when
+	        # linking a shared library.
+	        output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+	      fi
 
+	      hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir'
+	      case $host_os in
+		solaris2.[0-5] | solaris2.[0-5].*) ;;
+		*)
+		  whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+		  ;;
+	      esac
+	    fi
+	    ;;
+        esac
+        ;;
 
-# Checks for libraries.
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+      no_undefined_flag_CXX='${wl}-z,text'
+      archive_cmds_need_lc_CXX=no
+      hardcode_shlibpath_var_CXX=no
+      runpath_var='LD_RUN_PATH'
 
-case "$host" in
-*mingw*)
-	# We need to link against the Winsock2 library for SCPI over TCP.
-	LIBS="$LIBS -lws2_32";;
-esac
+      case $cc_basename in
+        CC*)
+	  archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+      esac
+      ;;
 
-# This variable collects the pkg-config names of all detected libs.
-# It is then used to construct the "Requires.private:" field in the
-# libsigrok.pc file.
-SR_PKGLIBS=""
+      sysv5* | sco3.2v5* | sco5v6*)
+	# Note: We can NOT use -z defs as we might desire, because we do not
+	# link with -lc, and that would cause any symbols used from libc to
+	# always be unresolved, which means just about no library would
+	# ever link correctly.  If we're not using GNU ld we use -z text
+	# though, which does catch some bad symbols but isn't as heavy-handed
+	# as -z defs.
+	no_undefined_flag_CXX='${wl}-z,text'
+	allow_undefined_flag_CXX='${wl}-z,nodefs'
+	archive_cmds_need_lc_CXX=no
+	hardcode_shlibpath_var_CXX=no
+	hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir'
+	hardcode_libdir_separator_CXX=':'
+	link_all_deplibs_CXX=yes
+	export_dynamic_flag_spec_CXX='${wl}-Bexport'
+	runpath_var='LD_RUN_PATH'
 
-# libm (the standard math library) is always needed.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pow" >&5
-$as_echo_n "checking for library containing pow... " >&6; }
-if ${ac_cv_search_pow+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_func_search_save_LIBS=$LIBS
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char pow ();
-int
-main ()
-{
-return pow ();
-  ;
-  return 0;
-}
-_ACEOF
-for ac_lib in '' m; do
-  if test -z "$ac_lib"; then
-    ac_res="none required"
-  else
-    ac_res=-l$ac_lib
-    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
-  fi
-  if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_search_pow=$ac_res
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext
-  if ${ac_cv_search_pow+:} false; then :
-  break
-fi
-done
-if ${ac_cv_search_pow+:} false; then :
+	case $cc_basename in
+          CC*)
+	    archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~
+	      '"$old_archive_cmds_CXX"
+	    reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~
+	      '"$reload_cmds_CXX"
+	    ;;
+	  *)
+	    archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    ;;
+	esac
+      ;;
 
-else
-  ac_cv_search_pow=no
-fi
-rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pow" >&5
-$as_echo "$ac_cv_search_pow" >&6; }
-ac_res=$ac_cv_search_pow
-if test "$ac_res" != no; then :
-  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+      tandem*)
+        case $cc_basename in
+          NCC*)
+	    # NonStop-UX NCC 3.20
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    ld_shlibs_CXX=no
+	    ;;
+        esac
+        ;;
 
-fi
+      vxworks*)
+        # FIXME: insert proper C++ library support
+        ld_shlibs_CXX=no
+        ;;
 
+      *)
+        # FIXME: insert proper C++ library support
+        ld_shlibs_CXX=no
+        ;;
+    esac
 
-# RPC is only needed for VXI support.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for RPC support" >&5
-$as_echo_n "checking for RPC support... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <rpc/rpc.h>
-int
-main ()
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5
+$as_echo "$ld_shlibs_CXX" >&6; }
+    test "$ld_shlibs_CXX" = no && can_build_shared=no
+
+    GCC_CXX="$GXX"
+    LD_CXX="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    # Dependencies to place before and after the object being linked:
+predep_objects_CXX=
+postdep_objects_CXX=
+predeps_CXX=
+postdeps_CXX=
+compiler_lib_search_path_CXX=
+
+cat > conftest.$ac_ext <<_LT_EOF
+class Foo
 {
-CLIENT *rpc_test(void)
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }; have_rpc=1
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; };  have_rpc=0
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-# Define HAVE_RPC in config.h if we found RPC support.
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_RPC $have_rpc
-_ACEOF
+public:
+  Foo (void) { a = 0; }
+private:
+  int a;
+};
+_LT_EOF
 
-# VXI support is only compiled if RPC support was found.
- if test "x$have_rpc" != "x0"; then
-  NEED_RPC_TRUE=
-  NEED_RPC_FALSE='#'
-else
-  NEED_RPC_TRUE='#'
-  NEED_RPC_FALSE=
-fi
 
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
 
-# libglib-2.0 is always needed. Abort if it's not found.
-# Note: glib-2.0 is part of the libsigrok API (hard pkg-config requirement).
-# We require at least 2.32.0 due to e.g. g_variant_new_fixed_array().
-# Check whether --enable-glibtest was given.
-if test "${enable_glibtest+set}" = set; then :
-  enableval=$enable_glibtest;
-else
-  enable_glibtest=yes
-fi
+if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  # Parse the compiler output and extract the necessary
+  # objects, libraries and library flags.
+
+  # Sentinel used to keep track of whether or not we are before
+  # the conftest object file.
+  pre_test_object_deps_done=no
+
+  for p in `eval "$output_verbose_link_cmd"`; do
+    case ${prev}${p} in
+
+    -L* | -R* | -l*)
+       # Some compilers place space between "-{L,R}" and the path.
+       # Remove the space.
+       if test $p = "-L" ||
+          test $p = "-R"; then
+	 prev=$p
+	 continue
+       fi
 
+       # Expand the sysroot to ease extracting the directories later.
+       if test -z "$prev"; then
+         case $p in
+         -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
+         -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
+         -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
+         esac
+       fi
+       case $p in
+       =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
+       esac
+       if test "$pre_test_object_deps_done" = no; then
+	 case ${prev} in
+	 -L | -R)
+	   # Internal compiler library paths should come after those
+	   # provided the user.  The postdeps already come after the
+	   # user supplied libs so there is no need to process them.
+	   if test -z "$compiler_lib_search_path_CXX"; then
+	     compiler_lib_search_path_CXX="${prev}${p}"
+	   else
+	     compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}"
+	   fi
+	   ;;
+	 # The "-l" case would never come before the object being
+	 # linked, so don't bother handling this case.
+	 esac
+       else
+	 if test -z "$postdeps_CXX"; then
+	   postdeps_CXX="${prev}${p}"
+	 else
+	   postdeps_CXX="${postdeps_CXX} ${prev}${p}"
+	 fi
+       fi
+       prev=
+       ;;
 
-  pkg_config_args=glib-2.0
-  for module in .
-  do
-      case "$module" in
-         gmodule)
-             pkg_config_args="$pkg_config_args gmodule-2.0"
-         ;;
-         gmodule-no-export)
-             pkg_config_args="$pkg_config_args gmodule-no-export-2.0"
-         ;;
-         gobject)
-             pkg_config_args="$pkg_config_args gobject-2.0"
-         ;;
-         gthread)
-             pkg_config_args="$pkg_config_args gthread-2.0"
-         ;;
-         gio*)
-             pkg_config_args="$pkg_config_args $module-2.0"
-         ;;
-      esac
-  done
+    *.lto.$objext) ;; # Ignore GCC LTO objects
+    *.$objext)
+       # This assumes that the test object file only shows up
+       # once in the compiler output.
+       if test "$p" = "conftest.$objext"; then
+	 pre_test_object_deps_done=yes
+	 continue
+       fi
 
+       if test "$pre_test_object_deps_done" = no; then
+	 if test -z "$predep_objects_CXX"; then
+	   predep_objects_CXX="$p"
+	 else
+	   predep_objects_CXX="$predep_objects_CXX $p"
+	 fi
+       else
+	 if test -z "$postdep_objects_CXX"; then
+	   postdep_objects_CXX="$p"
+	 else
+	   postdep_objects_CXX="$postdep_objects_CXX $p"
+	 fi
+       fi
+       ;;
 
+    *) ;; # Ignore the rest.
 
+    esac
+  done
 
+  # Clean up.
+  rm -f a.out a.exe
+else
+  echo "libtool.m4: error: problem compiling CXX test program"
+fi
 
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
 
+# PORTME: override above test on systems where it is broken
+case $host_os in
+interix[3-9]*)
+  # Interix 3.5 installs completely hosed .la files for C++, so rather than
+  # hack all around it, let's just trust "g++" to DTRT.
+  predep_objects_CXX=
+  postdep_objects_CXX=
+  postdeps_CXX=
+  ;;
 
+linux*)
+  case `$CC -V 2>&1 | sed 5q` in
+  *Sun\ C*)
+    # Sun C++ 5.9
+
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
 
-if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
-	if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
-set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_PKG_CONFIG+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  case $PKG_CONFIG in
-  [\\/]* | ?:[\\/]*)
-  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+    if test "$solaris_use_stlport4" != yes; then
+      postdeps_CXX='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
   ;;
-  *)
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
 
+solaris*)
+  case $cc_basename in
+  CC* | sunCC*)
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    # Adding this requires a known-good setup of shared libraries for
+    # Sun compiler versions before 5.6, else PIC objects from an old
+    # archive will be linked into the output, leading to subtle bugs.
+    if test "$solaris_use_stlport4" != yes; then
+      postdeps_CXX='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
   ;;
 esac
-fi
-PKG_CONFIG=$ac_cv_path_PKG_CONFIG
-if test -n "$PKG_CONFIG"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
-$as_echo "$PKG_CONFIG" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
 
-fi
-if test -z "$ac_cv_path_PKG_CONFIG"; then
-  ac_pt_PKG_CONFIG=$PKG_CONFIG
-  # Extract the first word of "pkg-config", so it can be a program name with args.
-set dummy pkg-config; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  case $ac_pt_PKG_CONFIG in
-  [\\/]* | ?:[\\/]*)
-  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
-  ;;
-  *)
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
 
-  ;;
+case " $postdeps_CXX " in
+*" -lc "*) archive_cmds_need_lc_CXX=no ;;
 esac
-fi
-ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
-if test -n "$ac_pt_PKG_CONFIG"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
-$as_echo "$ac_pt_PKG_CONFIG" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ compiler_lib_search_dirs_CXX=
+if test -n "${compiler_lib_search_path_CXX}"; then
+ compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
 fi
 
-  if test "x$ac_pt_PKG_CONFIG" = x; then
-    PKG_CONFIG=""
-  else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
-    PKG_CONFIG=$ac_pt_PKG_CONFIG
-  fi
-else
-  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
-fi
 
-fi
-if test -n "$PKG_CONFIG"; then
-	_pkg_min_version=0.16
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
-$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
-	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
-		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	else
-		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-		PKG_CONFIG=""
-	fi
-fi
 
-  no_glib=""
 
-  if test "x$PKG_CONFIG" = x ; then
-    no_glib=yes
-    PKG_CONFIG=no
-  fi
 
-  min_glib_version=2.32.0
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLIB - version >= $min_glib_version" >&5
-$as_echo_n "checking for GLIB - version >= $min_glib_version... " >&6; }
 
-  if test x$PKG_CONFIG != xno ; then
-    ## don't try to run the test against uninstalled libtool libs
-    if $PKG_CONFIG --uninstalled $pkg_config_args; then
-	  echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH"
-	  enable_glibtest=no
-    fi
 
-    if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then
-	  :
-    else
-	  no_glib=yes
-    fi
-  fi
 
-  if test x"$no_glib" = x ; then
-    GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
-    GOBJECT_QUERY=`$PKG_CONFIG --variable=gobject_query glib-2.0`
-    GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
-    GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable=glib_compile_resources gio-2.0`
-
-    GLIB_CFLAGS=`$PKG_CONFIG --cflags $pkg_config_args`
-    GLIB_LIBS=`$PKG_CONFIG --libs $pkg_config_args`
-    glib_config_major_version=`$PKG_CONFIG --modversion glib-2.0 | \
-           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
-    glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.0 | \
-           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
-    glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.0 | \
-           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
-    if test "x$enable_glibtest" = "xyes" ; then
-      ac_save_CFLAGS="$CFLAGS"
-      ac_save_LIBS="$LIBS"
-      CFLAGS="$CFLAGS $GLIB_CFLAGS"
-      LIBS="$GLIB_LIBS $LIBS"
-      rm -f conf.glibtest
-      if test "$cross_compiling" = yes; then :
-  echo $ac_n "cross compiling; assumed OK... $ac_c"
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
 
-#include <glib.h>
-#include <stdio.h>
-#include <stdlib.h>
 
-int
-main ()
-{
-  unsigned int major, minor, micro;
 
-  fclose (fopen ("conf.glibtest", "w"));
 
-  if (sscanf("$min_glib_version", "%u.%u.%u", &major, &minor, &micro) != 3) {
-     printf("%s, bad version string\n", "$min_glib_version");
-     exit(1);
-   }
 
-  if ((glib_major_version != $glib_config_major_version) ||
-      (glib_minor_version != $glib_config_minor_version) ||
-      (glib_micro_version != $glib_config_micro_version))
-    {
-      printf("\n*** 'pkg-config --modversion glib-2.0' returned %d.%d.%d, but GLIB (%d.%d.%d)\n",
-             $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
-             glib_major_version, glib_minor_version, glib_micro_version);
-      printf ("*** was found! If pkg-config was correct, then it is best\n");
-      printf ("*** to remove the old version of GLib. You may also be able to fix the error\n");
-      printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
-      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
-      printf("*** required on your system.\n");
-      printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
-      printf("*** to point to the correct configuration files\n");
-    }
-  else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
-	   (glib_minor_version != GLIB_MINOR_VERSION) ||
-           (glib_micro_version != GLIB_MICRO_VERSION))
-    {
-      printf("*** GLIB header files (version %d.%d.%d) do not match\n",
-	     GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
-      printf("*** library (version %d.%d.%d)\n",
-	     glib_major_version, glib_minor_version, glib_micro_version);
-    }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    lt_prog_compiler_wl_CXX=
+lt_prog_compiler_pic_CXX=
+lt_prog_compiler_static_CXX=
+
+
+  # C++ specific cases for pic, static, wl, etc.
+  if test "$GXX" = yes; then
+    lt_prog_compiler_wl_CXX='-Wl,'
+    lt_prog_compiler_static_CXX='-static'
+
+    case $host_os in
+    aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static_CXX='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            lt_prog_compiler_pic_CXX='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    mingw* | cygwin* | os2* | pw32* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      lt_prog_compiler_pic_CXX='-DDLL_EXPORT'
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic_CXX='-fno-common'
+      ;;
+    *djgpp*)
+      # DJGPP does not support shared libraries at all
+      lt_prog_compiler_pic_CXX=
+      ;;
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      lt_prog_compiler_static_CXX=
+      ;;
+    interix[3-9]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic_CXX=-Kconform_pic
+      fi
+      ;;
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+	;;
+      *)
+	lt_prog_compiler_pic_CXX='-fPIC'
+	;;
+      esac
+      ;;
+    *qnx* | *nto*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic_CXX='-fPIC -shared'
+      ;;
+    *)
+      lt_prog_compiler_pic_CXX='-fPIC'
+      ;;
+    esac
   else
-    {
-      if ((glib_major_version > major) ||
-        ((glib_major_version == major) && (glib_minor_version > minor)) ||
-        ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
-      {
-        return 0;
-       }
-     else
-      {
-        printf("\n*** An old version of GLIB (%u.%u.%u) was found.\n",
-               glib_major_version, glib_minor_version, glib_micro_version);
-        printf("*** You need a version of GLIB newer than %u.%u.%u. The latest version of\n",
-	       major, minor, micro);
-        printf("*** GLIB is always available from ftp://ftp.gtk.org.\n");
-        printf("***\n");
-        printf("*** If you have already installed a sufficiently new version, this error\n");
-        printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
-        printf("*** being found. The easiest way to fix this is to remove the old version\n");
-        printf("*** of GLIB, but you can also set the PKG_CONFIG environment to point to the\n");
-        printf("*** correct copy of pkg-config. (In this case, you will have to\n");
-        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
-        printf("*** so that the correct libraries are found at run-time))\n");
-      }
-    }
-  return 1;
-}
+    case $host_os in
+      aix[4-9]*)
+	# All AIX code is PIC.
+	if test "$host_cpu" = ia64; then
+	  # AIX 5 now supports IA64 processor
+	  lt_prog_compiler_static_CXX='-Bstatic'
+	else
+	  lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp'
+	fi
+	;;
+      chorus*)
+	case $cc_basename in
+	cxch68*)
+	  # Green Hills C++ Compiler
+	  # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+	  ;;
+	esac
+	;;
+      mingw* | cygwin* | os2* | pw32* | cegcc*)
+	# This hack is so that the source file can tell whether it is being
+	# built for inclusion in a dll (and should export symbols for example).
+	lt_prog_compiler_pic_CXX='-DDLL_EXPORT'
+	;;
+      dgux*)
+	case $cc_basename in
+	  ec++*)
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    ;;
+	  ghcx*)
+	    # Green Hills C++ Compiler
+	    lt_prog_compiler_pic_CXX='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      freebsd* | dragonfly*)
+	# FreeBSD uses GNU C++
+	;;
+      hpux9* | hpux10* | hpux11*)
+	case $cc_basename in
+	  CC*)
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_static_CXX='${wl}-a ${wl}archive'
+	    if test "$host_cpu" != ia64; then
+	      lt_prog_compiler_pic_CXX='+Z'
+	    fi
+	    ;;
+	  aCC*)
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_static_CXX='${wl}-a ${wl}archive'
+	    case $host_cpu in
+	    hppa*64*|ia64*)
+	      # +Z the default
+	      ;;
+	    *)
+	      lt_prog_compiler_pic_CXX='+Z'
+	      ;;
+	    esac
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      interix*)
+	# This is c89, which is MS Visual C++ (no shared libs)
+	# Anyone wants to do a port?
+	;;
+      irix5* | irix6* | nonstopux*)
+	case $cc_basename in
+	  CC*)
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_static_CXX='-non_shared'
+	    # CC pic flag -KPIC is the default.
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+	case $cc_basename in
+	  KCC*)
+	    # KAI C++ Compiler
+	    lt_prog_compiler_wl_CXX='--backend -Wl,'
+	    lt_prog_compiler_pic_CXX='-fPIC'
+	    ;;
+	  ecpc* )
+	    # old Intel C++ for x86_64 which still supported -KPIC.
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    lt_prog_compiler_static_CXX='-static'
+	    ;;
+	  icpc* )
+	    # Intel C++, used to be incompatible with GCC.
+	    # ICC 10 doesn't accept -KPIC any more.
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_pic_CXX='-fPIC'
+	    lt_prog_compiler_static_CXX='-static'
+	    ;;
+	  pgCC* | pgcpp*)
+	    # Portland Group C++ compiler
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_pic_CXX='-fpic'
+	    lt_prog_compiler_static_CXX='-Bstatic'
+	    ;;
+	  cxx*)
+	    # Compaq C++
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    lt_prog_compiler_pic_CXX=
+	    lt_prog_compiler_static_CXX='-non_shared'
+	    ;;
+	  xlc* | xlC* | bgxl[cC]* | mpixl[cC]*)
+	    # IBM XL 8.0, 9.0 on PPC and BlueGene
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_pic_CXX='-qpic'
+	    lt_prog_compiler_static_CXX='-qstaticlink'
+	    ;;
+	  *)
+	    case `$CC -V 2>&1 | sed 5q` in
+	    *Sun\ C*)
+	      # Sun C++ 5.9
+	      lt_prog_compiler_pic_CXX='-KPIC'
+	      lt_prog_compiler_static_CXX='-Bstatic'
+	      lt_prog_compiler_wl_CXX='-Qoption ld '
+	      ;;
+	    esac
+	    ;;
+	esac
+	;;
+      lynxos*)
+	;;
+      m88k*)
+	;;
+      mvs*)
+	case $cc_basename in
+	  cxx*)
+	    lt_prog_compiler_pic_CXX='-W c,exportall'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      netbsd* | netbsdelf*-gnu)
+	;;
+      *qnx* | *nto*)
+        # QNX uses GNU C++, but need to define -shared option too, otherwise
+        # it will coredump.
+        lt_prog_compiler_pic_CXX='-fPIC -shared'
+        ;;
+      osf3* | osf4* | osf5*)
+	case $cc_basename in
+	  KCC*)
+	    lt_prog_compiler_wl_CXX='--backend -Wl,'
+	    ;;
+	  RCC*)
+	    # Rational C++ 2.4.1
+	    lt_prog_compiler_pic_CXX='-pic'
+	    ;;
+	  cxx*)
+	    # Digital/Compaq C++
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    lt_prog_compiler_pic_CXX=
+	    lt_prog_compiler_static_CXX='-non_shared'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      psos*)
+	;;
+      solaris*)
+	case $cc_basename in
+	  CC* | sunCC*)
+	    # Sun C++ 4.2, 5.x and Centerline C++
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    lt_prog_compiler_static_CXX='-Bstatic'
+	    lt_prog_compiler_wl_CXX='-Qoption ld '
+	    ;;
+	  gcx*)
+	    # Green Hills C++ Compiler
+	    lt_prog_compiler_pic_CXX='-PIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      sunos4*)
+	case $cc_basename in
+	  CC*)
+	    # Sun C++ 4.x
+	    lt_prog_compiler_pic_CXX='-pic'
+	    lt_prog_compiler_static_CXX='-Bstatic'
+	    ;;
+	  lcc*)
+	    # Lucid
+	    lt_prog_compiler_pic_CXX='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+	case $cc_basename in
+	  CC*)
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    lt_prog_compiler_static_CXX='-Bstatic'
+	    ;;
+	esac
+	;;
+      tandem*)
+	case $cc_basename in
+	  NCC*)
+	    # NonStop-UX NCC 3.20
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      vxworks*)
+	;;
+      *)
+	lt_prog_compiler_can_build_shared_CXX=no
+	;;
+    esac
+  fi
+
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic_CXX=
+    ;;
+  *)
+    lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC"
+    ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+if ${lt_cv_prog_compiler_pic_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; }
+lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic_works_CXX=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_pic_works_CXX=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then
+    case $lt_prog_compiler_pic_CXX in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;;
+     esac
+else
+    lt_prog_compiler_pic_CXX=
+     lt_prog_compiler_can_build_shared_CXX=no
+fi
+
+fi
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_static_works_CXX=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler_static_works_CXX=yes
+       fi
+     else
+       lt_cv_prog_compiler_static_works_CXX=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then
+    :
+else
+    lt_prog_compiler_static_CXX=
+fi
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o_CXX=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o_CXX=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; }
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o_CXX=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o_CXX=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+  if test "$hard_links" = no; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+  export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+  case $host_os in
+  aix[4-9]*)
+    # If we're using GNU nm, then we don't want the "-C" option.
+    # -C means demangle to AIX nm, but means don't demangle with GNU nm
+    # Also, AIX nm treats weak defined symbols like other global defined
+    # symbols, whereas GNU nm marks them as "W".
+    if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+      export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    else
+      export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    fi
+    ;;
+  pw32*)
+    export_symbols_cmds_CXX="$ltdll_cmds"
+    ;;
+  cygwin* | mingw* | cegcc*)
+    case $cc_basename in
+    cl*)
+      exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+      ;;
+    *)
+      export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+      exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+      ;;
+    esac
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    link_all_deplibs_CXX=no
+    ;;
+  *)
+    export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+    ;;
+  esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5
+$as_echo "$ld_shlibs_CXX" >&6; }
+test "$ld_shlibs_CXX" = no && can_build_shared=no
+
+with_gnu_ld_CXX=$with_gnu_ld
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc_CXX" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc_CXX=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds_CXX in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  $RM conftest*
+	echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+	if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } 2>conftest.err; then
+	  soname=conftest
+	  lib=conftest
+	  libobjs=conftest.$ac_objext
+	  deplibs=
+	  wl=$lt_prog_compiler_wl_CXX
+	  pic_flag=$lt_prog_compiler_pic_CXX
+	  compiler_flags=-v
+	  linker_flags=-v
+	  verstring=
+	  output_objdir=.
+	  libname=conftest
+	  lt_save_allow_undefined_flag=$allow_undefined_flag_CXX
+	  allow_undefined_flag_CXX=
+	  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+  (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+	  then
+	    lt_cv_archive_cmds_need_lc_CXX=no
+	  else
+	    lt_cv_archive_cmds_need_lc_CXX=yes
+	  fi
+	  allow_undefined_flag_CXX=$lt_save_allow_undefined_flag
+	else
+	  cat conftest.err 1>&5
+	fi
+	$RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5
+$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; }
+      archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[4-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[45]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl*)
+    # Native MSVC
+    libname_spec='$name'
+    soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+    library_names_spec='${libname}.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec="$LIB"
+      if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC wrapper
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[23].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[01]* | freebsdelf3.[01]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+  freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[3-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux # correct to gnu/linux during the next big refactor
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \
+	 LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\""
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  if  ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+  lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+
+fi
+
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*)	need_version=yes ;;
+    *)				need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+	;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action_CXX=
+if test -n "$hardcode_libdir_flag_spec_CXX" ||
+   test -n "$runpath_var_CXX" ||
+   test "X$hardcode_automatic_CXX" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$hardcode_direct_CXX" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no &&
+     test "$hardcode_minus_L_CXX" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action_CXX=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action_CXX=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action_CXX=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5
+$as_echo "$hardcode_action_CXX" >&6; }
+
+if test "$hardcode_action_CXX" = relink ||
+   test "$inherit_rpath_CXX" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+
+
+
+
+
+
+  fi # test -n "$compiler"
+
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+  LDCXX=$LD
+  LD=$lt_save_LD
+  GCC=$lt_save_GCC
+  with_gnu_ld=$lt_save_with_gnu_ld
+  lt_cv_path_LDCXX=$lt_cv_path_LD
+  lt_cv_path_LD=$lt_save_path_LD
+  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test "$_lt_caught_CXX_error" != yes
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+        ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+# Set up the libsigrok version defines.
+SR_PACKAGE_VERSION=0.4.0
+sr_git_deps=
+# Check if we can get revision information from git.
+sr_head=`git -C "$srcdir" rev-parse --verify --short HEAD 2>&5`
+
+if test "$?" = 0 && test "x$sr_head" != x; then :
+  	test ! -f "$srcdir/.git/HEAD" \
+		|| sr_git_deps="$sr_git_deps \$(top_srcdir)/.git/HEAD"
+
+	sr_head_name=`git -C "$srcdir" rev-parse --symbolic-full-name HEAD 2>&5`
+	if test "$?" = 0 && test -f "$srcdir/.git/$sr_head_name"; then :
+  sr_git_deps="$sr_git_deps \$(top_srcdir)/.git/$sr_head_name"
+fi
+
+	# Append the revision hash unless we are exactly on a tagged release.
+	git -C "$srcdir" describe --match "libsigrok-0.4.0" \
+		--exact-match >&5 2>&5 \
+		|| SR_PACKAGE_VERSION="$SR_PACKAGE_VERSION-git-$sr_head"
+
+fi
+# Use $(wildcard) so that things do not break if for whatever
+# reason these files do not exist anymore at make time.
+if test -n "$sr_git_deps"; then :
+  CONFIG_STATUS_DEPENDENCIES=${CONFIG_STATUS_DEPENDENCIES}${CONFIG_STATUS_DEPENDENCIES:+' '}"\$(wildcard$sr_git_deps)"
+fi
+
+$as_echo "#define SR_PACKAGE_VERSION_MAJOR 0" >>confdefs.h
+
+$as_echo "#define SR_PACKAGE_VERSION_MINOR 4" >>confdefs.h
+
+$as_echo "#define SR_PACKAGE_VERSION_MICRO 0" >>confdefs.h
+
+cat >>confdefs.h <<_ACEOF
+#define SR_PACKAGE_VERSION_STRING "$SR_PACKAGE_VERSION"
+_ACEOF
+
+
+
+# Library version for libsigrok (NOT the same as the package version).
+# Carefully read the libtool docs before updating these numbers!
+# The algorithm for determining which number to change (and how) is nontrivial!
+# http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
+# Format: current:revision:age.
+SR_LIB_VERSION=3:0:0
+
+$as_echo "#define SR_LIB_VERSION_CURRENT 3" >>confdefs.h
+
+$as_echo "#define SR_LIB_VERSION_REVISION 0" >>confdefs.h
+
+$as_echo "#define SR_LIB_VERSION_AGE 0" >>confdefs.h
+
+$as_echo "#define SR_LIB_VERSION_STRING \"3:0:0\"" >>confdefs.h
+
+
+
+ if test -z "${host_os##mingw*}" || test -z "${host_os##cygwin*}"; then
+  WIN32_TRUE=
+  WIN32_FALSE='#'
+else
+  WIN32_TRUE='#'
+  WIN32_FALSE=
+fi
+
+
+#############################
+##  Optional dependencies  ##
+#############################
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Initialize pkg-config.
+# We require at least 0.22, as "Requires.private" behaviour changed there.
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+  ac_pt_PKG_CONFIG=$PKG_CONFIG
+  # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_PKG_CONFIG" = x; then
+    PKG_CONFIG=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    PKG_CONFIG=$ac_pt_PKG_CONFIG
+  fi
+else
+  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=0.22
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	else
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+		PKG_CONFIG=""
+	fi
+fi
+
+# Keep track of all checked modules so we can list them at the end.
+sr_pkglibs_summary=
+sr_pkg_check_summary_append() {
+	sr_aligned=`printf '%.32s' "$1................................"`
+	sr_pkglibs_summary="${sr_pkglibs_summary} - $sr_aligned $2"'
+'
+}
+
+
+# Collect the pkg-config module names of all dependencies in SR_PKGLIBS.
+# These are used to derive the compiler flags and for the "Requires.private"
+# field in the generated libsigrok.pc file.
+SR_PKGLIBS=
+sr_deps_avail=
+
+SR_PKGLIBS_TESTS=
+SR_PKGLIBS_CXX=
+SR_PKGLIBS_PYTHON=
+SR_PKGLIBS_RUBY=
+SR_EXTRA_LIBS=
+
+
+# Check whether --with-libserialport was given.
+if test "${with_libserialport+set}" = set; then :
+  withval=$with_libserialport;
+fi
+
+if test "x$with_libserialport" = xno; then :
+  sr_have_libserialport=no
+elif test "x$sr_have_libserialport" != xyes; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libserialport" >&5
+$as_echo_n "checking for libserialport... " >&6; }
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libserialport >= 0.1.1\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libserialport >= 0.1.1") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_libserialport=yes
+	SR_PKGLIBS=${SR_PKGLIBS}${SR_PKGLIBS:+' '}"libserialport >= 0.1.1"
+	sr_libserialport_version=`$PKG_CONFIG --modversion "libserialport >= 0.1.1" 2>&5`
+	sr_pkg_check_summary_append "libserialport >= 0.1.1" "$sr_libserialport_version"
+else
+  	sr_pkg_check_summary_append "libserialport >= 0.1.1" no
+	sr_have_libserialport=no sr_libserialport_version=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_have_libserialport" >&5
+$as_echo "$sr_have_libserialport" >&6; }
+fi
+if test "x$with_libserialport$sr_have_libserialport" = xyesno; then :
+  as_fn_error $? "libserialport support requested, but it was not found." "$LINENO" 5
+fi
+if test "x$sr_have_libserialport" = xyes; then :
+
+	sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}"libserialport"
+
+$as_echo "#define HAVE_LIBSERIALPORT 1" >>confdefs.h
+
+fi
+ if test "x$sr_have_libserialport" = xyes; then
+  NEED_SERIAL_TRUE=
+  NEED_SERIAL_FALSE='#'
+else
+  NEED_SERIAL_TRUE='#'
+  NEED_SERIAL_FALSE=
+fi
+
+
+if test "x$sr_have_libserialport" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define CONF_LIBSERIALPORT_VERSION "$sr_libserialport_version"
+_ACEOF
+
+fi
+
+
+
+# Check whether --with-libftdi was given.
+if test "${with_libftdi+set}" = set; then :
+  withval=$with_libftdi;
+fi
+
+if test "x$with_libftdi" = xno; then :
+  sr_have_libftdi=no
+elif test "x$sr_have_libftdi" != xyes; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libftdi" >&5
+$as_echo_n "checking for libftdi... " >&6; }
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libftdi1 >= 1.0\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libftdi1 >= 1.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_libftdi=yes
+	SR_PKGLIBS=${SR_PKGLIBS}${SR_PKGLIBS:+' '}"libftdi1 >= 1.0"
+	sr_libftdi_version=`$PKG_CONFIG --modversion "libftdi1 >= 1.0" 2>&5`
+	sr_pkg_check_summary_append "libftdi1 >= 1.0" "$sr_libftdi_version"
+else
+  	sr_pkg_check_summary_append "libftdi1 >= 1.0" no
+	if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libftdi >= 0.16\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libftdi >= 0.16") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_libftdi=yes
+	SR_PKGLIBS=${SR_PKGLIBS}${SR_PKGLIBS:+' '}"libftdi >= 0.16"
+	sr_libftdi_version=`$PKG_CONFIG --modversion "libftdi >= 0.16" 2>&5`
+	sr_pkg_check_summary_append "libftdi >= 0.16" "$sr_libftdi_version"
+else
+  	sr_pkg_check_summary_append "libftdi >= 0.16" no
+	sr_have_libftdi=no sr_libftdi_version=
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_have_libftdi" >&5
+$as_echo "$sr_have_libftdi" >&6; }
+fi
+if test "x$with_libftdi$sr_have_libftdi" = xyesno; then :
+  as_fn_error $? "libftdi support requested, but it was not found." "$LINENO" 5
+fi
+if test "x$sr_have_libftdi" = xyes; then :
+
+	sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}"libftdi"
+
+$as_echo "#define HAVE_LIBFTDI 1" >>confdefs.h
+
+fi
+
+if test "x$sr_have_libftdi" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define CONF_LIBFTDI_VERSION "$sr_libftdi_version"
+_ACEOF
+
+fi
+
+
+# FreeBSD comes with an "integrated" libusb-1.0-style USB API.
+# This means libusb-1.0 is always available; no need to check for it.
+# On Windows, require the latest version we can get our hands on,
+# until the new generic event handling has seen an official release.
+case $host_os in #(
+  freebsd*) :
+    sr_libusb_req='libusb-1.0' sr_have_libusb=yes ;; #(
+  mingw*) :
+    sr_libusb_req='libusb-1.0 >= 1.0.20' ;; #(
+  *) :
+    sr_libusb_req='libusb-1.0 >= 1.0.16' ;;
+esac
+
+
+# Check whether --with-libusb was given.
+if test "${with_libusb+set}" = set; then :
+  withval=$with_libusb;
+fi
+
+if test "x$with_libusb" = xno; then :
+  sr_have_libusb=no
+elif test "x$sr_have_libusb" != xyes; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libusb" >&5
+$as_echo_n "checking for libusb... " >&6; }
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$sr_libusb_req\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$sr_libusb_req") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_libusb=yes
+	SR_PKGLIBS=${SR_PKGLIBS}${SR_PKGLIBS:+' '}"$sr_libusb_req"
+	sr_libusb_version=`$PKG_CONFIG --modversion "$sr_libusb_req" 2>&5`
+	sr_pkg_check_summary_append "$sr_libusb_req" "$sr_libusb_version"
+else
+  	sr_pkg_check_summary_append "$sr_libusb_req" no
+	sr_have_libusb=no sr_libusb_version=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_have_libusb" >&5
+$as_echo "$sr_have_libusb" >&6; }
+fi
+if test "x$with_libusb$sr_have_libusb" = xyesno; then :
+  as_fn_error $? "libusb support requested, but it was not found." "$LINENO" 5
+fi
+if test "x$sr_have_libusb" = xyes; then :
+
+	sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}"libusb"
+
+$as_echo "#define HAVE_LIBUSB_1_0 1" >>confdefs.h
+
+fi
+ if test "x$sr_have_libusb" = xyes; then
+  NEED_USB_TRUE=
+  NEED_USB_FALSE='#'
+else
+  NEED_USB_TRUE='#'
+  NEED_USB_FALSE=
+fi
+
+
+if test "x$sr_have_libusb" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define CONF_LIBUSB_1_0_VERSION "$sr_libusb_version"
+_ACEOF
+
+fi
+
+
+
+# Check whether --with-librevisa was given.
+if test "${with_librevisa+set}" = set; then :
+  withval=$with_librevisa;
+fi
+
+if test "x$with_librevisa" = xno; then :
+  sr_have_librevisa=no
+elif test "x$sr_have_librevisa" != xyes; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for librevisa" >&5
+$as_echo_n "checking for librevisa... " >&6; }
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"librevisa >= 0.0.20130412\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "librevisa >= 0.0.20130412") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_librevisa=yes
+	SR_PKGLIBS=${SR_PKGLIBS}${SR_PKGLIBS:+' '}"librevisa >= 0.0.20130412"
+	sr_librevisa_version=`$PKG_CONFIG --modversion "librevisa >= 0.0.20130412" 2>&5`
+	sr_pkg_check_summary_append "librevisa >= 0.0.20130412" "$sr_librevisa_version"
+else
+  	sr_pkg_check_summary_append "librevisa >= 0.0.20130412" no
+	sr_have_librevisa=no sr_librevisa_version=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_have_librevisa" >&5
+$as_echo "$sr_have_librevisa" >&6; }
+fi
+if test "x$with_librevisa$sr_have_librevisa" = xyesno; then :
+  as_fn_error $? "librevisa support requested, but it was not found." "$LINENO" 5
+fi
+if test "x$sr_have_librevisa" = xyes; then :
+
+	sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}"librevisa"
+
+$as_echo "#define HAVE_LIBREVISA 1" >>confdefs.h
+
+fi
+ if test "x$sr_have_librevisa" = xyes; then
+  NEED_VISA_TRUE=
+  NEED_VISA_FALSE='#'
+else
+  NEED_VISA_TRUE='#'
+  NEED_VISA_FALSE=
+fi
+
+
+if test "x$sr_have_librevisa" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define CONF_LIBREVISA_VERSION "$sr_librevisa_version"
+_ACEOF
+
+fi
+
+
+
+# Check whether --with-libgpib was given.
+if test "${with_libgpib+set}" = set; then :
+  withval=$with_libgpib;
+fi
+
+if test "x$with_libgpib" = xno; then :
+  sr_have_libgpib=no
+elif test "x$sr_have_libgpib" != xyes; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libgpib" >&5
+$as_echo_n "checking for libgpib... " >&6; }
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libgpib\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libgpib") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_libgpib=yes
+	SR_PKGLIBS=${SR_PKGLIBS}${SR_PKGLIBS:+' '}"libgpib"
+	sr_libgpib_version=`$PKG_CONFIG --modversion "libgpib" 2>&5`
+	sr_pkg_check_summary_append "libgpib" "$sr_libgpib_version"
+else
+  	sr_pkg_check_summary_append "libgpib" no
+	sr_have_libgpib=no sr_libgpib_version=
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_have_libgpib" >&5
+$as_echo "$sr_have_libgpib" >&6; }
+fi
+if test "x$with_libgpib$sr_have_libgpib" = xyesno; then :
+  as_fn_error $? "libgpib support requested, but it was not found." "$LINENO" 5
+fi
+if test "x$sr_have_libgpib" = xyes; then :
+
+	sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}"libgpib"
+
+$as_echo "#define HAVE_LIBGPIB 1" >>confdefs.h
+
+fi
+ if test "x$sr_have_libgpib" = xyes; then
+  NEED_GPIB_TRUE=
+  NEED_GPIB_FALSE='#'
+else
+  NEED_GPIB_TRUE='#'
+  NEED_GPIB_FALSE=
+fi
+
+
+if test "x$sr_have_libgpib" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define CONF_LIBGPIB_VERSION "$sr_libgpib_version"
+_ACEOF
+
+fi
+
+
+
+# Check whether --with-libieee1284 was given.
+if test "${with_libieee1284+set}" = set; then :
+  withval=$with_libieee1284;
+fi
+
+if test "x$with_libieee1284" = xno; then :
+  sr_have_libieee1284=no
+elif test "x$sr_have_libieee1284" != xyes; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libieee1284" >&5
+$as_echo_n "checking for libieee1284... " >&6; }
+
+	sr_save_LIBS=$LIBS
+	LIBS="-lieee1284 $LIBS"
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ieee1284.h>
+int
+main ()
+{
+(void) ieee1284_open(0, 0, 0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  sr_have_libieee1284=yes
+else
+  sr_have_libieee1284=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+	LIBS=$sr_save_LIBS
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_have_libieee1284" >&5
+$as_echo "$sr_have_libieee1284" >&6; }
+fi
+if test "x$with_libieee1284$sr_have_libieee1284" = xyesno; then :
+  as_fn_error $? "libieee1284 support requested, but it was not found." "$LINENO" 5
+fi
+if test "x$sr_have_libieee1284" = xyes; then :
+
+	sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}"libieee1284"
+
+$as_echo "#define HAVE_LIBIEEE1284 1" >>confdefs.h
+
+fi
+
+sr_pkg_check_summary_append "libieee1284" "$sr_have_libieee1284"
+
+if test "x$sr_have_libieee1284" = xyes; then :
+  SR_EXTRA_LIBS=-lieee1284${SR_EXTRA_LIBS:+' '}$SR_EXTRA_LIBS
+fi
+
+######################
+##  Feature checks  ##
+######################
+
+# The Check unit testing framework is optional. Disable if not found.
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"check >= 0.9.4\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "check >= 0.9.4") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_check=yes
+	SR_PKGLIBS_TESTS=${SR_PKGLIBS_TESTS}${SR_PKGLIBS_TESTS:+' '}"check >= 0.9.4"
+	sr_check_version=`$PKG_CONFIG --modversion "check >= 0.9.4" 2>&5`
+	sr_pkg_check_summary_append "check >= 0.9.4" "$sr_check_version"
+else
+  	sr_pkg_check_summary_append "check >= 0.9.4" no
+	sr_have_check=no sr_check_version=
+fi
+
+ if test "x$sr_have_check" = xyes; then
+  HAVE_CHECK_TRUE=
+  HAVE_CHECK_FALSE='#'
+else
+  HAVE_CHECK_TRUE='#'
+  HAVE_CHECK_FALSE=
+fi
+
+
+# Enable the C99 standard if possible, and enforce the use
+# of SR_API to explicitly mark all public API functions.
+SR_EXTRA_CFLAGS=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking compiler flag for C99" >&5
+$as_echo_n "checking compiler flag for C99... " >&6; }
+sr_ccf_result=no
+sr_ccf_save_CPPFLAGS=$CPPFLAGS
+for sr_flag in -std=c99 -c99 -AC99 -qlanglvl=extc99
+do
+	CPPFLAGS="$sr_ccf_save_CPPFLAGS $sr_flag"
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  sr_ccf_result=$sr_flag
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	test "x$sr_ccf_result" = xno || break
+done
+CPPFLAGS=$sr_ccf_save_CPPFLAGS
+if test "x$sr_ccf_result" != xno; then :
+  SR_EXTRA_CFLAGS=${SR_EXTRA_CFLAGS}${SR_EXTRA_CFLAGS:+' '}$sr_ccf_result
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_ccf_result" >&5
+$as_echo "$sr_ccf_result" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking compiler flag for visibility" >&5
+$as_echo_n "checking compiler flag for visibility... " >&6; }
+sr_ccf_result=no
+sr_ccf_save_CPPFLAGS=$CPPFLAGS
+for sr_flag in -fvisibility=hidden
+do
+	CPPFLAGS="$sr_ccf_save_CPPFLAGS $sr_flag"
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  sr_ccf_result=$sr_flag
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	test "x$sr_ccf_result" = xno || break
+done
+CPPFLAGS=$sr_ccf_save_CPPFLAGS
+if test "x$sr_ccf_result" != xno; then :
+  SR_EXTRA_CFLAGS=${SR_EXTRA_CFLAGS}${SR_EXTRA_CFLAGS:+' '}$sr_ccf_result
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_ccf_result" >&5
+$as_echo "$sr_ccf_result" >&6; }
+
+
+
+# Check whether --enable-warnings was given.
+if test "${enable_warnings+set}" = set; then :
+  enableval=$enable_warnings; sr_enable_warnings=$enableval
+else
+  sr_enable_warnings=max
+fi
+# Test whether the compiler accepts each flag.  Look at standard output,
+# since GCC only shows a warning message if an option is not supported.
+sr_check_compile_warning_flags() {
+	for sr_flag
+	do
+		sr_cc_out=`$sr_cc $sr_warning_flags $sr_flag -c "$sr_conftest" 2>&1 || echo failed`
+		if test "$?$sr_cc_out" = 0; then :
+  sr_warning_flags=${sr_warning_flags}${sr_warning_flags:+' '}$sr_flag
+else
+  $as_echo "$sr_cc: $sr_cc_out" >&5
+fi
+		rm -f "conftest.${OBJEXT:-o}"
+	done
+}
+
+case $ac_compile in #(
+  *'$CXXFLAGS '*) :
+    sr_lang='C++' sr_cc=$CXX sr_conftest="conftest.${ac_ext:-cc}" ;; #(
+  *'$CFLAGS '*) :
+    sr_lang=C sr_cc=$CC sr_conftest="conftest.${ac_ext:-c}" ;; #(
+  *) :
+    as_fn_error $? "current language is neither C nor C++" "$LINENO" 5 ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which $sr_lang compiler warning flags to use" >&5
+$as_echo_n "checking which $sr_lang compiler warning flags to use... " >&6; }
+sr_warning_flags=
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int main(int argc, char** argv) { return (argv != 0) ? argc : 0; }
+
+_ACEOF
+case $sr_enable_warnings in #(
+  no) :
+     ;; #(
+  min) :
+    sr_check_compile_warning_flags -Wall ;; #(
+  fatal) :
+    sr_check_compile_warning_flags -Wall -Wextra -Wmissing-prototypes -Werror ;; #(
+  *) :
+    sr_check_compile_warning_flags -Wall -Wextra -Wmissing-prototypes ;;
+esac
+rm -f "$sr_conftest"
+SR_WFLAGS=$sr_warning_flags
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${sr_warning_flags:-none}" >&5
+$as_echo "${sr_warning_flags:-none}" >&6; }
+
+# Check host characteristics.
+# Check whether --enable-largefile was given.
+if test "${enable_largefile+set}" = set; then :
+  enableval=$enable_largefile;
+fi
+
+if test "$enable_largefile" != no; then
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5
+$as_echo_n "checking for special C compiler options needed for large files... " >&6; }
+if ${ac_cv_sys_largefile_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_sys_largefile_CC=no
+     if test "$GCC" != yes; then
+       ac_save_CC=$CC
+       while :; do
+	 # IRIX 6.2 and later do not support large files by default,
+	 # so use the C compiler's -n32 option if that helps.
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+	 if ac_fn_c_try_compile "$LINENO"; then :
+  break
+fi
+rm -f core conftest.err conftest.$ac_objext
+	 CC="$CC -n32"
+	 if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_sys_largefile_CC=' -n32'; break
+fi
+rm -f core conftest.err conftest.$ac_objext
+	 break
+       done
+       CC=$ac_save_CC
+       rm -f conftest.$ac_ext
+    fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5
+$as_echo "$ac_cv_sys_largefile_CC" >&6; }
+  if test "$ac_cv_sys_largefile_CC" != no; then
+    CC=$CC$ac_cv_sys_largefile_CC
+  fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5
+$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; }
+if ${ac_cv_sys_file_offset_bits+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  while :; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_sys_file_offset_bits=no; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#define _FILE_OFFSET_BITS 64
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_sys_file_offset_bits=64; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  ac_cv_sys_file_offset_bits=unknown
+  break
+done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5
+$as_echo "$ac_cv_sys_file_offset_bits" >&6; }
+case $ac_cv_sys_file_offset_bits in #(
+  no | unknown) ;;
+  *)
+cat >>confdefs.h <<_ACEOF
+#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits
+_ACEOF
+;;
+esac
+rm -rf conftest*
+  if test $ac_cv_sys_file_offset_bits = unknown; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5
+$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; }
+if ${ac_cv_sys_large_files+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  while :; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_sys_large_files=no; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#define _LARGE_FILES 1
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_sys_large_files=1; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  ac_cv_sys_large_files=unknown
+  break
+done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5
+$as_echo "$ac_cv_sys_large_files" >&6; }
+case $ac_cv_sys_large_files in #(
+  no | unknown) ;;
+  *)
+cat >>confdefs.h <<_ACEOF
+#define _LARGE_FILES $ac_cv_sys_large_files
+_ACEOF
+;;
+esac
+rm -rf conftest*
+  fi
+
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if ${ac_cv_c_bigendian+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_bigendian=unknown
+    # See if we're dealing with a universal compiler.
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifndef __APPLE_CC__
+	       not a universal capable compiler
+	     #endif
+	     typedef int dummy;
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+	# Check for potential -arch flags.  It is not universal unless
+	# there are at least two -arch flags with different values.
+	ac_arch=
+	ac_prev=
+	for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+	 if test -n "$ac_prev"; then
+	   case $ac_word in
+	     i?86 | x86_64 | ppc | ppc64)
+	       if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+		 ac_arch=$ac_word
+	       else
+		 ac_cv_c_bigendian=universal
+		 break
+	       fi
+	       ;;
+	   esac
+	   ac_prev=
+	 elif test "x$ac_word" = "x-arch"; then
+	   ac_prev=arch
+	 fi
+       done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if sys/param.h defines the BYTE_ORDER macro.
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+	     #include <sys/param.h>
+
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+		     && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+		     && LITTLE_ENDIAN)
+	      bogus endian macros
+	     #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+		#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+		 not big endian
+		#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+	      bogus endian macros
+	     #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to _BIG_ENDIAN or not.
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+		 not big endian
+		#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # Compile a test program.
+      if test "$cross_compiling" = yes; then :
+  # Try to guess by grepping values from an object file.
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+short int ascii_mm[] =
+		  { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+		short int ascii_ii[] =
+		  { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+		int use_ascii (int i) {
+		  return ascii_mm[i] + ascii_ii[i];
+		}
+		short int ebcdic_ii[] =
+		  { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+		short int ebcdic_mm[] =
+		  { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+		int use_ebcdic (int i) {
+		  return ebcdic_mm[i] + ebcdic_ii[i];
+		}
+		extern int foo;
+
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+	      ac_cv_c_bigendian=yes
+	    fi
+	    if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+	      if test "$ac_cv_c_bigendian" = unknown; then
+		ac_cv_c_bigendian=no
+	      else
+		# finding both strings is unlikely to happen, but who knows?
+		ac_cv_c_bigendian=unknown
+	      fi
+	    fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+	     /* Are we little or big endian?  From Harbison&Steele.  */
+	     union
+	     {
+	       long int l;
+	       char c[sizeof (long int)];
+	     } u;
+	     u.l = 1;
+	     return u.c[sizeof (long int) - 1] == 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ac_cv_c_bigendian=no
+else
+  ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+   yes)
+     $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
+;; #(
+   no)
+      ;; #(
+   universal)
+
+$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+     ;; #(
+   *)
+     as_fn_error $? "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+
+for ac_header in sys/mman.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_mman_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_MMAN_H 1
+_ACEOF
+ sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}sys_mman_h
+fi
+
+done
+
+for ac_header in sys/ioctl.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "sys/ioctl.h" "ac_cv_header_sys_ioctl_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_ioctl_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_IOCTL_H 1
+_ACEOF
+ sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}sys_ioctl_h
+fi
+
+done
+
+for ac_header in sys/timerfd.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "sys/timerfd.h" "ac_cv_header_sys_timerfd_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_timerfd_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_TIMERFD_H 1
+_ACEOF
+ sr_deps_avail=${sr_deps_avail}${sr_deps_avail:+' '}sys_timerfd_h
+fi
+
+done
+
+
+# We need to link against the Winsock2 library for SCPI over TCP.
+case $host_os in #(
+  mingw*) :
+    SR_EXTRA_LIBS=-lws2_32${SR_EXTRA_LIBS:+' '}$SR_EXTRA_LIBS ;; #(
+  *) :
+     ;;
+esac
+
+# libm (the standard math library) is always needed.
+sr_sl_save_LIBS=$LIBS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pow" >&5
+$as_echo_n "checking for library containing pow... " >&6; }
+if ${ac_cv_search_pow+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pow ();
+int
+main ()
+{
+return pow ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' m; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib $SR_EXTRA_LIBS $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_pow=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_pow+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_pow+:} false; then :
+
+else
+  ac_cv_search_pow=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pow" >&5
+$as_echo "$ac_cv_search_pow" >&6; }
+ac_res=$ac_cv_search_pow
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+LIBS=$sr_sl_save_LIBS
+case $ac_cv_search_pow in #(
+  no*) :
+     ;; #(
+  *) :
+    SR_EXTRA_LIBS=$ac_cv_search_pow${SR_EXTRA_LIBS:+' '}$SR_EXTRA_LIBS ;;
+esac
+
+
+# RPC is only needed for VXI support.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for RPC support" >&5
+$as_echo_n "checking for RPC support... " >&6; }
+if ${sr_cv_have_rpc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <rpc/rpc.h>
+CLIENT *rpc_test(void);
+int
+main ()
+{
+(void) clnt_create("", 0, 0, "");
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  sr_cv_have_rpc=yes
+else
+  sr_cv_have_rpc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_cv_have_rpc" >&5
+$as_echo "$sr_cv_have_rpc" >&6; }
+if test "x$sr_cv_have_rpc" = xyes; then :
+
+$as_echo "#define HAVE_RPC 1" >>confdefs.h
+
+fi
+# VXI support is only compiled if RPC support was found.
+ if test "x$sr_cv_have_rpc" = xyes; then
+  NEED_RPC_TRUE=
+  NEED_RPC_FALSE='#'
+else
+  NEED_RPC_TRUE='#'
+  NEED_RPC_FALSE=
+fi
+
+
+########################
+##  Hardware drivers  ##
+########################
+
+# Keep track of all drivers so we can list them at the end.
+sr_driver_summary=
+sr_driver_summary_append() {
+	sr_aligned=`printf '%.32s' "$1................................"`
+	sr_driver_summary="${sr_driver_summary} - $sr_aligned $2"'
+'
+}
+
+
+# Check whether the sr_deps_avail list contains all of the arguments.
+# Unavailable dependencies are collected in sr_deps_missing.
+sr_check_driver_deps() {
+	sr_deps_missing=
+	for sr_dep
+	do
+		case " $sr_deps_avail " in #(
+  *" $sr_dep "*) :
+     ;; #(
+  *) :
+    sr_deps_missing=${sr_deps_missing}${sr_deps_missing:+', '}$sr_dep ;;
+esac
+	done
+	test -z "$sr_deps_missing" || return 1
+}
+
+# Check whether --enable-all-drivers was given.
+if test "${enable_all_drivers+set}" = set; then :
+  enableval=$enable_all_drivers;
+else
+  enable_all_drivers=yes
+fi
+
+
+## _SR_DRIVER(Device name, driver-name, var-name, [dependencies...])
+
+
+## SR_DRIVER(Device name, driver-name, [dependencies...])
+
+
+
+	# Check whether --enable-agilent-dmm was given.
+if test "${enable_agilent_dmm+set}" = set; then :
+  enableval=$enable_agilent_dmm; HW_AGILENT_DMM=$enableval
+else
+  HW_AGILENT_DMM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_AGILENT_DMM" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_AGILENT_DMM=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "agilent-dmm" "$sr_hw_info"
+
+	 if test "x$HW_AGILENT_DMM" = xyes; then
+  HW_AGILENT_DMM_TRUE=
+  HW_AGILENT_DMM_FALSE='#'
+else
+  HW_AGILENT_DMM_TRUE='#'
+  HW_AGILENT_DMM_FALSE=
+fi
+
+	if test -z "$HW_AGILENT_DMM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_AGILENT_DMM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-appa-55ii was given.
+if test "${enable_appa_55ii+set}" = set; then :
+  enableval=$enable_appa_55ii; HW_APPA_55II=$enableval
+else
+  HW_APPA_55II=$enable_all_drivers
+fi
+
+
+	if test "x$HW_APPA_55II" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_APPA_55II=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "appa-55ii" "$sr_hw_info"
+
+	 if test "x$HW_APPA_55II" = xyes; then
+  HW_APPA_55II_TRUE=
+  HW_APPA_55II_FALSE='#'
+else
+  HW_APPA_55II_TRUE='#'
+  HW_APPA_55II_FALSE=
+fi
+
+	if test -z "$HW_APPA_55II_TRUE"; then :
+
+$as_echo "#define HAVE_HW_APPA_55II 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-asix-sigma was given.
+if test "${enable_asix_sigma+set}" = set; then :
+  enableval=$enable_asix_sigma; HW_ASIX_SIGMA=$enableval
+else
+  HW_ASIX_SIGMA=$enable_all_drivers
+fi
+
+
+	if test "x$HW_ASIX_SIGMA" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libftdi \
+			|| HW_ASIX_SIGMA=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "asix-sigma" "$sr_hw_info"
+
+	 if test "x$HW_ASIX_SIGMA" = xyes; then
+  HW_ASIX_SIGMA_TRUE=
+  HW_ASIX_SIGMA_FALSE='#'
+else
+  HW_ASIX_SIGMA_TRUE='#'
+  HW_ASIX_SIGMA_FALSE=
+fi
+
+	if test -z "$HW_ASIX_SIGMA_TRUE"; then :
+
+$as_echo "#define HAVE_HW_ASIX_SIGMA 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-atten-pps3xxx was given.
+if test "${enable_atten_pps3xxx+set}" = set; then :
+  enableval=$enable_atten_pps3xxx; HW_ATTEN_PPS3XXX=$enableval
+else
+  HW_ATTEN_PPS3XXX=$enable_all_drivers
+fi
+
+
+	if test "x$HW_ATTEN_PPS3XXX" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_ATTEN_PPS3XXX=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "atten-pps3xxx" "$sr_hw_info"
+
+	 if test "x$HW_ATTEN_PPS3XXX" = xyes; then
+  HW_ATTEN_PPS3XXX_TRUE=
+  HW_ATTEN_PPS3XXX_FALSE='#'
+else
+  HW_ATTEN_PPS3XXX_TRUE='#'
+  HW_ATTEN_PPS3XXX_FALSE=
+fi
+
+	if test -z "$HW_ATTEN_PPS3XXX_TRUE"; then :
+
+$as_echo "#define HAVE_HW_ATTEN_PPS3XXX 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-baylibre-acme was given.
+if test "${enable_baylibre_acme+set}" = set; then :
+  enableval=$enable_baylibre_acme; HW_BAYLIBRE_ACME=$enableval
+else
+  HW_BAYLIBRE_ACME=$enable_all_drivers
+fi
+
+
+	if test "x$HW_BAYLIBRE_ACME" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps sys_timerfd_h \
+			|| HW_BAYLIBRE_ACME=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "baylibre-acme" "$sr_hw_info"
+
+	 if test "x$HW_BAYLIBRE_ACME" = xyes; then
+  HW_BAYLIBRE_ACME_TRUE=
+  HW_BAYLIBRE_ACME_FALSE='#'
+else
+  HW_BAYLIBRE_ACME_TRUE='#'
+  HW_BAYLIBRE_ACME_FALSE=
+fi
+
+	if test -z "$HW_BAYLIBRE_ACME_TRUE"; then :
+
+$as_echo "#define HAVE_HW_BAYLIBRE_ACME 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-beaglelogic was given.
+if test "${enable_beaglelogic+set}" = set; then :
+  enableval=$enable_beaglelogic; HW_BEAGLELOGIC=$enableval
+else
+  HW_BEAGLELOGIC=$enable_all_drivers
+fi
+
+
+	if test "x$HW_BEAGLELOGIC" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps sys_mman_h sys_ioctl_h \
+			|| HW_BEAGLELOGIC=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "beaglelogic" "$sr_hw_info"
+
+	 if test "x$HW_BEAGLELOGIC" = xyes; then
+  HW_BEAGLELOGIC_TRUE=
+  HW_BEAGLELOGIC_FALSE='#'
+else
+  HW_BEAGLELOGIC_TRUE='#'
+  HW_BEAGLELOGIC_FALSE=
+fi
+
+	if test -z "$HW_BEAGLELOGIC_TRUE"; then :
+
+$as_echo "#define HAVE_HW_BEAGLELOGIC 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-brymen-bm86x was given.
+if test "${enable_brymen_bm86x+set}" = set; then :
+  enableval=$enable_brymen_bm86x; HW_BRYMEN_BM86X=$enableval
+else
+  HW_BRYMEN_BM86X=$enable_all_drivers
+fi
+
+
+	if test "x$HW_BRYMEN_BM86X" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_BRYMEN_BM86X=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "brymen-bm86x" "$sr_hw_info"
+
+	 if test "x$HW_BRYMEN_BM86X" = xyes; then
+  HW_BRYMEN_BM86X_TRUE=
+  HW_BRYMEN_BM86X_FALSE='#'
+else
+  HW_BRYMEN_BM86X_TRUE='#'
+  HW_BRYMEN_BM86X_FALSE=
+fi
+
+	if test -z "$HW_BRYMEN_BM86X_TRUE"; then :
+
+$as_echo "#define HAVE_HW_BRYMEN_BM86X 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-brymen-dmm was given.
+if test "${enable_brymen_dmm+set}" = set; then :
+  enableval=$enable_brymen_dmm; HW_BRYMEN_DMM=$enableval
+else
+  HW_BRYMEN_DMM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_BRYMEN_DMM" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_BRYMEN_DMM=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "brymen-dmm" "$sr_hw_info"
+
+	 if test "x$HW_BRYMEN_DMM" = xyes; then
+  HW_BRYMEN_DMM_TRUE=
+  HW_BRYMEN_DMM_FALSE='#'
+else
+  HW_BRYMEN_DMM_TRUE='#'
+  HW_BRYMEN_DMM_FALSE=
+fi
+
+	if test -z "$HW_BRYMEN_DMM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_BRYMEN_DMM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-cem-dt-885x was given.
+if test "${enable_cem_dt_885x+set}" = set; then :
+  enableval=$enable_cem_dt_885x; HW_CEM_DT_885X=$enableval
+else
+  HW_CEM_DT_885X=$enable_all_drivers
+fi
+
+
+	if test "x$HW_CEM_DT_885X" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_CEM_DT_885X=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "cem-dt-885x" "$sr_hw_info"
+
+	 if test "x$HW_CEM_DT_885X" = xyes; then
+  HW_CEM_DT_885X_TRUE=
+  HW_CEM_DT_885X_FALSE='#'
+else
+  HW_CEM_DT_885X_TRUE='#'
+  HW_CEM_DT_885X_FALSE=
+fi
+
+	if test -z "$HW_CEM_DT_885X_TRUE"; then :
+
+$as_echo "#define HAVE_HW_CEM_DT_885X 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-center-3xx was given.
+if test "${enable_center_3xx+set}" = set; then :
+  enableval=$enable_center_3xx; HW_CENTER_3XX=$enableval
+else
+  HW_CENTER_3XX=$enable_all_drivers
+fi
+
+
+	if test "x$HW_CENTER_3XX" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_CENTER_3XX=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "center-3xx" "$sr_hw_info"
+
+	 if test "x$HW_CENTER_3XX" = xyes; then
+  HW_CENTER_3XX_TRUE=
+  HW_CENTER_3XX_FALSE='#'
+else
+  HW_CENTER_3XX_TRUE='#'
+  HW_CENTER_3XX_FALSE=
+fi
+
+	if test -z "$HW_CENTER_3XX_TRUE"; then :
+
+$as_echo "#define HAVE_HW_CENTER_3XX 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-chronovu-la was given.
+if test "${enable_chronovu_la+set}" = set; then :
+  enableval=$enable_chronovu_la; HW_CHRONOVU_LA=$enableval
+else
+  HW_CHRONOVU_LA=$enable_all_drivers
+fi
+
+
+	if test "x$HW_CHRONOVU_LA" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb libftdi \
+			|| HW_CHRONOVU_LA=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "chronovu-la" "$sr_hw_info"
+
+	 if test "x$HW_CHRONOVU_LA" = xyes; then
+  HW_CHRONOVU_LA_TRUE=
+  HW_CHRONOVU_LA_FALSE='#'
+else
+  HW_CHRONOVU_LA_TRUE='#'
+  HW_CHRONOVU_LA_FALSE=
+fi
+
+	if test -z "$HW_CHRONOVU_LA_TRUE"; then :
+
+$as_echo "#define HAVE_HW_CHRONOVU_LA 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-colead-slm was given.
+if test "${enable_colead_slm+set}" = set; then :
+  enableval=$enable_colead_slm; HW_COLEAD_SLM=$enableval
+else
+  HW_COLEAD_SLM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_COLEAD_SLM" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_COLEAD_SLM=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "colead-slm" "$sr_hw_info"
+
+	 if test "x$HW_COLEAD_SLM" = xyes; then
+  HW_COLEAD_SLM_TRUE=
+  HW_COLEAD_SLM_FALSE='#'
+else
+  HW_COLEAD_SLM_TRUE='#'
+  HW_COLEAD_SLM_FALSE=
+fi
+
+	if test -z "$HW_COLEAD_SLM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_COLEAD_SLM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-conrad-digi-35-cpu was given.
+if test "${enable_conrad_digi_35_cpu+set}" = set; then :
+  enableval=$enable_conrad_digi_35_cpu; HW_CONRAD_DIGI_35_CPU=$enableval
+else
+  HW_CONRAD_DIGI_35_CPU=$enable_all_drivers
+fi
+
+
+	if test "x$HW_CONRAD_DIGI_35_CPU" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_CONRAD_DIGI_35_CPU=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "conrad-digi-35-cpu" "$sr_hw_info"
+
+	 if test "x$HW_CONRAD_DIGI_35_CPU" = xyes; then
+  HW_CONRAD_DIGI_35_CPU_TRUE=
+  HW_CONRAD_DIGI_35_CPU_FALSE='#'
+else
+  HW_CONRAD_DIGI_35_CPU_TRUE='#'
+  HW_CONRAD_DIGI_35_CPU_FALSE=
+fi
+
+	if test -z "$HW_CONRAD_DIGI_35_CPU_TRUE"; then :
+
+$as_echo "#define HAVE_HW_CONRAD_DIGI_35_CPU 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-deree-de5000 was given.
+if test "${enable_deree_de5000+set}" = set; then :
+  enableval=$enable_deree_de5000; HW_DEREE_DE5000=$enableval
+else
+  HW_DEREE_DE5000=$enable_all_drivers
+fi
+
+
+	if test "x$HW_DEREE_DE5000" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_DEREE_DE5000=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "deree-de5000" "$sr_hw_info"
+
+	 if test "x$HW_DEREE_DE5000" = xyes; then
+  HW_DEREE_DE5000_TRUE=
+  HW_DEREE_DE5000_FALSE='#'
+else
+  HW_DEREE_DE5000_TRUE='#'
+  HW_DEREE_DE5000_FALSE=
+fi
+
+	if test -z "$HW_DEREE_DE5000_TRUE"; then :
+
+$as_echo "#define HAVE_HW_DEREE_DE5000 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-demo was given.
+if test "${enable_demo+set}" = set; then :
+  enableval=$enable_demo; HW_DEMO=$enableval
+else
+  HW_DEMO=$enable_all_drivers
+fi
+
+
+	if test "x$HW_DEMO" = xyes; then :
+  sr_hw_info=yes
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "demo" "$sr_hw_info"
+
+	 if test "x$HW_DEMO" = xyes; then
+  HW_DEMO_TRUE=
+  HW_DEMO_FALSE='#'
+else
+  HW_DEMO_TRUE='#'
+  HW_DEMO_FALSE=
+fi
+
+	if test -z "$HW_DEMO_TRUE"; then :
+
+$as_echo "#define HAVE_HW_DEMO 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-fluke-dmm was given.
+if test "${enable_fluke_dmm+set}" = set; then :
+  enableval=$enable_fluke_dmm; HW_FLUKE_DMM=$enableval
+else
+  HW_FLUKE_DMM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_FLUKE_DMM" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_FLUKE_DMM=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "fluke-dmm" "$sr_hw_info"
+
+	 if test "x$HW_FLUKE_DMM" = xyes; then
+  HW_FLUKE_DMM_TRUE=
+  HW_FLUKE_DMM_FALSE='#'
+else
+  HW_FLUKE_DMM_TRUE='#'
+  HW_FLUKE_DMM_FALSE=
+fi
+
+	if test -z "$HW_FLUKE_DMM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_FLUKE_DMM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-fx2lafw was given.
+if test "${enable_fx2lafw+set}" = set; then :
+  enableval=$enable_fx2lafw; HW_FX2LAFW=$enableval
+else
+  HW_FX2LAFW=$enable_all_drivers
+fi
+
+
+	if test "x$HW_FX2LAFW" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_FX2LAFW=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "fx2lafw" "$sr_hw_info"
+
+	 if test "x$HW_FX2LAFW" = xyes; then
+  HW_FX2LAFW_TRUE=
+  HW_FX2LAFW_FALSE='#'
+else
+  HW_FX2LAFW_TRUE='#'
+  HW_FX2LAFW_FALSE=
+fi
+
+	if test -z "$HW_FX2LAFW_TRUE"; then :
+
+$as_echo "#define HAVE_HW_FX2LAFW 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-gmc-mh-1x-2x was given.
+if test "${enable_gmc_mh_1x_2x+set}" = set; then :
+  enableval=$enable_gmc_mh_1x_2x; HW_GMC_MH_1X_2X=$enableval
+else
+  HW_GMC_MH_1X_2X=$enable_all_drivers
+fi
+
+
+	if test "x$HW_GMC_MH_1X_2X" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_GMC_MH_1X_2X=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "gmc-mh-1x-2x" "$sr_hw_info"
+
+	 if test "x$HW_GMC_MH_1X_2X" = xyes; then
+  HW_GMC_MH_1X_2X_TRUE=
+  HW_GMC_MH_1X_2X_FALSE='#'
+else
+  HW_GMC_MH_1X_2X_TRUE='#'
+  HW_GMC_MH_1X_2X_FALSE=
+fi
+
+	if test -z "$HW_GMC_MH_1X_2X_TRUE"; then :
+
+$as_echo "#define HAVE_HW_GMC_MH_1X_2X 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-gwinstek-gds-800 was given.
+if test "${enable_gwinstek_gds_800+set}" = set; then :
+  enableval=$enable_gwinstek_gds_800; HW_GWINSTEK_GDS_800=$enableval
+else
+  HW_GWINSTEK_GDS_800=$enable_all_drivers
+fi
+
+
+	if test "x$HW_GWINSTEK_GDS_800" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_GWINSTEK_GDS_800=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "gwinstek-gds-800" "$sr_hw_info"
+
+	 if test "x$HW_GWINSTEK_GDS_800" = xyes; then
+  HW_GWINSTEK_GDS_800_TRUE=
+  HW_GWINSTEK_GDS_800_FALSE='#'
+else
+  HW_GWINSTEK_GDS_800_TRUE='#'
+  HW_GWINSTEK_GDS_800_FALSE=
+fi
+
+	if test -z "$HW_GWINSTEK_GDS_800_TRUE"; then :
+
+$as_echo "#define HAVE_HW_GWINSTEK_GDS_800 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-hameg-hmo was given.
+if test "${enable_hameg_hmo+set}" = set; then :
+  enableval=$enable_hameg_hmo; HW_HAMEG_HMO=$enableval
+else
+  HW_HAMEG_HMO=$enable_all_drivers
+fi
+
+
+	if test "x$HW_HAMEG_HMO" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_HAMEG_HMO=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "hameg-hmo" "$sr_hw_info"
+
+	 if test "x$HW_HAMEG_HMO" = xyes; then
+  HW_HAMEG_HMO_TRUE=
+  HW_HAMEG_HMO_FALSE='#'
+else
+  HW_HAMEG_HMO_TRUE='#'
+  HW_HAMEG_HMO_FALSE=
+fi
+
+	if test -z "$HW_HAMEG_HMO_TRUE"; then :
+
+$as_echo "#define HAVE_HW_HAMEG_HMO 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-hantek-dso was given.
+if test "${enable_hantek_dso+set}" = set; then :
+  enableval=$enable_hantek_dso; HW_HANTEK_DSO=$enableval
+else
+  HW_HANTEK_DSO=$enable_all_drivers
+fi
+
+
+	if test "x$HW_HANTEK_DSO" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_HANTEK_DSO=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "hantek-dso" "$sr_hw_info"
+
+	 if test "x$HW_HANTEK_DSO" = xyes; then
+  HW_HANTEK_DSO_TRUE=
+  HW_HANTEK_DSO_FALSE='#'
+else
+  HW_HANTEK_DSO_TRUE='#'
+  HW_HANTEK_DSO_FALSE=
+fi
+
+	if test -z "$HW_HANTEK_DSO_TRUE"; then :
+
+$as_echo "#define HAVE_HW_HANTEK_DSO 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-hung-chang-dso-2100 was given.
+if test "${enable_hung_chang_dso_2100+set}" = set; then :
+  enableval=$enable_hung_chang_dso_2100; HW_HUNG_CHANG_DSO_2100=$enableval
+else
+  HW_HUNG_CHANG_DSO_2100=$enable_all_drivers
+fi
+
+
+	if test "x$HW_HUNG_CHANG_DSO_2100" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libieee1284 \
+			|| HW_HUNG_CHANG_DSO_2100=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "hung-chang-dso-2100" "$sr_hw_info"
+
+	 if test "x$HW_HUNG_CHANG_DSO_2100" = xyes; then
+  HW_HUNG_CHANG_DSO_2100_TRUE=
+  HW_HUNG_CHANG_DSO_2100_FALSE='#'
+else
+  HW_HUNG_CHANG_DSO_2100_TRUE='#'
+  HW_HUNG_CHANG_DSO_2100_FALSE=
+fi
+
+	if test -z "$HW_HUNG_CHANG_DSO_2100_TRUE"; then :
+
+$as_echo "#define HAVE_HW_HUNG_CHANG_DSO_2100 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-ikalogic-scanalogic2 was given.
+if test "${enable_ikalogic_scanalogic2+set}" = set; then :
+  enableval=$enable_ikalogic_scanalogic2; HW_IKALOGIC_SCANALOGIC2=$enableval
+else
+  HW_IKALOGIC_SCANALOGIC2=$enable_all_drivers
+fi
+
+
+	if test "x$HW_IKALOGIC_SCANALOGIC2" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_IKALOGIC_SCANALOGIC2=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "ikalogic-scanalogic2" "$sr_hw_info"
+
+	 if test "x$HW_IKALOGIC_SCANALOGIC2" = xyes; then
+  HW_IKALOGIC_SCANALOGIC2_TRUE=
+  HW_IKALOGIC_SCANALOGIC2_FALSE='#'
+else
+  HW_IKALOGIC_SCANALOGIC2_TRUE='#'
+  HW_IKALOGIC_SCANALOGIC2_FALSE=
+fi
+
+	if test -z "$HW_IKALOGIC_SCANALOGIC2_TRUE"; then :
+
+$as_echo "#define HAVE_HW_IKALOGIC_SCANALOGIC2 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-ikalogic-scanaplus was given.
+if test "${enable_ikalogic_scanaplus+set}" = set; then :
+  enableval=$enable_ikalogic_scanaplus; HW_IKALOGIC_SCANAPLUS=$enableval
+else
+  HW_IKALOGIC_SCANAPLUS=$enable_all_drivers
+fi
+
+
+	if test "x$HW_IKALOGIC_SCANAPLUS" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libftdi \
+			|| HW_IKALOGIC_SCANAPLUS=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "ikalogic-scanaplus" "$sr_hw_info"
+
+	 if test "x$HW_IKALOGIC_SCANAPLUS" = xyes; then
+  HW_IKALOGIC_SCANAPLUS_TRUE=
+  HW_IKALOGIC_SCANAPLUS_FALSE='#'
+else
+  HW_IKALOGIC_SCANAPLUS_TRUE='#'
+  HW_IKALOGIC_SCANAPLUS_FALSE=
+fi
+
+	if test -z "$HW_IKALOGIC_SCANAPLUS_TRUE"; then :
+
+$as_echo "#define HAVE_HW_IKALOGIC_SCANAPLUS 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-kecheng-kc-330b was given.
+if test "${enable_kecheng_kc_330b+set}" = set; then :
+  enableval=$enable_kecheng_kc_330b; HW_KECHENG_KC_330B=$enableval
+else
+  HW_KECHENG_KC_330B=$enable_all_drivers
+fi
+
+
+	if test "x$HW_KECHENG_KC_330B" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_KECHENG_KC_330B=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "kecheng-kc-330b" "$sr_hw_info"
+
+	 if test "x$HW_KECHENG_KC_330B" = xyes; then
+  HW_KECHENG_KC_330B_TRUE=
+  HW_KECHENG_KC_330B_FALSE='#'
+else
+  HW_KECHENG_KC_330B_TRUE='#'
+  HW_KECHENG_KC_330B_FALSE=
+fi
+
+	if test -z "$HW_KECHENG_KC_330B_TRUE"; then :
+
+$as_echo "#define HAVE_HW_KECHENG_KC_330B 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-kern-scale was given.
+if test "${enable_kern_scale+set}" = set; then :
+  enableval=$enable_kern_scale; HW_KERN_SCALE=$enableval
+else
+  HW_KERN_SCALE=$enable_all_drivers
+fi
+
+
+	if test "x$HW_KERN_SCALE" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_KERN_SCALE=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "kern-scale" "$sr_hw_info"
+
+	 if test "x$HW_KERN_SCALE" = xyes; then
+  HW_KERN_SCALE_TRUE=
+  HW_KERN_SCALE_FALSE='#'
+else
+  HW_KERN_SCALE_TRUE='#'
+  HW_KERN_SCALE_FALSE=
+fi
+
+	if test -z "$HW_KERN_SCALE_TRUE"; then :
+
+$as_echo "#define HAVE_HW_KERN_SCALE 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-korad-kaxxxxp was given.
+if test "${enable_korad_kaxxxxp+set}" = set; then :
+  enableval=$enable_korad_kaxxxxp; HW_KORAD_KAXXXXP=$enableval
+else
+  HW_KORAD_KAXXXXP=$enable_all_drivers
+fi
+
+
+	if test "x$HW_KORAD_KAXXXXP" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_KORAD_KAXXXXP=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "korad-kaxxxxp" "$sr_hw_info"
+
+	 if test "x$HW_KORAD_KAXXXXP" = xyes; then
+  HW_KORAD_KAXXXXP_TRUE=
+  HW_KORAD_KAXXXXP_FALSE='#'
+else
+  HW_KORAD_KAXXXXP_TRUE='#'
+  HW_KORAD_KAXXXXP_FALSE=
+fi
+
+	if test -z "$HW_KORAD_KAXXXXP_TRUE"; then :
+
+$as_echo "#define HAVE_HW_KORAD_KAXXXXP 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-lascar-el-usb was given.
+if test "${enable_lascar_el_usb+set}" = set; then :
+  enableval=$enable_lascar_el_usb; HW_LASCAR_EL_USB=$enableval
+else
+  HW_LASCAR_EL_USB=$enable_all_drivers
+fi
+
+
+	if test "x$HW_LASCAR_EL_USB" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_LASCAR_EL_USB=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "lascar-el-usb" "$sr_hw_info"
+
+	 if test "x$HW_LASCAR_EL_USB" = xyes; then
+  HW_LASCAR_EL_USB_TRUE=
+  HW_LASCAR_EL_USB_FALSE='#'
+else
+  HW_LASCAR_EL_USB_TRUE='#'
+  HW_LASCAR_EL_USB_FALSE=
+fi
+
+	if test -z "$HW_LASCAR_EL_USB_TRUE"; then :
+
+$as_echo "#define HAVE_HW_LASCAR_EL_USB 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-lecroy-logicstudio was given.
+if test "${enable_lecroy_logicstudio+set}" = set; then :
+  enableval=$enable_lecroy_logicstudio; HW_LECROY_LOGICSTUDIO=$enableval
+else
+  HW_LECROY_LOGICSTUDIO=$enable_all_drivers
+fi
+
+
+	if test "x$HW_LECROY_LOGICSTUDIO" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_LECROY_LOGICSTUDIO=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "lecroy-logicstudio" "$sr_hw_info"
+
+	 if test "x$HW_LECROY_LOGICSTUDIO" = xyes; then
+  HW_LECROY_LOGICSTUDIO_TRUE=
+  HW_LECROY_LOGICSTUDIO_FALSE='#'
+else
+  HW_LECROY_LOGICSTUDIO_TRUE='#'
+  HW_LECROY_LOGICSTUDIO_FALSE=
+fi
+
+	if test -z "$HW_LECROY_LOGICSTUDIO_TRUE"; then :
+
+$as_echo "#define HAVE_HW_LECROY_LOGICSTUDIO 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-manson-hcs-3xxx was given.
+if test "${enable_manson_hcs_3xxx+set}" = set; then :
+  enableval=$enable_manson_hcs_3xxx; HW_MANSON_HCS_3XXX=$enableval
+else
+  HW_MANSON_HCS_3XXX=$enable_all_drivers
+fi
+
+
+	if test "x$HW_MANSON_HCS_3XXX" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_MANSON_HCS_3XXX=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "manson-hcs-3xxx" "$sr_hw_info"
+
+	 if test "x$HW_MANSON_HCS_3XXX" = xyes; then
+  HW_MANSON_HCS_3XXX_TRUE=
+  HW_MANSON_HCS_3XXX_FALSE='#'
+else
+  HW_MANSON_HCS_3XXX_TRUE='#'
+  HW_MANSON_HCS_3XXX_FALSE=
+fi
+
+	if test -z "$HW_MANSON_HCS_3XXX_TRUE"; then :
+
+$as_echo "#define HAVE_HW_MANSON_HCS_3XXX 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-maynuo-m97 was given.
+if test "${enable_maynuo_m97+set}" = set; then :
+  enableval=$enable_maynuo_m97; HW_MAYNUO_M97=$enableval
+else
+  HW_MAYNUO_M97=$enable_all_drivers
+fi
+
+
+	if test "x$HW_MAYNUO_M97" = xyes; then :
+  sr_hw_info=yes
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "maynuo-m97" "$sr_hw_info"
+
+	 if test "x$HW_MAYNUO_M97" = xyes; then
+  HW_MAYNUO_M97_TRUE=
+  HW_MAYNUO_M97_FALSE='#'
+else
+  HW_MAYNUO_M97_TRUE='#'
+  HW_MAYNUO_M97_FALSE=
+fi
+
+	if test -z "$HW_MAYNUO_M97_TRUE"; then :
+
+$as_echo "#define HAVE_HW_MAYNUO_M97 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-mic-985xx was given.
+if test "${enable_mic_985xx+set}" = set; then :
+  enableval=$enable_mic_985xx; HW_MIC_985XX=$enableval
+else
+  HW_MIC_985XX=$enable_all_drivers
+fi
+
+
+	if test "x$HW_MIC_985XX" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_MIC_985XX=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "mic-985xx" "$sr_hw_info"
+
+	 if test "x$HW_MIC_985XX" = xyes; then
+  HW_MIC_985XX_TRUE=
+  HW_MIC_985XX_FALSE='#'
+else
+  HW_MIC_985XX_TRUE='#'
+  HW_MIC_985XX_FALSE=
+fi
+
+	if test -z "$HW_MIC_985XX_TRUE"; then :
+
+$as_echo "#define HAVE_HW_MIC_985XX 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-motech-lps-30x was given.
+if test "${enable_motech_lps_30x+set}" = set; then :
+  enableval=$enable_motech_lps_30x; HW_MOTECH_LPS_30X=$enableval
+else
+  HW_MOTECH_LPS_30X=$enable_all_drivers
+fi
+
+
+	if test "x$HW_MOTECH_LPS_30X" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_MOTECH_LPS_30X=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "motech-lps-30x" "$sr_hw_info"
+
+	 if test "x$HW_MOTECH_LPS_30X" = xyes; then
+  HW_MOTECH_LPS_30X_TRUE=
+  HW_MOTECH_LPS_30X_FALSE='#'
+else
+  HW_MOTECH_LPS_30X_TRUE='#'
+  HW_MOTECH_LPS_30X_FALSE=
+fi
+
+	if test -z "$HW_MOTECH_LPS_30X_TRUE"; then :
+
+$as_echo "#define HAVE_HW_MOTECH_LPS_30X 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-norma-dmm was given.
+if test "${enable_norma_dmm+set}" = set; then :
+  enableval=$enable_norma_dmm; HW_NORMA_DMM=$enableval
+else
+  HW_NORMA_DMM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_NORMA_DMM" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_NORMA_DMM=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "norma-dmm" "$sr_hw_info"
+
+	 if test "x$HW_NORMA_DMM" = xyes; then
+  HW_NORMA_DMM_TRUE=
+  HW_NORMA_DMM_FALSE='#'
+else
+  HW_NORMA_DMM_TRUE='#'
+  HW_NORMA_DMM_FALSE=
+fi
+
+	if test -z "$HW_NORMA_DMM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_NORMA_DMM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-openbench-logic-sniffer was given.
+if test "${enable_openbench_logic_sniffer+set}" = set; then :
+  enableval=$enable_openbench_logic_sniffer; HW_OPENBENCH_LOGIC_SNIFFER=$enableval
+else
+  HW_OPENBENCH_LOGIC_SNIFFER=$enable_all_drivers
+fi
+
+
+	if test "x$HW_OPENBENCH_LOGIC_SNIFFER" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_OPENBENCH_LOGIC_SNIFFER=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "openbench-logic-sniffer" "$sr_hw_info"
+
+	 if test "x$HW_OPENBENCH_LOGIC_SNIFFER" = xyes; then
+  HW_OPENBENCH_LOGIC_SNIFFER_TRUE=
+  HW_OPENBENCH_LOGIC_SNIFFER_FALSE='#'
+else
+  HW_OPENBENCH_LOGIC_SNIFFER_TRUE='#'
+  HW_OPENBENCH_LOGIC_SNIFFER_FALSE=
+fi
+
+	if test -z "$HW_OPENBENCH_LOGIC_SNIFFER_TRUE"; then :
+
+$as_echo "#define HAVE_HW_OPENBENCH_LOGIC_SNIFFER 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-pipistrello-ols was given.
+if test "${enable_pipistrello_ols+set}" = set; then :
+  enableval=$enable_pipistrello_ols; HW_PIPISTRELLO_OLS=$enableval
+else
+  HW_PIPISTRELLO_OLS=$enable_all_drivers
+fi
+
+
+	if test "x$HW_PIPISTRELLO_OLS" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libftdi \
+			|| HW_PIPISTRELLO_OLS=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "pipistrello-ols" "$sr_hw_info"
+
+	 if test "x$HW_PIPISTRELLO_OLS" = xyes; then
+  HW_PIPISTRELLO_OLS_TRUE=
+  HW_PIPISTRELLO_OLS_FALSE='#'
+else
+  HW_PIPISTRELLO_OLS_TRUE='#'
+  HW_PIPISTRELLO_OLS_FALSE=
+fi
+
+	if test -z "$HW_PIPISTRELLO_OLS_TRUE"; then :
+
+$as_echo "#define HAVE_HW_PIPISTRELLO_OLS 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-rigol-ds was given.
+if test "${enable_rigol_ds+set}" = set; then :
+  enableval=$enable_rigol_ds; HW_RIGOL_DS=$enableval
+else
+  HW_RIGOL_DS=$enable_all_drivers
+fi
+
+
+	if test "x$HW_RIGOL_DS" = xyes; then :
+  sr_hw_info=yes
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "rigol-ds" "$sr_hw_info"
+
+	 if test "x$HW_RIGOL_DS" = xyes; then
+  HW_RIGOL_DS_TRUE=
+  HW_RIGOL_DS_FALSE='#'
+else
+  HW_RIGOL_DS_TRUE='#'
+  HW_RIGOL_DS_FALSE=
+fi
+
+	if test -z "$HW_RIGOL_DS_TRUE"; then :
+
+$as_echo "#define HAVE_HW_RIGOL_DS 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-saleae-logic16 was given.
+if test "${enable_saleae_logic16+set}" = set; then :
+  enableval=$enable_saleae_logic16; HW_SALEAE_LOGIC16=$enableval
+else
+  HW_SALEAE_LOGIC16=$enable_all_drivers
+fi
+
+
+	if test "x$HW_SALEAE_LOGIC16" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_SALEAE_LOGIC16=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "saleae-logic16" "$sr_hw_info"
+
+	 if test "x$HW_SALEAE_LOGIC16" = xyes; then
+  HW_SALEAE_LOGIC16_TRUE=
+  HW_SALEAE_LOGIC16_FALSE='#'
+else
+  HW_SALEAE_LOGIC16_TRUE='#'
+  HW_SALEAE_LOGIC16_FALSE=
+fi
+
+	if test -z "$HW_SALEAE_LOGIC16_TRUE"; then :
+
+$as_echo "#define HAVE_HW_SALEAE_LOGIC16 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-scpi-pps was given.
+if test "${enable_scpi_pps+set}" = set; then :
+  enableval=$enable_scpi_pps; HW_SCPI_PPS=$enableval
+else
+  HW_SCPI_PPS=$enable_all_drivers
+fi
+
+
+	if test "x$HW_SCPI_PPS" = xyes; then :
+  sr_hw_info=yes
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "scpi-pps" "$sr_hw_info"
+
+	 if test "x$HW_SCPI_PPS" = xyes; then
+  HW_SCPI_PPS_TRUE=
+  HW_SCPI_PPS_FALSE='#'
+else
+  HW_SCPI_PPS_TRUE='#'
+  HW_SCPI_PPS_FALSE=
+fi
+
+	if test -z "$HW_SCPI_PPS_TRUE"; then :
+
+$as_echo "#define HAVE_HW_SCPI_PPS 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-serial-dmm was given.
+if test "${enable_serial_dmm+set}" = set; then :
+  enableval=$enable_serial_dmm; HW_SERIAL_DMM=$enableval
+else
+  HW_SERIAL_DMM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_SERIAL_DMM" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_SERIAL_DMM=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "serial-dmm" "$sr_hw_info"
+
+	 if test "x$HW_SERIAL_DMM" = xyes; then
+  HW_SERIAL_DMM_TRUE=
+  HW_SERIAL_DMM_FALSE='#'
+else
+  HW_SERIAL_DMM_TRUE='#'
+  HW_SERIAL_DMM_FALSE=
+fi
+
+	if test -z "$HW_SERIAL_DMM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_SERIAL_DMM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-sysclk-lwla was given.
+if test "${enable_sysclk_lwla+set}" = set; then :
+  enableval=$enable_sysclk_lwla; HW_SYSCLK_LWLA=$enableval
+else
+  HW_SYSCLK_LWLA=$enable_all_drivers
+fi
+
+
+	if test "x$HW_SYSCLK_LWLA" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_SYSCLK_LWLA=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "sysclk-lwla" "$sr_hw_info"
+
+	 if test "x$HW_SYSCLK_LWLA" = xyes; then
+  HW_SYSCLK_LWLA_TRUE=
+  HW_SYSCLK_LWLA_FALSE='#'
+else
+  HW_SYSCLK_LWLA_TRUE='#'
+  HW_SYSCLK_LWLA_FALSE=
+fi
+
+	if test -z "$HW_SYSCLK_LWLA_TRUE"; then :
+
+$as_echo "#define HAVE_HW_SYSCLK_LWLA 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-teleinfo was given.
+if test "${enable_teleinfo+set}" = set; then :
+  enableval=$enable_teleinfo; HW_TELEINFO=$enableval
+else
+  HW_TELEINFO=$enable_all_drivers
+fi
+
+
+	if test "x$HW_TELEINFO" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_TELEINFO=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "teleinfo" "$sr_hw_info"
+
+	 if test "x$HW_TELEINFO" = xyes; then
+  HW_TELEINFO_TRUE=
+  HW_TELEINFO_FALSE='#'
+else
+  HW_TELEINFO_TRUE='#'
+  HW_TELEINFO_FALSE=
+fi
+
+	if test -z "$HW_TELEINFO_TRUE"; then :
+
+$as_echo "#define HAVE_HW_TELEINFO 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-testo was given.
+if test "${enable_testo+set}" = set; then :
+  enableval=$enable_testo; HW_TESTO=$enableval
+else
+  HW_TESTO=$enable_all_drivers
+fi
+
+
+	if test "x$HW_TESTO" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_TESTO=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "testo" "$sr_hw_info"
+
+	 if test "x$HW_TESTO" = xyes; then
+  HW_TESTO_TRUE=
+  HW_TESTO_FALSE='#'
+else
+  HW_TESTO_TRUE='#'
+  HW_TESTO_FALSE=
+fi
+
+	if test -z "$HW_TESTO_TRUE"; then :
+
+$as_echo "#define HAVE_HW_TESTO 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-tondaj-sl-814 was given.
+if test "${enable_tondaj_sl_814+set}" = set; then :
+  enableval=$enable_tondaj_sl_814; HW_TONDAJ_SL_814=$enableval
+else
+  HW_TONDAJ_SL_814=$enable_all_drivers
+fi
+
+
+	if test "x$HW_TONDAJ_SL_814" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libserialport \
+			|| HW_TONDAJ_SL_814=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "tondaj-sl-814" "$sr_hw_info"
+
+	 if test "x$HW_TONDAJ_SL_814" = xyes; then
+  HW_TONDAJ_SL_814_TRUE=
+  HW_TONDAJ_SL_814_FALSE='#'
+else
+  HW_TONDAJ_SL_814_TRUE='#'
+  HW_TONDAJ_SL_814_FALSE=
+fi
+
+	if test -z "$HW_TONDAJ_SL_814_TRUE"; then :
+
+$as_echo "#define HAVE_HW_TONDAJ_SL_814 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-uni-t-dmm was given.
+if test "${enable_uni_t_dmm+set}" = set; then :
+  enableval=$enable_uni_t_dmm; HW_UNI_T_DMM=$enableval
+else
+  HW_UNI_T_DMM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_UNI_T_DMM" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_UNI_T_DMM=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "uni-t-dmm" "$sr_hw_info"
+
+	 if test "x$HW_UNI_T_DMM" = xyes; then
+  HW_UNI_T_DMM_TRUE=
+  HW_UNI_T_DMM_FALSE='#'
+else
+  HW_UNI_T_DMM_TRUE='#'
+  HW_UNI_T_DMM_FALSE=
+fi
+
+	if test -z "$HW_UNI_T_DMM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_UNI_T_DMM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-uni-t-ut32x was given.
+if test "${enable_uni_t_ut32x+set}" = set; then :
+  enableval=$enable_uni_t_ut32x; HW_UNI_T_UT32X=$enableval
+else
+  HW_UNI_T_UT32X=$enable_all_drivers
+fi
+
+
+	if test "x$HW_UNI_T_UT32X" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_UNI_T_UT32X=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "uni-t-ut32x" "$sr_hw_info"
+
+	 if test "x$HW_UNI_T_UT32X" = xyes; then
+  HW_UNI_T_UT32X_TRUE=
+  HW_UNI_T_UT32X_FALSE='#'
+else
+  HW_UNI_T_UT32X_TRUE='#'
+  HW_UNI_T_UT32X_FALSE=
+fi
+
+	if test -z "$HW_UNI_T_UT32X_TRUE"; then :
+
+$as_echo "#define HAVE_HW_UNI_T_UT32X 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-victor-dmm was given.
+if test "${enable_victor_dmm+set}" = set; then :
+  enableval=$enable_victor_dmm; HW_VICTOR_DMM=$enableval
+else
+  HW_VICTOR_DMM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_VICTOR_DMM" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_VICTOR_DMM=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "victor-dmm" "$sr_hw_info"
+
+	 if test "x$HW_VICTOR_DMM" = xyes; then
+  HW_VICTOR_DMM_TRUE=
+  HW_VICTOR_DMM_FALSE='#'
+else
+  HW_VICTOR_DMM_TRUE='#'
+  HW_VICTOR_DMM_FALSE=
+fi
+
+	if test -z "$HW_VICTOR_DMM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_VICTOR_DMM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-yokogawa-dlm was given.
+if test "${enable_yokogawa_dlm+set}" = set; then :
+  enableval=$enable_yokogawa_dlm; HW_YOKOGAWA_DLM=$enableval
+else
+  HW_YOKOGAWA_DLM=$enable_all_drivers
+fi
+
+
+	if test "x$HW_YOKOGAWA_DLM" = xyes; then :
+  sr_hw_info=yes
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "yokogawa-dlm" "$sr_hw_info"
+
+	 if test "x$HW_YOKOGAWA_DLM" = xyes; then
+  HW_YOKOGAWA_DLM_TRUE=
+  HW_YOKOGAWA_DLM_FALSE='#'
+else
+  HW_YOKOGAWA_DLM_TRUE='#'
+  HW_YOKOGAWA_DLM_FALSE=
+fi
+
+	if test -z "$HW_YOKOGAWA_DLM_TRUE"; then :
+
+$as_echo "#define HAVE_HW_YOKOGAWA_DLM 1" >>confdefs.h
+
+fi
+
+
+	# Check whether --enable-zeroplus-logic-cube was given.
+if test "${enable_zeroplus_logic_cube+set}" = set; then :
+  enableval=$enable_zeroplus_logic_cube; HW_ZEROPLUS_LOGIC_CUBE=$enableval
+else
+  HW_ZEROPLUS_LOGIC_CUBE=$enable_all_drivers
+fi
+
+
+	if test "x$HW_ZEROPLUS_LOGIC_CUBE" = xyes; then :
+  sr_hw_info=yes
+		sr_check_driver_deps libusb \
+			|| HW_ZEROPLUS_LOGIC_CUBE=no sr_hw_info="no (missing: $sr_deps_missing)"
+
+else
+  sr_hw_info='no (disabled)'
+fi
+	sr_driver_summary_append "zeroplus-logic-cube" "$sr_hw_info"
+
+	 if test "x$HW_ZEROPLUS_LOGIC_CUBE" = xyes; then
+  HW_ZEROPLUS_LOGIC_CUBE_TRUE=
+  HW_ZEROPLUS_LOGIC_CUBE_FALSE='#'
+else
+  HW_ZEROPLUS_LOGIC_CUBE_TRUE='#'
+  HW_ZEROPLUS_LOGIC_CUBE_FALSE=
+fi
+
+	if test -z "$HW_ZEROPLUS_LOGIC_CUBE_TRUE"; then :
+
+$as_echo "#define HAVE_HW_ZEROPLUS_LOGIC_CUBE 1" >>confdefs.h
+
+fi
+
+
+###############################
+##  Language bindings setup  ##
+###############################
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+case $ac_compile in #(
+  *'$CXXFLAGS '*) :
+    sr_lang='C++' sr_cc=$CXX sr_conftest="conftest.${ac_ext:-cc}" ;; #(
+  *'$CFLAGS '*) :
+    sr_lang=C sr_cc=$CC sr_conftest="conftest.${ac_ext:-c}" ;; #(
+  *) :
+    as_fn_error $? "current language is neither C nor C++" "$LINENO" 5 ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which $sr_lang compiler warning flags to use" >&5
+$as_echo_n "checking which $sr_lang compiler warning flags to use... " >&6; }
+sr_warning_flags=
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int main(int argc, char** argv) { return (argv != 0) ? argc : 0; }
 
 _ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+case $sr_enable_warnings in #(
+  no) :
+     ;; #(
+  min) :
+    sr_check_compile_warning_flags -Wall ;; #(
+  fatal) :
+    sr_check_compile_warning_flags -Wall -Wextra -Werror ;; #(
+  *) :
+    sr_check_compile_warning_flags -Wall -Wextra ;;
+esac
+rm -f "$sr_conftest"
+SR_WXXFLAGS=$sr_warning_flags
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${sr_warning_flags:-none}" >&5
+$as_echo "${sr_warning_flags:-none}" >&6; }
+
+# Check whether --enable-bindings was given.
+if test "${enable_bindings+set}" = set; then :
+  enableval=$enable_bindings;
 else
-  no_glib=yes
+  enable_bindings=yes
 fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-  conftest.$ac_objext conftest.beam conftest.$ac_ext
+
+
+# Check whether --enable-cxx was given.
+if test "${enable_cxx+set}" = set; then :
+  enableval=$enable_cxx;
+else
+  enable_cxx=$enable_bindings
 fi
 
-       CFLAGS="$ac_save_CFLAGS"
-       LIBS="$ac_save_LIBS"
-     fi
-  fi
-  if test "x$no_glib" = x ; then
-     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version)" >&5
-$as_echo "yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version)" >&6; }
-     LIB_CFLAGS="$LIB_CFLAGS $GLIB_CFLAGS"; LIBS="$LIBS $GLIB_LIBS"
-  else
-     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-     if test "$PKG_CONFIG" = "no" ; then
-       echo "*** A new enough version of pkg-config was not found."
-       echo "*** See http://www.freedesktop.org/software/pkgconfig/"
-     else
-       if test -f conf.glibtest ; then
-        :
-       else
-          echo "*** Could not run GLIB test program, checking why..."
-          ac_save_CFLAGS="$CFLAGS"
-          ac_save_LIBS="$LIBS"
-          CFLAGS="$CFLAGS $GLIB_CFLAGS"
-          LIBS="$LIBS $GLIB_LIBS"
-          cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+
+# Check whether --enable-python was given.
+if test "${enable_python+set}" = set; then :
+  enableval=$enable_python;
+else
+  enable_python=$enable_bindings
+fi
+
+
+# Check whether --enable-ruby was given.
+if test "${enable_ruby+set}" = set; then :
+  enableval=$enable_ruby;
+else
+  enable_ruby=$enable_bindings
+fi
+
+
+# Check whether --enable-java was given.
+if test "${enable_java+set}" = set; then :
+  enableval=$enable_java;
+else
+  enable_java=$enable_bindings
+fi
+
+
+####################
+##  C++ bindings  ##
+####################
+
+sr_cxx_missing=
+
+# Check if the C++ compiler supports the C++11 standard.
+    ax_cxx_compile_cxx11_required=false
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+  ac_success=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5
+$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; }
+if ${ax_cv_cxx_compile_cxx11+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-#include <glib.h>
-#include <stdio.h>
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    struct Base {
+    virtual void f() {}
+    };
+    struct Child : public Base {
+    virtual void f() override {}
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);
+
+    auto d = a;
+    auto l = [](){};
 
-int
-main ()
-{
- return ((glib_major_version) || (glib_minor_version) || (glib_micro_version));
-  ;
-  return 0;
-}
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-   echo "*** The test program compiled, but did not run. This usually means"
-          echo "*** that the run-time linker is not finding GLIB or finding the wrong"
-          echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your"
-          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
-          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
-          echo "*** is required on your system"
-	  echo "***"
-          echo "*** If you have an old version installed, it is best to remove it, although"
-          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
-else
-   echo "*** The test program failed to compile or link. See the file config.log for the"
-          echo "*** exact error that occured. This usually means GLIB is incorrectly installed."
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ax_cv_cxx_compile_cxx11=yes
+else
+  ax_cv_cxx_compile_cxx11=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-          CFLAGS="$ac_save_CFLAGS"
-          LIBS="$ac_save_LIBS"
-       fi
-     fi
-     GLIB_CFLAGS=""
-     GLIB_LIBS=""
-     GLIB_GENMARSHAL=""
-     GOBJECT_QUERY=""
-     GLIB_MKENUMS=""
-     GLIB_COMPILE_RESOURCES=""
-     :
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5
+$as_echo "$ax_cv_cxx_compile_cxx11" >&6; }
+  if test x$ax_cv_cxx_compile_cxx11 = xyes; then
+    ac_success=yes
   fi
 
 
 
+    if test x$ac_success = xno; then
+    for switch in -std=c++11 -std=c++0x; do
+      cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh`
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5
+$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; }
+if eval \${$cachevar+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
 
+    struct Base {
+    virtual void f() {}
+    };
+    struct Child : public Base {
+    virtual void f() override {}
+    };
 
-  rm -f conf.glibtest
+    typedef check<check<bool>> right_angle_brackets;
 
+    int a;
+    decltype(a) b;
 
-# libzip is always needed. Abort if it's not found.
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);
 
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libzip" >&5
-$as_echo_n "checking for libzip... " >&6; }
+    auto d = a;
+    auto l = [](){};
 
-if test -n "$libzip_CFLAGS"; then
-    pkg_cv_libzip_CFLAGS="$libzip_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libzip >= 0.10\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libzip >= 0.10") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_libzip_CFLAGS=`$PKG_CONFIG --cflags "libzip >= 0.10" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval $cachevar=yes
 else
-  pkg_failed=yes
+  eval $cachevar=no
 fi
- else
-    pkg_failed=untried
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+         CXXFLAGS="$ac_save_CXXFLAGS"
 fi
-if test -n "$libzip_LIBS"; then
-    pkg_cv_libzip_LIBS="$libzip_LIBS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libzip >= 0.10\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libzip >= 0.10") 2>&5
+eval ac_res=\$$cachevar
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+  if test x$ax_cxx_compile_cxx11_required = xtrue; then
+    if test x$ac_success = xno; then
+      as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5
+    fi
+  else
+    if test x$ac_success = xno; then
+      HAVE_CXX11=0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5
+$as_echo "$as_me: No compiler with C++11 support was found" >&6;}
+    else
+      HAVE_CXX11=1
+
+$as_echo "#define HAVE_CXX11 1" >>confdefs.h
+
+    fi
+
+
+  fi
+
+if test "x$HAVE_CXX11" != x1; then :
+  sr_cxx_missing=${sr_cxx_missing}${sr_cxx_missing:+', '}'C++11'
+fi
+
+# The C++ bindings need glibmm.
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glibmm-2.4 >= 2.32.0\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glibmm-2.4 >= 2.32.0") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_libzip_LIBS=`$PKG_CONFIG --libs "libzip >= 0.10" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+  	sr_have_glibmm=yes
+	SR_PKGLIBS_CXX=${SR_PKGLIBS_CXX}${SR_PKGLIBS_CXX:+' '}"glibmm-2.4 >= 2.32.0"
+	sr_glibmm_version=`$PKG_CONFIG --modversion "glibmm-2.4 >= 2.32.0" 2>&5`
+	sr_pkg_check_summary_append "glibmm-2.4 >= 2.32.0" "$sr_glibmm_version"
 else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
+  	sr_pkg_check_summary_append "glibmm-2.4 >= 2.32.0" no
+	sr_have_glibmm=no sr_glibmm_version=
 fi
 
+if test "x$sr_have_glibmm" != xyes; then :
+  sr_cxx_missing=${sr_cxx_missing}${sr_cxx_missing:+', '}glibmm
+fi
 
+# The C++ bindings use Doxygen to parse libsigrok symbols.
+# Extract the first word of "doxygen", so it can be a program name with args.
+set dummy doxygen; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_HAVE_DOXYGEN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$HAVE_DOXYGEN"; then
+  ac_cv_prog_HAVE_DOXYGEN="$HAVE_DOXYGEN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_HAVE_DOXYGEN="yes"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
 
-if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
+  test -z "$ac_cv_prog_HAVE_DOXYGEN" && ac_cv_prog_HAVE_DOXYGEN="no"
+fi
+fi
+HAVE_DOXYGEN=$ac_cv_prog_HAVE_DOXYGEN
+if test -n "$HAVE_DOXYGEN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $HAVE_DOXYGEN" >&5
+$as_echo "$HAVE_DOXYGEN" >&6; }
 else
-        _pkg_short_errors_supported=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
-        if test $_pkg_short_errors_supported = yes; then
-	        libzip_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libzip >= 0.10" 2>&1`
-        else
-	        libzip_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libzip >= 0.10" 2>&1`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$libzip_PKG_ERRORS" >&5
 
-	as_fn_error $? "Package requirements (libzip >= 0.10) were not met:
 
-$libzip_PKG_ERRORS
+if test "x$HAVE_DOXYGEN" != xyes; then :
+  sr_cxx_missing=${sr_cxx_missing}${sr_cxx_missing:+', '}Doxygen
+fi
 
-Consider adjusting the PKG_CONFIG_PATH environment variable if you
-installed software in a non-standard prefix.
+# Python is needed for the C++ bindings.
 
-Alternatively, you may set the environment variables libzip_CFLAGS
-and libzip_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details." "$LINENO" 5
-elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "The pkg-config script could not be found or is too old.  Make sure it
-is in your PATH or set the PKG_CONFIG environment variable to the full
-path to pkg-config.
 
-Alternatively, you may set the environment variables libzip_CFLAGS
-and libzip_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details.
 
-To get pkg-config, see <http://pkg-config.freedesktop.org/>.
-See \`config.log' for more details" "$LINENO" 5; }
-else
-	libzip_CFLAGS=$pkg_cv_libzip_CFLAGS
-	libzip_LIBS=$pkg_cv_libzip_LIBS
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	LIB_CFLAGS="$LIB_CFLAGS $libzip_CFLAGS"; LIBS="$LIBS $libzip_LIBS";
-	SR_PKGLIBS="$SR_PKGLIBS libzip"
-fi
 
-# libserialport is only needed for some hardware drivers. Disable the
-# respective drivers if it is not found.
 
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libserialport" >&5
-$as_echo_n "checking for libserialport... " >&6; }
 
-if test -n "$libserialport_CFLAGS"; then
-    pkg_cv_libserialport_CFLAGS="$libserialport_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libserialport >= 0.1.0\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libserialport >= 0.1.0") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_libserialport_CFLAGS=`$PKG_CONFIG --cflags "libserialport >= 0.1.0" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+        if test -n "$PYTHON"; then
+      # If the user set $PYTHON, use it and don't search something else.
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version is >= 2.7" >&5
+$as_echo_n "checking whether $PYTHON version is >= 2.7... " >&6; }
+      prog="import sys
+# split strings by '.' and convert to numeric.  Append some zeros
+# because we need at least 4 digits for the hex conversion.
+# map returns an iterator in Python 3.0 and a list in 2.x
+minver = list(map(int, '2.7'.split('.'))) + [0, 0, 0]
+minverhex = 0
+# xrange is not present in Python 3.0 and range returns an iterator
+for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i]
+sys.exit(sys.hexversion < minverhex)"
+  if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5
+   ($PYTHON -c "$prog") >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
 else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+			       as_fn_error $? "Python interpreter is too old" "$LINENO" 5
 fi
-if test -n "$libserialport_LIBS"; then
-    pkg_cv_libserialport_LIBS="$libserialport_LIBS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libserialport >= 0.1.0\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libserialport >= 0.1.0") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_libserialport_LIBS=`$PKG_CONFIG --libs "libserialport >= 0.1.0" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+      am_display_PYTHON=$PYTHON
+    else
+      # Otherwise, try each interpreter until we find one that satisfies
+      # VERSION.
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 2.7" >&5
+$as_echo_n "checking for a Python interpreter with version >= 2.7... " >&6; }
+if ${am_cv_pathless_PYTHON+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  pkg_failed=yes
+
+	for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7  python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
+	  test "$am_cv_pathless_PYTHON" = none && break
+	  prog="import sys
+# split strings by '.' and convert to numeric.  Append some zeros
+# because we need at least 4 digits for the hex conversion.
+# map returns an iterator in Python 3.0 and a list in 2.x
+minver = list(map(int, '2.7'.split('.'))) + [0, 0, 0]
+minverhex = 0
+# xrange is not present in Python 3.0 and range returns an iterator
+for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i]
+sys.exit(sys.hexversion < minverhex)"
+  if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5
+   ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }; then :
+  break
 fi
- else
-    pkg_failed=untried
+	done
 fi
-
-
-
-if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5
+$as_echo "$am_cv_pathless_PYTHON" >&6; }
+      # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
+      if test "$am_cv_pathless_PYTHON" = none; then
+	PYTHON=:
+      else
+        # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args.
+set dummy $am_cv_pathless_PYTHON; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PYTHON+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-        _pkg_short_errors_supported=no
+  case $PYTHON in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
 fi
-        if test $_pkg_short_errors_supported = yes; then
-	        libserialport_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libserialport >= 0.1.0" 2>&1`
-        else
-	        libserialport_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libserialport >= 0.1.0" 2>&1`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$libserialport_PKG_ERRORS" >&5
-
-	have_libserialport="no"; HW_AGILENT_DMM="no"; HW_APPA_55II="no";
-	HW_ATTEN_PPS3XXX="no"; HW_BRYMEN_DMM="no"; HW_CEM_DT_885X="no";
-	HW_CENTER_3XX="no"; HW_COLEAD_SLM="no"; HW_CONRAD_DIGI_35_CPU="no";
-	HW_FLUKE_DMM="no"; HW_GMC_MH_1X_2X="no"; HW_HAMEG_HMO="no";
-	HW_MIC_985XX="no"; HW_NORMA_DMM="no"; HW_OLS="no";
-	HW_SERIAL_DMM="no"; HW_TELEINFO="no"; HW_TONDAJ_SL_814="no"
-elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+PYTHON=$ac_cv_path_PYTHON
+if test -n "$PYTHON"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5
+$as_echo "$PYTHON" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-	have_libserialport="no"; HW_AGILENT_DMM="no"; HW_APPA_55II="no";
-	HW_ATTEN_PPS3XXX="no"; HW_BRYMEN_DMM="no"; HW_CEM_DT_885X="no";
-	HW_CENTER_3XX="no"; HW_COLEAD_SLM="no"; HW_CONRAD_DIGI_35_CPU="no";
-	HW_FLUKE_DMM="no"; HW_GMC_MH_1X_2X="no"; HW_HAMEG_HMO="no";
-	HW_MIC_985XX="no"; HW_NORMA_DMM="no"; HW_OLS="no";
-	HW_SERIAL_DMM="no"; HW_TELEINFO="no"; HW_TONDAJ_SL_814="no"
-else
-	libserialport_CFLAGS=$pkg_cv_libserialport_CFLAGS
-	libserialport_LIBS=$pkg_cv_libserialport_LIBS
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	have_libserialport="yes"; LIB_CFLAGS="$LIB_CFLAGS $libserialport_CFLAGS";
-	LIBS="$LIBS $libserialport_LIBS";
-	SR_PKGLIBS="$SR_PKGLIBS libserialport"
 fi
 
-# Define HAVE_LIBSERIALPORT in config.h if we found libserialport.
-if test "x$have_libserialport" != "xno"; then
 
-cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBSERIALPORT 1
-_ACEOF
+      fi
+      am_display_PYTHON=$am_cv_pathless_PYTHON
+    fi
 
-fi
 
-# Serial port helper code is only compiled in if libserialport was found.
- if test "x$have_libserialport" != xno; then
-  NEED_SERIAL_TRUE=
-  NEED_SERIAL_FALSE='#'
+  if test "$PYTHON" = :; then
+      HAVE_PYTHON=no
+	sr_cxx_missing=${sr_cxx_missing}${sr_cxx_missing:+', '}Python
+  else
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5
+$as_echo_n "checking for $am_display_PYTHON version... " >&6; }
+if ${am_cv_python_version+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  NEED_SERIAL_TRUE='#'
-  NEED_SERIAL_FALSE=
+  am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[:3])"`
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5
+$as_echo "$am_cv_python_version" >&6; }
+  PYTHON_VERSION=$am_cv_python_version
 
 
 
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for librevisa" >&5
-$as_echo_n "checking for librevisa... " >&6; }
+  PYTHON_PREFIX='${prefix}'
 
-if test -n "$librevisa_CFLAGS"; then
-    pkg_cv_librevisa_CFLAGS="$librevisa_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"librevisa >= 0.0.20130812\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "librevisa >= 0.0.20130812") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_librevisa_CFLAGS=`$PKG_CONFIG --cflags "librevisa >= 0.0.20130812" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+  PYTHON_EXEC_PREFIX='${exec_prefix}'
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5
+$as_echo_n "checking for $am_display_PYTHON platform... " >&6; }
+if ${am_cv_python_platform+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
-fi
-if test -n "$librevisa_LIBS"; then
-    pkg_cv_librevisa_LIBS="$librevisa_LIBS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"librevisa >= 0.0.20130812\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "librevisa >= 0.0.20130812") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_librevisa_LIBS=`$PKG_CONFIG --libs "librevisa >= 0.0.20130812" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+  am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5
+$as_echo "$am_cv_python_platform" >&6; }
+  PYTHON_PLATFORM=$am_cv_python_platform
+
+
+  # Just factor out some code duplication.
+  am_python_setup_sysconfig="\
+import sys
+# Prefer sysconfig over distutils.sysconfig, for better compatibility
+# with python 3.x.  See automake bug#10227.
+try:
+    import sysconfig
+except ImportError:
+    can_use_sysconfig = 0
+else:
+    can_use_sysconfig = 1
+# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs:
+# <https://github.com/pypa/virtualenv/issues/118>
+try:
+    from platform import python_implementation
+    if python_implementation() == 'CPython' and sys.version[:3] == '2.7':
+        can_use_sysconfig = 0
+except ImportError:
+    pass"
+
+
+            { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory" >&5
+$as_echo_n "checking for $am_display_PYTHON script directory... " >&6; }
+if ${am_cv_python_pythondir+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
-fi
+  if test "x$prefix" = xNONE
+     then
+       am_py_prefix=$ac_default_prefix
+     else
+       am_py_prefix=$prefix
+     fi
+     am_cv_python_pythondir=`$PYTHON -c "
+$am_python_setup_sysconfig
+if can_use_sysconfig:
+    sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'})
+else:
+    from distutils import sysconfig
+    sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix')
+sys.stdout.write(sitedir)"`
+     case $am_cv_python_pythondir in
+     $am_py_prefix*)
+       am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'`
+       am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"`
+       ;;
+     *)
+       case $am_py_prefix in
+         /usr|/System*) ;;
+         *)
+	  am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages
+	  ;;
+       esac
+       ;;
+     esac
 
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5
+$as_echo "$am_cv_python_pythondir" >&6; }
+  pythondir=$am_cv_python_pythondir
 
 
-if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
 
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
-else
-        _pkg_short_errors_supported=no
-fi
-        if test $_pkg_short_errors_supported = yes; then
-	        librevisa_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "librevisa >= 0.0.20130812" 2>&1`
-        else
-	        librevisa_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "librevisa >= 0.0.20130812" 2>&1`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$librevisa_PKG_ERRORS" >&5
+  pkgpythondir=\${pythondir}/$PACKAGE
 
-	have_librevisa="no"
-elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-	have_librevisa="no"
-else
-	librevisa_CFLAGS=$pkg_cv_librevisa_CFLAGS
-	librevisa_LIBS=$pkg_cv_librevisa_LIBS
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	have_librevisa="yes"; LIB_CFLAGS="$LIB_CFLAGS $librevisa_CFLAGS";
-	LIBS="$LIBS $librevisa_LIBS";
-	SR_PKGLIBS="$SR_PKGLIBS librevisa"
-fi
 
-# VISA SCPI backend is only compiled in if librevisa was found.
- if test "x$have_librevisa" != xno; then
-  NEED_VISA_TRUE=
-  NEED_VISA_FALSE='#'
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory" >&5
+$as_echo_n "checking for $am_display_PYTHON extension module directory... " >&6; }
+if ${am_cv_python_pyexecdir+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  NEED_VISA_TRUE='#'
-  NEED_VISA_FALSE=
-fi
+  if test "x$exec_prefix" = xNONE
+     then
+       am_py_exec_prefix=$am_py_prefix
+     else
+       am_py_exec_prefix=$exec_prefix
+     fi
+     am_cv_python_pyexecdir=`$PYTHON -c "
+$am_python_setup_sysconfig
+if can_use_sysconfig:
+    sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'})
+else:
+    from distutils import sysconfig
+    sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix')
+sys.stdout.write(sitedir)"`
+     case $am_cv_python_pyexecdir in
+     $am_py_exec_prefix*)
+       am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'`
+       am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"`
+       ;;
+     *)
+       case $am_py_exec_prefix in
+         /usr|/System*) ;;
+         *)
+	   am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages
+	   ;;
+       esac
+       ;;
+     esac
 
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5
+$as_echo "$am_cv_python_pyexecdir" >&6; }
+  pyexecdir=$am_cv_python_pyexecdir
 
-# Define HAVE_LIBREVISA in config.h if we found librevisa.
-if test "x$have_librevisa" != "xno"; then
 
-cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBREVISA 1
-_ACEOF
 
-fi
+  pkgpyexecdir=\${pyexecdir}/$PACKAGE
 
-# libusb-1.0 is only needed for some hardware drivers. Disable the respective
-# drivers if it is not found.
-case "$host" in
-*freebsd*)
-	# FreeBSD comes with an "integrated" libusb-1.0-style USB API.
-	# This means libusb-1.0 is always available, no need to check for it,
-	# and no need to (potentially) disable any drivers if it's not found.
 
-cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBUSB_1_0 1
-_ACEOF
+    HAVE_PYTHON=yes
+  fi
 
-	;;
-*)
 
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libusb" >&5
-$as_echo_n "checking for libusb... " >&6; }
 
-if test -n "$libusb_CFLAGS"; then
-    pkg_cv_libusb_CFLAGS="$libusb_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libusb-1.0 >= 1.0.16\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libusb-1.0 >= 1.0.16") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_libusb_CFLAGS=`$PKG_CONFIG --cflags "libusb-1.0 >= 1.0.16" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+if test -z "$sr_cxx_missing"; then :
+  BINDINGS_CXX=$enable_cxx
 else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
+  BINDINGS_CXX=no
 fi
-if test -n "$libusb_LIBS"; then
-    pkg_cv_libusb_LIBS="$libusb_LIBS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libusb-1.0 >= 1.0.16\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libusb-1.0 >= 1.0.16") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_libusb_LIBS=`$PKG_CONFIG --libs "libusb-1.0 >= 1.0.16" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+ if test "x$BINDINGS_CXX" = xyes; then
+  BINDINGS_CXX_TRUE=
+  BINDINGS_CXX_FALSE='#'
 else
-  pkg_failed=yes
+  BINDINGS_CXX_TRUE='#'
+  BINDINGS_CXX_FALSE=
 fi
- else
-    pkg_failed=untried
-fi
-
 
 
-if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+# C++ bindings want stoi and stod.
+if test -z "$BINDINGS_CXX_TRUE"; then :
 
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stoi and stod" >&5
+$as_echo_n "checking for stoi and stod... " >&6; }
+if ${sr_cv_have_stoi_stod+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-        _pkg_short_errors_supported=no
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string>
+int
+main ()
+{
+(void) std::stoi("1"); (void) std::stod("1.0");
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  sr_cv_have_stoi_stod=yes
+else
+  sr_cv_have_stoi_stod=no
 fi
-        if test $_pkg_short_errors_supported = yes; then
-	        libusb_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libusb-1.0 >= 1.0.16" 2>&1`
-        else
-	        libusb_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libusb-1.0 >= 1.0.16" 2>&1`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$libusb_PKG_ERRORS" >&5
-
-	have_libusb1_0="no"; HW_BRYMEN_BM86X="no"; HW_FX2LAFW="no";
-		HW_HANTEK_DSO="no"; HW_IKALOGIC_SCANALOGIC2="no";
-		HW_KECHENG_KC_330B="no"; HW_LASCAR_EL_USB="no";
-		HW_SYSCLK_LWLA="no"; HW_UNI_T_DMM="no";
-		HW_UNI_T_UT32X="no"; HW_VICTOR_DMM="no";
-		HW_ZEROPLUS_LOGIC_CUBE="no"; HW_SALEAE_LOGIC16="no"
-elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-	have_libusb1_0="no"; HW_BRYMEN_BM86X="no"; HW_FX2LAFW="no";
-		HW_HANTEK_DSO="no"; HW_IKALOGIC_SCANALOGIC2="no";
-		HW_KECHENG_KC_330B="no"; HW_LASCAR_EL_USB="no";
-		HW_SYSCLK_LWLA="no"; HW_UNI_T_DMM="no";
-		HW_UNI_T_UT32X="no"; HW_VICTOR_DMM="no";
-		HW_ZEROPLUS_LOGIC_CUBE="no"; HW_SALEAE_LOGIC16="no"
-else
-	libusb_CFLAGS=$pkg_cv_libusb_CFLAGS
-	libusb_LIBS=$pkg_cv_libusb_LIBS
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	have_libusb1_0="yes"; LIB_CFLAGS="$LIB_CFLAGS $libusb_CFLAGS";
-		LIBS="$LIBS $libusb_LIBS";
-		SR_PKGLIBS="$SR_PKGLIBS libusb-1.0"
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sr_cv_have_stoi_stod" >&5
+$as_echo "$sr_cv_have_stoi_stod" >&6; }
+	if test "x$sr_cv_have_stoi_stod" = xyes; then :
 
-	# Define HAVE_LIBUSB_1_0 in config.h if we found libusb-1.0.
-	if test "x$have_libusb1_0" != "xno"; then
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBUSB_1_0 1
-_ACEOF
+$as_echo "#define HAVE_STOI_STOD 1" >>confdefs.h
 
-	fi
-	;;
-esac
+fi
 
-# USB + FX2 firmware helper code is only compiled in if libusb-1.0 was found.
- if test "x$have_libusb1_0" != xno; then
-  NEED_USB_TRUE=
-  NEED_USB_FALSE='#'
-else
-  NEED_USB_TRUE='#'
-  NEED_USB_FALSE=
 fi
 
+#######################
+##  Python bindings  ##
+#######################
 
-# libftdi is only needed for some hardware drivers. Disable them if not found.
+if test "x$BINDINGS_CXX" = xyes; then :
+  sr_python_missing=
+else
+  sr_python_missing='C++ bindings'
+fi
 
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libftdi" >&5
-$as_echo_n "checking for libftdi... " >&6; }
+# Extract major and minor version number of the Python interpreter.
+sr_pymajor=${PYTHON_VERSION%%.*}
+sr_pyminor=${PYTHON_VERSION#*.}
 
-if test -n "$libftdi_CFLAGS"; then
-    pkg_cv_libftdi_CFLAGS="$libftdi_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libftdi >= 0.16\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libftdi >= 0.16") 2>&5
+# The Python bindings need Python development files. Check for either
+# Python 3 or Python 2 headers depending on the interpreter version.
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python = \$PYTHON_VERSION\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "python = $PYTHON_VERSION") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_libftdi_CFLAGS=`$PKG_CONFIG --cflags "libftdi >= 0.16" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
-else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
-fi
-if test -n "$libftdi_LIBS"; then
-    pkg_cv_libftdi_LIBS="$libftdi_LIBS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libftdi >= 0.16\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libftdi >= 0.16") 2>&5
+  	sr_have_python_dev=yes
+	SR_PKGLIBS_PYTHON=${SR_PKGLIBS_PYTHON}${SR_PKGLIBS_PYTHON:+' '}"python = $PYTHON_VERSION"
+	sr_python_dev_version=`$PKG_CONFIG --modversion "python = $PYTHON_VERSION" 2>&5`
+	sr_pkg_check_summary_append "python = $PYTHON_VERSION" "$sr_python_dev_version"
+else
+  	sr_pkg_check_summary_append "python = $PYTHON_VERSION" no
+	if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python\$sr_pymajor = \$PYTHON_VERSION\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "python$sr_pymajor = $PYTHON_VERSION") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_libftdi_LIBS=`$PKG_CONFIG --libs "libftdi >= 0.16" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+  	sr_have_python_dev=yes
+	SR_PKGLIBS_PYTHON=${SR_PKGLIBS_PYTHON}${SR_PKGLIBS_PYTHON:+' '}"python$sr_pymajor = $PYTHON_VERSION"
+	sr_python_dev_version=`$PKG_CONFIG --modversion "python$sr_pymajor = $PYTHON_VERSION" 2>&5`
+	sr_pkg_check_summary_append "python$sr_pymajor = $PYTHON_VERSION" "$sr_python_dev_version"
+else
+  	sr_pkg_check_summary_append "python$sr_pymajor = $PYTHON_VERSION" no
+	if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python\$sr_pymajor\$sr_pyminor = \$PYTHON_VERSION\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "python$sr_pymajor$sr_pyminor = $PYTHON_VERSION") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_python_dev=yes
+	SR_PKGLIBS_PYTHON=${SR_PKGLIBS_PYTHON}${SR_PKGLIBS_PYTHON:+' '}"python$sr_pymajor$sr_pyminor = $PYTHON_VERSION"
+	sr_python_dev_version=`$PKG_CONFIG --modversion "python$sr_pymajor$sr_pyminor = $PYTHON_VERSION" 2>&5`
+	sr_pkg_check_summary_append "python$sr_pymajor$sr_pyminor = $PYTHON_VERSION" "$sr_python_dev_version"
+else
+  	sr_pkg_check_summary_append "python$sr_pymajor$sr_pyminor = $PYTHON_VERSION" no
+	if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"python-\$PYTHON_VERSION = \$PYTHON_VERSION\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "python-$PYTHON_VERSION = $PYTHON_VERSION") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_python_dev=yes
+	SR_PKGLIBS_PYTHON=${SR_PKGLIBS_PYTHON}${SR_PKGLIBS_PYTHON:+' '}"python-$PYTHON_VERSION = $PYTHON_VERSION"
+	sr_python_dev_version=`$PKG_CONFIG --modversion "python-$PYTHON_VERSION = $PYTHON_VERSION" 2>&5`
+	sr_pkg_check_summary_append "python-$PYTHON_VERSION = $PYTHON_VERSION" "$sr_python_dev_version"
 else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
+  	sr_pkg_check_summary_append "python-$PYTHON_VERSION = $PYTHON_VERSION" no
+	sr_have_python_dev=no sr_python_dev_version=
 fi
 
+fi
 
-
-if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
-else
-        _pkg_short_errors_supported=no
 fi
-        if test $_pkg_short_errors_supported = yes; then
-	        libftdi_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libftdi >= 0.16" 2>&1`
-        else
-	        libftdi_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libftdi >= 0.16" 2>&1`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$libftdi_PKG_ERRORS" >&5
 
-	HW_ASIX_SIGMA="no"; HW_CHRONOVU_LA="no"; HW_IKALOGIC_SCANAPLUS="no"
-elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-	HW_ASIX_SIGMA="no"; HW_CHRONOVU_LA="no"; HW_IKALOGIC_SCANAPLUS="no"
-else
-	libftdi_CFLAGS=$pkg_cv_libftdi_CFLAGS
-	libftdi_LIBS=$pkg_cv_libftdi_LIBS
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	LIB_CFLAGS="$LIB_CFLAGS $libftdi_CFLAGS";
-	LIBS="$LIBS $libftdi_LIBS";
-	SR_PKGLIBS="$SR_PKGLIBS libftdi"
 fi
 
-# The Check unit testing framework is optional. Disable if not found.
 
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for check" >&5
-$as_echo_n "checking for check... " >&6; }
+if test "x$sr_have_python_dev" != xyes; then :
+  sr_python_missing=${sr_python_missing}${sr_python_missing:+', '}Headers
+fi
 
-if test -n "$check_CFLAGS"; then
-    pkg_cv_check_CFLAGS="$check_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"check >= 0.9.4\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "check >= 0.9.4") 2>&5
+# PyGObject is needed for the Python bindings.
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"pygobject-3.0 >= 3.0.0\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "pygobject-3.0 >= 3.0.0") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_check_CFLAGS=`$PKG_CONFIG --cflags "check >= 0.9.4" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
+  	sr_have_pygobject=yes
+	SR_PKGLIBS_PYTHON=${SR_PKGLIBS_PYTHON}${SR_PKGLIBS_PYTHON:+' '}"pygobject-3.0 >= 3.0.0"
+	sr_pygobject_version=`$PKG_CONFIG --modversion "pygobject-3.0 >= 3.0.0" 2>&5`
+	sr_pkg_check_summary_append "pygobject-3.0 >= 3.0.0" "$sr_pygobject_version"
 else
-  pkg_failed=yes
+  	sr_pkg_check_summary_append "pygobject-3.0 >= 3.0.0" no
+	sr_have_pygobject=no sr_pygobject_version=
 fi
- else
-    pkg_failed=untried
+
+if test "x$sr_have_pygobject" != xyes; then :
+  sr_python_missing=${sr_python_missing}${sr_python_missing:+', '}PyGObject
 fi
-if test -n "$check_LIBS"; then
-    pkg_cv_check_LIBS="$check_LIBS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"check >= 0.9.4\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "check >= 0.9.4") 2>&5
+
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"pygobject-3.0 < 3.7.91\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "pygobject-3.0 < 3.7.91") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_check_LIBS=`$PKG_CONFIG --libs "check >= 0.9.4" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
-else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
+
+$as_echo "#define PYGOBJECT_FLAGS_SIGNED 1" >>confdefs.h
+
 fi
 
+# The Python bindings need the setuptools and numpy Python modules.
+# We'll let it go through even if the AX macro wasn't found, as the
+# Python modules may still be there.
 
 
-if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+    if test -z $PYTHON;
+    then
+        if test -z "";
+        then
+            PYTHON="python3"
+        else
+            PYTHON=""
+        fi
+    fi
+    PYTHON_NAME=`basename $PYTHON`
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking $PYTHON_NAME module: setuptools" >&5
+$as_echo_n "checking $PYTHON_NAME module: setuptools... " >&6; }
+    $PYTHON -c "import setuptools" 2>/dev/null
+    if test $? -eq 0;
+    then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+        eval HAVE_PYMOD_SETUPTOOLS=yes
+    else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
+        eval HAVE_PYMOD_SETUPTOOLS=no
+        #
+        if test -n ""
+        then
+            as_fn_error $? "failed to find required module setuptools" "$LINENO" 5
+            exit 1
+        fi
+    fi
 
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
-else
-        _pkg_short_errors_supported=no
-fi
-        if test $_pkg_short_errors_supported = yes; then
-	        check_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "check >= 0.9.4" 2>&1`
+
+    if test -z $PYTHON;
+    then
+        if test -z "";
+        then
+            PYTHON="python3"
         else
-	        check_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "check >= 0.9.4" 2>&1`
+            PYTHON=""
         fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$check_PKG_ERRORS" >&5
-
-	have_check="no"
-elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-	have_check="no"
-else
-	check_CFLAGS=$pkg_cv_check_CFLAGS
-	check_LIBS=$pkg_cv_check_LIBS
+    fi
+    PYTHON_NAME=`basename $PYTHON`
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking $PYTHON_NAME module: numpy" >&5
+$as_echo_n "checking $PYTHON_NAME module: numpy... " >&6; }
+    $PYTHON -c "import numpy" 2>/dev/null
+    if test $? -eq 0;
+    then
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	have_check="yes"; LIB_CFLAGS="$LIB_CFLAGS $check_CFLAGS";
-	LIBS="$LIBS $check_LIBS"
+        eval HAVE_PYMOD_NUMPY=yes
+    else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+        eval HAVE_PYMOD_NUMPY=no
+        #
+        if test -n ""
+        then
+            as_fn_error $? "failed to find required module numpy" "$LINENO" 5
+            exit 1
+        fi
+    fi
+
+
+if test "x$HAVE_PYMOD_SETUPTOOLS" != xyes; then :
+  sr_python_missing=${sr_python_missing}${sr_python_missing:+', '}setuptools
 fi
- if test x"$have_check" = "xyes"; then
-  HAVE_CHECK_TRUE=
-  HAVE_CHECK_FALSE='#'
+if test "x$HAVE_PYMOD_NUMPY" != xyes; then :
+  sr_python_missing=${sr_python_missing}${sr_python_missing:+', '}numpy
+fi
+
+# The Python bindings use SWIG to generate code.
+for ac_prog in swig swig3.0 swig2.0
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SWIG+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  HAVE_CHECK_TRUE='#'
-  HAVE_CHECK_FALSE=
+  if test -n "$SWIG"; then
+  ac_cv_prog_SWIG="$SWIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_SWIG="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+SWIG=$ac_cv_prog_SWIG
+if test -n "$SWIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5
+$as_echo "$SWIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 
 
-# The OLS driver uses serial port file descriptors directly, and therefore
-# will not currently work on Windows.
-case "$host" in
-*mingw*)
-       HW_OLS="no"
-       ;;
-esac
+  test -n "$SWIG" && break
+done
 
+if test "x$SWIG" != x; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SWIG version" >&5
+$as_echo_n "checking for $SWIG version... " >&6; }
+    SWIG_VERSION=`$SWIG -version 2>&1 | sed -n 's/SWIG Version\s*//p'`
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_VERSION" >&5
+$as_echo "$SWIG_VERSION" >&6; }
+fi
+if test "x$SWIG" = x; then :
+  sr_python_missing=${sr_python_missing}${sr_python_missing:+', '}SWIG
+fi
 
+if test -z "$sr_python_missing"; then :
+  BINDINGS_PYTHON=$enable_python
+else
+  BINDINGS_PYTHON=no
+fi
+ if test "x$BINDINGS_PYTHON" = xyes; then
+  BINDINGS_PYTHON_TRUE=
+  BINDINGS_PYTHON_FALSE='#'
+else
+  BINDINGS_PYTHON_TRUE='#'
+  BINDINGS_PYTHON_FALSE=
+fi
 
-CFLAGS="$CFLAGS $LIB_CFLAGS"
 
-# Now set AM_CONDITIONALs and AC_DEFINEs for the enabled/disabled drivers.
+#####################
+##  Ruby bindings  ##
+#####################
 
- if test x$HW_AGILENT_DMM = xyes; then
-  HW_AGILENT_DMM_TRUE=
-  HW_AGILENT_DMM_FALSE='#'
+if test "x$BINDINGS_CXX" = xyes; then :
+  sr_ruby_missing=
 else
-  HW_AGILENT_DMM_TRUE='#'
-  HW_AGILENT_DMM_FALSE=
+  sr_ruby_missing='C++ bindings'
+fi
+
+for ac_prog in "${RUBY-ruby}"
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_RUBY+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $RUBY in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_RUBY="$RUBY" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_RUBY="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+RUBY=$ac_cv_path_RUBY
+if test -n "$RUBY"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY" >&5
+$as_echo "$RUBY" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 
-if test "x$HW_AGILENT_DMM" = "xyes"; then
 
-$as_echo "#define HAVE_HW_AGILENT_DMM 1" >>confdefs.h
+  test -n "$RUBY" && break
+done
+
+if test "x$RUBY" != x; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby version" >&5
+$as_echo_n "checking for Ruby version... " >&6; }
+    RUBY_VERSION=`$RUBY -e 'puts RUBY_VERSION'`
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_VERSION" >&5
+$as_echo "$RUBY_VERSION" >&6; }
+    RUBY_DLEXT=`$RUBY -rrbconfig -e 'puts RbConfig::CONFIG["DLEXT"]'`
+
+fi
 
+if test "x$RUBY" = x; then :
+  sr_ruby_missing=${sr_ruby_missing}${sr_ruby_missing:+', '}Ruby
 fi
 
- if test x$HW_APPA_55II = xyes; then
-  HW_APPA_55II_TRUE=
-  HW_APPA_55II_FALSE='#'
+# Extract major and minor version number of the Ruby interpreter.
+sr_rbmajor=${RUBY_VERSION%%.*}
+sr_rbminor=${RUBY_VERSION#*.}
+sr_rbminor=${sr_rbminor%%.*}
+
+# The Ruby bindings need Ruby development files.
+if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ruby\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "ruby") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_ruby_dev=yes
+	SR_PKGLIBS_RUBY=${SR_PKGLIBS_RUBY}${SR_PKGLIBS_RUBY:+' '}"ruby"
+	sr_ruby_dev_version=`$PKG_CONFIG --modversion "ruby" 2>&5`
+	sr_pkg_check_summary_append "ruby" "$sr_ruby_dev_version"
+else
+  	sr_pkg_check_summary_append "ruby" no
+	if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ruby-\$sr_rbmajor.\$sr_rbminor\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "ruby-$sr_rbmajor.$sr_rbminor") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  	sr_have_ruby_dev=yes
+	SR_PKGLIBS_RUBY=${SR_PKGLIBS_RUBY}${SR_PKGLIBS_RUBY:+' '}"ruby-$sr_rbmajor.$sr_rbminor"
+	sr_ruby_dev_version=`$PKG_CONFIG --modversion "ruby-$sr_rbmajor.$sr_rbminor" 2>&5`
+	sr_pkg_check_summary_append "ruby-$sr_rbmajor.$sr_rbminor" "$sr_ruby_dev_version"
 else
-  HW_APPA_55II_TRUE='#'
-  HW_APPA_55II_FALSE=
+  	sr_pkg_check_summary_append "ruby-$sr_rbmajor.$sr_rbminor" no
+	sr_have_ruby_dev=no sr_ruby_dev_version=
 fi
 
-if test "x$HW_APPA_55II" = "xyes"; then
+fi
 
-$as_echo "#define HAVE_HW_APPA_55II 1" >>confdefs.h
 
+if test "x$sr_have_ruby_dev" != xyes; then :
+  sr_ruby_missing=${sr_ruby_missing}${sr_ruby_missing:+', '}Headers
 fi
 
- if test x$HW_ASIX_SIGMA = xyes; then
-  HW_ASIX_SIGMA_TRUE=
-  HW_ASIX_SIGMA_FALSE='#'
+# The Ruby bindings use SWIG >= 3.0.8 to generate code.
+if test "x$SWIG" = x; then :
+  sr_ruby_missing=${sr_ruby_missing}${sr_ruby_missing:+', '}SWIG
 else
-  HW_ASIX_SIGMA_TRUE='#'
-  HW_ASIX_SIGMA_FALSE=
+  as_arg_v1=$SWIG_VERSION
+as_arg_v2="3.0.8"
+awk "$as_awk_strverscmp" v1="$as_arg_v1" v2="$as_arg_v2" /dev/null
+case $? in #(
+  1) :
+    sr_ruby_missing=${sr_ruby_missing}${sr_ruby_missing:+', '}'SWIG >= 3.0.8' ;; #(
+  0) :
+     ;; #(
+  2) :
+     ;; #(
+  *) :
+     ;;
+esac
 fi
 
-if test "x$HW_ASIX_SIGMA" = "xyes"; then
-
-$as_echo "#define HAVE_HW_ASIX_SIGMA 1" >>confdefs.h
-
+if test -z "$sr_ruby_missing"; then :
+  BINDINGS_RUBY=$enable_ruby
+else
+  BINDINGS_RUBY=no
 fi
-
- if test x$HW_ATTEN_PPS3XXX = xyes; then
-  HW_ATTEN_PPS3XXX_TRUE=
-  HW_ATTEN_PPS3XXX_FALSE='#'
+ if test "x$BINDINGS_RUBY" = xyes; then
+  BINDINGS_RUBY_TRUE=
+  BINDINGS_RUBY_FALSE='#'
 else
-  HW_ATTEN_PPS3XXX_TRUE='#'
-  HW_ATTEN_PPS3XXX_FALSE=
+  BINDINGS_RUBY_TRUE='#'
+  BINDINGS_RUBY_FALSE=
 fi
 
-if test "x$HW_ATTEN_PPS3XXX" = "xyes"; then
-
-$as_echo "#define HAVE_HW_ATTEN_PPS3XXX 1" >>confdefs.h
 
-fi
+####################
+##  Java bindings ##
+####################
 
- if test x$HW_BRYMEN_BM86X = xyes; then
-  HW_BRYMEN_BM86X_TRUE=
-  HW_BRYMEN_BM86X_FALSE='#'
+if test "x$BINDINGS_CXX" = xyes; then :
+  sr_java_missing=
 else
-  HW_BRYMEN_BM86X_TRUE='#'
-  HW_BRYMEN_BM86X_FALSE=
+  sr_java_missing='C++ bindings'
 fi
 
-if test "x$HW_BRYMEN_BM86X" = "xyes"; then
+# The Java bindings use SWIG to generate code.
+if test "x$SWIG" = x; then :
+  sr_java_missing=${sr_java_missing}${sr_java_missing:+', '}SWIG
+fi
 
-$as_echo "#define HAVE_HW_BRYMEN_BM86X 1" >>confdefs.h
+# Find Java compiler and JNI includes for Java bindings.
+# Extract the first word of "javac", so it can be a program name with args.
+set dummy javac; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_HAVE_JAVAC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$HAVE_JAVAC"; then
+  ac_cv_prog_HAVE_JAVAC="$HAVE_JAVAC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_HAVE_JAVAC="yes"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
 
+  test -z "$ac_cv_prog_HAVE_JAVAC" && ac_cv_prog_HAVE_JAVAC="no"
 fi
-
- if test x$HW_BRYMEN_DMM = xyes; then
-  HW_BRYMEN_DMM_TRUE=
-  HW_BRYMEN_DMM_FALSE='#'
+fi
+HAVE_JAVAC=$ac_cv_prog_HAVE_JAVAC
+if test -n "$HAVE_JAVAC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $HAVE_JAVAC" >&5
+$as_echo "$HAVE_JAVAC" >&6; }
 else
-  HW_BRYMEN_DMM_TRUE='#'
-  HW_BRYMEN_DMM_FALSE=
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 
-if test "x$HW_BRYMEN_DMM" = "xyes"; then
-
-$as_echo "#define HAVE_HW_BRYMEN_DMM 1" >>confdefs.h
 
+if test "x$HAVE_JAVAC" = xno; then :
+  sr_java_missing=${sr_java_missing}${sr_java_missing:+', '}JavaC
 fi
 
- if test x$HW_CEM_DT_885X = xyes; then
-  HW_CEM_DT_885X_TRUE=
-  HW_CEM_DT_885X_FALSE='#'
+
+# Check whether --with-jni-include-path was given.
+if test "${with_jni_include_path+set}" = set; then :
+  withval=$with_jni_include_path; JNI_INCLUDE_DIRS=" $withval"
 else
-  HW_CEM_DT_885X_TRUE='#'
-  HW_CEM_DT_885X_FALSE=
+  JNI_INCLUDE_DIRS=
 fi
 
-if test "x$HW_CEM_DT_885X" = "xyes"; then
 
-$as_echo "#define HAVE_HW_CEM_DT_885X 1" >>confdefs.h
+JNI_CPPFLAGS=
+if test "x$enable_java$HAVE_JAVAC" = xyesyes; then :
 
-fi
 
- if test x$HW_CENTER_3XX = xyes; then
-  HW_CENTER_3XX_TRUE=
-  HW_CENTER_3XX_FALSE='#'
+if test "x$JAVAPREFIX" = x; then :
+  test "x$JAVAC" = x && for ac_prog in "gcj -C" guavac jikes javac
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_JAVAC+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  HW_CENTER_3XX_TRUE='#'
-  HW_CENTER_3XX_FALSE=
-fi
-
-if test "x$HW_CENTER_3XX" = "xyes"; then
-
-$as_echo "#define HAVE_HW_CENTER_3XX 1" >>confdefs.h
+  if test -n "$JAVAC"; then
+  ac_cv_prog_JAVAC="$JAVAC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_JAVAC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
 
 fi
-
- if test x$HW_CHRONOVU_LA = xyes; then
-  HW_CHRONOVU_LA_TRUE=
-  HW_CHRONOVU_LA_FALSE='#'
+fi
+JAVAC=$ac_cv_prog_JAVAC
+if test -n "$JAVAC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $JAVAC" >&5
+$as_echo "$JAVAC" >&6; }
 else
-  HW_CHRONOVU_LA_TRUE='#'
-  HW_CHRONOVU_LA_FALSE=
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 
-if test "x$HW_CHRONOVU_LA" = "xyes"; then
 
-$as_echo "#define HAVE_HW_CHRONOVU_LA 1" >>confdefs.h
+  test -n "$JAVAC" && break
+done
 
-fi
+else
+  test "x$JAVAC" = x && for ac_prog in "gcj -C" guavac jikes javac
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_JAVAC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$JAVAC"; then
+  ac_cv_prog_JAVAC="$JAVAC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $JAVAPREFIX/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_JAVAC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
 
- if test x$HW_COLEAD_SLM = xyes; then
-  HW_COLEAD_SLM_TRUE=
-  HW_COLEAD_SLM_FALSE='#'
+fi
+fi
+JAVAC=$ac_cv_prog_JAVAC
+if test -n "$JAVAC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $JAVAC" >&5
+$as_echo "$JAVAC" >&6; }
 else
-  HW_COLEAD_SLM_TRUE='#'
-  HW_COLEAD_SLM_FALSE=
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 
-if test "x$HW_COLEAD_SLM" = "xyes"; then
 
-$as_echo "#define HAVE_HW_COLEAD_SLM 1" >>confdefs.h
+  test -n "$JAVAC" && break
+done
 
 fi
+test "x$JAVAC" = x && as_fn_error $? "no acceptable Java compiler found in \$PATH" "$LINENO" 5
 
- if test x$HW_CONRAD_DIGI_35_CPU = xyes; then
-  HW_CONRAD_DIGI_35_CPU_TRUE=
-  HW_CONRAD_DIGI_35_CPU_FALSE='#'
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $JAVAC works" >&5
+$as_echo_n "checking if $JAVAC works... " >&6; }
+if ${ac_cv_prog_javac_works+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  HW_CONRAD_DIGI_35_CPU_TRUE='#'
-  HW_CONRAD_DIGI_35_CPU_FALSE=
-fi
-
-if test "x$HW_CONRAD_DIGI_35_CPU" = "xyes"; then
-
-$as_echo "#define HAVE_HW_CONRAD_DIGI_35_CPU 1" >>confdefs.h
 
+JAVA_TEST=Test.java
+CLASS_TEST=Test.class
+cat << \EOF > $JAVA_TEST
+/* #line 20845 "configure" */
+public class Test {
+}
+EOF
+if { ac_try='$JAVAC $JAVACFLAGS $JAVA_TEST'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; } >/dev/null 2>&1; then
+  ac_cv_prog_javac_works=yes
+else
+  as_fn_error $? "The Java compiler $JAVAC failed (see config.log, check the CLASSPATH?)" "$LINENO" 5
+  echo "configure: failed program was:" >&5
+  cat $JAVA_TEST >&5
 fi
+rm -f $JAVA_TEST $CLASS_TEST
 
- if test x$HW_DEMO = xyes; then
-  HW_DEMO_TRUE=
-  HW_DEMO_FALSE='#'
-else
-  HW_DEMO_TRUE='#'
-  HW_DEMO_FALSE=
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_javac_works" >&5
+$as_echo "$ac_cv_prog_javac_works" >&6; }
 
-if test "x$HW_DEMO" = "xyes"; then
 
-$as_echo "#define HAVE_HW_DEMO 1" >>confdefs.h
+	if test -z "$JNI_INCLUDE_DIRS" && test "x$cross_compiling" != xyes; then :
 
-fi
+		## Work around the totally broken logic in AX_JNI_INCLUDE_DIR:
+		## If we can find jni.h without any special search path, skip
+		## the execution of the broken macro to increase our chances of
+		## success.
+		cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <jni.h>
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
 
- if test x$HW_FLUKE_DMM = xyes; then
-  HW_FLUKE_DMM_TRUE=
-  HW_FLUKE_DMM_FALSE='#'
 else
-  HW_FLUKE_DMM_TRUE='#'
-  HW_FLUKE_DMM_FALSE=
-fi
 
-if test "x$HW_FLUKE_DMM" = "xyes"; then
 
-$as_echo "#define HAVE_HW_FLUKE_DMM 1" >>confdefs.h
+JNI_INCLUDE_DIRS=""
 
-fi
+if test "x$JAVA_HOME" != x; then
+	_JTOPDIR="$JAVA_HOME"
+else
+	if test "x$JAVAC" = x; then
+		JAVAC=javac
+	fi
+	# Extract the first word of "$JAVAC", so it can be a program name with args.
+set dummy $JAVAC; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path__ACJNI_JAVAC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $_ACJNI_JAVAC in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path__ACJNI_JAVAC="$_ACJNI_JAVAC" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path__ACJNI_JAVAC="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
 
- if test x$HW_FX2LAFW = xyes; then
-  HW_FX2LAFW_TRUE=
-  HW_FX2LAFW_FALSE='#'
+  test -z "$ac_cv_path__ACJNI_JAVAC" && ac_cv_path__ACJNI_JAVAC="no"
+  ;;
+esac
+fi
+_ACJNI_JAVAC=$ac_cv_path__ACJNI_JAVAC
+if test -n "$_ACJNI_JAVAC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_ACJNI_JAVAC" >&5
+$as_echo "$_ACJNI_JAVAC" >&6; }
 else
-  HW_FX2LAFW_TRUE='#'
-  HW_FX2LAFW_FALSE=
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 
-if test "x$HW_FX2LAFW" = "xyes"; then
 
-$as_echo "#define HAVE_HW_FX2LAFW 1" >>confdefs.h
+	if test "x$_ACJNI_JAVAC" = xno; then
+		as_fn_error $? "cannot find JDK; try setting \$JAVAC or \$JAVA_HOME" "$LINENO" 5
+	fi
 
+# find the include directory relative to the javac executable
+_cur=""$_ACJNI_JAVAC""
+while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking symlink for $_cur" >&5
+$as_echo_n "checking symlink for $_cur... " >&6; }
+        _slink=`ls -ld "$_cur" | sed 's/.* -> //'`
+        case "$_slink" in
+        /*) _cur="$_slink";;
+        # 'X' avoids triggering unwanted echo options.
+        *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[^/]*$::'`"$_slink";;
+        esac
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_cur" >&5
+$as_echo "$_cur" >&6; }
+done
+_ACJNI_FOLLOWED="$_cur"
+
+	_JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[^/]*$::'`
 fi
 
- if test x$HW_GMC_MH_1X_2X = xyes; then
-  HW_GMC_MH_1X_2X_TRUE=
-  HW_GMC_MH_1X_2X_FALSE='#'
+case "$host_os" in
+        darwin*)        # Apple JDK is at /System location and has headers symlinked elsewhere
+                        case "$_JTOPDIR" in
+                        /System/Library/Frameworks/JavaVM.framework/*)
+				_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[^/]*$::'`
+				_JINC="$_JTOPDIR/Headers";;
+			*)      _JINC="$_JTOPDIR/include";;
+                        esac;;
+        *)              _JINC="$_JTOPDIR/include";;
+esac
+$as_echo "$as_me:${as_lineno-$LINENO}: _JTOPDIR=$_JTOPDIR" >&5
+$as_echo "$as_me:${as_lineno-$LINENO}: _JINC=$_JINC" >&5
+
+# On Mac OS X 10.6.4, jni.h is a symlink:
+# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
+# -> ../../CurrentJDK/Headers/jni.h.
+as_ac_File=`$as_echo "ac_cv_file_$_JINC/jni.h" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $_JINC/jni.h" >&5
+$as_echo_n "checking for $_JINC/jni.h... " >&6; }
+if eval \${$as_ac_File+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  HW_GMC_MH_1X_2X_TRUE='#'
-  HW_GMC_MH_1X_2X_FALSE=
+  test "$cross_compiling" = yes &&
+  as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
+if test -r "$_JINC/jni.h"; then
+  eval "$as_ac_File=yes"
+else
+  eval "$as_ac_File=no"
 fi
-
-if test "x$HW_GMC_MH_1X_2X" = "xyes"; then
-
-$as_echo "#define HAVE_HW_GMC_MH_1X_2X 1" >>confdefs.h
-
 fi
-
- if test x$HW_HANTEK_DSO = xyes; then
-  HW_HANTEK_DSO_TRUE=
-  HW_HANTEK_DSO_FALSE='#'
+eval ac_res=\$$as_ac_File
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
+  JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JINC"
+else
+  _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[^/]*$::'`
+	 as_ac_File=`$as_echo "ac_cv_file_$_JTOPDIR/include/jni.h" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $_JTOPDIR/include/jni.h" >&5
+$as_echo_n "checking for $_JTOPDIR/include/jni.h... " >&6; }
+if eval \${$as_ac_File+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  HW_HANTEK_DSO_TRUE='#'
-  HW_HANTEK_DSO_FALSE=
+  test "$cross_compiling" = yes &&
+  as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
+if test -r "$_JTOPDIR/include/jni.h"; then
+  eval "$as_ac_File=yes"
+else
+  eval "$as_ac_File=no"
+fi
+fi
+eval ac_res=\$$as_ac_File
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
+  JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include"
+else
+  as_fn_error $? "cannot find JDK header files" "$LINENO" 5
 fi
 
-if test "x$HW_HANTEK_DSO" = "xyes"; then
-
-$as_echo "#define HAVE_HW_HANTEK_DSO 1" >>confdefs.h
 
 fi
 
- if test x$HW_HAMEG_HMO = xyes; then
-  HW_HAMEG_HMO_TRUE=
-  HW_HAMEG_HMO_FALSE='#'
-else
-  HW_HAMEG_HMO_TRUE='#'
-  HW_HAMEG_HMO_FALSE=
-fi
 
-if test "x$HW_HAMEG_HMO" = "xyes"; then
+# get the likely subdirectories for system specific java includes
+case "$host_os" in
+bsdi*)          _JNI_INC_SUBDIRS="bsdos";;
+freebsd*)       _JNI_INC_SUBDIRS="freebsd";;
+darwin*)        _JNI_INC_SUBDIRS="darwin";;
+linux*)         _JNI_INC_SUBDIRS="linux genunix";;
+osf*)           _JNI_INC_SUBDIRS="alpha";;
+solaris*)       _JNI_INC_SUBDIRS="solaris";;
+mingw*)		_JNI_INC_SUBDIRS="win32";;
+cygwin*)	_JNI_INC_SUBDIRS="win32";;
+*)              _JNI_INC_SUBDIRS="genunix";;
+esac
+
+# add any subdirectories that are present
+for JINCSUBDIR in $_JNI_INC_SUBDIRS
+do
+    if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
+         JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
+    fi
+done
 
-$as_echo "#define HAVE_HW_HAMEG_HMO 1" >>confdefs.h
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 fi
 
- if test x$HW_IKALOGIC_SCANALOGIC2 = xyes; then
-  HW_IKALOGIC_SCANALOGIC2_TRUE=
-  HW_IKALOGIC_SCANALOGIC2_FALSE='#'
-else
-  HW_IKALOGIC_SCANALOGIC2_TRUE='#'
-  HW_IKALOGIC_SCANALOGIC2_FALSE=
 fi
+for sr_dir in $JNI_INCLUDE_DIRS
+do
+	JNI_CPPFLAGS=${JNI_CPPFLAGS}${JNI_CPPFLAGS:+' '}"-I$sr_dir"
+done
 
-if test "x$HW_IKALOGIC_SCANALOGIC2" = "xyes"; then
 
-$as_echo "#define HAVE_HW_IKALOGIC_SCANALOGIC2 1" >>confdefs.h
+sr_save_cppflags=$CPPFLAGS
+CPPFLAGS=${CPPFLAGS}${CPPFLAGS:+' '}$JNI_CPPFLAGS
 
-fi
+ac_fn_cxx_check_header_mongrel "$LINENO" "jni.h" "ac_cv_header_jni_h" "$ac_includes_default"
+if test "x$ac_cv_header_jni_h" = xyes; then :
 
- if test x$HW_IKALOGIC_SCANAPLUS = xyes; then
-  HW_IKALOGIC_SCANAPLUS_TRUE=
-  HW_IKALOGIC_SCANAPLUS_FALSE='#'
 else
-  HW_IKALOGIC_SCANAPLUS_TRUE='#'
-  HW_IKALOGIC_SCANAPLUS_FALSE=
+  sr_java_missing=${sr_java_missing}${sr_java_missing:+', '}'JNI headers'
 fi
 
-if test "x$HW_IKALOGIC_SCANAPLUS" = "xyes"; then
 
-$as_echo "#define HAVE_HW_IKALOGIC_SCANAPLUS 1" >>confdefs.h
+CPPFLAGS=$sr_save_cppflags
 
+if test -z "$sr_java_missing"; then :
+  BINDINGS_JAVA=$enable_java
+else
+  BINDINGS_JAVA=no
 fi
-
- if test x$HW_KECHENG_KC_330B = xyes; then
-  HW_KECHENG_KC_330B_TRUE=
-  HW_KECHENG_KC_330B_FALSE='#'
+ if test "x$BINDINGS_JAVA" = xyes; then
+  BINDINGS_JAVA_TRUE=
+  BINDINGS_JAVA_FALSE='#'
 else
-  HW_KECHENG_KC_330B_TRUE='#'
-  HW_KECHENG_KC_330B_FALSE=
+  BINDINGS_JAVA_TRUE='#'
+  BINDINGS_JAVA_FALSE=
 fi
 
-if test "x$HW_KECHENG_KC_330B" = "xyes"; then
 
-$as_echo "#define HAVE_HW_KECHENG_KC_330B 1" >>confdefs.h
+##############################
+##  Finalize configuration  ##
+##############################
 
-fi
+# Add mandatory dependencies to module list.
+SR_PKGLIBS=${SR_PKGLIBS}${SR_PKGLIBS:+' '}'libzip >= 0.10'
 
- if test x$HW_LASCAR_EL_USB = xyes; then
-  HW_LASCAR_EL_USB_TRUE=
-  HW_LASCAR_EL_USB_FALSE='#'
-else
-  HW_LASCAR_EL_USB_TRUE='#'
-  HW_LASCAR_EL_USB_FALSE=
-fi
 
-if test "x$HW_LASCAR_EL_USB" = "xyes"; then
+# Retrieve the compile and link flags for all modules combined.
+# Also, bail out at this point if any module dependency is not met.
 
-$as_echo "#define HAVE_HW_LASCAR_EL_USB 1" >>confdefs.h
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBSIGROK" >&5
+$as_echo_n "checking for LIBSIGROK... " >&6; }
 
+if test -n "$LIBSIGROK_CFLAGS"; then
+    pkg_cv_LIBSIGROK_CFLAGS="$LIBSIGROK_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.32.0 \$SR_PKGLIBS\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.32.0 $SR_PKGLIBS") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBSIGROK_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.32.0 $SR_PKGLIBS" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
 fi
-
- if test x$HW_MIC_985XX = xyes; then
-  HW_MIC_985XX_TRUE=
-  HW_MIC_985XX_FALSE='#'
+ else
+    pkg_failed=untried
+fi
+if test -n "$LIBSIGROK_LIBS"; then
+    pkg_cv_LIBSIGROK_LIBS="$LIBSIGROK_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.32.0 \$SR_PKGLIBS\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.32.0 $SR_PKGLIBS") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBSIGROK_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.32.0 $SR_PKGLIBS" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
-  HW_MIC_985XX_TRUE='#'
-  HW_MIC_985XX_FALSE=
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
 fi
 
-if test "x$HW_MIC_985XX" = "xyes"; then
 
-$as_echo "#define HAVE_HW_MIC_985XX 1" >>confdefs.h
 
-fi
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
- if test x$HW_NORMA_DMM = xyes; then
-  HW_NORMA_DMM_TRUE=
-  HW_NORMA_DMM_FALSE='#'
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
 else
-  HW_NORMA_DMM_TRUE='#'
-  HW_NORMA_DMM_FALSE=
+        _pkg_short_errors_supported=no
 fi
+        if test $_pkg_short_errors_supported = yes; then
+	        LIBSIGROK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.32.0 $SR_PKGLIBS" 2>&1`
+        else
+	        LIBSIGROK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.32.0 $SR_PKGLIBS" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$LIBSIGROK_PKG_ERRORS" >&5
 
-if test "x$HW_NORMA_DMM" = "xyes"; then
+	as_fn_error $? "Package requirements (glib-2.0 >= 2.32.0 $SR_PKGLIBS) were not met:
 
-$as_echo "#define HAVE_HW_NORMA_DMM 1" >>confdefs.h
+$LIBSIGROK_PKG_ERRORS
 
-fi
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
 
- if test x$HW_OLS = xyes; then
-  HW_OLS_TRUE=
-  HW_OLS_FALSE='#'
-else
-  HW_OLS_TRUE='#'
-  HW_OLS_FALSE=
-fi
+Alternatively, you may set the environment variables LIBSIGROK_CFLAGS
+and LIBSIGROK_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
 
-if test "x$HW_OLS" = "xyes"; then
+Alternatively, you may set the environment variables LIBSIGROK_CFLAGS
+and LIBSIGROK_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
 
-$as_echo "#define HAVE_HW_OLS 1" >>confdefs.h
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+	LIBSIGROK_CFLAGS=$pkg_cv_LIBSIGROK_CFLAGS
+	LIBSIGROK_LIBS=$pkg_cv_LIBSIGROK_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
 
 fi
 
- if test x$HW_RIGOL_DS = xyes; then
-  HW_RIGOL_DS_TRUE=
-  HW_RIGOL_DS_FALSE='#'
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TESTS" >&5
+$as_echo_n "checking for TESTS... " >&6; }
+
+if test -n "$TESTS_CFLAGS"; then
+    pkg_cv_TESTS_CFLAGS="$TESTS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SR_PKGLIBS_TESTS glib-2.0 \$SR_PKGLIBS\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_TESTS_CFLAGS=`$PKG_CONFIG --cflags "$SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
-  HW_RIGOL_DS_TRUE='#'
-  HW_RIGOL_DS_FALSE=
+  pkg_failed=yes
 fi
-
-if test "x$HW_RIGOL_DS" = "xyes"; then
-
-$as_echo "#define HAVE_HW_RIGOL_DS 1" >>confdefs.h
-
+ else
+    pkg_failed=untried
 fi
-
- if test x$HW_SALEAE_LOGIC16 = xyes; then
-  HW_SALEAE_LOGIC16_TRUE=
-  HW_SALEAE_LOGIC16_FALSE='#'
+if test -n "$TESTS_LIBS"; then
+    pkg_cv_TESTS_LIBS="$TESTS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SR_PKGLIBS_TESTS glib-2.0 \$SR_PKGLIBS\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_TESTS_LIBS=`$PKG_CONFIG --libs "$SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
-  HW_SALEAE_LOGIC16_TRUE='#'
-  HW_SALEAE_LOGIC16_FALSE=
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
 fi
 
-if test "x$HW_SALEAE_LOGIC16" = "xyes"; then
 
-$as_echo "#define HAVE_HW_SALEAE_LOGIC16 1" >>confdefs.h
 
-fi
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
- if test x$HW_SERIAL_DMM = xyes; then
-  HW_SERIAL_DMM_TRUE=
-  HW_SERIAL_DMM_FALSE='#'
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
 else
-  HW_SERIAL_DMM_TRUE='#'
-  HW_SERIAL_DMM_FALSE=
+        _pkg_short_errors_supported=no
 fi
+        if test $_pkg_short_errors_supported = yes; then
+	        TESTS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS" 2>&1`
+        else
+	        TESTS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$TESTS_PKG_ERRORS" >&5
 
-if test "x$HW_SERIAL_DMM" = "xyes"; then
+	as_fn_error $? "Package requirements ($SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS) were not met:
 
-$as_echo "#define HAVE_HW_SERIAL_DMM 1" >>confdefs.h
+$TESTS_PKG_ERRORS
 
-fi
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
 
- if test x$HW_SYSCLK_LWLA = xyes; then
-  HW_SYSCLK_LWLA_TRUE=
-  HW_SYSCLK_LWLA_FALSE='#'
+Alternatively, you may set the environment variables TESTS_CFLAGS
+and TESTS_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables TESTS_CFLAGS
+and TESTS_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
 else
-  HW_SYSCLK_LWLA_TRUE='#'
-  HW_SYSCLK_LWLA_FALSE=
+	TESTS_CFLAGS=$pkg_cv_TESTS_CFLAGS
+	TESTS_LIBS=$pkg_cv_TESTS_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
 fi
 
-if test "x$HW_SYSCLK_LWLA" = "xyes"; then
+# SR_PKGLIBS_CXX may be empty, so only invoke these checks when
+# the C++ bindings are enabled.
+if test -z "$BINDINGS_CXX_TRUE"; then :
 
-$as_echo "#define HAVE_HW_SYSCLK_LWLA 1" >>confdefs.h
 
-fi
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBSIGROKCXX" >&5
+$as_echo_n "checking for LIBSIGROKCXX... " >&6; }
 
- if test x$HW_TELEINFO = xyes; then
-  HW_TELEINFO_TRUE=
-  HW_TELEINFO_FALSE='#'
+if test -n "$LIBSIGROKCXX_CFLAGS"; then
+    pkg_cv_LIBSIGROKCXX_CFLAGS="$LIBSIGROKCXX_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SR_PKGLIBS_CXX\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$SR_PKGLIBS_CXX") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBSIGROKCXX_CFLAGS=`$PKG_CONFIG --cflags "$SR_PKGLIBS_CXX" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
-  HW_TELEINFO_TRUE='#'
-  HW_TELEINFO_FALSE=
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$LIBSIGROKCXX_LIBS"; then
+    pkg_cv_LIBSIGROKCXX_LIBS="$LIBSIGROKCXX_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SR_PKGLIBS_CXX\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$SR_PKGLIBS_CXX") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBSIGROKCXX_LIBS=`$PKG_CONFIG --libs "$SR_PKGLIBS_CXX" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
 fi
 
-if test "x$HW_TELEINFO" = "xyes"; then
 
-$as_echo "#define HAVE_HW_TELEINFO 1" >>confdefs.h
 
-fi
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
- if test x$HW_TONDAJ_SL_814 = xyes; then
-  HW_TONDAJ_SL_814_TRUE=
-  HW_TONDAJ_SL_814_FALSE='#'
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
 else
-  HW_TONDAJ_SL_814_TRUE='#'
-  HW_TONDAJ_SL_814_FALSE=
+        _pkg_short_errors_supported=no
 fi
+        if test $_pkg_short_errors_supported = yes; then
+	        LIBSIGROKCXX_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SR_PKGLIBS_CXX" 2>&1`
+        else
+	        LIBSIGROKCXX_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SR_PKGLIBS_CXX" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$LIBSIGROKCXX_PKG_ERRORS" >&5
 
-if test "x$HW_TONDAJ_SL_814" = "xyes"; then
+	as_fn_error $? "Package requirements ($SR_PKGLIBS_CXX) were not met:
 
-$as_echo "#define HAVE_HW_TONDAJ_SL_814 1" >>confdefs.h
+$LIBSIGROKCXX_PKG_ERRORS
 
-fi
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
 
- if test x$HW_UNI_T_DMM = xyes; then
-  HW_UNI_T_DMM_TRUE=
-  HW_UNI_T_DMM_FALSE='#'
-else
-  HW_UNI_T_DMM_TRUE='#'
-  HW_UNI_T_DMM_FALSE=
-fi
+Alternatively, you may set the environment variables LIBSIGROKCXX_CFLAGS
+and LIBSIGROKCXX_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
 
-if test "x$HW_UNI_T_DMM" = "xyes"; then
+Alternatively, you may set the environment variables LIBSIGROKCXX_CFLAGS
+and LIBSIGROKCXX_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
 
-$as_echo "#define HAVE_HW_UNI_T_DMM 1" >>confdefs.h
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+	LIBSIGROKCXX_CFLAGS=$pkg_cv_LIBSIGROKCXX_CFLAGS
+	LIBSIGROKCXX_LIBS=$pkg_cv_LIBSIGROKCXX_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
 
 fi
 
- if test x$HW_UNI_T_UT32X = xyes; then
-  HW_UNI_T_UT32X_TRUE=
-  HW_UNI_T_UT32X_FALSE='#'
-else
-  HW_UNI_T_UT32X_TRUE='#'
-  HW_UNI_T_UT32X_FALSE=
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PYSIGROK" >&5
+$as_echo_n "checking for PYSIGROK... " >&6; }
+
+if test -n "$PYSIGROK_CFLAGS"; then
+    pkg_cv_PYSIGROK_CFLAGS="$PYSIGROK_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SR_PKGLIBS_PYTHON \$SR_PKGLIBS_CXX\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_PYSIGROK_CFLAGS=`$PKG_CONFIG --cflags "$SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
 fi
-
-if test "x$HW_UNI_T_UT32X" = "xyes"; then
-
-$as_echo "#define HAVE_HW_UNI_T_UT32X 1" >>confdefs.h
-
+ else
+    pkg_failed=untried
 fi
-
- if test x$HW_VICTOR_DMM = xyes; then
-  HW_VICTOR_DMM_TRUE=
-  HW_VICTOR_DMM_FALSE='#'
+if test -n "$PYSIGROK_LIBS"; then
+    pkg_cv_PYSIGROK_LIBS="$PYSIGROK_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SR_PKGLIBS_PYTHON \$SR_PKGLIBS_CXX\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_PYSIGROK_LIBS=`$PKG_CONFIG --libs "$SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
-  HW_VICTOR_DMM_TRUE='#'
-  HW_VICTOR_DMM_FALSE=
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
 fi
 
-if test "x$HW_VICTOR_DMM" = "xyes"; then
 
-$as_echo "#define HAVE_HW_VICTOR_DMM 1" >>confdefs.h
 
-fi
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
- if test x$HW_ZEROPLUS_LOGIC_CUBE = xyes; then
-  HW_ZEROPLUS_LOGIC_CUBE_TRUE=
-  HW_ZEROPLUS_LOGIC_CUBE_FALSE='#'
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
 else
-  HW_ZEROPLUS_LOGIC_CUBE_TRUE='#'
-  HW_ZEROPLUS_LOGIC_CUBE_FALSE=
+        _pkg_short_errors_supported=no
 fi
+        if test $_pkg_short_errors_supported = yes; then
+	        PYSIGROK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX" 2>&1`
+        else
+	        PYSIGROK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$PYSIGROK_PKG_ERRORS" >&5
 
-if test "x$HW_ZEROPLUS_LOGIC_CUBE" = "xyes"; then
+	as_fn_error $? "Package requirements ($SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX) were not met:
 
-$as_echo "#define HAVE_HW_ZEROPLUS_LOGIC_CUBE 1" >>confdefs.h
+$PYSIGROK_PKG_ERRORS
 
-fi
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
 
-# Checks for header files.
-# These are already checked: inttypes.h stdint.h stdlib.h string.h unistd.h.
+Alternatively, you may set the environment variables PYSIGROK_CFLAGS
+and PYSIGROK_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
 
-# Checks for typedefs, structures, and compiler characteristics.
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
-$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
-if ${ac_cv_c_bigendian+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_cv_c_bigendian=unknown
-    # See if we're dealing with a universal compiler.
-    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#ifndef __APPLE_CC__
-	       not a universal capable compiler
-	     #endif
-	     typedef int dummy;
+Alternatively, you may set the environment variables PYSIGROK_CFLAGS
+and PYSIGROK_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
 
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+	PYSIGROK_CFLAGS=$pkg_cv_PYSIGROK_CFLAGS
+	PYSIGROK_LIBS=$pkg_cv_PYSIGROK_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
 
-	# Check for potential -arch flags.  It is not universal unless
-	# there are at least two -arch flags with different values.
-	ac_arch=
-	ac_prev=
-	for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
-	 if test -n "$ac_prev"; then
-	   case $ac_word in
-	     i?86 | x86_64 | ppc | ppc64)
-	       if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
-		 ac_arch=$ac_word
-	       else
-		 ac_cv_c_bigendian=universal
-		 break
-	       fi
-	       ;;
-	   esac
-	   ac_prev=
-	 elif test "x$ac_word" = "x-arch"; then
-	   ac_prev=arch
-	 fi
-       done
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-    if test $ac_cv_c_bigendian = unknown; then
-      # See if sys/param.h defines the BYTE_ORDER macro.
-      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <sys/types.h>
-	     #include <sys/param.h>
-
-int
-main ()
-{
-#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
-		     && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
-		     && LITTLE_ENDIAN)
-	      bogus endian macros
-	     #endif
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  # It does; now see whether it defined to BIG_ENDIAN or not.
-	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <sys/types.h>
-		#include <sys/param.h>
 
-int
-main ()
-{
-#if BYTE_ORDER != BIG_ENDIAN
-		 not big endian
-		#endif
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for RBSIGROK" >&5
+$as_echo_n "checking for RBSIGROK... " >&6; }
 
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_c_bigendian=yes
+if test -n "$RBSIGROK_CFLAGS"; then
+    pkg_cv_RBSIGROK_CFLAGS="$RBSIGROK_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SR_PKGLIBS_RUBY \$SR_PKGLIBS_CXX\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_RBSIGROK_CFLAGS=`$PKG_CONFIG --cflags "$SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
-  ac_cv_c_bigendian=no
+  pkg_failed=yes
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ else
+    pkg_failed=untried
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-    fi
-    if test $ac_cv_c_bigendian = unknown; then
-      # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
-      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <limits.h>
-
-int
-main ()
-{
-#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
-	      bogus endian macros
-	     #endif
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  # It does; now see whether it defined to _BIG_ENDIAN or not.
-	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <limits.h>
-
-int
-main ()
-{
-#ifndef _BIG_ENDIAN
-		 not big endian
-		#endif
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_c_bigendian=yes
+if test -n "$RBSIGROK_LIBS"; then
+    pkg_cv_RBSIGROK_LIBS="$RBSIGROK_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SR_PKGLIBS_RUBY \$SR_PKGLIBS_CXX\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "$SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_RBSIGROK_LIBS=`$PKG_CONFIG --libs "$SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
-  ac_cv_c_bigendian=no
+  pkg_failed=yes
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ else
+    pkg_failed=untried
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-    fi
-    if test $ac_cv_c_bigendian = unknown; then
-      # Compile a test program.
-      if test "$cross_compiling" = yes; then :
-  # Try to guess by grepping values from an object file.
-	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-short int ascii_mm[] =
-		  { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
-		short int ascii_ii[] =
-		  { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
-		int use_ascii (int i) {
-		  return ascii_mm[i] + ascii_ii[i];
-		}
-		short int ebcdic_ii[] =
-		  { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
-		short int ebcdic_mm[] =
-		  { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
-		int use_ebcdic (int i) {
-		  return ebcdic_mm[i] + ebcdic_ii[i];
-		}
-		extern int foo;
 
-int
-main ()
-{
-return use_ascii (foo) == use_ebcdic (foo);
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
-	      ac_cv_c_bigendian=yes
-	    fi
-	    if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
-	      if test "$ac_cv_c_bigendian" = unknown; then
-		ac_cv_c_bigendian=no
-	      else
-		# finding both strings is unlikely to happen, but who knows?
-		ac_cv_c_bigendian=unknown
-	      fi
-	    fi
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$ac_includes_default
-int
-main ()
-{
 
-	     /* Are we little or big endian?  From Harbison&Steele.  */
-	     union
-	     {
-	       long int l;
-	       char c[sizeof (long int)];
-	     } u;
-	     u.l = 1;
-	     return u.c[sizeof (long int) - 1] == 1;
 
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
-  ac_cv_c_bigendian=no
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
 else
-  ac_cv_c_bigendian=yes
+        _pkg_short_errors_supported=no
 fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-  conftest.$ac_objext conftest.beam conftest.$ac_ext
+        if test $_pkg_short_errors_supported = yes; then
+	        RBSIGROK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX" 2>&1`
+        else
+	        RBSIGROK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$RBSIGROK_PKG_ERRORS" >&5
+
+	as_fn_error $? "Package requirements ($SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX) were not met:
+
+$RBSIGROK_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables RBSIGROK_CFLAGS
+and RBSIGROK_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables RBSIGROK_CFLAGS
+and RBSIGROK_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+	RBSIGROK_CFLAGS=$pkg_cv_RBSIGROK_CFLAGS
+	RBSIGROK_LIBS=$pkg_cv_RBSIGROK_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	RBSIGROK_EXTDIR="lib/$($PKG_CONFIG --variable=sitearch $SR_PKGLIBS_RUBY)/$($PKG_CONFIG --variable=RUBY_BASE_NAME $SR_PKGLIBS_RUBY)/vendor_ruby/$($PKG_CONFIG --variable=ruby_version $SR_PKGLIBS_RUBY)"
+
 fi
 
-    fi
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
-$as_echo "$ac_cv_c_bigendian" >&6; }
- case $ac_cv_c_bigendian in #(
-   yes)
-     $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
-;; #(
-   no)
-      ;; #(
-   universal)
 
-$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+# Check for specific libusb features, now that we know the CFLAGS.
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
-     ;; #(
-   *)
-     as_fn_error $? "unknown endianness
- presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
- esac
+sr_save_cflags=$CFLAGS
+sr_save_libs=$LIBS
+CFLAGS="$LIBSIGROK_CFLAGS $CFLAGS"
+LIBS="$LIBSIGROK_LIBS $LIBS"
+ac_fn_c_check_type "$LINENO" "libusb_os_handle" "ac_cv_type_libusb_os_handle" "#include <libusb.h>
+"
+if test "x$ac_cv_type_libusb_os_handle" = xyes; then :
 
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBUSB_OS_HANDLE 1
+_ACEOF
+
+sr_have_libusb_os_handle=yes
+else
+  sr_have_libusb_os_handle=no
+fi
 
-FIRMWARE_DIR="$datadir/sigrok-firmware"
+for ac_func in zip_discard
+do :
+  ac_fn_c_check_func "$LINENO" "zip_discard" "ac_cv_func_zip_discard"
+if test "x$ac_cv_func_zip_discard" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_ZIP_DISCARD 1
+_ACEOF
 
-MAKEFLAGS='--no-print-directory'
+fi
+done
 
-AM_LIBTOOLFLAGS='--silent'
+LIBS=$sr_save_libs
+CFLAGS=$sr_save_cflags
 
+if test -z "$NEED_USB_TRUE"; then :
+  case $sr_have_libusb_os_handle:$host_os in #(
+  no:mingw*) :
+    as_fn_error $? "Windows builds require the event-abstraction branch of libusb" "$LINENO" 5 ;; #(
+  *) :
+     ;;
+esac
+fi
 
-SR_PACKAGE_VERSION_MAJOR=0
-SR_PACKAGE_VERSION_MINOR=3
-SR_PACKAGE_VERSION_MICRO=0
-SR_PACKAGE_VERSION=0.3.0
+sr_glib_version=`$PKG_CONFIG --modversion glib-2.0 2>&5`
+sr_libzip_version=`$PKG_CONFIG --modversion libzip 2>&5`
 
 
+cat >>confdefs.h <<_ACEOF
+#define CONF_LIBZIP_VERSION "$sr_libzip_version"
+_ACEOF
 
 
+cat >>confdefs.h <<_ACEOF
+#define CONF_HOST "$host"
+_ACEOF
 
 
-ac_config_files="$ac_config_files Makefile version.h libsigrok.pc"
+ac_config_files="$ac_config_files Makefile libsigrok.pc bindings/cxx/libsigrokcxx.pc"
 
 
 cat >confcache <<\_ACEOF
@@ -14875,26 +21758,39 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
   as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${NEED_RPC_TRUE}" && test -z "${NEED_RPC_FALSE}"; then
-  as_fn_error $? "conditional \"NEED_RPC\" was never defined.
+if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
+  as_fn_error $? "conditional \"am__fastdepCXX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WIN32_TRUE}" && test -z "${WIN32_FALSE}"; then
+  as_fn_error $? "conditional \"WIN32\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
 if test -z "${NEED_SERIAL_TRUE}" && test -z "${NEED_SERIAL_FALSE}"; then
   as_fn_error $? "conditional \"NEED_SERIAL\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${NEED_USB_TRUE}" && test -z "${NEED_USB_FALSE}"; then
+  as_fn_error $? "conditional \"NEED_USB\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${NEED_VISA_TRUE}" && test -z "${NEED_VISA_FALSE}"; then
   as_fn_error $? "conditional \"NEED_VISA\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${NEED_USB_TRUE}" && test -z "${NEED_USB_FALSE}"; then
-  as_fn_error $? "conditional \"NEED_USB\" was never defined.
+if test -z "${NEED_GPIB_TRUE}" && test -z "${NEED_GPIB_FALSE}"; then
+  as_fn_error $? "conditional \"NEED_GPIB\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
 if test -z "${HAVE_CHECK_TRUE}" && test -z "${HAVE_CHECK_FALSE}"; then
   as_fn_error $? "conditional \"HAVE_CHECK\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+
+if test -z "${NEED_RPC_TRUE}" && test -z "${NEED_RPC_FALSE}"; then
+  as_fn_error $? "conditional \"NEED_RPC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_AGILENT_DMM_TRUE}" && test -z "${HW_AGILENT_DMM_FALSE}"; then
   as_fn_error $? "conditional \"HW_AGILENT_DMM\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -14911,6 +21807,14 @@ if test -z "${HW_ATTEN_PPS3XXX_TRUE}" && test -z "${HW_ATTEN_PPS3XXX_FALSE}"; th
   as_fn_error $? "conditional \"HW_ATTEN_PPS3XXX\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_BAYLIBRE_ACME_TRUE}" && test -z "${HW_BAYLIBRE_ACME_FALSE}"; then
+  as_fn_error $? "conditional \"HW_BAYLIBRE_ACME\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HW_BEAGLELOGIC_TRUE}" && test -z "${HW_BEAGLELOGIC_FALSE}"; then
+  as_fn_error $? "conditional \"HW_BEAGLELOGIC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_BRYMEN_BM86X_TRUE}" && test -z "${HW_BRYMEN_BM86X_FALSE}"; then
   as_fn_error $? "conditional \"HW_BRYMEN_BM86X\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -14939,6 +21843,10 @@ if test -z "${HW_CONRAD_DIGI_35_CPU_TRUE}" && test -z "${HW_CONRAD_DIGI_35_CPU_F
   as_fn_error $? "conditional \"HW_CONRAD_DIGI_35_CPU\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_DEREE_DE5000_TRUE}" && test -z "${HW_DEREE_DE5000_FALSE}"; then
+  as_fn_error $? "conditional \"HW_DEREE_DE5000\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_DEMO_TRUE}" && test -z "${HW_DEMO_FALSE}"; then
   as_fn_error $? "conditional \"HW_DEMO\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -14955,14 +21863,22 @@ if test -z "${HW_GMC_MH_1X_2X_TRUE}" && test -z "${HW_GMC_MH_1X_2X_FALSE}"; then
   as_fn_error $? "conditional \"HW_GMC_MH_1X_2X\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${HW_HANTEK_DSO_TRUE}" && test -z "${HW_HANTEK_DSO_FALSE}"; then
-  as_fn_error $? "conditional \"HW_HANTEK_DSO\" was never defined.
+if test -z "${HW_GWINSTEK_GDS_800_TRUE}" && test -z "${HW_GWINSTEK_GDS_800_FALSE}"; then
+  as_fn_error $? "conditional \"HW_GWINSTEK_GDS_800\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
 if test -z "${HW_HAMEG_HMO_TRUE}" && test -z "${HW_HAMEG_HMO_FALSE}"; then
   as_fn_error $? "conditional \"HW_HAMEG_HMO\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_HANTEK_DSO_TRUE}" && test -z "${HW_HANTEK_DSO_FALSE}"; then
+  as_fn_error $? "conditional \"HW_HANTEK_DSO\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HW_HUNG_CHANG_DSO_2100_TRUE}" && test -z "${HW_HUNG_CHANG_DSO_2100_FALSE}"; then
+  as_fn_error $? "conditional \"HW_HUNG_CHANG_DSO_2100\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_IKALOGIC_SCANALOGIC2_TRUE}" && test -z "${HW_IKALOGIC_SCANALOGIC2_FALSE}"; then
   as_fn_error $? "conditional \"HW_IKALOGIC_SCANALOGIC2\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -14975,20 +21891,48 @@ if test -z "${HW_KECHENG_KC_330B_TRUE}" && test -z "${HW_KECHENG_KC_330B_FALSE}"
   as_fn_error $? "conditional \"HW_KECHENG_KC_330B\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_KERN_SCALE_TRUE}" && test -z "${HW_KERN_SCALE_FALSE}"; then
+  as_fn_error $? "conditional \"HW_KERN_SCALE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HW_KORAD_KAXXXXP_TRUE}" && test -z "${HW_KORAD_KAXXXXP_FALSE}"; then
+  as_fn_error $? "conditional \"HW_KORAD_KAXXXXP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_LASCAR_EL_USB_TRUE}" && test -z "${HW_LASCAR_EL_USB_FALSE}"; then
   as_fn_error $? "conditional \"HW_LASCAR_EL_USB\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_LECROY_LOGICSTUDIO_TRUE}" && test -z "${HW_LECROY_LOGICSTUDIO_FALSE}"; then
+  as_fn_error $? "conditional \"HW_LECROY_LOGICSTUDIO\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HW_MANSON_HCS_3XXX_TRUE}" && test -z "${HW_MANSON_HCS_3XXX_FALSE}"; then
+  as_fn_error $? "conditional \"HW_MANSON_HCS_3XXX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HW_MAYNUO_M97_TRUE}" && test -z "${HW_MAYNUO_M97_FALSE}"; then
+  as_fn_error $? "conditional \"HW_MAYNUO_M97\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_MIC_985XX_TRUE}" && test -z "${HW_MIC_985XX_FALSE}"; then
   as_fn_error $? "conditional \"HW_MIC_985XX\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_MOTECH_LPS_30X_TRUE}" && test -z "${HW_MOTECH_LPS_30X_FALSE}"; then
+  as_fn_error $? "conditional \"HW_MOTECH_LPS_30X\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_NORMA_DMM_TRUE}" && test -z "${HW_NORMA_DMM_FALSE}"; then
   as_fn_error $? "conditional \"HW_NORMA_DMM\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${HW_OLS_TRUE}" && test -z "${HW_OLS_FALSE}"; then
-  as_fn_error $? "conditional \"HW_OLS\" was never defined.
+if test -z "${HW_OPENBENCH_LOGIC_SNIFFER_TRUE}" && test -z "${HW_OPENBENCH_LOGIC_SNIFFER_FALSE}"; then
+  as_fn_error $? "conditional \"HW_OPENBENCH_LOGIC_SNIFFER\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HW_PIPISTRELLO_OLS_TRUE}" && test -z "${HW_PIPISTRELLO_OLS_FALSE}"; then
+  as_fn_error $? "conditional \"HW_PIPISTRELLO_OLS\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
 if test -z "${HW_RIGOL_DS_TRUE}" && test -z "${HW_RIGOL_DS_FALSE}"; then
@@ -14999,6 +21943,10 @@ if test -z "${HW_SALEAE_LOGIC16_TRUE}" && test -z "${HW_SALEAE_LOGIC16_FALSE}";
   as_fn_error $? "conditional \"HW_SALEAE_LOGIC16\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_SCPI_PPS_TRUE}" && test -z "${HW_SCPI_PPS_FALSE}"; then
+  as_fn_error $? "conditional \"HW_SCPI_PPS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_SERIAL_DMM_TRUE}" && test -z "${HW_SERIAL_DMM_FALSE}"; then
   as_fn_error $? "conditional \"HW_SERIAL_DMM\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -15011,6 +21959,10 @@ if test -z "${HW_TELEINFO_TRUE}" && test -z "${HW_TELEINFO_FALSE}"; then
   as_fn_error $? "conditional \"HW_TELEINFO\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_TESTO_TRUE}" && test -z "${HW_TESTO_FALSE}"; then
+  as_fn_error $? "conditional \"HW_TESTO\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_TONDAJ_SL_814_TRUE}" && test -z "${HW_TONDAJ_SL_814_FALSE}"; then
   as_fn_error $? "conditional \"HW_TONDAJ_SL_814\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -15027,11 +21979,30 @@ if test -z "${HW_VICTOR_DMM_TRUE}" && test -z "${HW_VICTOR_DMM_FALSE}"; then
   as_fn_error $? "conditional \"HW_VICTOR_DMM\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HW_YOKOGAWA_DLM_TRUE}" && test -z "${HW_YOKOGAWA_DLM_FALSE}"; then
+  as_fn_error $? "conditional \"HW_YOKOGAWA_DLM\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${HW_ZEROPLUS_LOGIC_CUBE_TRUE}" && test -z "${HW_ZEROPLUS_LOGIC_CUBE_FALSE}"; then
   as_fn_error $? "conditional \"HW_ZEROPLUS_LOGIC_CUBE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-
+if test -z "${BINDINGS_CXX_TRUE}" && test -z "${BINDINGS_CXX_FALSE}"; then
+  as_fn_error $? "conditional \"BINDINGS_CXX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${BINDINGS_PYTHON_TRUE}" && test -z "${BINDINGS_PYTHON_FALSE}"; then
+  as_fn_error $? "conditional \"BINDINGS_PYTHON\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${BINDINGS_RUBY_TRUE}" && test -z "${BINDINGS_RUBY_FALSE}"; then
+  as_fn_error $? "conditional \"BINDINGS_RUBY\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${BINDINGS_JAVA_TRUE}" && test -z "${BINDINGS_JAVA_FALSE}"; then
+  as_fn_error $? "conditional \"BINDINGS_JAVA\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 
 : "${CONFIG_STATUS=./config.status}"
 ac_write_fail=0
@@ -15429,7 +22400,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by libsigrok $as_me 0.3.0, which was
+This file was extended by libsigrok $as_me 0.4.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -15496,7 +22467,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-libsigrok config.status 0.3.0
+libsigrok config.status 0.4.0
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -15755,6 +22726,60 @@ enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_sub
 enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
 old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
 striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
+compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`'
+predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`'
+postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`'
+predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`'
+postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`'
+compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`'
+LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`'
+reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`'
+reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`'
+GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`'
+inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`'
+always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`'
+include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`'
+prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`'
+compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`'
+predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`'
+postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`'
+predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`'
+postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`'
+compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`'
 
 LTCC='$LTCC'
 LTCFLAGS='$LTCFLAGS'
@@ -15833,7 +22858,38 @@ soname_spec \
 install_override_mode \
 finish_eval \
 old_striplib \
-striplib; do
+striplib \
+compiler_lib_search_dirs \
+predep_objects \
+postdep_objects \
+predeps \
+postdeps \
+compiler_lib_search_path \
+LD_CXX \
+reload_flag_CXX \
+compiler_CXX \
+lt_prog_compiler_no_builtin_flag_CXX \
+lt_prog_compiler_pic_CXX \
+lt_prog_compiler_wl_CXX \
+lt_prog_compiler_static_CXX \
+lt_cv_prog_compiler_c_o_CXX \
+export_dynamic_flag_spec_CXX \
+whole_archive_flag_spec_CXX \
+compiler_needs_object_CXX \
+with_gnu_ld_CXX \
+allow_undefined_flag_CXX \
+no_undefined_flag_CXX \
+hardcode_libdir_flag_spec_CXX \
+hardcode_libdir_separator_CXX \
+exclude_expsyms_CXX \
+include_expsyms_CXX \
+file_list_spec_CXX \
+compiler_lib_search_dirs_CXX \
+predep_objects_CXX \
+postdep_objects_CXX \
+predeps_CXX \
+postdeps_CXX \
+compiler_lib_search_path_CXX; do
     case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
     *[\\\\\\\`\\"\\\$]*)
       eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
@@ -15863,7 +22919,18 @@ postinstall_cmds \
 postuninstall_cmds \
 finish_cmds \
 sys_lib_search_path_spec \
-sys_lib_dlsearch_path_spec; do
+sys_lib_dlsearch_path_spec \
+reload_cmds_CXX \
+old_archive_cmds_CXX \
+old_archive_from_new_cmds_CXX \
+old_archive_from_expsyms_cmds_CXX \
+archive_cmds_CXX \
+archive_expsym_cmds_CXX \
+module_cmds_CXX \
+module_expsym_cmds_CXX \
+export_symbols_cmds_CXX \
+prelink_cmds_CXX \
+postlink_cmds_CXX; do
     case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
     *[\\\\\\\`\\"\\\$]*)
       eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
@@ -15894,6 +22961,8 @@ fi
 
 
 
+
+
 _ACEOF
 
 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
@@ -15903,11 +22972,12 @@ for ac_config_target in $ac_config_targets
 do
   case $ac_config_target in
     "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "include/libsigrok/version.h") CONFIG_HEADERS="$CONFIG_HEADERS include/libsigrok/version.h" ;;
     "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
     "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
     "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
-    "version.h") CONFIG_FILES="$CONFIG_FILES version.h" ;;
     "libsigrok.pc") CONFIG_FILES="$CONFIG_FILES libsigrok.pc" ;;
+    "bindings/cxx/libsigrokcxx.pc") CONFIG_FILES="$CONFIG_FILES bindings/cxx/libsigrokcxx.pc" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -16646,7 +23716,7 @@ $as_echo X"$file" |
 
 
 # The names of the tagged configurations supported by this script.
-available_tags=""
+available_tags="CXX "
 
 # ### BEGIN LIBTOOL CONFIG
 
@@ -17033,6 +24103,20 @@ file_list_spec=$lt_file_list_spec
 # How to hardcode a shared library path into an executable.
 hardcode_action=$hardcode_action
 
+# The directories searched by this compiler when creating a shared library.
+compiler_lib_search_dirs=$lt_compiler_lib_search_dirs
+
+# Dependencies to place before and after the objects being linked to
+# create a shared library.
+predep_objects=$lt_predep_objects
+postdep_objects=$lt_postdep_objects
+predeps=$lt_predeps
+postdeps=$lt_postdeps
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path
+
 # ### END LIBTOOL CONFIG
 
 _LT_EOF
@@ -17225,6 +24309,159 @@ fi
     (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
   chmod +x "$ofile"
 
+
+    cat <<_LT_EOF >> "$ofile"
+
+# ### BEGIN LIBTOOL TAG CONFIG: CXX
+
+# The linker used to build libraries.
+LD=$lt_LD_CXX
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag_CXX
+reload_cmds=$lt_reload_cmds_CXX
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds_CXX
+
+# A language specific compiler.
+CC=$lt_compiler_CXX
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC_CXX
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic_CXX
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl_CXX
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static_CXX
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc_CXX
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object_CXX
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds_CXX
+archive_expsym_cmds=$lt_archive_expsym_cmds_CXX
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds_CXX
+module_expsym_cmds=$lt_module_expsym_cmds_CXX
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld_CXX
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag_CXX
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag_CXX
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct_CXX
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute_CXX
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L_CXX
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic_CXX
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath_CXX
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs_CXX
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols_CXX
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds_CXX
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms_CXX
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms_CXX
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds_CXX
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds_CXX
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec_CXX
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action_CXX
+
+# The directories searched by this compiler when creating a shared library.
+compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX
+
+# Dependencies to place before and after the objects being linked to
+# create a shared library.
+predep_objects=$lt_predep_objects_CXX
+postdep_objects=$lt_postdep_objects_CXX
+predeps=$lt_predeps_CXX
+postdeps=$lt_postdeps_CXX
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path_CXX
+
+# ### END LIBTOOL TAG CONFIG: CXX
+_LT_EOF
+
  ;;
 
   esac
@@ -17265,66 +24502,66 @@ $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
 fi
 
 
-echo
-echo "libsigrok configuration summary:"
-echo
-echo "  - Package version (major.minor.micro):    $SR_PACKAGE_VERSION"
-echo "  - Library version (current:revision:age): $SR_LIB_VERSION"
-echo "  - Prefix: $prefix"
-echo "  - Building on: $build"
-echo "  - Building for: $host"
-echo
-echo "Detected libraries:"
-echo
-
-# Note: This only works for libs with pkg-config integration.
-for lib in "glib-2.0 >= 2.32.0" "libzip >= 0.10" "libserialport >= 0.1.0" "librevisa >= 0.0.20130812" "libusb-1.0 >= 1.0.16" "libftdi >= 0.16" "check >= 0.9.4"; do
-	optional="OPTIONAL"
-	if test "x$lib" = "xglib-2.0 >= 2.32.0"; then optional="REQUIRED"; fi
-	if test "x$lib" = "xlibzip >= 0.10"; then optional="REQUIRED"; fi
-	if `$PKG_CONFIG --exists $lib`; then
-		ver=`$PKG_CONFIG --modversion $lib`
-		answer="yes ($ver)"
-	else
-		answer="no"
-	fi
-	echo "  - ($optional) $lib: $answer"
-done
+# Prepare bindings report messages.
+
+
+	sr_report_cxx=
+	test -z "$sr_cxx_missing" || sr_report_cxx=" (missing: $sr_cxx_missing)"
+	test "x$enable_cxx" = xyes || sr_report_cxx=' (disabled)'
+
+	sr_report_python=
+	test -z "$sr_python_missing" || sr_report_python=" (missing: $sr_python_missing)"
+	test "x$enable_python" = xyes || sr_report_python=' (disabled)'
+
+	sr_report_ruby=
+	test -z "$sr_ruby_missing" || sr_report_ruby=" (missing: $sr_ruby_missing)"
+	test "x$enable_ruby" = xyes || sr_report_ruby=' (disabled)'
+
+	sr_report_java=
+	test -z "$sr_java_missing" || sr_report_java=" (missing: $sr_java_missing)"
+	test "x$enable_java" = xyes || sr_report_java=' (disabled)'
+
+
+cat >&6 <<_EOF
+
+libsigrok configuration summary:
+ - Package version................. $SR_PACKAGE_VERSION
+ - Library ABI version............. $SR_LIB_VERSION
+ - Prefix.......................... $prefix
+ - Building on..................... $build
+ - Building for.................... $host
+
+Compile configuration:
+ - C compiler...................... $CC
+ - C compiler version.............. $sr_cc_version
+ - C compiler flags................ $CFLAGS
+ - Additional C compiler flags..... $SR_EXTRA_CFLAGS
+ - C compiler warnings............. $SR_WFLAGS
+ - C++ compiler.................... $CXX
+ - C++ compiler version............ $sr_cxx_version
+ - C++ compiler flags.............. $CXXFLAGS
+ - C++ compiler warnings........... $SR_WXXFLAGS
+
+Detected libraries (required):
+ - glib-2.0 >= 2.32.0.............. $sr_glib_version
+ - libzip >= 0.10.................. $sr_libzip_version
+
+Detected libraries (optional):
+$sr_pkglibs_summary
+Enabled hardware drivers:
+$sr_driver_summary
+Enabled SCPI backends:
+ - TCP............................. yes
+ - RPC............................. $sr_cv_have_rpc
+ - serial.......................... $sr_have_libserialport
+ - VISA............................ $sr_have_librevisa
+ - GPIB............................ $sr_have_libgpib
+ - USBTMC.......................... $sr_have_libusb
 
-echo -e "\nEnabled hardware drivers:\n"
-echo "  - agilent-dmm..................... $HW_AGILENT_DMM"
-echo "  - appa-55ii....................... $HW_APPA_55II"
-echo "  - asix-sigma...................... $HW_ASIX_SIGMA"
-echo "  - atten-pps3xxx................... $HW_ATTEN_PPS3XXX"
-echo "  - brymen-bm86x.................... $HW_BRYMEN_BM86X"
-echo "  - brymen-dmm...................... $HW_BRYMEN_DMM"
-echo "  - cem-dt-885x..................... $HW_CEM_DT_885X"
-echo "  - center-3xx...................... $HW_CENTER_3XX"
-echo "  - chronovu-la..................... $HW_CHRONOVU_LA"
-echo "  - colead-slm...................... $HW_COLEAD_SLM"
-echo "  - conrad-digi-35-cpu.............. $HW_CONRAD_DIGI_35_CPU"
-echo "  - demo............................ $HW_DEMO"
-echo "  - fluke-dmm....................... $HW_FLUKE_DMM"
-echo "  - fx2lafw......................... $HW_FX2LAFW"
-echo "  - gmc-mh-1x-2x.................... $HW_GMC_MH_1X_2X"
-echo "  - hameg-hmo....................... $HW_HAMEG_HMO"
-echo "  - hantek-dso...................... $HW_HANTEK_DSO"
-echo "  - ikalogic-scanalogic2............ $HW_IKALOGIC_SCANALOGIC2"
-echo "  - ikalogic-scanaplus.............. $HW_IKALOGIC_SCANAPLUS"
-echo "  - kecheng-kc-330b................. $HW_KECHENG_KC_330B"
-echo "  - lascar-el-usb................... $HW_LASCAR_EL_USB"
-echo "  - mic-985xx....................... $HW_MIC_985XX"
-echo "  - norma-dmm....................... $HW_NORMA_DMM"
-echo "  - openbench-logic-sniffer......... $HW_OLS"
-echo "  - rigol-ds........................ $HW_RIGOL_DS"
-echo "  - saleae-logic16.................. $HW_SALEAE_LOGIC16"
-echo "  - serial-dmm...................... $HW_SERIAL_DMM"
-echo "  - sysclk-lwla..................... $HW_SYSCLK_LWLA"
-echo "  - teleinfo........................ $HW_TELEINFO"
-echo "  - tondaj-sl-814................... $HW_TONDAJ_SL_814"
-echo "  - uni-t-dmm....................... $HW_UNI_T_DMM"
-echo "  - uni-t-ut32x..................... $HW_UNI_T_UT32X"
-echo "  - victor-dmm...................... $HW_VICTOR_DMM"
-echo "  - zeroplus-logic-cube............. $HW_ZEROPLUS_LOGIC_CUBE"
-echo
+Enabled language bindings:
+ - C++............................. $BINDINGS_CXX$sr_report_cxx
+ - Python.......................... $BINDINGS_PYTHON$sr_report_python
+ - Ruby............................ $BINDINGS_RUBY$sr_report_ruby
+ - Java............................ $BINDINGS_JAVA$sr_report_java
 
+_EOF
diff --git a/configure.ac b/configure.ac
index cb4eeaf..0df6588 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,631 +21,570 @@
 # We require at least autoconf 2.63 (AC_INIT format changed there).
 AC_PREREQ([2.63])
 
-# libsigrok package version number (NOT the same as shared lib version!).
-m4_define([sr_package_version_major], [0])
-m4_define([sr_package_version_minor], [3])
-m4_define([sr_package_version_micro], [0])
-m4_define([sr_package_version], [sr_package_version_major.sr_package_version_minor.sr_package_version_micro])
-
-AC_INIT([libsigrok], [sr_package_version], [sigrok-devel at lists.sourceforge.net],
+AC_INIT([libsigrok], [0.4.0],
+	[sigrok-devel at lists.sourceforge.net],
 	[libsigrok], [http://www.sigrok.org])
-AC_CONFIG_HEADER([config.h])
-AC_CONFIG_MACRO_DIR([autostuff])
+AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([autostuff])
+AC_CONFIG_HEADERS([config.h include/libsigrok/version.h])
 
 # We require at least automake 1.11 (needed for 'silent rules').
-AM_INIT_AUTOMAKE([1.11 -Wall -Werror subdir-objects check-news color-tests])
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AM_INIT_AUTOMAKE([1.11 -Wall -Werror no-define nostdinc subdir-objects check-news color-tests])
+AM_SILENT_RULES([yes])
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
 
-AH_TOP([#ifndef SR_CONFIG_H
-#define SR_CONFIG_H    /* To stop multiple inclusions. */])
-AH_BOTTOM([#endif /* SR_CONFIG_H */])
-
-# Enable more compiler warnings via -Wall and -Wextra. Add -fvisibility=hidden
-# and enforce use of SR_API to explicitly mark all public API functions.
-COMMON_FLAGS="$CFLAGS -Wall -Wextra -fvisibility=hidden"
-CFLAGS="$COMMON_FLAGS -Wmissing-prototypes"
+AC_CANONICAL_HOST
 
 # Checks for programs.
 AC_PROG_CC
-AC_PROG_CPP
+AC_PROG_CXX
 AC_PROG_INSTALL
 AC_PROG_LN_S
 
 # Required for per-target flags or subdir-objects with C sources.
 AM_PROG_CC_C_O
 
+# Set the standard the C library headers should conform to.
+AH_VERBATIM([_POSIX_C_SOURCE], [/* The targeted POSIX standard. */
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200112L
+#endif])
+
+# Get compiler versions.
+SR_PROG_VERSION([$CC], [sr_cc_version])
+SR_PROG_VERSION([$CXX], [sr_cxx_version])
+
+# Check for optional make features.
+SR_PROG_MAKE_ORDER_ONLY
+
 # Initialize libtool.
 LT_INIT
 
-# Initialize pkg-config.
-# We require at least 0.22, as "Requires.private" behaviour changed there.
-PKG_PROG_PKG_CONFIG([0.22])
+# Set up the libsigrok version defines.
+SR_PKG_VERSION_SET([SR_PACKAGE_VERSION], [AC_PACKAGE_VERSION])
 
 # Library version for libsigrok (NOT the same as the package version).
 # Carefully read the libtool docs before updating these numbers!
 # The algorithm for determining which number to change (and how) is nontrivial!
 # http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
-SR_LIB_VERSION_CURRENT=2
-SR_LIB_VERSION_REVISION=0
-SR_LIB_VERSION_AGE=0
-SR_LIB_VERSION="$SR_LIB_VERSION_CURRENT:$SR_LIB_VERSION_REVISION:$SR_LIB_VERSION_AGE"
-SR_LIB_LDFLAGS="-version-info $SR_LIB_VERSION"
-AC_SUBST(SR_LIB_VERSION_CURRENT)
-AC_SUBST(SR_LIB_VERSION_REVISION)
-AC_SUBST(SR_LIB_VERSION_AGE)
-AC_SUBST(SR_LIB_VERSION)
-AC_SUBST(SR_LIB_LDFLAGS)
-
-# Hardware support '--enable' options.
-
-AC_ARG_ENABLE(all-drivers, AC_HELP_STRING([--enable-all-drivers],
-	[enable all drivers by default [default=yes]]),
-	[HW_ENABLED_DEFAULT="$enableval"],
-	[HW_ENABLED_DEFAULT="yes"])
-
-AC_ARG_ENABLE(agilent-dmm, AC_HELP_STRING([--enable-agilent-dmm],
-	[enable Agilent DMM support [default=yes]]),
-	[HW_AGILENT_DMM="$enableval"],
-	[HW_AGILENT_DMM=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(appa-55ii, AC_HELP_STRING([--enable-appa-55ii],
-	[enable APPA 55II support [default=yes]]),
-	[HW_APPA_55II="$enableval"],
-	[HW_APPA_55II=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(asix-sigma, AC_HELP_STRING([--enable-asix-sigma],
-	[enable ASIX SIGMA/SIGMA2 support [default=yes]]),
-	[HW_ASIX_SIGMA="$enableval"],
-	[HW_ASIX_SIGMA=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(atten-pps3xxx, AC_HELP_STRING([--enable-atten-pps3xxx],
-	[enable Atten PPS3xxx support [default=yes]]),
-	[HW_ATTEN_PPS3XXX="$enableval"],
-	[HW_ATTEN_PPS3XXX=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(brymen-bm86x, AC_HELP_STRING([--enable-brymen-bm86x],
-	[enable Brymen BM86X support [default=yes]]),
-	[HW_BRYMEN_BM86X="$enableval"],
-	[HW_BRYMEN_BM86X=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(brymen-dmm, AC_HELP_STRING([--enable-brymen-dmm],
-	[enable Brymen DMM support [default=yes]]),
-	[HW_BRYMEN_DMM="$enableval"],
-	[HW_BRYMEN_DMM=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(cem-dt-885x, AC_HELP_STRING([--enable-cem-dt-885x],
-	[enable CEM DT-885x support [default=yes]]),
-	[HW_CEM_DT_885X="$enableval"],
-	[HW_CEM_DT_885X=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(center-3xx, AC_HELP_STRING([--enable-center-3xx],
-	[enable Center 3xx support [default=yes]]),
-	[HW_CENTER_3XX="$enableval"],
-	[HW_CENTER_3XX=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(chronovu-la, AC_HELP_STRING([--enable-chronovu-la],
-	[enable ChronoVu LA support [default=yes]]),
-	[HW_CHRONOVU_LA="$enableval"],
-	[HW_CHRONOVU_LA=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(colead-slm, AC_HELP_STRING([--enable-colead-slm],
-	[enable Colead SLM support [default=yes]]),
-	[HW_COLEAD_SLM="$enableval"],
-	[HW_COLEAD_SLM=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(conrad-digi-35-cpu, AC_HELP_STRING([--enable-conrad-digi-35-cpu],
-	[enable Conrad DIGI 35 CPU support [default=yes]]),
-	[HW_CONRAD_DIGI_35_CPU="$enableval"],
-	[HW_CONRAD_DIGI_35_CPU=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(demo, AC_HELP_STRING([--enable-demo],
-	[enable demo driver support [default=yes]]),
-	[HW_DEMO="$enableval"],
-	[HW_DEMO=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(fluke-dmm, AC_HELP_STRING([--enable-fluke-dmm],
-	[enable Fluke DMM support [default=yes]]),
-	[HW_FLUKE_DMM="$enableval"],
-	[HW_FLUKE_DMM=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(fx2lafw, AC_HELP_STRING([--enable-fx2lafw],
-	[enable fx2lafw support (for FX2 LAs). [default=yes]]),
-	[HW_FX2LAFW="$enableval"],
-	[HW_FX2LAFW=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(gmc-mh-1x-2x, AC_HELP_STRING([--enable-gmc-mh-1x-2x],
-	[enable gmc-mh-1x-2x support [default=yes]]),
-	[HW_GMC_MH_1X_2X="$enableval"],
-	[HW_GMC_MH_1X_2X=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(hameg-hmo, AC_HELP_STRING([--enable-hameg-hmo],
-	[enable Hameg HMO support [default=yes]]),
-	[HW_HAMEG_HMO="$enableval"],
-	[HW_HAMEG_HMO=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(hantek-dso, AC_HELP_STRING([--enable-hantek-dso],
-	[enable Hantek DSO support [default=yes]]),
-	[HW_HANTEK_DSO="$enableval"],
-	[HW_HANTEK_DSO=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(ikalogic-scanalogic2, AC_HELP_STRING([--enable-ikalogic-scanalogic2],
-	[enable IKALOGIC Scanalogic-2 support [default=yes]]),
-	[HW_IKALOGIC_SCANALOGIC2="$enableval"],
-	[HW_IKALOGIC_SCANALOGIC2=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(ikalogic-scanaplus, AC_HELP_STRING([--enable-ikalogic-scanaplus],
-	[enable IKALOGIC ScanaPLUS support [default=yes]]),
-	[HW_IKALOGIC_SCANAPLUS="$enableval"],
-	[HW_IKALOGIC_SCANAPLUS=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(kecheng-kc-330b, AC_HELP_STRING([--enable-kecheng-kc-330b],
-	[enable Kecheng KC-330B support [default=yes]]),
-	[HW_KECHENG_KC_330B="$enableval"],
-	[HW_KECHENG_KC_330B=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(lascar-el-usb, AC_HELP_STRING([--enable-lascar-el-usb],
-	[enable Lascar EL-USB support [default=yes]]),
-	[HW_LASCAR_EL_USB="$enableval"],
-	[HW_LASCAR_EL_USB=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(mic-985xx, AC_HELP_STRING([--enable-mic-985xx],
-	[enable MIC 985xx support [default=yes]]),
-	[HW_MIC_985XX="$enableval"],
-	[HW_MIC_985XX=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(norma-dmm, AC_HELP_STRING([--enable-norma-dmm],
-	[enable Norma DMM support [default=yes]]),
-	[HW_NORMA_DMM="$enableval"],
-	[HW_NORMA_DMM=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(ols, AC_HELP_STRING([--enable-ols],
-	[enable OpenBench Logic Sniffer (OLS) support [default=yes]]),
-	[HW_OLS="$enableval"],
-	[HW_OLS=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(rigol-ds, AC_HELP_STRING([--enable-rigol-ds],
-	[enable Rigol DS support [default=yes]]),
-	[HW_RIGOL_DS="$enableval"],
-	[HW_RIGOL_DS=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(saleae-logic16, AC_HELP_STRING([--enable-saleae-logic16],
-	[enable Saleae Logic16 support [default=yes]]),
-	[HW_SALEAE_LOGIC16="$enableval"],
-	[HW_SALEAE_LOGIC16=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(serial-dmm, AC_HELP_STRING([--enable-serial-dmm],
-	[enable serial DMM support [default=yes]]),
-	[HW_SERIAL_DMM="$enableval"],
-	[HW_SERIAL_DMM=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(sysclk-lwla, AC_HELP_STRING([--enable-sysclk-lwla],
-	[enable Sysclk LWLA support [default=yes]]),
-	[HW_SYSCLK_LWLA="$enableval"],
-	[HW_SYSCLK_LWLA=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(teleinfo, AC_HELP_STRING([--enable-teleinfo],
-	[enable Teleinfo support [default=yes]]),
-	[HW_TELEINFO="$enableval"],
-	[HW_TELEINFO=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(tondaj-sl-814, AC_HELP_STRING([--enable-tondaj-sl-814],
-	[enable Tondaj SL-814 support [default=yes]]),
-	[HW_TONDAJ_SL_814="$enableval"],
-	[HW_TONDAJ_SL_814=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(uni-t-dmm, AC_HELP_STRING([--enable-uni-t-dmm],
-	[enable UNI-T DMM support [default=yes]]),
-	[HW_UNI_T_DMM="$enableval"],
-	[HW_UNI_T_DMM=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(uni-t-ut32x, AC_HELP_STRING([--enable-uni-t-ut32x],
-	[enable UNI-T UT32x support [default=yes]]),
-	[HW_UNI_T_UT32X="$enableval"],
-	[HW_UNI_T_UT32X=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(victor-dmm, AC_HELP_STRING([--enable-victor-dmm],
-	[enable victor-dmm support [default=yes]]),
-	[HW_VICTOR_DMM="$enableval"],
-	[HW_VICTOR_DMM=$HW_ENABLED_DEFAULT])
-
-AC_ARG_ENABLE(zeroplus-logic-cube,
-	AC_HELP_STRING([--enable-zeroplus-logic-cube],
-	[enable ZEROPLUS Logic Cube support [default=yes]]),
-	[HW_ZEROPLUS_LOGIC_CUBE="$enableval"],
-	[HW_ZEROPLUS_LOGIC_CUBE=$HW_ENABLED_DEFAULT])
-
-# Checks for libraries.
-
-case "$host" in
-*mingw*)
-	# We need to link against the Winsock2 library for SCPI over TCP.
-	LIBS="$LIBS -lws2_32";;
-esac
-
-# This variable collects the pkg-config names of all detected libs.
-# It is then used to construct the "Requires.private:" field in the
-# libsigrok.pc file.
-SR_PKGLIBS=""
+# Format: current:revision:age.
+SR_LIB_VERSION_SET([SR_LIB_VERSION], [3:0:0])
 
-# libm (the standard math library) is always needed.
-AC_SEARCH_LIBS([pow], [m])
+AM_CONDITIONAL([WIN32], [test -z "${host_os##mingw*}" || test -z "${host_os##cygwin*}"])
 
-# RPC is only needed for VXI support.
-AC_MSG_CHECKING([for RPC support])
-AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <rpc/rpc.h>],
-				[CLIENT *rpc_test(void)],
-				[{ return clnt_create("", 0, 0, ""); }])],
-	       [AC_MSG_RESULT([yes]); have_rpc=1],
-	       [AC_MSG_RESULT([no]);  have_rpc=0])
-# Define HAVE_RPC in config.h if we found RPC support.
-AC_DEFINE_UNQUOTED(HAVE_RPC, [$have_rpc], [Specifies whether we have RPC support.])
-# VXI support is only compiled if RPC support was found.
-AM_CONDITIONAL(NEED_RPC, test "x$have_rpc" != "x0")
-
-# libglib-2.0 is always needed. Abort if it's not found.
-# Note: glib-2.0 is part of the libsigrok API (hard pkg-config requirement).
-# We require at least 2.32.0 due to e.g. g_variant_new_fixed_array().
-AM_PATH_GLIB_2_0([2.32.0],
-	[LIB_CFLAGS="$LIB_CFLAGS $GLIB_CFLAGS"; LIBS="$LIBS $GLIB_LIBS"])
-
-# libzip is always needed. Abort if it's not found.
-PKG_CHECK_MODULES([libzip], [libzip >= 0.10],
-	[LIB_CFLAGS="$LIB_CFLAGS $libzip_CFLAGS"; LIBS="$LIBS $libzip_LIBS";
-	SR_PKGLIBS="$SR_PKGLIBS libzip"])
-
-# libserialport is only needed for some hardware drivers. Disable the
-# respective drivers if it is not found.
-PKG_CHECK_MODULES([libserialport], [libserialport >= 0.1.0],
-	[have_libserialport="yes"; LIB_CFLAGS="$LIB_CFLAGS $libserialport_CFLAGS";
-	LIBS="$LIBS $libserialport_LIBS";
-	SR_PKGLIBS="$SR_PKGLIBS libserialport"],
-	[have_libserialport="no"; HW_AGILENT_DMM="no"; HW_APPA_55II="no";
-	HW_ATTEN_PPS3XXX="no"; HW_BRYMEN_DMM="no"; HW_CEM_DT_885X="no";
-	HW_CENTER_3XX="no"; HW_COLEAD_SLM="no"; HW_CONRAD_DIGI_35_CPU="no";
-	HW_FLUKE_DMM="no"; HW_GMC_MH_1X_2X="no"; HW_HAMEG_HMO="no";
-	HW_MIC_985XX="no"; HW_NORMA_DMM="no"; HW_OLS="no";
-	HW_SERIAL_DMM="no"; HW_TELEINFO="no"; HW_TONDAJ_SL_814="no"])
-
-# Define HAVE_LIBSERIALPORT in config.h if we found libserialport.
-if test "x$have_libserialport" != "xno"; then
-	AC_DEFINE_UNQUOTED(HAVE_LIBSERIALPORT, [1],
-		[Specifies whether we have libserialport.])
-fi
-
-# Serial port helper code is only compiled in if libserialport was found.
-AM_CONDITIONAL(NEED_SERIAL, test "x$have_libserialport" != xno)
-
-PKG_CHECK_MODULES([librevisa], [librevisa >= 0.0.20130812],
-	[have_librevisa="yes"; LIB_CFLAGS="$LIB_CFLAGS $librevisa_CFLAGS";
-	LIBS="$LIBS $librevisa_LIBS";
-	SR_PKGLIBS="$SR_PKGLIBS librevisa"],
-	[have_librevisa="no"])
-
-# VISA SCPI backend is only compiled in if librevisa was found.
-AM_CONDITIONAL(NEED_VISA, test "x$have_librevisa" != xno)
-
-# Define HAVE_LIBREVISA in config.h if we found librevisa.
-if test "x$have_librevisa" != "xno"; then
-	AC_DEFINE_UNQUOTED(HAVE_LIBREVISA, [1],
-		[Specifies whether we have librevisa.])
-fi
-
-# libusb-1.0 is only needed for some hardware drivers. Disable the respective
-# drivers if it is not found.
-case "$host" in
-*freebsd*)
-	# FreeBSD comes with an "integrated" libusb-1.0-style USB API.
-	# This means libusb-1.0 is always available, no need to check for it,
-	# and no need to (potentially) disable any drivers if it's not found.
-	AC_DEFINE_UNQUOTED(HAVE_LIBUSB_1_0, [1],
-		[Specifies whether we have a libusb.h header.])
-	;;
-*)
-	PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.16],
-		[have_libusb1_0="yes"; LIB_CFLAGS="$LIB_CFLAGS $libusb_CFLAGS";
-		LIBS="$LIBS $libusb_LIBS";
-		SR_PKGLIBS="$SR_PKGLIBS libusb-1.0"],
-		[have_libusb1_0="no"; HW_BRYMEN_BM86X="no"; HW_FX2LAFW="no";
-		HW_HANTEK_DSO="no"; HW_IKALOGIC_SCANALOGIC2="no";
-		HW_KECHENG_KC_330B="no"; HW_LASCAR_EL_USB="no";
-		HW_SYSCLK_LWLA="no"; HW_UNI_T_DMM="no";
-		HW_UNI_T_UT32X="no"; HW_VICTOR_DMM="no";
-		HW_ZEROPLUS_LOGIC_CUBE="no"; HW_SALEAE_LOGIC16="no"])
-
-	# Define HAVE_LIBUSB_1_0 in config.h if we found libusb-1.0.
-	if test "x$have_libusb1_0" != "xno"; then
-		AC_DEFINE_UNQUOTED(HAVE_LIBUSB_1_0, [1],
-			[Specifies whether we have a libusb.h header.])
-	fi
-	;;
-esac
-
-# USB + FX2 firmware helper code is only compiled in if libusb-1.0 was found.
-AM_CONDITIONAL(NEED_USB, test "x$have_libusb1_0" != xno)
-
-# libftdi is only needed for some hardware drivers. Disable them if not found.
-PKG_CHECK_MODULES([libftdi], [libftdi >= 0.16],
-	[LIB_CFLAGS="$LIB_CFLAGS $libftdi_CFLAGS";
-	LIBS="$LIBS $libftdi_LIBS";
-	SR_PKGLIBS="$SR_PKGLIBS libftdi"],
-	[HW_ASIX_SIGMA="no"; HW_CHRONOVU_LA="no"; HW_IKALOGIC_SCANAPLUS="no"])
+#############################
+##  Optional dependencies  ##
+#############################
+
+AC_LANG([C])
+
+# Initialize pkg-config.
+# We require at least 0.22, as "Requires.private" behaviour changed there.
+PKG_PROG_PKG_CONFIG([0.22])
+
+# Keep track of all checked modules so we can list them at the end.
+SR_PKG_CHECK_SUMMARY([sr_pkglibs_summary])
+
+# Collect the pkg-config module names of all dependencies in SR_PKGLIBS.
+# These are used to derive the compiler flags and for the "Requires.private"
+# field in the generated libsigrok.pc file.
+SR_VAR_OPT_PKG([SR_PKGLIBS], [sr_deps_avail])
+SR_PKGLIBS_TESTS=
+SR_PKGLIBS_CXX=
+SR_PKGLIBS_PYTHON=
+SR_PKGLIBS_RUBY=
+SR_EXTRA_LIBS=
+
+SR_ARG_OPT_PKG([libserialport], [LIBSERIALPORT], [NEED_SERIAL],
+	[libserialport >= 0.1.1])
+
+SR_ARG_OPT_PKG([libftdi], [LIBFTDI],,
+	[libftdi1 >= 1.0], [libftdi >= 0.16])
+
+# FreeBSD comes with an "integrated" libusb-1.0-style USB API.
+# This means libusb-1.0 is always available; no need to check for it.
+# On Windows, require the latest version we can get our hands on,
+# until the new generic event handling has seen an official release.
+AS_CASE([$host_os],
+	[freebsd*], [sr_libusb_req='libusb-1.0' sr_have_libusb=yes],
+	[mingw*], [sr_libusb_req='libusb-1.0 >= 1.0.20'],
+	[sr_libusb_req='libusb-1.0 >= 1.0.16'])
+
+SR_ARG_OPT_PKG([libusb], [LIBUSB_1_0], [NEED_USB],
+	[$sr_libusb_req])
+
+SR_ARG_OPT_PKG([librevisa], [LIBREVISA], [NEED_VISA],
+	[librevisa >= 0.0.20130412])
+
+SR_ARG_OPT_PKG([libgpib], [LIBGPIB], [NEED_GPIB],
+	[libgpib])
+
+SR_ARG_OPT_CHECK([libieee1284], [LIBIEEE1284],, [
+	sr_save_LIBS=$LIBS
+	LIBS="-lieee1284 $LIBS"
+	AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <ieee1284.h>]],
+				[[(void) ieee1284_open(0, 0, 0);]])],
+		[sr_have_libieee1284=yes], [sr_have_libieee1284=no])
+	LIBS=$sr_save_LIBS
+])
+AS_IF([test "x$sr_have_libieee1284" = xyes],
+	[SR_PREPEND([SR_EXTRA_LIBS], [-lieee1284])])
+
+######################
+##  Feature checks  ##
+######################
 
 # The Check unit testing framework is optional. Disable if not found.
-PKG_CHECK_MODULES([check], [check >= 0.9.4],
-	[have_check="yes"; LIB_CFLAGS="$LIB_CFLAGS $check_CFLAGS";
-	LIBS="$LIBS $check_LIBS"], [have_check="no"])
-AM_CONDITIONAL(HAVE_CHECK, test x"$have_check" = "xyes")
-
-# The OLS driver uses serial port file descriptors directly, and therefore
-# will not currently work on Windows.
-case "$host" in
-*mingw*)
-       HW_OLS="no"
-       ;;
-esac
-
-AC_SUBST(SR_PKGLIBS)
-
-CFLAGS="$CFLAGS $LIB_CFLAGS"
-
-# Now set AM_CONDITIONALs and AC_DEFINEs for the enabled/disabled drivers.
-
-AM_CONDITIONAL(HW_AGILENT_DMM, test x$HW_AGILENT_DMM = xyes)
-if test "x$HW_AGILENT_DMM" = "xyes"; then
-	AC_DEFINE(HAVE_HW_AGILENT_DMM, 1, [Agilent DMM support])
-fi
-
-AM_CONDITIONAL(HW_APPA_55II, test x$HW_APPA_55II = xyes)
-if test "x$HW_APPA_55II" = "xyes"; then
-	AC_DEFINE(HAVE_HW_APPA_55II, 1, [APPA 55II support])
-fi
-
-AM_CONDITIONAL(HW_ASIX_SIGMA, test x$HW_ASIX_SIGMA = xyes)
-if test "x$HW_ASIX_SIGMA" = "xyes"; then
-	AC_DEFINE(HAVE_HW_ASIX_SIGMA, 1, [ASIX SIGMA/SIGMA2 support])
-fi
-
-AM_CONDITIONAL(HW_ATTEN_PPS3XXX, test x$HW_ATTEN_PPS3XXX = xyes)
-if test "x$HW_ATTEN_PPS3XXX" = "xyes"; then
-       AC_DEFINE(HAVE_HW_ATTEN_PPS3XXX, 1, [Atten PPS3xxx support])
-fi
-
-AM_CONDITIONAL(HW_BRYMEN_BM86X, test x$HW_BRYMEN_BM86X = xyes)
-if test "x$HW_BRYMEN_BM86X" = "xyes"; then
-	AC_DEFINE(HAVE_HW_BRYMEN_BM86X, 1, [Brymen BM86X support])
-fi
-
-AM_CONDITIONAL(HW_BRYMEN_DMM, test x$HW_BRYMEN_DMM = xyes)
-if test "x$HW_BRYMEN_DMM" = "xyes"; then
-	AC_DEFINE(HAVE_HW_BRYMEN_DMM, 1, [Brymen DMM support])
-fi
-
-AM_CONDITIONAL(HW_CEM_DT_885X, test x$HW_CEM_DT_885X = xyes)
-if test "x$HW_CEM_DT_885X" = "xyes"; then
-	AC_DEFINE(HAVE_HW_CEM_DT_885X, 1, [CEM DT-885x support])
-fi
-
-AM_CONDITIONAL(HW_CENTER_3XX, test x$HW_CENTER_3XX = xyes)
-if test "x$HW_CENTER_3XX" = "xyes"; then
-	AC_DEFINE(HAVE_HW_CENTER_3XX, 1, [Center 3xx support])
-fi
-
-AM_CONDITIONAL(HW_CHRONOVU_LA, test x$HW_CHRONOVU_LA = xyes)
-if test "x$HW_CHRONOVU_LA" = "xyes"; then
-	AC_DEFINE(HAVE_HW_CHRONOVU_LA, 1, [ChronoVu LA support])
-fi
-
-AM_CONDITIONAL(HW_COLEAD_SLM, test x$HW_COLEAD_SLM = xyes)
-if test "x$HW_COLEAD_SLM" = "xyes"; then
-	AC_DEFINE(HAVE_HW_COLEAD_SLM, 1, [Colead SLM support])
-fi
-
-AM_CONDITIONAL(HW_CONRAD_DIGI_35_CPU, test x$HW_CONRAD_DIGI_35_CPU = xyes)
-if test "x$HW_CONRAD_DIGI_35_CPU" = "xyes"; then
-	AC_DEFINE(HAVE_HW_CONRAD_DIGI_35_CPU, 1, [Conrad DIGI 35 CPU support])
-fi
-
-AM_CONDITIONAL(HW_DEMO, test x$HW_DEMO = xyes)
-if test "x$HW_DEMO" = "xyes"; then
-	AC_DEFINE(HAVE_HW_DEMO, 1, [Demo driver support])
-fi
-
-AM_CONDITIONAL(HW_FLUKE_DMM, test x$HW_FLUKE_DMM = xyes)
-if test "x$HW_FLUKE_DMM" = "xyes"; then
-	AC_DEFINE(HAVE_HW_FLUKE_DMM, 1, [Fluke DMM support])
-fi
-
-AM_CONDITIONAL(HW_FX2LAFW, test x$HW_FX2LAFW = xyes)
-if test "x$HW_FX2LAFW" = "xyes"; then
-	AC_DEFINE(HAVE_HW_FX2LAFW, 1, [fx2lafw support])
-fi
-
-AM_CONDITIONAL(HW_GMC_MH_1X_2X, test x$HW_GMC_MH_1X_2X = xyes)
-if test "x$HW_GMC_MH_1X_2X" = "xyes"; then
-	AC_DEFINE(HAVE_HW_GMC_MH_1X_2X, 1, [gmc-mh-1x-2x support])
-fi
-
-AM_CONDITIONAL(HW_HANTEK_DSO, test x$HW_HANTEK_DSO = xyes)
-if test "x$HW_HANTEK_DSO" = "xyes"; then
-	AC_DEFINE(HAVE_HW_HANTEK_DSO, 1, [Hantek DSO support])
-fi
-
-AM_CONDITIONAL(HW_HAMEG_HMO, test x$HW_HAMEG_HMO = xyes)
-if test "x$HW_HAMEG_HMO" = "xyes"; then
-	AC_DEFINE(HAVE_HW_HAMEG_HMO, 1, [Hameg HMO support])
-fi
-
-AM_CONDITIONAL(HW_IKALOGIC_SCANALOGIC2, test x$HW_IKALOGIC_SCANALOGIC2 = xyes)
-if test "x$HW_IKALOGIC_SCANALOGIC2" = "xyes"; then
-	AC_DEFINE(HAVE_HW_IKALOGIC_SCANALOGIC2, 1, [IKALOGIC Scanalogic-2 support])
-fi
-
-AM_CONDITIONAL(HW_IKALOGIC_SCANAPLUS, test x$HW_IKALOGIC_SCANAPLUS = xyes)
-if test "x$HW_IKALOGIC_SCANAPLUS" = "xyes"; then
-	AC_DEFINE(HAVE_HW_IKALOGIC_SCANAPLUS, 1, [IKALOGIC ScanaPLUS support])
-fi
-
-AM_CONDITIONAL(HW_KECHENG_KC_330B, test x$HW_KECHENG_KC_330B = xyes)
-if test "x$HW_KECHENG_KC_330B" = "xyes"; then
-	AC_DEFINE(HAVE_HW_KECHENG_KC_330B, 1, [Kecheng KC-330B support])
-fi
-
-AM_CONDITIONAL(HW_LASCAR_EL_USB, test x$HW_LASCAR_EL_USB = xyes)
-if test "x$HW_LASCAR_EL_USB" = "xyes"; then
-	AC_DEFINE(HAVE_HW_LASCAR_EL_USB, 1, [Lascar EL-USB support])
-fi
-
-AM_CONDITIONAL(HW_MIC_985XX, test x$HW_MIC_985XX = xyes)
-if test "x$HW_MIC_985XX" = "xyes"; then
-	AC_DEFINE(HAVE_HW_MIC_985XX, 1, [MIC 985xx support])
-fi
-
-AM_CONDITIONAL(HW_NORMA_DMM, test x$HW_NORMA_DMM = xyes)
-if test "x$HW_NORMA_DMM" = "xyes"; then
-       AC_DEFINE(HAVE_HW_NORMA_DMM, 1, [Norma DMM support])
-fi
-
-AM_CONDITIONAL(HW_OLS, test x$HW_OLS = xyes)
-if test "x$HW_OLS" = "xyes"; then
-	AC_DEFINE(HAVE_HW_OLS, 1, [OpenBench Logic Sniffer (OLS) support])
-fi
-
-AM_CONDITIONAL(HW_RIGOL_DS, test x$HW_RIGOL_DS = xyes)
-if test "x$HW_RIGOL_DS" = "xyes"; then
-	AC_DEFINE(HAVE_HW_RIGOL_DS, 1, [Rigol DS support])
-fi
-
-AM_CONDITIONAL(HW_SALEAE_LOGIC16, test x$HW_SALEAE_LOGIC16 = xyes)
-if test "x$HW_SALEAE_LOGIC16" = "xyes"; then
-	AC_DEFINE(HAVE_HW_SALEAE_LOGIC16, 1, [Saleae Logic16 support])
-fi
-
-AM_CONDITIONAL(HW_SERIAL_DMM, test x$HW_SERIAL_DMM = xyes)
-if test "x$HW_SERIAL_DMM" = "xyes"; then
-	AC_DEFINE(HAVE_HW_SERIAL_DMM, 1, [Serial DMM support])
-fi
-
-AM_CONDITIONAL(HW_SYSCLK_LWLA, test x$HW_SYSCLK_LWLA = xyes)
-if test "x$HW_SYSCLK_LWLA" = "xyes"; then
-	AC_DEFINE(HAVE_HW_SYSCLK_LWLA, 1, [Sysclk LWLA support])
-fi
-
-AM_CONDITIONAL(HW_TELEINFO, test x$HW_TELEINFO = xyes)
-if test "x$HW_TELEINFO" = "xyes"; then
-	AC_DEFINE(HAVE_HW_TELEINFO, 1, [Teleinfo support])
-fi
-
-AM_CONDITIONAL(HW_TONDAJ_SL_814, test x$HW_TONDAJ_SL_814 = xyes)
-if test "x$HW_TONDAJ_SL_814" = "xyes"; then
-	AC_DEFINE(HAVE_HW_TONDAJ_SL_814, 1, [Tondaj SL-814 support])
-fi
-
-AM_CONDITIONAL(HW_UNI_T_DMM, test x$HW_UNI_T_DMM = xyes)
-if test "x$HW_UNI_T_DMM" = "xyes"; then
-	AC_DEFINE(HAVE_HW_UNI_T_DMM, 1, [UNI-T DMM support])
-fi
-
-AM_CONDITIONAL(HW_UNI_T_UT32X, test x$HW_UNI_T_UT32X = xyes)
-if test "x$HW_UNI_T_UT32X" = "xyes"; then
-	AC_DEFINE(HAVE_HW_UNI_T_UT32X, 1, [UNI-T UT32x support])
-fi
-
-AM_CONDITIONAL(HW_VICTOR_DMM, test x$HW_VICTOR_DMM = xyes)
-if test "x$HW_VICTOR_DMM" = "xyes"; then
-	AC_DEFINE(HAVE_HW_VICTOR_DMM, 1, [Victor DMM support])
-fi
-
-AM_CONDITIONAL(HW_ZEROPLUS_LOGIC_CUBE, test x$HW_ZEROPLUS_LOGIC_CUBE = xyes)
-if test "x$HW_ZEROPLUS_LOGIC_CUBE" = "xyes"; then
-	AC_DEFINE(HAVE_HW_ZEROPLUS_LOGIC_CUBE, 1, [ZEROPLUS Logic Cube support])
-fi
-
-# Checks for header files.
-# These are already checked: inttypes.h stdint.h stdlib.h string.h unistd.h.
-
-# Checks for typedefs, structures, and compiler characteristics.
-AC_C_BIGENDIAN
+SR_PKG_CHECK([check], [SR_PKGLIBS_TESTS], [check >= 0.9.4])
+AM_CONDITIONAL([HAVE_CHECK], [test "x$sr_have_check" = xyes])
+
+# Enable the C99 standard if possible, and enforce the use
+# of SR_API to explicitly mark all public API functions.
+SR_EXTRA_CFLAGS=
+SR_CHECK_COMPILE_FLAGS([SR_EXTRA_CFLAGS], [C99], [-std=c99 -c99 -AC99 -qlanglvl=extc99])
+SR_CHECK_COMPILE_FLAGS([SR_EXTRA_CFLAGS], [visibility], [-fvisibility=hidden])
 
-AC_SUBST(FIRMWARE_DIR, "$datadir/sigrok-firmware")
-AC_SUBST(MAKEFLAGS, '--no-print-directory')
-AC_SUBST(AM_LIBTOOLFLAGS, '--silent')
+SR_ARG_ENABLE_WARNINGS([SR_WFLAGS], [-Wall], [-Wall -Wextra -Wmissing-prototypes])
 
-SR_PACKAGE_VERSION_MAJOR=sr_package_version_major
-SR_PACKAGE_VERSION_MINOR=sr_package_version_minor
-SR_PACKAGE_VERSION_MICRO=sr_package_version_micro
-SR_PACKAGE_VERSION=sr_package_version
+# Check host characteristics.
+AC_SYS_LARGEFILE
+AC_C_BIGENDIAN
 
-AC_SUBST(SR_PACKAGE_VERSION_MAJOR)
-AC_SUBST(SR_PACKAGE_VERSION_MINOR)
-AC_SUBST(SR_PACKAGE_VERSION_MICRO)
-AC_SUBST(SR_PACKAGE_VERSION)
+AC_CHECK_HEADERS([sys/mman.h], [SR_APPEND([sr_deps_avail], [sys_mman_h])])
+AC_CHECK_HEADERS([sys/ioctl.h], [SR_APPEND([sr_deps_avail], [sys_ioctl_h])])
+AC_CHECK_HEADERS([sys/timerfd.h], [SR_APPEND([sr_deps_avail], [sys_timerfd_h])])
 
-AC_CONFIG_FILES([Makefile version.h libsigrok.pc])
+# We need to link against the Winsock2 library for SCPI over TCP.
+AS_CASE([$host_os], [mingw*], [SR_PREPEND([SR_EXTRA_LIBS], [-lws2_32])])
 
-AC_OUTPUT
+# libm (the standard math library) is always needed.
+SR_SEARCH_LIBS([SR_EXTRA_LIBS], [pow], [m])
 
-echo
-echo "libsigrok configuration summary:"
-echo
-echo "  - Package version (major.minor.micro):    $SR_PACKAGE_VERSION"
-echo "  - Library version (current:revision:age): $SR_LIB_VERSION"
-echo "  - Prefix: $prefix"
-echo "  - Building on: $build"
-echo "  - Building for: $host"
-echo
-echo "Detected libraries:"
-echo
-
-# Note: This only works for libs with pkg-config integration.
-for lib in "glib-2.0 >= 2.32.0" "libzip >= 0.10" "libserialport >= 0.1.0" "librevisa >= 0.0.20130812" "libusb-1.0 >= 1.0.16" "libftdi >= 0.16" "check >= 0.9.4"; do
-	optional="OPTIONAL"
-	if test "x$lib" = "xglib-2.0 >= 2.32.0"; then optional="REQUIRED"; fi
-	if test "x$lib" = "xlibzip >= 0.10"; then optional="REQUIRED"; fi
-	if `$PKG_CONFIG --exists $lib`; then
-		ver=`$PKG_CONFIG --modversion $lib`
-		answer="yes ($ver)"
-	else
-		answer="no"
-	fi
-	echo "  - ($optional) $lib: $answer"
+# RPC is only needed for VXI support.
+AC_CACHE_CHECK([for RPC support], [sr_cv_have_rpc],
+	[AC_LINK_IFELSE([AC_LANG_PROGRAM(
+			[[#include <rpc/rpc.h>]m4_newline[CLIENT *rpc_test(void);]],
+			[[(void) clnt_create("", 0, 0, "");]])],
+		[sr_cv_have_rpc=yes], [sr_cv_have_rpc=no])])
+AS_IF([test "x$sr_cv_have_rpc" = xyes],
+	[AC_DEFINE([HAVE_RPC], [1], [Specifies whether we have RPC support.])])
+# VXI support is only compiled if RPC support was found.
+AM_CONDITIONAL([NEED_RPC], [test "x$sr_cv_have_rpc" = xyes])
+
+########################
+##  Hardware drivers  ##
+########################
+
+# Keep track of all drivers so we can list them at the end.
+SR_VAR_SUMMARY([sr_driver_summary])
+
+# Check whether the sr_deps_avail list contains all of the arguments.
+# Unavailable dependencies are collected in sr_deps_missing.
+sr_check_driver_deps() {
+	sr_deps_missing=
+	for sr_dep
+	do
+		AS_CASE([" $sr_deps_avail "], [*" $sr_dep "*],,
+			[SR_APPEND([sr_deps_missing], [', '], [$sr_dep])])
+	done
+	test -z "$sr_deps_missing" || return 1
+}
+
+AC_ARG_ENABLE([all-drivers],
+	[AS_HELP_STRING([--enable-all-drivers],
+			[enable all drivers by default [default=yes]])],
+	[], [enable_all_drivers=yes])
+
+## _SR_DRIVER(Device name, driver-name, var-name, [dependencies...])
+m4_define([_SR_DRIVER], [
+	AC_ARG_ENABLE([$2],
+		[AS_HELP_STRING([--enable-$2], [enable $1 support])],
+		[$3=$enableval], [$3=$enable_all_drivers])
+
+	AS_IF([test "x[$]$3" = xyes], [sr_hw_info=yes[]m4_ifval([$4], [
+		sr_check_driver_deps $4 \
+			|| $3=no sr_hw_info="no (missing: $sr_deps_missing)"
+	])], [sr_hw_info='no (disabled)'])
+	sr_driver_summary_append "$2" "$sr_hw_info"
+
+	AM_CONDITIONAL([$3], [test "x[$]$3" = xyes])
+	AM_COND_IF([$3], [AC_DEFINE([HAVE_$3], [1], [Whether to support $1 device.])])
+])
+
+## SR_DRIVER(Device name, driver-name, [dependencies...])
+m4_define([SR_DRIVER],
+	[_SR_DRIVER([$1], [$2], m4_expand([AS_TR_CPP([HW_$2])]), [$3])])
+
+SR_DRIVER([Agilent DMM], [agilent-dmm], [libserialport])
+SR_DRIVER([Appa 55II], [appa-55ii], [libserialport])
+SR_DRIVER([ASIX SIGMA/SIGMA2], [asix-sigma], [libftdi])
+SR_DRIVER([Atten PPS3xxx], [atten-pps3xxx], [libserialport])
+SR_DRIVER([BayLibre ACME], [baylibre-acme], [sys_timerfd_h])
+SR_DRIVER([BeagleLogic], [beaglelogic], [sys_mman_h sys_ioctl_h])
+SR_DRIVER([Brymen BM86x], [brymen-bm86x], [libusb])
+SR_DRIVER([Brymen DMM], [brymen-dmm], [libserialport])
+SR_DRIVER([CEM DT-885x], [cem-dt-885x], [libserialport])
+SR_DRIVER([Center 3xx], [center-3xx], [libserialport])
+SR_DRIVER([ChronoVu LA], [chronovu-la], [libusb libftdi])
+SR_DRIVER([Colead SLM], [colead-slm], [libserialport])
+SR_DRIVER([Conrad DIGI 35 CPU], [conrad-digi-35-cpu], [libserialport])
+SR_DRIVER([DER EE DE-5000], [deree-de5000], [libserialport])
+SR_DRIVER([demo], [demo])
+SR_DRIVER([Fluke DMM], [fluke-dmm], [libserialport])
+SR_DRIVER([fx2lafw], [fx2lafw], [libusb])
+SR_DRIVER([GMC MH 1x/2x], [gmc-mh-1x-2x], [libserialport])
+SR_DRIVER([GW Instek GDS-800], [gwinstek-gds-800], [libserialport])
+SR_DRIVER([Hameg HMO], [hameg-hmo], [libserialport])
+SR_DRIVER([Hantek DSO], [hantek-dso], [libusb])
+SR_DRIVER([Hung-Chang DSO-2100], [hung-chang-dso-2100], [libieee1284])
+SR_DRIVER([Ikalogic Scanalogic-2], [ikalogic-scanalogic2], [libusb])
+SR_DRIVER([Ikalogic Scanaplus], [ikalogic-scanaplus], [libftdi])
+SR_DRIVER([Kecheng KC-330B], [kecheng-kc-330b], [libusb])
+SR_DRIVER([KERN scale], [kern-scale], [libserialport])
+SR_DRIVER([Korad KAxxxxP], [korad-kaxxxxp], [libserialport])
+SR_DRIVER([Lascar EL-USB], [lascar-el-usb], [libusb])
+SR_DRIVER([LeCroy LogicStudio], [lecroy-logicstudio], [libusb])
+SR_DRIVER([Manson HCS-3xxx], [manson-hcs-3xxx], [libserialport])
+SR_DRIVER([maynuo-m97], [maynuo-m97])
+SR_DRIVER([MIC 985xx], [mic-985xx], [libserialport])
+SR_DRIVER([Motech LPS 30x], [motech-lps-30x], [libserialport])
+SR_DRIVER([Norma DMM], [norma-dmm], [libserialport])
+SR_DRIVER([OpenBench Logic Sniffer], [openbench-logic-sniffer], [libserialport])
+SR_DRIVER([Pipistrello-OLS], [pipistrello-ols], [libftdi])
+SR_DRIVER([Rigol DS], [rigol-ds])
+SR_DRIVER([Saleae Logic16], [saleae-logic16], [libusb])
+SR_DRIVER([SCPI PPS], [scpi-pps])
+SR_DRIVER([serial DMM], [serial-dmm], [libserialport])
+SR_DRIVER([Sysclk LWLA], [sysclk-lwla], [libusb])
+SR_DRIVER([Teleinfo], [teleinfo], [libserialport])
+SR_DRIVER([Testo], [testo], [libusb])
+SR_DRIVER([Tondaj SL-814], [tondaj-sl-814], [libserialport])
+SR_DRIVER([UNI-T DMM], [uni-t-dmm], [libusb])
+SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [libusb])
+SR_DRIVER([Victor DMM], [victor-dmm], [libusb])
+SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm])
+SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb])
+
+###############################
+##  Language bindings setup  ##
+###############################
+
+AC_LANG([C++])
+SR_ARG_ENABLE_WARNINGS([SR_WXXFLAGS], [-Wall], [-Wall -Wextra])
+
+AC_ARG_ENABLE([bindings],
+	[AS_HELP_STRING([--enable-bindings], [build language bindings [default=yes]])],
+	[], [enable_bindings=yes])
+
+AC_ARG_ENABLE([cxx],
+	[AS_HELP_STRING([--enable-cxx], [build C++ bindings [default=yes]])],
+	[], [enable_cxx=$enable_bindings])
+
+AC_ARG_ENABLE([python],
+	[AS_HELP_STRING([--enable-python], [build Python bindings [default=yes]])],
+	[], [enable_python=$enable_bindings])
+
+AC_ARG_ENABLE([ruby],
+	[AS_HELP_STRING([--enable-ruby], [build Ruby bindings [default=yes]])],
+	[], [enable_ruby=$enable_bindings])
+
+AC_ARG_ENABLE([java],
+	[AS_HELP_STRING([--enable-java], [build Java bindings [default=yes]])],
+	[], [enable_java=$enable_bindings])
+
+####################
+##  C++ bindings  ##
+####################
+
+sr_cxx_missing=
+
+# Check if the C++ compiler supports the C++11 standard.
+AX_CXX_COMPILE_STDCXX_11([noext], [optional])
+AS_IF([test "x$HAVE_CXX11" != x1],
+	[SR_APPEND([sr_cxx_missing], [', '], ['C++11'])])
+
+# The C++ bindings need glibmm.
+SR_PKG_CHECK([glibmm], [SR_PKGLIBS_CXX], [glibmm-2.4 >= 2.32.0])
+AS_IF([test "x$sr_have_glibmm" != xyes],
+	[SR_APPEND([sr_cxx_missing], [', '], [glibmm])])
+
+# The C++ bindings use Doxygen to parse libsigrok symbols.
+AC_CHECK_PROG([HAVE_DOXYGEN], [doxygen], [yes], [no])
+AS_IF([test "x$HAVE_DOXYGEN" != xyes],
+	[SR_APPEND([sr_cxx_missing], [', '], [Doxygen])])
+
+# Python is needed for the C++ bindings.
+AM_PATH_PYTHON([2.7],
+	[HAVE_PYTHON=yes],
+	[HAVE_PYTHON=no
+	SR_APPEND([sr_cxx_missing], [', '], [Python])])
+
+AS_IF([test -z "$sr_cxx_missing"],
+	[BINDINGS_CXX=$enable_cxx], [BINDINGS_CXX=no])
+AM_CONDITIONAL([BINDINGS_CXX], [test "x$BINDINGS_CXX" = xyes])
+
+# C++ bindings want stoi and stod.
+AM_COND_IF([BINDINGS_CXX], [
+	AC_CACHE_CHECK([for stoi and stod], [sr_cv_have_stoi_stod],
+		[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <string>]],
+				[[(void) std::stoi("1"); (void) std::stod("1.0");]])],
+			[sr_cv_have_stoi_stod=yes], [sr_cv_have_stoi_stod=no])])
+	AS_IF([test "x$sr_cv_have_stoi_stod" = xyes],
+		[AC_DEFINE([HAVE_STOI_STOD], [1],
+			[Specifies whether we have the stoi and stod functions.])])
+])
+
+#######################
+##  Python bindings  ##
+#######################
+
+AS_IF([test "x$BINDINGS_CXX" = xyes],
+	[sr_python_missing=],
+	[sr_python_missing='C++ bindings'])
+
+# Extract major and minor version number of the Python interpreter.
+sr_pymajor=${PYTHON_VERSION%%.*}
+sr_pyminor=${PYTHON_VERSION#*.}
+
+# The Python bindings need Python development files. Check for either
+# Python 3 or Python 2 headers depending on the interpreter version.
+SR_PKG_CHECK([python_dev], [SR_PKGLIBS_PYTHON],
+	[python = $PYTHON_VERSION],
+	[python$sr_pymajor = $PYTHON_VERSION],
+	[python$sr_pymajor$sr_pyminor = $PYTHON_VERSION],
+	[python-$PYTHON_VERSION = $PYTHON_VERSION])
+
+AS_IF([test "x$sr_have_python_dev" != xyes],
+	[SR_APPEND([sr_python_missing], [', '], [Headers])])
+
+# PyGObject is needed for the Python bindings.
+SR_PKG_CHECK([pygobject], [SR_PKGLIBS_PYTHON], [pygobject-3.0 >= 3.0.0])
+AS_IF([test "x$sr_have_pygobject" != xyes],
+	[SR_APPEND([sr_python_missing], [', '], [PyGObject])])
+
+PKG_CHECK_EXISTS([pygobject-3.0 < 3.7.91],
+	[AC_DEFINE([PYGOBJECT_FLAGS_SIGNED], [1],
+		[Whether last argument to pyg_flags_get_value() is signed.])])
+
+# The Python bindings need the setuptools and numpy Python modules.
+# We'll let it go through even if the AX macro wasn't found, as the
+# Python modules may still be there.
+m4_ifdef([AX_PYTHON_MODULE], [
+	AX_PYTHON_MODULE([setuptools])
+	AX_PYTHON_MODULE([numpy])
+], [
+	HAVE_PYMOD_SETUPTOOLS=yes
+	HAVE_PYMOD_NUMPY=yes
+	m4_warn([unsupported],
+		[Missing macro AX_PYTHON_MODULE: no check for setuptools and numpy])
+])
+AS_IF([test "x$HAVE_PYMOD_SETUPTOOLS" != xyes],
+	[SR_APPEND([sr_python_missing], [', '], [setuptools])])
+AS_IF([test "x$HAVE_PYMOD_NUMPY" != xyes],
+	[SR_APPEND([sr_python_missing], [', '], [numpy])])
+
+# The Python bindings use SWIG to generate code.
+AC_CHECK_PROGS([SWIG], [swig swig3.0 swig2.0])
+AS_IF([test "x$SWIG" != x],
+    AC_MSG_CHECKING([for $SWIG version])
+    [SWIG_VERSION=`$SWIG -version 2>&1 | sed -n 's/SWIG Version\s*//p'`]
+    AC_MSG_RESULT([$SWIG_VERSION]))
+AS_IF([test "x$SWIG" = x],
+	[SR_APPEND([sr_python_missing], [', '], [SWIG])])
+
+AS_IF([test -z "$sr_python_missing"],
+	[BINDINGS_PYTHON=$enable_python], [BINDINGS_PYTHON=no])
+AM_CONDITIONAL([BINDINGS_PYTHON], [test "x$BINDINGS_PYTHON" = xyes])
+
+#####################
+##  Ruby bindings  ##
+#####################
+
+AS_IF([test "x$BINDINGS_CXX" = xyes],
+	[sr_ruby_missing=],
+	[sr_ruby_missing='C++ bindings'])
+
+AC_PATH_PROGS(RUBY, ["${RUBY-ruby}"], [])
+AS_IF([test "x$RUBY" != x],
+	AC_MSG_CHECKING([for Ruby version])
+    [RUBY_VERSION=`$RUBY -e 'puts RUBY_VERSION'`]
+    AC_MSG_RESULT([$RUBY_VERSION])
+    [RUBY_DLEXT=`$RUBY -rrbconfig -e 'puts RbConfig::CONFIG[["DLEXT"]]'`]
+    AC_SUBST(RUBY_DLEXT))
+
+AS_IF([test "x$RUBY" = x],
+	[SR_APPEND([sr_ruby_missing], [', '], [Ruby])])
+
+# Extract major and minor version number of the Ruby interpreter.
+sr_rbmajor=${RUBY_VERSION%%.*}
+sr_rbminor=${RUBY_VERSION#*.}
+sr_rbminor=${sr_rbminor%%.*}
+
+# The Ruby bindings need Ruby development files.
+SR_PKG_CHECK([ruby_dev], [SR_PKGLIBS_RUBY],
+	[ruby],
+	[ruby-$sr_rbmajor.$sr_rbminor])
+
+AS_IF([test "x$sr_have_ruby_dev" != xyes],
+	[SR_APPEND([sr_ruby_missing], [', '], [Headers])])
+
+# The Ruby bindings use SWIG >= 3.0.8 to generate code.
+AS_IF([test "x$SWIG" = x],
+	[SR_APPEND([sr_ruby_missing], [', '], [SWIG])],
+	[AS_VERSION_COMPARE($SWIG_VERSION, "3.0.8",
+		[SR_APPEND([sr_ruby_missing], [', '], ['SWIG >= 3.0.8'])])])
+
+AS_IF([test -z "$sr_ruby_missing"],
+	[BINDINGS_RUBY=$enable_ruby], [BINDINGS_RUBY=no])
+AM_CONDITIONAL([BINDINGS_RUBY], [test "x$BINDINGS_RUBY" = xyes])
+
+####################
+##  Java bindings ##
+####################
+
+AS_IF([test "x$BINDINGS_CXX" = xyes],
+	[sr_java_missing=],
+	[sr_java_missing='C++ bindings'])
+
+# The Java bindings use SWIG to generate code.
+AS_IF([test "x$SWIG" = x],
+	[SR_APPEND([sr_java_missing], [', '], [SWIG])])
+
+# Find Java compiler and JNI includes for Java bindings.
+AC_CHECK_PROG([HAVE_JAVAC], [javac], [yes], [no])
+AS_IF([test "x$HAVE_JAVAC" = xno],
+	[SR_APPEND([sr_java_missing], [', '], [JavaC])])
+
+AC_ARG_WITH([jni-include-path],
+	[AS_HELP_STRING([[--with-jni-include-path=DIR-LIST (space-separated)]],
+		[specify JNI include directories [default=detect]])],
+	[JNI_INCLUDE_DIRS=" $withval"], [JNI_INCLUDE_DIRS=])
+
+JNI_CPPFLAGS=
+AS_IF([test "x$enable_java$HAVE_JAVAC" = xyesyes], [
+	AX_PROG_JAVAC
+	AS_IF([test -z "$JNI_INCLUDE_DIRS" && test "x$cross_compiling" != xyes], [
+		## Work around the totally broken logic in AX_JNI_INCLUDE_DIR:
+		## If we can find jni.h without any special search path, skip
+		## the execution of the broken macro to increase our chances of
+		## success.
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <jni.h>]], [])],,
+			[AX_JNI_INCLUDE_DIR])
+	])
+])
+for sr_dir in $JNI_INCLUDE_DIRS
+do
+	SR_APPEND([JNI_CPPFLAGS], ["-I$sr_dir"])
 done
+AC_SUBST([JNI_CPPFLAGS])
+
+sr_save_cppflags=$CPPFLAGS
+SR_APPEND([CPPFLAGS], [$JNI_CPPFLAGS])
+AC_CHECK_HEADER([jni.h],,
+	[SR_APPEND([sr_java_missing], [', '], ['JNI headers'])])
+CPPFLAGS=$sr_save_cppflags
+
+AS_IF([test -z "$sr_java_missing"],
+	[BINDINGS_JAVA=$enable_java], [BINDINGS_JAVA=no])
+AM_CONDITIONAL([BINDINGS_JAVA], [test "x$BINDINGS_JAVA" = xyes])
+
+##############################
+##  Finalize configuration  ##
+##############################
+
+# Add mandatory dependencies to module list.
+SR_APPEND([SR_PKGLIBS], ['libzip >= 0.10'])
+AC_SUBST([SR_PKGLIBS])
+
+# Retrieve the compile and link flags for all modules combined.
+# Also, bail out at this point if any module dependency is not met.
+PKG_CHECK_MODULES([LIBSIGROK], [glib-2.0 >= 2.32.0 $SR_PKGLIBS])
+PKG_CHECK_MODULES([TESTS], [$SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS])
+
+# SR_PKGLIBS_CXX may be empty, so only invoke these checks when
+# the C++ bindings are enabled.
+AM_COND_IF([BINDINGS_CXX], [
+	PKG_CHECK_MODULES([LIBSIGROKCXX], [$SR_PKGLIBS_CXX])
+	PKG_CHECK_MODULES([PYSIGROK], [$SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX])
+	PKG_CHECK_MODULES([RBSIGROK], [$SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX], [AC_SUBST(RBSIGROK_EXTDIR, "lib/$($PKG_CONFIG --variable=sitearch $SR_PKGLIBS_RUBY)/$($PKG_CONFIG --variable=RUBY_BASE_NAME $SR_PKGLIBS_RUBY)/vendor_ruby/$($PKG_CONFIG --variable=ruby_version $SR_PKGLIBS_RUBY)")])
+])
+
+# Check for specific libusb features, now that we know the CFLAGS.
+AC_LANG([C])
+sr_save_cflags=$CFLAGS
+sr_save_libs=$LIBS
+CFLAGS="$LIBSIGROK_CFLAGS $CFLAGS"
+LIBS="$LIBSIGROK_LIBS $LIBS"
+AC_CHECK_TYPES([libusb_os_handle],
+	[sr_have_libusb_os_handle=yes], [sr_have_libusb_os_handle=no],
+	[[#include <libusb.h>]])
+AC_CHECK_FUNCS([zip_discard])
+LIBS=$sr_save_libs
+CFLAGS=$sr_save_cflags
+
+AM_COND_IF([NEED_USB], [AS_CASE([$sr_have_libusb_os_handle:$host_os], [no:mingw*],
+	[AC_MSG_ERROR([Windows builds require the event-abstraction branch of libusb])])])
+
+sr_glib_version=`$PKG_CONFIG --modversion glib-2.0 2>&AS_MESSAGE_LOG_FD`
+sr_libzip_version=`$PKG_CONFIG --modversion libzip 2>&AS_MESSAGE_LOG_FD`
+
+AC_DEFINE_UNQUOTED([CONF_LIBZIP_VERSION], ["$sr_libzip_version"],
+	[Build-time version of libzip.])
+AC_DEFINE_UNQUOTED([CONF_HOST], ["$host"],
+	[The canonical host libsigrok will run on.])
+
+AC_CONFIG_FILES([Makefile libsigrok.pc bindings/cxx/libsigrokcxx.pc])
 
-echo -e "\nEnabled hardware drivers:\n"
-echo "  - agilent-dmm..................... $HW_AGILENT_DMM"
-echo "  - appa-55ii....................... $HW_APPA_55II"
-echo "  - asix-sigma...................... $HW_ASIX_SIGMA"
-echo "  - atten-pps3xxx................... $HW_ATTEN_PPS3XXX"
-echo "  - brymen-bm86x.................... $HW_BRYMEN_BM86X"
-echo "  - brymen-dmm...................... $HW_BRYMEN_DMM"
-echo "  - cem-dt-885x..................... $HW_CEM_DT_885X"
-echo "  - center-3xx...................... $HW_CENTER_3XX"
-echo "  - chronovu-la..................... $HW_CHRONOVU_LA"
-echo "  - colead-slm...................... $HW_COLEAD_SLM"
-echo "  - conrad-digi-35-cpu.............. $HW_CONRAD_DIGI_35_CPU"
-echo "  - demo............................ $HW_DEMO"
-echo "  - fluke-dmm....................... $HW_FLUKE_DMM"
-echo "  - fx2lafw......................... $HW_FX2LAFW"
-echo "  - gmc-mh-1x-2x.................... $HW_GMC_MH_1X_2X"
-echo "  - hameg-hmo....................... $HW_HAMEG_HMO"
-echo "  - hantek-dso...................... $HW_HANTEK_DSO"
-echo "  - ikalogic-scanalogic2............ $HW_IKALOGIC_SCANALOGIC2"
-echo "  - ikalogic-scanaplus.............. $HW_IKALOGIC_SCANAPLUS"
-echo "  - kecheng-kc-330b................. $HW_KECHENG_KC_330B"
-echo "  - lascar-el-usb................... $HW_LASCAR_EL_USB"
-echo "  - mic-985xx....................... $HW_MIC_985XX"
-echo "  - norma-dmm....................... $HW_NORMA_DMM"
-echo "  - openbench-logic-sniffer......... $HW_OLS"
-echo "  - rigol-ds........................ $HW_RIGOL_DS"
-echo "  - saleae-logic16.................. $HW_SALEAE_LOGIC16"
-echo "  - serial-dmm...................... $HW_SERIAL_DMM"
-echo "  - sysclk-lwla..................... $HW_SYSCLK_LWLA"
-echo "  - teleinfo........................ $HW_TELEINFO"
-echo "  - tondaj-sl-814................... $HW_TONDAJ_SL_814"
-echo "  - uni-t-dmm....................... $HW_UNI_T_DMM"
-echo "  - uni-t-ut32x..................... $HW_UNI_T_UT32X"
-echo "  - victor-dmm...................... $HW_VICTOR_DMM"
-echo "  - zeroplus-logic-cube............. $HW_ZEROPLUS_LOGIC_CUBE"
-echo
+AC_OUTPUT
 
+# Prepare bindings report messages.
+m4_define([SR_PREPARE_BINDING_REPORT], [
+	sr_report_$1=
+	test -z "$sr_$1_missing" || sr_report_$1=" (missing: $sr_$1_missing)"
+	test "x$enable_$1" = xyes || sr_report_$1=' (disabled)'
+])
+m4_map_args([SR_PREPARE_BINDING_REPORT], [cxx], [python], [ruby], [java])
+
+cat >&AS_MESSAGE_FD <<_EOF
+
+libsigrok configuration summary:
+ - Package version................. $SR_PACKAGE_VERSION
+ - Library ABI version............. $SR_LIB_VERSION
+ - Prefix.......................... $prefix
+ - Building on..................... $build
+ - Building for.................... $host
+
+Compile configuration:
+ - C compiler...................... $CC
+ - C compiler version.............. $sr_cc_version
+ - C compiler flags................ $CFLAGS
+ - Additional C compiler flags..... $SR_EXTRA_CFLAGS
+ - C compiler warnings............. $SR_WFLAGS
+ - C++ compiler.................... $CXX
+ - C++ compiler version............ $sr_cxx_version
+ - C++ compiler flags.............. $CXXFLAGS
+ - C++ compiler warnings........... $SR_WXXFLAGS
+
+Detected libraries (required):
+ - glib-2.0 >= 2.32.0.............. $sr_glib_version
+ - libzip >= 0.10.................. $sr_libzip_version
+
+Detected libraries (optional):
+$sr_pkglibs_summary
+Enabled hardware drivers:
+$sr_driver_summary
+Enabled SCPI backends:
+ - TCP............................. yes
+ - RPC............................. $sr_cv_have_rpc
+ - serial.......................... $sr_have_libserialport
+ - VISA............................ $sr_have_librevisa
+ - GPIB............................ $sr_have_libgpib
+ - USBTMC.......................... $sr_have_libusb
+
+Enabled language bindings:
+ - C++............................. $BINDINGS_CXX$sr_report_cxx
+ - Python.......................... $BINDINGS_PYTHON$sr_report_python
+ - Ruby............................ $BINDINGS_RUBY$sr_report_ruby
+ - Java............................ $BINDINGS_JAVA$sr_report_java
+
+_EOF
diff --git a/contrib/z60_libsigrok.rules b/contrib/z60_libsigrok.rules
index 46590c1..b4751b2 100644
--- a/contrib/z60_libsigrok.rules
+++ b/contrib/z60_libsigrok.rules
@@ -25,6 +25,14 @@
 ACTION!="add|change", GOTO="libsigrok_rules_end"
 SUBSYSTEM!="usb|usbmisc|usb_device", GOTO="libsigrok_rules_end"
 
+# Agilent USBTMC-connected devices
+# 34405A
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0618", MODE="664", GROUP="plugdev"
+# 34410A
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0607", MODE="664", GROUP="plugdev"
+# DSO1000 series
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", MODE="664", GROUP="plugdev"
+
 # ASIX SIGMA
 # ASIX SIGMA2
 ATTRS{idVendor}=="a600", ATTRS{idProduct}=="a000", MODE="664", GROUP="plugdev"
@@ -67,6 +75,9 @@ ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="664", GROUP="plugdev"
 # Dangerous Prototypes Buspirate (v4)
 ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00", MODE="664", GROUP="plugdev"
 
+# DreamSourceLab DSLogic
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0001", MODE="664", GROUP="plugdev"
+
 # Hantek DSO-2090
 # lsusb: "04b4:2090 Cypress Semiconductor Corp."
 # lsusb after FW upload: "04b5:2090 ROHM LSI Systems USA, LLC"
@@ -111,6 +122,10 @@ ATTRS{idVendor}=="1041", ATTRS{idProduct}=="8101", MODE="664", GROUP="plugdev"
 # This is actually the generic SiLabs (Cygnal) F32x USBXpress VID:PID.
 ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="0002", MODE="664", GROUP="plugdev"
 
+# LeCroy LogicStudio16
+ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a001", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a002", MODE="664", GROUP="plugdev"
+
 # Link Instruments MSO-19
 ATTRS{idVendor}=="3195", ATTRS{idProduct}=="f190", MODE="664", GROUP="plugdev"
 
@@ -131,15 +146,15 @@ ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", MODE="664", GROUP="plugdev"
 # Rigol DS1000 series
 ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0588", MODE="664", GROUP="plugdev"
 
+# Rigol 1000Z series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04ce", MODE="664", GROUP="plugdev"
+
 # Rigol DS2000 series
 ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b0", MODE="664", GROUP="plugdev"
 
 # Rigol DG4000 series
 ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0641", MODE="664", GROUP="plugdev"
 
-# Agilent DSO1000 series
-ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", MODE="664", GROUP="plugdev"
-
 # Saleae Logic
 # EE Electronics ESLA100 (clone of the Saleae Logic)
 # Robomotic MiniLogic (clone of the Saleae Logic)
@@ -150,9 +165,21 @@ ATTRS{idVendor}=="0925", ATTRS{idProduct}=="3881", MODE="664", GROUP="plugdev"
 # Saleae Logic16
 ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1001", MODE="664", GROUP="plugdev"
 
+# sigrok FX2 LA (8ch)
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608c", MODE="664", GROUP="plugdev"
+
+# sigrok FX2 LA (16ch)
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608d", MODE="664", GROUP="plugdev"
+
+# SysClk LWLA1016
+ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6688", MODE="664", GROUP="plugdev"
+
 # SysClk LWLA1034
 ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6689", MODE="664", GROUP="plugdev"
 
+# Testo 435
+ATTRS{idVendor}=="128d", ATTRS{idProduct}=="0003", MODE="664", GROUP="plugdev"
+
 # UNI-T UT-D04 multimeter cable (for various UNI-T and rebranded DMMs)
 # http://sigrok.org/wiki/Device_cables#UNI-T_UT-D04
 # UNI-T UT325
diff --git a/device.c b/device.c
deleted file mode 100644
index f87bb66..0000000
--- a/device.c
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert at 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"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "device"
-/** @endcond */
-
-/**
- * @file
- *
- * Device handling in libsigrok.
- */
-
-/**
- * @defgroup grp_devices Devices
- *
- * Device handling in libsigrok.
- *
- * @{
- */
-
-/** @private
- *  Allocate and initialize new struct sr_channel
- *  @param[in]  index @copydoc sr_channel::index
- *  @param[in]  type @copydoc sr_channel::type
- *  @param[in]  enabled @copydoc sr_channel::enabled
- *  @param[in]  name @copydoc sr_channel::name
- *
- *  @return NULL (failure) or new struct sr_channel*.
- */
-SR_PRIV struct sr_channel *sr_channel_new(int index, int type,
-		gboolean enabled, const char *name)
-{
-	struct sr_channel *ch;
-
-	if (!(ch = g_try_malloc0(sizeof(struct sr_channel)))) {
-		sr_err("Channel malloc failed.");
-		return NULL;
-	}
-
-	ch->index = index;
-	ch->type = type;
-	ch->enabled = enabled;
-	if (name)
-		ch->name = g_strdup(name);
-
-	return ch;
-}
-
-/**
- * Set the name of the specified channel in the specified device.
- *
- * If the channel 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 channel is connected to.
- * @param[in] channelnum The number of the channel whose name to set.
- *                 Note that the channel numbers start at 0.
- * @param[in] name The new name that the specified channel should get. A copy
- *             of the string is made.
- *
- * @return SR_OK on success, or SR_ERR_ARG on invalid arguments.
- *
- * @since 0.3.0
- */
-SR_API int sr_dev_channel_name_set(const struct sr_dev_inst *sdi,
-		int channelnum, const char *name)
-{
-	GSList *l;
-	struct sr_channel *ch;
-	int ret;
-
-	if (!sdi) {
-		sr_err("%s: sdi was NULL", __func__);
-		return SR_ERR_ARG;
-	}
-
-	ret = SR_ERR_ARG;
-	for (l = sdi->channels; l; l = l->next) {
-		ch = l->data;
-		if (ch->index == channelnum) {
-			g_free(ch->name);
-			ch->name = g_strdup(name);
-			ret = SR_OK;
-			break;
-		}
-	}
-
-	return ret;
-}
-
-/**
- * Enable or disable a channel on the specified device.
- *
- * @param sdi The device instance the channel is connected to.
- * @param channelnum The channel number, starting from 0.
- * @param state TRUE to enable the channel, FALSE to disable.
- *
- * @return SR_OK on success or SR_ERR on failure.  In case of invalid
- *         arguments, SR_ERR_ARG is returned and the channel enabled state
- *         remains unchanged.
- *
- * @since 0.3.0
- */
-SR_API int sr_dev_channel_enable(const struct sr_dev_inst *sdi, int channelnum,
-		gboolean state)
-{
-	GSList *l;
-	struct sr_channel *ch;
-	int ret;
-	gboolean was_enabled;
-
-	if (!sdi)
-		return SR_ERR_ARG;
-
-	ret = SR_ERR_ARG;
-	for (l = sdi->channels; l; l = l->next) {
-		ch = l->data;
-		if (ch->index == channelnum) {
-			was_enabled = ch->enabled;
-			ch->enabled = state;
-			ret = SR_OK;
-			if (!state != !was_enabled && sdi->driver
-					&& sdi->driver->config_channel_set) {
-				ret = sdi->driver->config_channel_set(
-					sdi, ch, SR_CHANNEL_SET_ENABLED);
-				/* Roll back change if it wasn't applicable. */
-				if (ret == SR_ERR_ARG)
-					ch->enabled = was_enabled;
-			}
-			break;
-		}
-	}
-
-	return ret;
-}
-
-/**
- * Add a trigger to the specified device (and the specified channel).
- *
- * If the specified channel of this device already has a trigger, it will
- * be silently replaced.
- *
- * @param[in,out] sdi Pointer to the device instance; must not be NULL.
- * @param[in] channelnum Number of channel, starting at 0.
- * @param[in] trigger Trigger string, in the format used by sigrok-cli
- *
- * @return SR_OK on success or SR_ERR on failure.  In case of invalid
- *         arguments, SR_ERR_ARG is returned and the trigger settings
- *         remain unchanged.
- *
- * @since 0.2.0
- */
-SR_API int sr_dev_trigger_set(const struct sr_dev_inst *sdi, int channelnum,
-		const char *trigger)
-{
-	GSList *l;
-	struct sr_channel *ch;
-	char *old_trigger;
-	int ret;
-
-	if (!sdi)
-		return SR_ERR_ARG;
-
-	ret = SR_ERR_ARG;
-	for (l = sdi->channels; l; l = l->next) {
-		ch = l->data;
-		if (ch->index == channelnum) {
-			old_trigger = ch->trigger;
-			ret = SR_OK;
-			if (g_strcmp0(trigger, old_trigger) == 0)
-				break;
-			/* Set new trigger if it has changed. */
-			ch->trigger = g_strdup(trigger);
-
-			if (sdi->driver && sdi->driver->config_channel_set) {
-				ret = sdi->driver->config_channel_set(
-					sdi, ch, SR_CHANNEL_SET_TRIGGER);
-				/* Roll back change if it wasn't applicable. */
-				if (ret == SR_ERR_ARG) {
-					g_free(ch->trigger);
-					ch->trigger = old_trigger;
-					break;
-				}
-			}
-			g_free(old_trigger);
-			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[in] key The option that should be checked for is supported by the
- *            specified device.
- *
- * @retval TRUE Device has the specified option
- * @retval FALSE Device does not have the specified option, invalid input
- *         parameters or other error conditions.
- *
- * @since 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, sdi, 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
- *  Allocate and init new device instance struct.
- *  @param[in]  index   @copydoc sr_dev_inst::index
- *  @param[in]  status  @copydoc sr_dev_inst::status
- *  @param[in]  vendor  @copydoc sr_dev_inst::vendor
- *  @param[in]  model   @copydoc sr_dev_inst::model
- *  @param[in]  version @copydoc sr_dev_inst::version
- *
- *  @retval NULL Error
- *  @retval struct sr_dev_inst *. Dynamically allocated, free using
- *              sr_dev_inst_free().
- */
-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->channels = NULL;
-	sdi->channel_groups = NULL;
-	sdi->conn = NULL;
-	sdi->priv = NULL;
-
-	return sdi;
-}
-
-/** @private
- *  Free device instance struct created by sr_dev_inst().
- *  @param sdi  struct* to free.
- */
-SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi)
-{
-	struct sr_channel *ch;
-	GSList *l;
-
-	for (l = sdi->channels; l; l = l->next) {
-		ch = l->data;
-		g_free(ch->name);
-		g_free(ch->trigger);
-		g_free(ch);
-	}
-	g_slist_free(sdi->channels);
-
-	if (sdi->channel_groups)
-		g_slist_free(sdi->channel_groups);
-
-	g_free(sdi->vendor);
-	g_free(sdi->model);
-	g_free(sdi->version);
-	g_free(sdi);
-}
-
-#ifdef HAVE_LIBUSB_1_0
-
-/** @private
- *  Allocate and init struct for USB device instance.
- *  @param[in]  bus @copydoc sr_usb_dev_inst::bus
- *  @param[in]  address @copydoc sr_usb_dev_inst::address
- *  @param[in]  hdl @copydoc sr_usb_dev_inst::devhdl
- *
- *  @retval NULL Error
- *  @retval other struct sr_usb_dev_inst * for USB device instance.
- */
-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
- *  Free struct * allocated by sr_usb_dev_inst().
- *  @param usb  struct* to free. Must not be NULL.
- */
-SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb)
-{
-	g_free(usb);
-}
-
-#endif
-
-#ifdef HAVE_LIBSERIALPORT
-
-/**
- * @private
- *
- * Both parameters are copied to newly allocated strings, and freed
- * automatically by sr_serial_dev_inst_free().
- *
- * @param[in] port OS-specific serial port specification. Examples:
- *                 "/dev/ttyUSB0", "/dev/ttyACM1", "/dev/tty.Modem-0", "COM1".
- * @param[in] 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);
-
-	return serial;
-}
-
-/** @private
- *  Free struct sr_serial_dev_inst * allocated by sr_serial_dev_inst().
- *  @param serial   struct sr_serial_dev_inst * to free. Must not be NULL.
- */
-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);
-}
-#endif
-
-/** @private */
-SR_PRIV struct sr_usbtmc_dev_inst *sr_usbtmc_dev_inst_new(const char *device)
-{
-	struct sr_usbtmc_dev_inst *usbtmc;
-
-	if (!device) {
-		sr_err("Device name required.");
-		return NULL;
-	}
-
-	if (!(usbtmc = g_try_malloc0(sizeof(struct sr_usbtmc_dev_inst)))) {
-		sr_err("USBTMC device instance malloc failed.");
-		return NULL;
-	}
-
-	usbtmc->device = g_strdup(device);
-	usbtmc->fd = -1;
-
-	return usbtmc;
-}
-
-/** @private */
-SR_PRIV void sr_usbtmc_dev_inst_free(struct sr_usbtmc_dev_inst *usbtmc)
-{
-	g_free(usbtmc->device);
-	g_free(usbtmc);
-}
-
-/**
- * Get the list of devices/instances of the specified driver.
- *
- * @param driver The driver to use. Must not be NULL.
- *
- * @return The list of devices/instances of this driver, or NULL upon errors
- *         or if the list is empty.
- *
- * @since 0.2.0
- */
-SR_API GSList *sr_dev_list(const struct sr_dev_driver *driver)
-{
-	if (driver && driver->dev_list)
-		return driver->dev_list();
-	else
-		return NULL;
-}
-
-/**
- * Clear the list of device instances a driver knows about.
- *
- * @param driver The driver to use. This must be a pointer to one of
- *               the entries returned by sr_driver_list(). Must not be NULL.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid driver
- *
- * @since 0.2.0
- */
-SR_API int sr_dev_clear(const struct sr_dev_driver *driver)
-{
-	int ret;
-
-	if (!driver) {
-		sr_err("Invalid driver.");
-		return SR_ERR_ARG;
-	}
-
-	if (driver->dev_clear)
-		ret = driver->dev_clear();
-	else
-		ret = std_dev_clear(driver, NULL);
-
-	return ret;
-}
-
-/**
- * Open the specified device.
- *
- * @param sdi Device instance to use. Must not be NULL.
- *
- * @return SR_OK upon success, a negative error code upon errors.
- *
- * @since 0.2.0
- */
-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;
-}
-
-/**
- * Close the specified device.
- *
- * @param sdi Device instance to use. Must not be NULL.
- *
- * @return SR_OK upon success, a negative error code upon errors.
- *
- * @since 0.2.0
- */
-SR_API int sr_dev_close(struct sr_dev_inst *sdi)
-{
-	int ret;
-
-	if (!sdi || !sdi->driver || !sdi->driver->dev_close)
-		return SR_ERR;
-
-	ret = sdi->driver->dev_close(sdi);
-
-	return ret;
-}
-
-/** @} */
diff --git a/hardware/common/usb.c b/hardware/common/usb.c
deleted file mode 100644
index 62db385..0000000
--- a/hardware/common/usb.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Uwe Hermann <uwe at hermann-uwe.de>
- * Copyright (C) 2012 Bert Vermeulen <bert at 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+)$"
-
-#define LOG_PREFIX "usb"
-
-/**
- * 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;
-}
-
-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;
-}
-
-#ifdef _WIN32
-static gpointer usb_thread(gpointer data)
-{
-	struct sr_context *ctx = data;
-
-	while (ctx->usb_thread_running) {
-		g_mutex_lock(&ctx->usb_mutex);
-		libusb_wait_for_event(ctx->libusb_ctx, NULL);
-		SetEvent(ctx->usb_event);
-		g_mutex_unlock(&ctx->usb_mutex);
-		g_thread_yield();
-	}
-
-	return NULL;
-}
-
-static int usb_callback(int fd, int revents, void *cb_data)
-{
-	struct sr_context *ctx = cb_data;
-	int ret;
-
-	g_mutex_lock(&ctx->usb_mutex);
-	ret = ctx->usb_cb(fd, revents, ctx->usb_cb_data);
-
-	if (ctx->usb_thread_running) {
-		ResetEvent(ctx->usb_event);
-		g_mutex_unlock(&ctx->usb_mutex);
-	}
-
-	return ret;
-}
-#endif
-
-SR_PRIV int usb_source_add(struct sr_context *ctx, int timeout,
-		sr_receive_data_callback cb, void *cb_data)
-{
-	if (ctx->usb_source_present) {
-		sr_err("A USB event source is already present.");
-		return SR_ERR;
-	}
-
-#ifdef _WIN32
-	ctx->usb_event = CreateEvent(NULL, TRUE, FALSE, NULL);
-	g_mutex_init(&ctx->usb_mutex);
-	ctx->usb_thread_running = TRUE;
-	ctx->usb_thread = g_thread_new("usb", usb_thread, ctx);
-	ctx->usb_pollfd.fd = ctx->usb_event;
-	ctx->usb_pollfd.events = G_IO_IN;
-	ctx->usb_cb = cb;
-	ctx->usb_cb_data = cb_data;
-	sr_session_source_add_pollfd(&ctx->usb_pollfd, timeout, usb_callback, ctx);
-#else
-	const struct libusb_pollfd **lupfd;
-	unsigned int i;
-
-	lupfd = libusb_get_pollfds(ctx->libusb_ctx);
-	for (i = 0; lupfd[i]; i++)
-		sr_source_add(lupfd[i]->fd, lupfd[i]->events, timeout, cb, cb_data);
-	free(lupfd);
-#endif
-	ctx->usb_source_present = TRUE;
-
-	return SR_OK;
-}
-
-SR_PRIV int usb_source_remove(struct sr_context *ctx)
-{
-	if (!ctx->usb_source_present)
-		return SR_OK;
-
-#ifdef _WIN32
-	ctx->usb_thread_running = FALSE;
-	g_mutex_unlock(&ctx->usb_mutex);
-	libusb_unlock_events(ctx->libusb_ctx);
-	g_thread_join(ctx->usb_thread);
-	g_mutex_clear(&ctx->usb_mutex);
-	sr_session_source_remove_pollfd(&ctx->usb_pollfd);
-	CloseHandle(ctx->usb_event);
-#else
-	const struct libusb_pollfd **lupfd;
-	unsigned int i;
-
-	lupfd = libusb_get_pollfds(ctx->libusb_ctx);
-	for (i = 0; lupfd[i]; i++)
-		sr_source_remove(lupfd[i]->fd);
-	free(lupfd);
-#endif
-	ctx->usb_source_present = FALSE;
-
-	return SR_OK;
-}
diff --git a/hardware/serial-dmm/api.c b/hardware/serial-dmm/api.c
deleted file mode 100644
index 136d2a7..0000000
--- a/hardware/serial-dmm/api.c
+++ /dev/null
@@ -1,632 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert at biot.com>
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me at gmail.com>
- * Copyright (C) 2012 Uwe Hermann <uwe at hermann-uwe.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#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_LIMIT_MSEC,
-	SR_CONF_CONTINUOUS,
-};
-
-SR_PRIV struct sr_dev_driver bbcgm_m2110_driver_info;
-SR_PRIV struct sr_dev_driver digitek_dt4000zc_driver_info;
-SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info;
-SR_PRIV struct sr_dev_driver metex_me31_driver_info;
-SR_PRIV struct sr_dev_driver peaktech_3410_driver_info;
-SR_PRIV struct sr_dev_driver mastech_mas345_driver_info;
-SR_PRIV struct sr_dev_driver va_va18b_driver_info;
-SR_PRIV struct sr_dev_driver va_va40b_driver_info;
-SR_PRIV struct sr_dev_driver metex_m3640d_driver_info;
-SR_PRIV struct sr_dev_driver metex_m4650cr_driver_info;
-SR_PRIV struct sr_dev_driver peaktech_4370_driver_info;
-SR_PRIV struct sr_dev_driver pce_pce_dm32_driver_info;
-SR_PRIV struct sr_dev_driver radioshack_22_168_driver_info;
-SR_PRIV struct sr_dev_driver radioshack_22_805_driver_info;
-SR_PRIV struct sr_dev_driver radioshack_22_812_driver_info;
-SR_PRIV struct sr_dev_driver tecpel_dmm_8061_ser_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_m3650cr_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_m3650d_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_m4650cr_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_me42_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc820_ser_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc830_ser_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc840_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60a_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60e_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60g_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61b_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61c_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61d_ser_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info;
-SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info;
-SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info;
-SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info;
-
-SR_PRIV struct dmm_info dmms[] = {
-	{
-		"BBC Goertz Metrawatt", "M2110", "1200/7n2", 1200,
-		BBCGM_M2110_PACKET_SIZE, NULL,
-		sr_m2110_packet_valid, sr_m2110_parse,
-		NULL,
-		&bbcgm_m2110_driver_info, receive_data_BBCGM_M2110,
-	},
-	{
-		"Digitek", "DT4000ZC", "2400/8n1/dtr=1", 2400,
-		FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_10_temp_c,
-		&digitek_dt4000zc_driver_info, receive_data_DIGITEK_DT4000ZC,
-	},
-	{
-		"TekPower", "TP4000ZC", "2400/8n1/dtr=1", 2400,
-		FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_10_temp_c,
-		&tekpower_tp4000zc_driver_info, receive_data_TEKPOWER_TP4000ZC,
-	},
-	{
-		"Metex", "ME-31", "600/7n2/rts=0/dtr=1", 600,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&metex_me31_driver_info, receive_data_METEX_ME31,
-	},
-	{
-		"Peaktech", "3410", "600/7n2/rts=0/dtr=1", 600,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&peaktech_3410_driver_info, receive_data_PEAKTECH_3410,
-	},
-	{
-		"MASTECH", "MAS345", "600/7n2/rts=0/dtr=1", 600,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&mastech_mas345_driver_info, receive_data_MASTECH_MAS345,
-	},
-	{
-		"V&A", "VA18B", "2400/8n1", 2400,
-		FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_01_temp_c,
-		&va_va18b_driver_info, receive_data_VA_VA18B,
-	},
-	{
-		"V&A", "VA40B", "2400/8n1", 2400,
-		FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_max_c_min,
-		&va_va40b_driver_info, receive_data_VA_VA40B,
-	},
-	{
-		"Metex", "M-3640D", "1200/7n2/rts=0/dtr=1", 1200,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&metex_m3640d_driver_info, receive_data_METEX_M3640D,
-	},
-	{
-		"Metex", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&metex_m4650cr_driver_info, receive_data_METEX_M4650CR,
-	},
-	{
-		"PeakTech", "4370", "1200/7n2/rts=0/dtr=1", 1200,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&peaktech_4370_driver_info, receive_data_PEAKTECH_4370,
-	},
-	{
-		"PCE", "PCE-DM32", "2400/8n1", 2400,
-		FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_01_10_temp_f_c,
-		&pce_pce_dm32_driver_info, receive_data_PCE_PCE_DM32,
-	},
-	{
-		"RadioShack", "22-168", "1200/7n2/rts=0/dtr=1", 1200,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&radioshack_22_168_driver_info, receive_data_RADIOSHACK_22_168,
-	},
-	{
-		"RadioShack", "22-805", "600/7n2/rts=0/dtr=1", 600,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&radioshack_22_805_driver_info, receive_data_RADIOSHACK_22_805,
-	},
-	{
-		"RadioShack", "22-812", "4800/8n1/rts=0/dtr=1", 4800,
-		RS9LCD_PACKET_SIZE, NULL,
-		sr_rs9lcd_packet_valid, sr_rs9lcd_parse,
-		NULL,
-		&radioshack_22_812_driver_info, receive_data_RADIOSHACK_22_812,
-	},
-	{
-		"Tecpel", "DMM-8061 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_00_temp_c,
-		&tecpel_dmm_8061_ser_driver_info,
-		receive_data_TECPEL_DMM_8061_SER,
-	},
-	{
-		"Voltcraft", "M-3650CR", "1200/7n2/rts=0/dtr=1", 1200,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&voltcraft_m3650cr_driver_info, receive_data_VOLTCRAFT_M3650CR,
-	},
-	{
-		"Voltcraft", "M-3650D", "1200/7n2/rts=0/dtr=1", 1200,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&voltcraft_m3650d_driver_info, receive_data_VOLTCRAFT_M3650D,
-	},
-	{
-		"Voltcraft", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&voltcraft_m4650cr_driver_info, receive_data_VOLTCRAFT_M4650CR,
-	},
-	{
-		"Voltcraft", "ME-42", "600/7n2/rts=0/dtr=1", 600,
-		METEX14_PACKET_SIZE, sr_metex14_packet_request,
-		sr_metex14_packet_valid, sr_metex14_parse,
-		NULL,
-		&voltcraft_me42_driver_info, receive_data_VOLTCRAFT_ME42,
-	},
-	{
-		"Voltcraft", "VC-820 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		NULL,
-		&voltcraft_vc820_ser_driver_info,
-		receive_data_VOLTCRAFT_VC820_SER,
-	},
-	{
-		/*
-		 * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
-		 * the FS9922 protocol. Instead, it only sets the user-defined
-		 * bit "z1" to indicate "diode mode" and "voltage".
-		 */
-		"Voltcraft", "VC-830 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9922_PACKET_SIZE, NULL,
-		sr_fs9922_packet_valid, sr_fs9922_parse,
-		&sr_fs9922_z1_diode,
-		&voltcraft_vc830_ser_driver_info,
-		receive_data_VOLTCRAFT_VC830_SER,
-	},
-	{
-		"Voltcraft", "VC-840 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_00_temp_c,
-		&voltcraft_vc840_ser_driver_info,
-		receive_data_VOLTCRAFT_VC840_SER,
-	},
-	{
-		"UNI-T", "UT60A (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		NULL,
-		&uni_t_ut60a_ser_driver_info,
-		receive_data_UNI_T_UT60A_SER,
-	},
-	{
-		"UNI-T", "UT60E (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_00_temp_c,
-		&uni_t_ut60e_ser_driver_info,
-		receive_data_UNI_T_UT60E_SER,
-	},
-	{
-		/* Note: ES51986 baudrate is actually 19230! */
-		"UNI-T", "UT60G (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
-		19200, ES519XX_11B_PACKET_SIZE, NULL,
-		sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
-		NULL,
-		&uni_t_ut60g_ser_driver_info, receive_data_UNI_T_UT60G_SER,
-	},
-	{
-		"UNI-T", "UT61B (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9922_PACKET_SIZE, NULL,
-		sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
-		&uni_t_ut61b_ser_driver_info, receive_data_UNI_T_UT61B_SER,
-	},
-	{
-		"UNI-T", "UT61C (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9922_PACKET_SIZE, NULL,
-		sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
-		&uni_t_ut61c_ser_driver_info, receive_data_UNI_T_UT61C_SER,
-	},
-	{
-		"UNI-T", "UT61D (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9922_PACKET_SIZE, NULL,
-		sr_fs9922_packet_valid, sr_fs9922_parse, NULL,
-		&uni_t_ut61d_ser_driver_info, receive_data_UNI_T_UT61D_SER,
-	},
-	{
-		/* Note: ES51922 baudrate is actually 19230! */
-		"UNI-T", "UT61E (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
-		19200, ES519XX_14B_PACKET_SIZE, NULL,
-		sr_es519xx_19200_14b_packet_valid, sr_es519xx_19200_14b_parse,
-		NULL,
-		&uni_t_ut61e_ser_driver_info, receive_data_UNI_T_UT61E_SER,
-	},
-	{
-		"ISO-TECH", "IDM103N", "2400/7o1/rts=0/dtr=1",
-		2400, ES519XX_11B_PACKET_SIZE, NULL,
-		sr_es519xx_2400_11b_packet_valid, sr_es519xx_2400_11b_parse,
-		NULL,
-		&iso_tech_idm103n_driver_info, receive_data_ISO_TECH_IDM103N,
-	},
-	{
-		"Tenma", "72-7745 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
-		2400, FS9721_PACKET_SIZE, NULL,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_00_temp_c,
-		&tenma_72_7745_ser_driver_info, receive_data_TENMA_72_7745_SER,
-	},
-	{
-		/* Note: ES51986 baudrate is actually 19230! */
-		"Tenma", "72-7750 (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
-		19200, ES519XX_11B_PACKET_SIZE, NULL,
-		sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
-		NULL,
-		&tenma_72_7750_ser_driver_info, receive_data_TENMA_72_7750_SER,
-	},
-};
-
-static int dev_clear(int dmm)
-{
-	return std_dev_clear(dmms[dmm].di, NULL);
-}
-
-static int init(struct sr_context *sr_ctx, int dmm)
-{
-	sr_dbg("Selected '%s' subdriver.", dmms[dmm].di->name);
-
-	return std_init(sr_ctx, dmms[dmm].di, LOG_PREFIX);
-}
-
-static GSList *sdmm_scan(const char *conn, const char *serialcomm, int dmm)
-{
-	struct sr_dev_inst *sdi;
-	struct drv_context *drvc;
-	struct dev_context *devc;
-	struct sr_channel *ch;
-	struct sr_serial_dev_inst *serial;
-	GSList *devices;
-	int dropped, ret;
-	size_t len;
-	uint8_t buf[128];
-
-	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 serial port %s.", conn);
-
-	drvc = dmms[dmm].di->priv;
-	devices = NULL;
-	serial_flush(serial);
-
-	/* Request a packet if the DMM requires this. */
-	if (dmms[dmm].packet_request) {
-		if ((ret = dmms[dmm].packet_request(serial)) < 0) {
-			sr_err("Failed to request packet: %d.", ret);
-			return FALSE;
-		}
-	}
-
-	/*
-	 * There's no way to get an ID from the multimeter. It just sends data
-	 * periodically (or upon request), so the best we can do is check if
-	 * the packets match the expected format.
-	 */
-
-	/* Let's get a bit of data and see if we can find a packet. */
-	len = sizeof(buf);
-	ret = serial_stream_detect(serial, buf, &len, dmms[dmm].packet_size,
-				   dmms[dmm].packet_valid, 3000,
-				   dmms[dmm].baudrate);
-	if (ret != SR_OK)
-		goto scan_cleanup;
-
-	/*
-	 * If we dropped more than two packets worth of data, something is
-	 * wrong. We shouldn't quit however, since the dropped bytes might be
-	 * just zeroes at the beginning of the stream. Those can occur as a
-	 * combination of the nonstandard cable that ships with some devices
-	 * and the serial port or USB to serial adapter.
-	 */
-	dropped = len - dmms[dmm].packet_size;
-	if (dropped > 2 * dmms[dmm].packet_size)
-		sr_warn("Had to drop too much data.");
-
-	sr_info("Found device on port %s.", conn);
-
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, dmms[dmm].vendor,
-				    dmms[dmm].device, NULL)))
-		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;
-
-	sdi->priv = devc;
-	sdi->driver = dmms[dmm].di;
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-		goto scan_cleanup;
-	sdi->channels = g_slist_append(sdi->channels, ch);
-	drvc->instances = g_slist_append(drvc->instances, sdi);
-	devices = g_slist_append(devices, sdi);
-
-scan_cleanup:
-	serial_close(serial);
-
-	return devices;
-}
-
-static GSList *scan(GSList *options, int dmm)
-{
-	struct sr_config *src;
-	GSList *l, *devices;
-	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 = sdmm_scan(conn, serialcomm, dmm);
-	} else {
-		/* Try the default. */
-		devices = sdmm_scan(conn, dmms[dmm].conn, dmm);
-	}
-
-	return devices;
-}
-
-static GSList *dev_list(int dmm)
-{
-	return ((struct drv_context *)(dmms[dmm].di->priv))->instances;
-}
-
-static int cleanup(int dmm)
-{
-	return dev_clear(dmm);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg)
-{
-	struct dev_context *devc;
-
-	(void)cg;
-
-	if (sdi->status != SR_ST_ACTIVE)
-		return SR_ERR_DEV_CLOSED;
-
-	if (!(devc = sdi->priv)) {
-		sr_err("sdi->priv was NULL.");
-		return SR_ERR_BUG;
-	}
-
-	switch (id) {
-	case SR_CONF_LIMIT_SAMPLES:
-		devc->limit_samples = g_variant_get_uint64(data);
-		sr_dbg("Setting sample limit to %" PRIu64 ".",
-		       devc->limit_samples);
-		break;
-	case SR_CONF_LIMIT_MSEC:
-		devc->limit_msec = g_variant_get_uint64(data);
-		sr_dbg("Setting time limit to %" PRIu64 "ms.",
-		       devc->limit_msec);
-		break;
-	default:
-		return SR_ERR_NA;
-	}
-
-	return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg)
-{
-	(void)sdi;
-	(void)cg;
-
-	switch (key) {
-	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_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 dev_acquisition_start(const struct sr_dev_inst *sdi,
-				    void *cb_data, int dmm)
-{
-	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;
-	serial_source_add(serial, G_IO_IN, 50,
-		      dmms[dmm].receive_data, (void *)sdi);
-
-	return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
-	return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
-			sdi->conn, LOG_PREFIX);
-}
-
-/* Driver-specific API function wrappers */
-#define HW_INIT(X) \
-static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
-#define HW_CLEANUP(X) \
-static int cleanup_##X(void) { return cleanup(X); }
-#define HW_SCAN(X) \
-static GSList *scan_##X(GSList *options) { return scan(options, X); }
-#define HW_DEV_LIST(X) \
-static GSList *dev_list_##X(void) { return dev_list(X); }
-#define HW_DEV_CLEAR(X) \
-static int dev_clear_##X(void) { return dev_clear(X); }
-#define HW_DEV_ACQUISITION_START(X) \
-static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
-void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
-
-/* Driver structs and API function wrappers */
-#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
-HW_INIT(ID_UPPER) \
-HW_CLEANUP(ID_UPPER) \
-HW_SCAN(ID_UPPER) \
-HW_DEV_LIST(ID_UPPER) \
-HW_DEV_CLEAR(ID_UPPER) \
-HW_DEV_ACQUISITION_START(ID_UPPER) \
-SR_PRIV struct sr_dev_driver ID##_driver_info = { \
-	.name = NAME, \
-	.longname = LONGNAME, \
-	.api_version = 1, \
-	.init = init_##ID_UPPER, \
-	.cleanup = cleanup_##ID_UPPER, \
-	.scan = scan_##ID_UPPER, \
-	.dev_list = dev_list_##ID_UPPER, \
-	.dev_clear = dev_clear_##ID_UPPER, \
-	.config_get = NULL, \
-	.config_set = config_set, \
-	.config_list = config_list, \
-	.dev_open = std_serial_dev_open, \
-	.dev_close = std_serial_dev_close, \
-	.dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
-	.dev_acquisition_stop = dev_acquisition_stop, \
-	.priv = NULL, \
-};
-
-DRV(bbcgm_m2110, BBCGM_M2110, "bbcgm-m2110", "BBC Goertz Metrawatt M2110")
-DRV(digitek_dt4000zc, DIGITEK_DT4000ZC, "digitek-dt4000zc", "Digitek DT4000ZC")
-DRV(tekpower_tp4000zc, TEKPOWER_TP4000ZC, "tekpower-tp4000zc", "TekPower TP4000ZC")
-DRV(metex_me31, METEX_ME31, "metex-me31", "Metex ME-31")
-DRV(peaktech_3410, PEAKTECH_3410, "peaktech-3410", "PeakTech 3410")
-DRV(mastech_mas345, MASTECH_MAS345, "mastech-mas345", "MASTECH MAS345")
-DRV(va_va18b, VA_VA18B, "va-va18b", "V&A VA18B")
-DRV(va_va40b, VA_VA40B, "va-va40b", "V&A VA40B")
-DRV(metex_m3640d, METEX_M3640D, "metex-m3640d", "Metex M-3640D")
-DRV(metex_m4650cr, METEX_M4650CR, "metex-m4650cr", "Metex M-4650CR")
-DRV(peaktech_4370, PEAKTECH_4370, "peaktech-4370", "PeakTech 4370")
-DRV(pce_pce_dm32, PCE_PCE_DM32, "pce-pce-dm32", "PCE PCE-DM32")
-DRV(radioshack_22_168, RADIOSHACK_22_168, "radioshack-22-168", "RadioShack 22-168")
-DRV(radioshack_22_805, RADIOSHACK_22_805, "radioshack-22-805", "RadioShack 22-805")
-DRV(radioshack_22_812, RADIOSHACK_22_812, "radioshack-22-812", "RadioShack 22-812")
-DRV(tecpel_dmm_8061_ser, TECPEL_DMM_8061_SER, "tecpel-dmm-8061-ser", "Tecpel DMM-8061 (UT-D02 cable)")
-DRV(voltcraft_m3650cr, VOLTCRAFT_M3650CR, "voltcraft-m3650cr", "Voltcraft M-3650CR")
-DRV(voltcraft_m3650d, VOLTCRAFT_M3650D, "voltcraft-m3650d", "Voltcraft M-3650D")
-DRV(voltcraft_m4650cr, VOLTCRAFT_M4650CR, "voltcraft-m4650cr", "Voltcraft M-4650CR")
-DRV(voltcraft_me42, VOLTCRAFT_ME42, "voltcraft-me42", "Voltcraft ME-42")
-DRV(voltcraft_vc820_ser, VOLTCRAFT_VC820_SER, "voltcraft-vc820-ser", "Voltcraft VC-820 (UT-D02 cable)")
-DRV(voltcraft_vc830_ser, VOLTCRAFT_VC830_SER, "voltcraft-vc830-ser", "Voltcraft VC-830 (UT-D02 cable)")
-DRV(voltcraft_vc840_ser, VOLTCRAFT_VC840_SER, "voltcraft-vc840-ser", "Voltcraft VC-840 (UT-D02 cable)")
-DRV(uni_t_ut60a_ser, UNI_T_UT60A_SER, "uni-t-ut60a-ser", "UNI-T UT60A (UT-D02 cable)")
-DRV(uni_t_ut60e_ser, UNI_T_UT60E_SER, "uni-t-ut60e-ser", "UNI-T UT60E (UT-D02 cable)")
-DRV(uni_t_ut60g_ser, UNI_T_UT60G_SER, "uni-t-ut60g-ser", "UNI-T UT60G (UT-D02 cable)")
-DRV(uni_t_ut61b_ser, UNI_T_UT61B_SER, "uni-t-ut61b-ser", "UNI-T UT61B (UT-D02 cable)")
-DRV(uni_t_ut61c_ser, UNI_T_UT61C_SER, "uni-t-ut61c-ser", "UNI-T UT61C (UT-D02 cable)")
-DRV(uni_t_ut61d_ser, UNI_T_UT61D_SER, "uni-t-ut61d-ser", "UNI-T UT61D (UT-D02 cable)")
-DRV(uni_t_ut61e_ser, UNI_T_UT61E_SER, "uni-t-ut61e-ser", "UNI-T UT61E (UT-D02 cable)")
-DRV(iso_tech_idm103n, ISO_TECH_IDM103N, "iso-tech-idm103n", "ISO-TECH IDM103N")
-DRV(tenma_72_7745_ser, TENMA_72_7745_SER, "tenma-72-7745-ser", "Tenma 72-7745 (UT-D02 cable)")
-DRV(tenma_72_7750_ser, TENMA_72_7750_SER, "tenma-72-7750-ser", "Tenma 72-7750 (UT-D02 cable)")
diff --git a/hardware/serial-dmm/protocol.h b/hardware/serial-dmm/protocol.h
deleted file mode 100644
index f96e43d..0000000
--- a/hardware/serial-dmm/protocol.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me at 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_SERIAL_DMM_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_SERIAL_DMM_PROTOCOL_H
-
-#define LOG_PREFIX "serial-dmm"
-
-enum {
-	BBCGM_M2110,
-	DIGITEK_DT4000ZC,
-	TEKPOWER_TP4000ZC,
-	METEX_ME31,
-	PEAKTECH_3410,
-	MASTECH_MAS345,
-	VA_VA18B,
-	VA_VA40B,
-	METEX_M3640D,
-	METEX_M4650CR,
-	PEAKTECH_4370,
-	PCE_PCE_DM32,
-	RADIOSHACK_22_168,
-	RADIOSHACK_22_805,
-	RADIOSHACK_22_812,
-	TECPEL_DMM_8061_SER,
-	VOLTCRAFT_M3650CR,
-	VOLTCRAFT_M3650D,
-	VOLTCRAFT_M4650CR,
-	VOLTCRAFT_ME42,
-	VOLTCRAFT_VC820_SER,
-	VOLTCRAFT_VC830_SER,
-	VOLTCRAFT_VC840_SER,
-	UNI_T_UT60A_SER,
-	UNI_T_UT60E_SER,
-	UNI_T_UT60G_SER,
-	UNI_T_UT61B_SER,
-	UNI_T_UT61C_SER,
-	UNI_T_UT61D_SER,
-	UNI_T_UT61E_SER,
-	ISO_TECH_IDM103N,
-	TENMA_72_7745_SER,
-	TENMA_72_7750_SER,
-};
-
-struct dmm_info {
-	/** Manufacturer/brand. */
-	char *vendor;
-	/** Model. */
-	char *device;
-	/** serialconn string. */
-	char *conn;
-	/** Baud rate. */
-	uint32_t baudrate;
-	/** Packet size in bytes. */
-	int packet_size;
-	/** Packet request function. */
-	int (*packet_request)(struct sr_serial_dev_inst *);
-	/** Packet validation function. */
-	gboolean (*packet_valid)(const uint8_t *);
-	/** Packet parsing function. */
-	int (*packet_parse)(const uint8_t *, float *,
-			    struct sr_datafeed_analog *, void *);
-	/** */
-	void (*dmm_details)(struct sr_datafeed_analog *, void *);
-	/** libsigrok driver info struct. */
-	struct sr_dev_driver *di;
-	/** Data reception function. */
-	int (*receive_data)(int, int, void *);
-};
-
-extern SR_PRIV struct dmm_info dmms[];
-
-#define DMM_BUFSIZE 256
-
-/** Private, per-device-instance driver context. */
-struct dev_context {
-	/** The current sampling limit (in number of samples). */
-	uint64_t limit_samples;
-
-	/** The time limit (in milliseconds). */
-	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;
-
-	int64_t starttime;
-
-	uint8_t buf[DMM_BUFSIZE];
-	int bufoffset;
-	int buflen;
-};
-
-SR_PRIV int receive_data_BBCGM_M2110(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_DIGITEK_DT4000ZC(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TEKPOWER_TP4000ZC(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_METEX_ME31(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_PEAKTECH_3410(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_MASTECH_MAS345(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VA_VA18B(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VA_VA40B(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_METEX_M3640D(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_METEX_M4650CR(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_PEAKTECH_4370(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_PCE_PCE_DM32(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_RADIOSHACK_22_168(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_RADIOSHACK_22_805(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_RADIOSHACK_22_812(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TECPEL_DMM_8061_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_M3650CR(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_M3650D(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_M4650CR(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_ME42(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC820_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC830_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC840_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60A_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60E_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60G_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61B_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61C_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61D_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61E_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_ISO_TECH_IDM103N(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TENMA_72_7745_SER(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TENMA_72_7750_SER(int fd, int revents, void *cb_data);
-
-#endif
diff --git a/hardware/sysclk-lwla/api.c b/hardware/sysclk-lwla/api.c
deleted file mode 100644
index 12a1e08..0000000
--- a/hardware/sysclk-lwla/api.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta at 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"
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include <glib.h>
-#include <libusb.h>
-#include <stdlib.h>
-#include <string.h>
-
-static const int32_t hwopts[] = {
-	SR_CONF_CONN,
-};
-
-static const int32_t hwcaps[] = {
-	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_EXTERNAL_CLOCK,
-	SR_CONF_CLOCK_EDGE,
-	SR_CONF_TRIGGER_TYPE,
-	SR_CONF_TRIGGER_SOURCE,
-	SR_CONF_TRIGGER_SLOPE,
-	SR_CONF_LIMIT_MSEC,
-	SR_CONF_LIMIT_SAMPLES,
-};
-
-/* The hardware supports more samplerates than these, but these are the
- * options hardcoded into the vendor's Windows GUI.
- */
-static const uint64_t samplerates[] = {
-	SR_MHZ(125), SR_MHZ(100),
-	SR_MHZ(50),  SR_MHZ(20),  SR_MHZ(10),
-	SR_MHZ(5),   SR_MHZ(2),   SR_MHZ(1),
-	SR_KHZ(500), SR_KHZ(200), SR_KHZ(100),
-	SR_KHZ(50),  SR_KHZ(20),  SR_KHZ(10),
-	SR_KHZ(5),   SR_KHZ(2),   SR_KHZ(1),
-	SR_HZ(500),  SR_HZ(200),  SR_HZ(100),
-};
-
-/* Names assigned to available trigger sources.  Indices must match
- * trigger_source enum values.
- */
-static const char *const trigger_source_names[] = { "CH", "TRG" };
-
-/* Names assigned to available trigger slope choices.  Indices must
- * match the signal_edge enum values.
- */
-static const char *const signal_edge_names[] = { "r", "f" };
-
-SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info;
-static struct sr_dev_driver *const di = &sysclk_lwla_driver_info;
-
-static int init(struct sr_context *sr_ctx)
-{
-	return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *gen_channel_list(int num_channels)
-{
-	GSList *list;
-	struct sr_channel *ch;
-	int i;
-	char name[8];
-
-	list = NULL;
-
-	for (i = num_channels; i > 0; --i) {
-		/* The LWLA series simply number channels from CH1 to CHxx. */
-		g_snprintf(name, sizeof(name), "CH%d", i);
-
-		ch = sr_channel_new(i - 1, SR_CHANNEL_LOGIC, TRUE, name);
-		list = g_slist_prepend(list, ch);
-	}
-
-	return list;
-}
-
-static struct sr_dev_inst *dev_inst_new(int device_index)
-{
-	struct sr_dev_inst *sdi;
-	struct dev_context *devc;
-
-	/* Allocate memory for our private driver context. */
-	devc = g_try_new0(struct dev_context, 1);
-	if (!devc) {
-		sr_err("Device context malloc failed.");
-		return NULL;
-	}
-
-	/* Register the device with libsigrok. */
-	sdi = sr_dev_inst_new(device_index, SR_ST_INACTIVE,
-			      VENDOR_NAME, MODEL_NAME, NULL);
-	if (!sdi) {
-		sr_err("Failed to instantiate device.");
-		g_free(devc);
-		return NULL;
-	}
-
-	/* Enable all channels to match the default channel configuration. */
-	devc->channel_mask = ALL_CHANNELS_MASK;
-	devc->samplerate = DEFAULT_SAMPLERATE;
-
-	sdi->priv = devc;
-	sdi->channels = gen_channel_list(NUM_CHANNELS);
-
-	return sdi;
-}
-
-static GSList *scan(GSList *options)
-{
-	GSList *usb_devices, *devices, *node;
-	struct drv_context *drvc;
-	struct sr_dev_inst *sdi;
-	struct sr_usb_dev_inst *usb;
-	struct sr_config *src;
-	const char *conn;
-	int device_index;
-
-	drvc = di->priv;
-	conn = USB_VID_PID;
-
-	for (node = options; node != NULL; node = node->next) {
-		src = node->data;
-		if (src->key == SR_CONF_CONN) {
-			conn = g_variant_get_string(src->data, NULL);
-			break;
-		}
-	}
-	usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
-	devices = NULL;
-	device_index = g_slist_length(drvc->instances);
-
-	for (node = usb_devices; node != NULL; node = node->next) {
-		usb = node->data;
-
-		/* Create sigrok device instance. */
-		sdi = dev_inst_new(device_index);
-		if (!sdi) {
-			sr_usb_dev_inst_free(usb);
-			continue;
-		}
-		sdi->driver = di;
-		sdi->inst_type = SR_INST_USB;
-		sdi->conn = usb;
-
-		/* Register device instance with driver. */
-		drvc->instances = g_slist_append(drvc->instances, sdi);
-		devices = g_slist_append(devices, sdi);
-	}
-
-	g_slist_free(usb_devices);
-
-	return devices;
-}
-
-static GSList *dev_list(void)
-{
-	struct drv_context *drvc;
-
-	drvc = di->priv;
-
-	return drvc->instances;
-}
-
-static void clear_dev_context(void *priv)
-{
-	struct dev_context *devc;
-
-	devc = priv;
-
-	sr_dbg("Device context cleared.");
-
-	lwla_free_acquisition_state(devc->acquisition);
-	g_free(devc);
-}
-
-static int dev_clear(void)
-{
-	return std_dev_clear(di, &clear_dev_context);
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
-	struct drv_context *drvc;
-	struct sr_usb_dev_inst *usb;
-	int ret;
-
-	drvc = di->priv;
-
-	if (!drvc) {
-		sr_err("Driver was not initialized.");
-		return SR_ERR;
-	}
-
-	usb = sdi->conn;
-
-	ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
-	if (ret != SR_OK)
-		return ret;
-
-	ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
-	if (ret < 0) {
-		sr_err("Failed to claim interface: %s.",
-			libusb_error_name(ret));
-		return SR_ERR;
-	}
-
-	sdi->status = SR_ST_INITIALIZING;
-
-	ret = lwla_init_device(sdi);
-
-	if (ret == SR_OK)
-		sdi->status = SR_ST_ACTIVE;
-
-	return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
-	struct sr_usb_dev_inst *usb;
-
-	if (!di->priv) {
-		sr_err("Driver was not initialized.");
-		return SR_ERR;
-	}
-
-	usb = sdi->conn;
-	if (!usb->devhdl)
-		return SR_OK;
-
-	sdi->status = SR_ST_INACTIVE;
-
-	/* Trigger download of the shutdown bitstream. */
-	if (lwla_set_clock_config(sdi) != SR_OK)
-		sr_err("Unable to shut down device.");
-
-	libusb_release_interface(usb->devhdl, USB_INTERFACE);
-	libusb_close(usb->devhdl);
-
-	usb->devhdl = NULL;
-
-	return SR_OK;
-}
-
-static int cleanup(void)
-{
-	return dev_clear();
-}
-
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
-		      const struct sr_channel_group *cg)
-{
-	struct dev_context *devc;
-	size_t idx;
-
-	(void)cg;
-
-	if (!sdi)
-		return SR_ERR_ARG;
-
-	devc = sdi->priv;
-
-	switch (key) {
-	case SR_CONF_SAMPLERATE:
-		*data = g_variant_new_uint64(devc->samplerate);
-		break;
-	case SR_CONF_LIMIT_MSEC:
-		*data = g_variant_new_uint64(devc->limit_msec);
-		break;
-	case SR_CONF_LIMIT_SAMPLES:
-		*data = g_variant_new_uint64(devc->limit_samples);
-		break;
-	case SR_CONF_EXTERNAL_CLOCK:
-		*data = g_variant_new_boolean(devc->cfg_clock_source
-						== CLOCK_EXT_CLK);
-		break;
-	case SR_CONF_CLOCK_EDGE:
-		idx = devc->cfg_clock_edge;
-		if (idx >= G_N_ELEMENTS(signal_edge_names))
-			return SR_ERR_BUG;
-		*data = g_variant_new_string(signal_edge_names[idx]);
-		break;
-	case SR_CONF_TRIGGER_SOURCE:
-		idx = devc->cfg_trigger_source;
-		if (idx >= G_N_ELEMENTS(trigger_source_names))
-			return SR_ERR_BUG;
-		*data = g_variant_new_string(trigger_source_names[idx]);
-		break;
-	case SR_CONF_TRIGGER_SLOPE:
-		idx = devc->cfg_trigger_slope;
-		if (idx >= G_N_ELEMENTS(signal_edge_names))
-			return SR_ERR_BUG;
-		*data = g_variant_new_string(signal_edge_names[idx]);
-		break;
-	default:
-		return SR_ERR_NA;
-	}
-
-	return SR_OK;
-}
-
-/* Helper for mapping a string-typed configuration value to an index
- * within a table of possible values.
- */
-static int lookup_index(GVariant *value, const char *const *table, int len)
-{
-	const char *entry;
-	int i;
-
-	entry = g_variant_get_string(value, NULL);
-	if (!entry)
-		return -1;
-
-	/* Linear search is fine for very small tables. */
-	for (i = 0; i < len; ++i) {
-		if (strcmp(entry, table[i]) == 0)
-			return i;
-	}
-	return -1;
-}
-
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
-		      const struct sr_channel_group *cg)
-{
-	uint64_t value;
-	struct dev_context *devc;
-	int idx;
-
-	(void)cg;
-
-	devc = sdi->priv;
-	if (!devc)
-		return SR_ERR_DEV_CLOSED;
-
-	switch (key) {
-	case SR_CONF_SAMPLERATE:
-		value = g_variant_get_uint64(data);
-		if (value < samplerates[G_N_ELEMENTS(samplerates) - 1]
-				|| value > samplerates[0])
-			return SR_ERR_SAMPLERATE;
-		devc->samplerate = value;
-		break;
-	case SR_CONF_LIMIT_MSEC:
-		value = g_variant_get_uint64(data);
-		if (value > MAX_LIMIT_MSEC)
-			return SR_ERR_ARG;
-		devc->limit_msec = value;
-		break;
-	case SR_CONF_LIMIT_SAMPLES:
-		value = g_variant_get_uint64(data);
-		if (value > MAX_LIMIT_SAMPLES)
-			return SR_ERR_ARG;
-		devc->limit_samples = value;
-		break;
-	case SR_CONF_EXTERNAL_CLOCK:
-		devc->cfg_clock_source = (g_variant_get_boolean(data))
-			? CLOCK_EXT_CLK : CLOCK_INTERNAL;
-		break;
-	case SR_CONF_CLOCK_EDGE:
-		idx = lookup_index(data, signal_edge_names,
-				   G_N_ELEMENTS(signal_edge_names));
-		if (idx < 0)
-			return SR_ERR_ARG;
-		devc->cfg_clock_edge = idx;
-		break;
-	case SR_CONF_TRIGGER_SOURCE:
-		idx = lookup_index(data, trigger_source_names,
-				   G_N_ELEMENTS(trigger_source_names));
-		if (idx < 0)
-			return SR_ERR_ARG;
-		devc->cfg_trigger_source = idx;
-		break;
-	case SR_CONF_TRIGGER_SLOPE:
-		idx = lookup_index(data, signal_edge_names,
-				   G_N_ELEMENTS(signal_edge_names));
-		if (idx < 0)
-			return SR_ERR_ARG;
-		devc->cfg_trigger_slope = idx;
-		break;
-	default:
-		return SR_ERR_NA;
-	}
-
-	return SR_OK;
-}
-
-static int config_channel_set(const struct sr_dev_inst *sdi,
-			    struct sr_channel *ch, unsigned int changes)
-{
-	uint64_t channel_bit;
-	uint64_t trigger_mask;
-	uint64_t trigger_values;
-	uint64_t trigger_edge_mask;
-	struct dev_context *devc;
-
-	devc = sdi->priv;
-	if (!devc)
-		return SR_ERR_DEV_CLOSED;
-
-	if (ch->index < 0 || ch->index >= NUM_CHANNELS) {
-		sr_err("Channel index %d out of range.", ch->index);
-		return SR_ERR_BUG;
-	}
-	channel_bit = (uint64_t)1 << ch->index;
-
-	if ((changes & SR_CHANNEL_SET_ENABLED) != 0) {
-		/* Enable or disable input channel for this channel. */
-		if (ch->enabled)
-			devc->channel_mask |= channel_bit;
-		else
-			devc->channel_mask &= ~channel_bit;
-	}
-
-	if ((changes & SR_CHANNEL_SET_TRIGGER) != 0) {
-		trigger_mask = devc->trigger_mask & ~channel_bit;
-		trigger_values = devc->trigger_values & ~channel_bit;
-		trigger_edge_mask = devc->trigger_edge_mask & ~channel_bit;
-
-		if (ch->trigger && ch->trigger[0] != '\0') {
-			if (ch->trigger[1] != '\0') {
-				sr_warn("Trigger configuration \"%s\" with "
-					"multiple stages is not supported.",
-					ch->trigger);
-				return SR_ERR_ARG;
-			}
-			/* Enable trigger for this channel. */
-			trigger_mask |= channel_bit;
-
-			/* Configure edge mask and trigger value. */
-			switch (ch->trigger[0]) {
-			case '1': trigger_values |= channel_bit;
-			case '0': break;
-
-			case 'r': trigger_values |= channel_bit;
-			case 'f': trigger_edge_mask |= channel_bit;
-				  break;
-			default:
-				sr_warn("Trigger type '%c' is not supported.",
-					ch->trigger[0]);
-				return SR_ERR_ARG;
-			}
-		}
-		/* Store validated trigger setup. */
-		devc->trigger_mask = trigger_mask;
-		devc->trigger_values = trigger_values;
-		devc->trigger_edge_mask = trigger_edge_mask;
-	}
-
-	return SR_OK;
-}
-
-static int config_commit(const struct sr_dev_inst *sdi)
-{
-	if (sdi->status != SR_ST_ACTIVE) {
-		sr_err("Device not ready (status %d).", (int)sdi->status);
-		return SR_ERR;
-	}
-
-	return lwla_set_clock_config(sdi);
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
-		       const struct sr_channel_group *cg)
-{
-	GVariant *gvar;
-	GVariantBuilder gvb;
-
-	(void)sdi;
-	(void)cg;
-
-	switch (key) {
-	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, G_N_ELEMENTS(hwopts), sizeof(int32_t));
-		break;
-	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, G_N_ELEMENTS(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, G_N_ELEMENTS(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_TYPES);
-		break;
-	case SR_CONF_TRIGGER_SOURCE:
-		*data = g_variant_new_strv(trigger_source_names,
-					   G_N_ELEMENTS(trigger_source_names));
-		break;
-	case SR_CONF_TRIGGER_SLOPE:
-	case SR_CONF_CLOCK_EDGE:
-		*data = g_variant_new_strv(signal_edge_names,
-					   G_N_ELEMENTS(signal_edge_names));
-		break;
-	default:
-		return SR_ERR_NA;
-	}
-
-	return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
-	struct drv_context *drvc;
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-	int ret;
-
-	(void)cb_data;
-
-	if (sdi->status != SR_ST_ACTIVE)
-		return SR_ERR_DEV_CLOSED;
-
-	devc = sdi->priv;
-	drvc = di->priv;
-
-	if (devc->acquisition) {
-		sr_err("Acquisition still in progress?");
-		return SR_ERR;
-	}
-	acq = lwla_alloc_acquisition_state();
-	if (!acq)
-		return SR_ERR_MALLOC;
-
-	devc->stopping_in_progress = FALSE;
-	devc->transfer_error = FALSE;
-
-	sr_info("Starting acquisition.");
-
-	devc->acquisition = acq;
-	ret = lwla_setup_acquisition(sdi);
-	if (ret != SR_OK) {
-		sr_err("Failed to set up acquisition.");
-		devc->acquisition = NULL;
-		lwla_free_acquisition_state(acq);
-		return ret;
-	}
-
-	ret = lwla_start_acquisition(sdi);
-	if (ret != SR_OK) {
-		sr_err("Failed to start acquisition.");
-		devc->acquisition = NULL;
-		lwla_free_acquisition_state(acq);
-		return ret;
-	}
-	usb_source_add(drvc->sr_ctx, 100, &lwla_receive_data,
-		       (struct sr_dev_inst *)sdi);
-
-	sr_info("Waiting for data.");
-
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(sdi, LOG_PREFIX);
-
-	return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
-	(void)cb_data;
-
-	if (sdi->status != SR_ST_ACTIVE)
-		return SR_ERR_DEV_CLOSED;
-
-	sr_dbg("Stopping acquisition.");
-
-	sdi->status = SR_ST_STOPPING;
-
-	return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info = {
-	.name = "sysclk-lwla",
-	.longname = "SysClk LWLA series",
-	.api_version = 1,
-	.init = init,
-	.cleanup = cleanup,
-	.scan = scan,
-	.dev_list = dev_list,
-	.dev_clear = dev_clear,
-	.config_get = config_get,
-	.config_set = config_set,
-	.config_channel_set = config_channel_set,
-	.config_commit = config_commit,
-	.config_list = config_list,
-	.dev_open = dev_open,
-	.dev_close = dev_close,
-	.dev_acquisition_start = dev_acquisition_start,
-	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
-};
diff --git a/hardware/sysclk-lwla/lwla.h b/hardware/sysclk-lwla/lwla.h
deleted file mode 100644
index 94db07e..0000000
--- a/hardware/sysclk-lwla/lwla.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta at 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_SYSCLK_LWLA_LWLA_H
-#define LIBSIGROK_HARDWARE_SYSCLK_LWLA_LWLA_H
-
-#include "libsigrok.h"
-#include <stdint.h>
-#include <libusb.h>
-#include <glib.h>
-
-struct sr_usb_dev_inst;
-
-/* Rotate argument n bits to the left.
- * This construct is an idiom recognized by GCC as bit rotation.
- */
-#define LROTATE(a, n) (((a) << (n)) | ((a) >> (CHAR_BIT * sizeof(a) - (n))))
-
-/* Convert 16-bit little endian LWLA protocol word to machine word order. */
-#define LWLA_TO_UINT16(val) GUINT16_FROM_LE(val)
-
-/* Convert 32-bit mixed endian LWLA protocol word to machine word order. */
-#define LWLA_TO_UINT32(val) LROTATE(GUINT32_FROM_LE(val), 16)
-
-/* Convert 16-bit argument to LWLA protocol word. */
-#define LWLA_WORD(val) GUINT16_TO_LE(val)
-
-/* Extract 16-bit units in mixed endian order from 32/64-bit value. */
-#define LWLA_WORD_0(val) GUINT16_TO_LE(((val) >> 16) & 0xFFFF)
-#define LWLA_WORD_1(val) GUINT16_TO_LE((val) & 0xFFFF)
-#define LWLA_WORD_2(val) GUINT16_TO_LE(((val) >> 48) & 0xFFFF)
-#define LWLA_WORD_3(val) GUINT16_TO_LE(((val) >> 32) & 0xFFFF)
-
-/** USB device end points.
- */
-enum {
-	EP_COMMAND   = 2,
-	EP_BITSTREAM = 4,
-	EP_REPLY     = 6 | LIBUSB_ENDPOINT_IN
-};
-
-/** LWLA protocol command ID codes.
- */
-enum {
-	CMD_READ_REG	= 1,
-	CMD_WRITE_REG	= 2,
-	CMD_READ_MEM	= 6,
-	CMD_CAP_SETUP	= 7,
-	CMD_CAP_STATUS	= 8,
-};
-
-/** LWLA capture state flags.
- */
-enum {
-	STATUS_CAPTURING = 1 << 1,
-	STATUS_TRIGGERED = 1 << 4,
-	STATUS_MEM_AVAIL = 1 << 5,
-	STATUS_FLAG_MASK = 0x3F
-};
-
-/** LWLA register addresses.
- */
-enum {
-	REG_MEM_CTRL2   = 0x1074, /* capture buffer control ??? */
-	REG_MEM_FILL    = 0x1078, /* capture buffer fill level */
-	REG_MEM_CTRL4   = 0x107C, /* capture buffer control ??? */
-
-	REG_DIV_BYPASS  = 0x1094, /* bypass clock divider flag */
-
-	REG_CMD_CTRL1   = 0x10B0, /* command control ??? */
-	REG_CMD_CTRL2   = 0x10B4, /* command control ??? */
-	REG_CMD_CTRL3   = 0x10B8, /* command control ??? */
-	REG_CMD_CTRL4   = 0x10BC, /* command control ??? */
-
-	REG_FREQ_CH1    = 0x10C0, /* channel 1 live frequency */
-	REG_FREQ_CH2    = 0x10C4, /* channel 2 live frequency */
-	REG_FREQ_CH3    = 0x10C8, /* channel 3 live frequency */
-	REG_FREQ_CH4    = 0x10CC, /* channel 4 live frequency */
-};
-
-/** Register/value pair.
- */
-struct regval_pair {
-	unsigned int reg;
-	unsigned int val;
-};
-
-SR_PRIV int lwla_send_bitstream(const struct sr_usb_dev_inst *usb,
-				const char *basename);
-
-SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
-			      const uint16_t *command, int cmd_len);
-
-SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
-			       uint32_t *reply, int reply_len, int expect_len);
-
-SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
-			  uint16_t reg, uint32_t *value);
-
-SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
-			   uint16_t reg, uint32_t value);
-
-SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
-			    const struct regval_pair *regvals, int count);
-
-#endif /* !LIBSIGROK_HARDWARE_SYSCLK_LWLA_LWLA_H */
diff --git a/hardware/sysclk-lwla/protocol.c b/hardware/sysclk-lwla/protocol.c
deleted file mode 100644
index 7bb1948..0000000
--- a/hardware/sysclk-lwla/protocol.c
+++ /dev/null
@@ -1,985 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta at 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"
-#include <string.h>
-
-/* Bit mask for the RLE repeat-count-follows flag. */
-#define RLE_FLAG_LEN_FOLLOWS ((uint64_t)1 << 35)
-
-/* Start address of capture status memory area to read. */
-#define CAP_STAT_ADDR 5
-
-/* Number of 64-bit words read from the capture status memory. */
-#define CAP_STAT_LEN 5
-
-/* The bitstream filenames are indexed by the clock_config enumeration.
- */
-static const char bitstream_map[][32] = {
-	"sysclk-lwla1034-off.rbf",
-	"sysclk-lwla1034-int.rbf",
-	"sysclk-lwla1034-extpos.rbf",
-	"sysclk-lwla1034-extneg.rbf",
-};
-
-/* Submit an already filled-in USB transfer.
- */
-static int submit_transfer(struct dev_context *devc,
-			   struct libusb_transfer *xfer)
-{
-	int ret;
-
-	ret = libusb_submit_transfer(xfer);
-
-	if (ret != 0) {
-		sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
-		devc->transfer_error = TRUE;
-		return SR_ERR;
-	}
-
-	return SR_OK;
-}
-
-/* Set up the LWLA in preparation for an acquisition session.
- */
-static int capture_setup(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-	uint64_t divider_count;
-	uint64_t trigger_mask;
-	uint64_t memory_limit;
-	uint16_t command[3 + 10*4];
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	command[0] = LWLA_WORD(CMD_CAP_SETUP);
-	command[1] = LWLA_WORD(0); /* address */
-	command[2] = LWLA_WORD(10); /* length */
-
-	command[3] = LWLA_WORD_0(devc->channel_mask);
-	command[4] = LWLA_WORD_1(devc->channel_mask);
-	command[5] = LWLA_WORD_2(devc->channel_mask);
-	command[6] = LWLA_WORD_3(devc->channel_mask);
-
-	/* Set the clock divide counter maximum for samplerates of up to
-	 * 100 MHz. At the highest samplerate of 125 MHz the clock divider
-	 * is bypassed.
-	 */
-	if (!acq->bypass_clockdiv && devc->samplerate > 0)
-		divider_count = SR_MHZ(100) / devc->samplerate - 1;
-	else
-		divider_count = 0;
-
-	command[7]  = LWLA_WORD_0(divider_count);
-	command[8]  = LWLA_WORD_1(divider_count);
-	command[9]  = LWLA_WORD_2(divider_count);
-	command[10] = LWLA_WORD_3(divider_count);
-
-	command[11] = LWLA_WORD_0(devc->trigger_values);
-	command[12] = LWLA_WORD_1(devc->trigger_values);
-	command[13] = LWLA_WORD_2(devc->trigger_values);
-	command[14] = LWLA_WORD_3(devc->trigger_values);
-
-	command[15] = LWLA_WORD_0(devc->trigger_edge_mask);
-	command[16] = LWLA_WORD_1(devc->trigger_edge_mask);
-	command[17] = LWLA_WORD_2(devc->trigger_edge_mask);
-	command[18] = LWLA_WORD_3(devc->trigger_edge_mask);
-
-	trigger_mask = devc->trigger_mask;
-	/* Set bits to select external TRG input edge. */
-	if (devc->cfg_trigger_source == TRIGGER_EXT_TRG)
-		switch (devc->cfg_trigger_slope) {
-		case EDGE_POSITIVE: trigger_mask |= (uint64_t)1 << 35; break; 
-		case EDGE_NEGATIVE: trigger_mask |= (uint64_t)1 << 34; break; 
-		}
-
-	command[19] = LWLA_WORD_0(trigger_mask);
-	command[20] = LWLA_WORD_1(trigger_mask);
-	command[21] = LWLA_WORD_2(trigger_mask);
-	command[22] = LWLA_WORD_3(trigger_mask);
-
-	/* Set the capture memory full threshold. This is slightly less
-	 * than the actual maximum, most likely in order to compensate for
-	 * pipeline latency.
-	 */
-	memory_limit = MEMORY_DEPTH - 16;
-
-	command[23] = LWLA_WORD_0(memory_limit);
-	command[24] = LWLA_WORD_1(memory_limit);
-	command[25] = LWLA_WORD_2(memory_limit);
-	command[26] = LWLA_WORD_3(memory_limit);
-
-	/* Fill remaining 64-bit words with zeroes. */
-	memset(&command[27], 0, 16 * sizeof(uint16_t));
-
-	return lwla_send_command(sdi->conn, command, G_N_ELEMENTS(command));
-}
-
-/* Issue a register write command as an asynchronous USB transfer.
- */
-static int issue_write_reg(const struct sr_dev_inst *sdi,
-			   unsigned int reg, unsigned int value)
-{
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	acq->xfer_buf_out[0] = LWLA_WORD(CMD_WRITE_REG);
-	acq->xfer_buf_out[1] = LWLA_WORD(reg);
-	acq->xfer_buf_out[2] = LWLA_WORD_0(value);
-	acq->xfer_buf_out[3] = LWLA_WORD_1(value);
-
-	acq->xfer_out->length = 4 * sizeof(uint16_t);
-
-	return submit_transfer(devc, acq->xfer_out);
-}
-
-/* Issue a register write command as an asynchronous USB transfer for the
- * next register/value pair of the currently active register write sequence.
- */
-static int issue_next_write_reg(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct regval_pair *regval;
-	int ret;
-
-	devc = sdi->priv;
-
-	if (devc->reg_write_pos >= devc->reg_write_len) {
-		sr_err("Already written all registers in sequence.");
-		return SR_ERR_BUG;
-	}
-	regval = &devc->reg_write_seq[devc->reg_write_pos];
-
-	ret = issue_write_reg(sdi, regval->reg, regval->val);
-	if (ret != SR_OK)
-		return ret;
-
-	++devc->reg_write_pos;
-	return SR_OK;
-}
-
-/* Issue a capture status request as an asynchronous USB transfer.
- */
-static void request_capture_status(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	acq->xfer_buf_out[0] = LWLA_WORD(CMD_CAP_STATUS);
-	acq->xfer_buf_out[1] = LWLA_WORD(CAP_STAT_ADDR);
-	acq->xfer_buf_out[2] = LWLA_WORD(CAP_STAT_LEN);
-
-	acq->xfer_out->length = 3 * sizeof(uint16_t);
-
-	if (submit_transfer(devc, acq->xfer_out) == SR_OK)
-		devc->state = STATE_STATUS_REQUEST;
-}
-
-/* Issue a request for the capture buffer fill level as
- * an asynchronous USB transfer.
- */
-static void request_capture_length(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_REG);
-	acq->xfer_buf_out[1] = LWLA_WORD(REG_MEM_FILL);
-
-	acq->xfer_out->length = 2 * sizeof(uint16_t);
-
-	if (submit_transfer(devc, acq->xfer_out) == SR_OK)
-		devc->state = STATE_LENGTH_REQUEST;
-}
-
-/* Initiate the capture memory read operation:  Reset the acquisition state
- * and start a sequence of register writes in order to set up the device for
- * reading from the capture buffer.
- */
-static void issue_read_start(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-	struct regval_pair *regvals;
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	/* Reset RLE state. */
-	acq->rle = RLE_STATE_DATA;
-	acq->sample  = 0;
-	acq->run_len = 0;
-
-	acq->samples_done = 0;
-
-	/* For some reason, the start address is 4 rather than 0. */
-	acq->mem_addr_done = 4;
-	acq->mem_addr_next = 4;
-	acq->mem_addr_stop = acq->mem_addr_fill;
-
-	/* Sample position in the packet output buffer. */
-	acq->out_index = 0;
-
-	regvals = devc->reg_write_seq;
-
-	regvals[0].reg = REG_DIV_BYPASS;
-	regvals[0].val = 1;
-
-	regvals[1].reg = REG_MEM_CTRL2;
-	regvals[1].val = 2;
-
-	regvals[2].reg = REG_MEM_CTRL4;
-	regvals[2].val = 4;
-
-	devc->reg_write_pos = 0;
-	devc->reg_write_len = 3;
-
-	if (issue_next_write_reg(sdi) == SR_OK)
-		devc->state = STATE_READ_PREPARE;
-}
-
-/* Issue a command as an asynchronous USB transfer which returns the device
- * to normal state after a read operation.  Sets a new device context state
- * on success.
- */
-static void issue_read_end(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-
-	devc = sdi->priv;
-
-	if (issue_write_reg(sdi, REG_DIV_BYPASS, 0) == SR_OK)
-		devc->state = STATE_READ_END;
-}
-
-/* Decode an incoming reponse to a buffer fill level request and act on it
- * as appropriate.  Note that this function changes the device context state.
- */
-static void process_capture_length(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	if (acq->xfer_in->actual_length != 4) {
-		sr_err("Received size %d doesn't match expected size 4.",
-		       acq->xfer_in->actual_length);
-		devc->transfer_error = TRUE;
-		return;
-	}
-	acq->mem_addr_fill = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
-
-	sr_dbg("%zu words in capture buffer.", acq->mem_addr_fill);
-
-	if (acq->mem_addr_fill > 0 && sdi->status == SR_ST_ACTIVE)
-		issue_read_start(sdi);
-	else
-		issue_read_end(sdi);
-}
-
-/* Initiate a sequence of register write commands with the effect of
- * cancelling a running capture operation.  This sets a new device state
- * if issuing the first command succeeds.
- */
-static void issue_stop_capture(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct regval_pair *regvals;
-
-	devc = sdi->priv;
-
-	if (devc->stopping_in_progress)
-		return;
-
-	regvals = devc->reg_write_seq;
-
-	regvals[0].reg = REG_CMD_CTRL2;
-	regvals[0].val = 10;
-
-	regvals[1].reg = REG_CMD_CTRL3;
-	regvals[1].val = 0;
-
-	regvals[2].reg = REG_CMD_CTRL4;
-	regvals[2].val = 0;
-
-	regvals[3].reg = REG_CMD_CTRL1;
-	regvals[3].val = 0;
-
-	regvals[4].reg = REG_DIV_BYPASS;
-	regvals[4].val = 0;
-
-	devc->reg_write_pos = 0;
-	devc->reg_write_len = 5;
-
-	if (issue_next_write_reg(sdi) == SR_OK) {
-		devc->stopping_in_progress = TRUE;
-		devc->state = STATE_STOP_CAPTURE;
-	}
-}
-
-/* Decode an incoming capture status reponse and act on it as appropriate.
- * Note that this function changes the device state.
- */
-static void process_capture_status(const struct sr_dev_inst *sdi)
-{
-	uint64_t duration;
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-	unsigned int mem_fill;
-	unsigned int flags;
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	if (acq->xfer_in->actual_length != CAP_STAT_LEN * 8) {
-		sr_err("Received size %d doesn't match expected size %d.",
-		       acq->xfer_in->actual_length, CAP_STAT_LEN * 8);
-		devc->transfer_error = TRUE;
-		return;
-	}
-
-	/* TODO: Find out the actual bit width of these fields as stored
-	 * in the FPGA.  These fields are definitely less than 64 bit wide
-	 * internally, and the unused bits occasionally even contain garbage.
-	 */
-	mem_fill = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
-	duration = LWLA_TO_UINT32(acq->xfer_buf_in[4]);
-	flags    = LWLA_TO_UINT32(acq->xfer_buf_in[8]) & STATUS_FLAG_MASK;
-
-	/* The LWLA1034 runs at 125 MHz if the clock divider is bypassed.
-	 * However, the time base used for the duration is apparently not
-	 * adjusted for this "boost" mode.  Whereas normally the duration
-	 * unit is 1 ms, it is 0.8 ms when the clock divider is bypassed.
-	 * As 0.8 = 100 MHz / 125 MHz, it seems that the internal cycle
-	 * counter period is the same as at the 100 MHz setting.
-	 */
-	if (acq->bypass_clockdiv)
-		acq->duration_now = duration * 4 / 5;
-	else
-		acq->duration_now = duration;
-
-	sr_spew("Captured %u words, %" PRIu64 " ms, flags 0x%02X.",
-		mem_fill, acq->duration_now, flags);
-
-	if ((flags & STATUS_TRIGGERED) > (acq->capture_flags & STATUS_TRIGGERED))
-		sr_info("Capture triggered.");
-
-	acq->capture_flags = flags;
-
-	if (acq->duration_now >= acq->duration_max) {
-		sr_dbg("Time limit reached, stopping capture.");
-		issue_stop_capture(sdi);
-		return;
-	}
-	devc->state = STATE_STATUS_WAIT;
-
-	if ((acq->capture_flags & STATUS_TRIGGERED) == 0) {
-		sr_spew("Waiting for trigger.");
-	} else if ((acq->capture_flags & STATUS_MEM_AVAIL) == 0) {
-		sr_dbg("Capture memory filled.");
-		request_capture_length(sdi);
-	} else if ((acq->capture_flags & STATUS_CAPTURING) != 0) {
-		sr_spew("Sampling in progress.");
-	}
-}
-
-/* Issue a capture buffer read request as an asynchronous USB transfer.
- * The address and size of the memory area to read are derived from the
- * current acquisition state.
- */
-static void request_read_mem(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-	size_t count;
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	if (acq->mem_addr_next >= acq->mem_addr_stop)
-		return;
-
-	/* Always read a multiple of 8 device words. */
-	count = (acq->mem_addr_stop - acq->mem_addr_next + 7) / 8 * 8;
-	count = MIN(count, READ_CHUNK_LEN);
-
-	acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_MEM);
-	acq->xfer_buf_out[1] = LWLA_WORD_0(acq->mem_addr_next);
-	acq->xfer_buf_out[2] = LWLA_WORD_1(acq->mem_addr_next);
-	acq->xfer_buf_out[3] = LWLA_WORD_0(count);
-	acq->xfer_buf_out[4] = LWLA_WORD_1(count);
-
-	acq->xfer_out->length = 5 * sizeof(uint16_t);
-
-	if (submit_transfer(devc, acq->xfer_out) == SR_OK) {
-		acq->mem_addr_next += count;
-		devc->state = STATE_READ_REQUEST;
-	}
-}
-
-/* Demangle and decompress incoming sample data from the capture buffer.
- * The data chunk is taken from the acquisition state, and is expected to
- * contain a multiple of 8 device words.
- * All data currently in the acquisition buffer will be processed.  Packets
- * of decoded samples are sent off to the session bus whenever the output
- * buffer becomes full while decoding.
- */
-static int process_sample_data(const struct sr_dev_inst *sdi)
-{
-	uint64_t sample;
-	uint64_t high_nibbles;
-	uint64_t word;
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-	uint8_t *out_p;
-	uint32_t *slice;
-	struct sr_datafeed_packet packet;
-	struct sr_datafeed_logic logic;
-	size_t expect_len;
-	size_t actual_len;
-	size_t out_max_samples;
-	size_t out_run_samples;
-	size_t ri;
-	size_t in_words_left;
-	size_t si;
-
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	if (acq->mem_addr_done >= acq->mem_addr_stop
-			|| acq->samples_done >= acq->samples_max)
-		return SR_OK;
-
-	in_words_left = MIN(acq->mem_addr_stop - acq->mem_addr_done,
-			    READ_CHUNK_LEN);
-	expect_len = LWLA1034_MEMBUF_LEN(in_words_left) * sizeof(uint32_t);
-	actual_len = acq->xfer_in->actual_length;
-
-	if (actual_len != expect_len) {
-		sr_err("Received size %zu does not match expected size %zu.",
-		       actual_len, expect_len);
-		devc->transfer_error = TRUE;
-		return SR_ERR;
-	}
-	acq->mem_addr_done += in_words_left;
-
-	/* Prepare session packet. */
-	packet.type    = SR_DF_LOGIC;
-	packet.payload = &logic;
-	logic.unitsize = UNIT_SIZE;
-	logic.data     = acq->out_packet;
-
-	slice = acq->xfer_buf_in;
-	si = 0; /* word index within slice */
-
-	for (;;) {
-		/* Calculate number of samples to write into packet. */
-		out_max_samples = MIN(acq->samples_max - acq->samples_done,
-				      PACKET_LENGTH - acq->out_index);
-		out_run_samples = MIN(acq->run_len, out_max_samples);
-
-		/* Expand run-length samples into session packet. */
-		sample = acq->sample;
-		out_p = &acq->out_packet[acq->out_index * UNIT_SIZE];
-
-		for (ri = 0; ri < out_run_samples; ++ri) {
-			out_p[0] =  sample        & 0xFF;
-			out_p[1] = (sample >>  8) & 0xFF;
-			out_p[2] = (sample >> 16) & 0xFF;
-			out_p[3] = (sample >> 24) & 0xFF;
-			out_p[4] = (sample >> 32) & 0xFF;
-			out_p += UNIT_SIZE;
-		}
-		acq->run_len -= out_run_samples;
-		acq->out_index += out_run_samples;
-		acq->samples_done += out_run_samples;
-
-		/* Packet full or sample count limit reached? */
-		if (out_run_samples == out_max_samples) {
-			logic.length = acq->out_index * UNIT_SIZE;
-			sr_session_send(sdi, &packet);
-			acq->out_index = 0;
-
-			if (acq->samples_done >= acq->samples_max)
-				return SR_OK; /* sample limit reached */
-			if (acq->run_len > 0)
-				continue; /* need another packet */
-		}
-
-		if (in_words_left == 0)
-			break; /* done with current chunk */
-
-		/* Now work on the current slice. */
-		high_nibbles = LWLA_TO_UINT32(slice[8]);
-		word = LWLA_TO_UINT32(slice[si]);
-		word |= (high_nibbles << (4 * si + 4)) & ((uint64_t)0xF << 32);
-
-		if (acq->rle == RLE_STATE_DATA) {
-			acq->sample = word & ALL_CHANNELS_MASK;
-			acq->run_len = ((word >> NUM_CHANNELS) & 1) + 1;
-			if (word & RLE_FLAG_LEN_FOLLOWS)
-				acq->rle = RLE_STATE_LEN;
-		} else {
-			acq->run_len += word << 1;
-			acq->rle = RLE_STATE_DATA;
-		}
-
-		/* Move to next word. */
-		si = (si + 1) % 8;
-		if (si == 0)
-			slice += 9;
-		--in_words_left;
-	}
-
-	/* Send out partially filled packet if this was the last chunk. */
-	if (acq->mem_addr_done >= acq->mem_addr_stop && acq->out_index > 0) {
-		logic.length = acq->out_index * UNIT_SIZE;
-		sr_session_send(sdi, &packet);
-		acq->out_index = 0;
-	}
-	return SR_OK;
-}
-
-/* Finish an acquisition session.  This sends the end packet to the session
- * bus and removes the listener for asynchronous USB transfers.
- */
-static void end_acquisition(struct sr_dev_inst *sdi)
-{
-	struct drv_context *drvc;
-	struct dev_context *devc;
-	struct sr_datafeed_packet packet;
-
-	drvc = sdi->driver->priv;
-	devc = sdi->priv;
-
-	if (devc->state == STATE_IDLE)
-		return;
-
-	devc->state = STATE_IDLE;
-
-	/* Remove USB file descriptors from polling. */
-	usb_source_remove(drvc->sr_ctx);
-
-	packet.type = SR_DF_END;
-	sr_session_send(sdi, &packet);
-
-	lwla_free_acquisition_state(devc->acquisition);
-	devc->acquisition = NULL;
-
-	sdi->status = SR_ST_ACTIVE;
-}
-
-/* USB output transfer completion callback.
- */
-static void receive_transfer_out(struct libusb_transfer *transfer)
-{
-	struct sr_dev_inst *sdi;
-	struct dev_context *devc;
-
-	sdi  = transfer->user_data;
-	devc = sdi->priv;
-
-	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		sr_err("Transfer to device failed: %d.", transfer->status);
-		devc->transfer_error = TRUE;
-		return;
-	}
-
-	if (devc->reg_write_pos < devc->reg_write_len) {
-		issue_next_write_reg(sdi);
-	} else {
-		switch (devc->state) {
-		case STATE_START_CAPTURE:
-			devc->state = STATE_STATUS_WAIT;
-			break;
-		case STATE_STATUS_REQUEST:
-			devc->state = STATE_STATUS_RESPONSE;
-			submit_transfer(devc, devc->acquisition->xfer_in);
-			break;
-		case STATE_STOP_CAPTURE:
-			if (sdi->status == SR_ST_ACTIVE)
-				request_capture_length(sdi);
-			else
-				end_acquisition(sdi);
-			break;
-		case STATE_LENGTH_REQUEST:
-			devc->state = STATE_LENGTH_RESPONSE;
-			submit_transfer(devc, devc->acquisition->xfer_in);
-			break;
-		case STATE_READ_PREPARE:
-			request_read_mem(sdi);
-			break;
-		case STATE_READ_REQUEST:
-			devc->state = STATE_READ_RESPONSE;
-			submit_transfer(devc, devc->acquisition->xfer_in);
-			break;
-		case STATE_READ_END:
-			end_acquisition(sdi);
-			break;
-		default:
-			sr_err("Unexpected device state %d.", devc->state);
-			break;
-		}
-	}
-}
-
-/* USB input transfer completion callback.
- */
-static void receive_transfer_in(struct libusb_transfer *transfer)
-{
-	struct sr_dev_inst *sdi;
-	struct dev_context *devc;
-	struct acquisition_state *acq;
-
-	sdi  = transfer->user_data;
-	devc = sdi->priv;
-	acq  = devc->acquisition;
-
-	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		sr_err("Transfer from device failed: %d.", transfer->status);
-		devc->transfer_error = TRUE;
-		return;
-	}
-
-	switch (devc->state) {
-	case STATE_STATUS_RESPONSE:
-		process_capture_status(sdi);
-		break;
-	case STATE_LENGTH_RESPONSE:
-		process_capture_length(sdi);
-		break;
-	case STATE_READ_RESPONSE:
-		if (process_sample_data(sdi) == SR_OK
-				&& acq->mem_addr_next < acq->mem_addr_stop
-				&& acq->samples_done < acq->samples_max)
-			request_read_mem(sdi);
-		else
-			issue_read_end(sdi);
-		break;
-	default:
-		sr_err("Unexpected device state %d.", devc->state);
-		break;
-	}
-}
-
-/* Initialize the LWLA.  This downloads a bitstream into the FPGA
- * and executes a simple device test sequence.
- */
-SR_PRIV int lwla_init_device(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	int ret;
-	uint32_t value;
-
-	devc = sdi->priv;
-
-	/* Force reload of bitstream */
-	devc->cur_clock_config = CONF_CLOCK_NONE;
-
-	ret = lwla_set_clock_config(sdi);
-
-	if (ret != SR_OK)
-		return ret;
-
-	ret = lwla_write_reg(sdi->conn, REG_CMD_CTRL2, 100);
-	if (ret != SR_OK)
-		return ret;
-
-	ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL1, &value);
-	if (ret != SR_OK)
-		return ret;
-	sr_dbg("Received test word 0x%08X back.", value);
-	if (value != 0x12345678)
-		return SR_ERR;
-
-	ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL4, &value);
-	if (ret != SR_OK)
-		return ret;
-	sr_dbg("Received test word 0x%08X back.", value);
-	if (value != 0x12345678)
-		return SR_ERR;
-
-	ret = lwla_read_reg(sdi->conn, REG_CMD_CTRL3, &value);
-	if (ret != SR_OK)
-		return ret;
-	sr_dbg("Received test word 0x%08X back.", value);
-	if (value != 0x87654321)
-		return SR_ERR;
-
-	return ret;
-}
-
-/* Select the LWLA clock configuration.  If the clock source changed from
- * the previous setting, this will download a new bitstream to the FPGA.
- */
-SR_PRIV int lwla_set_clock_config(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	int ret;
-	enum clock_config choice;
-
-	devc = sdi->priv;
-
-	if (sdi->status == SR_ST_INACTIVE)
-		choice = CONF_CLOCK_NONE;
-	else if (devc->cfg_clock_source == CLOCK_INTERNAL)
-		choice = CONF_CLOCK_INT;
-	else if (devc->cfg_clock_edge == EDGE_POSITIVE)
-		choice = CONF_CLOCK_EXT_RISE;
-	else
-		choice = CONF_CLOCK_EXT_FALL;
-
-	if (choice != devc->cur_clock_config) {
-		devc->cur_clock_config = CONF_CLOCK_NONE;
-		ret = lwla_send_bitstream(sdi->conn, bitstream_map[choice]);
-		if (ret == SR_OK)
-			devc->cur_clock_config = choice;
-		return ret;
-	}
-	return SR_OK;
-}
-
-/* Configure the LWLA in preparation for an acquisition session.
- */
-SR_PRIV int lwla_setup_acquisition(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct sr_usb_dev_inst *usb;
-	struct acquisition_state *acq;
-	struct regval_pair regvals[7];
-	int ret;
-
-	devc = sdi->priv;
-	usb  = sdi->conn;
-	acq  = devc->acquisition;
-
-	if (devc->limit_msec > 0) {
-		acq->duration_max = devc->limit_msec;
-		sr_info("Acquisition time limit %" PRIu64 " ms.",
-			devc->limit_msec);
-	} else
-		acq->duration_max = MAX_LIMIT_MSEC;
-
-	if (devc->limit_samples > 0) {
-		acq->samples_max = devc->limit_samples;
-		sr_info("Acquisition sample count limit %" PRIu64 ".",
-			devc->limit_samples);
-	} else
-		acq->samples_max = MAX_LIMIT_SAMPLES;
-
-	if (devc->cfg_clock_source == CLOCK_INTERNAL) {
-		sr_info("Internal clock, samplerate %" PRIu64 ".",
-			devc->samplerate);
-		if (devc->samplerate == 0)
-			return SR_ERR_BUG;
-		/* At 125 MHz, the clock divider is bypassed. */
-		acq->bypass_clockdiv = (devc->samplerate > SR_MHZ(100));
-
-		/* If only one of the limits is set, derive the other one. */
-		if (devc->limit_msec == 0 && devc->limit_samples > 0)
-			acq->duration_max = devc->limit_samples
-					* 1000 / devc->samplerate + 1;
-		else if (devc->limit_samples == 0 && devc->limit_msec > 0)
-			acq->samples_max = devc->limit_msec
-					* devc->samplerate / 1000;
-	} else {
-		acq->bypass_clockdiv = TRUE;
-
-		if (devc->cfg_clock_edge == EDGE_NEGATIVE)
-			sr_info("External clock, falling edge.");
-		else
-			sr_info("External clock, rising edge.");
-	}
-
-	regvals[0].reg = REG_MEM_CTRL2;
-	regvals[0].val = 2;
-
-	regvals[1].reg = REG_MEM_CTRL2;
-	regvals[1].val = 1;
-
-	regvals[2].reg = REG_CMD_CTRL2;
-	regvals[2].val = 10;
-
-	regvals[3].reg = REG_CMD_CTRL3;
-	regvals[3].val = 0x74;
-
-	regvals[4].reg = REG_CMD_CTRL4;
-	regvals[4].val = 0;
-
-	regvals[5].reg = REG_CMD_CTRL1;
-	regvals[5].val = 0;
-
-	regvals[6].reg = REG_DIV_BYPASS;
-	regvals[6].val = acq->bypass_clockdiv;
-
-	ret = lwla_write_regs(usb, regvals, G_N_ELEMENTS(regvals));
-	if (ret != SR_OK)
-		return ret;
-
-	return capture_setup(sdi);
-}
-
-/* Start the capture operation on the LWLA device.  Beginning with this
- * function, all USB transfers will be asynchronous until the end of the
- * acquisition session.
- */
-SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct sr_usb_dev_inst *usb;
-	struct acquisition_state *acq;
-	struct regval_pair *regvals;
-
-	devc = sdi->priv;
-	usb  = sdi->conn;
-	acq  = devc->acquisition;
-
-	acq->duration_now  = 0;
-	acq->mem_addr_fill = 0;
-	acq->capture_flags = 0;
-
-	libusb_fill_bulk_transfer(acq->xfer_out, usb->devhdl, EP_COMMAND,
-				  (unsigned char *)acq->xfer_buf_out, 0,
-				  &receive_transfer_out,
-				  (struct sr_dev_inst *)sdi, USB_TIMEOUT);
-
-	libusb_fill_bulk_transfer(acq->xfer_in, usb->devhdl, EP_REPLY,
-				  (unsigned char *)acq->xfer_buf_in,
-				  sizeof acq->xfer_buf_in,
-				  &receive_transfer_in,
-				  (struct sr_dev_inst *)sdi, USB_TIMEOUT);
-
-	regvals = devc->reg_write_seq;
-
-	regvals[0].reg = REG_CMD_CTRL2;
-	regvals[0].val = 10;
-
-	regvals[1].reg = REG_CMD_CTRL3;
-	regvals[1].val = 1;
-
-	regvals[2].reg = REG_CMD_CTRL4;
-	regvals[2].val = 0;
-
-	regvals[3].reg = REG_CMD_CTRL1;
-	regvals[3].val = 0;
-
-	devc->reg_write_pos = 0;
-	devc->reg_write_len = 4;
-
-	devc->state = STATE_START_CAPTURE;
-
-	return issue_next_write_reg(sdi);
-}
-
-/* Allocate an acquisition state object.
- */
-SR_PRIV struct acquisition_state *lwla_alloc_acquisition_state(void)
-{
-	struct acquisition_state *acq;
-
-	acq = g_try_new0(struct acquisition_state, 1);
-	if (!acq) {
-		sr_err("Acquisition state malloc failed.");
-		return NULL;
-	}
-
-	acq->xfer_in = libusb_alloc_transfer(0);
-	if (!acq->xfer_in) {
-		sr_err("Transfer malloc failed.");
-		g_free(acq);
-		return NULL;
-	}
-
-	acq->xfer_out = libusb_alloc_transfer(0);
-	if (!acq->xfer_out) {
-		sr_err("Transfer malloc failed.");
-		libusb_free_transfer(acq->xfer_in);
-		g_free(acq);
-		return NULL;
-	}
-
-	return acq;
-}
-
-/* Deallocate an acquisition state object.
- */
-SR_PRIV void lwla_free_acquisition_state(struct acquisition_state *acq)
-{
-	if (acq) {
-		libusb_free_transfer(acq->xfer_out);
-		libusb_free_transfer(acq->xfer_in);
-		g_free(acq);
-	}
-}
-
-/* USB I/O source callback.
- */
-SR_PRIV int lwla_receive_data(int fd, int revents, void *cb_data)
-{
-	struct sr_dev_inst *sdi;
-	struct dev_context *devc;
-	struct drv_context *drvc;
-	struct timeval tv;
-	int ret;
-
-	(void)fd;
-
-	sdi  = cb_data;
-	devc = sdi->priv;
-	drvc = sdi->driver->priv;
-
-	if (!devc || !drvc)
-		return FALSE;
-
-	/* No timeout: return immediately. */
-	tv.tv_sec  = 0;
-	tv.tv_usec = 0;
-
-	ret = libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx,
-						     &tv, NULL);
-	if (ret != 0)
-		sr_err("Event handling failed: %s.", libusb_error_name(ret));
-
-	/* If no event flags are set the timeout must have expired. */
-	if (revents == 0 && devc->state == STATE_STATUS_WAIT) {
-		if (sdi->status == SR_ST_STOPPING)
-			issue_stop_capture(sdi);
-		else
-			request_capture_status(sdi);
-	}
-
-	/* Check if an error occurred on a transfer. */
-	if (devc->transfer_error)
-		end_acquisition(sdi);
-
-	return TRUE;
-}
diff --git a/hardware/sysclk-lwla/protocol.h b/hardware/sysclk-lwla/protocol.h
deleted file mode 100644
index d77db0f..0000000
--- a/hardware/sysclk-lwla/protocol.h
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2014 Daniel Elstner <daniel.kitta at 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_SYSCLK_LWLA_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H
-
-#define LOG_PREFIX "sysclk-lwla"
-
-#include "lwla.h"
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include <stdint.h>
-#include <glib.h>
-
-/* For now, only the LWLA1034 is supported.
- */
-#define VENDOR_NAME	"SysClk"
-#define MODEL_NAME	"LWLA1034"
-
-#define USB_VID_PID	"2961.6689"
-#define USB_INTERFACE	0
-#define USB_TIMEOUT	3000 /* ms */
-
-#define NUM_CHANNELS	34
-#define TRIGGER_TYPES	"01fr"
-
-/* Bit mask covering all 34 channels.
- */
-#define ALL_CHANNELS_MASK (((uint64_t)1 << NUM_CHANNELS) - 1)
-
-/** Unit and packet size for the sigrok logic datafeed.
- */
-#define UNIT_SIZE	((NUM_CHANNELS + 7) / 8)
-#define PACKET_LENGTH	10000	/* units */
-
-/** Size of the acquisition buffer in device memory units.
- */
-#define MEMORY_DEPTH	(256 * 1024)	/* 256k x 36 bit */
-
-/** Number of device memory units (36 bit) to read at a time.  Slices of 8
- * consecutive 36-bit words are mapped to 9 32-bit words each, so the chunk
- * length should be a multiple of 8 to ensure alignment to slice boundaries.
- *
- * Experimentation has shown that reading chunks larger than about 1024 bytes
- * is unreliable.  The threshold seems to relate to the buffer size on the FX2
- * USB chip:  The configured endpoint buffer size is 512, and with double or
- * triple buffering enabled a multiple of 512 bytes can be kept in fly.
- *
- * The vendor software limits reads to 120 words (15 slices, 540 bytes) at
- * a time.  So far, it appears safe to increase this to 224 words (28 slices,
- * 1008 bytes), thus making the most of two 512 byte buffers.
- */
-#define READ_CHUNK_LEN	(28 * 8)
-
-/** Calculate the required buffer size in 32-bit units for reading a given
- * number of device memory words.  Rounded to a multiple of 8 device words.
- */
-#define LWLA1034_MEMBUF_LEN(count) (((count) + 7) / 8 * 9)
-
-/** Maximum number of 16-bit words sent at a time during acquisition.
- * Used for allocating the libusb transfer buffer.
- */
-#define MAX_ACQ_SEND_WORDS	8 /* 5 for memory read request plus stuffing */
-
-/** Maximum number of 32-bit words received at a time during acquisition.
- * Round to the next multiple of the endpoint buffer size to avoid nasty
- * transfer overflow conditions on hiccups.
- */
-#define MAX_ACQ_RECV_LEN	((READ_CHUNK_LEN / 8 * 9 + 127) / 128 * 128)
-
-/** Maximum length of a register write sequence.
- */
-#define MAX_REG_WRITE_SEQ_LEN   5
-
-/** Default configured samplerate.
- */
-#define DEFAULT_SAMPLERATE	SR_MHZ(125)
-
-/** Maximum configurable sample count limit.
- */
-#define MAX_LIMIT_SAMPLES	(UINT64_C(1) << 48)
-
-/** Maximum configurable capture duration in milliseconds.
- */
-#define MAX_LIMIT_MSEC		(UINT64_C(1) << 32)
-
-/** LWLA1034 FPGA clock configurations.
- */
-enum clock_config {
-	CONF_CLOCK_NONE,
-	CONF_CLOCK_INT,
-	CONF_CLOCK_EXT_RISE,
-	CONF_CLOCK_EXT_FALL,
-};
-
-/** Available clock sources.
- */
-enum clock_source {
-	CLOCK_INTERNAL,
-	CLOCK_EXT_CLK,
-};
-
-/** Available trigger sources.
- */
-enum trigger_source {
-	TRIGGER_CHANNELS = 0,
-	TRIGGER_EXT_TRG,
-};
-
-/** Available edge choices for the external clock and trigger inputs.
- */
-enum signal_edge {
-	EDGE_POSITIVE = 0,
-	EDGE_NEGATIVE,
-};
-
-/** LWLA device states.
- */
-enum device_state {
-	STATE_IDLE = 0,
-
-	STATE_START_CAPTURE,
-
-	STATE_STATUS_WAIT,
-	STATE_STATUS_REQUEST,
-	STATE_STATUS_RESPONSE,
-
-	STATE_STOP_CAPTURE,
-
-	STATE_LENGTH_REQUEST,
-	STATE_LENGTH_RESPONSE,
-
-	STATE_READ_PREPARE,
-	STATE_READ_REQUEST,
-	STATE_READ_RESPONSE,
-	STATE_READ_END,
-};
-
-/** LWLA run-length encoding states.
- */
-enum rle_state {
-	RLE_STATE_DATA,
-	RLE_STATE_LEN
-};
-
-/** LWLA sample acquisition and decompression state.
- */
-struct acquisition_state {
-	uint64_t sample;
-	uint64_t run_len;
-
-	/** Maximum number of samples to process. */
-	uint64_t samples_max;
-	/** Number of samples sent to the session bus. */
-	uint64_t samples_done;
-
-	/** Maximum duration of capture, in milliseconds. */
-	uint64_t duration_max;
-	/** Running capture duration since trigger event. */
-	uint64_t duration_now;
-
-	/** Capture memory fill level. */
-	size_t mem_addr_fill;
-
-	size_t mem_addr_done;
-	size_t mem_addr_next;
-	size_t mem_addr_stop;
-
-	size_t out_index;
-
-	struct libusb_transfer *xfer_in;
-	struct libusb_transfer *xfer_out;
-
-	unsigned int capture_flags;
-
-	enum rle_state rle;
-
-	/** Whether to bypass the clock divider. */
-	gboolean bypass_clockdiv;
-
-	/* Payload data buffers for incoming and outgoing transfers. */
-	uint32_t xfer_buf_in[MAX_ACQ_RECV_LEN];
-	uint16_t xfer_buf_out[MAX_ACQ_SEND_WORDS];
-
-	/* Payload buffer for sigrok logic packets. */
-	uint8_t out_packet[PACKET_LENGTH * UNIT_SIZE];
-};
-
-/** Private, per-device-instance driver context.
- */
-struct dev_context {
-	/** The samplerate selected by the user. */
-	uint64_t samplerate;
-
-	/** The maximimum sampling duration, in milliseconds. */
-	uint64_t limit_msec;
-
-	/** The maximimum number of samples to acquire. */
-	uint64_t limit_samples;
-
-	/** Channels to use. */
-	uint64_t channel_mask;
-
-	uint64_t trigger_mask;
-	uint64_t trigger_edge_mask;
-	uint64_t trigger_values;
-
-	struct acquisition_state *acquisition;
-
-	struct regval_pair reg_write_seq[MAX_REG_WRITE_SEQ_LEN];
-	int reg_write_pos;
-	int reg_write_len;
-
-	enum device_state state;
-
-	/** The currently active clock configuration of the device. */
-	enum clock_config cur_clock_config;
-
-	/** Clock source configuration setting. */
-	enum clock_source cfg_clock_source;
-	/** Clock edge configuration setting. */
-	enum signal_edge cfg_clock_edge;
-
-	/** Trigger source configuration setting. */
-	enum trigger_source cfg_trigger_source;
-	/** Trigger slope configuration setting. */
-	enum signal_edge cfg_trigger_slope;
-
-	/* Indicates that stopping the acquisition is currently in progress. */
-	gboolean stopping_in_progress;
-
-	/* Indicates whether a transfer failed. */
-	gboolean transfer_error;
-};
-
-SR_PRIV struct acquisition_state *lwla_alloc_acquisition_state(void);
-SR_PRIV void lwla_free_acquisition_state(struct acquisition_state *acq);
-
-SR_PRIV int lwla_init_device(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_set_clock_config(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_setup_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int lwla_abort_acquisition(const struct sr_dev_inst *sdi);
-
-SR_PRIV int lwla_receive_data(int fd, int revents, void *cb_data);
-
-#endif /* !LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H */
diff --git a/hardware/uni-t-dmm/api.c b/hardware/uni-t-dmm/api.c
deleted file mode 100644
index ef31de0..0000000
--- a/hardware/uni-t-dmm/api.c
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012-2013 Uwe Hermann <uwe at 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 <stdlib.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "protocol.h"
-
-#define UNI_T_UT_D04_NEW "1a86.e008"
-
-static const int32_t hwopts[] = {
-	SR_CONF_CONN,
-};
-
-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 tecpel_dmm_8061_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60a_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60e_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut60g_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61b_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61c_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61d_driver_info;
-SR_PRIV struct sr_dev_driver uni_t_ut61e_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc820_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc830_driver_info;
-SR_PRIV struct sr_dev_driver voltcraft_vc840_driver_info;
-SR_PRIV struct sr_dev_driver tenma_72_7745_driver_info;
-SR_PRIV struct sr_dev_driver tenma_72_7750_driver_info;
-
-SR_PRIV struct dmm_info udmms[] = {
-	{
-		"Tecpel", "DMM-8061", 2400,
-		FS9721_PACKET_SIZE,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_00_temp_c,
-		&tecpel_dmm_8061_driver_info, receive_data_TECPEL_DMM_8061,
-	},
-	{
-		"UNI-T", "UT60A", 2400,
-		FS9721_PACKET_SIZE,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		NULL,
-		&uni_t_ut60a_driver_info, receive_data_UNI_T_UT60A,
-	},
-	{
-		"UNI-T", "UT60E", 2400,
-		FS9721_PACKET_SIZE,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_00_temp_c,
-		&uni_t_ut60e_driver_info, receive_data_UNI_T_UT60E,
-	},
-	{
-		/* The baudrate is actually 19230, see "Note 1" below. */
-		"UNI-T", "UT60G", 19200,
-		ES519XX_11B_PACKET_SIZE,
-		sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
-		NULL,
-		&uni_t_ut60g_driver_info, receive_data_UNI_T_UT60G,
-	},
-	{
-		"UNI-T", "UT61B", 2400,
-		FS9922_PACKET_SIZE,
-		sr_fs9922_packet_valid, sr_fs9922_parse,
-		NULL,
-		&uni_t_ut61b_driver_info, receive_data_UNI_T_UT61B,
-	},
-	{
-		"UNI-T", "UT61C", 2400,
-		FS9922_PACKET_SIZE,
-		sr_fs9922_packet_valid, sr_fs9922_parse,
-		NULL,
-		&uni_t_ut61c_driver_info, receive_data_UNI_T_UT61C,
-	},
-	{
-		"UNI-T", "UT61D", 2400,
-		FS9922_PACKET_SIZE,
-		sr_fs9922_packet_valid, sr_fs9922_parse,
-		NULL,
-		&uni_t_ut61d_driver_info, receive_data_UNI_T_UT61D,
-	},
-	{
-		/* The baudrate is actually 19230, see "Note 1" below. */
-		"UNI-T", "UT61E", 19200,
-		ES519XX_14B_PACKET_SIZE,
-		sr_es519xx_19200_14b_packet_valid, sr_es519xx_19200_14b_parse,
-		NULL,
-		&uni_t_ut61e_driver_info, receive_data_UNI_T_UT61E,
-	},
-	{
-		"Voltcraft", "VC-820", 2400,
-		FS9721_PACKET_SIZE,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		NULL,
-		&voltcraft_vc820_driver_info, receive_data_VOLTCRAFT_VC820,
-	},
-	{
-		/*
-		 * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
-		 * the FS9922 protocol. Instead, it only sets the user-defined
-		 * bit "z1" to indicate "diode mode" and "voltage".
-		 */
-		"Voltcraft", "VC-830", 2400,
-		FS9922_PACKET_SIZE,
-		sr_fs9922_packet_valid, sr_fs9922_parse,
-		&sr_fs9922_z1_diode,
-		&voltcraft_vc830_driver_info, receive_data_VOLTCRAFT_VC830,
-	},
-	{
-		"Voltcraft", "VC-840", 2400,
-		FS9721_PACKET_SIZE,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_00_temp_c,
-		&voltcraft_vc840_driver_info, receive_data_VOLTCRAFT_VC840,
-	},
-	{
-		"Tenma", "72-7745", 2400,
-		FS9721_PACKET_SIZE,
-		sr_fs9721_packet_valid, sr_fs9721_parse,
-		sr_fs9721_00_temp_c,
-		&tenma_72_7745_driver_info, receive_data_TENMA_72_7745,
-	},
-	{
-		/* The baudrate is actually 19230, see "Note 1" below. */
-		"Tenma", "72-7750", 19200,
-		ES519XX_11B_PACKET_SIZE,
-		sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
-		NULL,
-		&tenma_72_7750_driver_info, receive_data_TENMA_72_7750,
-	},
-};
-
-/*
- * Note 1: The actual baudrate of the Cyrustek ES519xx chip used in this DMM
- * is 19230. However, the WCH CH9325 chip (UART to USB/HID) used in (some
- * versions of) the UNI-T UT-D04 cable doesn't support 19230 baud. It only
- * supports 19200, and setting an unsupported baudrate will result in the
- * default of 2400 being used (which will not work with this DMM, of course).
- */
-
-static int dev_clear(int dmm)
-{
-	return std_dev_clear(udmms[dmm].di, NULL);
-}
-
-static int init(struct sr_context *sr_ctx, int dmm)
-{
-	sr_dbg("Selected '%s' subdriver.", udmms[dmm].di->name);
-
-	return std_init(sr_ctx, udmms[dmm].di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options, int dmm)
-{
-	GSList *usb_devices, *devices, *l;
-	struct sr_dev_inst *sdi;
-	struct dev_context *devc;
-	struct drv_context *drvc;
-	struct sr_usb_dev_inst *usb;
-	struct sr_config *src;
-	struct sr_channel *ch;
-	const char *conn;
-
-	drvc = udmms[dmm].di->priv;
-
-	conn = NULL;
-	for (l = options; l; l = l->next) {
-		src = l->data;
-		switch (src->key) {
-		case SR_CONF_CONN:
-			conn = g_variant_get_string(src->data, NULL);
-			break;
-		}
-	}
-	if (!conn)
-		return NULL;
-
-	devices = NULL;
-	if (!(usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
-		g_slist_free_full(usb_devices, g_free);
-		return NULL;
-	}
-
-	for (l = usb_devices; l; l = l->next) {
-		usb = l->data;
-
-		if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-			sr_err("Device context malloc failed.");
-			return NULL;
-		}
-
-		devc->first_run = TRUE;
-
-		if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
-				udmms[dmm].vendor, udmms[dmm].device, NULL))) {
-			sr_err("sr_dev_inst_new returned NULL.");
-			return NULL;
-		}
-		sdi->priv = devc;
-		sdi->driver = udmms[dmm].di;
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-
-		sdi->inst_type = SR_INST_USB;
-		sdi->conn = usb;
-
-		drvc->instances = g_slist_append(drvc->instances, sdi);
-		devices = g_slist_append(devices, sdi);
-	}
-
-	return devices;
-}
-
-static GSList *dev_list(int dmm)
-{
-	return ((struct drv_context *)(udmms[dmm].di->priv))->instances;
-}
-
-static int dev_open(struct sr_dev_inst *sdi, int dmm)
-{
-	struct drv_context *drvc;
-	struct sr_usb_dev_inst *usb;
-	int ret;
-
-	drvc = udmms[dmm].di->priv;
-	usb = sdi->conn;
-
-	if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
-		sdi->status = SR_ST_ACTIVE;
-
-	return ret;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
-	/* TODO */
-
-	sdi->status = SR_ST_INACTIVE;
-
-	return SR_OK;
-}
-
-static int cleanup(int dmm)
-{
-	return dev_clear(dmm);
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg)
-{
-	struct dev_context *devc;
-
-	(void)cg;
-
-	devc = sdi->priv;
-
-	switch (id) {
-	case SR_CONF_LIMIT_MSEC:
-		if (g_variant_get_uint64(data) == 0) {
-			sr_err("Time limit cannot 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:
-		if (g_variant_get_uint64(data) == 0) {
-			sr_err("Sample limit cannot be 0.");
-			return SR_ERR;
-		}
-		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,
-		const struct sr_channel_group *cg)
-{
-	(void)sdi;
-	(void)cg;
-
-	switch (key) {
-	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_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 dev_acquisition_start(const struct sr_dev_inst *sdi,
-				    void *cb_data, int dmm)
-{
-	struct dev_context *devc;
-
-	devc = sdi->priv;
-
-	devc->cb_data = cb_data;
-
-	devc->starttime = g_get_monotonic_time();
-
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(cb_data, LOG_PREFIX);
-
-	sr_source_add(0, 0, 10 /* poll_timeout */,
-		      udmms[dmm].receive_data, (void *)sdi);
-
-	return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
-	struct sr_datafeed_packet packet;
-
-	(void)sdi;
-
-	sr_dbg("Stopping acquisition.");
-
-	/* Send end packet to the session bus. */
-	sr_dbg("Sending SR_DF_END.");
-	packet.type = SR_DF_END;
-	sr_session_send(cb_data, &packet);
-
-	/* TODO? */
-	sr_source_remove(0);
-
-	return SR_OK;
-}
-
-/* Driver-specific API function wrappers */
-#define HW_INIT(X) \
-static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
-#define HW_CLEANUP(X) \
-static int cleanup_##X(void) { return cleanup(X); }
-#define HW_SCAN(X) \
-static GSList *scan_##X(GSList *options) { return scan(options, X); }
-#define HW_DEV_LIST(X) \
-static GSList *dev_list_##X(void) { return dev_list(X); }
-#define HW_DEV_CLEAR(X) \
-static int dev_clear_##X(void) { return dev_clear(X); }
-#define HW_DEV_ACQUISITION_START(X) \
-static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
-void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
-#define HW_DEV_OPEN(X) \
-static int dev_open_##X(struct sr_dev_inst *sdi) { return dev_open(sdi, X); }
-
-/* Driver structs and API function wrappers */
-#define DRV(ID, ID_UPPER, NAME, LONGNAME) \
-HW_INIT(ID_UPPER) \
-HW_CLEANUP(ID_UPPER) \
-HW_SCAN(ID_UPPER) \
-HW_DEV_LIST(ID_UPPER) \
-HW_DEV_CLEAR(ID_UPPER) \
-HW_DEV_ACQUISITION_START(ID_UPPER) \
-HW_DEV_OPEN(ID_UPPER) \
-SR_PRIV struct sr_dev_driver ID##_driver_info = { \
-	.name = NAME, \
-	.longname = LONGNAME, \
-	.api_version = 1, \
-	.init = init_##ID_UPPER, \
-	.cleanup = cleanup_##ID_UPPER, \
-	.scan = scan_##ID_UPPER, \
-	.dev_list = dev_list_##ID_UPPER, \
-	.dev_clear = dev_clear_##ID_UPPER, \
-	.config_get = NULL, \
-	.config_set = config_set, \
-	.config_list = config_list, \
-	.dev_open = dev_open_##ID_UPPER, \
-	.dev_close = dev_close, \
-	.dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
-	.dev_acquisition_stop = dev_acquisition_stop, \
-	.priv = NULL, \
-};
-
-DRV(tecpel_dmm_8061, TECPEL_DMM_8061, "tecpel-dmm-8061", "Tecpel DMM-8061")
-DRV(uni_t_ut60a, UNI_T_UT60A, "uni-t-ut60a", "UNI-T UT60A")
-DRV(uni_t_ut60e, UNI_T_UT60E, "uni-t-ut60e", "UNI-T UT60E")
-DRV(uni_t_ut60g, UNI_T_UT60G, "uni-t-ut60g", "UNI-T UT60G")
-DRV(uni_t_ut61b, UNI_T_UT61B, "uni-t-ut61b", "UNI-T UT61B")
-DRV(uni_t_ut61c, UNI_T_UT61C, "uni-t-ut61c", "UNI-T UT61C")
-DRV(uni_t_ut61d, UNI_T_UT61D, "uni-t-ut61d", "UNI-T UT61D")
-DRV(uni_t_ut61e, UNI_T_UT61E, "uni-t-ut61e", "UNI-T UT61E")
-DRV(voltcraft_vc820, VOLTCRAFT_VC820, "voltcraft-vc820", "Voltcraft VC-820")
-DRV(voltcraft_vc830, VOLTCRAFT_VC830, "voltcraft-vc830", "Voltcraft VC-830")
-DRV(voltcraft_vc840, VOLTCRAFT_VC840, "voltcraft-vc840", "Voltcraft VC-840")
-DRV(tenma_72_7745, TENMA_72_7745, "tenma-72-7745", "Tenma 72-7745")
-DRV(tenma_72_7750, TENMA_72_7750, "tenma-72-7750", "Tenma 72-7750")
diff --git a/hwdriver.c b/hwdriver.c
deleted file mode 100644
index f028ea1..0000000
--- a/hwdriver.c
+++ /dev/null
@@ -1,796 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert at 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 <stdio.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <string.h>
-#include <glib.h>
-#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "hwdriver"
-/** @endcond */
-
-/**
- * @file
- *
- * Hardware driver handling in libsigrok.
- */
-
-/**
- * @defgroup grp_driver Hardware drivers
- *
- * Hardware driver handling in libsigrok.
- *
- * @{
- */
-
-static struct sr_config_info sr_config_info_data[] = {
-	{SR_CONF_CONN, SR_T_STRING, "conn",
-		"Connection", NULL},
-	{SR_CONF_SERIALCOMM, SR_T_STRING, "serialcomm",
-		"Serial communication", NULL},
-	{SR_CONF_SAMPLERATE, SR_T_UINT64, "samplerate",
-		"Sample rate", NULL},
-	{SR_CONF_CAPTURE_RATIO, SR_T_UINT64, "captureratio",
-		"Pre-trigger capture ratio", NULL},
-	{SR_CONF_PATTERN_MODE, SR_T_STRING, "pattern",
-		"Pattern", NULL},
-	{SR_CONF_TRIGGER_TYPE, SR_T_STRING, "triggertype",
-		"Trigger types", NULL},
-	{SR_CONF_EXTERNAL_CLOCK, SR_T_BOOL, "external_clock",
-		"External clock mode", NULL},
-	{SR_CONF_SWAP, SR_T_BOOL, "swap",
-		"Swap channel order", NULL},
-	{SR_CONF_RLE, SR_T_BOOL, "rle",
-		"Run Length Encoding", NULL},
-	{SR_CONF_TRIGGER_SLOPE, SR_T_STRING, "triggerslope",
-		"Trigger slope", NULL},
-	{SR_CONF_TRIGGER_SOURCE, SR_T_STRING, "triggersource",
-		"Trigger source", NULL},
-	{SR_CONF_HORIZ_TRIGGERPOS, SR_T_FLOAT, "horiz_triggerpos",
-		"Horizontal trigger position", NULL},
-	{SR_CONF_BUFFERSIZE, SR_T_UINT64, "buffersize",
-		"Buffer size", NULL},
-	{SR_CONF_TIMEBASE, SR_T_RATIONAL_PERIOD, "timebase",
-		"Time base", NULL},
-	{SR_CONF_FILTER, SR_T_STRING, "filter",
-		"Filter targets", NULL},
-	{SR_CONF_VDIV, SR_T_RATIONAL_VOLT, "vdiv",
-		"Volts/div", NULL},
-	{SR_CONF_COUPLING, SR_T_STRING, "coupling",
-		"Coupling", NULL},
-	{SR_CONF_DATALOG, SR_T_BOOL, "datalog",
-		"Datalog", NULL},
-	{SR_CONF_SPL_WEIGHT_FREQ, SR_T_STRING, "spl_weight_freq",
-		"Sound pressure level frequency weighting", NULL},
-	{SR_CONF_SPL_WEIGHT_TIME, SR_T_STRING, "spl_weight_time",
-		"Sound pressure level time weighting", NULL},
-	{SR_CONF_HOLD_MAX, SR_T_BOOL, "hold_max",
-		"Hold max", NULL},
-	{SR_CONF_HOLD_MIN, SR_T_BOOL, "hold_min",
-		"Hold min", NULL},
-	{SR_CONF_SPL_MEASUREMENT_RANGE, SR_T_UINT64_RANGE, "spl_meas_range",
-		"Sound pressure level measurement range", NULL},
-	{SR_CONF_VOLTAGE_THRESHOLD, SR_T_DOUBLE_RANGE, "voltage_threshold",
-		"Voltage threshold", NULL },
-	{SR_CONF_POWER_OFF, SR_T_BOOL, "power_off",
-		"Power off", NULL},
-	{SR_CONF_DATA_SOURCE, SR_T_STRING, "data_source",
-		"Data source", NULL},
-	{SR_CONF_NUM_LOGIC_CHANNELS, SR_T_INT32, "logic_channels",
-		"Number of logic channels", NULL},
-	{SR_CONF_NUM_ANALOG_CHANNELS, SR_T_INT32, "analog_channels",
-		"Number of analog channels", NULL},
-	{SR_CONF_OUTPUT_VOLTAGE, SR_T_FLOAT, "output_voltage",
-		"Current output voltage", NULL},
-	{SR_CONF_OUTPUT_VOLTAGE_MAX, SR_T_FLOAT, "output_voltage_max",
-		"Maximum output voltage", NULL},
-	{SR_CONF_OUTPUT_CURRENT, SR_T_FLOAT, "output_current",
-		"Current output current", NULL},
-	{SR_CONF_OUTPUT_CURRENT_MAX, SR_T_FLOAT, "output_current_max",
-		"Maximum output current", NULL},
-	{SR_CONF_OUTPUT_ENABLED, SR_T_BOOL, "output_enabled",
-		"Output enabled", NULL},
-	{SR_CONF_OUTPUT_CHANNEL, SR_T_STRING, "output_channel",
-		"Output channel modes", NULL},
-	{SR_CONF_OVER_VOLTAGE_PROTECTION, SR_T_BOOL, "ovp",
-		"Over-voltage protection", NULL},
-	{SR_CONF_OVER_CURRENT_PROTECTION, SR_T_BOOL, "ocp",
-		"Over-current protection", NULL},
-	{SR_CONF_LIMIT_SAMPLES, SR_T_UINT64, "limit_samples",
-		"Sample limit", NULL},
-	{SR_CONF_CLOCK_EDGE, SR_T_STRING, "clock_edge",
-		"Clock edge", NULL},
-	{0, 0, NULL, NULL, NULL},
-};
-
-/** @cond PRIVATE */
-#ifdef HAVE_HW_APPA_55II
-extern SR_PRIV struct sr_dev_driver appa_55ii_driver_info;
-#endif
-#ifdef HAVE_HW_ATTEN_PPS3XXX
-extern SR_PRIV struct sr_dev_driver atten_pps3203_driver_info;
-#endif
-#ifdef HAVE_HW_BRYMEN_BM86X
-extern SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info;
-#endif
-#ifdef HAVE_HW_BRYMEN_DMM
-extern SR_PRIV struct sr_dev_driver brymen_bm857_driver_info;
-#endif
-#ifdef HAVE_HW_CEM_DT_885X
-extern SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info;
-#endif
-#ifdef HAVE_HW_CENTER_3XX
-extern SR_PRIV struct sr_dev_driver center_309_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_k204_driver_info;
-#endif
-#ifdef HAVE_HW_COLEAD_SLM
-extern SR_PRIV struct sr_dev_driver colead_slm_driver_info;
-#endif
-#ifdef HAVE_HW_CONRAD_DIGI_35_CPU
-extern SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info;
-#endif
-#ifdef HAVE_HW_DEMO
-extern SR_PRIV struct sr_dev_driver demo_driver_info;
-#endif
-#ifdef HAVE_HW_GMC_MH_1X_2X
-extern SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info;
-extern SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info;
-#endif
-#ifdef HAVE_HW_HAMEG_HMO
-extern SR_PRIV struct sr_dev_driver hameg_hmo_driver_info;
-#endif
-#ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
-extern SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info;
-#endif
-#ifdef HAVE_HW_IKALOGIC_SCANAPLUS
-extern SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info;
-#endif
-#ifdef HAVE_HW_KECHENG_KC_330B
-extern SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info;
-#endif
-#ifdef HAVE_HW_LASCAR_EL_USB
-extern SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info;
-#endif
-#ifdef HAVE_HW_MIC_985XX
-extern SR_PRIV struct sr_dev_driver mic_98581_driver_info;
-extern SR_PRIV struct sr_dev_driver mic_98583_driver_info;
-#endif
-#ifdef HAVE_HW_NORMA_DMM
-extern SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
-#endif
-#ifdef HAVE_HW_OLS
-extern SR_PRIV struct sr_dev_driver ols_driver_info;
-#endif
-#ifdef HAVE_HW_RIGOL_DS
-extern SR_PRIV struct sr_dev_driver rigol_ds_driver_info;
-#endif
-#ifdef HAVE_HW_SALEAE_LOGIC16
-extern SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
-#endif
-#ifdef HAVE_HW_SYSCLK_LWLA
-extern SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info;
-#endif
-#ifdef HAVE_HW_TELEINFO
-extern SR_PRIV struct sr_dev_driver teleinfo_driver_info;
-#endif
-#ifdef HAVE_HW_TONDAJ_SL_814
-extern SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info;
-#endif
-#ifdef HAVE_HW_UNI_T_UT32X
-extern SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info;
-#endif
-#ifdef HAVE_HW_VICTOR_DMM
-extern SR_PRIV struct sr_dev_driver victor_dmm_driver_info;
-#endif
-#ifdef HAVE_HW_ZEROPLUS_LOGIC_CUBE
-extern SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info;
-#endif
-#ifdef HAVE_HW_ASIX_SIGMA
-extern SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
-#endif
-#ifdef HAVE_HW_CHRONOVU_LA
-extern SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
-#endif
-#ifdef HAVE_HW_LINK_MSO19
-extern SR_PRIV struct sr_dev_driver link_mso19_driver_info;
-#endif
-#ifdef HAVE_HW_FX2LAFW
-extern SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
-#endif
-#ifdef HAVE_HW_HANTEK_DSO
-extern SR_PRIV struct sr_dev_driver hantek_dso_driver_info;
-#endif
-#ifdef HAVE_HW_AGILENT_DMM
-extern SR_PRIV struct sr_dev_driver agdmm_driver_info;
-#endif
-#ifdef HAVE_HW_FLUKE_DMM
-extern SR_PRIV struct sr_dev_driver flukedmm_driver_info;
-#endif
-#ifdef HAVE_HW_SERIAL_DMM
-extern SR_PRIV struct sr_dev_driver bbcgm_m2110_driver_info;
-extern SR_PRIV struct sr_dev_driver digitek_dt4000zc_driver_info;
-extern SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info;
-extern SR_PRIV struct sr_dev_driver metex_me31_driver_info;
-extern SR_PRIV struct sr_dev_driver peaktech_3410_driver_info;
-extern SR_PRIV struct sr_dev_driver mastech_mas345_driver_info;
-extern SR_PRIV struct sr_dev_driver va_va18b_driver_info;
-extern SR_PRIV struct sr_dev_driver va_va40b_driver_info;
-extern SR_PRIV struct sr_dev_driver metex_m3640d_driver_info;
-extern SR_PRIV struct sr_dev_driver metex_m4650cr_driver_info;
-extern SR_PRIV struct sr_dev_driver peaktech_4370_driver_info;
-extern SR_PRIV struct sr_dev_driver pce_pce_dm32_driver_info;
-extern SR_PRIV struct sr_dev_driver radioshack_22_168_driver_info;
-extern SR_PRIV struct sr_dev_driver radioshack_22_805_driver_info;
-extern SR_PRIV struct sr_dev_driver radioshack_22_812_driver_info;
-extern SR_PRIV struct sr_dev_driver tecpel_dmm_8061_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_m3650cr_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_m3650d_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_m4650cr_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_me42_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc820_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc830_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc840_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60a_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60e_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60g_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61b_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61c_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61d_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info;
-extern SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info;
-extern SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info;
-#endif
-#ifdef HAVE_HW_UNI_T_DMM
-extern SR_PRIV struct sr_dev_driver tecpel_dmm_8061_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60a_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60e_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut60g_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61b_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61c_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61d_driver_info;
-extern SR_PRIV struct sr_dev_driver uni_t_ut61e_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc820_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc830_driver_info;
-extern SR_PRIV struct sr_dev_driver voltcraft_vc840_driver_info;
-extern SR_PRIV struct sr_dev_driver tenma_72_7745_driver_info;
-extern SR_PRIV struct sr_dev_driver tenma_72_7750_driver_info;
-#endif
-/** @endcond */
-
-static struct sr_dev_driver *drivers_list[] = {
-#ifdef HAVE_HW_APPA_55II
-	&appa_55ii_driver_info,
-#endif
-#ifdef HAVE_HW_ATTEN_PPS3XXX
-	&atten_pps3203_driver_info,
-#endif
-#ifdef HAVE_HW_BRYMEN_BM86X
-	&brymen_bm86x_driver_info,
-#endif
-#ifdef HAVE_HW_BRYMEN_DMM
-	&brymen_bm857_driver_info,
-#endif
-#ifdef HAVE_HW_CEM_DT_885X
-	&cem_dt_885x_driver_info,
-#endif
-#ifdef HAVE_HW_CENTER_3XX
-	&center_309_driver_info,
-	&voltcraft_k204_driver_info,
-#endif
-#ifdef HAVE_HW_COLEAD_SLM
-	&colead_slm_driver_info,
-#endif
-#ifdef HAVE_HW_CONRAD_DIGI_35_CPU
-	&conrad_digi_35_cpu_driver_info,
-#endif
-#ifdef HAVE_HW_DEMO
-	&demo_driver_info,
-#endif
-#ifdef HAVE_HW_GMC_MH_1X_2X
-	&gmc_mh_1x_2x_rs232_driver_info,
-	&gmc_mh_2x_bd232_driver_info,
-#endif
-#ifdef HAVE_HW_HAMEG_HMO
-	&hameg_hmo_driver_info,
-#endif
-#ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
-	&ikalogic_scanalogic2_driver_info,
-#endif
-#ifdef HAVE_HW_IKALOGIC_SCANAPLUS
-	&ikalogic_scanaplus_driver_info,
-#endif
-#ifdef HAVE_HW_KECHENG_KC_330B
-	&kecheng_kc_330b_driver_info,
-#endif
-#ifdef HAVE_HW_LASCAR_EL_USB
-	&lascar_el_usb_driver_info,
-#endif
-#ifdef HAVE_HW_MIC_985XX
-	&mic_98581_driver_info,
-	&mic_98583_driver_info,
-#endif
-#ifdef HAVE_HW_NORMA_DMM
-	&norma_dmm_driver_info,
-#endif
-#ifdef HAVE_HW_OLS
-	&ols_driver_info,
-#endif
-#ifdef HAVE_HW_RIGOL_DS
-	&rigol_ds_driver_info,
-#endif
-#ifdef HAVE_HW_SALEAE_LOGIC16
-	&saleae_logic16_driver_info,
-#endif
-#ifdef HAVE_HW_SYSCLK_LWLA
-	&sysclk_lwla_driver_info,
-#endif
-#ifdef HAVE_HW_TELEINFO
-	&teleinfo_driver_info,
-#endif
-#ifdef HAVE_HW_TONDAJ_SL_814
-	&tondaj_sl_814_driver_info,
-#endif
-#ifdef HAVE_HW_UNI_T_UT32X
-	&uni_t_ut32x_driver_info,
-#endif
-#ifdef HAVE_HW_VICTOR_DMM
-	&victor_dmm_driver_info,
-#endif
-#ifdef HAVE_HW_ZEROPLUS_LOGIC_CUBE
-	&zeroplus_logic_cube_driver_info,
-#endif
-#ifdef HAVE_HW_ASIX_SIGMA
-	&asix_sigma_driver_info,
-#endif
-#ifdef HAVE_HW_CHRONOVU_LA
-	&chronovu_la_driver_info,
-#endif
-#ifdef HAVE_HW_LINK_MSO19
-	&link_mso19_driver_info,
-#endif
-#ifdef HAVE_HW_FX2LAFW
-	&fx2lafw_driver_info,
-#endif
-#ifdef HAVE_HW_HANTEK_DSO
-	&hantek_dso_driver_info,
-#endif
-#ifdef HAVE_HW_AGILENT_DMM
-	&agdmm_driver_info,
-#endif
-#ifdef HAVE_HW_FLUKE_DMM
-	&flukedmm_driver_info,
-#endif
-#ifdef HAVE_HW_SERIAL_DMM
-	&bbcgm_m2110_driver_info,
-	&digitek_dt4000zc_driver_info,
-	&tekpower_tp4000zc_driver_info,
-	&metex_me31_driver_info,
-	&peaktech_3410_driver_info,
-	&mastech_mas345_driver_info,
-	&va_va18b_driver_info,
-	&va_va40b_driver_info,
-	&metex_m3640d_driver_info,
-	&metex_m4650cr_driver_info,
-	&peaktech_4370_driver_info,
-	&pce_pce_dm32_driver_info,
-	&radioshack_22_168_driver_info,
-	&radioshack_22_805_driver_info,
-	&radioshack_22_812_driver_info,
-	&tecpel_dmm_8061_ser_driver_info,
-	&voltcraft_m3650cr_driver_info,
-	&voltcraft_m3650d_driver_info,
-	&voltcraft_m4650cr_driver_info,
-	&voltcraft_me42_driver_info,
-	&voltcraft_vc820_ser_driver_info,
-	&voltcraft_vc830_ser_driver_info,
-	&voltcraft_vc840_ser_driver_info,
-	&uni_t_ut60a_ser_driver_info,
-	&uni_t_ut60e_ser_driver_info,
-	&uni_t_ut60g_ser_driver_info,
-	&uni_t_ut61b_ser_driver_info,
-	&uni_t_ut61c_ser_driver_info,
-	&uni_t_ut61d_ser_driver_info,
-	&uni_t_ut61e_ser_driver_info,
-	&iso_tech_idm103n_driver_info,
-	&tenma_72_7745_ser_driver_info,
-	&tenma_72_7750_ser_driver_info,
-#endif
-#ifdef HAVE_HW_UNI_T_DMM
-	&tecpel_dmm_8061_driver_info,
-	&uni_t_ut60a_driver_info,
-	&uni_t_ut60e_driver_info,
-	&uni_t_ut60g_driver_info,
-	&uni_t_ut61b_driver_info,
-	&uni_t_ut61c_driver_info,
-	&uni_t_ut61d_driver_info,
-	&uni_t_ut61e_driver_info,
-	&voltcraft_vc820_driver_info,
-	&voltcraft_vc830_driver_info,
-	&voltcraft_vc840_driver_info,
-	&tenma_72_7745_driver_info,
-	&tenma_72_7750_driver_info,
-#endif
-	NULL,
-};
-
-/**
- * Return the list of supported hardware drivers.
- *
- * @return Pointer to the NULL-terminated list of hardware driver pointers.
- *
- * @since 0.1.0
- */
-SR_API struct sr_dev_driver **sr_driver_list(void)
-{
-
-	return drivers_list;
-}
-
-/**
- * Initialize a hardware driver.
- *
- * This usually involves memory allocations and variable initializations
- * within the driver, but _not_ scanning for attached devices.
- * The API call sr_driver_scan() is used for that.
- *
- * @param ctx A libsigrok context object allocated by a previous call to
- *            sr_init(). Must not be NULL.
- * @param driver The driver to initialize. This must be a pointer to one of
- *               the entries returned by sr_driver_list(). Must not be NULL.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid parameter(s).
- * @retval SR_ERR_BUG Internal errors.
- * @retval other Another negative error code upon other errors.
- *
- * @since 0.2.0
- */
-SR_API int sr_driver_init(struct sr_context *ctx, struct sr_dev_driver *driver)
-{
-	int ret;
-
-	if (!ctx) {
-		sr_err("Invalid libsigrok context, can't initialize.");
-		return SR_ERR_ARG;
-	}
-
-	if (!driver) {
-		sr_err("Invalid driver, can't initialize.");
-		return SR_ERR_ARG;
-	}
-
-	sr_spew("Initializing driver '%s'.", driver->name);
-	if ((ret = driver->init(ctx)) < 0)
-		sr_err("Failed to initialize the driver: %d.", ret);
-
-	return ret;
-}
-
-/**
- * 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 driver The driver that should scan. This must be a pointer to one of
- *               the entries returned by sr_driver_list(). Must not be NULL.
- * @param options A 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
- */
-SR_API GSList *sr_driver_scan(struct sr_dev_driver *driver, GSList *options)
-{
-	GSList *l;
-
-	if (!driver) {
-		sr_err("Invalid driver, can't scan for devices.");
-		return NULL;
-	}
-
-	if (!driver->priv) {
-		sr_err("Driver not initialized, can't scan for devices.");
-		return NULL;
-	}
-
-	l = driver->scan(options);
-
-	sr_spew("Scan of '%s' found %d devices.", driver->name,
-		g_slist_length(l));
-
-	return l;
-}
-
-/** Call driver cleanup function for all drivers.
- *  @private */
-SR_PRIV void sr_hw_cleanup_all(void)
-{
-	int i;
-	struct sr_dev_driver **drivers;
-
-	drivers = sr_driver_list();
-	for (i = 0; drivers[i]; i++) {
-		if (drivers[i]->cleanup)
-			drivers[i]->cleanup();
-	}
-}
-
-/** Allocate struct sr_config.
- *  A floating reference can be passed in for data.
- *  @private
- */
-SR_PRIV struct sr_config *sr_config_new(int key, GVariant *data)
-{
-	struct sr_config *src;
-
-	if (!(src = g_try_malloc(sizeof(struct sr_config))))
-		return NULL;
-	src->key = key;
-	src->data = g_variant_ref_sink(data);
-
-	return src;
-}
-
-/** Free struct sr_config.
- *  @private
- */
-SR_PRIV void sr_config_free(struct sr_config *src)
-{
-
-	if (!src || !src->data) {
-		sr_err("%s: invalid data!", __func__);
-		return;
-	}
-
-	g_variant_unref(src->data);
-	g_free(src);
-
-}
-
-/**
- * Query value of a configuration key at the given driver or device instance.
- *
- * @param[in] driver The sr_dev_driver struct to query.
- * @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.
- * @param[in] cg The channel group on the device for which to list the
- *                    values, or NULL.
- * @param[in] key The configuration key (SR_CONF_*).
- * @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
- */
-SR_API int sr_config_get(const struct sr_dev_driver *driver,
-		const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg,
-		int key, GVariant **data)
-{
-	int ret;
-
-	if (!driver || !data)
-		return SR_ERR;
-
-	if (!driver->config_get)
-		return SR_ERR_ARG;
-
-	if ((ret = driver->config_get(key, data, sdi, cg)) == SR_OK) {
-		/* Got a floating reference from the driver. Sink it here,
-		 * caller will need to unref when done with it. */
-		g_variant_ref_sink(*data);
-	}
-
-	return ret;
-}
-
-/**
- * Set value of a configuration key in a device instance.
- *
- * @param[in] sdi The device instance.
- * @param[in] cg The channel group on the device for which to list the
- *                    values, or NULL.
- * @param[in] key The configuration key (SR_CONF_*).
- * @param data The new value for the key, as a GVariant with GVariantType
- *        appropriate to that key. A floating reference can be passed
- *        in; its refcount will be sunk and unreferenced after use.
- *
- * @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
- */
-SR_API int sr_config_set(const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg,
-		int key, GVariant *data)
-{
-	int ret;
-
-	g_variant_ref_sink(data);
-
-	if (!sdi || !sdi->driver || !data)
-		ret = SR_ERR;
-	else if (!sdi->driver->config_set)
-		ret = SR_ERR_ARG;
-	else
-		ret = sdi->driver->config_set(key, data, sdi, cg);
-
-	g_variant_unref(data);
-
-	return ret;
-}
-
-/**
- * Apply configuration settings to the device hardware.
- *
- * @param sdi The device instance.
- *
- * @return SR_OK upon success or SR_ERR in case of error.
- *
- * @since 0.3.0
- */
-SR_API int sr_config_commit(const struct sr_dev_inst *sdi)
-{
-	int ret;
-
-	if (!sdi || !sdi->driver)
-		ret = SR_ERR;
-	else if (!sdi->driver->config_commit)
-		ret = SR_OK;
-	else
-		ret = sdi->driver->config_commit(sdi);
-
-	return ret;
-}
-
-/**
- * List all possible values for a configuration key.
- *
- * @param[in] driver The sr_dev_driver struct to query.
- * @param[in] sdi (optional) If the key is specific to a device, this must
- *            contain a pointer to the struct sr_dev_inst to be checked.
- * @param[in] cg The channel group on the device for which to list the
- *                    values, or NULL.
- * @param[in] key The configuration key (SR_CONF_*).
- * @param[in,out] data A pointer to a GVariant where the list will be stored.
- *             The caller is given ownership of the GVariant and must thus
- *             unref the GVariant after use. However if this function
- *             returns an error code, the field should be considered
- *             unused, and should not be unreferenced.
- *
- * @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
- */
-SR_API int sr_config_list(const struct sr_dev_driver *driver,
-		const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg,
-		int key, GVariant **data)
-{
-	int ret;
-
-	if (!driver || !data)
-		ret = SR_ERR;
-	else if (!driver->config_list)
-		ret = SR_ERR_ARG;
-	else if ((ret = driver->config_list(key, data, sdi, cg)) == SR_OK)
-		g_variant_ref_sink(*data);
-
-	return ret;
-}
-
-/**
- * Get information about a configuration key, by key.
- *
- * @param[in] key The configuration key.
- *
- * @return A pointer to a struct sr_config_info, or NULL if the key
- *         was not found.
- *
- * @since 0.2.0
- */
-SR_API const struct sr_config_info *sr_config_info_get(int key)
-{
-	int i;
-
-	for (i = 0; sr_config_info_data[i].key; i++) {
-		if (sr_config_info_data[i].key == key)
-			return &sr_config_info_data[i];
-	}
-
-	return NULL;
-}
-
-/**
- * Get information about a configuration key, by name.
- *
- * @param[in] optname The configuration key.
- *
- * @return A pointer to a struct sr_config_info, or NULL if the key
- *         was not found.
- *
- * @since 0.2.0
- */
-SR_API const struct sr_config_info *sr_config_info_name_get(const char *optname)
-{
-	int i;
-
-	for (i = 0; sr_config_info_data[i].key; i++) {
-		if (!strcmp(sr_config_info_data[i].id, optname))
-			return &sr_config_info_data[i];
-	}
-
-	return NULL;
-}
-
-/* Unnecessary level of indirection follows. */
-
-/** @private
- *  @see sr_session_source_remove()
- */
-SR_PRIV int sr_source_remove(int fd)
-{
-	return sr_session_source_remove(fd);
-}
-
-/** @private
- *  @see sr_session_source_add()
- */
-SR_PRIV int sr_source_add(int fd, int events, int timeout,
-			  sr_receive_data_callback cb, void *cb_data)
-{
-	return sr_session_source_add(fd, events, timeout, cb, cb_data);
-}
-
-/** @} */
diff --git a/libsigrok.h b/include/libsigrok/libsigrok.h
similarity index 62%
rename from libsigrok.h
rename to include/libsigrok/libsigrok.h
index 0b6888d..a384f61 100644
--- a/libsigrok.h
+++ b/include/libsigrok/libsigrok.h
@@ -74,11 +74,10 @@ enum sr_error_code {
 	SR_ERR_DEV_CLOSED    = -7, /**< Device is closed, but must be open. */
 	SR_ERR_TIMEOUT       = -8, /**< A timeout occurred. */
 	SR_ERR_CHANNEL_GROUP = -9, /**< A channel group must be specified. */
+	SR_ERR_DATA          =-10, /**< Data is invalid.  */
+	SR_ERR_IO            =-11, /**< Input/output error. */
 
-	/*
-	 * Note: When adding entries here, don't forget to also update the
-	 * sr_strerror() and sr_strerror_name() functions in error.c.
-	 */
+	/* Update sr_strerror()/sr_strerror_name() (error.c) upon changes! */
 };
 
 #define SR_MAX_CHANNELNAME_LEN 32
@@ -146,6 +145,9 @@ enum sr_datatype {
 	SR_T_UINT64_RANGE,
 	SR_T_DOUBLE_RANGE,
 	SR_T_INT32,
+	SR_T_MQ,
+
+	/* Update sr_variant_type_get() (hwdriver.c) upon changes! */
 };
 
 /** Value for sr_datafeed_packet.type. */
@@ -160,15 +162,19 @@ enum sr_packettype {
 	SR_DF_TRIGGER,
 	/** Payload is struct sr_datafeed_logic. */
 	SR_DF_LOGIC,
-	/** Payload is struct sr_datafeed_analog. */
-	SR_DF_ANALOG,
+	/** DEPRECATED! Use SR_DF_ANALOG instead. */
+	SR_DF_ANALOG_OLD,
 	/** Beginning of frame. No payload. */
 	SR_DF_FRAME_BEGIN,
 	/** End of frame. No payload. */
 	SR_DF_FRAME_END,
+	/** Payload is struct sr_datafeed_analog. */
+	SR_DF_ANALOG,
+
+	/* Update datafeed_dump() (session.c) upon changes! */
 };
 
-/** Measured quantity, sr_datafeed_analog.mq. */
+/** Measured quantity, sr_analog_meaning.mq. */
 enum sr_mq {
 	SR_MQ_VOLTAGE = 10000,
 	SR_MQ_CURRENT,
@@ -195,9 +201,43 @@ enum sr_mq {
 	SR_MQ_RELATIVE_HUMIDITY,
 	/** Time */
 	SR_MQ_TIME,
+	/** Wind speed */
+	SR_MQ_WIND_SPEED,
+	/** Pressure */
+	SR_MQ_PRESSURE,
+	/** Parallel inductance (LCR meter model). */
+	SR_MQ_PARALLEL_INDUCTANCE,
+	/** Parallel capacitance (LCR meter model). */
+	SR_MQ_PARALLEL_CAPACITANCE,
+	/** Parallel resistance (LCR meter model). */
+	SR_MQ_PARALLEL_RESISTANCE,
+	/** Series inductance (LCR meter model). */
+	SR_MQ_SERIES_INDUCTANCE,
+	/** Series capacitance (LCR meter model). */
+	SR_MQ_SERIES_CAPACITANCE,
+	/** Series resistance (LCR meter model). */
+	SR_MQ_SERIES_RESISTANCE,
+	/** Dissipation factor. */
+	SR_MQ_DISSIPATION_FACTOR,
+	/** Quality factor. */
+	SR_MQ_QUALITY_FACTOR,
+	/** Phase angle. */
+	SR_MQ_PHASE_ANGLE,
+	/** Difference from reference value. */
+	SR_MQ_DIFFERENCE,
+	/** Count. */
+	SR_MQ_COUNT,
+	/** Power factor. */
+	SR_MQ_POWER_FACTOR,
+	/** Apparent power */
+	SR_MQ_APPARENT_POWER,
+	/** Mass */
+	SR_MQ_MASS,
+
+	/* Update sr_key_info_mq[] (hwdriver.c) upon changes! */
 };
 
-/** Unit of measured quantity, sr_datafeed_analog.unit. */
+/** Unit of measured quantity, sr_analog_meaning.unit. */
 enum sr_unit {
 	/** Volt */
 	SR_UNIT_VOLT = 10000,
@@ -236,7 +276,7 @@ enum sr_unit {
 	 * a unitless quantity, for example.
 	 */
 	SR_UNIT_UNITLESS,
-	/** Sound pressure level relative so 20 micropascals. */
+	/** Sound pressure level, in decibels, relative to 20 micropascals. */
 	SR_UNIT_DECIBEL_SPL,
 	/**
 	 * Normalized (0 to 1) concentration of a substance or compound with 0
@@ -252,9 +292,46 @@ enum sr_unit {
 	SR_UNIT_WATT,
 	/** Consumption [Wh]. */
 	SR_UNIT_WATT_HOUR,
+	/** Wind speed in meters per second. */
+	SR_UNIT_METER_SECOND,
+	/** Pressure in hectopascal */
+	SR_UNIT_HECTOPASCAL,
+	/** Relative humidity assuming air temperature of 293 Kelvin (%rF). */
+	SR_UNIT_HUMIDITY_293K,
+	/** Plane angle in 1/360th of a full circle. */
+	SR_UNIT_DEGREE,
+	/** Henry (inductance). */
+	SR_UNIT_HENRY,
+	/** Mass in gram [g]. */
+	SR_UNIT_GRAM,
+	/** Mass in carat [ct]. */
+	SR_UNIT_CARAT,
+	/** Mass in ounce [oz]. */
+	SR_UNIT_OUNCE,
+	/** Mass in troy ounce [oz t]. */
+	SR_UNIT_TROY_OUNCE,
+	/** Mass in pound [lb]. */
+	SR_UNIT_POUND,
+	/** Mass in pennyweight [dwt]. */
+	SR_UNIT_PENNYWEIGHT,
+	/** Mass in grain [gr]. */
+	SR_UNIT_GRAIN,
+	/** Mass in tael (variants: Hong Kong, Singapore/Malaysia, Taiwan) */
+	SR_UNIT_TAEL,
+	/** Mass in momme. */
+	SR_UNIT_MOMME,
+	/** Mass in tola. */
+	SR_UNIT_TOLA,
+	/** Pieces (number of items). */
+	SR_UNIT_PIECE,
+
+	/*
+	 * Update unit_strings[] (analog.c) and fancyprint() (output/analog.c)
+	 * upon changes!
+	 */
 };
 
-/** Values for sr_datafeed_analog.flags. */
+/** Values for sr_analog_meaning.mqflags. */
 enum sr_mqflag {
 	/** Voltage measurement is alternating current (AC). */
 	SR_MQFLAG_AC = 0x01,
@@ -302,6 +379,68 @@ enum sr_mqflag {
 	SR_MQFLAG_DURATION = 0x20000,
 	/** Device is in "avg" mode, averaging upon each new value. */
 	SR_MQFLAG_AVG = 0x40000,
+	/** Reference value shown. */
+	SR_MQFLAG_REFERENCE = 0x80000,
+	/** Unstable value (hasn't settled yet). */
+	SR_MQFLAG_UNSTABLE = 0x100000,
+
+	/*
+	 * Update mq_strings[] (analog.c) and fancyprint() (output/analog.c)
+	 * upon changes!
+	 */
+};
+
+enum sr_trigger_matches {
+	SR_TRIGGER_ZERO = 1,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
+	SR_TRIGGER_EDGE,
+	SR_TRIGGER_OVER,
+	SR_TRIGGER_UNDER,
+};
+
+/** The representation of a trigger, consisting of one or more stages
+ * containing one or more matches on a channel.
+ */
+struct sr_trigger {
+	/** A name for this trigger. This may be NULL if none is needed. */
+	char *name;
+	/** List of pointers to struct sr_trigger_stage. */
+	GSList *stages;
+};
+
+/** A trigger stage. */
+struct sr_trigger_stage {
+	/** Starts at 0. */
+	int stage;
+	/** List of pointers to struct sr_trigger_match. */
+	GSList *matches;
+};
+
+/** A channel to match and what to match it on. */
+struct sr_trigger_match {
+	/** The channel to trigger on. */
+	struct sr_channel *channel;
+	/** The trigger match to use.
+	 * For logic channels, only the following matches may be used:
+	 * SR_TRIGGER_ZERO
+	 * SR_TRIGGER_ONE
+	 * SR_TRIGGER_RISING
+	 * SR_TRIGGER_FALLING
+	 * SR_TRIGGER_EDGE
+	 *
+	 * For analog channels, only these matches may be used:
+	 * SR_TRIGGER_RISING
+	 * SR_TRIGGER_FALLING
+	 * SR_TRIGGER_OVER
+	 * SR_TRIGGER_UNDER
+	 *
+	 */
+	int match;
+	/** If the trigger match is one of SR_TRIGGER_OVER or SR_TRIGGER_UNDER,
+	 * this contains the value to compare against. */
+	float value;
 };
 
 /**
@@ -314,6 +453,23 @@ enum sr_mqflag {
  */
 struct sr_context;
 
+/**
+ * @struct sr_session
+ * Opaque structure representing a libsigrok session.
+ *
+ * None of the fields of this structure are meant to be accessed directly.
+ *
+ * @see sr_session_new(), sr_session_destroy().
+ */
+struct sr_session;
+
+struct sr_rational {
+	/** Numerator of the rational number. */
+	int64_t p;
+	/** Denominator of the rational number. */
+	uint64_t q;
+};
+
 /** Packet in a sigrok data feed. */
 struct sr_datafeed_packet {
 	uint16_t type;
@@ -338,8 +494,8 @@ struct sr_datafeed_logic {
 	void *data;
 };
 
-/** Analog datafeed payload for type SR_DF_ANALOG. */
-struct sr_datafeed_analog {
+/** Analog datafeed payload for type SR_DF_ANALOG_OLD. */
+struct sr_datafeed_analog_old {
 	/** The channels for which data is included in this packet. */
 	GSList *channels;
 	/** Number of samples in data */
@@ -356,175 +512,83 @@ struct sr_datafeed_analog {
 	float *data;
 };
 
-/** Input (file) format struct. */
-struct sr_input {
-	/**
-	 * A pointer to this input format's 'struct sr_input_format'.
-	 * The frontend can use this to call the module's callbacks.
-	 */
-	struct sr_input_format *format;
-
-	GHashTable *param;
-
-	struct sr_dev_inst *sdi;
-
-	void *internal;
+/** Analog datafeed payload for type SR_DF_ANALOG. */
+struct sr_datafeed_analog {
+	void *data;
+	uint32_t num_samples;
+	struct sr_analog_encoding *encoding;
+	struct sr_analog_meaning *meaning;
+	struct sr_analog_spec *spec;
 };
 
-/** Input (file) format driver. */
-struct sr_input_format {
-	/** The unique ID for this input format. Must not be NULL. */
-	char *id;
-
-	/**
-	 * A short description of the input format, which can (for example)
-	 * be displayed to the user by frontends. Must not be NULL.
-	 */
-	char *description;
-
-	/**
-	 * Check if this input module can load and parse the specified file.
-	 *
-	 * @param[in] filename The name (and path) of the file to check.
-	 *
-	 * @retval TRUE This module knows the format.
-	 * @retval FALSE This module does not know the format.
-	 */
-	int (*format_match) (const char *filename);
-
-	/**
-	 * Initialize the input module.
-	 *
-	 * @param in A pointer to a valid 'struct sr_input' that the caller
-	 *           has to allocate and provide to this function. It is also
-	 *           the responsibility of the caller to free it later.
-	 * @param[in] filename The name (and path) of the file to use.
-	 *
-	 * @retval SR_OK Success
-	 * @retval other Negative error code.
-	 */
-	int (*init) (struct sr_input *in, const char *filename);
-
-	/**
-	 * Load a file, parsing the input according to the file's format.
-	 *
-	 * This function will send datafeed packets to the session bus, so
-	 * the calling frontend must have registered its session callbacks
-	 * beforehand.
-	 *
-	 * The packet types sent across the session bus by this function must
-	 * include at least SR_DF_HEADER, SR_DF_END, and an appropriate data
-	 * type such as SR_DF_LOGIC. It may also send a SR_DF_TRIGGER packet
-	 * if appropriate.
-	 *
-	 * @param in A pointer to a valid 'struct sr_input' that the caller
-	 *           has to allocate and provide to this function. It is also
-	 *           the responsibility of the caller to free it later.
-	 * @param filename The name (and path) of the file to use.
-	 *
-	 * @retval SR_OK Success
-	 * @retval other Negative error code.
-	 */
-	int (*loadfile) (struct sr_input *in, const char *filename);
+struct sr_analog_encoding {
+	uint8_t unitsize;
+	gboolean is_signed;
+	gboolean is_float;
+	gboolean is_bigendian;
+	uint8_t digits;
+	gboolean is_digits_decimal;
+	struct sr_rational scale;
+	struct sr_rational offset;
 };
 
-/** Output (file) format struct. */
-struct sr_output {
-	/** A pointer to this output's format.  */
-	struct sr_output_format *format;
-
-	/**
-	 * The device for which this output module is creating output. This
-	 * can be used by the module to find out channel names and numbers.
-	 */
-	const struct sr_dev_inst *sdi;
-
-	/**
-	 * An optional parameter which the frontend can pass in to the
-	 * output module. How the string is interpreted is entirely up to
-	 * the module.
-	 */
-	GHashTable *params;
-
-	/**
-	 * A generic pointer which can be used by the module to keep internal
-	 * state between calls into its callback functions.
-	 *
-	 * For example, the module might store a pointer to a chunk of output
-	 * there, and only flush it when it reaches a certain size.
-	 */
-	void *internal;
+struct sr_analog_meaning {
+	enum sr_mq mq;
+	enum sr_unit unit;
+	enum sr_mqflag mqflags;
+	GSList *channels;
 };
 
-/** Output (file) format driver. */
-struct sr_output_format {
-	/**
-	 * A unique ID for this output format. Must not be NULL.
-	 *
-	 * It can be used by frontends to select this output format for use.
-	 *
-	 * For example, calling sigrok-cli with <code>-O hex</code> will
-	 * select the hexadecimal text output format.
-	 */
-	char *id;
+struct sr_analog_spec {
+	uint8_t spec_digits;
+};
 
-	/**
-	 * A short description of the output format. Must not be NULL.
-	 *
-	 * This can be displayed by frontends, e.g. when selecting the output
-	 * format for saving a file.
-	 */
-	char *description;
+/** Generic option struct used by various subsystems. */
+struct sr_option {
+	/* Short name suitable for commandline usage, [a-z0-9-]. */
+	const char *id;
+	/* Short name suitable for GUI usage, can contain UTF-8. */
+	const char *name;
+	/* Description of the option, in a sentence. */
+	const char *desc;
+	/* Default value for this option. */
+	GVariant *def;
+	/* List of possible values, if this is an option with few values. */
+	GSList *values;
+};
 
-	/**
-	 * This function is called once, at the beginning of an output stream.
-	 *
-	 * The device struct will be available in the output struct passed in,
-	 * as well as the param field -- which may be NULL or an empty string,
-	 * if no parameter was passed.
-	 *
-	 * The module can use this to initialize itself, create a struct for
-	 * keeping state and storing it in the <code>internal</code> field.
-	 *
-	 * @param o Pointer to the respective 'struct sr_output'.
-	 *
-	 * @retval SR_OK Success
-	 * @retval other Negative error code.
-	 */
-	int (*init) (struct sr_output *o);
+/** Resource type.
+ * @since 0.4.0
+ */
+enum sr_resource_type {
+	SR_RESOURCE_FIRMWARE = 1,
+};
 
-	/**
-	 * This function is passed a copy of every packed in the data feed.
-	 * Any output generated by the output module in response to the
-	 * packet should be returned in a newly allocated GString
-	 * <code>out</code>, which will be freed by the caller.
-	 *
-	 * Packets not of interest to the output module can just be ignored,
-	 * and the <code>out</code> parameter set to NULL.
-	 *
-	 * @param o Pointer to the respective 'struct sr_output'.
-	 * @param sdi The device instance that generated the packet.
-	 * @param packet The complete packet.
-	 * @param out A pointer where a GString * should be stored if
-	 * the module generates output, or NULL if not.
-	 *
-	 * @retval SR_OK Success
-	 * @retval other Negative error code.
-	 */
-	int (*receive) (struct sr_output *o,
-			const struct sr_datafeed_packet *packet, GString **out);
+/** Resource descriptor.
+ * @since 0.4.0
+ */
+struct sr_resource {
+	/** Size of resource in bytes; set by resource open callback. */
+	uint64_t size;
+	/** File handle or equivalent; set by resource open callback. */
+	void *handle;
+	/** Resource type (SR_RESOURCE_FIRMWARE, ...) */
+	int type;
+};
 
-	/**
-	 * This function is called after the caller is finished using
-	 * the output module, and can be used to free any internal
-	 * resources the module may keep.
-	 *
-	 * @retval SR_OK Success
-	 * @retval other Negative error code.
-	 */
-	int (*cleanup) (struct sr_output *o);
+/** Output module flags. */
+enum sr_output_flag {
+	/** If set, this output module writes the output itself. */
+	SR_OUTPUT_INTERNAL_IO_HANDLING = 0x01,
 };
 
+struct sr_input;
+struct sr_input_module;
+struct sr_output;
+struct sr_output_module;
+struct sr_transform;
+struct sr_transform_module;
+
 /** Constants for channel type. */
 enum sr_channeltype {
 	/** Channel type is logic channel. */
@@ -535,7 +599,10 @@ enum sr_channeltype {
 
 /** Information on single channel. */
 struct sr_channel {
-	/** Number of channels, starting at 0. */
+	/** The device this channel is attached to. */
+	struct sr_dev_inst *sdi;
+	/** The index of this channel, starting at 0. Logic channels will
+	 * be encoded according to this index in SR_DF_LOGIC packets. */
 	int index;
 	/** Channel type (SR_CHANNEL_LOGIC, ...) */
 	int type;
@@ -543,8 +610,8 @@ struct sr_channel {
 	gboolean enabled;
 	/** Name of channel. */
 	char *name;
-	/** Trigger string, format like used by sigrok-cli */
-	char *trigger;
+	/** Private data for driver use. */
+	void *priv;
 };
 
 /** Structure for groups of channels that have common properties. */
@@ -560,26 +627,42 @@ struct sr_channel_group {
 /** Used for setting or getting value of a config item. */
 struct sr_config {
 	/** Config key like SR_CONF_CONN, etc. */
-	int key;
+	uint32_t key;
 	/** Key-specific data. */
 	GVariant *data;
 };
 
-/** Information about a config key. */
-struct sr_config_info {
-	/** Config key like SR_CONF_CONN, etc. */
-	int key;
-	/** Data type like SR_T_STRING, etc. */
+enum sr_keytype {
+	SR_KEY_CONFIG,
+	SR_KEY_MQ,
+	SR_KEY_MQFLAGS,
+};
+
+/** Information about a key. */
+struct sr_key_info {
+	/** Config key like SR_CONF_CONN, MQ value like SR_MQ_VOLTAGE, etc. */
+	uint32_t key;
+	/** Data type like SR_T_STRING, etc if applicable. */
 	int datatype;
-	/** Id string, e.g. "serialcomm". */
-	char *id;
-	/** Name, e.g. "Serial communication". */
-	char *name;
+	/** Short, lowercase ID string, e.g. "serialcomm", "voltage". */
+	const char *id;
+	/** Full capitalized name, e.g. "Serial communication". */
+	const char *name;
 	/** Verbose description (unused currently). */
-	char *description;
+	const char *description;
 };
 
-/** Constants for device classes */
+/** Configuration capabilities. */
+enum sr_configcap {
+	/** Value can be read. */
+	SR_CONF_GET = (1 << 31),
+	/** Value can be written. */
+	SR_CONF_SET = (1 << 30),
+	/** Possible values can be enumerated. */
+	SR_CONF_LIST = (1 << 29),
+};
+
+/** Configuration keys */
 enum sr_configkey {
 	/*--- Device classes ------------------------------------------------*/
 
@@ -607,12 +690,23 @@ enum sr_configkey {
 	/** The device can measure energy consumption. */
 	SR_CONF_ENERGYMETER,
 
-	/** The device can demodulate signals. */
+	/** The device can act as a signal demodulator. */
 	SR_CONF_DEMODULATOR,
 
-	/** Programmable power supply. */
+	/** The device can act as a programmable power supply. */
 	SR_CONF_POWER_SUPPLY,
 
+	/** The device can act as an LCR meter. */
+	SR_CONF_LCRMETER,
+
+	/** The device can act as an electronic load. */
+	SR_CONF_ELECTRONIC_LOAD,
+
+	/** The device can act as a scale. */
+	SR_CONF_SCALE,
+
+	/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
+
 	/*--- Driver scan options -------------------------------------------*/
 
 	/**
@@ -648,7 +742,17 @@ enum sr_configkey {
 	 */
 	SR_CONF_SERIALCOMM,
 
-	/*--- Device configuration ------------------------------------------*/
+	/**
+	 * Modbus slave address specification.
+	 *
+	 * This is always an optional parameter, since a driver typically
+	 * knows the default slave address of the device.
+	 */
+	SR_CONF_MODBUSADDR,
+
+	/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
+
+	/*--- Device (or channel group) configuration -----------------------*/
 
 	/** The device supports setting its samplerate, in Hz. */
 	SR_CONF_SAMPLERATE = 30000,
@@ -659,12 +763,21 @@ enum sr_configkey {
 	/** The device supports setting a pattern (pattern generator mode). */
 	SR_CONF_PATTERN_MODE,
 
-	/** The device supports Run Length Encoding. */
+	/** The device supports run-length encoding (RLE). */
 	SR_CONF_RLE,
 
 	/** The device supports setting trigger slope. */
 	SR_CONF_TRIGGER_SLOPE,
 
+	/** The device supports averaging. */
+	SR_CONF_AVERAGING,
+
+	/**
+	 * The device supports setting number of samples to be
+	 * averaged over.
+	 */
+	SR_CONF_AVG_SAMPLES,
+
 	/** Trigger source. */
 	SR_CONF_TRIGGER_SOURCE,
 
@@ -686,14 +799,14 @@ enum sr_configkey {
 	/** Coupling. */
 	SR_CONF_COUPLING,
 
-	/** Trigger types.  */
-	SR_CONF_TRIGGER_TYPE,
+	/** Trigger matches.  */
+	SR_CONF_TRIGGER_MATCH,
 
 	/** The device supports setting its sample interval, in ms. */
 	SR_CONF_SAMPLE_INTERVAL,
 
-	/** Number of timebases, as related to SR_CONF_TIMEBASE.  */
-	SR_CONF_NUM_TIMEBASE,
+	/** Number of horizontal divisions, as related to SR_CONF_TIMEBASE. */
+	SR_CONF_NUM_HDIV,
 
 	/** Number of vertical divisions, as related to SR_CONF_VDIV.  */
 	SR_CONF_NUM_VDIV,
@@ -737,43 +850,138 @@ enum sr_configkey {
 	/** The device supports setting the number of analog channels. */
 	SR_CONF_NUM_ANALOG_CHANNELS,
 
-	/** Output voltage. */
-	SR_CONF_OUTPUT_VOLTAGE,
+	/**
+	 * Current voltage.
+	 * @arg type: double
+	 * @arg get: get measured voltage
+	 */
+	SR_CONF_VOLTAGE,
+
+	/**
+	 * Maximum target voltage.
+	 * @arg type: double
+	 * @arg get: get target voltage
+	 * @arg set: change target voltage
+	 */
+	SR_CONF_VOLTAGE_TARGET,
+
+	/**
+	 * Current current.
+	 * @arg type: double
+	 * @arg get: get measured current
+	 */
+	SR_CONF_CURRENT,
+
+	/**
+	 * Current limit.
+	 * @arg type: double
+	 * @arg get: get current limit
+	 * @arg set: change current limit
+	 */
+	SR_CONF_CURRENT_LIMIT,
+
+	/**
+	 * Enabling/disabling channel.
+	 * @arg type: boolean
+	 * @arg get: @b true if currently enabled
+	 * @arg set: enable/disable
+	 */
+	SR_CONF_ENABLED,
 
-	/** Maximum output voltage. */
-	SR_CONF_OUTPUT_VOLTAGE_MAX,
+	/**
+	 * Channel configuration.
+	 * @arg type: string
+	 * @arg get: get current setting
+	 * @arg set: change current setting
+	 * @arg list: array of possible values
+	 */
+	SR_CONF_CHANNEL_CONFIG,
 
-	/** Output current. */
-	SR_CONF_OUTPUT_CURRENT,
+	/**
+	 * Over-voltage protection (OVP) feature
+	 * @arg type: boolean
+	 * @arg get: @b true if currently enabled
+	 * @arg set: enable/disable
+	 */
+	SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED,
 
-	/** Maximum output current. */
-	SR_CONF_OUTPUT_CURRENT_MAX,
+	/**
+	 * Over-voltage protection (OVP) active
+	 * @arg type: boolean
+	 * @arg get: @b true if device has activated OVP, i.e. the output voltage
+	 *      exceeds the over-voltage protection threshold.
+	 */
+	SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE,
 
-	/** Enabling/disabling output. */
-	SR_CONF_OUTPUT_ENABLED,
+	/**
+	 * Over-voltage protection (OVP) threshold
+	 * @arg type: double (voltage)
+	 * @arg get: get current threshold
+	 * @arg set: set new threshold
+	 */
+	SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD,
 
-	/** Channel output configuration. */
-	SR_CONF_OUTPUT_CHANNEL,
+	/**
+	 * Over-current protection (OCP) feature
+	 * @arg type: boolean
+	 * @arg get: @b true if currently enabled
+	 * @arg set: enable/disable
+	 */
+	SR_CONF_OVER_CURRENT_PROTECTION_ENABLED,
 
-	/** Over-voltage protection (OVP) */
-	SR_CONF_OVER_VOLTAGE_PROTECTION,
+	/**
+	 * Over-current protection (OCP) active
+	 * @arg type: boolean
+	 * @arg get: @b true if device has activated OCP, i.e. the current current
+	 *      exceeds the over-current protection threshold.
+	 */
+	SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE,
 
-	/** Over-current protection (OCP) */
-	SR_CONF_OVER_CURRENT_PROTECTION,
+	/**
+	 * Over-current protection (OCP) threshold
+	 * @arg type: double (current)
+	 * @arg get: get current threshold
+	 * @arg set: set new threshold
+	 */
+	SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD,
 
 	/** Choice of clock edge for external clock ("r" or "f"). */
 	SR_CONF_CLOCK_EDGE,
 
-	/*--- Special stuff -------------------------------------------------*/
+	/** Amplitude of a source without strictly-defined MQ. */
+	SR_CONF_AMPLITUDE,
+
+	/**
+	 * Channel regulation
+	 * get: "CV", "CC" or "UR", denoting constant voltage, constant current
+	 *      or unregulated.
+	 */
+	SR_CONF_REGULATION,
+
+	/** Over-temperature protection (OTP) */
+	SR_CONF_OVER_TEMPERATURE_PROTECTION,
+
+	/** Output frequency in Hz. */
+	SR_CONF_OUTPUT_FREQUENCY,
+
+	/** Output frequency target in Hz. */
+	SR_CONF_OUTPUT_FREQUENCY_TARGET,
 
-	/** Scan options supported by the driver. */
-	SR_CONF_SCAN_OPTIONS = 40000,
+	/** Measured quantity. */
+	SR_CONF_MEASURED_QUANTITY,
 
-	/** Device options for a particular device. */
-	SR_CONF_DEVICE_OPTIONS,
+	/** Equivalent circuit model. */
+	SR_CONF_EQUIV_CIRCUIT_MODEL,
+
+	/** Over-temperature protection (OTP) active. */
+	SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE,
+
+	/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
+
+	/*--- Special stuff -------------------------------------------------*/
 
 	/** Session filename. */
-	SR_CONF_SESSIONFILE,
+	SR_CONF_SESSIONFILE = 40000,
 
 	/** The device supports specifying a capturefile to inject. */
 	SR_CONF_CAPTUREFILE,
@@ -798,7 +1006,12 @@ enum sr_configkey {
 	 */
 	SR_CONF_DATA_SOURCE,
 
-	/*--- Acquisition modes ---------------------------------------------*/
+	/** The device supports setting a probe factor. */
+	SR_CONF_PROBE_FACTOR,
+
+	/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
+
+	/*--- Acquisition modes, sample limiting ----------------------------*/
 
 	/**
 	 * The device supports setting a sample time limit (how long
@@ -834,34 +1047,16 @@ enum sr_configkey {
 
 	/** Self test mode. */
 	SR_CONF_TEST_MODE,
+
+	/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
 };
 
-/** Device instance data
+/**
+ * Opaque structure representing a libsigrok device instance.
+ *
+ * None of the fields of this structure are meant to be accessed directly.
  */
-struct sr_dev_inst {
-	/** Device driver. */
-	struct sr_dev_driver *driver;
-	/** Index of device in driver. */
-	int index;
-	/** Device instance status. SR_ST_NOT_FOUND, etc. */
-	int status;
-	/** Device instance type. SR_INST_USB, etc. */
-	int inst_type;
-	/** Device vendor. */
-	char *vendor;
-	/** Device model. */
-	char *model;
-	/** Device version. */
-	char *version;
-	/** List of channels. */
-	GSList *channels;
-	/** List of sr_channel_group structs */
-	GSList *channel_groups;
-	/** Device instance connection data (used?) */
-	void *conn;
-	/** Device instance private data (used?) */
-	void *priv;
-};
+struct sr_dev_inst;
 
 /** Types of device instance, struct sr_dev_inst.type */
 enum sr_dev_inst_type {
@@ -871,6 +1066,10 @@ enum sr_dev_inst_type {
 	SR_INST_SERIAL,
 	/** Device instance type for SCPI devices. */
 	SR_INST_SCPI,
+	/** Device-instance type for user-created "devices". */
+	SR_INST_USER,
+	/** Device instance type for Modbus devices. */
+	SR_INST_MODBUS,
 };
 
 /** Device instance status, struct sr_dev_inst.status */
@@ -891,43 +1090,43 @@ enum sr_dev_inst_status {
 struct sr_dev_driver {
 	/* Driver-specific */
 	/** Driver name. Lowercase a-z, 0-9 and dashes (-) only. */
-	char *name;
+	const char *name;
 	/** Long name. Verbose driver name shown to user. */
-	char *longname;
+	const char *longname;
 	/** API version (currently 1).	*/
 	int api_version;
 	/** Called when driver is loaded, e.g. program startup. */
-	int (*init) (struct sr_context *sr_ctx);
+	int (*init) (struct sr_dev_driver *driver, struct sr_context *sr_ctx);
 	/** Called before driver is unloaded.
-	 *  Driver must free all resouces held by it. */
-	int (*cleanup) (void);
+	 *  Driver must free all resources held by it. */
+	int (*cleanup) (const struct sr_dev_driver *driver);
 	/** Scan for devices. Driver should do all initialisation required.
 	 *  Can be called several times, e.g. with different port options.
-	 *  \retval NULL Error or no devices found.
-	 *  \retval other GSList of a struct sr_dev_inst for each device.
+	 *  @retval NULL Error or no devices found.
+	 *  @retval other GSList of a struct sr_dev_inst for each device.
 	 *                Must be freed by caller!
 	 */
-	GSList *(*scan) (GSList *options);
+	GSList *(*scan) (struct sr_dev_driver *driver, GSList *options);
 	/** Get list of device instances the driver knows about.
-	 *  \returns NULL or GSList of a struct sr_dev_inst for each device.
+	 *  @returns NULL or GSList of a struct sr_dev_inst for each device.
 	 *           Must not be freed by caller!
 	 */
-	GSList *(*dev_list) (void);
+	GSList *(*dev_list) (const struct sr_dev_driver *driver);
 	/** Clear list of devices the driver knows about. */
-	int (*dev_clear) (void);
+	int (*dev_clear) (const struct sr_dev_driver *driver);
 	/** Query value of a configuration key in driver or given device instance.
 	 *  @see sr_config_get().
 	 */
-	int (*config_get) (int id, GVariant **data,
+	int (*config_get) (uint32_t key, GVariant **data,
 			const struct sr_dev_inst *sdi,
 			const struct sr_channel_group *cg);
 	/** Set value of a configuration key in driver or a given device instance.
 	 *  @see sr_config_set(). */
-	int (*config_set) (int id, GVariant *data,
+	int (*config_set) (uint32_t key, GVariant *data,
 			const struct sr_dev_inst *sdi,
 			const struct sr_channel_group *cg);
 	/** Channel status change.
-	 *  @see sr_dev_channel_enable(), sr_dev_trigger_set(). */
+	 *  @see sr_dev_channel_enable(). */
 	int (*config_channel_set) (const struct sr_dev_inst *sdi,
 			struct sr_channel *ch, unsigned int changes);
 	/** Apply configuration settings to the device hardware.
@@ -936,7 +1135,7 @@ struct sr_dev_driver {
 	/** List all possible values for a configuration key in a device instance.
 	 *  @see sr_config_list().
 	 */
-	int (*config_list) (int info_id, GVariant **data,
+	int (*config_list) (uint32_t key, GVariant **data,
 			const struct sr_dev_inst *sdi,
 			const struct sr_channel_group *cg);
 
@@ -953,20 +1152,20 @@ struct sr_dev_driver {
 			void *cb_data);
 
 	/* Dynamic */
-	/** Device driver private data. Initialized by init(). */
-	void *priv;
+	/** Device driver context, considered private. Initialized by init(). */
+	void *context;
 };
 
-/**
- * @struct sr_session
- *
- * Opaque data structure representing a libsigrok session. None of the fields
- * of this structure are meant to be accessed directly.
- */
-struct sr_session;
+/** Serial port descriptor. */
+struct sr_serial_port {
+	/** The OS dependent name of the serial port. */
+	char *name;
+	/** An end user friendly description for the serial port. */
+	char *description;
+};
 
-#include "proto.h"
-#include "version.h"
+#include <libsigrok/proto.h>
+#include <libsigrok/version.h>
 
 #ifdef __cplusplus
 }
diff --git a/include/libsigrok/proto.h b/include/libsigrok/proto.h
new file mode 100644
index 0000000..6ae3be8
--- /dev/null
+++ b/include/libsigrok/proto.h
@@ -0,0 +1,246 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at 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_PROTO_H
+#define LIBSIGROK_PROTO_H
+
+/**
+ * @file
+ *
+ * Header file containing API function prototypes.
+ */
+
+/*--- analog.c --------------------------------------------------------------*/
+
+SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
+		float *buf);
+SR_API int sr_analog_unit_to_string(const struct sr_datafeed_analog *analog,
+		char **result);
+SR_API void sr_rational_set(struct sr_rational *r, int64_t p, uint64_t q);
+
+/*--- backend.c -------------------------------------------------------------*/
+
+SR_API int sr_init(struct sr_context **ctx);
+SR_API int sr_exit(struct sr_context *ctx);
+
+/*--- log.c -----------------------------------------------------------------*/
+
+typedef int (*sr_log_callback)(void *cb_data, int loglevel,
+				const char *format, va_list args);
+
+SR_API int sr_log_loglevel_set(int loglevel);
+SR_API int sr_log_loglevel_get(void);
+SR_API int sr_log_callback_set(sr_log_callback cb, void *cb_data);
+SR_API int sr_log_callback_set_default(void);
+
+/*--- device.c --------------------------------------------------------------*/
+
+SR_API int sr_dev_channel_name_set(struct sr_channel *channel,
+		const char *name);
+SR_API int sr_dev_channel_enable(struct sr_channel *channel,
+		gboolean state);
+SR_API gboolean sr_dev_has_option(const struct sr_dev_inst *sdi, int key);
+SR_API int sr_dev_config_capabilities_list(const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg, int key);
+SR_API GArray *sr_dev_options(const struct sr_dev_driver *driver,
+		const struct sr_dev_inst *sdi, const struct sr_channel_group *cg);
+SR_API GSList *sr_dev_list(const struct sr_dev_driver *driver);
+SR_API int sr_dev_clear(const struct sr_dev_driver *driver);
+SR_API int sr_dev_open(struct sr_dev_inst *sdi);
+SR_API int sr_dev_close(struct sr_dev_inst *sdi);
+
+SR_API struct sr_dev_driver *sr_dev_inst_driver_get(const struct sr_dev_inst *sdi);
+SR_API const char *sr_dev_inst_vendor_get(const struct sr_dev_inst *sdi);
+SR_API const char *sr_dev_inst_model_get(const struct sr_dev_inst *sdi);
+SR_API const char *sr_dev_inst_version_get(const struct sr_dev_inst *sdi);
+SR_API const char *sr_dev_inst_sernum_get(const struct sr_dev_inst *sdi);
+SR_API const char *sr_dev_inst_connid_get(const struct sr_dev_inst *sdi);
+SR_API GSList *sr_dev_inst_channels_get(const struct sr_dev_inst *sdi);
+SR_API GSList *sr_dev_inst_channel_groups_get(const struct sr_dev_inst *sdi);
+
+SR_API struct sr_dev_inst *sr_dev_inst_user_new(const char *vendor,
+		const char *model, const char *version);
+SR_API int sr_dev_inst_channel_add(struct sr_dev_inst *sdi, int index, int type, const char *name);
+
+/*--- hwdriver.c ------------------------------------------------------------*/
+
+SR_API struct sr_dev_driver **sr_driver_list(const struct sr_context *ctx);
+SR_API int sr_driver_init(struct sr_context *ctx,
+		struct sr_dev_driver *driver);
+SR_API GArray *sr_driver_scan_options_list(const struct sr_dev_driver *driver);
+SR_API GSList *sr_driver_scan(struct sr_dev_driver *driver, GSList *options);
+SR_API int sr_config_get(const struct sr_dev_driver *driver,
+		const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg,
+		uint32_t key, GVariant **data);
+SR_API int sr_config_set(const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg,
+		uint32_t key, GVariant *data);
+SR_API int sr_config_commit(const struct sr_dev_inst *sdi);
+SR_API int sr_config_list(const struct sr_dev_driver *driver,
+		const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg,
+		uint32_t key, GVariant **data);
+SR_API const struct sr_key_info *sr_key_info_get(int keytype, uint32_t key);
+SR_API const struct sr_key_info *sr_key_info_name_get(int keytype, const char *keyid);
+
+/*--- session.c -------------------------------------------------------------*/
+
+typedef void (*sr_session_stopped_callback)(void *data);
+typedef void (*sr_datafeed_callback)(const struct sr_dev_inst *sdi,
+		const struct sr_datafeed_packet *packet, void *cb_data);
+
+SR_API struct sr_trigger *sr_session_trigger_get(struct sr_session *session);
+
+/* Session setup */
+SR_API int sr_session_load(struct sr_context *ctx, const char *filename,
+	struct sr_session **session);
+SR_API int sr_session_new(struct sr_context *ctx, struct sr_session **session);
+SR_API int sr_session_destroy(struct sr_session *session);
+SR_API int sr_session_dev_remove_all(struct sr_session *session);
+SR_API int sr_session_dev_add(struct sr_session *session,
+		struct sr_dev_inst *sdi);
+SR_API int sr_session_dev_remove(struct sr_session *session,
+		struct sr_dev_inst *sdi);
+SR_API int sr_session_dev_list(struct sr_session *session, GSList **devlist);
+SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger *trig);
+
+/* Datafeed setup */
+SR_API int sr_session_datafeed_callback_remove_all(struct sr_session *session);
+SR_API int sr_session_datafeed_callback_add(struct sr_session *session,
+		sr_datafeed_callback cb, void *cb_data);
+
+/* Session control */
+SR_API int sr_session_start(struct sr_session *session);
+SR_API int sr_session_run(struct sr_session *session);
+SR_API int sr_session_stop(struct sr_session *session);
+SR_API int sr_session_is_running(struct sr_session *session);
+SR_API int sr_session_stopped_callback_set(struct sr_session *session,
+		sr_session_stopped_callback cb, void *cb_data);
+
+/*--- input/input.c ---------------------------------------------------------*/
+
+SR_API const struct sr_input_module **sr_input_list(void);
+SR_API const char *sr_input_id_get(const struct sr_input_module *imod);
+SR_API const char *sr_input_name_get(const struct sr_input_module *imod);
+SR_API const char *sr_input_description_get(const struct sr_input_module *imod);
+SR_API const char *const *sr_input_extensions_get(
+		const struct sr_input_module *imod);
+SR_API const struct sr_input_module *sr_input_find(char *id);
+SR_API const struct sr_option **sr_input_options_get(const struct sr_input_module *imod);
+SR_API gboolean sr_output_test_flag(const struct sr_output_module *omod,
+		uint64_t flag);
+SR_API void sr_input_options_free(const struct sr_option **options);
+SR_API struct sr_input *sr_input_new(const struct sr_input_module *imod,
+		GHashTable *options);
+SR_API int sr_input_scan_buffer(GString *buf, const struct sr_input **in);
+SR_API int sr_input_scan_file(const char *filename, const struct sr_input **in);
+SR_API struct sr_dev_inst *sr_input_dev_inst_get(const struct sr_input *in);
+SR_API int sr_input_send(const struct sr_input *in, GString *buf);
+SR_API int sr_input_end(const struct sr_input *in);
+SR_API void sr_input_free(const struct sr_input *in);
+
+/*--- output/output.c -------------------------------------------------------*/
+
+SR_API const struct sr_output_module **sr_output_list(void);
+SR_API const char *sr_output_id_get(const struct sr_output_module *omod);
+SR_API const char *sr_output_name_get(const struct sr_output_module *omod);
+SR_API const char *sr_output_description_get(const struct sr_output_module *omod);
+SR_API const char *const *sr_output_extensions_get(
+		const struct sr_output_module *omod);
+SR_API const struct sr_output_module *sr_output_find(char *id);
+SR_API const struct sr_option **sr_output_options_get(const struct sr_output_module *omod);
+SR_API void sr_output_options_free(const struct sr_option **opts);
+SR_API const struct sr_output *sr_output_new(const struct sr_output_module *omod,
+		GHashTable *params, const struct sr_dev_inst *sdi,
+		const char *filename);
+SR_API int sr_output_send(const struct sr_output *o,
+		const struct sr_datafeed_packet *packet, GString **out);
+SR_API int sr_output_free(const struct sr_output *o);
+
+/*--- transform/transform.c -------------------------------------------------*/
+
+SR_API const struct sr_transform_module **sr_transform_list(void);
+SR_API const char *sr_transform_id_get(const struct sr_transform_module *tmod);
+SR_API const char *sr_transform_name_get(const struct sr_transform_module *tmod);
+SR_API const char *sr_transform_description_get(const struct sr_transform_module *tmod);
+SR_API const struct sr_transform_module *sr_transform_find(const char *id);
+SR_API const struct sr_option **sr_transform_options_get(const struct sr_transform_module *tmod);
+SR_API void sr_transform_options_free(const struct sr_option **opts);
+SR_API const struct sr_transform *sr_transform_new(const struct sr_transform_module *tmod,
+		GHashTable *params, const struct sr_dev_inst *sdi);
+SR_API int sr_transform_free(const struct sr_transform *t);
+
+/*--- trigger.c -------------------------------------------------------------*/
+
+SR_API struct sr_trigger *sr_trigger_new(const char *name);
+SR_API void sr_trigger_free(struct sr_trigger *trig);
+SR_API struct sr_trigger_stage *sr_trigger_stage_add(struct sr_trigger *trig);
+SR_API int sr_trigger_match_add(struct sr_trigger_stage *stage,
+		struct sr_channel *ch, int trigger_match, float value);
+
+/*--- serial.c --------------------------------------------------------------*/
+
+SR_API GSList *sr_serial_list(const struct sr_dev_driver *driver);
+SR_API void sr_serial_free(struct sr_serial_port *serial);
+
+/*--- resource.c ------------------------------------------------------------*/
+
+typedef int (*sr_resource_open_callback)(struct sr_resource *res,
+		const char *name, void *cb_data);
+typedef int (*sr_resource_close_callback)(struct sr_resource *res,
+		void *cb_data);
+typedef gssize (*sr_resource_read_callback)(const struct sr_resource *res,
+		void *buf, size_t count, void *cb_data);
+
+SR_API int sr_resource_set_hooks(struct sr_context *ctx,
+		sr_resource_open_callback open_cb,
+		sr_resource_close_callback close_cb,
+		sr_resource_read_callback read_cb, void *cb_data);
+
+/*--- strutil.c -------------------------------------------------------------*/
+
+SR_API char *sr_si_string_u64(uint64_t x, const char *unit);
+SR_API char *sr_samplerate_string(uint64_t samplerate);
+SR_API char *sr_period_string(uint64_t frequency);
+SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q);
+SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size);
+SR_API uint64_t sr_parse_timestring(const char *timestring);
+SR_API gboolean sr_parse_boolstring(const char *boolstring);
+SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q);
+SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q);
+
+/*--- version.c -------------------------------------------------------------*/
+
+SR_API int sr_package_version_major_get(void);
+SR_API int sr_package_version_minor_get(void);
+SR_API int sr_package_version_micro_get(void);
+SR_API const char *sr_package_version_string_get(void);
+
+SR_API int sr_lib_version_current_get(void);
+SR_API int sr_lib_version_revision_get(void);
+SR_API int sr_lib_version_age_get(void);
+SR_API const char *sr_lib_version_string_get(void);
+
+/*--- error.c ---------------------------------------------------------------*/
+
+SR_API const char *sr_strerror(int error_code);
+SR_API const char *sr_strerror_name(int error_code);
+
+#endif
diff --git a/version.h b/include/libsigrok/version.h.in
similarity index 84%
rename from version.h
rename to include/libsigrok/version.h.in
index 069dd70..1c977bd 100644
--- a/version.h
+++ b/include/libsigrok/version.h.in
@@ -37,32 +37,32 @@
  */
 
 /** The libsigrok package 'major' version number. */
-#define SR_PACKAGE_VERSION_MAJOR 0
+#undef SR_PACKAGE_VERSION_MAJOR
 
 /** The libsigrok package 'minor' version number. */
-#define SR_PACKAGE_VERSION_MINOR 3
+#undef SR_PACKAGE_VERSION_MINOR
 
 /** The libsigrok package 'micro' version number. */
-#define SR_PACKAGE_VERSION_MICRO 0
+#undef SR_PACKAGE_VERSION_MICRO
 
 /** The libsigrok package version ("major.minor.micro") as string. */
-#define SR_PACKAGE_VERSION_STRING "0.3.0"
+#undef SR_PACKAGE_VERSION_STRING
 
 /*
  * Library/libtool version macros (can be used for conditional compilation).
  */
 
 /** The libsigrok libtool 'current' version number. */
-#define SR_LIB_VERSION_CURRENT 2
+#undef SR_LIB_VERSION_CURRENT
 
 /** The libsigrok libtool 'revision' version number. */
-#define SR_LIB_VERSION_REVISION 0
+#undef SR_LIB_VERSION_REVISION
 
 /** The libsigrok libtool 'age' version number. */
-#define SR_LIB_VERSION_AGE 0
+#undef SR_LIB_VERSION_AGE
 
 /** The libsigrok libtool version ("current:revision:age") as string. */
-#define SR_LIB_VERSION_STRING "2:0:0"
+#undef SR_LIB_VERSION_STRING
 
 /** @} */
 
diff --git a/input/binary.c b/input/binary.c
deleted file mode 100644
index e0d4c1d..0000000
--- a/input/binary.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert at 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 <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/binary"
-
-#define CHUNKSIZE             (512 * 1024)
-#define DEFAULT_NUM_CHANNELS  8
-
-struct context {
-	uint64_t samplerate;
-};
-
-static int format_match(const char *filename)
-{
-	(void)filename;
-
-	/* This module will handle anything you throw at it. */
-	return TRUE;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
-	struct sr_channel *ch;
-	int num_channels, i;
-	char name[SR_MAX_CHANNELNAME_LEN + 1];
-	char *param;
-	struct context *ctx;
-
-	(void)filename;
-
-	if (!(ctx = g_try_malloc0(sizeof(*ctx)))) {
-		sr_err("Input format context malloc failed.");
-		return SR_ERR_MALLOC;
-	}
-
-	num_channels = DEFAULT_NUM_CHANNELS;
-	ctx->samplerate = 0;
-
-	if (in->param) {
-		param = g_hash_table_lookup(in->param, "numchannels");
-		if (param) {
-			num_channels = strtoul(param, NULL, 10);
-			if (num_channels < 1)
-				return SR_ERR;
-		}
-
-		param = g_hash_table_lookup(in->param, "samplerate");
-		if (param) {
-			if (sr_parse_sizestring(param, &ctx->samplerate) != SR_OK)
-				return SR_ERR;
-		}
-	}
-
-	/* Create a virtual device. */
-	in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
-	in->internal = ctx;
-
-	for (i = 0; i < num_channels; i++) {
-		snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
-		/* TODO: Check return value. */
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name)))
-			return SR_ERR;
-		in->sdi->channels = g_slist_append(in->sdi->channels, ch);
-	}
-
-	return SR_OK;
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
-	struct sr_datafeed_packet packet;
-	struct sr_datafeed_meta meta;
-	struct sr_datafeed_logic logic;
-	struct sr_config *src;
-	unsigned char buffer[CHUNKSIZE];
-	int fd, size, num_channels;
-	struct context *ctx;
-
-	ctx = in->internal;
-
-	if ((fd = open(filename, O_RDONLY)) == -1)
-		return SR_ERR;
-
-	num_channels = g_slist_length(in->sdi->channels);
-
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(in->sdi, LOG_PREFIX);
-
-	if (ctx->samplerate) {
-		packet.type = SR_DF_META;
-		packet.payload = &meta;
-		src = sr_config_new(SR_CONF_SAMPLERATE,
-				g_variant_new_uint64(ctx->samplerate));
-		meta.config = g_slist_append(NULL, src);
-		sr_session_send(in->sdi, &packet);
-		sr_config_free(src);
-	}
-
-	/* Chop up the input file into chunks & send it to the session bus. */
-	packet.type = SR_DF_LOGIC;
-	packet.payload = &logic;
-	logic.unitsize = (num_channels + 7) / 8;
-	logic.data = buffer;
-	while ((size = read(fd, buffer, CHUNKSIZE)) > 0) {
-		logic.length = size;
-		sr_session_send(in->sdi, &packet);
-	}
-	close(fd);
-
-	/* Send end packet to the session bus. */
-	packet.type = SR_DF_END;
-	sr_session_send(in->sdi, &packet);
-
-	g_free(ctx);
-	in->internal = NULL;
-
-	return SR_OK;
-}
-
-SR_PRIV struct sr_input_format input_binary = {
-	.id = "binary",
-	.description = "Raw binary",
-	.format_match = format_match,
-	.init = init,
-	.loadfile = loadfile,
-};
diff --git a/input/chronovu_la8.c b/input/chronovu_la8.c
deleted file mode 100644
index dcedcf8..0000000
--- a/input/chronovu_la8.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Uwe Hermann <uwe at 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 <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/chronovu-la8"
-
-#define NUM_PACKETS		2048
-#define PACKET_SIZE		4096
-#define DEFAULT_NUM_CHANNELS	8
-
-/**
- * Convert the LA8 'divcount' value to the respective samplerate (in Hz).
- *
- * 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 divcount The divcount value as needed by the hardware.
- *
- * @return The samplerate in Hz, or 0xffffffffffffffff upon errors.
- */
-static uint64_t divcount_to_samplerate(uint8_t divcount)
-{
-	if (divcount == 0xff)
-		return 0xffffffffffffffffULL;
-
-	return SR_MHZ(100) / (divcount + 1);
-}
-
-static int format_match(const char *filename)
-{
-	struct stat stat_buf;
-	int ret;
-
-	if (!filename) {
-		sr_err("%s: filename was NULL", __func__);
-		// return SR_ERR; /* FIXME */
-		return FALSE;
-	}
-
-	if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
-		sr_err("%s: input file '%s' does not exist",
-		       __func__, filename);
-		// return SR_ERR; /* FIXME */
-		return FALSE;
-	}
-
-	if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
-		sr_err("%s: input file '%s' not a regular file",
-		       __func__, filename);
-		// return SR_ERR; /* FIXME */
-		return FALSE;
-	}
-
-	/* Only accept files of length 8MB + 5 bytes. */
-	ret = stat(filename, &stat_buf);
-	if (ret != 0) {
-		sr_err("%s: Error getting file size of '%s'",
-		       __func__, filename);
-		return FALSE;
-	}
-	if (stat_buf.st_size != (8 * 1024 * 1024 + 5)) {
-		sr_dbg("%s: File size must be exactly 8388613 bytes ("
-		       "it actually is %d bytes in size), so this is not a "
-		       "ChronoVu LA8 file.", __func__, stat_buf.st_size);
-		return FALSE;
-	}
-
-	/* TODO: Check for divcount != 0xff. */
-
-	return TRUE;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
-	struct sr_channel *ch;
-	int num_channels, i;
-	char name[SR_MAX_CHANNELNAME_LEN + 1];
-	char *param;
-
-	(void)filename;
-
-	num_channels = DEFAULT_NUM_CHANNELS;
-
-	if (in->param) {
-		param = g_hash_table_lookup(in->param, "numchannels");
-		if (param) {
-			num_channels = strtoul(param, NULL, 10);
-			if (num_channels < 1) {
-				sr_err("%s: strtoul failed", __func__);
-				return SR_ERR;
-			}
-		}
-	}
-
-	/* Create a virtual device. */
-	in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
-
-	for (i = 0; i < num_channels; i++) {
-		snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
-		/* TODO: Check return value. */
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name)))
-			return SR_ERR;
-		in->sdi->channels = g_slist_append(in->sdi->channels, ch);
-	}
-
-	return SR_OK;
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
-	struct sr_datafeed_packet packet;
-	struct sr_datafeed_meta meta;
-	struct sr_datafeed_logic logic;
-	struct sr_config *src;
-	uint8_t buf[PACKET_SIZE], divcount;
-	int i, fd, size, num_channels;
-	uint64_t samplerate;
-
-	/* TODO: Use glib functions! GIOChannel, g_fopen, etc. */
-	if ((fd = open(filename, O_RDONLY)) == -1) {
-		sr_err("%s: file open failed", __func__);
-		return SR_ERR;
-	}
-
-	num_channels = g_slist_length(in->sdi->channels);
-
-	/* Seek to the end of the file, and read the divcount byte. */
-	divcount = 0x00; /* TODO: Don't hardcode! */
-
-	/* Convert the divcount value to a samplerate. */
-	samplerate = divcount_to_samplerate(divcount);
-	if (samplerate == 0xffffffffffffffffULL) {
-		close(fd); /* FIXME */
-		return SR_ERR;
-	}
-	sr_dbg("%s: samplerate is %" PRIu64, __func__, samplerate);
-
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(in->sdi, LOG_PREFIX);
-
-	/* Send metadata about the SR_DF_LOGIC packets to come. */
-	packet.type = SR_DF_META;
-	packet.payload = &meta;
-	src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
-	meta.config = g_slist_append(NULL, src);
-	sr_session_send(in->sdi, &packet);
-	sr_config_free(src);
-
-	/* TODO: Handle trigger point. */
-
-	/* Send data packets to the session bus. */
-	sr_dbg("%s: sending SR_DF_LOGIC data packets", __func__);
-	packet.type = SR_DF_LOGIC;
-	packet.payload = &logic;
-	logic.unitsize = (num_channels + 7) / 8;
-	logic.data = buf;
-
-	/* Send 8MB of total data to the session bus in small chunks. */
-	for (i = 0; i < NUM_PACKETS; i++) {
-		/* TODO: Handle errors, handle incomplete reads. */
-		size = read(fd, buf, PACKET_SIZE);
-		logic.length = size;
-		sr_session_send(in->sdi, &packet);
-	}
-	close(fd); /* FIXME */
-
-	/* Send end packet to the session bus. */
-	sr_dbg("%s: sending SR_DF_END", __func__);
-	packet.type = SR_DF_END;
-	packet.payload = NULL;
-	sr_session_send(in->sdi, &packet);
-
-	return SR_OK;
-}
-
-SR_PRIV struct sr_input_format input_chronovu_la8 = {
-	.id = "chronovu-la8",
-	.description = "ChronoVu LA8",
-	.format_match = format_match,
-	.init = init,
-	.loadfile = loadfile,
-};
diff --git a/input/csv.c b/input/csv.c
deleted file mode 100644
index 6dd06f4..0000000
--- a/input/csv.c
+++ /dev/null
@@ -1,868 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Marc Schink <sigrok-dev at marcschink.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/csv"
-
-/*
- * The CSV input module has the following options:
- *
- * single-column: Specifies the column number which stores the sample data for
- *                single column mode and enables single column mode. Multi
- *                column mode is used if this parameter is omitted.
- *
- * numchannels:   Specifies the number of channels to use. In multi column mode
- *                the number of channels are the number of columns and in single
- *                column mode the number of bits (LSB first) beginning at
- *                'first-channel'.
- *
- * delimiter:     Specifies the delimiter for columns. Must be at least one
- *                character. Comma is used as default delimiter.
- *
- * format:        Specifies the format of the sample data in single column mode.
- *                Available formats are: 'bin', 'hex' and 'oct'. The binary
- *                format is used by default. This option has no effect in multi
- *                column mode.
- *
- * comment:       Specifies the prefix character(s) for comments. No prefix
- *                characters are used by default which disables removing of
- *                comments.
- *
- * samplerate:    Samplerate which the sample data was captured with. Default
- *                value is 0.
- *
- * first-channel: Column number of the first channel in multi column mode and
- *                position of the bit for the first channel in single column mode.
- *                Default value is 0.
- *
- * header:        Determines if the first line should be treated as header
- *                and used for channel names in multi column mode. Empty header
- *                names will be replaced by the channel number. If enabled in
- *                single column mode the first line will be skipped. Usage of
- *                header is disabled by default.
- *
- * startline:     Line number to start processing sample data. Must be greater
- *                than 0. The default line number to start processing is 1.
- */
-
-/* Single column formats. */
-enum {
-	FORMAT_BIN,
-	FORMAT_HEX,
-	FORMAT_OCT
-};
-
-struct context {
-	/* Current selected samplerate. */
-	uint64_t samplerate;
-
-	/* Number of channels. */
-	gsize num_channels;
-
-	/* Column delimiter character(s). */
-	GString *delimiter;
-
-	/* Comment prefix character(s). */
-	GString *comment;
-
-	/* Determines if sample data is stored in multiple columns. */
-	gboolean multi_column_mode;
-
-	/* Column number of the sample data in single column mode. */
-	gsize single_column;
-
-	/*
-	 * Number of the first column to parse. Equivalent to the number of the
-	 * first channel in multi column mode and the single column number in
-	 * single column mode.
-	 */
-	gsize first_column;
-
-	/*
-	 * Column number of the first channel in multi column mode and position of
-	 * the bit for the first channel in single column mode.
-	 */
-	gsize first_channel;
-
-	/* Line number to start processing. */
-	gsize start_line;
-
-	/*
-	 * Determines if the first line should be treated as header and used for
-	 * channel names in multi column mode.
-	 */
-	gboolean header;
-
-	/* Format sample data is stored in single column mode. */
-	int format;
-
-	/* Size of the sample buffer. */
-	gsize sample_buffer_size;
-
-	/* Buffer to store sample data. */
-	uint8_t *sample_buffer;
-
-	GIOChannel *channel;
-
-	/* Buffer for the current line. */
-	GString *buffer;
-
-	/* Current line number. */
-	gsize line_number;
-};
-
-static int format_match(const char *filename)
-{
-	if (!filename) {
-		sr_err("%s: filename was NULL.", __func__);
-		return FALSE;
-	}
-
-	if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
-		sr_err("Input file '%s' does not exist.", filename);
-		return FALSE;
-	}
-
-	if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
-		sr_err("Input file '%s' not a regular file.", filename);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static void free_context(struct context *ctx)
-{
-	if (!ctx)
-		return;
-
-	if (ctx->delimiter)
-		g_string_free(ctx->delimiter, TRUE);
-
-	if (ctx->comment)
-		g_string_free(ctx->comment, TRUE);
-
-	if (ctx->channel) {
-		g_io_channel_shutdown(ctx->channel, FALSE, NULL);
-		g_io_channel_unref(ctx->channel);
-	}
-
-	if (ctx->sample_buffer)
-		g_free(ctx->sample_buffer);
-
-	if (ctx->buffer)
-		g_string_free(ctx->buffer, TRUE);
-
-	g_free(ctx);
-}
-
-static void strip_comment(GString *string, const GString *prefix)
-{
-	char *ptr;
-
-	if (!prefix->len)
-		return;
-
-	if (!(ptr = strstr(string->str, prefix->str)))
-		return;
-
-	g_string_truncate(string, ptr - string->str);
-}
-
-static int parse_binstr(const char *str, struct context *ctx)
-{
-	gsize i, j, length;
-
-	length = strlen(str);
-
-	if (!length) {
-		sr_err("Column %zu in line %zu is empty.", ctx->single_column,
-			ctx->line_number);
-		return SR_ERR;
-	}
-
-	/* Clear buffer in order to set bits only. */
-	memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
-
-	i = ctx->first_channel;
-
-	for (j = 0; i < length && j < ctx->num_channels; i++, j++) {
-		if (str[length - i - 1] == '1') {
-			ctx->sample_buffer[j / 8] |= (1 << (j % 8));
-		} else if (str[length - i - 1] != '0') {
-			sr_err("Invalid value '%s' in column %zu in line %zu.",
-				str, ctx->single_column, ctx->line_number);
-			return SR_ERR;
-		}
-	}
-
-	return SR_OK;
-}
-
-static int parse_hexstr(const char *str, struct context *ctx)
-{
-	gsize i, j, k, length;
-	uint8_t value;
-	char c;
-
-	length = strlen(str);
-
-	if (!length) {
-		sr_err("Column %zu in line %zu is empty.", ctx->single_column,
-			ctx->line_number);
-		return SR_ERR;
-	}
-
-	/* Clear buffer in order to set bits only. */
-	memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
-
-	/* Calculate the position of the first hexadecimal digit. */
-	i = ctx->first_channel / 4;
-
-	for (j = 0; i < length && j < ctx->num_channels; i++) {
-		c = str[length - i - 1];
-
-		if (!g_ascii_isxdigit(c)) {
-			sr_err("Invalid value '%s' in column %zu in line %zu.",
-				str, ctx->single_column, ctx->line_number);
-			return SR_ERR;
-		}
-
-		value = g_ascii_xdigit_value(c);
-
-		k = (ctx->first_channel + j) % 4;
-
-		for (; j < ctx->num_channels && k < 4; k++) {
-			if (value & (1 << k))
-				ctx->sample_buffer[j / 8] |= (1 << (j % 8));
-
-			j++;
-		}
-	}
-
-	return SR_OK;
-}
-
-static int parse_octstr(const char *str, struct context *ctx)
-{
-	gsize i, j, k, length;
-	uint8_t value;
-	char c;
-
-	length = strlen(str);
-
-	if (!length) {
-		sr_err("Column %zu in line %zu is empty.", ctx->single_column,
-			ctx->line_number);
-		return SR_ERR;
-	}
-
-	/* Clear buffer in order to set bits only. */
-	memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
-
-	/* Calculate the position of the first octal digit. */
-	i = ctx->first_channel / 3;
-
-	for (j = 0; i < length && j < ctx->num_channels; i++) {
-		c = str[length - i - 1];
-
-		if (c < '0' || c > '7') {
-			sr_err("Invalid value '%s' in column %zu in line %zu.",
-				str, ctx->single_column, ctx->line_number);
-			return SR_ERR;
-		}
-
-		value = g_ascii_xdigit_value(c);
-
-		k = (ctx->first_channel + j) % 3;
-
-		for (; j < ctx->num_channels && k < 3; k++) {
-			if (value & (1 << k))
-				ctx->sample_buffer[j / 8] |= (1 << (j % 8));
-
-			j++;
-		}
-	}
-
-	return SR_OK;
-}
-
-static char **parse_line(const struct context *ctx, int max_columns)
-{
-	const char *str, *remainder;
-	GSList *list, *l;
-	char **columns;
-	char *column;
-	gsize n, k;
-
-	n = 0;
-	k = 0;
-	list = NULL;
-
-	remainder = ctx->buffer->str;
-	str = strstr(remainder, ctx->delimiter->str);
-
-	while (str && max_columns) {
-		if (n >= ctx->first_column) {
-			column = g_strndup(remainder, str - remainder);
-			list = g_slist_prepend(list, g_strstrip(column));
-
-			max_columns--;
-			k++;
-		}
-
-		remainder = str + ctx->delimiter->len;
-		str = strstr(remainder, ctx->delimiter->str);
-		n++;
-	}
-
-	if (ctx->buffer->len && max_columns && n >= ctx->first_column) {
-		column = g_strdup(remainder);
-		list = g_slist_prepend(list, g_strstrip(column));
-		k++;
-	}
-
-	if (!(columns = g_try_new(char *, k + 1)))
-		return NULL;
-
-	columns[k--] = NULL;
-
-	for (l = list; l; l = l->next)
-		columns[k--] = l->data;
-
-	g_slist_free(list);
-
-	return columns;
-}
-
-static int parse_multi_columns(char **columns, struct context *ctx)
-{
-	gsize i;
-
-	/* Clear buffer in order to set bits only. */
-	memset(ctx->sample_buffer, 0, (ctx->num_channels + 7) >> 3);
-
-	for (i = 0; i < ctx->num_channels; i++) {
-		if (columns[i][0] == '1') {
-			ctx->sample_buffer[i / 8] |= (1 << (i % 8));
-		} else if (!strlen(columns[i])) {
-			sr_err("Column %zu in line %zu is empty.",
-				ctx->first_channel + i, ctx->line_number);
-			return SR_ERR;
-		} else if (columns[i][0] != '0') {
-			sr_err("Invalid value '%s' in column %zu in line %zu.",
-				columns[i], ctx->first_channel + i,
-				ctx->line_number);
-			return SR_ERR;
-		}
-	}
-
-	return SR_OK;
-}
-
-static int parse_single_column(const char *column, struct context *ctx)
-{
-	int res;
-
-	res = SR_ERR;
-
-	switch(ctx->format) {
-	case FORMAT_BIN:
-		res = parse_binstr(column, ctx);
-		break;
-	case FORMAT_HEX:
-		res = parse_hexstr(column, ctx);
-		break;
-	case FORMAT_OCT:
-		res = parse_octstr(column, ctx);
-		break;
-	}
-
-	return res;
-}
-
-static int send_samples(const struct sr_dev_inst *sdi, uint8_t *buffer,
-			gsize buffer_size, gsize count)
-{
-	int res;
-	struct sr_datafeed_packet packet;
-	struct sr_datafeed_logic logic;
-	gsize i;
-
-	packet.type = SR_DF_LOGIC;
-	packet.payload = &logic;
-	logic.unitsize = buffer_size;
-	logic.length = buffer_size;
-	logic.data = buffer;
-
-	for (i = 0; i < count; i++) {
-		if ((res = sr_session_send(sdi, &packet)) != SR_OK)
-			return res;
-	}
-
-	return SR_OK;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
-	int res;
-	struct context *ctx;
-	const char *param;
-	GIOStatus status;
-	gsize i, term_pos;
-	char channel_name[SR_MAX_CHANNELNAME_LEN + 1];
-	struct sr_channel *ch;
-	char **columns;
-	gsize num_columns;
-	char *ptr;
-
-	if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
-		sr_err("Context malloc failed.");
-		return SR_ERR_MALLOC;
-	}
-
-	/* Create a virtual device. */
-	in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
-	in->internal = ctx;
-
-	/* Set default samplerate. */
-	ctx->samplerate = 0;
-
-	/*
-	 * Enable auto-detection of the number of channels in multi column mode
-	 * and enforce the specification of the number of channels in single
-	 * column mode.
-	 */
-	ctx->num_channels = 0;
-
-	/* Set default delimiter. */
-	if (!(ctx->delimiter = g_string_new(","))) {
-		sr_err("Delimiter malloc failed.");
-		free_context(ctx);
-		return SR_ERR_MALLOC;
-	}
-
-	/*
-	 * Set default comment prefix. Note that an empty comment prefix
-	 * disables removing of comments.
-	 */
-	if (!(ctx->comment = g_string_new(""))) {
-		sr_err("Comment malloc failed.");
-		free_context(ctx);
-		return SR_ERR_MALLOC;
-	}
-
-	/* Enable multi column mode by default. */
-	ctx->multi_column_mode = TRUE;
-
-	/* Use first column as default single column number. */
-	ctx->single_column = 0;
-
-	/*
-	 * In multi column mode start parsing sample data at the first column
-	 * and in single column mode at the first bit.
-	 */
-	ctx->first_channel = 0;
-
-	/* Start at the beginning of the file. */
-	ctx->start_line = 1;
-
-	/* Disable the usage of the first line as header by default. */
-	ctx->header = FALSE;
-
-	/* Set default format for single column mode. */
-	ctx->format = FORMAT_BIN;
-
-	if (!(ctx->buffer = g_string_new(""))) {
-		sr_err("Line buffer malloc failed.");
-		free_context(ctx);
-		return SR_ERR_MALLOC;
-	}
-
-	if (in->param) {
-		if ((param = g_hash_table_lookup(in->param, "samplerate"))) {
-			res = sr_parse_sizestring(param, &ctx->samplerate);
-
-			if (res != SR_OK) {
-				sr_err("Invalid samplerate: %s.", param);
-				free_context(ctx);
-				return SR_ERR_ARG;
-			}
-		}
-
-		if ((param = g_hash_table_lookup(in->param, "numchannels")))
-			ctx->num_channels = g_ascii_strtoull(param, NULL, 10);
-
-		if ((param = g_hash_table_lookup(in->param, "delimiter"))) {
-			if (!strlen(param)) {
-				sr_err("Delimiter must be at least one character.");
-				free_context(ctx);
-				return SR_ERR_ARG;
-			}
-
-			if (!g_ascii_strcasecmp(param, "\\t"))
-				g_string_assign(ctx->delimiter, "\t");
-			else
-				g_string_assign(ctx->delimiter, param);
-		}
-
-		if ((param = g_hash_table_lookup(in->param, "comment")))
-			g_string_assign(ctx->comment, param);
-
-		if ((param = g_hash_table_lookup(in->param, "single-column"))) {
-			ctx->single_column = g_ascii_strtoull(param, &ptr, 10);
-			ctx->multi_column_mode = FALSE;
-
-			if (param == ptr) {
-				sr_err("Invalid single-colum number: %s.",
-					param);
-				free_context(ctx);
-				return SR_ERR_ARG;
-			}
-		}
-
-		if ((param = g_hash_table_lookup(in->param, "first-channel")))
-			ctx->first_channel = g_ascii_strtoull(param, NULL, 10);
-
-		if ((param = g_hash_table_lookup(in->param, "startline"))) {
-			ctx->start_line = g_ascii_strtoull(param, NULL, 10);
-
-			if (ctx->start_line < 1) {
-				sr_err("Invalid start line: %s.", param);
-				free_context(ctx);
-				return SR_ERR_ARG;
-			}
-		}
-
-		if ((param = g_hash_table_lookup(in->param, "header")))
-			ctx->header = sr_parse_boolstring(param);
-
-		if ((param = g_hash_table_lookup(in->param, "format"))) {
-			if (!g_ascii_strncasecmp(param, "bin", 3)) {
-				ctx->format = FORMAT_BIN;
-			} else if (!g_ascii_strncasecmp(param, "hex", 3)) {
-				ctx->format = FORMAT_HEX;
-			} else if (!g_ascii_strncasecmp(param, "oct", 3)) {
-				ctx->format = FORMAT_OCT;
-			} else {
-				sr_err("Invalid format: %s.", param);
-				free_context(ctx);
-				return SR_ERR;
-			}
-		}
-	}
-
-	if (ctx->multi_column_mode)
-		ctx->first_column = ctx->first_channel;
-	else
-		ctx->first_column = ctx->single_column;
-
-	if (!ctx->multi_column_mode && !ctx->num_channels) {
-		sr_err("Number of channels needs to be specified in single column mode.");
-		free_context(ctx);
-		return SR_ERR;
-	}
-
-	if (!(ctx->channel = g_io_channel_new_file(filename, "r", NULL))) {
-		sr_err("Input file '%s' could not be opened.", filename);
-		free_context(ctx);
-		return SR_ERR;
-	}
-
-	while (TRUE) {
-		ctx->line_number++;
-		status = g_io_channel_read_line_string(ctx->channel,
-			ctx->buffer, &term_pos, NULL);
-
-		if (status == G_IO_STATUS_EOF) {
-			sr_err("Input file is empty.");
-			free_context(ctx);
-			return SR_ERR;
-		}
-
-		if (status != G_IO_STATUS_NORMAL) {
-			sr_err("Error while reading line %zu.",
-				ctx->line_number);
-			free_context(ctx);
-			return SR_ERR;
-		}
-
-		if (ctx->start_line > ctx->line_number) {
-			sr_spew("Line %zu skipped.", ctx->line_number);
-			continue;
-		}
-
-		/* Remove line termination character(s). */
-		g_string_truncate(ctx->buffer, term_pos);
-
-		if (!ctx->buffer->len) {
-			sr_spew("Blank line %zu skipped.", ctx->line_number);
-			continue;
-		}
-
-		/* Remove trailing comment. */
-		strip_comment(ctx->buffer, ctx->comment);
-
-		if (ctx->buffer->len)
-			break;
-
-		sr_spew("Comment-only line %zu skipped.", ctx->line_number);
-	}
-
-	/*
-	 * In order to determine the number of columns parse the current line
-	 * without limiting the number of columns.
-	 */
-	if (!(columns = parse_line(ctx, -1))) {
-		sr_err("Error while parsing line %zu.", ctx->line_number);
-		free_context(ctx);
-		return SR_ERR;
-	}
-
-	num_columns = g_strv_length(columns);
-
-	/* Ensure that the first column is not out of bounds. */
-	if (!num_columns) {
-		sr_err("Column %zu in line %zu is out of bounds.",
-			ctx->first_column, ctx->line_number);
-		g_strfreev(columns);
-		free_context(ctx);
-		return SR_ERR;
-	}
-
-	if (ctx->multi_column_mode) {
-		/*
-		 * Detect the number of channels in multi column mode
-		 * automatically if not specified.
-		 */
-		if (!ctx->num_channels) {
-			ctx->num_channels = num_columns;
-			sr_info("Number of auto-detected channels: %zu.",
-				ctx->num_channels);
-		}
-
-		/*
-		 * Ensure that the number of channels does not exceed the number
-		 * of columns in multi column mode.
-		 */
-		if (num_columns < ctx->num_channels) {
-			sr_err("Not enough columns for desired number of channels in line %zu.",
-				ctx->line_number);
-			g_strfreev(columns);
-			free_context(ctx);
-			return SR_ERR;
-		}
-	}
-
-	for (i = 0; i < ctx->num_channels; i++) {
-		if (ctx->header && ctx->multi_column_mode && strlen(columns[i]))
-			snprintf(channel_name, sizeof(channel_name), "%s",
-				columns[i]);
-		else
-			snprintf(channel_name, sizeof(channel_name), "%zu", i);
-
-		ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name);
-
-		if (!ch) {
-			sr_err("Channel creation failed.");
-			free_context(ctx);
-			g_strfreev(columns);
-			return SR_ERR;
-		}
-
-		in->sdi->channels = g_slist_append(in->sdi->channels, ch);
-	}
-
-	g_strfreev(columns);
-
-	/*
-	 * Calculate the minimum buffer size to store the sample data of the
-	 * channels.
-	 */
-	ctx->sample_buffer_size = (ctx->num_channels + 7) >> 3;
-
-	if (!(ctx->sample_buffer = g_try_malloc(ctx->sample_buffer_size))) {
-		sr_err("Sample buffer malloc failed.");
-		free_context(ctx);
-		return SR_ERR_MALLOC;
-	}
-
-	return SR_OK;
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
-	int res;
-	struct context *ctx;
-	struct sr_datafeed_packet packet;
-	struct sr_datafeed_meta meta;
-	struct sr_config *cfg;
-	GIOStatus status;
-	gboolean read_new_line;
-	gsize term_pos;
-	char **columns;
-	gsize num_columns;
-	int max_columns;
-
-	(void)filename;
-
-	ctx = in->internal;
-
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(in->sdi, LOG_PREFIX);
-
-	if (ctx->samplerate) {
-		packet.type = SR_DF_META;
-		packet.payload = &meta;
-		cfg = sr_config_new(SR_CONF_SAMPLERATE,
-			g_variant_new_uint64(ctx->samplerate));
-		meta.config = g_slist_append(NULL, cfg);
-		sr_session_send(in->sdi, &packet);
-		sr_config_free(cfg);
-	}
-
-	read_new_line = FALSE;
-
-	/* Limit the number of columns to parse. */
-	if (ctx->multi_column_mode)
-		max_columns = ctx->num_channels;
-	else
-		max_columns = 1;
-
-	while (TRUE) {
-		/*
-		 * Skip reading a new line for the first time if the last read
-		 * line was not a header because the sample data is not parsed
-		 * yet.
-		 */
-		if (read_new_line || ctx->header) {
-			ctx->line_number++;
-			status = g_io_channel_read_line_string(ctx->channel,
-				ctx->buffer, &term_pos, NULL);
-
-			if (status == G_IO_STATUS_EOF)
-				break;
-
-			if (status != G_IO_STATUS_NORMAL) {
-				sr_err("Error while reading line %zu.",
-					ctx->line_number);
-				free_context(ctx);
-				return SR_ERR;
-			}
-
-			/* Remove line termination character(s). */
-			g_string_truncate(ctx->buffer, term_pos);
-		}
-
-		read_new_line = TRUE;
-
-		if (!ctx->buffer->len) {
-			sr_spew("Blank line %zu skipped.", ctx->line_number);
-			continue;
-		}
-
-		/* Remove trailing comment. */
-		strip_comment(ctx->buffer, ctx->comment);
-
-		if (!ctx->buffer->len) {
-			sr_spew("Comment-only line %zu skipped.",
-				ctx->line_number);
-			continue;
-		}
-
-		if (!(columns = parse_line(ctx, max_columns))) {
-			sr_err("Error while parsing line %zu.",
-				ctx->line_number);
-			free_context(ctx);
-			return SR_ERR;
-		}
-
-		num_columns = g_strv_length(columns);
-
-		/* Ensure that the first column is not out of bounds. */
-		if (!num_columns) {
-			sr_err("Column %zu in line %zu is out of bounds.",
-				ctx->first_column, ctx->line_number);
-			g_strfreev(columns);
-			free_context(ctx);
-			return SR_ERR;
-		}
-
-		/*
-		 * Ensure that the number of channels does not exceed the number
-		 * of columns in multi column mode.
-		 */
-		if (ctx->multi_column_mode && num_columns < ctx->num_channels) {
-			sr_err("Not enough columns for desired number of channels in line %zu.",
-				ctx->line_number);
-			g_strfreev(columns);
-			free_context(ctx);
-			return SR_ERR;
-		}
-
-		if (ctx->multi_column_mode)
-			res = parse_multi_columns(columns, ctx);
-		else
-			res = parse_single_column(columns[0], ctx);
-
-		if (res != SR_OK) {
-			g_strfreev(columns);
-			free_context(ctx);
-			return SR_ERR;
-		}
-
-		g_strfreev(columns);
-
-		/*
-		 * TODO: Parse sample numbers / timestamps and use it for
-		 * decompression.
-		 */
-
-		/* Send sample data to the session bus. */
-		res = send_samples(in->sdi, ctx->sample_buffer,
-			ctx->sample_buffer_size, 1);
-
-		if (res != SR_OK) {
-			sr_err("Sending samples failed.");
-			free_context(ctx);
-			return SR_ERR;
-		}
-	}
-
-	/* Send end packet to the session bus. */
-	packet.type = SR_DF_END;
-	sr_session_send(in->sdi, &packet);
-
-	free_context(ctx);
-
-	return SR_OK;
-}
-
-SR_PRIV struct sr_input_format input_csv = {
-	.id = "csv",
-	.description = "Comma-separated values (CSV)",
-	.format_match = format_match,
-	.init = init,
-	.loadfile = loadfile,
-};
diff --git a/input/input.c b/input/input.c
deleted file mode 100644
index d22b373..0000000
--- a/input/input.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert at 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 "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/**
- * @file
- *
- * Input file/data format handling.
- */
-
-/**
- * @defgroup grp_input Input formats
- *
- * Input file/data format handling.
- *
- * libsigrok can process acquisition data in several different ways.
- * Aside from acquiring data from a hardware device, it can also take it from
- * a file in various formats (binary, CSV, VCD, and so on).
- *
- * Like everything in libsigrok that handles data, processing is done in a
- * streaming manner -- input should be supplied to libsigrok a chunk at a time.
- * This way anything that processes data can do so in real time, without the
- * user having to wait for the whole thing to be finished.
- *
- * Every input module is "pluggable", meaning it's handled as being separate
- * from the main libsigrok, but linked in to it statically. To keep things
- * modular and separate like this, functions within an input module should be
- * declared static, with only the respective 'struct sr_input_format' being
- * exported for use into the wider libsigrok namespace.
- *
- * @{
- */
-
-/** @cond PRIVATE */
-extern SR_PRIV struct sr_input_format input_chronovu_la8;
-extern SR_PRIV struct sr_input_format input_csv;
-extern SR_PRIV struct sr_input_format input_binary;
-extern SR_PRIV struct sr_input_format input_vcd;
-extern SR_PRIV struct sr_input_format input_wav;
-/* @endcond */
-
-static struct sr_input_format *input_module_list[] = {
-	&input_vcd,
-	&input_chronovu_la8,
-	&input_wav,
-	&input_csv,
-	/* This one has to be last, because it will take any input. */
-	&input_binary,
-	NULL,
-};
-
-/** @since 0.1.0 */
-SR_API struct sr_input_format **sr_input_list(void)
-{
-	return input_module_list;
-}
-
-/** @} */
diff --git a/input/vcd.c b/input/vcd.c
deleted file mode 100644
index 6759d73..0000000
--- a/input/vcd.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2012 Petteri Aimonen <jpa at sr.mail.kapsi.fi>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/* The VCD input module has the following options:
- *
- * numchannels: Maximum number of channels to use. The channels are
- *              detected in the same order as they are listed
- *              in the $var sections of the VCD file.
- *
- * skip:        Allows skipping until given timestamp in the file.
- *              This can speed up analyzing of long captures.
- *            
- *              Value < 0: Skip until first timestamp listed in
- *              the file. (default)
- *
- *              Value = 0: Do not skip, instead generate samples
- *              beginning from timestamp 0.
- *
- *              Value > 0: Start at the given timestamp.
- *
- * downsample:  Divide the samplerate by the given factor.
- *              This can speed up analyzing of long captures.
- *
- * compress:    Compress idle periods longer than this value.
- *              This can speed up analyzing of long captures.
- *              Default 0 = don't compress.
- *
- * Based on Verilog standard IEEE Std 1364-2001 Version C
- *
- * Supported features:
- * - $var with 'wire' and 'reg' types of scalar variables
- * - $timescale definition for samplerate
- * - multiple character variable identifiers
- *
- * Most important unsupported features:
- * - vector variables (bit vectors etc.)
- * - analog, integer and real number variables
- * - $dumpvars initial value declaration
- * - $scope namespaces
- * - more than 64 channels
- */
-
-#include <stdlib.h>
-#include <glib.h>
-#include <stdio.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/vcd"
-
-#define DEFAULT_NUM_CHANNELS 8
-#define CHUNKSIZE 1024
-
-struct context {
-	uint64_t samplerate;
-	int maxchannels;
-	int channelcount;
-	int downsample;
-	unsigned compress;
-	int64_t skip;
-	GSList *channels;
-};
-
-struct vcd_channel {
-	gchar *name;
-	gchar *identifier;
-};
-
-
-/* Read until specific type of character occurs in file.
- * Skip input if dest is NULL.
- * Modes:
- * 'W' read until whitespace
- * 'N' read until non-whitespace, and ungetc() the character
- * '$' read until $end
- */
-static gboolean read_until(FILE *file, GString *dest, char mode)
-{
-	int  c;
-	char prev[4] = "";
-
-	for(;;) {
-		c = fgetc(file);
-
-		if (c == EOF) {
-			if (mode == '$')
-				sr_err("Unexpected EOF.");
-			return FALSE;
-		}
-
-		if (mode == 'W' && g_ascii_isspace(c))
-			return TRUE;
-
-		if (mode == 'N' && !g_ascii_isspace(c)) {
-			ungetc(c, file);
-			return TRUE;
-		}
-
-		if (mode == '$') {
-			prev[0] = prev[1]; prev[1] = prev[2]; prev[2] = prev[3]; prev[3] = c;
-			if (prev[0] == '$' && prev[1] == 'e' && prev[2] == 'n' && prev[3] == 'd') {
-				if (dest != NULL)
-					g_string_truncate(dest, dest->len - 3);
-
-				return TRUE;
-			}
-		}
-
-		if (dest != NULL)
-			g_string_append_c(dest, c);
-	}
-}
-
-/*
- * Reads a single VCD section from input file and parses it to structure.
- * e.g. $timescale 1ps $end  => "timescale" "1ps"
- */
-static gboolean parse_section(FILE *file, gchar **name, gchar **contents)
-{
-	gboolean status;
-	GString *sname, *scontents;
-
-	/* Skip any initial white-space */
-	if (!read_until(file, NULL, 'N')) return FALSE;
-
-	/* Section tag should start with $. */
-	if (fgetc(file) != '$') {
-		sr_err("Expected $ at beginning of section.");
-		return FALSE;
-	}
-
-	/* Read the section tag */
-	sname = g_string_sized_new(32);
-	status = read_until(file, sname, 'W');
-
-	/* Skip whitespace before content */
-	status = status && read_until(file, NULL, 'N');
-
-	/* Read the content */
-	scontents = g_string_sized_new(128);
-	status = status && read_until(file, scontents, '$');
-	g_strchomp(scontents->str);
-
-	/* Release strings if status is FALSE, return them if status is TRUE */
-	*name = g_string_free(sname, !status);
-	*contents = g_string_free(scontents, !status);
-	return status;
-}
-
-static void free_channel(void *data)
-{
-	struct vcd_channel *vcd_ch = data;
-	g_free(vcd_ch->name);
-	g_free(vcd_ch->identifier);
-	g_free(vcd_ch);
-}
-
-static void release_context(struct context *ctx)
-{
-	g_slist_free_full(ctx->channels, free_channel);
-	g_free(ctx);
-}
-
-/* Remove empty parts from an array returned by g_strsplit. */
-static void remove_empty_parts(gchar **parts)
-{
-	gchar **src = parts;
-	gchar **dest = parts;
-	while (*src != NULL) {
-		if (**src != '\0')
-			*dest++ = *src;
-		src++;
-	}
-
-	*dest = NULL;
-}
-
-/*
- * Parse VCD header to get values for context structure.
- * The context structure should be zeroed before calling this.
- */
-static gboolean parse_header(FILE *file, struct context *ctx)
-{
-	uint64_t p, q;
-	gchar *name = NULL, *contents = NULL;
-	gboolean status = FALSE;
-	struct vcd_channel *vcd_ch;
-
-	while (parse_section(file, &name, &contents)) {
-		sr_dbg("Section '%s', contents '%s'.", name, contents);
-
-		if (g_strcmp0(name, "enddefinitions") == 0) {
-			status = TRUE;
-			break;
-		} else if (g_strcmp0(name, "timescale") == 0) {
-			/*
-			 * The standard allows for values 1, 10 or 100
-			 * and units s, ms, us, ns, ps and fs.
-			 * */
-			if (sr_parse_period(contents, &p, &q) == SR_OK) {
-				ctx->samplerate = q / p;
-				if (q % p != 0) {
-					/* Does not happen unless time value is non-standard */
-					sr_warn("Inexact rounding of samplerate, %" PRIu64 " / %" PRIu64 " to %" PRIu64 " Hz.",
-						q, p, ctx->samplerate);
-				}
-
-				sr_dbg("Samplerate: %" PRIu64, ctx->samplerate);
-			} else {
-				sr_err("Parsing timescale failed.");
-			}
-		} else if (g_strcmp0(name, "var") == 0) {
-			/* Format: $var type size identifier reference $end */
-			gchar **parts = g_strsplit_set(contents, " \r\n\t", 0);
-			remove_empty_parts(parts);
-
-			if (g_strv_length(parts) != 4)
-				sr_warn("$var section should have 4 items");
-			else if (g_strcmp0(parts[0], "reg") != 0 && g_strcmp0(parts[0], "wire") != 0)
-				sr_info("Unsupported signal type: '%s'", parts[0]);
-			else if (strtol(parts[1], NULL, 10) != 1)
-				sr_info("Unsupported signal size: '%s'", parts[1]);
-			else if (ctx->channelcount >= ctx->maxchannels)
-				sr_warn("Skipping '%s' because only %d channels requested.", parts[3], ctx->maxchannels);
-			else {
-				sr_info("Channel %d is '%s' identified by '%s'.", ctx->channelcount, parts[3], parts[2]);
-				vcd_ch = g_malloc(sizeof(struct vcd_channel));
-				vcd_ch->identifier = g_strdup(parts[2]);
-				vcd_ch->name = g_strdup(parts[3]);
-				ctx->channels = g_slist_append(ctx->channels, vcd_ch);
-				ctx->channelcount++;
-			}
-
-			g_strfreev(parts);
-		}
-
-		g_free(name); name = NULL;
-		g_free(contents); contents = NULL;
-	}
-
-	g_free(name);
-	g_free(contents);
-
-	return status;
-}
-
-static int format_match(const char *filename)
-{
-	FILE *file;
-	gchar *name = NULL, *contents = NULL;
-	gboolean status;
-
-	file = fopen(filename, "r");
-	if (file == NULL)
-		return FALSE;
-
-	/*
-	 * If we can parse the first section correctly,
-	 * then it is assumed to be a VCD file.
-	 */
-	status = parse_section(file, &name, &contents);
-	status = status && (*name != '\0');
-
-	g_free(name);
-	g_free(contents);
-	fclose(file);
-
-	return status;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
-	struct sr_channel *ch;
-	int num_channels, i;
-	char name[SR_MAX_CHANNELNAME_LEN + 1];
-	char *param;
-	struct context *ctx;
-
-	(void)filename;
-
-	if (!(ctx = g_try_malloc0(sizeof(*ctx)))) {
-		sr_err("Input format context malloc failed.");
-		return SR_ERR_MALLOC;
-	}
-
-	num_channels = DEFAULT_NUM_CHANNELS;
-	ctx->samplerate = 0;
-	ctx->downsample = 1;
-	ctx->skip = -1;
-
-	if (in->param) {
-		param = g_hash_table_lookup(in->param, "numchannels");
-		if (param) {
-			num_channels = strtoul(param, NULL, 10);
-			if (num_channels < 1) {
-				release_context(ctx);
-				return SR_ERR;
-			} else if (num_channels > 64) {
-				sr_err("No more than 64 channels supported.");
-				return SR_ERR;
-			}
-		}
-
-		param = g_hash_table_lookup(in->param, "downsample");
-		if (param) {
-			ctx->downsample = strtoul(param, NULL, 10);
-			if (ctx->downsample < 1)
-				ctx->downsample = 1;
-		}
-
-		param = g_hash_table_lookup(in->param, "compress");
-		if (param)
-			ctx->compress = strtoul(param, NULL, 10);
-
-		param = g_hash_table_lookup(in->param, "skip");
-		if (param)
-			ctx->skip = strtoul(param, NULL, 10) / ctx->downsample;
-	}
-
-	/* Maximum number of channels to parse from the VCD */
-	ctx->maxchannels = num_channels;
-
-	/* Create a virtual device. */
-	in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
-	in->internal = ctx;
-
-	for (i = 0; i < num_channels; i++) {
-		snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i);
-
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name))) {
-			release_context(ctx);
-			return SR_ERR;
-		}
-
-		in->sdi->channels = g_slist_append(in->sdi->channels, ch);
-	}
-
-	return SR_OK;
-}
-
-/* Send N samples of the given value. */
-static void send_samples(const struct sr_dev_inst *sdi, uint64_t sample, uint64_t count)
-{
-	struct sr_datafeed_packet packet;
-	struct sr_datafeed_logic logic;
-	uint64_t buffer[CHUNKSIZE];
-	uint64_t i;
-	unsigned chunksize = CHUNKSIZE;
-
-	if (count < chunksize)
-		chunksize = count;
-
-	for (i = 0; i < chunksize; i++)
-		buffer[i] = sample;
-
-	packet.type = SR_DF_LOGIC;
-	packet.payload = &logic;
-	logic.unitsize = sizeof(uint64_t);
-	logic.data = buffer;
-
-	while (count) {
-		if (count < chunksize)
-			chunksize = count;
-
-		logic.length = sizeof(uint64_t) * chunksize;
-
-		sr_session_send(sdi, &packet);
-		count -= chunksize;
-	}
-}
-
-/* Parse the data section of VCD */
-static void parse_contents(FILE *file, const struct sr_dev_inst *sdi, struct context *ctx)
-{
-	GString *token = g_string_sized_new(32);
-
-	uint64_t prev_timestamp = 0;
-	uint64_t prev_values = 0;
-
-	/* Read one space-delimited token at a time. */
-	while (read_until(file, NULL, 'N') && read_until(file, token, 'W')) {
-		if (token->str[0] == '#' && g_ascii_isdigit(token->str[1])) {
-			/* Numeric value beginning with # is a new timestamp value */
-			uint64_t timestamp;
-			timestamp = strtoull(token->str + 1, NULL, 10);
-
-			if (ctx->downsample > 1)
-				timestamp /= ctx->downsample;
-
-			/*
-			 * Skip < 0 => skip until first timestamp.
-			 * Skip = 0 => don't skip
-			 * Skip > 0 => skip until timestamp >= skip.
-			 */
-			if (ctx->skip < 0) {
-				ctx->skip = timestamp;
-				prev_timestamp = timestamp;
-			} else if (ctx->skip > 0 && timestamp < (uint64_t)ctx->skip) {
-				prev_timestamp = ctx->skip;
-			}
-			else if (timestamp == prev_timestamp) {
-				/* Ignore repeated timestamps (e.g. sigrok outputs these) */
-			}
-			else {
-				if (ctx->compress != 0 && timestamp - prev_timestamp > ctx->compress)
-				{
-					/* Compress long idle periods */
-					prev_timestamp = timestamp - ctx->compress;
-				}
-
-				sr_dbg("New timestamp: %" PRIu64, timestamp);
-
-				/* Generate samples from prev_timestamp up to timestamp - 1. */
-				send_samples(sdi, prev_values, timestamp - prev_timestamp);
-				prev_timestamp = timestamp;
-			}
-		} else if (token->str[0] == '$' && token->len > 1) {
-			/* This is probably a $dumpvars, $comment or similar.
-			 * $dump* contain useful data, but other tags will be skipped until $end. */
-			if (g_strcmp0(token->str, "$dumpvars") == 0
-					|| g_strcmp0(token->str, "$dumpon") == 0
-					|| g_strcmp0(token->str, "$dumpoff") == 0
-					|| g_strcmp0(token->str, "$end") == 0) {
-				/* Ignore, parse contents as normally. */
-			} else {
-				/* Skip until $end */
-				read_until(file, NULL, '$');
-			}
-		}
-		else if (strchr("bBrR", token->str[0]) != NULL) {
-			/* A vector value. Skip it and also the following identifier. */
-			read_until(file, NULL, 'N');
-			read_until(file, NULL, 'W');
-		} else if (strchr("01xXzZ", token->str[0]) != NULL) {
-			/* A new 1-bit sample value */
-			int i, bit;
-			GSList *l;
-			struct vcd_channel *vcd_ch;
-
-			bit = (token->str[0] == '1');
-
-			g_string_erase(token, 0, 1);
-			if (token->len == 0) {
-				/* There was a space between value and identifier.
-				 * Read in the rest.
-				 */
-				read_until(file, NULL, 'N');
-				read_until(file, token, 'W');
-			}
-
-			for (i = 0, l = ctx->channels; i < ctx->channelcount && l; i++, l = l->next) {
-				vcd_ch = l->data;
-
-				if (g_strcmp0(token->str, vcd_ch->identifier) == 0) {
-					/* Found our channel */
-					if (bit)
-						prev_values |= (uint64_t)1 << i;
-					else
-						prev_values &= ~((uint64_t)1 << i);
-
-					break;
-				}
-			}
-
-			if (i == ctx->channelcount)
-				sr_dbg("Did not find channel for identifier '%s'.", token->str);
-		} else {
-			sr_warn("Skipping unknown token '%s'.", token->str);
-		}
-
-		g_string_truncate(token, 0);
-	}
-
-	g_string_free(token, TRUE);
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
-	struct sr_datafeed_packet packet;
-	struct sr_datafeed_meta meta;
-	struct sr_config *src;
-	FILE *file;
-	struct context *ctx;
-	uint64_t samplerate;
-
-	ctx = in->internal;
-
-	if ((file = fopen(filename, "r")) == NULL)
-		return SR_ERR;
-
-	if (!parse_header(file, ctx)) {
-		sr_err("VCD parsing failed");
-		fclose(file);
-		return SR_ERR;
-	}
-
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(in->sdi, LOG_PREFIX);
-
-	/* Send metadata about the SR_DF_LOGIC packets to come. */
-	packet.type = SR_DF_META;
-	packet.payload = &meta;
-	samplerate = ctx->samplerate / ctx->downsample;
-	src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
-	meta.config = g_slist_append(NULL, src);
-	sr_session_send(in->sdi, &packet);
-	sr_config_free(src);
-
-	/* Parse the contents of the VCD file */
-	parse_contents(file, in->sdi, ctx);
-
-	/* Send end packet to the session bus. */
-	packet.type = SR_DF_END;
-	sr_session_send(in->sdi, &packet);
-
-	fclose(file);
-	release_context(ctx);
-	in->internal = NULL;
-
-	return SR_OK;
-}
-
-SR_PRIV struct sr_input_format input_vcd = {
-	.id = "vcd",
-	.description = "Value Change Dump",
-	.format_match = format_match,
-	.init = init,
-	.loadfile = loadfile,
-};
diff --git a/input/wav.c b/input/wav.c
deleted file mode 100644
index 1c3f050..0000000
--- a/input/wav.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert at 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 <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "input/wav"
-
-#define CHUNK_SIZE 4096
-
-struct context {
-	uint64_t samplerate;
-	int samplesize;
-	int num_channels;
-};
-
-static int get_wav_header(const char *filename, char *buf)
-{
-	struct stat st;
-	int fd, l;
-
-	l = strlen(filename);
-	if (l <= 4 || strcasecmp(filename + l - 4, ".wav"))
-		return SR_ERR;
-
-	if (stat(filename, &st) == -1)
-		return SR_ERR;
-	if (st.st_size <= 45)
-		/* Minimum size of header + 1 8-bit mono PCM sample. */
-		return SR_ERR;
-
-	if ((fd = open(filename, O_RDONLY)) == -1)
-		return SR_ERR;
-
-	l = read(fd, buf, 40);
-	close(fd);
-	if (l != 40)
-		return SR_ERR;
-
-	return SR_OK;
-}
-
-static int format_match(const char *filename)
-{
-	char buf[40];
-
-	if (get_wav_header(filename, buf) != SR_OK)
-		return FALSE;
-
-	if (strncmp(buf, "RIFF", 4))
-		return FALSE;
-	if (strncmp(buf + 8, "WAVE", 4))
-		return FALSE;
-	if (strncmp(buf + 12, "fmt ", 4))
-		return FALSE;
-	if (GUINT16_FROM_LE(*(uint16_t *)(buf + 20)) != 1)
-		/* Not PCM. */
-		return FALSE;
-	if (strncmp(buf + 36, "data", 4))
-		return FALSE;
-
-	return TRUE;
-}
-
-static int init(struct sr_input *in, const char *filename)
-{
-	struct sr_channel *ch;
-	struct context *ctx;
-	char buf[40], channelname[8];
-	int i;
-
-	if (get_wav_header(filename, buf) != SR_OK)
-		return SR_ERR;
-
-	if (!(ctx = g_try_malloc0(sizeof(struct context))))
-		return SR_ERR_MALLOC;
-
-	/* Create a virtual device. */
-	in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
-	in->sdi->priv = ctx;
-
-   	ctx->samplerate = GUINT32_FROM_LE(*(uint32_t *)(buf + 24));
-	ctx->samplesize = GUINT16_FROM_LE(*(uint16_t *)(buf + 34)) / 8;
-	if (ctx->samplesize != 1 && ctx->samplesize != 2 && ctx->samplesize != 4) {
-		sr_err("only 8, 16 or 32 bits per sample supported.");
-		return SR_ERR;
-	}
-
-	if ((ctx->num_channels = GUINT16_FROM_LE(*(uint16_t *)(buf + 22))) > 20) {
-		sr_err("%d channels seems crazy.", ctx->num_channels);
-		return SR_ERR;
-	}
-
-	for (i = 0; i < ctx->num_channels; i++) {
-		snprintf(channelname, 8, "CH%d", i + 1);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, channelname)))
-			return SR_ERR;
-		in->sdi->channels = g_slist_append(in->sdi->channels, ch);
-	}
-
-	return SR_OK;
-}
-
-static int loadfile(struct sr_input *in, const char *filename)
-{
-	struct sr_datafeed_packet packet;
-	struct sr_datafeed_meta meta;
-	struct sr_datafeed_analog analog;
-	struct sr_config *src;
-	struct context *ctx;
-	float fdata[CHUNK_SIZE];
-	uint64_t sample;
-	int num_samples, chunk_samples, s, c, fd, l;
-	char buf[CHUNK_SIZE];
-
-	ctx = in->sdi->priv;
-
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(in->sdi, LOG_PREFIX);
-
-	packet.type = SR_DF_META;
-	packet.payload = &meta;
-	src = sr_config_new(SR_CONF_SAMPLERATE,
-			g_variant_new_uint64(ctx->samplerate));
-	meta.config = g_slist_append(NULL, src);
-	sr_session_send(in->sdi, &packet);
-	sr_config_free(src);
-
-	if ((fd = open(filename, O_RDONLY)) == -1)
-		return SR_ERR;
-
-	lseek(fd, 40, SEEK_SET);
-	l = read(fd, buf, 4);
-	num_samples = GUINT32_FROM_LE((uint32_t)*(buf));
-	num_samples /= ctx->samplesize / ctx->num_channels;
-	while (TRUE) {
-		if ((l = read(fd, buf, CHUNK_SIZE)) < 1)
-			break;
-		chunk_samples = l / ctx->samplesize / ctx->num_channels;
-		for (s = 0; s < chunk_samples; s++) {
-			for (c = 0; c < ctx->num_channels; c++) {
-				sample = 0;
-				memcpy(&sample, buf + s * ctx->samplesize + c, ctx->samplesize);
-				switch (ctx->samplesize) {
-				case 1:
-					/* 8-bit PCM samples are unsigned. */
-					fdata[s + c] = (uint8_t)sample / 255.0;
-					break;
-				case 2:
-					fdata[s + c] = GINT16_FROM_LE(sample) / 32767.0;
-					break;
-				case 4:
-					fdata[s + c] = GINT32_FROM_LE(sample) / 65535.0;
-					break;
-				}
-			}
-		}
-		packet.type = SR_DF_ANALOG;
-		packet.payload = &analog;
-		analog.channels = in->sdi->channels;
-		analog.num_samples = chunk_samples;
-		analog.mq = 0;
-		analog.unit = 0;
-		analog.data = fdata;
-		sr_session_send(in->sdi, &packet);
-	}
-
-	close(fd);
-	packet.type = SR_DF_END;
-	sr_session_send(in->sdi, &packet);
-
-	return SR_OK;
-}
-
-
-SR_PRIV struct sr_input_format input_wav = {
-	.id = "wav",
-	.description = "WAV file",
-	.format_match = format_match,
-	.init = init,
-	.loadfile = loadfile,
-};
-
diff --git a/libsigrok-internal.h b/libsigrok-internal.h
deleted file mode 100644
index eb4b1a2..0000000
--- a/libsigrok-internal.h
+++ /dev/null
@@ -1,616 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert at 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/>.
- */
-
-/** @file
-  * @internal
-  */
-
-#ifndef LIBSIGROK_LIBSIGROK_INTERNAL_H
-#define LIBSIGROK_LIBSIGROK_INTERNAL_H
-
-#include <stdarg.h>
-#include <glib.h>
-#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
-#ifdef HAVE_LIBUSB_1_0
-#include <libusb.h>
-#endif
-#ifdef HAVE_LIBSERIALPORT
-#include <libserialport.h>
-#endif
-
-/**
- * @file
- *
- * libsigrok private header file, only to be used internally.
- */
-
-/*--- Macros ----------------------------------------------------------------*/
-
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#endif
-
-#ifndef ARRAY_AND_SIZE
-#define ARRAY_AND_SIZE(a) (a), ARRAY_SIZE(a)
-#endif
-
-/**
- * Read a 8 bits integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define R8(x)     ((unsigned)((const uint8_t*)(x))[0])
-
-/**
- * Read a 16 bits big endian integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define RB16(x)  (((unsigned)((const uint8_t*)(x))[0] <<  8) |  \
-                   (unsigned)((const uint8_t*)(x))[1])
-
-/**
- * Read a 16 bits little endian integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define RL16(x)  (((unsigned)((const uint8_t*)(x))[1] <<  8) | \
-                   (unsigned)((const uint8_t*)(x))[0])
-
-/**
- * Read a 32 bits big endian integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define RB32(x)  (((unsigned)((const uint8_t*)(x))[0] << 24) | \
-                  ((unsigned)((const uint8_t*)(x))[1] << 16) |  \
-                  ((unsigned)((const uint8_t*)(x))[2] <<  8) |  \
-                   (unsigned)((const uint8_t*)(x))[3])
-
-/**
- * Read a 32 bits little endian integer out of memory.
- * @param x a pointer to the input memory
- * @return the corresponding integer
- */
-#define RL32(x)  (((unsigned)((const uint8_t*)(x))[3] << 24) | \
-                  ((unsigned)((const uint8_t*)(x))[2] << 16) |  \
-                  ((unsigned)((const uint8_t*)(x))[1] <<  8) |  \
-                   (unsigned)((const uint8_t*)(x))[0])
-
-/**
- * Write a 8 bits integer to memory.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define W8(p, x)    do { ((uint8_t*)(p))[0] = (uint8_t) (x);      } while(0)
-
-/**
- * Write a 16 bits integer to memory stored as big endian.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define WB16(p, x)  do { ((uint8_t*)(p))[1] = (uint8_t) (x);      \
-                         ((uint8_t*)(p))[0] = (uint8_t)((x)>>8);  } while(0)
-
-/**
- * Write a 16 bits integer to memory stored as little endian.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define WL16(p, x)  do { ((uint8_t*)(p))[0] = (uint8_t) (x);      \
-                         ((uint8_t*)(p))[1] = (uint8_t)((x)>>8);  } while(0)
-
-/**
- * Write a 32 bits integer to memory stored as big endian.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define WB32(p, x)  do { ((uint8_t*)(p))[3] = (uint8_t) (x);      \
-                         ((uint8_t*)(p))[2] = (uint8_t)((x)>>8);  \
-                         ((uint8_t*)(p))[1] = (uint8_t)((x)>>16); \
-                         ((uint8_t*)(p))[0] = (uint8_t)((x)>>24); } while(0)
-
-/**
- * Write a 32 bits integer to memory stored as little endian.
- * @param p a pointer to the output memory
- * @param x the input integer
- */
-#define WL32(p, x)  do { ((uint8_t*)(p))[0] = (uint8_t) (x);      \
-                         ((uint8_t*)(p))[1] = (uint8_t)((x)>>8);  \
-                         ((uint8_t*)(p))[2] = (uint8_t)((x)>>16); \
-                         ((uint8_t*)(p))[3] = (uint8_t)((x)>>24); } while(0)
-
-/* Portability fixes for FreeBSD. */
-#ifdef __FreeBSD__
-#define LIBUSB_CLASS_APPLICATION 0xfe
-#define libusb_handle_events_timeout_completed(ctx, tv, c) \
-	libusb_handle_events_timeout(ctx, tv)
-#endif
-
-struct sr_context {
-#ifdef HAVE_LIBUSB_1_0
-	libusb_context *libusb_ctx;
-	gboolean usb_source_present;
-#ifdef _WIN32
-	GThread *usb_thread;
-	gboolean usb_thread_running;
-	GMutex usb_mutex;
-	HANDLE usb_event;
-	GPollFD usb_pollfd;
-	sr_receive_data_callback usb_cb;
-	void *usb_cb_data;
-#endif
-#endif
-};
-
-#ifdef HAVE_LIBUSB_1_0
-/** USB device instance */
-struct sr_usb_dev_inst {
-	/** USB bus */
-	uint8_t bus;
-	/** Device address on USB bus */
-	uint8_t address;
-	/** libusb device handle */
-	struct libusb_device_handle *devhdl;
-};
-#endif
-
-#ifdef HAVE_LIBSERIALPORT
-#define SERIAL_PARITY_NONE SP_PARITY_NONE
-#define SERIAL_PARITY_EVEN SP_PARITY_EVEN
-#define SERIAL_PARITY_ODD  SP_PARITY_ODD
-struct sr_serial_dev_inst {
-	/** Port name, e.g. '/dev/tty42'. */
-	char *port;
-	/** Comm params for serial_set_paramstr(). */
-	char *serialcomm;
-	/** Port is non-blocking. */
-	int nonblocking;
-	/** libserialport port handle */
-	struct sp_port *data;
-	/** libserialport event set */
-	struct sp_event_set *event_set;
-	/** GPollFDs for event polling */
-	GPollFD *pollfds;
-};
-#endif
-
-struct sr_usbtmc_dev_inst {
-	char *device;
-	int fd;
-};
-
-/* Private driver context. */
-struct drv_context {
-	/** sigrok context */
-	struct sr_context *sr_ctx;
-	GSList *instances;
-};
-
-/*--- log.c -----------------------------------------------------------------*/
-
-SR_PRIV int sr_log(int loglevel, const char *format, ...);
-SR_PRIV int sr_spew(const char *format, ...);
-SR_PRIV int sr_dbg(const char *format, ...);
-SR_PRIV int sr_info(const char *format, ...);
-SR_PRIV int sr_warn(const char *format, ...);
-SR_PRIV int sr_err(const char *format, ...);
-
-/* Message logging helpers with subsystem-specific prefix string. */
-#ifndef NO_LOG_WRAPPERS
-#define sr_log(l, s, args...) sr_log(l, "%s: " s, LOG_PREFIX, ## args)
-#define sr_spew(s, args...) sr_spew("%s: " s, LOG_PREFIX, ## args)
-#define sr_dbg(s, args...) sr_dbg("%s: " s, LOG_PREFIX, ## args)
-#define sr_info(s, args...) sr_info("%s: " s, LOG_PREFIX, ## args)
-#define sr_warn(s, args...) sr_warn("%s: " s, LOG_PREFIX, ## args)
-#define sr_err(s, args...) sr_err("%s: " s, LOG_PREFIX, ## args)
-#endif
-
-/*--- device.c --------------------------------------------------------------*/
-
-/** Values for the changes argument of sr_dev_driver.config_channel_set. */
-enum {
-	/** The enabled state of the channel has been changed. */
-	SR_CHANNEL_SET_ENABLED = 1 << 0,
-	/** The trigger setup of the channel has been changed. */
-	SR_CHANNEL_SET_TRIGGER = 1 << 1,
-};
-
-SR_PRIV struct sr_channel *sr_channel_new(int index, int type,
-		gboolean enabled, const char *name);
-
-/* Generic device instances */
-SR_PRIV struct sr_dev_inst *sr_dev_inst_new(int index, int status,
-		const char *vendor, const char *model, const char *version);
-SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi);
-
-#ifdef HAVE_LIBUSB_1_0
-/* USB-specific instances */
-SR_PRIV struct sr_usb_dev_inst *sr_usb_dev_inst_new(uint8_t bus,
-		uint8_t address, struct libusb_device_handle *hdl);
-SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb);
-#endif
-
-#ifdef HAVE_LIBSERIALPORT
-/* Serial-specific instances */
-SR_PRIV struct sr_serial_dev_inst *sr_serial_dev_inst_new(const char *port,
-		const char *serialcomm);
-SR_PRIV void sr_serial_dev_inst_free(struct sr_serial_dev_inst *serial);
-#endif
-
-/* USBTMC-specific instances */
-SR_PRIV struct sr_usbtmc_dev_inst *sr_usbtmc_dev_inst_new(const char *device);
-SR_PRIV void sr_usbtmc_dev_inst_free(struct sr_usbtmc_dev_inst *usbtmc);
-
-/*--- hwdriver.c ------------------------------------------------------------*/
-
-SR_PRIV void sr_hw_cleanup_all(void);
-SR_PRIV struct sr_config *sr_config_new(int key, GVariant *data);
-SR_PRIV void sr_config_free(struct sr_config *src);
-SR_PRIV int sr_source_remove(int fd);
-SR_PRIV int sr_source_add(int fd, int events, int timeout,
-		sr_receive_data_callback cb, void *cb_data);
-
-/*--- session.c -------------------------------------------------------------*/
-
-struct sr_session {
-	/** List of struct sr_dev pointers. */
-	GSList *devs;
-	/** List of struct datafeed_callback pointers. */
-	GSList *datafeed_callbacks;
-	GTimeVal starttime;
-	gboolean running;
-
-	unsigned int num_sources;
-
-	/*
-	 * Both "sources" and "pollfds" are of the same size and contain pairs
-	 * of descriptor and callback function. We can not embed the GPollFD
-	 * into the source struct since we want to be able to pass the array
-	 * of all poll descriptors to g_poll().
-	 */
-	struct source *sources;
-	GPollFD *pollfds;
-	int source_timeout;
-
-	/*
-	 * These are our synchronization primitives for stopping the session in
-	 * an async fashion. We need to make sure the session is stopped from
-	 * within the session thread itself.
-	 */
-	/** Mutex protecting access to abort_session. */
-	GMutex stop_mutex;
-	/** Abort current session. See sr_session_stop(). */
-	gboolean abort_session;
-};
-
-SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
-		const struct sr_datafeed_packet *packet);
-SR_PRIV int sr_session_stop_sync(void);
-SR_PRIV int sr_sessionfile_check(const char *filename);
-
-/*--- std.c -----------------------------------------------------------------*/
-
-typedef int (*dev_close_callback)(struct sr_dev_inst *sdi);
-typedef void (*std_dev_clear_callback)(void *priv);
-
-SR_PRIV int std_init(struct sr_context *sr_ctx, struct sr_dev_driver *di,
-		const char *prefix);
-#ifdef HAVE_LIBSERIALPORT
-SR_PRIV int std_serial_dev_open(struct sr_dev_inst *sdi);
-SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi,
-		void *cb_data, dev_close_callback dev_close_fn,
-		struct sr_serial_dev_inst *serial, const char *prefix);
-#endif
-SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi,
-		const char *prefix);
-SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
-		std_dev_clear_callback clear_private);
-SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi);
-
-/*--- strutil.c -------------------------------------------------------------*/
-
-SR_PRIV int sr_atol(const char *str, long *ret);
-SR_PRIV int sr_atoi(const char *str, int *ret);
-SR_PRIV int sr_atod(const char *str, double *ret);
-SR_PRIV int sr_atof(const char *str, float *ret);
-SR_PRIV int sr_atof_ascii(const char *str, float *ret);
-
-/*--- hardware/common/serial.c ----------------------------------------------*/
-
-#ifdef HAVE_LIBSERIALPORT
-enum {
-	SERIAL_RDWR = 1,
-	SERIAL_RDONLY = 2,
-	SERIAL_NONBLOCK = 4,
-};
-
-typedef gboolean (*packet_valid_callback)(const uint8_t *buf);
-
-SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags);
-SR_PRIV int serial_close(struct sr_serial_dev_inst *serial);
-SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial);
-SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
-		const void *buf, size_t count);
-SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial,
-		const void *buf, size_t count);
-SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial,
-		const void *buf, size_t count);
-SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
-		size_t count);
-SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf,
-		size_t count);
-SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf,
-		size_t count);
-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);
-SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
-		const char *paramstr);
-SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
-		int *buflen, gint64 timeout_ms);
-SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
-				 uint8_t *buf, size_t *buflen,
-				 size_t packet_size,
-				 packet_valid_callback is_valid,
-				 uint64_t timeout_ms, int baudrate);
-SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_device,
-				      const char **serial_options);
-SR_PRIV int serial_source_add(struct sr_serial_dev_inst *serial, int events,
-		int timeout, sr_receive_data_callback cb, void *cb_data);
-SR_PRIV int serial_source_remove(struct sr_serial_dev_inst *serial);
-SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id);
-#endif
-
-/*--- hardware/common/ezusb.c -----------------------------------------------*/
-
-#ifdef HAVE_LIBUSB_1_0
-SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear);
-SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl,
-				   const char *filename);
-SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
-				  const char *filename);
-#endif
-
-/*--- hardware/common/usb.c -------------------------------------------------*/
-
-#ifdef HAVE_LIBUSB_1_0
-SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn);
-SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb);
-SR_PRIV int usb_source_add(struct sr_context *ctx, int timeout,
-		sr_receive_data_callback cb, void *cb_data);
-SR_PRIV int usb_source_remove(struct sr_context *ctx);
-#endif
-
-/*--- hardware/common/scpi.c ------------------------------------------------*/
-
-#define SCPI_CMD_IDN "*IDN?"
-#define SCPI_CMD_OPC "*OPC?"
-
-enum {
-	SCPI_CMD_SET_TRIGGER_SOURCE,
-	SCPI_CMD_SET_TIMEBASE,
-	SCPI_CMD_SET_VERTICAL_DIV,
-	SCPI_CMD_SET_TRIGGER_SLOPE,
-	SCPI_CMD_SET_COUPLING,
-	SCPI_CMD_SET_HORIZ_TRIGGERPOS,
-	SCPI_CMD_GET_ANALOG_CHAN_STATE,
-	SCPI_CMD_GET_DIG_CHAN_STATE,
-	SCPI_CMD_GET_TIMEBASE,
-	SCPI_CMD_GET_VERTICAL_DIV,
-	SCPI_CMD_GET_VERTICAL_OFFSET,
-	SCPI_CMD_GET_TRIGGER_SOURCE,
-	SCPI_CMD_GET_HORIZ_TRIGGERPOS,
-	SCPI_CMD_GET_TRIGGER_SLOPE,
-	SCPI_CMD_GET_COUPLING,
-	SCPI_CMD_SET_ANALOG_CHAN_STATE,
-	SCPI_CMD_SET_DIG_CHAN_STATE,
-	SCPI_CMD_GET_DIG_POD_STATE,
-	SCPI_CMD_SET_DIG_POD_STATE,
-	SCPI_CMD_GET_ANALOG_DATA,
-	SCPI_CMD_GET_DIG_DATA,
-	SCPI_CMD_GET_SAMPLE_RATE,
-	SCPI_CMD_GET_SAMPLE_RATE_LIVE,
-};
-
-struct sr_scpi_hw_info {
-	char *manufacturer;
-	char *model;
-	char *serial_number;
-	char *firmware_version;
-};
-
-struct sr_scpi_dev_inst {
-	const char *name;
-	const char *prefix;
-	int priv_size;
-	GSList *(*scan)(struct drv_context *drvc);
-	int (*dev_inst_new)(void *priv, struct drv_context *drvc,
-		const char *resource, char **params, const char *serialcomm);
-	int (*open)(void *priv);
-	int (*source_add)(void *priv, int events,
-		int timeout, sr_receive_data_callback cb, void *cb_data);
-	int (*source_remove)(void *priv);
-	int (*send)(void *priv, const char *command);
-	int (*read_begin)(void *priv);
-	int (*read_data)(void *priv, char *buf, int maxlen);
-	int (*read_complete)(void *priv);
-	int (*close)(void *priv);
-	void (*free)(void *priv);
-	void *priv;
-};
-
-SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
-		struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi));
-SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
-		const char *resource, const char *serialcomm);
-SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_source_add(struct sr_scpi_dev_inst *scpi, int events,
-		int timeout, sr_receive_data_callback cb, void *cb_data);
-SR_PRIV int sr_scpi_source_remove(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi,
-		const char *format, ...);
-SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi,
-		const char *format, va_list args);
-SR_PRIV int sr_scpi_read_begin(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen);
-SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi);
-SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi);
-
-SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
-			const char *command, char **scpi_response);
-SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
-			const char *command, gboolean *scpi_response);
-SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
-			const char *command, int *scpi_response);
-SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
-			const char *command, float *scpi_response);
-SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
-			const char *command, double *scpi_response);
-SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi);
-SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
-			const char *command, GArray **scpi_response);
-SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
-			const char *command, GArray **scpi_response);
-SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
-			struct sr_scpi_hw_info **scpi_response);
-SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info);
-
-/*--- hardware/common/dmm/es519xx.c -----------------------------------------*/
-
-/**
- * All 11-byte es519xx chips repeat each block twice for each conversion cycle
- * so always read 2 blocks at a time.
- */
-#define ES519XX_11B_PACKET_SIZE (11 * 2)
-#define ES519XX_14B_PACKET_SIZE 14
-
-struct es519xx_info {
-	gboolean is_judge, is_voltage, is_auto, is_micro, is_current;
-	gboolean is_milli, is_resistance, is_continuity, is_diode;
-	gboolean is_frequency, is_rpm, is_capacitance, is_duty_cycle;
-	gboolean is_temperature, is_celsius, is_fahrenheit;
-	gboolean is_adp0, is_adp1, is_adp2, is_adp3;
-	gboolean is_sign, is_batt, is_ol, is_pmax, is_pmin, is_apo;
-	gboolean is_dc, is_ac, is_vahz, is_min, is_max, is_rel, is_hold;
-	gboolean is_digit4, is_ul, is_vasel, is_vbar, is_lpf1, is_lpf0, is_rmr;
-	uint32_t baudrate;
-	int packet_size;
-	gboolean alt_functions, fivedigits, clampmeter, selectable_lpf;
-};
-
-SR_PRIV gboolean sr_es519xx_2400_11b_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_2400_11b_parse(const uint8_t *buf, float *floatval,
-		struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_2400_11b_altfn_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_2400_11b_altfn_parse(const uint8_t *buf,
-		float *floatval, struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_11b_5digits_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_11b_5digits_parse(const uint8_t *buf,
-		float *floatval, struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_11b_clamp_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_11b_clamp_parse(const uint8_t *buf,
-		float *floatval, struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_11b_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_11b_parse(const uint8_t *buf, float *floatval,
-		struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_14b_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_14b_parse(const uint8_t *buf, float *floatval,
-		struct sr_datafeed_analog *analog, void *info);
-SR_PRIV gboolean sr_es519xx_19200_14b_sel_lpf_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_es519xx_19200_14b_sel_lpf_parse(const uint8_t *buf,
-		float *floatval, struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/fs9922.c ------------------------------------------*/
-
-#define FS9922_PACKET_SIZE 14
-
-struct fs9922_info {
-	gboolean is_auto, is_dc, is_ac, is_rel, is_hold, is_bpn, is_z1, is_z2;
-	gboolean is_max, is_min, is_apo, is_bat, is_nano, is_z3, is_micro;
-	gboolean is_milli, is_kilo, is_mega, is_beep, is_diode, is_percent;
-	gboolean is_z4, is_volt, is_ampere, is_ohm, is_hfe, is_hertz, is_farad;
-	gboolean is_celsius, is_fahrenheit;
-	int bargraph_sign, bargraph_value;
-};
-
-SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
-			    struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/fs9721.c ------------------------------------------*/
-
-#define FS9721_PACKET_SIZE 14
-
-struct fs9721_info {
-	gboolean is_ac, is_dc, is_auto, is_rs232, is_micro, is_nano, is_kilo;
-	gboolean is_diode, is_milli, is_percent, is_mega, is_beep, is_farad;
-	gboolean is_ohm, is_rel, is_hold, is_ampere, is_volt, is_hz, is_bat;
-	gboolean is_c2c1_11, is_c2c1_10, is_c2c1_01, is_c2c1_00, is_sign;
-};
-
-SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
-			    struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *info);
-SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/m2110.c -----------------------------------------*/
-
-#define BBCGM_M2110_PACKET_SIZE 9
-
-SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
-			     struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/metex14.c -----------------------------------------*/
-
-#define METEX14_PACKET_SIZE 14
-
-struct metex14_info {
-	gboolean is_ac, is_dc, is_resistance, is_capacity, is_temperature;
-	gboolean is_diode, is_frequency, is_ampere, is_volt, is_farad;
-	gboolean is_hertz, is_ohm, is_celsius, is_pico, is_nano, is_micro;
-	gboolean is_milli, is_kilo, is_mega, is_gain, is_decibel, is_hfe;
-	gboolean is_unitless, is_logic;
-};
-
-#ifdef HAVE_LIBSERIALPORT
-SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial);
-#endif
-SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
-			     struct sr_datafeed_analog *analog, void *info);
-
-/*--- hardware/common/dmm/rs9lcd.c ------------------------------------------*/
-
-#define RS9LCD_PACKET_SIZE 9
-
-/* Dummy info struct. The parser does not use it. */
-struct rs9lcd_info { int dummy; };
-
-SR_PRIV gboolean sr_rs9lcd_packet_valid(const uint8_t *buf);
-SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval,
-			    struct sr_datafeed_analog *analog, void *info);
-
-#endif
diff --git a/libsigrok.pc.in b/libsigrok.pc.in
index 219f7c3..791e3f0 100644
--- a/libsigrok.pc.in
+++ b/libsigrok.pc.in
@@ -8,8 +8,7 @@ Description: Backend library of the sigrok logic analyzer software
 URL: http://www.sigrok.org
 Requires: glib-2.0
 Requires.private: @SR_PKGLIBS@
-Version: @VERSION@
+Version: @SR_PACKAGE_VERSION@
 Libs: -L${libdir} -lsigrok
-Libs.private: -lm
+Libs.private: @SR_EXTRA_LIBS@
 Cflags: -I${includedir}
-
diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4
new file mode 100644
index 0000000..163a4c6
--- /dev/null
+++ b/m4/ax_cxx_compile_stdcxx_11.m4
@@ -0,0 +1,142 @@
+# ============================================================================
+#  http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the C++11
+#   standard; if necessary, add switches to CXXFLAGS to enable support.
+#
+#   The first argument, if specified, indicates whether you insist on an
+#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+#   -std=c++11).  If neither is specified, you get whatever works, with
+#   preference for an extended mode.
+#
+#   The second argument, if specified 'mandatory' or if left unspecified,
+#   indicates that baseline C++11 support is required and that the macro
+#   should error out if no mode with that support is found.  If specified
+#   'optional', then configuration proceeds regardless, after defining
+#   HAVE_CXX11 if and only if a supporting mode is found.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz at redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw at panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr at ices.utexas.edu>
+#   Copyright (c) 2014 Alexey Sokolov <sokolov at google.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 4
+
+m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    struct Base {
+    virtual void f() {}
+    };
+    struct Child : public Base {
+    virtual void f() override {}
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);
+
+    auto d = a;
+    auto l = [](){};
+]])
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl
+  m4_if([$1], [], [],
+        [$1], [ext], [],
+        [$1], [noext], [],
+        [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl
+  m4_if([$2], [], [ax_cxx_compile_cxx11_required=true],
+        [$2], [mandatory], [ax_cxx_compile_cxx11_required=true],
+        [$2], [optional], [ax_cxx_compile_cxx11_required=false],
+        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])
+  AC_LANG_PUSH([C++])dnl
+  ac_success=no
+  AC_CACHE_CHECK(whether $CXX supports C++11 features by default,
+  ax_cv_cxx_compile_cxx11,
+  [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+    [ax_cv_cxx_compile_cxx11=yes],
+    [ax_cv_cxx_compile_cxx11=no])])
+  if test x$ax_cv_cxx_compile_cxx11 = xyes; then
+    ac_success=yes
+  fi
+
+  m4_if([$1], [noext], [], [dnl
+  if test x$ac_success = xno; then
+    for switch in -std=gnu++11 -std=gnu++0x; do
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
+                     $cachevar,
+        [ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXXFLAGS="$ac_save_CXXFLAGS"])
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+
+  m4_if([$1], [ext], [], [dnl
+  if test x$ac_success = xno; then
+    for switch in -std=c++11 -std=c++0x; do
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
+                     $cachevar,
+        [ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXXFLAGS="$ac_save_CXXFLAGS"])
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+  AC_LANG_POP([C++])
+  if test x$ax_cxx_compile_cxx11_required = xtrue; then
+    if test x$ac_success = xno; then
+      AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.])
+    fi
+  else
+    if test x$ac_success = xno; then
+      HAVE_CXX11=0
+      AC_MSG_NOTICE([No compiler with C++11 support was found])
+    else
+      HAVE_CXX11=1
+      AC_DEFINE(HAVE_CXX11,1,
+                [define if the compiler supports basic C++11 syntax])
+    fi
+
+    AC_SUBST(HAVE_CXX11)
+  fi
+])
diff --git a/autostuff/libtool.m4 b/m4/libtool.m4
similarity index 100%
rename from autostuff/libtool.m4
rename to m4/libtool.m4
diff --git a/autostuff/ltoptions.m4 b/m4/ltoptions.m4
similarity index 100%
rename from autostuff/ltoptions.m4
rename to m4/ltoptions.m4
diff --git a/autostuff/ltsugar.m4 b/m4/ltsugar.m4
similarity index 100%
rename from autostuff/ltsugar.m4
rename to m4/ltsugar.m4
diff --git a/autostuff/ltversion.m4 b/m4/ltversion.m4
similarity index 100%
rename from autostuff/ltversion.m4
rename to m4/ltversion.m4
diff --git a/autostuff/lt~obsolete.m4 b/m4/lt~obsolete.m4
similarity index 100%
rename from autostuff/lt~obsolete.m4
rename to m4/lt~obsolete.m4
diff --git a/m4/sigrok.m4 b/m4/sigrok.m4
new file mode 100644
index 0000000..2ca2444
--- /dev/null
+++ b/m4/sigrok.m4
@@ -0,0 +1,449 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2009 Openismus GmbH
+## Copyright (C) 2015 Daniel Elstner <daniel.kitta at 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/>.
+##
+
+#serial 20150910
+
+## SR_APPEND(var-name, [list-sep,] element)
+##
+## Append the shell word <element> to the shell variable named <var-name>,
+## prefixed by <list-sep> unless the list was empty before appending. If
+## only two arguments are supplied, <list-sep> defaults to a single space
+## character.
+##
+AC_DEFUN([SR_APPEND],
+[dnl
+m4_assert([$# >= 2])[]dnl
+$1=[$]{$1[}]m4_if([$#], [2], [[$]{$1:+' '}$2], [[$]{$1:+$2}$3])[]dnl
+])
+
+## SR_PREPEND(var-name, [list-sep,] element)
+##
+## Prepend the shell word <element> to the shell variable named <var-name>,
+## suffixed by <list-sep> unless the list was empty before prepending. If
+## only two arguments are supplied, <list-sep> defaults to a single space
+## character.
+##
+AC_DEFUN([SR_PREPEND],
+[dnl
+m4_assert([$# >= 2])[]dnl
+$1=m4_if([$#], [2], [$2[$]{$1:+' '}], [$3[$]{$1:+$2}])[$]$1[]dnl
+])
+
+## _SR_PKG_VERSION_SET(var-prefix, pkg-name, tag-prefix, base-version, major, minor, [micro])
+##
+m4_define([_SR_PKG_VERSION_SET],
+[dnl
+m4_assert([$# >= 6])[]dnl
+$1=$4
+sr_git_deps=
+# Check if we can get revision information from git.
+sr_head=`git -C "$srcdir" rev-parse --verify --short HEAD 2>&AS_MESSAGE_LOG_FD`
+
+AS_IF([test "$?" = 0 && test "x$sr_head" != x], [dnl
+	test ! -f "$srcdir/.git/HEAD" \
+		|| sr_git_deps="$sr_git_deps \$(top_srcdir)/.git/HEAD"
+
+	sr_head_name=`git -C "$srcdir" rev-parse --symbolic-full-name HEAD 2>&AS_MESSAGE_LOG_FD`
+	AS_IF([test "$?" = 0 && test -f "$srcdir/.git/$sr_head_name"],
+		[sr_git_deps="$sr_git_deps \$(top_srcdir)/.git/$sr_head_name"])
+
+	# Append the revision hash unless we are exactly on a tagged release.
+	git -C "$srcdir" describe --match "$3$4" \
+		--exact-match >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD \
+		|| $1="[$]$1-git-$sr_head"
+])
+# Use $(wildcard) so that things do not break if for whatever
+# reason these files do not exist anymore at make time.
+AS_IF([test -n "$sr_git_deps"],
+	[SR_APPEND([CONFIG_STATUS_DEPENDENCIES], ["\$(wildcard$sr_git_deps)"])])
+AC_SUBST([CONFIG_STATUS_DEPENDENCIES])[]dnl
+AC_SUBST([$1])[]dnl
+dnl
+AC_DEFINE([$1_MAJOR], [$5], [Major version number of $2.])[]dnl
+AC_DEFINE([$1_MINOR], [$6], [Minor version number of $2.])[]dnl
+m4_ifval([$7], [AC_DEFINE([$1_MICRO], [$7], [Micro version number of $2.])])[]dnl
+AC_DEFINE_UNQUOTED([$1_STRING], ["[$]$1"], [Version of $2.])[]dnl
+])
+
+## SR_PKG_VERSION_SET(var-prefix, version-triple)
+##
+## Set up substitution variables and macro definitions for the package
+## version components. Derive the version suffix from the repository
+## revision if possible.
+##
+## Substitutions: <var-prefix>
+## Macro defines: <var-prefix>_{MAJOR,MINOR,MICRO,STRING}
+##
+AC_DEFUN([SR_PKG_VERSION_SET],
+[dnl
+m4_assert([$# >= 2])[]dnl
+_SR_PKG_VERSION_SET([$1],
+	m4_defn([AC_PACKAGE_NAME]),
+	m4_defn([AC_PACKAGE_TARNAME])[-],
+	m4_expand([$2]),
+	m4_unquote(m4_split(m4_expand([$2]), [\.])))
+])
+
+## _SR_LIB_VERSION_SET(var-prefix, pkg-name, abi-triple, current, revision, age)
+##
+m4_define([_SR_LIB_VERSION_SET],
+[dnl
+m4_assert([$# >= 6])[]dnl
+$1=$3
+AC_SUBST([$1])[]dnl
+AC_DEFINE([$1_CURRENT], [$4], [Binary version of $2.])[]dnl
+AC_DEFINE([$1_REVISION], [$5], [Binary revision of $2.])[]dnl
+AC_DEFINE([$1_AGE], [$6], [Binary age of $2.])[]dnl
+AC_DEFINE([$1_STRING], ["$3"], [Binary version triple of $2.])[]dnl
+])
+
+## SR_LIB_VERSION_SET(var-prefix, abi-triple)
+##
+## Set up substitution variables and macro definitions for a library
+## binary version.
+##
+## Substitutions: <var-prefix>
+## Macro defines: <var-prefix>_{CURRENT,REVISION,AGE,STRING}
+##
+AC_DEFUN([SR_LIB_VERSION_SET],
+[dnl
+m4_assert([$# >= 1])[]dnl
+_SR_LIB_VERSION_SET([$1],
+	m4_defn([AC_PACKAGE_NAME]),
+	[$2], m4_unquote(m4_split([$2], [:])))
+])
+
+## SR_SEARCH_LIBS(libs-var, function, search-libs,
+##                [action-if-found], [action-if-not-found], [other-libs])
+##
+## Same as AC_SEARCH_LIBS, except that the result is prepended
+## to <libs-var> instead of LIBS. Calls AC_SUBST on <libs-var>.
+##
+AC_DEFUN([SR_SEARCH_LIBS],
+[dnl
+m4_assert([$# >= 3])[]dnl
+sr_sl_save_LIBS=$LIBS
+AC_SEARCH_LIBS([$2], [$3],,, m4_join([$6], [[$]$1]))
+LIBS=$sr_sl_save_LIBS
+AS_CASE([$ac_cv_search_$2], [no*],,
+	[SR_PREPEND([$1], [$ac_cv_search_$2])])
+m4_ifvaln([$4$5], [AS_IF([test "x$ac_cv_search_$2" = xno], [$5], [$4])])[]dnl
+AC_SUBST([$1])[]dnl
+])
+
+## _SR_VAR_SUMMARY(tag, var-name, line-leader, align-columns, align-char)
+##
+m4_define([_SR_VAR_SUMMARY], [dnl
+$2=
+$1_append() {
+	sr_aligned=`printf '%.$4s' "[$][1]m4_for([i], [1], [$4],, [$5])"`
+	$2="[$]{$2}$3$sr_aligned [$]2"'
+'
+}
+])
+
+## SR_VAR_SUMMARY(tag, [var-name = <tag>],
+##                [line-leader = [ - ]], [align-columns = 32], [align-char = .])
+##
+## Define a shell function <tag>_append() to be used for aggregating
+## a summary of test results in the shell variable <var-name>.
+##
+AC_DEFUN([SR_VAR_SUMMARY],
+[dnl
+m4_assert([$# >= 1])[]dnl
+_SR_VAR_SUMMARY([$1],
+	m4_default_quoted([$2], [$1]),
+	m4_default_quoted([$3], [ - ]),
+	m4_default_quoted([$4], [32]),
+	m4_default_quoted([$5], [.]))[]dnl
+])
+
+## SR_PKG_CHECK_SUMMARY([var-name = sr_pkg_check_summary],
+##                      [line-leader = [ - ]], [align-columns = 32], [align-char = .])
+##
+## Prepare for the aggregation of package check results
+## in the shell variable <var-name>.
+##
+AC_DEFUN([SR_PKG_CHECK_SUMMARY],
+	[SR_VAR_SUMMARY([sr_pkg_check_summary], $@)])
+
+## SR_PKG_CHECK(tag, [collect-var], module...)
+##
+## Check for each pkg-config <module> in the argument list. <module> may
+## include a version constraint.
+##
+## Output variables: sr_have_<tag>, sr_<tag>_version
+##
+AC_DEFUN([SR_PKG_CHECK],
+[dnl
+m4_assert([$# >= 3])[]dnl
+AC_REQUIRE([PKG_PROG_PKG_CONFIG])[]dnl
+AC_REQUIRE([SR_PKG_CHECK_SUMMARY])[]dnl
+dnl
+PKG_CHECK_EXISTS([$3], [dnl
+	sr_have_$1=yes
+	m4_ifval([$2], [SR_APPEND([$2], ["$3"])
+	])sr_$1_version=`$PKG_CONFIG --modversion "$3" 2>&AS_MESSAGE_LOG_FD`
+	sr_pkg_check_summary_append "$3" "$sr_$1_version"[]dnl
+], [dnl
+	sr_pkg_check_summary_append "$3" no
+	m4_ifval([$4],
+		[SR_PKG_CHECK([$1], [$2], m4_shift3($@))],
+		[sr_have_$1=no sr_$1_version=])[]dnl
+])
+])
+
+## SR_VAR_OPT_PKG([modules-var], [features-var])
+##
+## Enable the collection of SR_ARG_OPT_PKG results into the shell variables
+## <modules-var> and <features-var>.
+##
+AC_DEFUN([SR_VAR_OPT_PKG],
+[dnl
+m4_define([_SR_VAR_OPT_PKG_MODULES], [$1])[]dnl
+m4_define([_SR_VAR_OPT_PKG_FEATURES], [$2])[]dnl
+m4_ifvaln([$1], [$1=])[]dnl
+m4_ifvaln([$2], [$2=])[]dnl
+])
+
+## _SR_ARG_OPT_IMPL(sh-name, [features-var], opt-name,
+##                  [cpp-name], [cond-name], check-commands)
+##
+m4_define([_SR_ARG_OPT_IMPL],
+[dnl
+AC_ARG_WITH([$3], [AS_HELP_STRING([--without-$3],
+			[disable $3 support [default=detect]])])
+AS_IF([test "x$with_$1" = xno], [sr_have_$1=no],
+	[test "x$sr_have_$1" != xyes], [dnl
+AC_MSG_CHECKING([for $3])
+$6
+AC_MSG_RESULT([$sr_have_$1])[]dnl
+])
+AS_IF([test "x$with_$1$sr_have_$1" = xyesno],
+	[AC_MSG_ERROR([$3 support requested, but it was not found.])])
+AS_IF([test "x$sr_have_$1" = xyes], [m4_ifval([$2], [
+	SR_APPEND([$2], ["$3"])])[]m4_ifval([$4], [
+	AC_DEFINE([HAVE_$4], [1], [Whether $3 is available.])])[]dnl
+])
+m4_ifvaln([$5], [AM_CONDITIONAL([$5], [test "x$sr_have_$1" = xyes])])[]dnl
+])
+
+## _SR_ARG_OPT_CHECK(sh-name, [features-var], opt-name, [cpp-name],
+##                   [cond-name], check-commands, [summary-result])
+##
+m4_define([_SR_ARG_OPT_CHECK],
+[dnl
+_SR_ARG_OPT_IMPL($@)
+sr_pkg_check_summary_append "$3" m4_default([$7], ["$sr_have_$1"])
+])
+
+## SR_ARG_OPT_CHECK(opt-name, [cpp-name], [cond-name], check-commands,
+##                  [summary-result = $sr_have_<opt-name>])
+##
+## Provide a --without-<opt-name> configure option for explicit disabling
+## of an optional dependency. If not disabled, the availability of the
+## optional dependency is auto-detected by running <check-commands>.
+##
+## The <check-commands> should set the shell variable sr_have_<opt-name>
+## to "yes" if the dependency is available, otherwise to "no". Optionally,
+## the <summary-result> argument may be used to generate a line in the
+## configuration summary. If supplied, it should be a shell word which
+## expands to the result to be displayed for the <opt-name> dependency.
+##
+## Use SR_VAR_OPT_PKG to generate lists of available modules and features.
+##
+AC_DEFUN([SR_ARG_OPT_CHECK],
+[dnl
+m4_assert([$# >= 4])[]dnl
+AC_REQUIRE([SR_PKG_CHECK_SUMMARY])[]dnl
+AC_REQUIRE([SR_VAR_OPT_PKG])[]dnl
+dnl
+_SR_ARG_OPT_CHECK(m4_expand([AS_TR_SH([$1])]),
+	m4_defn([_SR_VAR_OPT_PKG_FEATURES]),
+	$@)[]dnl
+])
+
+## _SR_ARG_OPT_PKG([features-var], [cond-name], opt-name,
+##                 [cpp-name], sh-name, [modules-var], module...)
+##
+m4_define([_SR_ARG_OPT_PKG],
+[dnl
+_SR_ARG_OPT_IMPL([$5], [$1], [$3], [$4], [$2],
+	[SR_PKG_CHECK(m4_shiftn([4], $@))])
+m4_ifvaln([$4], [AS_IF([test "x$sr_have_$5" = xyes],
+	[AC_DEFINE_UNQUOTED([CONF_$4_VERSION], ["$sr_$5_version"],
+		[Build-time version of $3.])])])[]dnl
+])
+
+## SR_ARG_OPT_PKG(opt-name, [cpp-name], [cond-name], module...)
+##
+## Provide a --without-<opt-name> configure option for explicit disabling
+## of an optional dependency. If not disabled, the availability of the
+## optional dependency is auto-detected.
+##
+## Each pkg-config <module> argument is checked in turn, and the first one
+## found is selected. On success, the shell variable sr_have_<opt-name>
+## is set to "yes", otherwise to "no". Optionally, a preprocessor macro
+## HAVE_<cpp-name> and an Automake conditional <cond-name> are generated.
+##
+## Use SR_VAR_OPT_PKG to generate lists of available modules and features.
+##
+AC_DEFUN([SR_ARG_OPT_PKG],
+[dnl
+m4_assert([$# >= 4])[]dnl
+AC_REQUIRE([PKG_PROG_PKG_CONFIG])[]dnl
+AC_REQUIRE([SR_PKG_CHECK_SUMMARY])[]dnl
+AC_REQUIRE([SR_VAR_OPT_PKG])[]dnl
+dnl
+_SR_ARG_OPT_PKG(m4_defn([_SR_VAR_OPT_PKG_FEATURES]),
+	[$3], [$1], [$2],
+	m4_expand([AS_TR_SH([$1])]),
+	m4_defn([_SR_VAR_OPT_PKG_MODULES]),
+	m4_shift3($@))[]dnl
+])
+
+## SR_PROG_VERSION(program, sh-var)
+##
+## Obtain the version of <program> and store it in <sh-var>.
+##
+AC_DEFUN([SR_PROG_VERSION],
+[dnl
+m4_assert([$# >= 2])[]dnl
+sr_prog_ver=`$1 --version 2>&AS_MESSAGE_LOG_FD | sed 1q 2>&AS_MESSAGE_LOG_FD`
+AS_CASE([[$]?:$sr_prog_ver],
+	[[0:*[0-9].[0-9]*]], [$2=$sr_prog_ver],
+	[$2=unknown])[]dnl
+])
+
+## SR_PROG_MAKE_ORDER_ONLY
+##
+## Check whether the make program supports order-only prerequisites.
+## If so, set the substitution variable ORDER to '|', or to the empty
+## string otherwise.
+##
+AC_DEFUN([SR_PROG_MAKE_ORDER_ONLY],
+[dnl
+AC_CACHE_CHECK([whether [$]{MAKE:-make} supports order-only prerequisites],
+	[sr_cv_prog_make_order_only], [
+cat >conftest.mk <<'_SREOF'
+a: b | c
+a b c: ; @:
+.PHONY: a b c
+_SREOF
+AS_IF([[$]{MAKE:-make} -f conftest.mk >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD],
+	[sr_cv_prog_make_order_only=yes], [sr_cv_prog_make_order_only=no])
+rm -f conftest.mk
+])
+AS_IF([test "x$sr_cv_prog_make_order_only" = xyes], [ORDER='|'], [ORDER=])
+AC_SUBST([ORDER])
+AM_SUBST_NOTMAKE([ORDER])[]dnl
+])
+
+## SR_CHECK_COMPILE_FLAGS(flags-var, description, flags)
+##
+## Find a compiler flag for <description>. For each flag in <flags>, check
+## if the compiler for the current language accepts it. On success, stop the
+## search and append the last tested flag to <flags-var>. Calls AC_SUBST
+## on <flags-var>.
+##
+AC_DEFUN([SR_CHECK_COMPILE_FLAGS],
+[dnl
+m4_assert([$# >= 3])[]dnl
+AC_MSG_CHECKING([compiler flag for $2])
+sr_ccf_result=no
+sr_ccf_save_CPPFLAGS=$CPPFLAGS
+for sr_flag in $3
+do
+	CPPFLAGS="$sr_ccf_save_CPPFLAGS $sr_flag"
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])], [sr_ccf_result=$sr_flag])
+	test "x$sr_ccf_result" = xno || break
+done
+CPPFLAGS=$sr_ccf_save_CPPFLAGS
+AS_IF([test "x$sr_ccf_result" != xno],
+	[SR_APPEND([$1], [$sr_ccf_result])])
+AC_MSG_RESULT([$sr_ccf_result])
+AC_SUBST([$1])
+])
+
+## _SR_ARG_ENABLE_WARNINGS_ONCE
+##
+## Implementation helper macro of SR_ARG_ENABLE_WARNINGS. Pulled in
+## through AC_REQUIRE so that it is only expanded once.
+##
+m4_define([_SR_ARG_ENABLE_WARNINGS_ONCE],
+[dnl
+AC_PROVIDE([$0])[]dnl
+AC_ARG_ENABLE([warnings],
+		[AS_HELP_STRING([[--enable-warnings[=min|max|fatal|no]]],
+				[set compile pedantry level [default=max]])],
+		[sr_enable_warnings=$enableval],
+		[sr_enable_warnings=max])[]dnl
+dnl
+# Test whether the compiler accepts each flag.  Look at standard output,
+# since GCC only shows a warning message if an option is not supported.
+sr_check_compile_warning_flags() {
+	for sr_flag
+	do
+		sr_cc_out=`$sr_cc $sr_warning_flags $sr_flag -c "$sr_conftest" 2>&1 || echo failed`
+		AS_IF([test "$?$sr_cc_out" = 0],
+			[SR_APPEND([sr_warning_flags], [$sr_flag])],
+			[AS_ECHO(["$sr_cc: $sr_cc_out"]) >&AS_MESSAGE_LOG_FD])
+		rm -f "conftest.[$]{OBJEXT:-o}"
+	done
+}
+])
+
+## SR_ARG_ENABLE_WARNINGS(variable, min-flags, max-flags)
+##
+## Provide the --enable-warnings configure argument, set to "min" by default.
+## <min-flags> and <max-flags> should be space-separated lists of compiler
+## warning flags to use with --enable-warnings=min or --enable-warnings=max,
+## respectively. Warning level "fatal" is the same as "max" but in addition
+## enables -Werror mode.
+##
+## In order to determine the warning options to use with the C++ compiler,
+## call AC_LANG([C++]) first to change the current language. If different
+## output variables are used, it is also fine to call SR_ARG_ENABLE_WARNINGS
+## repeatedly, once for each language setting.
+##
+AC_DEFUN([SR_ARG_ENABLE_WARNINGS],
+[dnl
+m4_assert([$# >= 3])[]dnl
+AC_REQUIRE([_SR_ARG_ENABLE_WARNINGS_ONCE])[]dnl
+dnl
+AS_CASE([$ac_compile],
+	[[*'$CXXFLAGS '*]], [sr_lang='C++' sr_cc=$CXX sr_conftest="conftest.[$]{ac_ext:-cc}"],
+	[[*'$CFLAGS '*]],   [sr_lang=C sr_cc=$CC sr_conftest="conftest.[$]{ac_ext:-c}"],
+	[AC_MSG_ERROR([[current language is neither C nor C++]])])
+dnl
+AC_MSG_CHECKING([which $sr_lang compiler warning flags to use])
+sr_warning_flags=
+AC_LANG_CONFTEST([AC_LANG_SOURCE([[
+int main(int argc, char** argv) { return (argv != 0) ? argc : 0; }
+]])])
+AS_CASE([$sr_enable_warnings],
+	[no], [],
+	[min], [sr_check_compile_warning_flags $2],
+	[fatal], [sr_check_compile_warning_flags $3 -Werror],
+	[sr_check_compile_warning_flags $3])
+rm -f "$sr_conftest"
+AC_SUBST([$1], [$sr_warning_flags])
+AC_MSG_RESULT([[$]{sr_warning_flags:-none}])[]dnl
+])
diff --git a/output/csv.c b/output/csv.c
deleted file mode 100644
index d17969e..0000000
--- a/output/csv.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011 Uwe Hermann <uwe at 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 <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include "config.h" /* Needed for PACKAGE_STRING and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "output/csv"
-
-struct context {
-	unsigned int num_enabled_channels;
-	uint64_t samplerate;
-	char separator;
-	gboolean header_done;
-	int *channel_index;
-};
-
-/*
- * TODO:
- *  - Option to specify delimiter character and/or string.
- *  - Option to (not) print metadata as comments.
- *  - Option to specify the comment character(s), e.g. # or ; or C/C++-style.
- *  - Option to (not) print samplenumber / time as extra column.
- *  - Option to "compress" output (only print changed samples, VCD-like).
- *  - Option to print comma-separated bits, or whole bytes/words (for 8/16
- *    channel LAs) as ASCII/hex etc. etc.
- *  - Trigger support.
- */
-
-static int init(struct sr_output *o)
-{
-	struct context *ctx;
-	struct sr_channel *ch;
-	GSList *l;
-	int i;
-
-	if (!o || !o->sdi)
-		return SR_ERR_ARG;
-
-	ctx = g_malloc0(sizeof(struct context));
-	o->internal = ctx;
-	ctx->separator = ',';
-
-	/* Get the number of channels, and the unitsize. */
-	for (l = o->sdi->channels; l; l = l->next) {
-		ch = l->data;
-		if (ch->type != SR_CHANNEL_LOGIC)
-			continue;
-		if (!ch->enabled)
-			continue;
-		ctx->num_enabled_channels++;
-	}
-	ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
-
-	/* Once more to map the enabled channels. */
-	for (i = 0, l = o->sdi->channels; l; l = l->next) {
-		ch = l->data;
-		if (ch->type != SR_CHANNEL_LOGIC)
-			continue;
-		if (!ch->enabled)
-			continue;
-		ctx->channel_index[i++] = ch->index;
-	}
-
-	return SR_OK;
-}
-
-static GString *gen_header(struct sr_output *o)
-{
-	struct context *ctx;
-	struct sr_channel *ch;
-	GVariant *gvar;
-	GString *header;
-	GSList *l;
-	time_t t;
-	int num_channels, i;
-	char *samplerate_s;
-
-	ctx = o->internal;
-	header = g_string_sized_new(512);
-
-	/* Some metadata */
-	t = time(NULL);
-	g_string_append_printf(header, "; CSV, generated by %s on %s",
-			PACKAGE_STRING, ctime(&t));
-
-	/* Columns / channels */
-	num_channels = g_slist_length(o->sdi->channels);
-	g_string_append_printf(header, "; Channels (%d/%d):",
-			ctx->num_enabled_channels, num_channels);
-	for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
-		ch = l->data;
-		if (ch->type != SR_CHANNEL_LOGIC)
-			continue;
-		if (!ch->enabled)
-			continue;
-		g_string_append_printf(header, " %s,", ch->name);
-	}
-	if (o->sdi->channels)
-		/* Drop last separator. */
-		g_string_truncate(header, header->len - 1);
-	g_string_append_printf(header, "\n");
-
-	if (ctx->samplerate == 0) {
-		if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
-				&gvar) == SR_OK) {
-			ctx->samplerate = g_variant_get_uint64(gvar);
-			g_variant_unref(gvar);
-		}
-	}
-	if (ctx->samplerate != 0) {
-		samplerate_s = sr_samplerate_string(ctx->samplerate);
-		g_string_append_printf(header, "; Samplerate: %s\n", samplerate_s);
-		g_free(samplerate_s);
-	}
-
-	return header;
-}
-
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
-		GString **out)
-{
-	const struct sr_datafeed_meta *meta;
-	const struct sr_datafeed_logic *logic;
-	const struct sr_config *src;
-	GSList *l;
-	struct context *ctx;
-	int idx;
-	uint64_t i, j;
-	gchar *p, c;
-
-	*out = NULL;
-	if (!o || !o->sdi)
-		return SR_ERR_ARG;
-	if (!(ctx = o->internal))
-		return SR_ERR_ARG;
-
-	switch (packet->type) {
-	case SR_DF_META:
-		meta = packet->payload;
-		for (l = meta->config; l; l = l->next) {
-			src = l->data;
-			if (src->key != SR_CONF_SAMPLERATE)
-				continue;
-			ctx->samplerate = g_variant_get_uint64(src->data);
-		}
-		break;
-	case SR_DF_LOGIC:
-		logic = packet->payload;
-		if (!ctx->header_done) {
-			*out = gen_header(o);
-			ctx->header_done = TRUE;
-		} else {
-			*out = g_string_sized_new(512);
-		}
-
-		for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
-			for (j = 0; j < ctx->num_enabled_channels; j++) {
-				idx = ctx->channel_index[j];
-				p = logic->data + i + idx / 8;
-				c = *p & (1 << (idx % 8));
-				g_string_append_c(*out, c ? '1' : '0');
-				g_string_append_c(*out, ctx->separator);
-			}
-			if (j) {
-				/* Drop last separator. */
-				g_string_truncate(*out, (*out)->len - 1);
-			}
-			g_string_append_printf(*out, "\n");
-		}
-		break;
-	}
-
-	return SR_OK;
-}
-
-static int cleanup(struct sr_output *o)
-{
-	struct context *ctx;
-
-	if (!o || !o->sdi)
-		return SR_ERR_ARG;
-
-	if (o->internal) {
-		ctx = o->internal;
-		g_free(ctx->channel_index);
-		g_free(o->internal);
-		o->internal = NULL;
-	}
-
-	return SR_OK;
-}
-
-SR_PRIV struct sr_output_format output_csv = {
-	.id = "csv",
-	.description = "Comma-separated values (CSV)",
-	.init = init,
-	.receive = receive,
-	.cleanup = cleanup,
-};
diff --git a/output/output.c b/output/output.c
deleted file mode 100644
index 4cf78f6..0000000
--- a/output/output.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert at 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 "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/**
- * @file
- *
- * Output file/data format handling.
- */
-
-/**
- * @defgroup grp_output Output formats
- *
- * Output file/data format handling.
- *
- * libsigrok supports several output (file) formats, e.g. binary, VCD,
- * gnuplot, and so on. It provides an output API that frontends can use.
- * New output formats can be added/implemented in libsigrok without having
- * to change the frontends at all.
- *
- * All output modules are fed data in a stream. Devices that can stream data
- * into libsigrok, instead of storing and then transferring the whole buffer,
- * can thus generate output live.
- *
- * Output modules generate a newly allocated GString. The caller is then
- * expected to free this with g_string_free() when finished with it.
- *
- * @{
- */
-
-/** @cond PRIVATE */
-extern SR_PRIV struct sr_output_format output_bits;
-extern SR_PRIV struct sr_output_format output_hex;
-extern SR_PRIV struct sr_output_format output_ascii;
-extern SR_PRIV struct sr_output_format output_binary;
-extern SR_PRIV struct sr_output_format output_vcd;
-extern SR_PRIV struct sr_output_format output_ols;
-extern SR_PRIV struct sr_output_format output_gnuplot;
-extern SR_PRIV struct sr_output_format output_chronovu_la8;
-extern SR_PRIV struct sr_output_format output_csv;
-extern SR_PRIV struct sr_output_format output_analog;
-/* @endcond */
-
-static struct sr_output_format *output_module_list[] = {
-	&output_ascii,
-	&output_binary,
-	&output_bits,
-	&output_csv,
-	&output_gnuplot,
-	&output_hex,
-	&output_ols,
-	&output_vcd,
-	&output_chronovu_la8,
-	&output_analog,
-	NULL,
-};
-
-/** @since 0.1.0 */
-SR_API struct sr_output_format **sr_output_list(void)
-{
-	return output_module_list;
-}
-
-/** @since 0.3.0 */
-SR_API struct sr_output *sr_output_new(struct sr_output_format *of,
-		GHashTable *params, const struct sr_dev_inst *sdi)
-{
-	struct sr_output *o;
-
-	o = g_malloc(sizeof(struct sr_output));
-	o->format = of;
-	o->sdi = sdi;
-	o->params = params;
-	if (o->format->init && o->format->init(o) != SR_OK) {
-		g_free(o);
-		o = NULL;
-	}
-
-	return o;
-}
-
-/** @since 0.3.0 */
-SR_API int sr_output_send(struct sr_output *o,
-		const struct sr_datafeed_packet *packet, GString **out)
-{
-	return o->format->receive(o, packet, out);
-}
-
-/** @since 0.3.0 */
-SR_API int sr_output_free(struct sr_output *o)
-{
-	int ret;
-
-	ret = SR_OK;
-	if (o->format->cleanup)
-		ret = o->format->cleanup(o);
-	g_free(o);
-
-	return ret;
-}
-
-/** @} */
diff --git a/proto.h b/proto.h
deleted file mode 100644
index 49353d5..0000000
--- a/proto.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert at 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_PROTO_H
-#define LIBSIGROK_PROTO_H
-
-/**
- * @file
- *
- * Header file containing API function prototypes.
- */
-
-/*--- backend.c -------------------------------------------------------------*/
-
-SR_API int sr_init(struct sr_context **ctx);
-SR_API int sr_exit(struct sr_context *ctx);
-
-/*--- log.c -----------------------------------------------------------------*/
-
-typedef int (*sr_log_callback)(void *cb_data, int loglevel,
-				const char *format, va_list args);
-
-SR_API int sr_log_loglevel_set(int loglevel);
-SR_API int sr_log_loglevel_get(void);
-SR_API int sr_log_callback_set(sr_log_callback cb, void *cb_data);
-SR_API int sr_log_callback_set_default(void);
-SR_API int sr_log_logdomain_set(const char *logdomain);
-SR_API char *sr_log_logdomain_get(void);
-
-/*--- device.c --------------------------------------------------------------*/
-
-SR_API int sr_dev_channel_name_set(const struct sr_dev_inst *sdi,
-		int channelnum, const char *name);
-SR_API int sr_dev_channel_enable(const struct sr_dev_inst *sdi, int channelnum,
-		gboolean state);
-SR_API int sr_dev_trigger_set(const struct sr_dev_inst *sdi, int channelnum,
-		const char *trigger);
-SR_API gboolean sr_dev_has_option(const struct sr_dev_inst *sdi, int key);
-SR_API GSList *sr_dev_list(const struct sr_dev_driver *driver);
-SR_API int sr_dev_clear(const struct sr_dev_driver *driver);
-SR_API int sr_dev_open(struct sr_dev_inst *sdi);
-SR_API int sr_dev_close(struct sr_dev_inst *sdi);
-
-/*--- hwdriver.c ------------------------------------------------------------*/
-
-SR_API struct sr_dev_driver **sr_driver_list(void);
-SR_API int sr_driver_init(struct sr_context *ctx,
-		struct sr_dev_driver *driver);
-SR_API GSList *sr_driver_scan(struct sr_dev_driver *driver, GSList *options);
-SR_API int sr_config_get(const struct sr_dev_driver *driver,
-		const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg,
-		int key, GVariant **data);
-SR_API int sr_config_set(const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg,
-		int key, GVariant *data);
-SR_API int sr_config_commit(const struct sr_dev_inst *sdi);
-SR_API int sr_config_list(const struct sr_dev_driver *driver,
-		const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg,
-		int key, GVariant **data);
-SR_API const struct sr_config_info *sr_config_info_get(int key);
-SR_API const struct sr_config_info *sr_config_info_name_get(const char *optname);
-
-/*--- session.c -------------------------------------------------------------*/
-
-typedef void (*sr_datafeed_callback)(const struct sr_dev_inst *sdi,
-		const struct sr_datafeed_packet *packet, void *cb_data);
-
-/* Session setup */
-SR_API int sr_session_load(const char *filename);
-SR_API struct sr_session *sr_session_new(void);
-SR_API int sr_session_destroy(void);
-SR_API int sr_session_dev_remove_all(void);
-SR_API int sr_session_dev_add(const struct sr_dev_inst *sdi);
-SR_API int sr_session_dev_list(GSList **devlist);
-
-/* Datafeed setup */
-SR_API int sr_session_datafeed_callback_remove_all(void);
-SR_API int sr_session_datafeed_callback_add(sr_datafeed_callback cb,
-		void *cb_data);
-
-/* Session control */
-SR_API int sr_session_start(void);
-SR_API int sr_session_run(void);
-SR_API int sr_session_stop(void);
-SR_API int sr_session_save(const char *filename, const struct sr_dev_inst *sdi,
-		unsigned char *buf, int unitsize, int units);
-SR_API int sr_session_save_init(const char *filename, uint64_t samplerate,
-		char **channels);
-SR_API int sr_session_append(const char *filename, unsigned char *buf,
-		int unitsize, int units);
-SR_API int sr_session_source_add(int fd, int events, int timeout,
-		sr_receive_data_callback cb, void *cb_data);
-SR_API int sr_session_source_add_pollfd(GPollFD *pollfd, int timeout,
-		sr_receive_data_callback cb, void *cb_data);
-SR_API int sr_session_source_add_channel(GIOChannel *channel, int events,
-		int timeout, sr_receive_data_callback cb, void *cb_data);
-SR_API int sr_session_source_remove(int fd);
-SR_API int sr_session_source_remove_pollfd(GPollFD *pollfd);
-SR_API int sr_session_source_remove_channel(GIOChannel *channel);
-
-/*--- input/input.c ---------------------------------------------------------*/
-
-SR_API struct sr_input_format **sr_input_list(void);
-
-/*--- output/output.c -------------------------------------------------------*/
-
-SR_API struct sr_output_format **sr_output_list(void);
-SR_API struct sr_output *sr_output_new(struct sr_output_format *of,
-		GHashTable *params, const struct sr_dev_inst *sdi);
-SR_API int sr_output_send(struct sr_output *o,
-		const struct sr_datafeed_packet *packet, GString **out);
-SR_API int sr_output_free(struct sr_output *o);
-
-/*--- strutil.c -------------------------------------------------------------*/
-
-SR_API char *sr_si_string_u64(uint64_t x, const char *unit);
-SR_API char *sr_samplerate_string(uint64_t samplerate);
-SR_API char *sr_period_string(uint64_t frequency);
-SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q);
-SR_API char **sr_parse_triggerstring(const struct sr_dev_inst *sdi,
-		const char *triggerstring);
-SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size);
-SR_API uint64_t sr_parse_timestring(const char *timestring);
-SR_API gboolean sr_parse_boolstring(const char *boolstring);
-SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q);
-SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q);
-
-/*--- version.c -------------------------------------------------------------*/
-
-SR_API int sr_package_version_major_get(void);
-SR_API int sr_package_version_minor_get(void);
-SR_API int sr_package_version_micro_get(void);
-SR_API const char *sr_package_version_string_get(void);
-
-SR_API int sr_lib_version_current_get(void);
-SR_API int sr_lib_version_revision_get(void);
-SR_API int sr_lib_version_age_get(void);
-SR_API const char *sr_lib_version_string_get(void);
-
-/*--- error.c ---------------------------------------------------------------*/
-
-SR_API const char *sr_strerror(int error_code);
-SR_API const char *sr_strerror_name(int error_code);
-
-#endif
diff --git a/session.c b/session.c
deleted file mode 100644
index 0540942..0000000
--- a/session.c
+++ /dev/null
@@ -1,835 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert at 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 <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "session"
-/** @endcond */
-
-/**
- * @file
- *
- * Creating, using, or destroying libsigrok sessions.
- */
-
-/**
- * @defgroup grp_session Session handling
- *
- * Creating, using, or destroying libsigrok sessions.
- *
- * @{
- */
-
-struct source {
-	int timeout;
-	sr_receive_data_callback cb;
-	void *cb_data;
-
-	/* This is used to keep track of the object (fd, pollfd or channel) which is
-	 * being polled and will be used to match the source when removing it again.
-	 */
-	gintptr poll_object;
-};
-
-struct datafeed_callback {
-	sr_datafeed_callback cb;
-	void *cb_data;
-};
-
-/* There can only be one session at a time. */
-/* 'session' is not static, it's used elsewhere (via 'extern'). */
-struct sr_session *session;
-
-/**
- * Create a new session.
- *
- * @todo Should it use the file-global "session" variable or take an argument?
- *       The same question applies to all the other session functions.
- *
- * @retval NULL Error.
- * @retval other A pointer to the newly allocated session.
- *
- * @since 0.1.0
- */
-SR_API struct sr_session *sr_session_new(void)
-{
-	if (!(session = g_try_malloc0(sizeof(struct sr_session)))) {
-		sr_err("Session malloc failed.");
-		return NULL;
-	}
-
-	session->source_timeout = -1;
-	session->running = FALSE;
-	session->abort_session = FALSE;
-	g_mutex_init(&session->stop_mutex);
-
-	return session;
-}
-
-/**
- * Destroy the current session.
- * This frees up all memory used by the session.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG No session exists.
- *
- * @since 0.1.0
- */
-SR_API int sr_session_destroy(void)
-{
-	if (!session) {
-		sr_err("%s: session was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	sr_session_dev_remove_all();
-
-	/* TODO: Error checks needed? */
-
-	g_mutex_clear(&session->stop_mutex);
-
-	g_free(session);
-	session = NULL;
-
-	return SR_OK;
-}
-
-/**
- * Remove all the devices from the current session.
- *
- * The session itself (i.e., the struct sr_session) is not free'd and still
- * exists after this function returns.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG No session exists.
- *
- * @since 0.1.0
- */
-SR_API int sr_session_dev_remove_all(void)
-{
-	if (!session) {
-		sr_err("%s: session was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	g_slist_free(session->devs);
-	session->devs = NULL;
-
-	return SR_OK;
-}
-
-/**
- * Add a device instance to the current session.
- *
- * @param sdi The device instance to add to the current session. Must not
- *            be NULL. Also, sdi->driver and sdi->driver->dev_open must
- *            not be NULL.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_BUG No session exists.
- *
- * @since 0.2.0
- */
-SR_API int sr_session_dev_add(const struct sr_dev_inst *sdi)
-{
-	int ret;
-
-	if (!sdi) {
-		sr_err("%s: sdi was NULL", __func__);
-		return SR_ERR_ARG;
-	}
-
-	if (!session) {
-		sr_err("%s: session was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	/* If sdi->driver is NULL, this is a virtual device. */
-	if (!sdi->driver) {
-		sr_dbg("%s: sdi->driver was NULL, this seems to be "
-		       "a virtual device; continuing", __func__);
-		/* Just add the device, don't run dev_open(). */
-		session->devs = g_slist_append(session->devs, (gpointer)sdi);
-		return SR_OK;
-	}
-
-	/* sdi->driver is non-NULL (i.e. we have a real device). */
-	if (!sdi->driver->dev_open) {
-		sr_err("%s: sdi->driver->dev_open was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	session->devs = g_slist_append(session->devs, (gpointer)sdi);
-
-	if (session->running) {
-		/* Adding a device to a running session. Commit settings
-		 * and start acquisition on that device now. */
-		if ((ret = sr_config_commit(sdi)) != SR_OK) {
-			sr_err("Failed to commit device settings before "
-			       "starting acquisition in running session (%s)",
-			       sr_strerror(ret));
-			return ret;
-		}
-		if ((ret = sdi->driver->dev_acquisition_start(sdi,
-						(void *)sdi)) != SR_OK) {
-			sr_err("Failed to start acquisition of device in "
-			       "running session (%s)", sr_strerror(ret));
-			return ret;
-		}
-	}
-
-	return SR_OK;
-}
-
-/**
- * List all device instances attached to the current session.
- *
- * @param devlist A pointer where the device instance list will be
- *                stored on return. If no devices are in the session,
- *                this will be NULL. Each element in the list points
- *                to a struct sr_dev_inst *.
- *                The list must be freed by the caller, but not the
- *                elements pointed to.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Invalid argument.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_dev_list(GSList **devlist)
-{
-
-	*devlist = NULL;
-
-	if (!session)
-		return SR_ERR;
-
-	*devlist = g_slist_copy(session->devs);
-
-	return SR_OK;
-}
-
-/**
- * Remove all datafeed callbacks in the current session.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG No session exists.
- *
- * @since 0.1.0
- */
-SR_API int sr_session_datafeed_callback_remove_all(void)
-{
-	if (!session) {
-		sr_err("%s: session was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	g_slist_free_full(session->datafeed_callbacks, g_free);
-	session->datafeed_callbacks = NULL;
-
-	return SR_OK;
-}
-
-/**
- * Add a datafeed callback to the current session.
- *
- * @param cb Function to call when a chunk of data is received.
- *           Must not be NULL.
- * @param cb_data Opaque pointer passed in by the caller.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG No session exists.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_datafeed_callback_add(sr_datafeed_callback cb, void *cb_data)
-{
-	struct datafeed_callback *cb_struct;
-
-	if (!session) {
-		sr_err("%s: session was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	if (!cb) {
-		sr_err("%s: cb was NULL", __func__);
-		return SR_ERR_ARG;
-	}
-
-	if (!(cb_struct = g_try_malloc0(sizeof(struct datafeed_callback))))
-		return SR_ERR_MALLOC;
-
-	cb_struct->cb = cb;
-	cb_struct->cb_data = cb_data;
-
-	session->datafeed_callbacks =
-	    g_slist_append(session->datafeed_callbacks, cb_struct);
-
-	return SR_OK;
-}
-
-/**
- * Call every device in the session's callback.
- *
- * For sessions not driven by select loops such as sr_session_run(),
- * but driven by another scheduler, this can be used to poll the devices
- * from within that scheduler.
- *
- * @param block If TRUE, this call will wait for any of the session's
- *              sources to fire an event on the file descriptors, or
- *              any of their timeouts to activate. In other words, this
- *              can be used as a select loop.
- *              If FALSE, all sources have their callback run, regardless
- *              of file descriptor or timeout status.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Error occured.
- */
-static int sr_session_iteration(gboolean block)
-{
-	unsigned int i;
-	int ret;
-
-	ret = g_poll(session->pollfds, session->num_sources,
-			block ? session->source_timeout : 0);
-	for (i = 0; i < session->num_sources; i++) {
-		if (session->pollfds[i].revents > 0 || (ret == 0
-			&& session->source_timeout == session->sources[i].timeout)) {
-			/*
-			 * Invoke the source's callback on an event,
-			 * or if the poll timed out and this source
-			 * asked for that timeout.
-			 */
-			if (!session->sources[i].cb(session->pollfds[i].fd,
-					session->pollfds[i].revents,
-					session->sources[i].cb_data))
-				sr_session_source_remove(session->sources[i].poll_object);
-		}
-		/*
-		 * We want to take as little time as possible to stop
-		 * the session if we have been told to do so. Therefore,
-		 * we check the flag after processing every source, not
-		 * just once per main event loop.
-		 */
-		g_mutex_lock(&session->stop_mutex);
-		if (session->abort_session) {
-			sr_session_stop_sync();
-			/* But once is enough. */
-			session->abort_session = FALSE;
-		}
-		g_mutex_unlock(&session->stop_mutex);
-	}
-
-	return SR_OK;
-}
-
-/**
- * Start a session.
- *
- * There can only be one session at a time.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR Error occured.
- *
- * @since 0.1.0
- */
-SR_API int sr_session_start(void)
-{
-	struct sr_dev_inst *sdi;
-	GSList *l;
-	int ret;
-
-	if (!session) {
-		sr_err("%s: session was NULL; a session must be "
-		       "created before starting it.", __func__);
-		return SR_ERR_BUG;
-	}
-
-	if (!session->devs) {
-		sr_err("%s: session->devs was NULL; a session "
-		       "cannot be started without devices.", __func__);
-		return SR_ERR_BUG;
-	}
-
-	sr_info("Starting.");
-
-	ret = SR_OK;
-	for (l = session->devs; l; l = l->next) {
-		sdi = l->data;
-		if ((ret = sr_config_commit(sdi)) != SR_OK) {
-			sr_err("Failed to commit device settings before "
-			       "starting acquisition (%s)", sr_strerror(ret));
-			break;
-		}
-		if ((ret = sdi->driver->dev_acquisition_start(sdi, sdi)) != SR_OK) {
-			sr_err("%s: could not start an acquisition "
-			       "(%s)", __func__, sr_strerror(ret));
-			break;
-		}
-	}
-
-	/* TODO: What if there are multiple devices? Which return code? */
-
-	return ret;
-}
-
-/**
- * Run the session.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG Error occured.
- *
- * @since 0.1.0
- */
-SR_API int sr_session_run(void)
-{
-	if (!session) {
-		sr_err("%s: session was NULL; a session must be "
-		       "created first, before running it.", __func__);
-		return SR_ERR_BUG;
-	}
-
-	if (!session->devs) {
-		/* TODO: Actually the case? */
-		sr_err("%s: session->devs was NULL; a session "
-		       "cannot be run without devices.", __func__);
-		return SR_ERR_BUG;
-	}
-	session->running = TRUE;
-
-	sr_info("Running.");
-
-	/* Do we have real sources? */
-	if (session->num_sources == 1 && session->pollfds[0].fd == -1) {
-		/* Dummy source, freewheel over it. */
-		while (session->num_sources)
-			session->sources[0].cb(-1, 0, session->sources[0].cb_data);
-	} else {
-		/* Real sources, use g_poll() main loop. */
-		while (session->num_sources)
-			sr_session_iteration(TRUE);
-	}
-
-	return SR_OK;
-}
-
-/**
- * Stop the current session.
- *
- * The current session is stopped immediately, with all acquisition sessions
- * being stopped and hardware drivers cleaned up.
- *
- * This must be called from within the session thread, to prevent freeing
- * resources that the session thread will try to use.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG No session exists.
- *
- * @private
- */
-SR_PRIV int sr_session_stop_sync(void)
-{
-	struct sr_dev_inst *sdi;
-	GSList *l;
-
-	if (!session) {
-		sr_err("%s: session was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	sr_info("Stopping.");
-
-	for (l = session->devs; l; l = l->next) {
-		sdi = l->data;
-		if (sdi->driver) {
-			if (sdi->driver->dev_acquisition_stop)
-				sdi->driver->dev_acquisition_stop(sdi, sdi);
-		}
-	}
-	session->running = FALSE;
-
-	return SR_OK;
-}
-
-/**
- * Stop the current session.
- *
- * The current session is stopped immediately, with all acquisition sessions
- * being stopped and hardware drivers cleaned up.
- *
- * If the session is run in a separate thread, this function will not block
- * until the session is finished executing. It is the caller's responsibility
- * to wait for the session thread to return before assuming that the session is
- * completely decommissioned.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_BUG No session exists.
- *
- * @since 0.1.0
- */
-SR_API int sr_session_stop(void)
-{
-	if (!session) {
-		sr_err("%s: session was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	g_mutex_lock(&session->stop_mutex);
-	session->abort_session = TRUE;
-	g_mutex_unlock(&session->stop_mutex);
-
-	return SR_OK;
-}
-
-/**
- * Debug helper.
- *
- * @param packet The packet to show debugging information for.
- */
-static void datafeed_dump(const struct sr_datafeed_packet *packet)
-{
-	const struct sr_datafeed_logic *logic;
-	const struct sr_datafeed_analog *analog;
-
-	switch (packet->type) {
-	case SR_DF_HEADER:
-		sr_dbg("bus: Received SR_DF_HEADER packet.");
-		break;
-	case SR_DF_TRIGGER:
-		sr_dbg("bus: Received SR_DF_TRIGGER packet.");
-		break;
-	case SR_DF_META:
-		sr_dbg("bus: Received SR_DF_META packet.");
-		break;
-	case SR_DF_LOGIC:
-		logic = packet->payload;
-		sr_dbg("bus: Received SR_DF_LOGIC packet (%" PRIu64 " bytes, "
-		       "unitsize = %d).", logic->length, logic->unitsize);
-		break;
-	case SR_DF_ANALOG:
-		analog = packet->payload;
-		sr_dbg("bus: Received SR_DF_ANALOG packet (%d samples).",
-		       analog->num_samples);
-		break;
-	case SR_DF_END:
-		sr_dbg("bus: Received SR_DF_END packet.");
-		break;
-	case SR_DF_FRAME_BEGIN:
-		sr_dbg("bus: Received SR_DF_FRAME_BEGIN packet.");
-		break;
-	case SR_DF_FRAME_END:
-		sr_dbg("bus: Received SR_DF_FRAME_END packet.");
-		break;
-	default:
-		sr_dbg("bus: Received unknown packet type: %d.", packet->type);
-		break;
-	}
-}
-
-/**
- * Send a packet to whatever is listening on the datafeed bus.
- *
- * Hardware drivers use this to send a data packet to the frontend.
- *
- * @param sdi TODO.
- * @param packet The datafeed packet to send to the session bus.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- *
- * @private
- */
-SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
-			    const struct sr_datafeed_packet *packet)
-{
-	GSList *l;
-	struct datafeed_callback *cb_struct;
-
-	if (!sdi) {
-		sr_err("%s: sdi was NULL", __func__);
-		return SR_ERR_ARG;
-	}
-
-	if (!packet) {
-		sr_err("%s: packet was NULL", __func__);
-		return SR_ERR_ARG;
-	}
-
-	for (l = session->datafeed_callbacks; l; l = l->next) {
-		if (sr_log_loglevel_get() >= SR_LOG_DBG)
-			datafeed_dump(packet);
-		cb_struct = l->data;
-		cb_struct->cb(sdi, packet, cb_struct->cb_data);
-	}
-
-	return SR_OK;
-}
-
-/**
- * Add an event source for a file descriptor.
- *
- * @param pollfd The GPollFD.
- * @param[in] timeout Max time to wait before the callback is called,
- *              ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- * @param poll_object TODO.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- */
-static int _sr_session_source_add(GPollFD *pollfd, int timeout,
-	sr_receive_data_callback cb, void *cb_data, gintptr poll_object)
-{
-	struct source *new_sources, *s;
-	GPollFD *new_pollfds;
-
-	if (!cb) {
-		sr_err("%s: cb was NULL", __func__);
-		return SR_ERR_ARG;
-	}
-
-	/* Note: cb_data can be NULL, that's not a bug. */
-
-	new_pollfds = g_try_realloc(session->pollfds,
-			sizeof(GPollFD) * (session->num_sources + 1));
-	if (!new_pollfds) {
-		sr_err("%s: new_pollfds malloc failed", __func__);
-		return SR_ERR_MALLOC;
-	}
-
-	new_sources = g_try_realloc(session->sources, sizeof(struct source) *
-			(session->num_sources + 1));
-	if (!new_sources) {
-		sr_err("%s: new_sources malloc failed", __func__);
-		return SR_ERR_MALLOC;
-	}
-
-	new_pollfds[session->num_sources] = *pollfd;
-	s = &new_sources[session->num_sources++];
-	s->timeout = timeout;
-	s->cb = cb;
-	s->cb_data = cb_data;
-	s->poll_object = poll_object;
-	session->pollfds = new_pollfds;
-	session->sources = new_sources;
-
-	if (timeout != session->source_timeout && timeout > 0
-	    && (session->source_timeout == -1 || timeout < session->source_timeout))
-		session->source_timeout = timeout;
-
-	return SR_OK;
-}
-
-/**
- * Add an event source for a file descriptor.
- *
- * @param fd The file descriptor.
- * @param events Events to check for.
- * @param timeout Max time to wait before the callback is called, ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_source_add(int fd, int events, int timeout,
-		sr_receive_data_callback cb, void *cb_data)
-{
-	GPollFD p;
-
-	p.fd = fd;
-	p.events = events;
-
-	return _sr_session_source_add(&p, timeout, cb, cb_data, (gintptr)fd);
-}
-
-/**
- * Add an event source for a GPollFD.
- *
- * @param pollfd The GPollFD.
- * @param timeout Max time to wait before the callback is called, ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_source_add_pollfd(GPollFD *pollfd, int timeout,
-		sr_receive_data_callback cb, void *cb_data)
-{
-	return _sr_session_source_add(pollfd, timeout, cb,
-				      cb_data, (gintptr)pollfd);
-}
-
-/**
- * Add an event source for a GIOChannel.
- *
- * @param channel The GIOChannel.
- * @param events Events to poll on.
- * @param timeout Max time to wait before the callback is called, ignored if 0.
- * @param cb Callback function to add. Must not be NULL.
- * @param cb_data Data for the callback function. Can be NULL.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_source_add_channel(GIOChannel *channel, int events,
-		int timeout, sr_receive_data_callback cb, void *cb_data)
-{
-	GPollFD p;
-
-#ifdef _WIN32
-	g_io_channel_win32_make_pollfd(channel, events, &p);
-#else
-	p.fd = g_io_channel_unix_get_fd(channel);
-	p.events = events;
-#endif
-
-	return _sr_session_source_add(&p, timeout, cb, cb_data, (gintptr)channel);
-}
-
-/**
- * Remove the source belonging to the specified channel.
- *
- * @todo Add more error checks and logging.
- *
- * @param poll_object The channel for which the source should be removed.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR_MALLOC Memory allocation error
- * @retval SR_ERR_BUG Internal error
- */
-static int _sr_session_source_remove(gintptr poll_object)
-{
-	struct source *new_sources;
-	GPollFD *new_pollfds;
-	unsigned int old;
-
-	if (!session->sources || !session->num_sources) {
-		sr_err("%s: sources was NULL", __func__);
-		return SR_ERR_BUG;
-	}
-
-	for (old = 0; old < session->num_sources; old++) {
-		if (session->sources[old].poll_object == poll_object)
-			break;
-	}
-
-	/* fd not found, nothing to do */
-	if (old == session->num_sources)
-		return SR_OK;
-
-	session->num_sources -= 1;
-
-	if (old != session->num_sources) {
-		memmove(&session->pollfds[old], &session->pollfds[old+1],
-			(session->num_sources - old) * sizeof(GPollFD));
-		memmove(&session->sources[old], &session->sources[old+1],
-			(session->num_sources - old) * sizeof(struct source));
-	}
-
-	new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources);
-	if (!new_pollfds && session->num_sources > 0) {
-		sr_err("%s: new_pollfds malloc failed", __func__);
-		return SR_ERR_MALLOC;
-	}
-
-	new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources);
-	if (!new_sources && session->num_sources > 0) {
-		sr_err("%s: new_sources malloc failed", __func__);
-		return SR_ERR_MALLOC;
-	}
-
-	session->pollfds = new_pollfds;
-	session->sources = new_sources;
-
-	return SR_OK;
-}
-
-/**
- * Remove the source belonging to the specified file descriptor.
- *
- * @param fd The file descriptor for which the source should be removed.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid argument
- * @retval SR_ERR_MALLOC Memory allocation error.
- * @retval SR_ERR_BUG Internal error.
- *
- * @since 0.3.0
- */
-SR_API int sr_session_source_remove(int fd)
-{
-	return _sr_session_source_remove((gintptr)fd);
-}
-
-/**
- * Remove the source belonging to the specified poll descriptor.
- *
- * @param pollfd The poll descriptor for which the source should be removed.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
- *         internal errors.
- *
- * @since 0.2.0
- */
-SR_API int sr_session_source_remove_pollfd(GPollFD *pollfd)
-{
-	return _sr_session_source_remove((gintptr)pollfd);
-}
-
-/**
- * Remove the source belonging to the specified channel.
- *
- * @param channel The channel for which the source should be removed.
- *
- * @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid argument.
- * @retval SR_ERR_MALLOC Memory allocation error.
- * @return SR_ERR_BUG Internal error.
- *
- * @since 0.2.0
- */
-SR_API int sr_session_source_remove_channel(GIOChannel *channel)
-{
-	return _sr_session_source_remove((gintptr)channel);
-}
-
-/** @} */
diff --git a/session_file.c b/session_file.c
deleted file mode 100644
index 880ef6d..0000000
--- a/session_file.c
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert at 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 <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <zip.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include "config.h" /* Needed for PACKAGE_VERSION and others. */
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-/** @cond PRIVATE */
-#define LOG_PREFIX "session-file"
-/** @endcond */
-
-/**
- * @file
- *
- * Loading and saving libsigrok session files.
- */
-
-/**
- * @addtogroup grp_session
- *
- * @{
- */
-
-extern struct sr_session *session;
-extern SR_PRIV struct sr_dev_driver session_driver;
-
-/** @private */
-SR_PRIV int sr_sessionfile_check(const char *filename)
-{
-	struct stat st;
-	struct zip *archive;
-	struct zip_file *zf;
-	struct zip_stat zs;
-	int version, ret;
-	char s[11];
-
-	if (!filename)
-		return SR_ERR_ARG;
-
-	if (stat(filename, &st) == -1) {
-		sr_err("Couldn't stat %s: %s", filename, strerror(errno));
-		return SR_ERR;
-	}
-
-	if (!(archive = zip_open(filename, 0, &ret)))
-		/* No logging: this can be used just to check if it's
-		 * a sigrok session file or not. */
-		return SR_ERR;
-
-	/* check "version" */
-	version = 0;
-	if (!(zf = zip_fopen(archive, "version", 0))) {
-		sr_dbg("Not a sigrok session file: no version found.");
-		return SR_ERR;
-	}
-	if ((ret = zip_fread(zf, s, 10)) == -1)
-		return SR_ERR;
-	zip_fclose(zf);
-	s[ret] = 0;
-	version = strtoull(s, NULL, 10);
-	if (version > 2) {
-		sr_dbg("Cannot handle sigrok session file version %d.", version);
-		return SR_ERR;
-	}
-	sr_spew("Detected sigrok session file version %d.", version);
-
-	/* read "metadata" */
-	if (zip_stat(archive, "metadata", 0, &zs) == -1) {
-		sr_dbg("Not a valid sigrok session file.");
-		return SR_ERR;
-	}
-
-	return SR_OK;
-}
-
-/**
- * Load the session from the specified filename.
- *
- * @param filename The name of the session file to load. Must not be NULL.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments,
- *         SR_ERR_MALLOC upon memory allocation errors, or SR_ERR upon
- *         other errors.
- */
-SR_API int sr_session_load(const char *filename)
-{
-	GKeyFile *kf;
-	GPtrArray *capturefiles;
-	struct zip *archive;
-	struct zip_file *zf;
-	struct zip_stat zs;
-	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
-	int ret, channelnum, devcnt, i, j;
-	uint64_t tmp_u64, total_channels, enabled_channels, p;
-	char **sections, **keys, *metafile, *val;
-	char channelname[SR_MAX_CHANNELNAME_LEN + 1];
-
-	if ((ret = sr_sessionfile_check(filename)) != SR_OK)
-		return ret;
-
-	if (!(archive = zip_open(filename, 0, &ret)))
-		return SR_ERR;
-
-	if (zip_stat(archive, "metadata", 0, &zs) == -1)
-		return SR_ERR;
-
-	if (!(metafile = g_try_malloc(zs.size))) {
-		sr_err("%s: metafile malloc failed", __func__);
-		return SR_ERR_MALLOC;
-	}
-
-	zf = zip_fopen_index(archive, zs.index, 0);
-	zip_fread(zf, metafile, zs.size);
-	zip_fclose(zf);
-
-	kf = g_key_file_new();
-	if (!g_key_file_load_from_data(kf, metafile, zs.size, 0, NULL)) {
-		sr_dbg("Failed to parse metadata.");
-		return SR_ERR;
-	}
-
-	sr_session_new();
-
-	devcnt = 0;
-	capturefiles = g_ptr_array_new_with_free_func(g_free);
-	sections = g_key_file_get_groups(kf, NULL);
-	for (i = 0; sections[i]; i++) {
-		if (!strcmp(sections[i], "global"))
-			/* nothing really interesting in here yet */
-			continue;
-		if (!strncmp(sections[i], "device ", 7)) {
-			/* device section */
-			sdi = NULL;
-			enabled_channels = total_channels = 0;
-			keys = g_key_file_get_keys(kf, sections[i], NULL, NULL);
-			for (j = 0; keys[j]; j++) {
-				val = g_key_file_get_string(kf, sections[i], keys[j], NULL);
-				if (!strcmp(keys[j], "capturefile")) {
-					sdi = sr_dev_inst_new(devcnt, SR_ST_ACTIVE, NULL, NULL, NULL);
-					sdi->driver = &session_driver;
-					if (devcnt == 0)
-						/* first device, init the driver */
-						sdi->driver->init(NULL);
-					sr_dev_open(sdi);
-					sr_session_dev_add(sdi);
-					sdi->driver->config_set(SR_CONF_SESSIONFILE,
-							g_variant_new_string(filename), sdi, NULL);
-					sdi->driver->config_set(SR_CONF_CAPTUREFILE,
-							g_variant_new_string(val), sdi, NULL);
-					g_ptr_array_add(capturefiles, val);
-				} else if (!strcmp(keys[j], "samplerate")) {
-					sr_parse_sizestring(val, &tmp_u64);
-					sdi->driver->config_set(SR_CONF_SAMPLERATE,
-							g_variant_new_uint64(tmp_u64), sdi, NULL);
-				} else if (!strcmp(keys[j], "unitsize")) {
-					tmp_u64 = strtoull(val, NULL, 10);
-					sdi->driver->config_set(SR_CONF_CAPTURE_UNITSIZE,
-							g_variant_new_uint64(tmp_u64), sdi, NULL);
-				} else if (!strcmp(keys[j], "total probes")) {
-					total_channels = strtoull(val, NULL, 10);
-					sdi->driver->config_set(SR_CONF_NUM_LOGIC_CHANNELS,
-							g_variant_new_uint64(total_channels), sdi, NULL);
-					for (p = 0; p < total_channels; p++) {
-						snprintf(channelname, SR_MAX_CHANNELNAME_LEN, "%" PRIu64, p);
-						if (!(ch = sr_channel_new(p, SR_CHANNEL_LOGIC, TRUE,
-								channelname)))
-							return SR_ERR;
-						sdi->channels = g_slist_append(sdi->channels, ch);
-					}
-				} else if (!strncmp(keys[j], "probe", 5)) {
-					if (!sdi)
-						continue;
-					enabled_channels++;
-					tmp_u64 = strtoul(keys[j]+5, NULL, 10);
-					/* sr_session_save() */
-					sr_dev_channel_name_set(sdi, tmp_u64 - 1, val);
-				} else if (!strncmp(keys[j], "trigger", 7)) {
-					channelnum = strtoul(keys[j]+7, NULL, 10);
-					sr_dev_trigger_set(sdi, channelnum, val);
-				}
-			}
-			g_strfreev(keys);
-			/* Disable channels not specifically listed. */
-			if (total_channels)
-				for (p = enabled_channels; p < total_channels; p++)
-					sr_dev_channel_enable(sdi, p, FALSE);
-		}
-		devcnt++;
-	}
-	g_strfreev(sections);
-	g_key_file_free(kf);
-
-	return SR_OK;
-}
-
-/**
- * Save the current session to the specified file.
- *
- * @param filename The name of the filename to save the current session as.
- *                 Must not be NULL.
- * @param sdi The device instance from which the data was captured.
- * @param buf The data to be saved.
- * @param unitsize The number of bytes per sample.
- * @param units The number of samples.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR Other errors
- *
- * @since 0.2.0
- */
-SR_API int sr_session_save(const char *filename, const struct sr_dev_inst *sdi,
-		unsigned char *buf, int unitsize, int units)
-{
-	struct sr_channel *ch;
-	GSList *l;
-	GVariant *gvar;
-	uint64_t samplerate;
-	int cnt, ret;
-	char **channel_names;
-
-	samplerate = 0;
-	if (sr_dev_has_option(sdi, SR_CONF_SAMPLERATE)) {
-		if (sr_config_get(sdi->driver, sdi, NULL,
-					SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
-			samplerate = g_variant_get_uint64(gvar);
-			g_variant_unref(gvar);
-		}
-	}
-
-	channel_names = g_malloc0(sizeof(char *) * (g_slist_length(sdi->channels) + 1));
-	cnt = 0;
-	for (l = sdi->channels; l; l = l->next) {
-		ch = l->data;
-		if (ch->type != SR_CHANNEL_LOGIC)
-			continue;
-		if (ch->enabled != TRUE)
-			continue;
-		if (!ch->name)
-			continue;
-		/* Just borrowing the ptr. */
-		channel_names[cnt++] = ch->name;
-	}
-
-	if ((ret = sr_session_save_init(filename, samplerate, channel_names)) != SR_OK)
-		return ret;
-
-	ret = sr_session_append(filename, buf, unitsize, units);
-
-	return ret;
-}
-
-/**
- * Initialize a saved session file.
- *
- * @param filename The name of the filename to save the current session as.
- *                 Must not be NULL.
- * @param samplerate The samplerate to store for this session.
- * @param channels A NULL-terminated array of strings containing the names
- * of all the channels active in this session.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR Other errors
- *
- * @since 0.3.0
- */
-SR_API int sr_session_save_init(const char *filename, uint64_t samplerate,
-		char **channels)
-{
-	FILE *meta;
-	struct zip *zipfile;
-	struct zip_source *versrc, *metasrc;
-	int tmpfile, cnt, ret, i;
-	char version[1], metafile[32], *s;
-
-	if (!filename) {
-		sr_err("%s: filename was NULL", __func__);
-		return SR_ERR_ARG;
-	}
-
-	/* Quietly delete it first, libzip wants replace ops otherwise. */
-	unlink(filename);
-	if (!(zipfile = zip_open(filename, ZIP_CREATE, &ret)))
-		return SR_ERR;
-
-	/* "version" */
-	version[0] = '2';
-	if (!(versrc = zip_source_buffer(zipfile, version, 1, 0)))
-		return SR_ERR;
-	if (zip_add(zipfile, "version", versrc) == -1) {
-		sr_info("error saving version into zipfile: %s",
-			zip_strerror(zipfile));
-		return SR_ERR;
-	}
-
-	/* init "metadata" */
-	strcpy(metafile, "sigrok-meta-XXXXXX");
-	if ((tmpfile = g_mkstemp(metafile)) == -1)
-		return SR_ERR;
-	close(tmpfile);
-	meta = g_fopen(metafile, "wb");
-	fprintf(meta, "[global]\n");
-	fprintf(meta, "sigrok version = %s\n", PACKAGE_VERSION);
-
-	/* metadata */
-	fprintf(meta, "[device 1]\n");
-
-	/* metadata */
-	fprintf(meta, "capturefile = logic-1\n");
-	cnt = 0;
-	for (i = 0; channels[i]; i++)
-		cnt++;
-	fprintf(meta, "total probes = %d\n", cnt);
-	s = sr_samplerate_string(samplerate);
-	fprintf(meta, "samplerate = %s\n", s);
-	g_free(s);
-
-	for (i = 0; channels[i]; i++)
-		fprintf(meta, "probe%d = %s\n", i + 1, channels[i]);
-
-	fclose(meta);
-
-	if (!(metasrc = zip_source_file(zipfile, metafile, 0, -1))) {
-		unlink(metafile);
-		return SR_ERR;
-	}
-	if (zip_add(zipfile, "metadata", metasrc) == -1) {
-		unlink(metafile);
-		return SR_ERR;
-	}
-
-	if ((ret = zip_close(zipfile)) == -1) {
-		sr_info("error saving zipfile: %s", zip_strerror(zipfile));
-		unlink(metafile);
-		return SR_ERR;
-	}
-
-	unlink(metafile);
-
-	return SR_OK;
-}
-
-/**
- * Append data to an existing session file.
- *
- * The session file must have been created with sr_session_save_init()
- * or sr_session_save() beforehand.
- *
- * @param filename The name of the filename to append to. Must not be NULL.
- * @param buf The data to be appended.
- * @param unitsize The number of bytes per sample.
- * @param units The number of samples.
- *
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid arguments
- * @retval SR_ERR Other errors
- *
- * @since 0.3.0
- */
-SR_API int sr_session_append(const char *filename, unsigned char *buf,
-		int unitsize, int units)
-{
-	struct zip *archive;
-	struct zip_source *logicsrc;
-	zip_int64_t num_files;
-	struct zip_file *zf;
-	struct zip_stat zs;
-	struct zip_source *metasrc;
-	GKeyFile *kf;
-	GError *error;
-	gsize len;
-	int chunk_num, next_chunk_num, tmpfile, ret, i;
-	const char *entry_name;
-	char *metafile, tmpname[32], chunkname[16];
-
-	if ((ret = sr_sessionfile_check(filename)) != SR_OK)
-		return ret;
-
-	if (!(archive = zip_open(filename, 0, &ret)))
-		return SR_ERR;
-
-	if (zip_stat(archive, "metadata", 0, &zs) == -1)
-		return SR_ERR;
-
-	metafile = g_malloc(zs.size);
-	zf = zip_fopen_index(archive, zs.index, 0);
-	zip_fread(zf, metafile, zs.size);
-	zip_fclose(zf);
-
-	/*
-	 * If the file was only initialized but doesn't yet have any
-	 * data it in, it won't have a unitsize field in metadata yet.
-	 */
-	error = NULL;
-	kf = g_key_file_new();
-	if (!g_key_file_load_from_data(kf, metafile, zs.size, 0, &error)) {
-		sr_err("Failed to parse metadata: %s.", error->message);
-		return SR_ERR;
-	}
-	g_free(metafile);
-	tmpname[0] = '\0';
-	if (!g_key_file_has_key(kf, "device 1", "unitsize", &error)) {
-		if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
-			sr_err("Failed to check unitsize key: %s", error ? error->message : "?");
-			return SR_ERR;
-		}
-		/* Add unitsize field. */
-		g_key_file_set_integer(kf, "device 1", "unitsize", unitsize);
-		metafile = g_key_file_to_data(kf, &len, &error);
-		strcpy(tmpname, "sigrok-meta-XXXXXX");
-		if ((tmpfile = g_mkstemp(tmpname)) == -1)
-			return SR_ERR;
-		if (write(tmpfile, metafile, len) < 0) {
-			sr_dbg("Failed to create new metadata: %s", strerror(errno));
-			g_free(metafile);
-			unlink(tmpname);
-			return SR_ERR;
-		}
-		close(tmpfile);
-		if (!(metasrc = zip_source_file(archive, tmpname, 0, -1))) {
-			sr_err("Failed to create zip source for metadata.");
-			g_free(metafile);
-			unlink(tmpname);
-			return SR_ERR;
-		}
-		if (zip_replace(archive, zs.index, metasrc) == -1) {
-			sr_err("Failed to replace metadata file.");
-			g_free(metafile);
-			unlink(tmpname);
-			return SR_ERR;
-		}
-		g_free(metafile);
-	}
-	g_key_file_free(kf);
-
-	next_chunk_num = 1;
-	num_files = zip_get_num_entries(archive, 0);
-	for (i = 0; i < num_files; i++) {
-		entry_name = zip_get_name(archive, i, 0);
-		if (strncmp(entry_name, "logic-1", 7))
-			continue;
-		if (strlen(entry_name) == 7) {
-			/* This file has no extra chunks, just a single "logic-1".
-			 * Rename it to "logic-1-1" * and continue with chunk 2. */
-			if (zip_rename(archive, i, "logic-1-1") == -1) {
-				sr_err("Failed to rename 'logic-1' to 'logic-1-1'.");
-				unlink(tmpname);
-				return SR_ERR;
-			}
-			next_chunk_num = 2;
-			break;
-		} else if (strlen(entry_name) > 8 && entry_name[7] == '-') {
-			chunk_num = strtoull(entry_name + 8, NULL, 10);
-			if (chunk_num >= next_chunk_num)
-				next_chunk_num = chunk_num + 1;
-		}
-	}
-	snprintf(chunkname, 15, "logic-1-%d", next_chunk_num);
-	if (!(logicsrc = zip_source_buffer(archive, buf, units * unitsize, FALSE))) {
-		unlink(tmpname);
-		return SR_ERR;
-	}
-	if (zip_add(archive, chunkname, logicsrc) == -1) {
-		unlink(tmpname);
-		return SR_ERR;
-	}
-	if ((ret = zip_close(archive)) == -1) {
-		sr_info("error saving session file: %s", zip_strerror(archive));
-		unlink(tmpname);
-		return SR_ERR;
-	}
-	unlink(tmpname);
-
-	return SR_OK;
-}
-
-/** @} */
diff --git a/src/analog.c b/src/analog.c
new file mode 100644
index 0000000..892cc6e
--- /dev/null
+++ b/src/analog.c
@@ -0,0 +1,356 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "analog"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Handling and converting analog data.
+ */
+
+/**
+ * @defgroup grp_analog Analog data handling
+ *
+ * Handling and converting analog data.
+ *
+ * @{
+ */
+
+struct unit_mq_string {
+	uint64_t value;
+	const char *str;
+};
+
+/* Please use the same order as in enum sr_unit (libsigrok.h). */
+static struct unit_mq_string unit_strings[] = {
+	{ SR_UNIT_VOLT, "V" },
+	{ SR_UNIT_AMPERE, "A" },
+	{ SR_UNIT_OHM, "\xe2\x84\xa6" },
+	{ SR_UNIT_FARAD, "F" },
+	{ SR_UNIT_KELVIN, "K" },
+	{ SR_UNIT_CELSIUS, "\xc2\xb0""C" },
+	{ SR_UNIT_FAHRENHEIT, "\xc2\xb0""F" },
+	{ SR_UNIT_HERTZ, "Hz" },
+	{ SR_UNIT_PERCENTAGE, "%" },
+	{ SR_UNIT_BOOLEAN, "" },
+	{ SR_UNIT_SECOND, "s" },
+	{ SR_UNIT_SIEMENS, "S" },
+	{ SR_UNIT_DECIBEL_MW, "dBu" },
+	{ SR_UNIT_DECIBEL_VOLT, "dBv" },
+	{ SR_UNIT_UNITLESS, "" },
+	{ SR_UNIT_DECIBEL_SPL, "dB" },
+	{ SR_UNIT_CONCENTRATION, "ppm" },
+	{ SR_UNIT_REVOLUTIONS_PER_MINUTE, "RPM" },
+	{ SR_UNIT_VOLT_AMPERE, "VA" },
+	{ SR_UNIT_WATT, "W" },
+	{ SR_UNIT_WATT_HOUR, "Wh" },
+	{ SR_UNIT_METER_SECOND, "m/s" },
+	{ SR_UNIT_HECTOPASCAL, "hPa" },
+	{ SR_UNIT_HUMIDITY_293K, "%rF" },
+	{ SR_UNIT_DEGREE, "\xc2\xb0" },
+	{ SR_UNIT_HENRY, "H" },
+	{ SR_UNIT_GRAM, "g" },
+	{ SR_UNIT_CARAT, "ct" },
+	{ SR_UNIT_OUNCE, "oz" },
+	{ SR_UNIT_TROY_OUNCE, "oz t" },
+	{ SR_UNIT_POUND, "lb" },
+	{ SR_UNIT_PENNYWEIGHT, "dwt" },
+	{ SR_UNIT_GRAIN, "gr" },
+	{ SR_UNIT_TAEL, "tael" },
+	{ SR_UNIT_MOMME, "momme" },
+	{ SR_UNIT_TOLA, "tola" },
+	{ SR_UNIT_PIECE, "pcs" },
+	ALL_ZERO
+};
+
+/* Please use the same order as in enum sr_mqflag (libsigrok.h). */
+static struct unit_mq_string mq_strings[] = {
+	{ SR_MQFLAG_AC, " AC" },
+	{ SR_MQFLAG_DC, " DC" },
+	{ SR_MQFLAG_RMS, " RMS" },
+	{ SR_MQFLAG_DIODE, " DIODE" },
+	{ SR_MQFLAG_HOLD, " HOLD" },
+	{ SR_MQFLAG_MAX, " MAX" },
+	{ SR_MQFLAG_MIN, " MIN" },
+	{ SR_MQFLAG_AUTORANGE, " AUTO" },
+	{ SR_MQFLAG_RELATIVE, " REL" },
+	{ SR_MQFLAG_SPL_FREQ_WEIGHT_A, "(A)" },
+	{ SR_MQFLAG_SPL_FREQ_WEIGHT_C, "(C)" },
+	{ SR_MQFLAG_SPL_FREQ_WEIGHT_Z, "(Z)" },
+	{ SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT, "(SPL)" },
+	{ SR_MQFLAG_SPL_TIME_WEIGHT_S, " S" },
+	{ SR_MQFLAG_SPL_TIME_WEIGHT_F, " F" },
+	{ SR_MQFLAG_SPL_LAT, " LAT" },
+	/* Not a standard function for SLMs, so this is a made-up notation. */
+	{ SR_MQFLAG_SPL_PCT_OVER_ALARM, "%oA" },
+	{ SR_MQFLAG_DURATION, " DURATION" },
+	{ SR_MQFLAG_AVG, " AVG" },
+	{ SR_MQFLAG_REFERENCE, " REF" },
+	{ SR_MQFLAG_UNSTABLE, " UNSTABLE" },
+	ALL_ZERO
+};
+
+SR_PRIV int sr_analog_init(struct sr_datafeed_analog *analog,
+		struct sr_analog_encoding *encoding,
+		struct sr_analog_meaning *meaning,
+		struct sr_analog_spec *spec,
+		int digits)
+{
+	memset(analog, 0, sizeof(*analog));
+	memset(encoding, 0, sizeof(*encoding));
+	memset(meaning, 0, sizeof(*meaning));
+	memset(spec, 0, sizeof(*spec));
+
+	analog->encoding = encoding;
+	analog->meaning = meaning;
+	analog->spec = spec;
+
+	encoding->unitsize = sizeof(float);
+	encoding->is_float = TRUE;
+#ifdef WORDS_BIGENDIAN
+	encoding->is_bigendian = TRUE;
+#else
+	encoding->is_bigendian = FALSE;
+#endif
+	encoding->digits = digits;
+	encoding->is_digits_decimal = TRUE;
+	encoding->scale.p = 1;
+	encoding->scale.q = 1;
+	encoding->offset.p = 0;
+	encoding->offset.q = 1;
+
+	spec->spec_digits = digits;
+
+	return SR_OK;
+}
+
+/**
+ * Convert an analog datafeed payload to an array of floats.
+ *
+ * @param[in] analog The analog payload to convert. Must not be NULL.
+ *                   analog->data, analog->meaning, and analog->encoding
+ *                   must not be NULL.
+ * @param[out] outbuf Memory where to store the result. Must not be NULL.
+ *
+ * Sufficient memory for outbuf must have been pre-allocated by the caller,
+ * who is also responsible for freeing it when no longer needed.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Unsupported encoding.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
+		float *outbuf)
+{
+	float offset;
+	unsigned int b, i, count;
+	gboolean bigendian;
+
+	if (!analog || !(analog->data) || !(analog->meaning)
+			|| !(analog->encoding) || !outbuf)
+		return SR_ERR_ARG;
+
+	count = analog->num_samples * g_slist_length(analog->meaning->channels);
+
+#ifdef WORDS_BIGENDIAN
+	bigendian = TRUE;
+#else
+	bigendian = FALSE;
+#endif
+	if (!analog->encoding->is_float) {
+		float offset = analog->encoding->offset.p / (float)analog->encoding->offset.q;
+		float scale = analog->encoding->scale.p / (float)analog->encoding->scale.q;
+		gboolean is_signed = analog->encoding->is_signed;
+		gboolean is_bigendian = analog->encoding->is_bigendian;
+		int8_t *data8 = (int8_t *)(analog->data);
+		int16_t *data16 = (int16_t *)(analog->data);
+		int32_t *data32 = (int32_t *)(analog->data);
+
+		switch (analog->encoding->unitsize) {
+		case 1:
+			if (is_signed) {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * data8[i];
+					outbuf[i] += offset;
+				}
+			} else {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * R8(data8 + i);
+					outbuf[i] += offset;
+				}
+			}
+			break;
+		case 2:
+			if (is_signed && is_bigendian) {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * RB16S(&data16[i]);
+					outbuf[i] += offset;
+				}
+			} else if (is_bigendian) {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * RB16(&data16[i]);
+					outbuf[i] += offset;
+				}
+			} else if (is_signed) {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * RL16S(&data16[i]);
+					outbuf[i] += offset;
+				}
+			} else {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * RL16(&data16[i]);
+					outbuf[i] += offset;
+				}
+			}
+			break;
+		case 4:
+			if (is_signed && is_bigendian) {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * RB32S(&data32[i]);
+					outbuf[i] += offset;
+				}
+			} else if (is_bigendian) {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * RB32(&data32[i]);
+					outbuf[i] += offset;
+				}
+			} else if (is_signed) {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * RL32S(&data32[i]);
+					outbuf[i] += offset;
+				}
+			} else {
+				for (unsigned int i = 0; i < count; i++) {
+					outbuf[i] = scale * RL32(&data32[i]);
+					outbuf[i] += offset;
+				}
+			}
+			break;
+		default:
+			sr_err("Unsupported unit size '%d' for analog-to-float conversion.",
+				analog->encoding->unitsize);
+			return SR_ERR;
+		}
+		return SR_OK;
+	}
+
+	if (analog->encoding->unitsize == sizeof(float)
+			&& analog->encoding->is_bigendian == bigendian
+			&& analog->encoding->scale.p == 1
+			&& analog->encoding->scale.q == 1
+			&& analog->encoding->offset.p / (float)analog->encoding->offset.q == 0) {
+		/* The data is already in the right format. */
+		memcpy(outbuf, analog->data, count * sizeof(float));
+	} else {
+		for (i = 0; i < count; i += analog->encoding->unitsize) {
+			for (b = 0; b < analog->encoding->unitsize; b++) {
+				if (analog->encoding->is_bigendian == bigendian)
+					((uint8_t *)outbuf)[i + b] =
+						((uint8_t *)analog->data)[i * analog->encoding->unitsize + b];
+				else
+					((uint8_t *)outbuf)[i + (analog->encoding->unitsize - b)] =
+						((uint8_t *)analog->data)[i * analog->encoding->unitsize + b];
+			}
+			if (analog->encoding->scale.p != 1
+					|| analog->encoding->scale.q != 1)
+				outbuf[i] = (outbuf[i] * analog->encoding->scale.p) / analog->encoding->scale.q;
+			offset = ((float)analog->encoding->offset.p / (float)analog->encoding->offset.q);
+			outbuf[i] += offset;
+		}
+	}
+
+	return SR_OK;
+}
+
+/**
+ * Convert the unit/MQ/MQ flags in the analog struct to a string.
+ *
+ * @param[in] analog Struct containing the unit, MQ and MQ flags.
+ *                   Must not be NULL. analog->meaning must not be NULL.
+ * @param[out] result Pointer to store result. Must not be NULL.
+ *
+ * The string is allocated by the function and must be freed by the caller
+ * after use by calling g_free().
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_analog_unit_to_string(const struct sr_datafeed_analog *analog,
+		char **result)
+{
+	int i;
+	GString *buf;
+
+	if (!analog || !(analog->meaning) || !result)
+		return SR_ERR_ARG;
+
+	buf = g_string_new(NULL);
+
+	for (i = 0; unit_strings[i].value; i++) {
+		if (analog->meaning->unit == unit_strings[i].value) {
+			g_string_assign(buf, unit_strings[i].str);
+			break;
+		}
+	}
+
+	/* More than one MQ flag may apply. */
+	for (i = 0; mq_strings[i].value; i++)
+		if (analog->meaning->mqflags & mq_strings[i].value)
+			g_string_append(buf, mq_strings[i].str);
+
+	*result = buf->str;
+	g_string_free(buf, FALSE);
+
+	return SR_OK;
+}
+
+/**
+ * Set sr_rational r to the given value.
+ *
+ * @param[out] r Rational number struct to set. Must not be NULL.
+ * @param[in] p Numerator.
+ * @param[in] q Denominator.
+ *
+ * @since 0.4.0
+ */
+SR_API void sr_rational_set(struct sr_rational *r, int64_t p, uint64_t q)
+{
+	if (!r)
+		return;
+
+	r->p = p;
+	r->q = q;
+}
+
+/** @} */
diff --git a/backend.c b/src/backend.c
similarity index 60%
rename from backend.c
rename to src/backend.c
index 2f51eed..aa51a92 100644
--- a/backend.c
+++ b/src/backend.c
@@ -18,17 +18,18 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <glib.h>
-#include "config.h" /* Needed for HAVE_LIBUSB_1_0 and others. */
-#include "libsigrok.h"
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 /** @cond PRIVATE */
 #define LOG_PREFIX "backend"
 /** @endcond */
 
-extern struct sr_session *session;
-
 /**
  * @mainpage libsigrok API
  *
@@ -57,7 +58,7 @@ extern struct sr_session *session;
  *
  * @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>.
+ * There is one mailing list for sigrok/libsigrok: <a href="https://lists.sourceforge.net/lists/listinfo/sigrok-devel">sigrok-devel</a>.
  *
  * @section sec_irc IRC
  *
@@ -81,9 +82,10 @@ extern struct sr_session *session;
  *
  * 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.
+ * Before using any of the libsigrok functionality (except for
+ * sr_log_loglevel_set()), 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.
@@ -120,21 +122,108 @@ extern struct sr_session *session;
  * @{
  */
 
+static void print_versions(void)
+{
+	GString *s;
+#if defined(HAVE_LIBUSB_1_0) && !defined(__FreeBSD__)
+	const struct libusb_version *lv;
+#endif
+
+	s = g_string_sized_new(200);
+
+	sr_dbg("libsigrok %s/%s (rt: %s/%s).",
+		SR_PACKAGE_VERSION_STRING, SR_LIB_VERSION_STRING,
+		sr_package_version_string_get(), sr_lib_version_string_get());
+
+	g_string_append(s, "Libs: ");
+	g_string_append_printf(s, "glib %d.%d.%d (rt: %d.%d.%d/%d:%d), ",
+		GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION,
+		glib_major_version, glib_minor_version, glib_micro_version,
+		glib_binary_age, glib_interface_age);
+	g_string_append_printf(s, "libzip %s, ", CONF_LIBZIP_VERSION);
+#ifdef HAVE_LIBSERIALPORT
+	g_string_append_printf(s, "libserialport %s/%s (rt: %s/%s), ",
+		SP_PACKAGE_VERSION_STRING, SP_LIB_VERSION_STRING,
+		sp_get_package_version_string(), sp_get_lib_version_string());
+#endif
+#ifdef HAVE_LIBUSB_1_0
+#ifdef __FreeBSD__
+	g_string_append_printf(s, "libusb-1.0 %s, ", CONF_LIBUSB_1_0_VERSION);
+#else
+	lv = libusb_get_version();
+	g_string_append_printf(s, "libusb-1.0 %d.%d.%d.%d%s, ",
+		lv->major, lv->minor, lv->micro, lv->nano, lv->rc);
+#endif
+#endif
+#ifdef HAVE_LIBFTDI
+	g_string_append_printf(s, "libftdi %s, ", CONF_LIBFTDI_VERSION);
+#endif
+#ifdef HAVE_LIBGPIB
+	g_string_append_printf(s, "libgpib %s, ", CONF_LIBGPIB_VERSION);
+#endif
+#ifdef HAVE_LIBREVISA
+	g_string_append_printf(s, "librevisa %s, ", CONF_LIBREVISA_VERSION);
+#endif
+	s->str[s->len - 2] = '.';
+	s->str[s->len - 1] = '\0';
+	sr_dbg("%s", s->str);
+
+	s = g_string_truncate(s, 0);
+	g_string_append_printf(s, "Host: %s, ", CONF_HOST);
+#ifdef WORDS_BIGENDIAN
+	g_string_append_printf(s, "big-endian.");
+#else
+	g_string_append_printf(s, "little-endian.");
+#endif
+	sr_dbg("%s", s->str);
+
+	s = g_string_truncate(s, 0);
+	g_string_append_printf(s, "SCPI backends: ");
+
+	g_string_append_printf(s, "TCP, ");
+#if HAVE_RPC
+	g_string_append_printf(s, "RPC, ");
+#endif
+#ifdef HAVE_LIBSERIALPORT
+	g_string_append_printf(s, "serial, ");
+#endif
+#ifdef HAVE_LIBREVISA
+	g_string_append_printf(s, "VISA, ");
+#endif
+#ifdef HAVE_LIBGPIB
+	g_string_append_printf(s, "GPIB, ");
+#endif
+#ifdef HAVE_LIBUSB_1_0
+	g_string_append_printf(s, "USBTMC, ");
+#endif
+	s->str[s->len - 2] = '.';
+	s->str[s->len - 1] = '\0';
+	sr_dbg("%s", s->str);
+
+	g_string_free(s, TRUE);
+}
+
 /**
  * Sanity-check all libsigrok drivers.
  *
+ * @param[in] ctx Pointer to a libsigrok context struct. Must not be NULL.
+ *
  * @retval SR_OK All drivers are OK
  * @retval SR_ERR One or more drivers have issues.
+ * @retval SR_ERR_ARG Invalid argument.
  */
-static int sanity_check_all_drivers(void)
+static int sanity_check_all_drivers(const struct sr_context *ctx)
 {
 	int i, errors, ret = SR_OK;
 	struct sr_dev_driver **drivers;
 	const char *d;
 
+	if (!ctx)
+		return SR_ERR_ARG;
+
 	sr_spew("Sanity-checking all drivers.");
 
-	drivers = sr_driver_list();
+	drivers = sr_driver_list(ctx);
 	for (i = 0; drivers[i]; i++) {
 		errors = 0;
 
@@ -216,7 +305,7 @@ static int sanity_check_all_drivers(void)
 static int sanity_check_all_input_modules(void)
 {
 	int i, errors, ret = SR_OK;
-	struct sr_input_format **inputs;
+	const struct sr_input_module **inputs;
 	const char *d;
 
 	sr_spew("Sanity-checking all input modules.");
@@ -231,20 +320,24 @@ static int sanity_check_all_input_modules(void)
 			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);
+		if (!inputs[i]->name) {
+			sr_err("No name in module %d ('%s').", i, d);
 			errors++;
 		}
-		if (!inputs[i]->format_match) {
-			sr_err("No format_match in module %d ('%s').", i, d);
+		if (!inputs[i]->desc) {
+			sr_err("No description 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);
+		if (!inputs[i]->receive) {
+			sr_err("No receive in module %d ('%s').", i, d);
+			errors++;
+		}
+		if (!inputs[i]->end) {
+			sr_err("No end in module %d ('%s').", i, d);
 			errors++;
 		}
 
@@ -266,7 +359,7 @@ static int sanity_check_all_input_modules(void)
 static int sanity_check_all_output_modules(void)
 {
 	int i, errors, ret = SR_OK;
-	struct sr_output_format **outputs;
+	const struct sr_output_module **outputs;
 	const char *d;
 
 	sr_spew("Sanity-checking all output modules.");
@@ -281,7 +374,11 @@ static int sanity_check_all_output_modules(void)
 			sr_err("No ID in module %d ('%s').", i, d);
 			errors++;
 		}
-		if (!outputs[i]->description) {
+		if (!outputs[i]->name) {
+			sr_err("No name in module %d ('%s').", i, d);
+			errors++;
+		}
+		if (!outputs[i]->desc) {
 			sr_err("No description in module '%s'.", d);
 			errors++;
 		}
@@ -300,6 +397,55 @@ static int sanity_check_all_output_modules(void)
 }
 
 /**
+ * Sanity-check all libsigrok transform modules.
+ *
+ * @retval SR_OK All modules are OK
+ * @retval SR_ERR One or more modules have issues.
+ */
+static int sanity_check_all_transform_modules(void)
+{
+	int i, errors, ret = SR_OK;
+	const struct sr_transform_module **transforms;
+	const char *d;
+
+	sr_spew("Sanity-checking all transform modules.");
+
+	transforms = sr_transform_list();
+	for (i = 0; transforms[i]; i++) {
+		errors = 0;
+
+		d = (transforms[i]->id) ? transforms[i]->id : "NULL";
+
+		if (!transforms[i]->id) {
+			sr_err("No ID in module %d ('%s').", i, d);
+			errors++;
+		}
+		if (!transforms[i]->name) {
+			sr_err("No name in module %d ('%s').", i, d);
+			errors++;
+		}
+		if (!transforms[i]->desc) {
+			sr_err("No description in module '%s'.", d);
+			errors++;
+		}
+		/* Note: options() is optional. */
+		/* Note: init() is optional. */
+		if (!transforms[i]->receive) {
+			sr_err("No receive in module '%s'.", d);
+			errors++;
+		}
+		/* Note: cleanup() is optional. */
+
+		if (errors == 0)
+			continue;
+
+		ret = SR_ERR;
+	}
+
+	return ret;
+}
+
+/**
  * Initialize libsigrok.
  *
  * This function must be called before any other libsigrok function.
@@ -319,13 +465,30 @@ SR_API int sr_init(struct sr_context **ctx)
 {
 	int ret = SR_ERR;
 	struct sr_context *context;
+	struct sr_dev_driver ***lists, **drivers;
+	GArray *array;
+#ifdef _WIN32
+	WSADATA wsadata;
+#endif
+
+	print_versions();
 
 	if (!ctx) {
 		sr_err("%s(): libsigrok context was NULL.", __func__);
 		return SR_ERR;
 	}
 
-	if (sanity_check_all_drivers() < 0) {
+	context = g_malloc0(sizeof(struct sr_context));
+
+	/* Generate ctx->driver_list at runtime. */
+	array = g_array_new(TRUE, FALSE, sizeof(struct sr_dev_driver *));
+	for (lists = drivers_lists; *lists; lists++)
+		for (drivers = *lists; *drivers; drivers++)
+			g_array_append_val(array, *drivers);
+	context->driver_list = (struct sr_dev_driver **)array->data;
+	g_array_free(array, FALSE);
+
+	if (sanity_check_all_drivers(context) < 0) {
 		sr_err("Internal driver error(s), aborting.");
 		return ret;
 	}
@@ -340,13 +503,18 @@ SR_API int sr_init(struct sr_context **ctx)
 		return ret;
 	}
 
-	/* + 1 to handle when struct sr_context has no members. */
-	context = g_try_malloc0(sizeof(struct sr_context) + 1);
+	if (sanity_check_all_transform_modules() < 0) {
+		sr_err("Internal transform module error(s), aborting.");
+		return ret;
+	}
 
-	if (!context) {
-		ret = SR_ERR_MALLOC;
+#ifdef _WIN32
+	if ((ret = WSAStartup(MAKEWORD(2, 2), &wsadata)) != 0) {
+		sr_err("WSAStartup failed with error code %d.", ret);
+		ret = SR_ERR;
 		goto done;
 	}
+#endif
 
 #ifdef HAVE_LIBUSB_1_0
 	ret = libusb_init(&context->libusb_ctx);
@@ -356,15 +524,16 @@ SR_API int sr_init(struct sr_context **ctx)
 		goto done;
 	}
 #endif
+	sr_resource_set_hooks(context, NULL, NULL, NULL, NULL);
 
 	*ctx = context;
 	context = NULL;
-	session = NULL;
 	ret = SR_OK;
 
+#if defined(HAVE_LIBUSB_1_0) || defined(_WIN32)
 done:
-	if (context)
-		g_free(context);
+#endif
+	g_free(context);
 	return ret;
 }
 
@@ -385,12 +554,17 @@ SR_API int sr_exit(struct sr_context *ctx)
 		return SR_ERR;
 	}
 
-	sr_hw_cleanup_all();
+	sr_hw_cleanup_all(ctx);
+
+#ifdef _WIN32
+	WSACleanup();
+#endif
 
 #ifdef HAVE_LIBUSB_1_0
 	libusb_exit(ctx->libusb_ctx);
 #endif
 
+	g_free(sr_driver_list(ctx));
 	g_free(ctx);
 
 	return SR_OK;
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..e932ea2
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,722 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "device"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Device handling in libsigrok.
+ */
+
+/**
+ * @defgroup grp_devices Devices
+ *
+ * Device handling in libsigrok.
+ *
+ * @{
+ */
+
+/** @private
+ *  Allocate and initialize new struct sr_channel and add to sdi.
+ *  @param[in]  sdi The device instance the channel is connected to.
+ *  @param[in]  index @copydoc sr_channel::index
+ *  @param[in]  type @copydoc sr_channel::type
+ *  @param[in]  enabled @copydoc sr_channel::enabled
+ *  @param[in]  name @copydoc sr_channel::name
+ *
+ *  @return A new struct sr_channel*.
+ */
+SR_PRIV struct sr_channel *sr_channel_new(struct sr_dev_inst *sdi,
+		int index, int type, gboolean enabled, const char *name)
+{
+	struct sr_channel *ch;
+
+	ch = g_malloc0(sizeof(struct sr_channel));
+	ch->sdi = sdi;
+	ch->index = index;
+	ch->type = type;
+	ch->enabled = enabled;
+	if (name)
+		ch->name = g_strdup(name);
+
+	sdi->channels = g_slist_append(sdi->channels, ch);
+
+	return ch;
+}
+
+/**
+ * Set the name of the specified channel.
+ *
+ * If the channel already has a different name assigned to it, it will be
+ * removed, and the new name will be saved instead.
+ *
+ * @param[in] channel The channel whose name to set.
+ * @param[in] name    The new name that the specified channel should get. A
+ *                    copy of the string is made.
+ *
+ * @return SR_OK on success, or SR_ERR_ARG on invalid arguments.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_dev_channel_name_set(struct sr_channel *channel,
+		const char *name)
+{
+	if (!channel) {
+		sr_err("%s: channel was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	g_free(channel->name);
+	channel->name = g_strdup(name);
+	return SR_OK;
+}
+
+/**
+ * Enable or disable a channel.
+ *
+ * @param[in] channel The channel to enable or disable.
+ * @param[in] state   TRUE to enable the channel, FALSE to disable.
+ *
+ * @return SR_OK on success or SR_ERR on failure.  In case of invalid
+ *         arguments, SR_ERR_ARG is returned and the channel enabled state
+ *         remains unchanged.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_dev_channel_enable(struct sr_channel *channel,
+		gboolean state)
+{
+	int ret;
+	gboolean was_enabled;
+	struct sr_dev_inst *sdi;
+
+	if (!channel)
+		return SR_ERR_ARG;
+
+	sdi = channel->sdi;
+	was_enabled = channel->enabled;
+	channel->enabled = state;
+	if (!state != !was_enabled && sdi->driver
+			&& sdi->driver->config_channel_set) {
+		ret = sdi->driver->config_channel_set(
+			sdi, channel, SR_CHANNEL_SET_ENABLED);
+		/* Roll back change if it wasn't applicable. */
+		if (ret != SR_OK)
+			return ret;
+	}
+
+	return SR_OK;
+}
+
+/* Returns the next enabled channel, wrapping around if necessary. */
+SR_PRIV struct sr_channel *sr_next_enabled_channel(const struct sr_dev_inst *sdi,
+		struct sr_channel *cur_channel)
+{
+	struct sr_channel *next_channel;
+	GSList *l;
+
+	next_channel = cur_channel;
+	do {
+		l = g_slist_find(sdi->channels, next_channel);
+		if (l && l->next)
+			next_channel = l->next->data;
+		else
+			next_channel = sdi->channels->data;
+	} while (!next_channel->enabled);
+
+	return next_channel;
+}
+
+/**
+ * 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[in] key The option that should be checked for is supported by the
+ *            specified device.
+ *
+ * @retval TRUE Device has the specified option
+ * @retval FALSE Device does not have the specified option, invalid input
+ *         parameters or other error conditions.
+ *
+ * @since 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, sdi, 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] & SR_CONF_MASK) == key) {
+			ret = TRUE;
+			break;
+		}
+	}
+	g_variant_unref(gvar);
+
+	return ret;
+}
+
+/**
+ * Enumerate the configuration options of the specified item.
+ *
+ * @param driver Pointer to the driver to be checked. Must not be NULL.
+ * @param sdi Pointer to the device instance to be checked. May be NULL to
+ *            check driver options.
+ * @param cg  Pointer to a channel group, if a specific channel group is to
+ *            be checked. Must be NULL to check device-wide options.
+ * @return A GArray * of enum sr_configkey values, or NULL on invalid
+ *         arguments. The array must be freed by the caller using
+ *         g_array_free().
+ *
+ * @since 0.4.0
+ */
+SR_API GArray *sr_dev_options(
+		const struct sr_dev_driver *driver, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	GVariant *gvar;
+	const uint32_t *opts;
+	uint32_t opt;
+	gsize num_opts, i;
+	GArray *result;
+
+	if (!driver || !driver->config_list)
+		return NULL;
+
+	if (sdi && sdi->driver != driver)
+		return NULL;
+
+	if (driver->config_list(SR_CONF_DEVICE_OPTIONS, &gvar, sdi, cg) != SR_OK)
+		return NULL;
+
+	opts = g_variant_get_fixed_array(gvar, &num_opts, sizeof(uint32_t));
+
+	result = g_array_sized_new(FALSE, FALSE, sizeof(uint32_t), num_opts);
+
+	for (i = 0; i < num_opts; i++) {
+		opt = opts[i] & SR_CONF_MASK;
+		g_array_insert_val(result, i, opt);
+	}
+
+	g_variant_unref(gvar);
+
+	return result;
+}
+
+/**
+ * Enumerate the configuration capabilities supported by a device instance
+ * for a given configuration key.
+ *
+ * @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 cg  Pointer to a channel group, if a specific channel group is to
+ *            be checked. Must be NULL to check device-wide options.
+ * @param[in] key The option that should be checked for is supported by the
+ *            specified device.
+ *
+ * @retval A bitmask of enum sr_configcap values, which will be zero for
+ *         invalid inputs or if the key is unsupported.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_dev_config_capabilities_list(const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg, const int key)
+{
+	GVariant *gvar;
+	const int *devopts;
+	gsize num_opts, i;
+	int ret;
+
+	if (!sdi || !sdi->driver || !sdi->driver->config_list)
+		return 0;
+
+	if (sdi->driver->config_list(SR_CONF_DEVICE_OPTIONS,
+				&gvar, sdi, cg) != SR_OK)
+		return 0;
+
+	ret = 0;
+	devopts = g_variant_get_fixed_array(gvar, &num_opts, sizeof(int32_t));
+	for (i = 0; i < num_opts; i++) {
+		if ((devopts[i] & SR_CONF_MASK) == key) {
+			ret = devopts[i] & ~SR_CONF_MASK;
+			break;
+		}
+	}
+	g_variant_unref(gvar);
+
+	return ret;
+}
+
+/**
+ * Allocate and init a new user-generated device instance.
+ *
+ * @param vendor Device vendor
+ * @param model Device model
+ * @param version Device version
+ *
+ * @retval struct sr_dev_inst *. Dynamically allocated, free using
+ *         sr_dev_inst_free().
+ */
+SR_API struct sr_dev_inst *sr_dev_inst_user_new(const char *vendor,
+		const char *model, const char *version)
+{
+	struct sr_dev_inst *sdi;
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+
+	sdi->vendor = g_strdup(vendor);
+	sdi->model = g_strdup(model);
+	sdi->version = g_strdup(version);
+	sdi->inst_type = SR_INST_USER;
+
+	return sdi;
+}
+
+/**
+ * Add a new channel to the specified device instance.
+ */
+SR_API int sr_dev_inst_channel_add(struct sr_dev_inst *sdi, int index, int type, const char *name)
+{
+	if (!sdi || sdi->inst_type != SR_INST_USER || index < 0)
+		return SR_ERR_ARG;
+
+	sr_channel_new(sdi, index, type, TRUE, name);
+
+	return SR_OK;
+}
+
+/** @private
+ *  Free device instance struct created by sr_dev_inst().
+ *  @param sdi device instance to free.
+ */
+SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi)
+{
+	struct sr_channel *ch;
+	struct sr_channel_group *cg;
+	GSList *l;
+
+	for (l = sdi->channels; l; l = l->next) {
+		ch = l->data;
+		g_free(ch->name);
+		g_free(ch->priv);
+		g_free(ch);
+	}
+	g_slist_free(sdi->channels);
+
+	for (l = sdi->channel_groups; l; l = l->next) {
+		cg = l->data;
+		g_free(cg->name);
+		g_slist_free(cg->channels);
+		g_free(cg->priv);
+		g_free(cg);
+	}
+	g_slist_free(sdi->channel_groups);
+
+	if (sdi->session)
+		sr_session_dev_remove(sdi->session, sdi);
+
+	g_free(sdi->vendor);
+	g_free(sdi->model);
+	g_free(sdi->version);
+	g_free(sdi->serial_num);
+	g_free(sdi->connection_id);
+	g_free(sdi);
+}
+
+#ifdef HAVE_LIBUSB_1_0
+
+/** @private
+ *  Allocate and init struct for USB device instance.
+ *  @param[in]  bus @copydoc sr_usb_dev_inst::bus
+ *  @param[in]  address @copydoc sr_usb_dev_inst::address
+ *  @param[in]  hdl @copydoc sr_usb_dev_inst::devhdl
+ *
+ *  @retval other struct sr_usb_dev_inst * for USB device instance.
+ */
+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;
+
+	udi = g_malloc0(sizeof(struct sr_usb_dev_inst));
+	udi->bus = bus;
+	udi->address = address;
+	udi->devhdl = hdl;
+
+	return udi;
+}
+
+/** @private
+ *  Free struct * allocated by sr_usb_dev_inst().
+ *  @param usb  struct* to free. Must not be NULL.
+ */
+SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb)
+{
+	g_free(usb);
+}
+
+#endif
+
+#ifdef HAVE_LIBSERIALPORT
+
+/**
+ * @private
+ *
+ * Both parameters are copied to newly allocated strings, and freed
+ * automatically by sr_serial_dev_inst_free().
+ *
+ * @param[in] port OS-specific serial port specification. Examples:
+ *                 "/dev/ttyUSB0", "/dev/ttyACM1", "/dev/tty.Modem-0", "COM1".
+ *                 Must not be NULL.
+ * @param[in] 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. Can be NULL.
+ *
+ * @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;
+
+	serial = g_malloc0(sizeof(struct sr_serial_dev_inst));
+	serial->port = g_strdup(port);
+	if (serialcomm)
+		serial->serialcomm = g_strdup(serialcomm);
+
+	return serial;
+}
+
+/** @private
+ *  Free struct sr_serial_dev_inst * allocated by sr_serial_dev_inst().
+ *  @param serial   struct sr_serial_dev_inst * to free. Must not be NULL.
+ */
+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);
+}
+#endif
+
+/** @private */
+SR_PRIV struct sr_usbtmc_dev_inst *sr_usbtmc_dev_inst_new(const char *device)
+{
+	struct sr_usbtmc_dev_inst *usbtmc;
+
+	usbtmc = g_malloc0(sizeof(struct sr_usbtmc_dev_inst));
+	usbtmc->device = g_strdup(device);
+	usbtmc->fd = -1;
+
+	return usbtmc;
+}
+
+/** @private */
+SR_PRIV void sr_usbtmc_dev_inst_free(struct sr_usbtmc_dev_inst *usbtmc)
+{
+	g_free(usbtmc->device);
+	g_free(usbtmc);
+}
+
+/**
+ * Get the list of devices/instances of the specified driver.
+ *
+ * @param driver The driver to use. Must not be NULL.
+ *
+ * @return The list of devices/instances of this driver, or NULL upon errors
+ *         or if the list is empty.
+ *
+ * @since 0.2.0
+ */
+SR_API GSList *sr_dev_list(const struct sr_dev_driver *driver)
+{
+	if (driver && driver->dev_list)
+		return driver->dev_list(driver);
+	else
+		return NULL;
+}
+
+/**
+ * Clear the list of device instances a driver knows about.
+ *
+ * @param driver The driver to use. This must be a pointer to one of
+ *               the entries returned by sr_driver_list(). Must not be NULL.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid driver
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_dev_clear(const struct sr_dev_driver *driver)
+{
+	int ret;
+
+	if (!driver) {
+		sr_err("Invalid driver.");
+		return SR_ERR_ARG;
+	}
+
+	if (driver->dev_clear)
+		ret = driver->dev_clear(driver);
+	else
+		ret = std_dev_clear(driver, NULL);
+
+	return ret;
+}
+
+/**
+ * Open the specified device.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return SR_OK upon success, a negative error code upon errors.
+ *
+ * @since 0.2.0
+ */
+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;
+}
+
+/**
+ * Close the specified device.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return SR_OK upon success, a negative error code upon errors.
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_dev_close(struct sr_dev_inst *sdi)
+{
+	int ret;
+
+	if (!sdi || !sdi->driver || !sdi->driver->dev_close)
+		return SR_ERR;
+
+	ret = sdi->driver->dev_close(sdi);
+
+	return ret;
+}
+
+/**
+ * Queries a device instances' driver.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return The driver instance or NULL on error.
+ */
+SR_API struct sr_dev_driver *sr_dev_inst_driver_get(const struct sr_dev_inst *sdi)
+{
+	if (!sdi || !sdi->driver)
+		return NULL;
+
+	return sdi->driver;
+}
+
+/**
+ * Queries a device instances' vendor.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return The vendor string or NULL.
+ */
+SR_API const char *sr_dev_inst_vendor_get(const struct sr_dev_inst *sdi)
+{
+	if (!sdi)
+		return NULL;
+
+	return sdi->vendor;
+}
+
+/**
+ * Queries a device instances' model.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return The model string or NULL.
+ */
+SR_API const char *sr_dev_inst_model_get(const struct sr_dev_inst *sdi)
+{
+	if (!sdi)
+		return NULL;
+
+	return sdi->model;
+}
+
+/**
+ * Queries a device instances' version.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return The version string or NULL.
+ */
+SR_API const char *sr_dev_inst_version_get(const struct sr_dev_inst *sdi)
+{
+	if (!sdi)
+		return NULL;
+
+	return sdi->version;
+}
+
+/**
+ * Queries a device instances' serial number.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return The serial number string or NULL.
+ */
+SR_API const char *sr_dev_inst_sernum_get(const struct sr_dev_inst *sdi)
+{
+	if (!sdi)
+		return NULL;
+
+	return sdi->serial_num;
+}
+
+/**
+ * Queries a device instances' connection identifier.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return A copy of the connection id string or NULL. The caller is responsible
+ *         for g_free()ing the string when it is no longer needed.
+ */
+SR_API const char *sr_dev_inst_connid_get(const struct sr_dev_inst *sdi)
+{
+#ifdef HAVE_LIBUSB_1_0
+	struct drv_context *drvc;
+	int cnt, i, a, b;
+	char connection_id[64];
+	struct sr_usb_dev_inst *usb;
+	struct libusb_device **devlist;
+#endif
+
+	if (!sdi)
+		return NULL;
+
+#ifdef HAVE_LIBSERIALPORT
+	struct sr_serial_dev_inst *serial;
+
+	if ((!sdi->connection_id) && (sdi->inst_type == SR_INST_SERIAL)) {
+		/* connection_id isn't populated, let's do that here. */
+
+		serial = sdi->conn;
+		((struct sr_dev_inst *)sdi)->connection_id = g_strdup(serial->port);
+	}
+#endif
+
+#ifdef HAVE_LIBUSB_1_0
+	if ((!sdi->connection_id) && (sdi->inst_type == SR_INST_USB)) {
+		/* connection_id isn't populated, let's do that here. */
+
+		drvc = sdi->driver->context;
+		usb = sdi->conn;
+
+		if ((cnt = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist)) < 0) {
+			sr_err("Failed to retrieve device list: %s.",
+			       libusb_error_name(cnt));
+			return NULL;
+		}
+
+		for (i = 0; i < cnt; i++) {
+			/* Find the USB device by the logical address we know. */
+			b = libusb_get_bus_number(devlist[i]);
+			a = libusb_get_device_address(devlist[i]);
+			if (b != usb->bus || a != usb->address)
+				continue;
+
+			usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+			((struct sr_dev_inst *)sdi)->connection_id = g_strdup(connection_id);
+			break;
+		}
+
+		libusb_free_device_list(devlist, 1);
+	}
+#endif
+
+	return sdi->connection_id;
+}
+
+/**
+ * Queries a device instances' channel list.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return The GSList of channels or NULL.
+ */
+SR_API GSList *sr_dev_inst_channels_get(const struct sr_dev_inst *sdi)
+{
+	if (!sdi)
+		return NULL;
+
+	return sdi->channels;
+}
+
+/**
+ * Queries a device instances' channel groups list.
+ *
+ * @param sdi Device instance to use. Must not be NULL.
+ *
+ * @return The GSList of channel groups or NULL.
+ */
+SR_API GSList *sr_dev_inst_channel_groups_get(const struct sr_dev_inst *sdi)
+{
+	if (!sdi)
+		return NULL;
+
+	return sdi->channel_groups;
+}
+
+/** @} */
diff --git a/src/dmm/bm25x.c b/src/dmm/bm25x.c
new file mode 100644
index 0000000..e7aa45a
--- /dev/null
+++ b/src/dmm/bm25x.c
@@ -0,0 +1,215 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Janne Huttunen <jahuttun at 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/>.
+ */
+
+/**
+ * @file
+ *
+ * Brymen BM25x serial protocol parser.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "brymen-bm25x"
+
+#define MAX_DIGITS 4
+
+SR_PRIV gboolean sr_brymen_bm25x_packet_valid(const uint8_t *buf)
+{
+	int i;
+
+	if (buf[0] != 2)
+		return FALSE;
+
+	for (i = 1; i < BRYMEN_BM25X_PACKET_SIZE; i++)
+		if ((buf[i] >> 4) != i)
+			return FALSE;
+
+	return TRUE;
+}
+
+static int decode_digit(int num, const uint8_t *buf)
+{
+	int val;
+
+	val = (buf[3 + 2 * num] & 0xe) | ((buf[4 + 2 * num] << 4) & 0xf0);
+
+	switch (val) {
+	case 0xbe: return 0;
+	case 0xa0: return 1;
+	case 0xda: return 2;
+	case 0xf8: return 3;
+	case 0xe4: return 4;
+	case 0x7c: return 5;
+	case 0x7e: return 6;
+	case 0xa8: return 7;
+	case 0xfe: return 8;
+	case 0xfc: return 9;
+	case 0x00: return ' ';
+	case 0x40: return '-';
+	case 0x16: return 'L';
+	case 0x1e: return 'C';
+	case 0x4e: return 'F';
+	case 0x5e: return 'E';
+	case 0x62: return 'n';
+	case 0x42: return 'r';
+	default:
+		sr_dbg("Unknown digit: 0x%02x.", val);
+		return -1;
+	}
+}
+
+static int decode_point(const uint8_t *buf)
+{
+	int i, p = 0;
+
+	for (i = 1; i < MAX_DIGITS; i++) {
+		if ((buf[11 - 2 * i] & 1) == 0)
+			continue;
+		if (p != 0) {
+			sr_spew("Multiple decimal points found!");
+			return -1;
+		}
+		p = i;
+	}
+
+	return p;
+}
+
+static float scale_value(float val, int point, int digits)
+{
+	int pos;
+
+	pos = point ? point + digits - MAX_DIGITS : 0;
+
+	switch (pos) {
+	case 0: return val;
+	case 1: return val * 1e-1;
+	case 2: return val * 1e-2;
+	case 3: return val * 1e-3;
+	}
+
+	sr_dbg("Invalid decimal point %d (%d digits).", point, digits);
+
+	return NAN;
+}
+
+static float decode_prefix(const uint8_t *buf)
+{
+	if (buf[11] & 2) return 1e+6;
+	if (buf[11] & 1) return 1e+3;
+	if (buf[13] & 1) return 1e-3;
+	if (buf[13] & 2) return 1e-6;
+	if (buf[12] & 1) return 1e-9;
+
+	return 1.0f;
+}
+
+static float decode_value(const uint8_t *buf)
+{
+	float val = 0.0f;
+	int i, digit;
+
+	for (i = 0; i < MAX_DIGITS; i++) {
+		digit = decode_digit(i, buf);
+		if (i == 3 && (digit == 'C' || digit == 'F'))
+			break;
+		if (digit < 0 || digit > 9)
+			goto special;
+		val = 10.0 * val + digit;
+	}
+
+	return scale_value(val, decode_point(buf), i);
+
+special:
+	if (decode_digit(1, buf) == 0 && decode_digit(2, buf) == 'L')
+		return INFINITY;
+
+	return NAN;
+}
+
+SR_PRIV int sr_brymen_bm25x_parse(const uint8_t *buf, float *floatval,
+				struct sr_datafeed_analog_old *analog, void *info)
+{
+	float val;
+
+	(void)info;
+
+	analog->mq = SR_MQ_GAIN;
+	analog->unit = SR_UNIT_UNITLESS;
+	analog->mqflags = 0;
+
+	if (buf[1] & 8)
+		analog->mqflags |= SR_MQFLAG_AUTORANGE;
+	if (buf[1] & 4)
+		analog->mqflags |= SR_MQFLAG_DC;
+	if (buf[1] & 2)
+		analog->mqflags |= SR_MQFLAG_AC;
+	if (buf[1] & 1)
+		analog->mqflags |= SR_MQFLAG_RELATIVE;
+	if (buf[11] & 8)
+		analog->mqflags |= SR_MQFLAG_HOLD;
+	if (buf[13] & 8)
+		analog->mqflags |= SR_MQFLAG_MAX;
+	if (buf[14] & 8)
+		analog->mqflags |= SR_MQFLAG_MIN;
+
+	if (buf[14] & 4) {
+		analog->mq = SR_MQ_VOLTAGE;
+		analog->unit = SR_UNIT_VOLT;
+		if ((analog->mqflags & (SR_MQFLAG_DC | SR_MQFLAG_AC)) == 0)
+			analog->mqflags |= SR_MQFLAG_DIODE;
+	}
+	if (buf[14] & 2) {
+		analog->mq = SR_MQ_CURRENT;
+		analog->unit = SR_UNIT_AMPERE;
+	}
+	if (buf[12] & 4) {
+		analog->mq = SR_MQ_RESISTANCE;
+		analog->unit = SR_UNIT_OHM;
+	}
+	if (buf[13] & 4) {
+		analog->mq = SR_MQ_CAPACITANCE;
+		analog->unit = SR_UNIT_FARAD;
+	}
+	if (buf[12] & 2) {
+		analog->mq = SR_MQ_FREQUENCY;
+		analog->unit = SR_UNIT_HERTZ;
+	}
+
+	if (decode_digit(3, buf) == 'C') {
+		analog->mq = SR_MQ_TEMPERATURE;
+		analog->unit = SR_UNIT_CELSIUS;
+	}
+	if (decode_digit(3, buf) == 'F') {
+		analog->mq = SR_MQ_TEMPERATURE;
+		analog->unit = SR_UNIT_FAHRENHEIT;
+	}
+
+	val = decode_value(buf) * decode_prefix(buf);
+
+	if (buf[3] & 1)
+		val = -val;
+
+	*floatval = val;
+
+	return SR_OK;
+}
diff --git a/hardware/common/dmm/fs9721.c b/src/dmm/dtm0660.c
similarity index 57%
copy from hardware/common/dmm/fs9721.c
copy to src/dmm/dtm0660.c
index f3101f8..794650a 100644
--- a/hardware/common/dmm/fs9721.c
+++ b/src/dmm/dtm0660.c
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2012 Uwe Hermann <uwe at hermann-uwe.de>
  * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ * Copyright (C) 2015 Matthieu Gaillet <matthieu at gaillet.be>
  *
  * 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
@@ -20,48 +21,47 @@
  */
 
 /*
- * Fortune Semiconductor FS9721_LP3/FS9721B protocol parser.
+ * Dream Tech International DTM0660 protocol parser.
  *
- * FS9721_LP3: 4000 counts (3 3/4 digits)
- * FS9721B/Q100: 2400 counts (3 2/3 digits)
+ * 6000 counts (5 5/6 digits)
  *
- * Same for both chips:
- *  - Packages: Bare die (78 pins) or QFP-100
+ *  - Package: QFP-64
  *  - Communication parameters: Unidirectional, 2400/8n1
- *  - The protocol seems to be exactly the same.
+ *  - The protocol is similar to FS9721 but with 15 bytes and reversed nibbles.
  */
 
+#include <config.h>
 #include <string.h>
 #include <ctype.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-#define LOG_PREFIX "fs9721"
+#define LOG_PREFIX "dtm0660"
 
 static int parse_digit(uint8_t b)
 {
 	switch (b) {
-	case 0x7d:
+	case 0xeb:
 		return 0;
-	case 0x05:
+	case 0x0a:
 		return 1;
-	case 0x5b:
+	case 0xad:
 		return 2;
-	case 0x1f:
+	case 0x8f:
 		return 3;
-	case 0x27:
+	case 0x4e:
 		return 4;
-	case 0x3e:
+	case 0xc7:
 		return 5;
-	case 0x7e:
+	case 0xe7:
 		return 6;
-	case 0x15:
+	case 0x8a:
 		return 7;
-	case 0x7f:
+	case 0xef:
 		return 8;
-	case 0x3f:
+	case 0xcf:
 		return 9;
 	default:
 		sr_dbg("Invalid digit byte: 0x%02x.", b);
@@ -74,7 +74,7 @@ static gboolean sync_nibbles_valid(const uint8_t *buf)
 	int i;
 
 	/* Check the synchronization nibbles, and make sure they all match. */
-	for (i = 0; i < FS9721_PACKET_SIZE; i++) {
+	for (i = 0; i < DTM0660_PACKET_SIZE; i++) {
 		if (((buf[i] >> 4) & 0x0f) != (i + 1)) {
 			sr_dbg("Sync nibble in byte %d (0x%02x) is invalid.",
 			       i, buf[i]);
@@ -85,7 +85,7 @@ static gboolean sync_nibbles_valid(const uint8_t *buf)
 	return TRUE;
 }
 
-static gboolean flags_valid(const struct fs9721_info *info)
+static gboolean flags_valid(const struct dtm0660_info *info)
 {
 	int count;
 
@@ -135,13 +135,13 @@ static int parse_value(const uint8_t *buf, float *result)
 	uint8_t digit_bytes[4];
 	float floatval;
 
-	/* Byte 1: LCD SEG2 */
-	sign = ((buf[1] & (1 << 3)) != 0) ? -1 : 1;
+	/* Byte 1 contains sign in bit 0. */
+	sign = ((buf[1] & (1 << 0)) != 0) ? -1 : 1;
 
 	/*
 	 * Bytes 1-8: Value (4 decimal digits, sign, decimal point)
 	 *
-	 * Over limit: "0L" (LCD), 0x00 0x7d 0x68 0x00 (digit bytes).
+	 * Over limit: "0L" (LCD), 0x00 0xeb 0x61 0x00 (digit bytes).
 	 */
 
 	/* Merge the two nibbles for a digit into one byte. */
@@ -149,13 +149,13 @@ static int parse_value(const uint8_t *buf, float *result)
 		digit_bytes[i] = ((buf[1 + (i * 2)] & 0x0f) << 4);
 		digit_bytes[i] |= (buf[1 + (i * 2) + 1] & 0x0f);
 
-		/* Bit 7 in the byte is not part of the digit. */
-		digit_bytes[i] &= ~(1 << 7);
+		/* Bit 4 in the byte is not part of the digit. */
+		digit_bytes[i] &= ~(1 << 4);
 	}
 
 	/* Check for "OL". */
-	if (digit_bytes[0] == 0x00 && digit_bytes[1] == 0x7d &&
-	    digit_bytes[2] == 0x68 && digit_bytes[3] == 0x00) {
+	if (digit_bytes[0] == 0x00 && digit_bytes[1] == 0xeb &&
+	    digit_bytes[2] == 0x61 && digit_bytes[3] == 0x00) {
 		sr_spew("Over limit.");
 		*result = INFINITY;
 		return SR_OK;
@@ -177,13 +177,13 @@ static int parse_value(const uint8_t *buf, float *result)
 	floatval = (float)intval;
 
 	/* Decimal point position. */
-	if ((buf[3] & (1 << 3)) != 0) {
+	if ((buf[3] & 0x01) != 0) {
 		floatval /= 1000;
 		sr_spew("Decimal point after first digit.");
-	} else if ((buf[5] & (1 << 3)) != 0) {
+	} else if ((buf[5] & 0x01) != 0) {
 		floatval /= 100;
 		sr_spew("Decimal point after second digit.");
-	} else if ((buf[7] & (1 << 3)) != 0) {
+	} else if ((buf[7] & 0x01) != 0) {
 		floatval /= 10;
 		sr_spew("Decimal point after third digit.");
 	} else {
@@ -200,50 +200,56 @@ static int parse_value(const uint8_t *buf, float *result)
 	return SR_OK;
 }
 
-static void parse_flags(const uint8_t *buf, struct fs9721_info *info)
+static void parse_flags(const uint8_t *buf, struct dtm0660_info *info)
 {
 	/* Byte 0: LCD SEG1 */
-	info->is_ac         = (buf[0] & (1 << 3)) != 0;
-	info->is_dc         = (buf[0] & (1 << 2)) != 0;
-	info->is_auto       = (buf[0] & (1 << 1)) != 0;
-	info->is_rs232      = (buf[0] & (1 << 0)) != 0;
+	info->is_ac         = (buf[0] & (1 << 0)) != 0;
+	info->is_dc         = (buf[0] & (1 << 1)) != 0;
+	info->is_auto       = (buf[0] & (1 << 2)) != 0;
+	info->is_rs232      = (buf[0] & (1 << 3)) != 0;
 
 	/* Byte 1: LCD SEG2 */
-	info->is_sign       = (buf[1] & (1 << 3)) != 0;
+	info->is_sign       = (buf[1] & (1 << 0)) != 0;
 
 	/* Byte 9: LCD SEG10 */
-	info->is_micro      = (buf[9] & (1 << 3)) != 0;
-	info->is_nano       = (buf[9] & (1 << 2)) != 0;
-	info->is_kilo       = (buf[9] & (1 << 1)) != 0;
-	info->is_diode      = (buf[9] & (1 << 0)) != 0;
+	info->is_micro      = (buf[9] & (1 << 0)) != 0;
+	info->is_nano       = (buf[9] & (1 << 1)) != 0;
+	info->is_kilo       = (buf[9] & (1 << 2)) != 0;
+	info->is_diode      = (buf[9] & (1 << 3)) != 0;
 
 	/* Byte 10: LCD SEG11 */
-	info->is_milli      = (buf[10] & (1 << 3)) != 0;
-	info->is_percent    = (buf[10] & (1 << 2)) != 0;
-	info->is_mega       = (buf[10] & (1 << 1)) != 0;
-	info->is_beep       = (buf[10] & (1 << 0)) != 0;
+	info->is_milli      = (buf[10] & (1 << 0)) != 0;
+	info->is_percent    = (buf[10] & (1 << 1)) != 0;
+	info->is_mega       = (buf[10] & (1 << 2)) != 0;
+	info->is_beep       = (buf[10] & (1 << 3)) != 0;
 
 	/* Byte 11: LCD SEG12 */
-	info->is_farad      = (buf[11] & (1 << 3)) != 0;
-	info->is_ohm        = (buf[11] & (1 << 2)) != 0;
-	info->is_rel        = (buf[11] & (1 << 1)) != 0;
-	info->is_hold       = (buf[11] & (1 << 0)) != 0;
+	info->is_farad      = (buf[11] & (1 << 0)) != 0;
+	info->is_ohm        = (buf[11] & (1 << 1)) != 0;
+	info->is_rel        = (buf[11] & (1 << 2)) != 0;
+	info->is_hold       = (buf[11] & (1 << 3)) != 0;
 
 	/* Byte 12: LCD SEG13 */
-	info->is_ampere     = (buf[12] & (1 << 3)) != 0;
-	info->is_volt       = (buf[12] & (1 << 2)) != 0;
-	info->is_hz         = (buf[12] & (1 << 1)) != 0;
-	info->is_bat        = (buf[12] & (1 << 0)) != 0;
+	info->is_ampere     = (buf[12] & (1 << 0)) != 0;
+	info->is_volt       = (buf[12] & (1 << 1)) != 0;
+	info->is_hz         = (buf[12] & (1 << 2)) != 0;
+	info->is_bat        = (buf[12] & (1 << 3)) != 0;
 
 	/* Byte 13: LCD SEG14 */
-	info->is_c2c1_11    = (buf[13] & (1 << 3)) != 0;
-	info->is_c2c1_10    = (buf[13] & (1 << 2)) != 0;
-	info->is_c2c1_01    = (buf[13] & (1 << 1)) != 0;
-	info->is_c2c1_00    = (buf[13] & (1 << 0)) != 0;
+	info->is_degf       = (buf[13] & (1 << 0)) != 0;
+	info->is_degc       = (buf[13] & (1 << 1)) != 0;
+	info->is_c2c1_00    = (buf[13] & (1 << 2)) != 0;
+	info->is_c2c1_01    = (buf[13] & (1 << 3)) != 0;
+
+	/* Byte 14: LCD SEG15 */
+	info->is_apo        = (buf[14] & (1 << 0)) != 0;
+	info->is_min        = (buf[14] & (1 << 1)) != 0;
+	info->is_minmax     = (buf[14] & (1 << 2)) != 0;
+	info->is_max        = (buf[14] & (1 << 3)) != 0;
 }
 
-static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
-			 const struct fs9721_info *info)
+static void handle_flags(struct sr_datafeed_analog_old *analog, float *floatval,
+			 const struct dtm0660_info *info)
 {
 	/* Factors */
 	if (info->is_nano)
@@ -291,6 +297,14 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
 		analog->mq = SR_MQ_DUTY_CYCLE;
 		analog->unit = SR_UNIT_PERCENTAGE;
 	}
+	if (info->is_degc) {
+		analog->mq = SR_MQ_TEMPERATURE;
+		analog->unit = SR_UNIT_CELSIUS;
+	}
+	if (info->is_degf) {
+		analog->mq = SR_MQ_TEMPERATURE;
+		analog->unit = SR_UNIT_FAHRENHEIT;
+	}
 
 	/* Measurement related flags */
 	if (info->is_ac)
@@ -305,25 +319,29 @@ static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
 		analog->mqflags |= SR_MQFLAG_HOLD;
 	if (info->is_rel)
 		analog->mqflags |= SR_MQFLAG_RELATIVE;
+	if (info->is_min)
+		analog->mqflags |= SR_MQFLAG_MIN;
+	if (info->is_max)
+		analog->mqflags |= SR_MQFLAG_MAX;
 
 	/* Other flags */
 	if (info->is_rs232)
 		sr_spew("RS232 enabled.");
 	if (info->is_bat)
 		sr_spew("Battery is low.");
+	if (info->is_apo)
+		sr_spew("Auto power-off mode is active.");
+	if (info->is_minmax)
+		sr_spew("Min/max mode active.");
 	if (info->is_c2c1_00)
 		sr_spew("User-defined LCD symbol 0 is active.");
 	if (info->is_c2c1_01)
 		sr_spew("User-defined LCD symbol 1 is active.");
-	if (info->is_c2c1_10)
-		sr_spew("User-defined LCD symbol 2 is active.");
-	if (info->is_c2c1_11)
-		sr_spew("User-defined LCD symbol 3 is active.");
 }
 
-SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf)
+SR_PRIV gboolean sr_dtm0660_packet_valid(const uint8_t *buf)
 {
-	struct fs9721_info info;
+	struct dtm0660_info info;
 
 	parse_flags(buf, &info);
 
@@ -333,25 +351,25 @@ SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf)
 /**
  * Parse a protocol packet.
  *
- * @param buf Buffer containing the 14-byte protocol packet. Must not be NULL.
+ * @param buf Buffer containing the 15-byte protocol packet. Must not be NULL.
  * @param floatval Pointer to a float variable. That variable will contain the
- *                 result value upon parsing success. Mut not be NULL.
- * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
+ *                 result value upon parsing success. Must not be NULL.
+ * @param analog Pointer to a struct sr_datafeed_analog_old. The struct will be
  *               filled with data according to the protocol packet.
  *               Must not be NULL.
- * @param info Pointer to a struct fs9721_info. The struct will be filled
+ * @param info Pointer to a struct dtm0660_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_fs9721_parse(const uint8_t *buf, float *floatval,
-			    struct sr_datafeed_analog *analog, void *info)
+SR_PRIV int sr_dtm0660_parse(const uint8_t *buf, float *floatval,
+			     struct sr_datafeed_analog_old *analog, void *info)
 {
 	int ret;
-	struct fs9721_info *info_local;
+	struct dtm0660_info *info_local;
 
-	info_local = (struct fs9721_info *)info;
+	info_local = (struct dtm0660_info *)info;
 
 	if ((ret = parse_value(buf, floatval)) != SR_OK) {
 		sr_dbg("Error parsing value: %d.", ret);
@@ -363,83 +381,3 @@ SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
 
 	return SR_OK;
 }
-
-SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info)
-{
-	struct fs9721_info *info_local;
-
-	info_local = (struct fs9721_info *)info;
-
-	/* User-defined FS9721_LP3 flag 'c2c1_00' means temperature (C). */
-	if (info_local->is_c2c1_00) {
-		analog->mq = SR_MQ_TEMPERATURE;
-		analog->unit = SR_UNIT_CELSIUS;
-	}
-}
-
-SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info)
-{
-	struct fs9721_info *info_local;
-
-	info_local = (struct fs9721_info *)info;
-
-	/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
-	if (info_local->is_c2c1_01) {
-		analog->mq = SR_MQ_TEMPERATURE;
-		analog->unit = SR_UNIT_CELSIUS;
-	}
-}
-
-SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info)
-{
-	struct fs9721_info *info_local;
-
-	info_local = (struct fs9721_info *)info;
-
-	/* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
-	if (info_local->is_c2c1_10) {
-		analog->mq = SR_MQ_TEMPERATURE;
-		analog->unit = SR_UNIT_CELSIUS;
-	}
-}
-
-SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *info)
-{
-	struct fs9721_info *info_local;
-
-	info_local = (struct fs9721_info *)info;
-
-	/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (F). */
-	if (info_local->is_c2c1_01) {
-		analog->mq = SR_MQ_TEMPERATURE;
-		analog->unit = SR_UNIT_FAHRENHEIT;
-	}
-
-	/* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
-	if (info_local->is_c2c1_10) {
-		analog->mq = SR_MQ_TEMPERATURE;
-		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 = (struct fs9721_info *)info;
-
-	/* User-defined FS9721_LP3 flag 'c2c1_00' means MAX. */
-	if (info_local->is_c2c1_00)
-		analog->mqflags |= SR_MQFLAG_MAX;
-
-	/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
-	if (info_local->is_c2c1_01) {
-		analog->mq = SR_MQ_TEMPERATURE;
-		analog->unit = SR_UNIT_CELSIUS;
-	}
-
-	/* User-defined FS9721_LP3 flag 'c2c1_11' means MIN. */
-	if (info_local->is_c2c1_11)
-		analog->mqflags |= SR_MQFLAG_MIN;
-
-}
diff --git a/hardware/common/dmm/es519xx.c b/src/dmm/es519xx.c
similarity index 97%
rename from hardware/common/dmm/es519xx.c
rename to src/dmm/es519xx.c
index 075587c..69d8175 100644
--- a/hardware/common/dmm/es519xx.c
+++ b/src/dmm/es519xx.c
@@ -25,11 +25,12 @@
  * Communication parameters: Unidirectional, 2400/7o1 or 19230/7o1
  */
 
+#include <config.h>
 #include <string.h>
 #include <ctype.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "es519xx"
@@ -443,7 +444,7 @@ static void parse_flags(const uint8_t *buf, struct es519xx_info *info)
 	}
 }
 
-static void handle_flags(struct sr_datafeed_analog *analog,
+static void handle_flags(struct sr_datafeed_analog_old *analog,
 			 float *floatval, const struct es519xx_info *info)
 {
 	/*
@@ -603,7 +604,7 @@ static gboolean sr_es519xx_packet_valid(const uint8_t *buf,
 }
 
 static int sr_es519xx_parse(const uint8_t *buf, float *floatval,
-                            struct sr_datafeed_analog *analog,
+                            struct sr_datafeed_analog_old *analog,
                             struct es519xx_info *info)
 {
 	int ret;
@@ -639,7 +640,7 @@ SR_PRIV gboolean sr_es519xx_2400_11b_packet_valid(const uint8_t *buf)
 }
 
 SR_PRIV int sr_es519xx_2400_11b_parse(const uint8_t *buf, float *floatval,
-				struct sr_datafeed_analog *analog, void *info)
+				struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct es519xx_info *info_local;
 
@@ -668,7 +669,7 @@ SR_PRIV gboolean sr_es519xx_2400_11b_altfn_packet_valid(const uint8_t *buf)
 }
 
 SR_PRIV int sr_es519xx_2400_11b_altfn_parse(const uint8_t *buf,
-		float *floatval, struct sr_datafeed_analog *analog, void *info)
+		float *floatval, struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct es519xx_info *info_local;
 
@@ -698,7 +699,7 @@ SR_PRIV gboolean sr_es519xx_19200_11b_5digits_packet_valid(const uint8_t *buf)
 }
 
 SR_PRIV int sr_es519xx_19200_11b_5digits_parse(const uint8_t *buf,
-		float *floatval, struct sr_datafeed_analog *analog, void *info)
+		float *floatval, struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct es519xx_info *info_local;
 
@@ -728,7 +729,7 @@ SR_PRIV gboolean sr_es519xx_19200_11b_clamp_packet_valid(const uint8_t *buf)
 }
 
 SR_PRIV int sr_es519xx_19200_11b_clamp_parse(const uint8_t *buf,
-		float *floatval, struct sr_datafeed_analog *analog, void *info)
+		float *floatval, struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct es519xx_info *info_local;
 
@@ -757,7 +758,7 @@ SR_PRIV gboolean sr_es519xx_19200_11b_packet_valid(const uint8_t *buf)
 }
 
 SR_PRIV int sr_es519xx_19200_11b_parse(const uint8_t *buf, float *floatval,
-			struct sr_datafeed_analog *analog, void *info)
+			struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct es519xx_info *info_local;
 
@@ -785,7 +786,7 @@ SR_PRIV gboolean sr_es519xx_19200_14b_packet_valid(const uint8_t *buf)
 }
 
 SR_PRIV int sr_es519xx_19200_14b_parse(const uint8_t *buf, float *floatval,
-			struct sr_datafeed_analog *analog, void *info)
+			struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct es519xx_info *info_local;
 
@@ -814,7 +815,7 @@ SR_PRIV gboolean sr_es519xx_19200_14b_sel_lpf_packet_valid(const uint8_t *buf)
 }
 
 SR_PRIV int sr_es519xx_19200_14b_sel_lpf_parse(const uint8_t *buf,
-		float *floatval, struct sr_datafeed_analog *analog, void *info)
+		float *floatval, struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct es519xx_info *info_local;
 
diff --git a/hardware/common/dmm/fs9721.c b/src/dmm/fs9721.c
similarity index 93%
rename from hardware/common/dmm/fs9721.c
rename to src/dmm/fs9721.c
index f3101f8..3c98102 100644
--- a/hardware/common/dmm/fs9721.c
+++ b/src/dmm/fs9721.c
@@ -31,11 +31,12 @@
  *  - The protocol seems to be exactly the same.
  */
 
+#include <config.h>
 #include <string.h>
 #include <ctype.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "fs9721"
@@ -242,7 +243,7 @@ static void parse_flags(const uint8_t *buf, struct fs9721_info *info)
 	info->is_c2c1_00    = (buf[13] & (1 << 0)) != 0;
 }
 
-static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+static void handle_flags(struct sr_datafeed_analog_old *analog, float *floatval,
 			 const struct fs9721_info *info)
 {
 	/* Factors */
@@ -335,8 +336,8 @@ 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 floatval Pointer to a float variable. That variable will contain the
- *                 result value upon parsing success. Mut not be NULL.
- * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
+ *                 result value upon parsing success. Must not be NULL.
+ * @param analog Pointer to a struct sr_datafeed_analog_old. The struct will be
  *               filled with data according to the protocol packet.
  *               Must not be NULL.
  * @param info Pointer to a struct fs9721_info. The struct will be filled
@@ -346,7 +347,7 @@ SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf)
  *         'analog' variable contents are undefined and should not be used.
  */
 SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
-			    struct sr_datafeed_analog *analog, void *info)
+			    struct sr_datafeed_analog_old *analog, void *info)
 {
 	int ret;
 	struct fs9721_info *info_local;
@@ -364,7 +365,7 @@ SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
 	return SR_OK;
 }
 
-SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info)
+SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct fs9721_info *info_local;
 
@@ -377,7 +378,7 @@ SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog *analog, void *info)
 	}
 }
 
-SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info)
+SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct fs9721_info *info_local;
 
@@ -390,7 +391,7 @@ SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog *analog, void *info)
 	}
 }
 
-SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info)
+SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct fs9721_info *info_local;
 
@@ -403,7 +404,7 @@ SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog *analog, void *info)
 	}
 }
 
-SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *info)
+SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct fs9721_info *info_local;
 
@@ -422,7 +423,7 @@ SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog *analog, void *i
 	}
 }
 
-SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog *analog, void *info)
+SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct fs9721_info *info_local;
 
diff --git a/hardware/common/dmm/fs9922.c b/src/dmm/fs9922.c
similarity index 96%
rename from hardware/common/dmm/fs9922.c
rename to src/dmm/fs9922.c
index caaa51c..5325e6d 100644
--- a/hardware/common/dmm/fs9922.c
+++ b/src/dmm/fs9922.c
@@ -22,11 +22,12 @@
  * Fortune Semiconductor FS9922-DMM3/FS9922-DMM4 protocol parser.
  */
 
+#include <config.h>
 #include <string.h>
 #include <ctype.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "fs9922"
@@ -112,7 +113,9 @@ static int parse_value(const uint8_t *buf, float *result)
 	} else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
 		   !isdigit(buf[3]) || !isdigit(buf[4])) {
 		sr_dbg("Value contained invalid digits: %02x %02x %02x %02x ("
-		       "%c %c %c %c).", buf[1], buf[2], buf[3], buf[4]);
+			"%c %c %c %c).",
+			buf[1], buf[2], buf[3], buf[4],
+			buf[1], buf[2], buf[3], buf[4]);
 		return SR_ERR;
 	}
 	intval = 0;
@@ -220,7 +223,7 @@ static void parse_flags(const uint8_t *buf, struct fs9922_info *info)
 	/* Byte 13: Always '\n' (newline, 0x0a, 10) */
 }
 
-static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+static void handle_flags(struct sr_datafeed_analog_old *analog, float *floatval,
 			 const struct fs9922_info *info)
 {
 	/* Factors */
@@ -341,7 +344,7 @@ SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf)
  * @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
+ * @param analog Pointer to a struct sr_datafeed_analog_old. The struct will be
  *               filled with data according to the protocol packet.
  *               Must not be NULL.
  * @param info Pointer to a struct fs9922_info. The struct will be filled
@@ -351,7 +354,7 @@ SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf)
  *         'analog' variable contents are undefined and should not be used.
  */
 SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
-			    struct sr_datafeed_analog *analog, void *info)
+			    struct sr_datafeed_analog_old *analog, void *info)
 {
 	int ret;
 	struct fs9922_info *info_local;
@@ -369,7 +372,7 @@ SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
 	return SR_OK;
 }
 
-SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog *analog, void *info)
+SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct fs9922_info *info_local;
 
diff --git a/hardware/common/dmm/m2110.c b/src/dmm/m2110.c
similarity index 94%
rename from hardware/common/dmm/m2110.c
rename to src/dmm/m2110.c
index 5863cef..ea53fae 100644
--- a/hardware/common/dmm/m2110.c
+++ b/src/dmm/m2110.c
@@ -25,10 +25,11 @@
  * Most probably the simplest multimeter protocol ever ;-) .
  */
 
+#include <config.h>
 #include <string.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "m2110"
@@ -50,7 +51,7 @@ SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf)
 }
 
 SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
-				struct sr_datafeed_analog *analog, void *info)
+				struct sr_datafeed_analog_old *analog, void *info)
 {
 	float val;
 
diff --git a/hardware/common/dmm/metex14.c b/src/dmm/metex14.c
similarity index 84%
rename from hardware/common/dmm/metex14.c
rename to src/dmm/metex14.c
index 8151aa9..b39c932 100644
--- a/hardware/common/dmm/metex14.c
+++ b/src/dmm/metex14.c
@@ -30,11 +30,13 @@
  * It does _not_ work for all Metex DMMs, some use a quite different protocol.
  */
 
+#include <config.h>
 #include <string.h>
+#include <strings.h>
 #include <ctype.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "metex14"
@@ -55,14 +57,14 @@ static int parse_value(const uint8_t *buf, struct metex14_info *info,
 
 	/* Bytes 5-7: Over limit (various forms) */
 	is_ol = 0;
-	is_ol += (!strcasecmp((const char *)&valstr, ".OL")) ? 1 : 0;
-	is_ol += (!strcasecmp((const char *)&valstr, "O.L")) ? 1 : 0;
-	is_ol += (!strcasecmp((const char *)&valstr, "OL.")) ? 1 : 0;
-	is_ol += (!strcasecmp((const char *)&valstr, "OL")) ? 1 : 0;
-	is_ol += (!strcasecmp((const char *)&valstr, "-.OL")) ? 1 : 0;
-	is_ol += (!strcasecmp((const char *)&valstr, "-O.L")) ? 1 : 0;
-	is_ol += (!strcasecmp((const char *)&valstr, "-OL.")) ? 1 : 0;
-	is_ol += (!strcasecmp((const char *)&valstr, "-OL")) ? 1 : 0;
+	is_ol += (!g_ascii_strcasecmp((const char *)&valstr, ".OL")) ? 1 : 0;
+	is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "O.L")) ? 1 : 0;
+	is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "OL.")) ? 1 : 0;
+	is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "OL")) ? 1 : 0;
+	is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-.OL")) ? 1 : 0;
+	is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-O.L")) ? 1 : 0;
+	is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-OL.")) ? 1 : 0;
+	is_ol += (!g_ascii_strcasecmp((const char *)&valstr, "-OL")) ? 1 : 0;
 	if (is_ol != 0) {
 		sr_spew("Over limit.");
 		*result = INFINITY;
@@ -113,35 +115,35 @@ static void parse_flags(const char *buf, struct metex14_info *info)
 
 	/* Bytes 9-12: Unit */
 	u = (const char *)&unit;
-	if (!strcasecmp(u, "A"))
+	if (!g_ascii_strcasecmp(u, "A"))
 		info->is_ampere = TRUE;
-	else if (!strcasecmp(u, "mA"))
+	else if (!g_ascii_strcasecmp(u, "mA"))
 		info->is_milli = info->is_ampere = TRUE;
-	else if (!strcasecmp(u, "uA"))
+	else if (!g_ascii_strcasecmp(u, "uA"))
 		info->is_micro = info->is_ampere = TRUE;
-	else if (!strcasecmp(u, "V"))
+	else if (!g_ascii_strcasecmp(u, "V"))
 		info->is_volt = TRUE;
-	else if (!strcasecmp(u, "mV"))
+	else if (!g_ascii_strcasecmp(u, "mV"))
 		info->is_milli = info->is_volt = TRUE;
-	else if (!strcasecmp(u, "Ohm"))
+	else if (!g_ascii_strcasecmp(u, "Ohm"))
 		info->is_ohm = TRUE;
-	else if (!strcasecmp(u, "KOhm"))
+	else if (!g_ascii_strcasecmp(u, "KOhm"))
 		info->is_kilo = info->is_ohm = TRUE;
-	else if (!strcasecmp(u, "MOhm"))
+	else if (!g_ascii_strcasecmp(u, "MOhm"))
 		info->is_mega = info->is_ohm = TRUE;
-	else if (!strcasecmp(u, "pF"))
+	else if (!g_ascii_strcasecmp(u, "pF"))
 		info->is_pico = info->is_farad = TRUE;
-	else if (!strcasecmp(u, "nF"))
+	else if (!g_ascii_strcasecmp(u, "nF"))
 		info->is_nano = info->is_farad = TRUE;
-	else if (!strcasecmp(u, "uF"))
+	else if (!g_ascii_strcasecmp(u, "uF"))
 		info->is_micro = info->is_farad = TRUE;
-	else if (!strcasecmp(u, "KHz"))
+	else if (!g_ascii_strcasecmp(u, "KHz"))
 		info->is_kilo = info->is_hertz = TRUE;
-	else if (!strcasecmp(u, "C"))
+	else if (!g_ascii_strcasecmp(u, "C"))
 		info->is_celsius = TRUE;
-	else if (!strcasecmp(u, "DB"))
+	else if (!g_ascii_strcasecmp(u, "DB"))
 		info->is_decibel = TRUE;
-	else if (!strcasecmp(u, ""))
+	else if (!g_ascii_strcasecmp(u, ""))
 		info->is_unitless = TRUE;
 
 	/* Bytes 0-1: Measurement mode, except AC/DC */
@@ -168,7 +170,7 @@ static void parse_flags(const char *buf, struct metex14_info *info)
 	/* Byte 13: Always '\r' (carriage return, 0x0d, 13) */
 }
 
-static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+static void handle_flags(struct sr_datafeed_analog_old *analog, float *floatval,
 			 const struct metex14_info *info)
 {
 	/* Factors */
@@ -283,7 +285,7 @@ SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial)
 
 	sr_spew("Requesting DMM packet.");
 
-	return (serial_write(serial, &wbuf, 1) == 1) ? SR_OK : SR_ERR;
+	return (serial_write_nonblocking(serial, &wbuf, 1) == 1) ? SR_OK : SR_ERR;
 }
 #endif
 
@@ -309,7 +311,7 @@ SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf)
  * @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
+ * @param analog Pointer to a struct sr_datafeed_analog_old. 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
@@ -319,7 +321,7 @@ SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf)
  *         '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)
+			     struct sr_datafeed_analog_old *analog, void *info)
 {
 	int ret;
 	struct metex14_info *info_local;
diff --git a/hardware/common/dmm/rs9lcd.c b/src/dmm/rs9lcd.c
similarity index 98%
rename from hardware/common/dmm/rs9lcd.c
rename to src/dmm/rs9lcd.c
index 539c233..d337d7f 100644
--- a/hardware/common/dmm/rs9lcd.c
+++ b/src/dmm/rs9lcd.c
@@ -28,11 +28,12 @@
  * and protocol is used on any other device.
  */
 
+#include <config.h>
 #include <string.h>
 #include <ctype.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "rs9lcd"
@@ -286,7 +287,7 @@ static double lcd_to_double(const struct rs9lcd_packet *rs_packet, int type)
 		rawval *= -1;
 
 	/* See if we need to multiply our raw value by anything. */
-	if (rs_packet->indicatrix1 & IND2_NANO)
+	if (rs_packet->indicatrix2 & IND2_NANO)
 		rawval *= 1E-9;
 	else if (rs_packet->indicatrix2 & IND2_MICRO)
 		rawval *= 1E-6;
@@ -317,7 +318,7 @@ static gboolean is_logic_high(const struct rs9lcd_packet *rs_packet)
 }
 
 SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval,
-			    struct sr_datafeed_analog *analog, void *info)
+			    struct sr_datafeed_analog_old *analog, void *info)
 {
 	const struct rs9lcd_packet *rs_packet = (void *)buf;
 	double rawval;
diff --git a/src/dmm/ut372.c b/src/dmm/ut372.c
new file mode 100644
index 0000000..74f8757
--- /dev/null
+++ b/src/dmm/ut372.c
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Martin Ling <martin-sigrok at 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 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
+ */
+
+/*
+ * UNI-T UT372 protocol parser.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ut372"
+
+static const uint8_t lookup[] = {
+	0x7B,
+	0x60,
+	0x5E,
+	0x7C,
+	0x65,
+	0x3D,
+	0x3F,
+	0x70,
+	0x7F,
+	0x7D,
+};
+
+#define DECIMAL_POINT_MASK 0x80
+
+#define FLAGS1_HOLD_MASK (1 << 2)
+
+#define FLAGS2_RPM_MASK (1 << 0)
+#define FLAGS2_COUNT_MASK (1 << 1)
+#define FLAGS2_MAX_MASK (1 << 4)
+#define FLAGS2_MIN_MASK (1 << 5)
+#define FLAGS2_AVG_MASK (1 << 6)
+
+/* Decode a pair of characters into a byte. */
+static uint8_t decode_pair(const uint8_t *buf)
+{
+	unsigned int i;
+	char hex[3];
+
+	hex[2] = '\0';
+
+	for (i = 0; i < 2; i++) {
+		hex[i] = buf[i];
+		if (hex[i] > 0x39)
+			hex[i] += 7;
+	}
+
+	return strtol(hex, NULL, 16);
+}
+
+SR_PRIV gboolean sr_ut372_packet_valid(const uint8_t *buf)
+{
+	uint8_t flags2;
+
+	if (!(buf[25] == '\r' && buf[26] == '\n'))
+		return FALSE;
+
+	flags2 = decode_pair(buf + 23);
+
+	if (!(flags2 & (FLAGS2_RPM_MASK | FLAGS2_COUNT_MASK)))
+		/* Device is in the setup menu - no valid data shown. */
+		return FALSE;
+
+	return TRUE;
+}
+
+SR_PRIV int sr_ut372_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info)
+{
+	unsigned int i, j, value, divisor;
+	uint8_t segments, flags1, flags2;
+
+	(void) info;
+
+	flags1 = decode_pair(buf + 21);
+	flags2 = decode_pair(buf + 23);
+
+	if (flags2 & FLAGS2_RPM_MASK) {
+		analog->mq = SR_MQ_FREQUENCY;
+		analog->unit = SR_UNIT_REVOLUTIONS_PER_MINUTE;
+	} else if (flags2 & FLAGS2_COUNT_MASK) {
+		analog->mq = SR_MQ_COUNT;
+		analog->unit = SR_UNIT_UNITLESS;
+	}
+
+	if (flags1 & FLAGS1_HOLD_MASK)
+		analog->mqflags |= SR_MQFLAG_HOLD;
+	if (flags2 & FLAGS2_MIN_MASK)
+		analog->mqflags |= SR_MQFLAG_MIN;
+	if (flags2 & FLAGS2_MAX_MASK)
+		analog->mqflags |= SR_MQFLAG_MAX;
+	if (flags2 & FLAGS2_AVG_MASK)
+		analog->mqflags |= SR_MQFLAG_AVG;
+
+	value = 0;
+	divisor = 1;
+
+	for (i = 0; i < 5; i++) {
+		segments = decode_pair(buf + 1 + (2 * i));
+		for (j = 0; j < ARRAY_SIZE(lookup); j++) {
+			if (lookup[j] == (segments & ~DECIMAL_POINT_MASK)) {
+				value += j * pow(10, i);
+				break;
+			}
+		}
+		if (segments & DECIMAL_POINT_MASK)
+			divisor = pow(10, i);
+	}
+
+	*floatval = (float) value / divisor;
+
+	return SR_OK;
+}
diff --git a/src/dmm/ut71x.c b/src/dmm/ut71x.c
new file mode 100644
index 0000000..9af8fc1
--- /dev/null
+++ b/src/dmm/ut71x.c
@@ -0,0 +1,352 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe at 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
+ */
+
+/*
+ * UNI-T UT71x protocol parser.
+ *
+ * Communication parameters: Unidirectional, 2400/7o1
+ */
+
+#include <config.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ut71x"
+
+/*
+ * Factors for the respective measurement mode (0 means "invalid").
+ *
+ * The Conrad/Voltcraft protocol descriptions have a typo (they suggest
+ * index 0 for the 10A range (which is incorrect, it's range 1).
+ */
+static const float factors[16][8] = {
+	{1e-5, 0,     0,     0,     0,    0,    0,    0   }, /* AC mV */
+	{0,    1e-4,  1e-3,  1e-2,  1e-1, 0,    0,    0   }, /* DC V */
+	{0,    1e-4,  1e-3,  1e-2,  1e-1, 0,    0,    0   }, /* AC V */
+	{1e-5, 0,     0,     0,     0,    0,    0,    0   }, /* DC mV */
+	{0,    1e-1,  1,     1e1,   1e2,  1e3,  1e4,  0   }, /* Resistance */
+	{0,    1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6}, /* Capacitance */
+	{1e-1, 0,     0,     0,     0,    0,    0,    0   }, /* Temp (C) */
+	{1e-8, 1e-7,  0,     0,     0,    0,    0,    0   }, /* uA */
+	{1e-6, 1e-5,  0,     0,     0,    0,    0,    0   }, /* mA */
+	{0,    1e-3,  0,     0,     0,    0,    0,    0   }, /* 10A */
+	{1e-1, 0,     0,     0,     0,    0,    0,    0   }, /* Continuity */
+	{1e-4, 0,     0,     0,     0,    0,    0,    0   }, /* Diode */
+	{1e-3, 1e-2,  1e-1,  1,     1e1,  1e2,  1e3,  1e4 }, /* Frequency */
+	{1e-1, 0,     0,     0,     0,    0,    0,    0   }, /* Temp (F) */
+	{0,    0,     0,     1,     0,    0,    0,    0   }, /* Power */
+	{1e-2, 0,     0,     0,     0,    0,    0,    0   }, /* Loop current */
+};
+
+static int parse_value(const uint8_t *buf, struct ut71x_info *info, float *result)
+{
+	int i, intval, num_digits = 5;
+
+	/* Bytes 0-4: Value (5 decimal digits) */
+	if (!strncmp((const char *)buf, "::0<:", 5)) {
+		sr_spew("Over limit.");
+		*result = INFINITY;
+		return SR_OK;
+	} else if (!strncmp((const char *)buf, ":<0::", 5)) {
+		sr_spew("Under limit.");
+		*result = INFINITY;
+		return SR_OK;
+	} else if (buf[4] == ':') {
+		sr_dbg("4000 count mode, only 4 digits used.");
+		num_digits = 4;
+	} else if (!isdigit(buf[0]) || !isdigit(buf[1]) ||
+	           !isdigit(buf[2]) || !isdigit(buf[3]) || !isdigit(buf[4])) {
+		sr_dbg("Invalid digits: %02x %02x %02x %02x %02x (%c %c "
+		       "%c %c %c).", buf[0], buf[1], buf[2], buf[3], buf[4],
+		       buf[0], buf[1], buf[2], buf[3], buf[4]);
+		return SR_ERR;
+	}
+	for (i = 0, intval = 0; i < num_digits; i++)
+		intval = 10 * intval + (buf[i] - '0');
+
+	/* Apply sign. */
+	intval *= info->is_sign ? -1 : 1;
+
+	/* Note: The decimal point position will be parsed later. */
+	*result = (float)intval;
+	sr_spew("The display value is %f.", *result);
+
+	return SR_OK;
+}
+
+static int parse_range(const uint8_t *buf, float *floatval)
+{
+	int idx, mode;
+	float factor = 0;
+
+	idx = buf[5] - '0';
+	if (idx < 0 || idx > 7) {
+		sr_dbg("Invalid range byte 0x%02x (idx 0x%02x).", buf[5], idx);
+		return SR_ERR;
+	}
+
+	mode = buf[6] - '0';
+	if (mode < 0 || mode > 15) {
+		sr_dbg("Invalid mode byte 0x%02x (idx 0x%02x).", buf[6], mode);
+		return SR_ERR;
+	}
+
+	sr_spew("mode/idx = %d/%d", mode, idx);
+
+	factor = factors[mode][idx];
+	if (factor == 0) {
+		sr_dbg("Invalid factor for range byte: 0x%02x.", buf[5]);
+		return SR_ERR;
+	}
+
+	/* Apply respective factor (mode-dependent) on the value. */
+	*floatval *= factor;
+	sr_dbg("Applying factor %f, new value is %f.", factor, *floatval);
+
+	return SR_OK;
+}
+
+static void parse_flags(const uint8_t *buf, struct ut71x_info *info)
+{
+	/* Function byte */
+	switch (buf[6] - '0') {
+	case 0: /* AC mV */
+		info->is_voltage = info->is_ac = TRUE;
+		break;
+	case 1: /* DC V */
+		info->is_voltage = info->is_dc = TRUE;
+		break;
+	case 2: /* AC V */
+		info->is_voltage = info->is_ac = TRUE;
+		break;
+	case 3: /* DC mV */
+		info->is_voltage = info->is_dc = TRUE;
+		break;
+	case 4: /* Resistance */
+		info->is_resistance = TRUE;
+		break;
+	case 5: /* Capacitance */
+		info->is_capacitance = TRUE;
+		break;
+	case 6: /* Temperature (Celsius) */
+		info->is_temperature = info->is_celsius = TRUE;
+		break;
+	case 7: /* uA */
+		info->is_current = info->is_dc = TRUE;
+		break;
+	case 8: /* mA */
+		info->is_current = info->is_dc = TRUE;
+		break;
+	case 9: /* 10A */
+		info->is_current = info->is_dc = TRUE;
+		break;
+	case 10: /* Continuity */
+		info->is_continuity = TRUE;
+		break;
+	case 11: /* Diode */
+		info->is_diode = TRUE;
+		break;
+	case 12: /* Frequency */
+		info->is_frequency = TRUE;
+		break;
+	case 13: /* Temperature (F) */
+		info->is_temperature = info->is_fahrenheit = TRUE;
+		break;
+	case 14: /* Power */
+		/* Note: Only available on UT71E (range 0-2500W). */
+		info->is_power = TRUE;
+		break;
+	case 15: /* DC loop current, percentage display (range 4-20mA) */
+		info->is_loop_current = TRUE;
+		break;
+	default:
+		sr_dbg("Invalid function byte: 0x%02x.", buf[6]);
+		break;
+	}
+
+	/*
+	 * State 1 byte: bit 0 = AC, bit 1 = DC
+	 * Either AC or DC or both or none can be set at the same time.
+	 */
+	info->is_ac = (buf[7] & (1 << 0)) != 0;
+	info->is_dc = (buf[7] & (1 << 1)) != 0;
+
+	/*
+	 * State 2 byte: bit 0 = auto, bit 1 = manual, bit 2 = sign
+	 *
+	 * The Conrad/Voltcraft protocol descriptions have a typo
+	 * (they suggest bit 3 as sign bit, which is incorrect).
+	 *
+	 * For modes where there's only one possible range (e.g. AC mV)
+	 * neither the "auto" nor the "manual" bits will be set.
+	 */
+	info->is_auto   = (buf[8] & (1 << 0)) != 0;
+	info->is_manual = (buf[8] & (1 << 1)) != 0;
+	info->is_sign   = (buf[8] & (1 << 2)) != 0;
+
+	/* Note: "Frequency mode + sign bit" means "duty cycle mode". */
+	if (info->is_frequency && info->is_sign) {
+		info->is_duty_cycle = TRUE;
+		info->is_frequency = info->is_sign = FALSE;
+	}
+}
+
+static void handle_flags(struct sr_datafeed_analog_old *analog,
+		float *floatval, const struct ut71x_info *info)
+{
+	/* 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 || *floatval > 60.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;
+	}
+	if (info->is_power) {
+		analog->mq = SR_MQ_POWER;
+		analog->unit = SR_UNIT_WATT;
+	}
+	if (info->is_loop_current) {
+		/* 4mA = 0%, 20mA = 100% */
+		analog->mq = SR_MQ_CURRENT;
+		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_ac)
+		/* All AC modes do True-RMS measurements. */
+		analog->mqflags |= SR_MQFLAG_RMS;
+	if (info->is_auto)
+		analog->mqflags |= SR_MQFLAG_AUTORANGE;
+	if (info->is_diode)
+		analog->mqflags |= SR_MQFLAG_DIODE;
+}
+
+static gboolean flags_valid(const struct ut71x_info *info)
+{
+	int count;
+
+	/* Does the packet "measure" more than one type of value? */
+	count  = (info->is_voltage) ? 1 : 0;
+	count += (info->is_current) ? 1 : 0;
+	count += (info->is_resistance) ? 1 : 0;
+	count += (info->is_capacitance) ? 1 : 0;
+	count += (info->is_frequency) ? 1 : 0;
+	count += (info->is_temperature) ? 1 : 0;
+	count += (info->is_continuity) ? 1 : 0;
+	count += (info->is_diode) ? 1 : 0;
+	count += (info->is_power) ? 1 : 0;
+	count += (info->is_loop_current) ? 1 : 0;
+	if (count > 1) {
+		sr_dbg("More than one measurement type detected in packet.");
+		return FALSE;
+	}
+
+	/* Auto and manual can't be active at the same time. */
+	if (info->is_auto && info->is_manual) {
+		sr_dbg("Auto and manual modes are both active.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+SR_PRIV gboolean sr_ut71x_packet_valid(const uint8_t *buf)
+{
+	struct ut71x_info info;
+
+	memset(&info, 0, sizeof(struct ut71x_info));
+
+	if (buf[9] != '\r' || buf[10] != '\n')
+		return FALSE;
+
+	parse_flags(buf, &info);
+
+	return flags_valid(&info);
+}
+
+SR_PRIV int sr_ut71x_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info)
+{
+	int ret;
+	struct ut71x_info *info_local;
+
+	info_local = (struct ut71x_info *)info;
+	memset(info_local, 0, sizeof(struct ut71x_info));
+
+	if (!sr_ut71x_packet_valid(buf))
+		return SR_ERR;
+
+	parse_flags(buf, info_local);
+
+	if ((ret = parse_value(buf, info, floatval)) != SR_OK) {
+		sr_dbg("Error parsing value: %d.", ret);
+		return ret;
+	}
+
+	if ((ret = parse_range(buf, floatval)) != SR_OK)
+		return ret;
+
+	handle_flags(analog, floatval, info);
+
+	return SR_OK;
+}
diff --git a/src/dmm/vc870.c b/src/dmm/vc870.c
new file mode 100644
index 0000000..2663260
--- /dev/null
+++ b/src/dmm/vc870.c
@@ -0,0 +1,439 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014-2015 Uwe Hermann <uwe at 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 <config.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "vc870"
+
+/* Factors for the respective measurement mode (0 means "invalid"). */
+static const float factors[][8] = {
+	{1e-4,  1e-3,  1e-2,  1e-1, 0,    0,    0,    0},    /* DCV */
+	{1e-3,  1e-2,  1e-1,  1,    0,    0,    0,    0},    /* ACV */
+	{1e-5,  0,     0,     0,    0,    0,    0,    0},    /* DCmV */
+ 	{1e-1,  0,     0,     0,    0,    0,    0,    0},    /* Temperature (C) */
+//	{1e-2,  0,     0,     0,    0,    0,    0,    0},    /* TODO: Temperature (F) */
+	/*
+	 * Note: The sequence 1e-1 -> 1e1 for the resistance
+	 * value is correct and verified in practice!
+	 * Don't trust the vendor docs on this.
+	 */
+	{1e-2,  1e-1,  1e1,   1e2,  1e3,  1e4,  0,    0},    /* Resistance */
+	{1e-2,  0,     0,     0,    0,    0,    0,    0},    /* Continuity */
+	{1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 0},    /* Capacitance */
+	{1e-4,  0,     0,     0,    0,    0,    0,    0},    /* Diode */
+	{1e-3,  1e-2,  1e-1,  1,    1e1,  1e2,  1e3,  1e4},  /* Frequency */
+	{1e-2,  0,     0,     0,    0,    0,    0,    0},    /* Loop current */
+	/*
+	 * Note: Measurements showed that AC and DC differ
+	 * in the factors used, although docs say they should
+	 * be the same.
+	 */
+	{1e-8,  1e-7,  0,     0,    0,    0,    0,    0},    /* DCµA */
+	{1e-7,  1e-6,  0,     0,    0,    0,    0,    0},    /* ACµA */
+	{1e-6,  1e-5,  0,     0,    0,    0,    0,    0},    /* DCmA */
+	{1e-5,  1e-4,  0,     0,    0,    0,    0,    0},    /* ACmA */
+	{1e-3,  0,     0,     0,    0,    0,    0,    0},    /* DCA */
+	/* TODO: Verify factor for ACA */
+	{1e-3,  0,     0,     0,    0,    0,    0,    0},    /* ACA */
+	{1e-1,  0,     0,     0,    0,    0,    0,    0},    /* Act+apparent power */
+	{1e-3,  0,     0,     0,    0,    0,    0,    0},    /* Power factor / freq */
+	{1e-1,  0,     0,     0,    0,    0,    0,    0},    /* V eff + A eff */
+};
+
+static int parse_value(const uint8_t *buf, struct vc870_info *info,
+                       float *result)
+{
+	int i, intval;
+
+	/* Bytes 3-7: Main display value (5 decimal digits) */
+	if (info->is_open || info->is_ol1) {
+		sr_spew("Over limit.");
+		*result = INFINITY;
+		return SR_OK;
+	} else if (!isdigit(buf[3]) || !isdigit(buf[4]) ||
+	           !isdigit(buf[5]) || !isdigit(buf[6]) || !isdigit(buf[7])) {
+		sr_dbg("Invalid digits: %02x %02x %02x %02x %02X "
+			"(%c %c %c %c %c).",
+			buf[3], buf[4], buf[5], buf[6], buf[7],
+			buf[3], buf[4], buf[5], buf[6], buf[7]);
+		return SR_ERR;
+	}
+
+	intval = 0;
+	for (i = 0; i < 5; i++)
+		intval = 10 * intval + (buf[i + 3] - '0'); /* Main display. */
+		// intval = 10 * intval + (buf[i + 8] - '0'); /* TODO: Aux display. */
+
+	/* Apply sign. */
+	intval *= info->is_sign1 ? -1 : 1;
+	// intval *= info->is_sign2 ? -1 : 1; /* TODO: Fahrenheit / aux display. */
+
+	/* Note: The decimal point position will be parsed later. */
+
+	sr_spew("The display value without comma is %05d.", intval);
+
+	*result = (float)intval;
+
+	return SR_OK;
+}
+
+static int parse_range(uint8_t b, float *floatval,
+                       const struct vc870_info *info)
+{
+	int idx, mode;
+	float factor = 0;
+
+	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 && info->is_dc && !info->is_milli)
+		mode = 0; /* DCV */
+	else if (info->is_voltage && info->is_ac)
+		mode = 1; /* ACV */
+	else if (info->is_voltage && info->is_dc && info->is_milli)
+		mode = 2; /* DCmV */
+	else if (info->is_temperature)
+		mode = 3; /* Temperature */
+	else if (info->is_resistance || info->is_continuity)
+		mode = 4; /* Resistance */
+	else if (info->is_continuity)
+		mode = 5; /* Continuity */
+	else if (info->is_capacitance)
+		mode = 6; /* Capacitance */
+	else if (info->is_diode)
+		mode = 7; /* Diode */
+	else if (info->is_frequency)
+		mode = 8; /* Frequency */
+	else if (info->is_loop_current)
+		mode = 9; /* Loop current */
+	else if (info->is_current && info->is_micro && info->is_dc)
+		mode = 10; /* DCµA */
+	else if (info->is_current && info->is_micro && info->is_ac)
+		mode = 11; /* ACµA */
+	else if (info->is_current && info->is_milli && info->is_dc)
+		mode = 12; /* DCmA */
+	else if (info->is_current && info->is_milli && info->is_ac)
+		mode = 13; /* ACmA */
+	else if (info->is_current && !info->is_milli && !info->is_micro && info->is_dc)
+		mode = 14; /* DCA */
+	else if (info->is_current && !info->is_milli && !info->is_micro && info->is_ac)
+		mode = 15; /* ACA */
+	else if (info->is_power_apparent_power)
+		mode = 16; /* Act+apparent power */
+	else if (info->is_power_factor_freq)
+		mode = 17; /* Power factor / freq */
+	else if (info->is_v_a_rms_value)
+		mode = 18; /* V eff + A eff */
+	else {
+		sr_dbg("Invalid mode, range byte was: 0x%02x.", b);
+		return SR_ERR;
+	}
+
+	factor = factors[mode][idx];
+
+	if (factor == 0) {
+		sr_dbg("Invalid factor for range byte: 0x%02x (mode=%d, idx=%d).", b, mode, idx);
+		return SR_ERR;
+	}
+
+	/* Apply respective factor (mode-dependent) on the value. */
+	*floatval *= factor;
+	sr_dbg("Applying factor %f, new value is %f.", factor, *floatval);
+
+	return SR_OK;
+}
+
+static void parse_flags(const uint8_t *buf, struct vc870_info *info)
+{
+	/* Bytes 0/1: Function / function select  */
+	/* Note: Some of these mappings are fixed up later. */
+	switch (buf[0]) {
+	case 0x30: /* DCV / ACV */
+		info->is_voltage = TRUE;
+		info->is_dc = (buf[1] == 0x30);
+		info->is_ac = (buf[1] == 0x31);
+		break;
+	case 0x31: /* DCmV / Celsius */
+		if (buf[1] == 0x30)
+			info->is_voltage = info->is_milli = info->is_dc = TRUE;
+		else if (buf[1] == 0x31)
+			info->is_temperature = TRUE;
+		break;
+	case 0x32: /* Resistance / Short-circuit test */
+		info->is_resistance = (buf[1] == 0x30);
+		info->is_continuity = (buf[1] == 0x31);
+		break;
+	case 0x33: /* Capacitance */
+		info->is_capacitance = (buf[1] == 0x30);
+		break;
+	case 0x34: /* Diode */
+		info->is_diode = (buf[1] == 0x30);
+		break;
+	case 0x35: /* (4~20mA)% */
+		info->is_frequency = (buf[1] == 0x30);
+		info->is_loop_current = (buf[1] == 0x31);
+		break;
+	case 0x36: /* DCµA / ACµA */
+		info->is_current = info->is_micro = TRUE;
+		info->is_dc = (buf[1] == 0x30);
+		info->is_ac = (buf[1] == 0x31);
+		break;
+	case 0x37: /* DCmA / ACmA */
+		info->is_current = info->is_milli = TRUE;
+		info->is_dc = (buf[1] == 0x30);
+		info->is_ac = (buf[1] == 0x31);
+		break;
+	case 0x38: /* DCA / ACA */
+		info->is_current = TRUE;
+		info->is_dc = (buf[1] == 0x30);
+		info->is_ac = (buf[1] == 0x31);
+		break;
+	case 0x39: /* Active power + apparent power / power factor + frequency */
+		if (buf[1] == 0x30)
+			/* Active power + apparent power */
+			info->is_power_apparent_power = TRUE;
+		else if (buf[1] == 0x31)
+			/* Power factor + frequency */
+			info->is_power_factor_freq = TRUE;
+		else if (buf[1] == 0x32)
+			/* Voltage effective value + current effective value */
+			info->is_v_a_rms_value = TRUE;
+		break;
+	default:
+		sr_dbg("Invalid function bytes: %02x %02x.", buf[0], buf[1]);
+		break;
+	}
+
+	/* Byte 2: Range */
+
+	/* Byte 3-7: Main display digits */
+
+	/* Byte 8-12: Auxiliary display digits */
+
+	/* Byte 13: TODO: "Simulate strip tens digit". */
+
+	/* Byte 14: TODO: "Simulate strip the single digit". */
+
+	/* Byte 15: Status */
+	info->is_sign2        = (buf[15] & (1 << 3)) != 0;
+	info->is_sign1        = (buf[15] & (1 << 2)) != 0;
+	info->is_batt         = (buf[15] & (1 << 1)) != 0; /* Bat. low */
+	info->is_ol1          = (buf[15] & (1 << 0)) != 0; /* Overflow (main display) */
+
+	/* Byte 16: Option 1 */
+	info->is_max          = (buf[16] & (1 << 3)) != 0;
+	info->is_min          = (buf[16] & (1 << 2)) != 0;
+	info->is_maxmin       = (buf[16] & (1 << 1)) != 0;
+	info->is_rel          = (buf[16] & (1 << 0)) != 0;
+
+	/* Byte 17: Option 2 */
+	info->is_ol2          = (buf[17] & (1 << 3)) != 0;
+	info->is_open         = (buf[17] & (1 << 2)) != 0;
+	info->is_manu         = (buf[17] & (1 << 1)) != 0; /* Manual mode */
+	info->is_hold         = (buf[17] & (1 << 0)) != 0; /* Hold */
+
+	/* Byte 18: Option 3 */
+	info->is_light        = (buf[18] & (1 << 3)) != 0;
+	info->is_usb          = (buf[18] & (1 << 2)) != 0; /* Always on */
+	info->is_warning      = (buf[18] & (1 << 1)) != 0; /* Never seen? */
+	info->is_auto_power   = (buf[18] & (1 << 0)) != 0; /* Always on */
+
+	/* Byte 19: Option 4 */
+	info->is_misplug_warn = (buf[19] & (1 << 3)) != 0; /* Never gets set? */
+	info->is_lo           = (buf[19] & (1 << 2)) != 0;
+	info->is_hi           = (buf[19] & (1 << 1)) != 0;
+	info->is_open2        = (buf[19] & (1 << 0)) != 0; /* TODO: Unknown. */
+
+	/* Byte 20: Dual display bit */
+	info->is_dual_display = (buf[20] & (1 << 0)) != 0;
+
+	/* Byte 21: Always '\r' (carriage return, 0x0d, 13) */
+
+	/* Byte 22: Always '\n' (newline, 0x0a, 10) */
+
+	info->is_auto = !info->is_manu;
+}
+
+static void handle_flags(struct sr_datafeed_analog_old *analog,
+			 float *floatval, const struct vc870_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) {
+		analog->mq = SR_MQ_TEMPERATURE;
+		analog->unit = SR_UNIT_CELSIUS;
+		/* TODO: Handle Fahrenheit in auxiliary display. */
+		// analog->unit = SR_UNIT_FAHRENHEIT;
+	}
+	if (info->is_continuity) {
+		analog->mq = SR_MQ_CONTINUITY;
+		analog->unit = SR_UNIT_BOOLEAN;
+		/* Vendor docs: "< 20 Ohm acoustic" */
+		*floatval = (*floatval < 0.0 || *floatval > 20.0) ? 0.0 : 1.0;
+	}
+	if (info->is_diode) {
+		analog->mq = SR_MQ_VOLTAGE;
+		analog->unit = SR_UNIT_VOLT;
+	}
+	if (info->is_loop_current) {
+		/* 4mA = 0%, 20mA = 100% */
+		analog->mq = SR_MQ_CURRENT;
+		analog->unit = SR_UNIT_PERCENTAGE;
+	}
+	if (info->is_power) {
+		analog->mq = SR_MQ_POWER;
+		analog->unit = SR_UNIT_WATT;
+	}
+	if (info->is_power_apparent_power) {
+		analog->mq = SR_MQ_POWER;
+		analog->unit = SR_UNIT_WATT;
+		/* TODO: Handle apparent power. */
+		// analog->mq = SR_MQ_APPARENT_POWER;
+		// analog->unit = SR_UNIT_VOLT_AMPERE;
+	}
+	if (info->is_power_factor_freq) {
+		analog->mq = SR_MQ_POWER_FACTOR;
+		analog->unit = SR_UNIT_UNITLESS;
+		/* TODO: Handle frequency. */
+		// analog->mq = SR_MQ_FREQUENCY;
+		// analog->unit = SR_UNIT_HERTZ;
+	}
+	if (info->is_v_a_rms_value) {
+		analog->mqflags |= SR_MQFLAG_RMS;
+		analog->mq = SR_MQ_VOLTAGE;
+		analog->unit = SR_UNIT_VOLT;
+		/* TODO: Handle effective current value */
+		// analog->mq = SR_MQ_CURRENT;
+		// analog->unit = SR_UNIT_AMPERE;
+	}
+
+	/* 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_diode)
+		analog->mqflags |= SR_MQFLAG_DIODE;
+	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_batt)
+		sr_spew("Battery is low.");
+	if (info->is_auto_power)
+		sr_spew("Auto-Power-Off enabled.");
+}
+
+static gboolean flags_valid(const struct vc870_info *info)
+{
+	(void)info;
+
+	/* TODO: Implement. */
+	return TRUE;
+}
+
+SR_PRIV gboolean sr_vc870_packet_valid(const uint8_t *buf)
+{
+	struct vc870_info info;
+
+	/* Byte 21: Always '\r' (carriage return, 0x0d, 13) */
+	/* Byte 22: Always '\n' (newline, 0x0a, 10) */
+	if (buf[21] != '\r' || buf[22] != '\n')
+		return FALSE;
+
+	parse_flags(buf, &info);
+
+	return flags_valid(&info);
+}
+
+SR_PRIV int sr_vc870_parse(const uint8_t *buf, float *floatval,
+			   struct sr_datafeed_analog_old *analog, void *info)
+{
+	int ret;
+	struct vc870_info *info_local;
+
+	info_local = (struct vc870_info *)info;
+	memset(info_local, 0, sizeof(struct vc870_info));
+
+	if (!sr_vc870_packet_valid(buf))
+		return SR_ERR;
+
+	parse_flags(buf, info_local);
+
+	if ((ret = parse_value(buf, info_local, floatval)) != SR_OK) {
+		sr_dbg("Error parsing value: %d.", ret);
+		return ret;
+	}
+
+	if ((ret = parse_range(buf[2], floatval, info_local)) != SR_OK)
+		return ret;
+
+	handle_flags(analog, floatval, info_local);
+
+	return SR_OK;
+}
diff --git a/src/drivers.c b/src/drivers.c
new file mode 100644
index 0000000..702228d
--- /dev/null
+++ b/src/drivers.c
@@ -0,0 +1,351 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#ifdef HAVE_HW_AGILENT_DMM
+extern SR_PRIV struct sr_dev_driver agdmm_driver_info;
+#endif
+#ifdef HAVE_HW_APPA_55II
+extern SR_PRIV struct sr_dev_driver appa_55ii_driver_info;
+#endif
+#ifdef HAVE_HW_ASIX_SIGMA
+extern SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
+#endif
+#ifdef HAVE_HW_ATTEN_PPS3XXX
+extern SR_PRIV struct sr_dev_driver atten_pps3203_driver_info;
+#endif
+#ifdef HAVE_HW_BAYLIBRE_ACME
+extern SR_PRIV struct sr_dev_driver baylibre_acme_driver_info;
+#endif
+#ifdef HAVE_HW_BEAGLELOGIC
+extern SR_PRIV struct sr_dev_driver beaglelogic_driver_info;
+#endif
+#ifdef HAVE_HW_BRYMEN_BM86X
+extern SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info;
+#endif
+#ifdef HAVE_HW_BRYMEN_DMM
+extern SR_PRIV struct sr_dev_driver brymen_bm857_driver_info;
+#endif
+#ifdef HAVE_HW_CEM_DT_885X
+extern SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info;
+#endif
+#ifdef HAVE_HW_CENTER_3XX
+extern SR_PRIV struct sr_dev_driver center_309_driver_info;
+extern SR_PRIV struct sr_dev_driver voltcraft_k204_driver_info;
+#endif
+#ifdef HAVE_HW_CHRONOVU_LA
+extern SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
+#endif
+#ifdef HAVE_HW_COLEAD_SLM
+extern SR_PRIV struct sr_dev_driver colead_slm_driver_info;
+#endif
+#ifdef HAVE_HW_CONRAD_DIGI_35_CPU
+extern SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info;
+#endif
+#ifdef HAVE_HW_DEMO
+extern SR_PRIV struct sr_dev_driver demo_driver_info;
+#endif
+#ifdef HAVE_HW_DEREE_DE5000
+extern SR_PRIV struct sr_dev_driver deree_de5000_driver_info;
+#endif
+#ifdef HAVE_HW_FLUKE_DMM
+extern SR_PRIV struct sr_dev_driver flukedmm_driver_info;
+#endif
+#ifdef HAVE_HW_FX2LAFW
+extern SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
+#endif
+#ifdef HAVE_HW_GMC_MH_1X_2X
+extern SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info;
+extern SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info;
+#endif
+#ifdef HAVE_HW_GWINSTEK_GDS_800
+extern SR_PRIV struct sr_dev_driver gwinstek_gds_800_driver_info;
+#endif
+#ifdef HAVE_HW_HAMEG_HMO
+extern SR_PRIV struct sr_dev_driver hameg_hmo_driver_info;
+#endif
+#ifdef HAVE_HW_HANTEK_DSO
+extern SR_PRIV struct sr_dev_driver hantek_dso_driver_info;
+#endif
+#ifdef HAVE_HW_HUNG_CHANG_DSO_2100
+extern SR_PRIV struct sr_dev_driver hung_chang_dso_2100_driver_info;
+#endif
+#ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
+extern SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info;
+#endif
+#ifdef HAVE_HW_IKALOGIC_SCANAPLUS
+extern SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info;
+#endif
+#ifdef HAVE_HW_KECHENG_KC_330B
+extern SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info;
+#endif
+#ifdef HAVE_HW_KERN_SCALE
+extern SR_PRIV struct sr_dev_driver *kern_scale_drivers[];
+#endif
+#ifdef HAVE_HW_KORAD_KAXXXXP
+extern SR_PRIV struct sr_dev_driver korad_kaxxxxp_driver_info;
+#endif
+#ifdef HAVE_HW_LASCAR_EL_USB
+extern SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info;
+#endif
+#ifdef HAVE_HW_LECROY_LOGICSTUDIO
+extern SR_PRIV struct sr_dev_driver lecroy_logicstudio_driver_info;
+#endif
+#ifdef HAVE_HW_LINK_MSO19
+extern SR_PRIV struct sr_dev_driver link_mso19_driver_info;
+#endif
+#ifdef HAVE_HW_MANSON_HCS_3XXX
+extern SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info;
+#endif
+#ifdef HAVE_HW_MAYNUO_M97
+extern SR_PRIV struct sr_dev_driver maynuo_m97_driver_info;
+#endif
+#ifdef HAVE_HW_MIC_985XX
+extern SR_PRIV struct sr_dev_driver mic_98581_driver_info;
+extern SR_PRIV struct sr_dev_driver mic_98583_driver_info;
+#endif
+#ifdef HAVE_HW_MOTECH_LPS_30X
+extern SR_PRIV struct sr_dev_driver motech_lps_301_driver_info;
+#endif
+#ifdef HAVE_HW_NORMA_DMM
+extern SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
+extern SR_PRIV struct sr_dev_driver siemens_b102x_driver_info;
+#endif
+#ifdef HAVE_HW_OPENBENCH_LOGIC_SNIFFER
+extern SR_PRIV struct sr_dev_driver ols_driver_info;
+#endif
+#ifdef HAVE_HW_PIPISTRELLO_OLS
+extern SR_PRIV struct sr_dev_driver p_ols_driver_info;
+#endif
+#ifdef HAVE_HW_RIGOL_DS
+extern SR_PRIV struct sr_dev_driver rigol_ds_driver_info;
+#endif
+#ifdef HAVE_HW_SALEAE_LOGIC16
+extern SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
+#endif
+#ifdef HAVE_HW_SCPI_PPS
+extern SR_PRIV struct sr_dev_driver scpi_pps_driver_info;
+#endif
+#ifdef HAVE_HW_SERIAL_DMM
+extern SR_PRIV struct sr_dev_driver *serial_dmm_drivers[];
+#endif
+#ifdef HAVE_HW_SYSCLK_LWLA
+extern SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info;
+#endif
+#ifdef HAVE_HW_TELEINFO
+extern SR_PRIV struct sr_dev_driver teleinfo_driver_info;
+#endif
+#ifdef HAVE_HW_TESTO
+extern SR_PRIV struct sr_dev_driver testo_driver_info;
+#endif
+#ifdef HAVE_HW_TONDAJ_SL_814
+extern SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info;
+#endif
+#ifdef HAVE_HW_UNI_T_DMM
+extern SR_PRIV struct sr_dev_driver *uni_t_dmm_drivers[];
+#endif
+#ifdef HAVE_HW_UNI_T_UT32X
+extern SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info;
+#endif
+#ifdef HAVE_HW_VICTOR_DMM
+extern SR_PRIV struct sr_dev_driver victor_dmm_driver_info;
+#endif
+#ifdef HAVE_HW_YOKOGAWA_DLM
+extern SR_PRIV struct sr_dev_driver yokogawa_dlm_driver_info;
+#endif
+#ifdef HAVE_HW_ZEROPLUS_LOGIC_CUBE
+extern SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info;
+#endif
+
+#define DRVS struct sr_dev_driver *[]
+
+SR_PRIV struct sr_dev_driver **drivers_lists[] = {
+#ifdef HAVE_HW_AGILENT_DMM
+	(DRVS) {&agdmm_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_APPA_55II
+	(DRVS) {&appa_55ii_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_ASIX_SIGMA
+	(DRVS) {&asix_sigma_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_ATTEN_PPS3XXX
+	(DRVS) {&atten_pps3203_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_BAYLIBRE_ACME
+	(DRVS) {&baylibre_acme_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_BEAGLELOGIC
+	(DRVS) {&beaglelogic_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_BRYMEN_BM86X
+	(DRVS) {&brymen_bm86x_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_BRYMEN_DMM
+	(DRVS) {&brymen_bm857_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_CEM_DT_885X
+	(DRVS) {&cem_dt_885x_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_CENTER_3XX
+	(DRVS) {
+		&center_309_driver_info,
+		&voltcraft_k204_driver_info,
+		NULL
+	},
+#endif
+#ifdef HAVE_HW_CHRONOVU_LA
+	(DRVS) {&chronovu_la_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_COLEAD_SLM
+	(DRVS) {&colead_slm_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_CONRAD_DIGI_35_CPU
+	(DRVS) {&conrad_digi_35_cpu_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_DEMO
+	(DRVS) {&demo_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_DEREE_DE5000
+	(DRVS) {&deree_de5000_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_FLUKE_DMM
+	(DRVS) {&flukedmm_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_FX2LAFW
+	(DRVS) {&fx2lafw_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_GMC_MH_1X_2X
+	(DRVS) {
+		&gmc_mh_1x_2x_rs232_driver_info,
+		&gmc_mh_2x_bd232_driver_info,
+		NULL
+	},
+#endif
+#ifdef HAVE_HW_GWINSTEK_GDS_800
+	(DRVS) {&gwinstek_gds_800_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_HAMEG_HMO
+	(DRVS) {&hameg_hmo_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_HANTEK_DSO
+	(DRVS) {&hantek_dso_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_HUNG_CHANG_DSO_2100
+	(DRVS) {&hung_chang_dso_2100_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
+	(DRVS) {&ikalogic_scanalogic2_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_IKALOGIC_SCANAPLUS
+	(DRVS) {&ikalogic_scanaplus_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_KECHENG_KC_330B
+	(DRVS) {&kecheng_kc_330b_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_KERN_SCALE
+	kern_scale_drivers,
+#endif
+#ifdef HAVE_HW_KORAD_KAXXXXP
+	(DRVS) {&korad_kaxxxxp_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_LASCAR_EL_USB
+	(DRVS) {&lascar_el_usb_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_LECROY_LOGICSTUDIO
+	(DRVS) {&lecroy_logicstudio_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_LINK_MSO19
+	(DRVS) {&link_mso19_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_MANSON_HCS_3XXX
+	(DRVS) {&manson_hcs_3xxx_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_MAYNUO_M97
+	(DRVS) {&maynuo_m97_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_MIC_985XX
+	(DRVS) {
+		&mic_98581_driver_info,
+		&mic_98583_driver_info,
+		NULL
+	},
+#endif
+#ifdef HAVE_HW_MOTECH_LPS_30X
+	(DRVS) {&motech_lps_301_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_NORMA_DMM
+	(DRVS) {
+		&norma_dmm_driver_info,
+		&siemens_b102x_driver_info,
+		NULL
+	},
+#endif
+#ifdef HAVE_HW_OPENBENCH_LOGIC_SNIFFER
+	(DRVS) {&ols_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_PIPISTRELLO_OLS
+	(DRVS) {&p_ols_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_RIGOL_DS
+	(DRVS) {&rigol_ds_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_SALEAE_LOGIC16
+	(DRVS) {&saleae_logic16_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_SCPI_PPS
+	(DRVS) {&scpi_pps_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_SERIAL_DMM
+	serial_dmm_drivers,
+#endif
+#ifdef HAVE_HW_SYSCLK_LWLA
+	(DRVS) {&sysclk_lwla_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_TELEINFO
+	(DRVS) {&teleinfo_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_TESTO
+	(DRVS) {&testo_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_TONDAJ_SL_814
+	(DRVS) {&tondaj_sl_814_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_UNI_T_DMM
+	uni_t_dmm_drivers,
+#endif
+#ifdef HAVE_HW_UNI_T_UT32X
+	(DRVS) {&uni_t_ut32x_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_VICTOR_DMM
+	(DRVS) {&victor_dmm_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_YOKOGAWA_DLM
+	(DRVS) {&yokogawa_dlm_driver_info, NULL},
+#endif
+#ifdef HAVE_HW_ZEROPLUS_LOGIC_CUBE
+	(DRVS) {&zeroplus_logic_cube_driver_info, NULL},
+#endif
+	NULL,
+};
+/** @endcond */
diff --git a/error.c b/src/error.c
similarity index 93%
rename from error.c
rename to src/error.c
index f877975..fcdfe5d 100644
--- a/error.c
+++ b/src/error.c
@@ -18,7 +18,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
-#include "libsigrok.h"
+#include <config.h>
+#include <libsigrok/libsigrok.h>
 
 /**
  * @file
@@ -78,6 +79,10 @@ SR_API const char *sr_strerror(int error_code)
 		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:
 		return "unknown error";
 	}
@@ -129,6 +134,10 @@ SR_API const char *sr_strerror_name(int error_code)
 		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:
 		return "unknown error code";
 	}
diff --git a/hardware/common/ezusb.c b/src/ezusb.c
similarity index 71%
rename from hardware/common/ezusb.c
rename to src/ezusb.c
index 044b464..c2d270c 100644
--- a/hardware/common/ezusb.c
+++ b/src/ezusb.c
@@ -21,17 +21,20 @@
  * Helper functions for the Cypress EZ-USB / FX2 series chips.
  */
 
+#include <config.h>
 #include <libusb.h>
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "ezusb"
 
+#define FW_CHUNKSIZE (4 * 1024)
+
 SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear)
 {
 	int ret;
@@ -49,46 +52,51 @@ SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear)
 	return ret;
 }
 
-SR_PRIV int ezusb_install_firmware(libusb_device_handle *hdl,
-				   const char *filename)
+SR_PRIV int ezusb_install_firmware(struct sr_context *ctx,
+				   libusb_device_handle *hdl,
+				   const char *name)
 {
-	FILE *fw;
-	int offset, chunksize, ret, result;
-	unsigned char buf[4096];
-
-	sr_info("Uploading firmware at %s", filename);
-	if ((fw = g_fopen(filename, "rb")) == NULL) {
-		sr_err("Unable to open firmware file %s for reading: %s",
-		       filename, strerror(errno));
+	unsigned char *firmware;
+	size_t length, offset, chunksize;
+	int ret, result;
+
+	/* Max size is 64 kiB since the value field of the setup packet,
+	 * which holds the firmware offset, is only 16 bit wide.
+	 */
+	firmware = sr_resource_load(ctx, SR_RESOURCE_FIRMWARE,
+			name, &length, 1 << 16);
+	if (!firmware)
 		return SR_ERR;
-	}
+
+	sr_info("Uploading firmware '%s'.", name);
 
 	result = SR_OK;
 	offset = 0;
-	while (1) {
-		chunksize = fread(buf, 1, 4096, fw);
-		if (chunksize == 0)
-			break;
+	while (offset < length) {
+		chunksize = MIN(length - offset, FW_CHUNKSIZE);
+
 		ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR |
 					      LIBUSB_ENDPOINT_OUT, 0xa0, offset,
-					      0x0000, buf, chunksize, 100);
+					      0x0000, firmware + offset,
+					      chunksize, 100);
 		if (ret < 0) {
 			sr_err("Unable to send firmware to device: %s.",
 					libusb_error_name(ret));
-			result = SR_ERR;
-			break;
+			g_free(firmware);
+			return SR_ERR;
 		}
-		sr_info("Uploaded %d bytes", chunksize);
+		sr_info("Uploaded %zu bytes.", chunksize);
 		offset += chunksize;
 	}
-	fclose(fw);
-	sr_info("Firmware upload done");
+	g_free(firmware);
+
+	sr_info("Firmware upload done.");
 
 	return result;
 }
 
-SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
-				  const char *filename)
+SR_PRIV int ezusb_upload_firmware(struct sr_context *ctx, libusb_device *dev,
+				  int configuration, const char *name)
 {
 	struct libusb_device_handle *hdl;
 	int ret;
@@ -102,7 +110,7 @@ SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
 	}
 
 /*
- * The libusbx darwin backend is broken: it can report a kernel driver being
+ * The libusb Darwin backend is broken: it can report a kernel driver being
  * active, but detaching it always returns an error.
  */
 #if !defined(__APPLE__)
@@ -124,7 +132,7 @@ SR_PRIV int ezusb_upload_firmware(libusb_device *dev, int configuration,
 	if ((ezusb_reset(hdl, 1)) < 0)
 		return SR_ERR;
 
-	if (ezusb_install_firmware(hdl, filename) < 0)
+	if (ezusb_install_firmware(ctx, hdl, name) < 0)
 		return SR_ERR;
 
 	if ((ezusb_reset(hdl, 0)) < 0)
diff --git a/hardware/conrad-digi-35-cpu/protocol.h b/src/fallback.c
similarity index 60%
copy from hardware/conrad-digi-35-cpu/protocol.h
copy to src/fallback.c
index fcde852..9a4a536 100644
--- a/hardware/conrad-digi-35-cpu/protocol.h
+++ b/src/fallback.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2014 Matthias Heidbrink <m-sigrok at heidbrink.biz>
+ * Copyright (C) 2014 Aurelien Jacobs <aurel at 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
@@ -17,24 +17,22 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @file
- * <em>Conrad DIGI 35 CPU</em> power supply driver
- * @internal
- */
+#include <config.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
 
-#ifndef LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
+#ifndef HAVE_LIBSERIALPORT
 
-#include <stdint.h>
-#include <errno.h>
-#include <string.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
+SR_API GSList *sr_serial_list(const struct sr_dev_driver *driver)
+{
+	(void)driver;
 
-#define LOG_PREFIX "conrad-digi-35-cpu"
+	return NULL;
+}
 
-SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param);
+SR_API void sr_serial_free(struct sr_serial_port *serial)
+{
+	(void)serial;
+}
 
 #endif
diff --git a/hardware/agilent-dmm/agilent-dmm.h b/src/hardware/agilent-dmm/agilent-dmm.h
similarity index 89%
rename from hardware/agilent-dmm/agilent-dmm.h
rename to src/hardware/agilent-dmm/agilent-dmm.h
index 2277111..77a4a3d 100644
--- a/hardware/agilent-dmm/agilent-dmm.h
+++ b/src/hardware/agilent-dmm/agilent-dmm.h
@@ -24,14 +24,21 @@
 
 #define AGDMM_BUFSIZE  256
 
+/* Always USB-serial, 1ms is plenty. */
+#define SERIAL_WRITE_TIMEOUT_MS 1
+
 /* Supported models */
 enum {
-	AGILENT_U1231A = 1,
-	AGILENT_U1232A,
-	AGILENT_U1233A,
-	AGILENT_U1251A,
-	AGILENT_U1252A,
-	AGILENT_U1253A,
+	AGILENT_U1231 = 1,
+	AGILENT_U1232,
+	AGILENT_U1233,
+
+	AGILENT_U1241,
+	AGILENT_U1242,
+
+	AGILENT_U1251,
+	AGILENT_U1252,
+	AGILENT_U1253,
 };
 
 /* Supported device profiles */
diff --git a/hardware/agilent-dmm/api.c b/src/hardware/agilent-dmm/api.c
similarity index 62%
rename from hardware/agilent-dmm/api.c
rename to src/hardware/agilent-dmm/api.c
index ccc2f65..82e4378 100644
--- a/hardware/agilent-dmm/api.c
+++ b/src/hardware/agilent-dmm/api.c
@@ -17,31 +17,34 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <glib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <string.h>
-#include <errno.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "agilent-dmm.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t drvopts[] = {
 	SR_CONF_MULTIMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
-	SR_CONF_CONTINUOUS,
 };
 
-extern const struct agdmm_job agdmm_jobs_u123x[];
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+};
+
+extern const struct agdmm_job agdmm_jobs_u12xx[];
 extern const struct agdmm_recv agdmm_recvs_u123x[];
-extern const struct agdmm_job agdmm_jobs_u125x[];
+extern const struct agdmm_recv agdmm_recvs_u124x[];
 extern const struct agdmm_recv agdmm_recvs_u125x[];
 
 /* This works on all the Agilent U12xxA series, although the
@@ -49,37 +52,44 @@ extern const struct agdmm_recv agdmm_recvs_u125x[];
 #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 }
+	{ AGILENT_U1231, "U1231A", agdmm_jobs_u12xx, agdmm_recvs_u123x },
+	{ AGILENT_U1232, "U1232A", agdmm_jobs_u12xx, agdmm_recvs_u123x },
+	{ AGILENT_U1233, "U1233A", agdmm_jobs_u12xx, agdmm_recvs_u123x },
+
+	{ AGILENT_U1241, "U1241A", agdmm_jobs_u12xx, agdmm_recvs_u124x },
+	{ AGILENT_U1242, "U1242A", agdmm_jobs_u12xx, agdmm_recvs_u124x },
+	{ AGILENT_U1241, "U1241B", agdmm_jobs_u12xx, agdmm_recvs_u124x },
+	{ AGILENT_U1242, "U1242B", agdmm_jobs_u12xx, agdmm_recvs_u124x },
+
+	{ AGILENT_U1251, "U1251A", agdmm_jobs_u12xx, agdmm_recvs_u125x },
+	{ AGILENT_U1252, "U1252A", agdmm_jobs_u12xx, agdmm_recvs_u125x },
+	{ AGILENT_U1253, "U1253A", agdmm_jobs_u12xx, agdmm_recvs_u125x },
+	{ AGILENT_U1251, "U1251B", agdmm_jobs_u12xx, agdmm_recvs_u125x },
+	{ AGILENT_U1252, "U1252B", agdmm_jobs_u12xx, agdmm_recvs_u125x },
+	{ AGILENT_U1253, "U1253B", agdmm_jobs_u12xx, agdmm_recvs_u125x },
+	ALL_ZERO
 };
 
 SR_PRIV struct sr_dev_driver agdmm_driver_info;
-static struct sr_dev_driver *di = &agdmm_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_config *src;
-	struct sr_channel *ch;
 	struct sr_serial_dev_inst *serial;
 	GSList *l, *devices;
 	int len, i;
 	const char *conn, *serialcomm;
 	char *buf, **tokens;
 
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	devices = NULL;
@@ -100,25 +110,20 @@ static GSList *scan(GSList *options)
 	if (!serialcomm)
 		serialcomm = SERIALCOMM;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != 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));
+	if (serial_write_blocking(serial, "*IDN?\r\n", 7, SERIAL_WRITE_TIMEOUT_MS) < 7) {
+		sr_err("Unable to send identification string.");
 		return NULL;
 	}
 
 	len = 128;
-	if (!(buf = g_try_malloc(len))) {
-		sr_err("Serial buffer malloc failed.");
-		return NULL;
-	}
-	serial_readline(serial, &buf, &len, 150);
+	buf = g_malloc(len);
+	serial_readline(serial, &buf, &len, 250);
 	if (!len)
 		return NULL;
 
@@ -128,22 +133,19 @@ static GSList *scan(GSList *options)
 		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, "Agilent",
-					tokens[1], tokens[3])))
-				return NULL;
-			if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-				sr_err("Device context malloc failed.");
-				return NULL;
-			}
+			sdi = g_malloc0(sizeof(struct sr_dev_inst));
+			sdi->status = SR_ST_INACTIVE;
+			sdi->vendor = g_strdup("Agilent");
+			sdi->model = g_strdup(tokens[1]);
+			sdi->version = g_strdup(tokens[3]);
+			devc = g_malloc0(sizeof(struct dev_context));
 			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 (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 			drvc->instances = g_slist_append(drvc->instances, sdi);
 			devices = g_slist_append(devices, sdi);
 			break;
@@ -159,17 +161,17 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -184,21 +186,13 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		return SR_ERR_BUG;
 	}
 
-	switch (id) {
+	switch (key) {
 	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;
@@ -207,7 +201,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -215,12 +209,16 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -249,7 +247,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 	/* Poll every 100ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 100, agdmm_receive_data, (void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 100,
+			agdmm_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -276,5 +275,5 @@ SR_PRIV struct sr_dev_driver agdmm_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/agilent-dmm/sched.c b/src/hardware/agilent-dmm/sched.c
similarity index 68%
rename from hardware/agilent-dmm/sched.c
rename to src/hardware/agilent-dmm/sched.c
index bd9cb38..e5c0cc5 100644
--- a/hardware/agilent-dmm/sched.c
+++ b/src/hardware/agilent-dmm/sched.c
@@ -17,14 +17,14 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #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>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "agilent-dmm.h"
 
 static void dispatch(const struct sr_dev_inst *sdi)
 {
@@ -105,8 +105,8 @@ SR_PRIV int agdmm_receive_data(int fd, int revents, void *cb_data)
 	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);
+		while (AGDMM_BUFSIZE - devc->buflen - 1 > 0) {
+			len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
 			if (len < 1)
 				break;
 			devc->buflen += len;
@@ -137,11 +137,11 @@ static int agdmm_send(const struct sr_dev_inst *sdi, const char *cmd)
 	sr_spew("Sending '%s'.", cmd);
 	strncpy(buf, cmd, 28);
 	if (!strncmp(buf, "*IDN?", 5))
-		strncat(buf, "\r\n", 32);
+		strcat(buf, "\r\n");
 	else
-		strncat(buf, "\n\r\n", 32);
-	if (serial_write(serial, buf, strlen(buf)) == -1) {
-		sr_err("Failed to send: %s.", strerror(errno));
+		strcat(buf, "\n\r\n");
+	if (serial_write_blocking(serial, buf, strlen(buf), SERIAL_WRITE_TIMEOUT_MS) < (int)strlen(buf)) {
+		sr_err("Failed to send.");
 		return SR_ERR;
 	}
 
@@ -197,6 +197,38 @@ static int recv_stat_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 	return SR_OK;
 }
 
+static int recv_stat_u124x(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;
+
+	/* 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 recv_stat_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 {
 	struct dev_context *devc;
@@ -232,8 +264,9 @@ 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;
+	struct sr_datafeed_analog_old analog;
 	float fvalue;
+	const char *s;
 	char *mstr;
 
 	sr_spew("FETC reply '%s'.", g_match_info_get_string(match));
@@ -245,16 +278,17 @@ static int recv_fetc(const struct sr_dev_inst *sdi, GMatchInfo *match)
 		 * get metadata soon enough. */
 		return SR_OK;
 
-	if (!strcmp(g_match_info_get_string(match), "+9.90000000E+37")) {
+	s = g_match_info_get_string(match);
+	if (!strcmp(s, "-9.90000000E+37") || !strcmp(s, "+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);
-		if (sr_atof_ascii(mstr, &fvalue) != SR_OK || fvalue == 0.0) {
+		if (sr_atof_ascii(mstr, &fvalue) != SR_OK) {
 			g_free(mstr);
-			sr_err("Invalid float.");
+			sr_dbg("Invalid float.");
 			return SR_ERR;
 		}
 		g_free(mstr);
@@ -262,14 +296,14 @@ static int recv_fetc(const struct sr_dev_inst *sdi, GMatchInfo *match)
 			fvalue /= devc->cur_divider;
 	}
 
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 	analog.mq = devc->cur_mq;
 	analog.unit = devc->cur_unit;
 	analog.mqflags = devc->cur_mqflags;
 	analog.channels = sdi->channels;
 	analog.num_samples = 1;
 	analog.data = &fvalue;
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 
@@ -296,11 +330,11 @@ static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 		devc->cur_unit = SR_UNIT_VOLT;
 		devc->cur_mqflags = 0;
 		devc->cur_divider = 0;
-	} else if(!strcmp(mstr, "MV")) {
+	} 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. */
+			/* No way to detect whether Fahrenheit or Celsius
+			 * is used, so we'll just default to Celsius. */
 			devc->cur_unit = SR_UNIT_CELSIUS;
 		devc->cur_mqflags = 0;
 		devc->cur_divider = 0;
@@ -310,22 +344,22 @@ static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 			devc->cur_mqflags = 0;
 			devc->cur_divider = 1000;
 		}
-	} else if(!strcmp(mstr, "A")) {
+	} 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")) {
+	} 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")) {
+	} 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")) {
+	} else if (!strcmp(mstr, "RES")) {
 		if (devc->mode_continuity) {
 			devc->cur_mq = SR_MQ_CONTINUITY;
 			devc->cur_unit = SR_UNIT_BOOLEAN;
@@ -335,7 +369,7 @@ static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 		}
 		devc->cur_mqflags = 0;
 		devc->cur_divider = 0;
-	} else if(!strcmp(mstr, "CAP")) {
+	} else if (!strcmp(mstr, "CAP")) {
 		devc->cur_mq = SR_MQ_CAPACITANCE;
 		devc->cur_unit = SR_UNIT_FARAD;
 		devc->cur_mqflags = 0;
@@ -347,12 +381,15 @@ static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 	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"))
+		if (!strcmp(mstr, "AC")) {
 			devc->cur_mqflags |= SR_MQFLAG_AC;
-		else if (!strcmp(mstr, "DC"))
+			if (devc->cur_mq == SR_MQ_VOLTAGE)
+				devc->cur_mqflags |= SR_MQFLAG_RMS;
+		} else if (!strcmp(mstr, "DC")) {
 			devc->cur_mqflags |= SR_MQFLAG_DC;
-		else
-			sr_dbg("Unknown third argument.");
+		} else {
+		sr_dbg("Unknown first argument '%s'.", mstr);
+		}
 		g_free(mstr);
 	} else
 		devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
@@ -360,10 +397,10 @@ static int recv_conf_u123x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 	return SR_OK;
 }
 
-static int recv_conf_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
+static int recv_conf_u124x_5x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 {
 	struct dev_context *devc;
-	char *mstr;
+	char *mstr, *m2;
 
 	sr_spew("CONF? response '%s'.", g_match_info_get_string(match));
 	devc = sdi->priv;
@@ -374,38 +411,72 @@ static int recv_conf_u125x(const struct sr_dev_inst *sdi, GMatchInfo *match)
 		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"))
+			if (!strncmp(mstr + 5, "AC", 2)) {
+				devc->cur_mqflags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+			} else if (!strncmp(mstr + 5, "DC", 2)) {
 				devc->cur_mqflags |= SR_MQFLAG_DC;
-			else
-				/* "ACDC" appears as well, no idea what it means. */
+			} else if (!strncmp(mstr + 5, "ACDC", 4)) {
+				/* AC + DC offset */
+				devc->cur_mqflags |= SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS;
+			} else {
 				devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
+			}
 		} else
 			devc->cur_mqflags &= ~(SR_MQFLAG_AC | SR_MQFLAG_DC);
-	} else if(!strcmp(mstr, "CURR")) {
+	} 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;
-		}
+	} else if (!strcmp(mstr, "RES")) {
+		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.");
+	} 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 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, "CONT")) {
+		devc->cur_mq = SR_MQ_CONTINUITY;
+		devc->cur_unit = SR_UNIT_BOOLEAN;
+		devc->cur_mqflags = 0;
+		devc->cur_divider = 0;
+	} else if (!strncmp(mstr, "T1", 2) || !strncmp(mstr, "T2", 2)) {
+		devc->cur_mq = SR_MQ_TEMPERATURE;
+		m2 = g_match_info_fetch(match, 2);
+		if (!strcmp(m2, "FAR"))
+			devc->cur_unit = SR_UNIT_FAHRENHEIT;
+		else
+			devc->cur_unit = SR_UNIT_CELSIUS;
+		g_free(m2);
+		devc->cur_mqflags = 0;
+		devc->cur_divider = 0;
+	} else if (!strcmp(mstr, "SCOU")) {
+		/*
+		 * Switch counter, not supported. Not sure what values
+		 * come from FETC in this mode, or how they would map
+		 * into libsigrok.
+		 */
+	} else if (!strncmp(mstr, "CPER:", 5)) {
+		devc->cur_mq = SR_MQ_CURRENT;
+		devc->cur_unit = SR_UNIT_PERCENTAGE;
+		devc->cur_mqflags = 0;
+		devc->cur_divider = 0;
+	} else {
+		sr_dbg("Unknown first argument '%s'.", mstr);
+	}
 	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;
@@ -414,7 +485,7 @@ static int recv_conf(const struct sr_dev_inst *sdi, GMatchInfo *match)
 	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")) {
+	if (!strcmp(mstr, "DIOD")) {
 		devc->cur_mq = SR_MQ_VOLTAGE;
 		devc->cur_unit = SR_UNIT_VOLT;
 		devc->cur_mqflags = SR_MQFLAG_DIODE;
@@ -439,11 +510,12 @@ static int recv_switch(const struct sr_dev_inst *sdi, GMatchInfo *match)
 	return SR_OK;
 }
 
-SR_PRIV const struct agdmm_job agdmm_jobs_u123x[] = {
+/* Poll keys/switches and values at 7Hz, mode at 1Hz. */
+SR_PRIV const struct agdmm_job agdmm_jobs_u12xx[] = {
 	{ 143, send_stat },
 	{ 1000, send_conf },
 	{ 143, send_fetc },
-	{ 0, NULL }
+	ALL_ZERO
 };
 
 SR_PRIV const struct agdmm_recv agdmm_recvs_u123x[] = {
@@ -453,22 +525,29 @@ SR_PRIV const struct agdmm_recv agdmm_recvs_u123x[] = {
 	{ "^\"(V|MV|A|UA|FREQ),(\\d),(AC|DC)\"$", recv_conf_u123x },
 	{ "^\"(RES|CAP),(\\d)\"$", recv_conf_u123x},
 	{ "^\"(DIOD)\"$", recv_conf },
-	{ NULL, NULL }
+	ALL_ZERO
 };
 
-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_u124x[] = {
+	{ "^\"(\\d\\d.{18}\\d)\"$", recv_stat_u124x },
+	{ "^\\*([0-9])$", recv_switch },
+	{ "^([-+][0-9]\\.[0-9]{8}E[-+][0-9]{2})$", recv_fetc },
+	{ "^\"(VOLT|CURR|RES|CAP|FREQ) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)\"$", recv_conf_u124x_5x },
+	{ "^\"(VOLT:[ACD]+) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)\"$", recv_conf_u124x_5x },
+	{ "^\"(CPER:[40]-20mA) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)\"$", recv_conf_u124x_5x },
+	{ "^\"(T[0-9]:[A-Z]+) ([A-Z]+)\"$", recv_conf_u124x_5x },
+	{ "^\"(DIOD)\"$", recv_conf },
+	ALL_ZERO
 };
 
 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 },
+	{ "^\"(VOLT|CURR|RES|CAP|FREQ) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)\"$", recv_conf_u124x_5x },
+	{ "^\"(VOLT:[ACD]+) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)\"$", recv_conf_u124x_5x },
+	{ "^\"(CPER:[40]-20mA) ([-+][0-9\\.E\\-+]+),([-+][0-9\\.E\\-+]+)\"$", recv_conf_u124x_5x },
+	{ "^\"(T[0-9]:[A-Z]+) ([A-Z]+)\"$", recv_conf_u124x_5x },
 	{ "^\"(DIOD)\"$", recv_conf },
-	{ NULL, NULL }
+	ALL_ZERO
 };
diff --git a/hardware/appa-55ii/api.c b/src/hardware/appa-55ii/api.c
similarity index 74%
rename from hardware/appa-55ii/api.c
rename to src/hardware/appa-55ii/api.c
index 38c23b7..0ed6144 100644
--- a/hardware/appa-55ii/api.c
+++ b/src/hardware/appa-55ii/api.c
@@ -17,20 +17,24 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include "protocol.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t drvopts[] = {
 	SR_CONF_THERMOMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
-	SR_CONF_CONTINUOUS,
-	SR_CONF_DATA_SOURCE,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
 static const char *data_sources[] = {
@@ -39,20 +43,18 @@ static const char *data_sources[] = {
 };
 
 SR_PRIV struct sr_dev_driver appa_55ii_driver_info;
-static struct sr_dev_driver *di = &appa_55ii_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_serial_dev_inst *serial;
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	struct sr_config *src;
 	GSList *devices, *l;
 	const char *conn, *serialcomm;
@@ -78,14 +80,14 @@ static GSList *scan(GSList *options)
 	if (!serialcomm)
 		serialcomm = "9600/8n1";
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
-	if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
+
+	if (serial_open(serial, SERIAL_RDONLY) != SR_OK)
 		return NULL;
 
 	sr_info("Probing serial port %s.", conn);
 
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 	serial_flush(serial);
 
@@ -96,27 +98,19 @@ static GSList *scan(GSList *options)
 
 	sr_info("Found device on port %s.", conn);
 
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "APPA", "55II", NULL)))
-		goto scan_cleanup;
-
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		goto scan_cleanup;
-	}
-
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("APPA");
+	sdi->model = g_strdup("55II");
+	devc = g_malloc0(sizeof(struct dev_context));
 	devc->data_source = DEFAULT_DATA_SOURCE;
-
 	sdi->inst_type = SR_INST_SERIAL;
 	sdi->conn = serial;
 	sdi->priv = devc;
 	sdi->driver = di;
 
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "T1")))
-		goto scan_cleanup;
-	sdi->channels = g_slist_append(sdi->channels, ch);
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "T2")))
-		goto scan_cleanup;
-	sdi->channels = g_slist_append(sdi->channels, ch);
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "T1");
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "T2");
 
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
@@ -127,17 +121,17 @@ scan_cleanup:
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc = sdi->priv;
@@ -161,7 +155,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -181,11 +175,9 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_samples = g_variant_get_uint64(data);
-		sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
 		break;
 	case SR_CONF_LIMIT_MSEC:
 		devc->limit_msec = g_variant_get_uint64(data);
-		sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
 		break;
 	case SR_CONF_DATA_SOURCE: {
 		tmp_str = g_variant_get_string(data, NULL);
@@ -205,20 +197,23 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
-	(void)sdi;
 	(void)cg;
 
 	switch (key) {
 	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DATA_SOURCE:
 		*data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
@@ -259,7 +254,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	std_session_send_df_header(cb_data, LOG_PREFIX);
 
 	/* Poll every 50ms, or whenever some data comes in. */
-	serial_source_add(serial, G_IO_IN, 50, appa_55ii_receive_data, (void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 50,
+			appa_55ii_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -286,5 +282,5 @@ SR_PRIV struct sr_dev_driver appa_55ii_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/appa-55ii/protocol.c b/src/hardware/appa-55ii/protocol.c
similarity index 95%
rename from hardware/appa-55ii/protocol.c
rename to src/hardware/appa-55ii/protocol.c
index 708262b..8482689 100644
--- a/hardware/appa-55ii/protocol.c
+++ b/src/hardware/appa-55ii/protocol.c
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include <math.h>
 #include "protocol.h"
@@ -91,7 +92,7 @@ static void appa_55ii_live_data(struct sr_dev_inst *sdi, const uint8_t *buf)
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct sr_channel *ch;
 	float values[APPA_55II_NUM_CHANNELS], *val_ptr;
 	int i;
@@ -102,7 +103,7 @@ static void appa_55ii_live_data(struct sr_dev_inst *sdi, const uint8_t *buf)
 		return;
 
 	val_ptr = values;
-	memset(&analog, 0, sizeof(analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 	analog.num_samples = 1;
 	analog.mq = SR_MQ_TEMPERATURE;
 	analog.unit = SR_UNIT_CELSIUS;
@@ -117,7 +118,7 @@ static void appa_55ii_live_data(struct sr_dev_inst *sdi, const uint8_t *buf)
 		*val_ptr++ = appa_55ii_temp(buf, i);
 	}
 
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->session_cb_data, &packet);
 	g_slist_free(analog.channels);
@@ -137,7 +138,7 @@ static void appa_55ii_log_data_parse(struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct sr_channel *ch;
 	float values[APPA_55II_NUM_CHANNELS], *val_ptr;
 	const uint8_t *buf;
@@ -154,7 +155,7 @@ static void appa_55ii_log_data_parse(struct sr_dev_inst *sdi)
 		/* FIXME: Timestamp should be sent in the packet. */
 		sr_dbg("Timestamp: %02d:%02d:%02d", buf[2], buf[3], buf[4]);
 
-		memset(&analog, 0, sizeof(analog));
+		memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 		analog.num_samples = 1;
 		analog.mq = SR_MQ_TEMPERATURE;
 		analog.unit = SR_UNIT_CELSIUS;
@@ -169,7 +170,7 @@ static void appa_55ii_log_data_parse(struct sr_dev_inst *sdi)
 			*val_ptr++ = temp == 0x7FFF ? INFINITY : (float)temp / 10;
 		}
 
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = &analog;
 		sr_session_send(devc->session_cb_data, &packet);
 		g_slist_free(analog.channels);
@@ -277,7 +278,7 @@ SR_PRIV int appa_55ii_receive_data(int fd, int revents, void *cb_data)
 
 	/* Try to get as much data as the buffer can hold. */
 	len = sizeof(devc->buf) - devc->buf_len;
-	len = serial_read(serial, devc->buf + devc->buf_len, len);
+	len = serial_read_nonblocking(serial, devc->buf + devc->buf_len, len);
 	if (len < 1) {
 		sr_err("Serial port read error: %d.", len);
 		return FALSE;
diff --git a/hardware/appa-55ii/protocol.h b/src/hardware/appa-55ii/protocol.h
similarity index 98%
rename from hardware/appa-55ii/protocol.h
rename to src/hardware/appa-55ii/protocol.h
index fa3c247..4e8b129 100644
--- a/hardware/appa-55ii/protocol.h
+++ b/src/hardware/appa-55ii/protocol.h
@@ -22,7 +22,7 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "appa-55ii"
diff --git a/src/hardware/asix-sigma/api.c b/src/hardware/asix-sigma/api.c
new file mode 100644
index 0000000..4ac74bd
--- /dev/null
+++ b/src/hardware/asix-sigma/api.c
@@ -0,0 +1,445 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Håvard Espeland <gus at ping.uio.no>,
+ * Copyright (C) 2010 Martin Stensgård <mastensg at ping.uio.no>
+ * Copyright (C) 2010 Carl Henrik Lunde <chlunde at 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/>.
+ */
+
+/*
+ * ASIX SIGMA/SIGMA2 logic analyzer driver
+ */
+
+#include <config.h>
+#include "protocol.h"
+
+SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
+
+/*
+ * Channel numbers seem to go from 1-16, according to this image:
+ * http://tools.asix.net/img/sigma_sigmacab_pins_720.jpg
+ * (the cable has two additional GND pins, and a TI and TO pin)
+ */
+static const char *channel_names[] = {
+	"1", "2", "3", "4", "5", "6", "7", "8",
+	"9", "10", "11", "12", "13", "14", "15", "16",
+};
+
+static const uint32_t drvopts[] = {
+	SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
+};
+
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, sigma_clear_helper);
+}
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct sr_dev_inst *sdi;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	GSList *devices;
+	struct ftdi_device_list *devlist;
+	char serial_txt[10];
+	uint32_t serial;
+	int ret;
+	unsigned int i;
+
+	(void)options;
+
+	drvc = di->context;
+
+	devices = NULL;
+
+	devc = g_malloc0(sizeof(struct dev_context));
+
+	ftdi_init(&devc->ftdic);
+
+	/* Look for SIGMAs. */
+
+	if ((ret = ftdi_usb_find_all(&devc->ftdic, &devlist,
+	    USB_VENDOR, USB_PRODUCT)) <= 0) {
+		if (ret < 0)
+			sr_err("ftdi_usb_find_all(): %d", ret);
+		goto free;
+	}
+
+	/* Make sure it's a version 1 or 2 SIGMA. */
+	ftdi_usb_get_strings(&devc->ftdic, devlist->dev, NULL, 0, NULL, 0,
+			     serial_txt, sizeof(serial_txt));
+	sscanf(serial_txt, "%x", &serial);
+
+	if (serial < 0xa6010000 || serial > 0xa602ffff) {
+		sr_err("Only SIGMA and SIGMA2 are supported "
+		       "in this version of libsigrok.");
+		goto free;
+	}
+
+	sr_info("Found ASIX SIGMA - Serial: %s", serial_txt);
+
+	devc->cur_samplerate = samplerates[0];
+	devc->period_ps = 0;
+	devc->limit_msec = 0;
+	devc->cur_firmware = -1;
+	devc->num_channels = 0;
+	devc->samples_per_event = 0;
+	devc->capture_ratio = 50;
+	devc->use_triggers = 0;
+
+	/* Register SIGMA device. */
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INITIALIZING;
+	sdi->vendor = g_strdup(USB_VENDOR_NAME);
+	sdi->model = g_strdup(USB_MODEL_NAME);
+	sdi->driver = di;
+
+	for (i = 0; i < ARRAY_SIZE(channel_names); i++)
+		sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_names[i]);
+
+	devices = g_slist_append(devices, sdi);
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+	sdi->priv = devc;
+
+	/* We will open the device again when we need it. */
+	ftdi_list_free(&devlist);
+
+	return devices;
+
+free:
+	ftdi_deinit(&devc->ftdic);
+	g_free(devc);
+	return NULL;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	int ret;
+
+	devc = sdi->priv;
+
+	/* Make sure it's an ASIX SIGMA. */
+	if ((ret = ftdi_usb_open_desc(&devc->ftdic,
+		USB_VENDOR, USB_PRODUCT, USB_DESCRIPTION, NULL)) < 0) {
+
+		sr_err("ftdi_usb_open failed: %s",
+		       ftdi_get_error_string(&devc->ftdic));
+
+		return 0;
+	}
+
+	sdi->status = SR_ST_ACTIVE;
+
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+
+	/* TODO */
+	if (sdi->status == SR_ST_ACTIVE)
+		ftdi_usb_close(&devc->ftdic);
+
+	sdi->status = SR_ST_INACTIVE;
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return dev_clear(di);
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (!sdi)
+		return SR_ERR;
+	devc = sdi->priv;
+
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		*data = g_variant_new_uint64(devc->cur_samplerate);
+		break;
+	case SR_CONF_LIMIT_MSEC:
+		*data = g_variant_new_uint64(devc->limit_msec);
+		break;
+	case SR_CONF_CAPTURE_RATIO:
+		*data = g_variant_new_uint64(devc->capture_ratio);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	uint64_t tmp;
+	int ret;
+
+	(void)cg;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		ret = sigma_set_samplerate(sdi, g_variant_get_uint64(data));
+		break;
+	case SR_CONF_LIMIT_MSEC:
+		tmp = g_variant_get_uint64(data);
+		if (tmp > 0)
+			devc->limit_msec = g_variant_get_uint64(data);
+		else
+			ret = SR_ERR;
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		tmp = g_variant_get_uint64(data);
+		devc->limit_msec = tmp * 1000 / devc->cur_samplerate;
+		break;
+	case SR_CONF_CAPTURE_RATIO:
+		tmp = g_variant_get_uint64(data);
+		if (tmp <= 100)
+			devc->capture_ratio = tmp;
+		else
+			ret = SR_ERR;
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	GVariant *gvar;
+	GVariantBuilder gvb;
+
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_DEVICE_OPTIONS:
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_SAMPLERATE:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+		gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
+				SAMPLERATES_COUNT, sizeof(uint64_t));
+		g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+		*data = g_variant_builder_end(&gvb);
+		break;
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				trigger_matches, ARRAY_SIZE(trigger_matches),
+				sizeof(int32_t));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc;
+	struct clockselect_50 clockselect;
+	int frac, triggerpin, ret;
+	uint8_t triggerselect = 0;
+	struct triggerinout triggerinout_conf;
+	struct triggerlut lut;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+
+	if (sigma_convert_trigger(sdi) != SR_OK) {
+		sr_err("Failed to configure triggers.");
+		return SR_ERR;
+	}
+
+	/* If the samplerate has not been set, default to 200 kHz. */
+	if (devc->cur_firmware == -1) {
+		if ((ret = sigma_set_samplerate(sdi, SR_KHZ(200))) != SR_OK)
+			return ret;
+	}
+
+	/* Enter trigger programming mode. */
+	sigma_set_register(WRITE_TRIGGER_SELECT1, 0x20, devc);
+
+	/* 100 and 200 MHz mode. */
+	if (devc->cur_samplerate >= SR_MHZ(100)) {
+		sigma_set_register(WRITE_TRIGGER_SELECT1, 0x81, devc);
+
+		/* Find which pin to trigger on from mask. */
+		for (triggerpin = 0; triggerpin < 8; triggerpin++)
+			if ((devc->trigger.risingmask | devc->trigger.fallingmask) &
+			    (1 << triggerpin))
+				break;
+
+		/* Set trigger pin and light LED on trigger. */
+		triggerselect = (1 << LEDSEL1) | (triggerpin & 0x7);
+
+		/* Default rising edge. */
+		if (devc->trigger.fallingmask)
+			triggerselect |= 1 << 3;
+
+	/* All other modes. */
+	} else if (devc->cur_samplerate <= SR_MHZ(50)) {
+		sigma_build_basic_trigger(&lut, devc);
+
+		sigma_write_trigger_lut(&lut, devc);
+
+		triggerselect = (1 << LEDSEL1) | (1 << LEDSEL0);
+	}
+
+	/* Setup trigger in and out pins to default values. */
+	memset(&triggerinout_conf, 0, sizeof(struct triggerinout));
+	triggerinout_conf.trgout_bytrigger = 1;
+	triggerinout_conf.trgout_enable = 1;
+
+	sigma_write_register(WRITE_TRIGGER_OPTION,
+			     (uint8_t *) &triggerinout_conf,
+			     sizeof(struct triggerinout), devc);
+
+	/* Go back to normal mode. */
+	sigma_set_register(WRITE_TRIGGER_SELECT1, triggerselect, devc);
+
+	/* Set clock select register. */
+	if (devc->cur_samplerate == SR_MHZ(200))
+		/* Enable 4 channels. */
+		sigma_set_register(WRITE_CLOCK_SELECT, 0xf0, devc);
+	else if (devc->cur_samplerate == SR_MHZ(100))
+		/* Enable 8 channels. */
+		sigma_set_register(WRITE_CLOCK_SELECT, 0x00, devc);
+	else {
+		/*
+		 * 50 MHz mode (or fraction thereof). Any fraction down to
+		 * 50 MHz / 256 can be used, but is not supported by sigrok API.
+		 */
+		frac = SR_MHZ(50) / devc->cur_samplerate - 1;
+
+		clockselect.async = 0;
+		clockselect.fraction = frac;
+		clockselect.disabled_channels = 0;
+
+		sigma_write_register(WRITE_CLOCK_SELECT,
+				     (uint8_t *) &clockselect,
+				     sizeof(clockselect), devc);
+	}
+
+	/* Setup maximum post trigger time. */
+	sigma_set_register(WRITE_POST_TRIGGER,
+			   (devc->capture_ratio * 255) / 100, devc);
+
+	/* Start acqusition. */
+	gettimeofday(&devc->start_tv, 0);
+	sigma_set_register(WRITE_MODE, 0x0d, devc);
+
+	devc->cb_data = cb_data;
+
+	/* Send header packet to the session bus. */
+	std_session_send_df_header(sdi, LOG_PREFIX);
+
+	/* Add capture source. */
+	sr_session_source_add(sdi->session, -1, 0, 10, sigma_receive_data, (void *)sdi);
+
+	devc->state.state = SIGMA_CAPTURE;
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc;
+
+	(void)cb_data;
+
+	devc = sdi->priv;
+	devc->state.state = SIGMA_IDLE;
+
+	sr_session_source_remove(sdi->session, -1);
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver asix_sigma_driver_info = {
+	.name = "asix-sigma",
+	.longname = "ASIX SIGMA/SIGMA2",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/hardware/asix-sigma/asix-sigma.c b/src/hardware/asix-sigma/protocol.c
similarity index 63%
rename from hardware/asix-sigma/asix-sigma.c
rename to src/hardware/asix-sigma/protocol.c
index df9a888..03d9e3c 100644
--- a/hardware/asix-sigma/asix-sigma.c
+++ b/src/hardware/asix-sigma/protocol.c
@@ -23,24 +23,14 @@
  * ASIX SIGMA/SIGMA2 logic analyzer driver
  */
 
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <ftdi.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "asix-sigma.h"
+#include <config.h>
+#include "protocol.h"
 
 #define USB_VENDOR			0xa600
 #define USB_PRODUCT			0xa000
 #define USB_DESCRIPTION			"ASIX SIGMA"
 #define USB_VENDOR_NAME			"ASIX"
 #define USB_MODEL_NAME			"SIGMA"
-#define TRIGGER_TYPE 			"rf10"
-
-SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
-static struct sr_dev_driver *di = &asix_sigma_driver_info;
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
 
 /*
  * The ASIX Sigma supports arbitrary integer frequency divider in
@@ -48,7 +38,7 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
  * very precise sampling rate selection. This driver supports only
  * a subset of the sampling rates.
  */
-static const uint64_t samplerates[] = {
+SR_PRIV const uint64_t samplerates[] = {
 	SR_KHZ(200),	/* div=250 */
 	SR_KHZ(250),	/* div=200 */
 	SR_KHZ(500),	/* div=100 */
@@ -61,35 +51,19 @@ static const uint64_t samplerates[] = {
 	SR_MHZ(200),	/* Special FW needed */
 };
 
-/*
- * Channel numbers seem to go from 1-16, according to this image:
- * http://tools.asix.net/img/sigma_sigmacab_pins_720.jpg
- * (the cable has two additional GND pins, and a TI and TO pin)
- */
-static const char *channel_names[] = {
-	"1", "2", "3", "4", "5", "6", "7", "8",
-	"9", "10", "11", "12", "13", "14", "15", "16",
-};
+SR_PRIV const int SAMPLERATES_COUNT = ARRAY_SIZE(samplerates);
 
-static const int32_t hwcaps[] = {
-	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_TRIGGER_TYPE,
-	SR_CONF_CAPTURE_RATIO,
-	SR_CONF_LIMIT_MSEC,
-};
-
-static const char *sigma_firmware_files[] = {
+static const char sigma_firmware_files[][24] = {
 	/* 50 MHz, supports 8 bit fractions */
-	FIRMWARE_DIR "/asix-sigma-50.fw",
+	"asix-sigma-50.fw",
 	/* 100 MHz */
-	FIRMWARE_DIR "/asix-sigma-100.fw",
+	"asix-sigma-100.fw",
 	/* 200 MHz */
-	FIRMWARE_DIR "/asix-sigma-200.fw",
+	"asix-sigma-200.fw",
 	/* Synchronous clock from pin */
-	FIRMWARE_DIR "/asix-sigma-50sync.fw",
+	"asix-sigma-50sync.fw",
 	/* Frequency counter */
-	FIRMWARE_DIR "/asix-sigma-phasor.fw",
+	"asix-sigma-phasor.fw",
 };
 
 static int sigma_read(void *buf, size_t size, struct dev_context *devc)
@@ -120,17 +94,27 @@ static int sigma_write(void *buf, size_t size, struct dev_context *devc)
 	return ret;
 }
 
-static int sigma_write_register(uint8_t reg, uint8_t *data, size_t len,
-				struct dev_context *devc)
+/*
+ * NOTE: We chose the buffer size to be large enough to hold any write to the
+ * device. We still print a message just in case.
+ */
+SR_PRIV int sigma_write_register(uint8_t reg, uint8_t *data, size_t len,
+				 struct dev_context *devc)
 {
 	size_t i;
-	uint8_t buf[len + 2];
+	uint8_t buf[80];
 	int idx = 0;
 
+	if ((len + 2) > sizeof(buf)) {
+		sr_err("Attempted to write %zu bytes, but buffer is too small.",
+		       len + 2);
+		return SR_ERR_BUG;
+	}
+
 	buf[idx++] = REG_ADDR_LOW | (reg & 0xf);
 	buf[idx++] = REG_ADDR_HIGH | (reg >> 4);
 
-	for (i = 0; i < len; ++i) {
+	for (i = 0; i < len; i++) {
 		buf[idx++] = REG_DATA_LOW | (data[i] & 0xf);
 		buf[idx++] = REG_DATA_HIGH_WRITE | (data[i] >> 4);
 	}
@@ -138,7 +122,7 @@ static int sigma_write_register(uint8_t reg, uint8_t *data, size_t len,
 	return sigma_write(buf, idx, devc);
 }
 
-static int sigma_set_register(uint8_t reg, uint8_t value, struct dev_context *devc)
+SR_PRIV int sigma_set_register(uint8_t reg, uint8_t value, struct dev_context *devc)
 {
 	return sigma_write_register(reg, &value, 1, devc);
 }
@@ -193,10 +177,10 @@ static int sigma_read_pos(uint32_t *stoppos, uint32_t *triggerpos,
 
 	/* Not really sure why this must be done, but according to spec. */
 	if ((--*stoppos & 0x1ff) == 0x1ff)
-		stoppos -= 64;
+		*stoppos -= 64;
 
 	if ((*--triggerpos & 0x1ff) == 0x1ff)
-		triggerpos -= 64;
+		*triggerpos -= 64;
 
 	return 1;
 }
@@ -217,7 +201,7 @@ static int sigma_read_dram(uint16_t startchunk, size_t numchunks,
 	buf[idx++] = REG_DRAM_BLOCK;
 	buf[idx++] = REG_DRAM_WAIT_ACK;
 
-	for (i = 0; i < numchunks; ++i) {
+	for (i = 0; i < numchunks; i++) {
 		/* Alternate bit to copy from DRAM to cache. */
 		if (i != (numchunks - 1))
 			buf[idx++] = REG_DRAM_BLOCK | (((i + 1) % 2) << 4);
@@ -234,14 +218,14 @@ static int sigma_read_dram(uint16_t startchunk, size_t numchunks,
 }
 
 /* Upload trigger look-up tables to Sigma. */
-static int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *devc)
+SR_PRIV int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *devc)
 {
 	int i;
 	uint8_t tmp[2];
 	uint16_t bit;
 
 	/* Transpose the table and send to Sigma. */
-	for (i = 0; i < 16; ++i) {
+	for (i = 0; i < 16; i++) {
 		bit = 1 << i;
 
 		tmp[0] = tmp[1] = 0;
@@ -292,7 +276,7 @@ static int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *d
 	return SR_OK;
 }
 
-static void clear_helper(void *priv)
+SR_PRIV void sigma_clear_helper(void *priv)
 {
 	struct dev_context *devc;
 
@@ -301,109 +285,6 @@ static void clear_helper(void *priv)
 	ftdi_deinit(&devc->ftdic);
 }
 
-static int dev_clear(void)
-{
-	return std_dev_clear(di, clear_helper);
-}
-
-static int init(struct sr_context *sr_ctx)
-{
-	return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static GSList *scan(GSList *options)
-{
-	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
-	struct drv_context *drvc;
-	struct dev_context *devc;
-	GSList *devices;
-	struct ftdi_device_list *devlist;
-	char serial_txt[10];
-	uint32_t serial;
-	int ret;
-	unsigned int i;
-
-	(void)options;
-
-	drvc = di->priv;
-
-	devices = NULL;
-
-	if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
-		sr_err("%s: devc malloc failed", __func__);
-		return NULL;
-	}
-
-	ftdi_init(&devc->ftdic);
-
-	/* Look for SIGMAs. */
-
-	if ((ret = ftdi_usb_find_all(&devc->ftdic, &devlist,
-	    USB_VENDOR, USB_PRODUCT)) <= 0) {
-		if (ret < 0)
-			sr_err("ftdi_usb_find_all(): %d", ret);
-		goto free;
-	}
-
-	/* Make sure it's a version 1 or 2 SIGMA. */
-	ftdi_usb_get_strings(&devc->ftdic, devlist->dev, NULL, 0, NULL, 0,
-			     serial_txt, sizeof(serial_txt));
-	sscanf(serial_txt, "%x", &serial);
-
-	if (serial < 0xa6010000 || serial > 0xa602ffff) {
-		sr_err("Only SIGMA and SIGMA2 are supported "
-		       "in this version of libsigrok.");
-		goto free;
-	}
-
-	sr_info("Found ASIX SIGMA - Serial: %s", serial_txt);
-
-	devc->cur_samplerate = samplerates[0];
-	devc->period_ps = 0;
-	devc->limit_msec = 0;
-	devc->cur_firmware = -1;
-	devc->num_channels = 0;
-	devc->samples_per_event = 0;
-	devc->capture_ratio = 50;
-	devc->use_triggers = 0;
-
-	/* Register SIGMA device. */
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING, USB_VENDOR_NAME,
-				    USB_MODEL_NAME, NULL))) {
-		sr_err("%s: sdi was NULL", __func__);
-		goto free;
-	}
-	sdi->driver = di;
-
-	for (i = 0; i < ARRAY_SIZE(channel_names); i++) {
-		ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-				    channel_names[i]);
-		if (!ch)
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-	}
-
-	devices = g_slist_append(devices, sdi);
-	drvc->instances = g_slist_append(drvc->instances, sdi);
-	sdi->priv = devc;
-
-	/* We will open the device again when we need it. */
-	ftdi_list_free(&devlist);
-
-	return devices;
-
-free:
-	ftdi_deinit(&devc->ftdic);
-	g_free(devc);
-	return NULL;
-}
-
-static GSList *dev_list(void)
-{
-	return ((struct drv_context *)(di->priv))->instances;
-}
-
 /*
  * Configure the FPGA for bitbang mode.
  * This sequence is documented in section 2. of the ASIX Sigma programming
@@ -419,7 +300,7 @@ static int sigma_fpga_init_bitbang(struct dev_context *devc)
 		0x01, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
 		0x01, 0x01,
 	};
-	int i, ret, timeout = 10000;
+	int i, ret, timeout = (10 * 1000);
 	uint8_t data;
 
 	/* Section 2. part 1), do the FPGA suicide. */
@@ -441,7 +322,7 @@ static int sigma_fpga_init_bitbang(struct dev_context *devc)
 		if (data & (1 << 5))
 			return 0;
 		/* The D6 was not asserted yet, wait a bit. */
-		usleep(10000);
+		g_usleep(10 * 1000);
 	}
 
 	return SR_ERR_TIMEOUT;
@@ -497,31 +378,20 @@ err:
  * pulses used to program the FPGA. Note that the *bb_cmd must be free()'d
  * by the caller of this function.
  */
-static int sigma_fw_2_bitbang(const char *filename,
+static int sigma_fw_2_bitbang(struct sr_context *ctx, const char *name,
 			      uint8_t **bb_cmd, gsize *bb_cmd_size)
 {
-	GMappedFile *file;
-	GError *error;
-	gsize i, file_size, bb_size;
-	gchar *firmware;
+	size_t i, file_size, bb_size;
+	char *firmware;
 	uint8_t *bb_stream, *bbs;
 	uint32_t imm;
 	int bit, v;
 	int ret = SR_OK;
 
-	/*
-	 * Map the file and make the mapped buffer writable.
-	 * NOTE: Using writable=TRUE does _NOT_ mean that file that is mapped
-	 *       will be modified. It will not be modified until someone uses
-	 *       g_file_set_contents() on it.
-	 */
-	error = NULL;
-	file = g_mapped_file_new(filename, TRUE, &error);
-	g_assert_no_error(error);
-
-	file_size = g_mapped_file_get_length(file);
-	firmware = g_mapped_file_get_contents(file);
-	g_assert(firmware);
+	firmware = sr_resource_load(ctx, SR_RESOURCE_FIRMWARE,
+			name, &file_size, 256 * 1024);
+	if (!firmware)
+		return SR_ERR;
 
 	/* Weird magic transformation below, I have no idea what it does. */
 	imm = 0x3f6df2ab;
@@ -560,11 +430,12 @@ static int sigma_fw_2_bitbang(const char *filename,
 	*bb_cmd_size = bb_size;
 
 exit:
-	g_mapped_file_unref(file);
+	g_free(firmware);
 	return ret;
 }
 
-static int upload_firmware(int firmware_idx, struct dev_context *devc)
+static int upload_firmware(struct sr_context *ctx,
+		int firmware_idx, struct dev_context *devc)
 {
 	int ret;
 	unsigned char *buf;
@@ -590,7 +461,7 @@ static int upload_firmware(int firmware_idx, struct dev_context *devc)
 	}
 
 	/* Four times the speed of sigmalogan - Works well. */
-	ret = ftdi_set_baudrate(ftdic, 750000);
+	ret = ftdi_set_baudrate(ftdic, 750 * 1000);
 	if (ret < 0) {
 		sr_err("ftdi_set_baudrate failed: %s",
 		       ftdi_get_error_string(ftdic));
@@ -603,14 +474,14 @@ static int upload_firmware(int firmware_idx, struct dev_context *devc)
 		return ret;
 
 	/* Prepare firmware. */
-	ret = sigma_fw_2_bitbang(firmware, &buf, &buf_size);
+	ret = sigma_fw_2_bitbang(ctx, firmware, &buf, &buf_size);
 	if (ret != SR_OK) {
-		sr_err("An error occured while reading the firmware: %s",
+		sr_err("An error occurred while reading the firmware: %s",
 		       firmware);
 		return ret;
 	}
 
-	/* Upload firmare. */
+	/* Upload firmware. */
 	sr_info("Uploading firmware file '%s'.", firmware);
 	sigma_write(buf, buf_size, devc);
 
@@ -641,35 +512,15 @@ static int upload_firmware(int firmware_idx, struct dev_context *devc)
 	return SR_OK;
 }
 
-static int dev_open(struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	int ret;
-
-	devc = sdi->priv;
-
-	/* Make sure it's an ASIX SIGMA. */
-	if ((ret = ftdi_usb_open_desc(&devc->ftdic,
-		USB_VENDOR, USB_PRODUCT, USB_DESCRIPTION, NULL)) < 0) {
-
-		sr_err("ftdi_usb_open failed: %s",
-		       ftdi_get_error_string(&devc->ftdic));
-
-		return 0;
-	}
-
-	sdi->status = SR_ST_ACTIVE;
-
-	return SR_OK;
-}
-
-static int set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
+SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
 {
 	struct dev_context *devc;
+	struct drv_context *drvc;
 	unsigned int i;
 	int ret;
 
 	devc = sdi->priv;
+	drvc = sdi->driver->context;
 	ret = SR_OK;
 
 	for (i = 0; i < ARRAY_SIZE(samplerates); i++) {
@@ -680,13 +531,13 @@ static int set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
 		return SR_ERR_SAMPLERATE;
 
 	if (samplerate <= SR_MHZ(50)) {
-		ret = upload_firmware(0, devc);
+		ret = upload_firmware(drvc->sr_ctx, 0, devc);
 		devc->num_channels = 16;
 	} else if (samplerate == SR_MHZ(100)) {
-		ret = upload_firmware(1, devc);
+		ret = upload_firmware(drvc->sr_ctx, 1, devc);
 		devc->num_channels = 8;
 	} else if (samplerate == SR_MHZ(200)) {
-		ret = upload_firmware(2, devc);
+		ret = upload_firmware(drvc->sr_ctx, 2, devc);
 		devc->num_channels = 4;
 	}
 
@@ -708,201 +559,83 @@ static int set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
  * The Sigma supports complex triggers using boolean expressions, but this
  * has not been implemented yet.
  */
-static int configure_channels(const struct sr_dev_inst *sdi)
+SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi)
 {
-	struct dev_context *devc = sdi->priv;
-	const struct sr_channel *ch;
-	const GSList *l;
-	int trigger_set = 0;
-	int channelbit;
+	struct dev_context *devc;
+	struct sr_trigger *trigger;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	const GSList *l, *m;
+	int channelbit, trigger_set;
 
+	devc = sdi->priv;
 	memset(&devc->trigger, 0, sizeof(struct sigma_trigger));
-
-	for (l = sdi->channels; l; l = l->next) {
-		ch = (struct sr_channel *)l->data;
-		channelbit = 1 << (ch->index);
-
-		if (!ch->enabled || !ch->trigger)
-			continue;
-
-		if (devc->cur_samplerate >= SR_MHZ(100)) {
-			/* Fast trigger support. */
-			if (trigger_set) {
-				sr_err("Only a single pin trigger in 100 and "
-				       "200MHz mode is supported.");
-				return SR_ERR;
-			}
-			if (ch->trigger[0] == 'f')
-				devc->trigger.fallingmask |= channelbit;
-			else if (ch->trigger[0] == 'r')
-				devc->trigger.risingmask |= channelbit;
-			else {
-				sr_err("Only rising/falling trigger in 100 "
-				       "and 200MHz mode is supported.");
-				return SR_ERR;
-			}
-
-			++trigger_set;
-		} else {
-			/* Simple trigger support (event). */
-			if (ch->trigger[0] == '1') {
-				devc->trigger.simplevalue |= channelbit;
-				devc->trigger.simplemask |= channelbit;
-			}
-			else if (ch->trigger[0] == '0') {
-				devc->trigger.simplevalue &= ~channelbit;
-				devc->trigger.simplemask |= channelbit;
-			}
-			else if (ch->trigger[0] == 'f') {
-				devc->trigger.fallingmask |= channelbit;
-				++trigger_set;
-			}
-			else if (ch->trigger[0] == 'r') {
-				devc->trigger.risingmask |= channelbit;
-				++trigger_set;
-			}
-
-			/*
-			 * Actually, Sigma supports 2 rising/falling triggers,
-			 * but they are ORed and the current trigger syntax
-			 * does not permit ORed triggers.
-			 */
-			if (trigger_set > 1) {
-				sr_err("Only 1 rising/falling trigger "
-				       "is supported.");
-				return SR_ERR;
+	if (!(trigger = sr_session_trigger_get(sdi->session)))
+		return SR_OK;
+
+	trigger_set = 0;
+	for (l = trigger->stages; l; l = l->next) {
+		stage = l->data;
+		for (m = stage->matches; m; m = m->next) {
+			match = m->data;
+			if (!match->channel->enabled)
+				/* Ignore disabled channels with a trigger. */
+				continue;
+			channelbit = 1 << (match->channel->index);
+			if (devc->cur_samplerate >= SR_MHZ(100)) {
+				/* Fast trigger support. */
+				if (trigger_set) {
+					sr_err("Only a single pin trigger is "
+							"supported in 100 and 200MHz mode.");
+					return SR_ERR;
+				}
+				if (match->match == SR_TRIGGER_FALLING)
+					devc->trigger.fallingmask |= channelbit;
+				else if (match->match == SR_TRIGGER_RISING)
+					devc->trigger.risingmask |= channelbit;
+				else {
+					sr_err("Only rising/falling trigger is "
+							"supported in 100 and 200MHz mode.");
+					return SR_ERR;
+				}
+
+				trigger_set++;
+			} else {
+				/* Simple trigger support (event). */
+				if (match->match == SR_TRIGGER_ONE) {
+					devc->trigger.simplevalue |= channelbit;
+					devc->trigger.simplemask |= channelbit;
+				}
+				else if (match->match == SR_TRIGGER_ZERO) {
+					devc->trigger.simplevalue &= ~channelbit;
+					devc->trigger.simplemask |= channelbit;
+				}
+				else if (match->match == SR_TRIGGER_FALLING) {
+					devc->trigger.fallingmask |= channelbit;
+					trigger_set++;
+				}
+				else if (match->match == SR_TRIGGER_RISING) {
+					devc->trigger.risingmask |= channelbit;
+					trigger_set++;
+				}
+
+				/*
+				 * Actually, Sigma supports 2 rising/falling triggers,
+				 * but they are ORed and the current trigger syntax
+				 * does not permit ORed triggers.
+				 */
+				if (trigger_set > 1) {
+					sr_err("Only 1 rising/falling trigger "
+						   "is supported.");
+					return SR_ERR;
+				}
 			}
 		}
-
-		if (trigger_set)
-			devc->use_triggers = 1;
 	}
 
 	return SR_OK;
 }
 
-static int dev_close(struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-
-	devc = sdi->priv;
-
-	/* TODO */
-	if (sdi->status == SR_ST_ACTIVE)
-		ftdi_usb_close(&devc->ftdic);
-
-	sdi->status = SR_ST_INACTIVE;
-
-	return SR_OK;
-}
-
-static int cleanup(void)
-{
-	return dev_clear();
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg)
-{
-	struct dev_context *devc;
-
-	(void)cg;
-
-	if (!sdi)
-		return SR_ERR;
-	devc = sdi->priv;
-
-	switch (id) {
-	case SR_CONF_SAMPLERATE:
-		*data = g_variant_new_uint64(devc->cur_samplerate);
-		break;
-	case SR_CONF_LIMIT_MSEC:
-		*data = g_variant_new_uint64(devc->limit_msec);
-		break;
-	case SR_CONF_CAPTURE_RATIO:
-		*data = g_variant_new_uint64(devc->capture_ratio);
-		break;
-	default:
-		return SR_ERR_NA;
-	}
-
-	return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg)
-{
-	struct dev_context *devc;
-	uint64_t tmp;
-	int ret;
-
-	(void)cg;
-
-	if (sdi->status != SR_ST_ACTIVE)
-		return SR_ERR_DEV_CLOSED;
-
-	devc = sdi->priv;
-
-	ret = SR_OK;
-	switch (id) {
-	case SR_CONF_SAMPLERATE:
-		ret = set_samplerate(sdi, g_variant_get_uint64(data));
-		break;
-	case SR_CONF_LIMIT_MSEC:
-		tmp = g_variant_get_uint64(data);
-		if (tmp > 0)
-			devc->limit_msec = g_variant_get_uint64(data);
-		else
-			ret = SR_ERR;
-		break;
-	case SR_CONF_LIMIT_SAMPLES:
-		tmp = g_variant_get_uint64(data);
-		devc->limit_msec = tmp * 1000 / devc->cur_samplerate;
-		break;
-	case SR_CONF_CAPTURE_RATIO:
-		tmp = g_variant_get_uint64(data);
-		if (tmp <= 100)
-			devc->capture_ratio = tmp;
-		else
-			ret = SR_ERR;
-		break;
-	default:
-		ret = SR_ERR_NA;
-	}
-
-	return ret;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg)
-{
-	GVariant *gvar;
-	GVariantBuilder gvb;
-
-	(void)sdi;
-	(void)cg;
-
-	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}", "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;
-}
 
 /* Software trigger to determine exact trigger position. */
 static int get_trigger_offset(uint8_t *samples, uint16_t last_sample,
@@ -911,7 +644,7 @@ static int get_trigger_offset(uint8_t *samples, uint16_t last_sample,
 	int i;
 	uint16_t sample = 0;
 
-	for (i = 0; i < 8; ++i) {
+	for (i = 0; i < 8; i++) {
 		if (i > 0)
 			last_sample = sample;
 		sample = samples[2 * i] | (samples[2 * i + 1] << 8);
@@ -937,7 +670,6 @@ static int get_trigger_offset(uint8_t *samples, uint16_t last_sample,
 	return i & 0x7;
 }
 
-
 /*
  * Return the timestamp of "DRAM cluster".
  */
@@ -991,7 +723,7 @@ static void sigma_decode_dram_cluster(struct sigma_dram_cluster *dram_cluster,
 		 */
 		if ((i == 1023) || (ts == (tsdiff - EVENTS_PER_CLUSTER))) {
 			logic.length = (i + 1) * logic.unitsize;
-			sr_session_send(devc->cb_data, &packet);
+			sr_session_send(sdi, &packet);
 		}
 	}
 
@@ -1019,14 +751,14 @@ static void sigma_decode_dram_cluster(struct sigma_dram_cluster *dram_cluster,
 		if (trigger_offset > 0) {
 			packet.type = SR_DF_LOGIC;
 			logic.length = trigger_offset * logic.unitsize;
-			sr_session_send(devc->cb_data, &packet);
+			sr_session_send(sdi, &packet);
 			events_in_cluster -= trigger_offset;
 		}
 
 		/* Only send trigger if explicitly enabled. */
 		if (devc->use_triggers) {
 			packet.type = SR_DF_TRIGGER;
-			sr_session_send(devc->cb_data, &packet);
+			sr_session_send(sdi, &packet);
 		}
 	}
 
@@ -1034,7 +766,7 @@ static void sigma_decode_dram_cluster(struct sigma_dram_cluster *dram_cluster,
 		packet.type = SR_DF_LOGIC;
 		logic.length = events_in_cluster * logic.unitsize;
 		logic.data = samples + (trigger_offset * logic.unitsize);
-		sr_session_send(devc->cb_data, &packet);
+		sr_session_send(sdi, &packet);
 	}
 
 	ss->lastsample =
@@ -1055,10 +787,9 @@ static void sigma_decode_dram_cluster(struct sigma_dram_cluster *dram_cluster,
 static int decode_chunk_ts(struct sigma_dram_line *dram_line,
 			   uint16_t events_in_line,
 			   uint32_t trigger_event,
-			   void *cb_data)
+			   struct sr_dev_inst *sdi)
 {
 	struct sigma_dram_cluster *dram_cluster;
-	struct sr_dev_inst *sdi = cb_data;
 	struct dev_context *devc = sdi->priv;
 	unsigned int clusters_in_line =
 		(events_in_line + (EVENTS_PER_CLUSTER - 1)) / EVENTS_PER_CLUSTER;
@@ -1073,7 +804,7 @@ static int decode_chunk_ts(struct sigma_dram_line *dram_line,
 					     trigger_event);
 		}
 
-		/* Find in which cluster the trigger occured. */
+		/* Find in which cluster the trigger occurred. */
 		trigger_cluster = trigger_event / EVENTS_PER_CLUSTER;
 	}
 
@@ -1100,7 +831,7 @@ static int decode_chunk_ts(struct sigma_dram_line *dram_line,
 static int download_capture(struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc = sdi->priv;
-	const int chunks_per_read = 32;
+	const uint32_t chunks_per_read = 32;
 	struct sigma_dram_line *dram_line;
 	int bufsz;
 	uint32_t stoppos, triggerpos;
@@ -1180,7 +911,7 @@ static int download_capture(struct sr_dev_inst *sdi)
 	packet.type = SR_DF_END;
 	sr_session_send(sdi, &packet);
 
-	dev_acquisition_stop(sdi, sdi);
+	sdi->driver->dev_acquisition_stop(sdi, sdi);
 
 	g_free(dram_line);
 
@@ -1219,7 +950,7 @@ static int sigma_capture_mode(struct sr_dev_inst *sdi)
 	return TRUE;
 }
 
-static int receive_data(int fd, int revents, void *cb_data)
+SR_PRIV int sigma_receive_data(int fd, int revents, void *cb_data)
 {
 	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
@@ -1245,20 +976,19 @@ static void build_lut_entry(uint16_t value, uint16_t mask, uint16_t *entry)
 	int i, j, k, bit;
 
 	/* For each quad channel. */
-	for (i = 0; i < 4; ++i) {
+	for (i = 0; i < 4; i++) {
 		entry[i] = 0xffff;
 
 		/* For each bit in LUT. */
-		for (j = 0; j < 16; ++j)
+		for (j = 0; j < 16; j++)
 
 			/* For each channel in quad. */
-			for (k = 0; k < 4; ++k) {
+			for (k = 0; k < 4; k++) {
 				bit = 1 << (i * 4 + k);
 
 				/* Set bit in entry */
-				if ((mask & bit) &&
-				    ((!(value & bit)) !=
-				    (!(j & (1 << k)))))
+				if ((mask & bit) && ((!(value & bit)) !=
+							(!(j & (1 << k)))))
 					entry[i] &= ~(1 << j);
 			}
 	}
@@ -1311,23 +1041,24 @@ static void add_trigger_function(enum triggerop oper, enum triggerfunc func,
 
 	/* Transpose if neg is set. */
 	if (neg) {
-		for (i = 0; i < 2; ++i) {
-			for (j = 0; j < 2; ++j) {
+		for (i = 0; i < 2; i++) {
+			for (j = 0; j < 2; j++) {
 				tmp = x[i][j];
-				x[i][j] = x[1-i][1-j];
-				x[1-i][1-j] = tmp;
+				x[i][j] = x[1 - i][1 - j];
+				x[1 - i][1 - j] = tmp;
 			}
 		}
 	}
 
 	/* Update mask with function. */
-	for (i = 0; i < 16; ++i) {
+	for (i = 0; i < 16; i++) {
 		a = (i >> (2 * index + 0)) & 1;
 		b = (i >> (2 * index + 1)) & 1;
 
 		aset = (*mask >> i) & 1;
 		bset = x[b][a];
 
+		rset = 0;
 		if (func == FUNC_AND || func == FUNC_NAND)
 			rset = aset & bset;
 		else if (func == FUNC_OR || func == FUNC_NOR)
@@ -1350,14 +1081,14 @@ static void add_trigger_function(enum triggerop oper, enum triggerfunc func,
  * simple pin change and state triggers. Only two transitions (rise/fall) can be
  * set at any time, but a full mask and value can be set (0/1).
  */
-static int build_basic_trigger(struct triggerlut *lut, struct dev_context *devc)
+SR_PRIV int sigma_build_basic_trigger(struct triggerlut *lut, struct dev_context *devc)
 {
 	int i,j;
 	uint16_t masks[2] = { 0, 0 };
 
 	memset(lut, 0, sizeof(struct triggerlut));
 
-	/* Contant for simple triggers. */
+	/* Constant for simple triggers. */
 	lut->m4 = 0xa000;
 
 	/* Value/mask trigger support. */
@@ -1365,7 +1096,7 @@ static int build_basic_trigger(struct triggerlut *lut, struct dev_context *devc)
 			lut->m2d);
 
 	/* Rise/fall trigger support. */
-	for (i = 0, j = 0; i < 16; ++i) {
+	for (i = 0, j = 0; i < 16; i++) {
 		if (devc->trigger.risingmask & (1 << i) ||
 		    devc->trigger.fallingmask & (1 << i))
 			masks[j++] = 1 << i;
@@ -1395,146 +1126,3 @@ static int build_basic_trigger(struct triggerlut *lut, struct dev_context *devc)
 
 	return SR_OK;
 }
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
-	struct dev_context *devc;
-	struct clockselect_50 clockselect;
-	int frac, triggerpin, ret;
-	uint8_t triggerselect = 0;
-	struct triggerinout triggerinout_conf;
-	struct triggerlut lut;
-
-	if (sdi->status != SR_ST_ACTIVE)
-		return SR_ERR_DEV_CLOSED;
-
-	devc = sdi->priv;
-
-	if (configure_channels(sdi) != SR_OK) {
-		sr_err("Failed to configure channels.");
-		return SR_ERR;
-	}
-
-	/* If the samplerate has not been set, default to 200 kHz. */
-	if (devc->cur_firmware == -1) {
-		if ((ret = set_samplerate(sdi, SR_KHZ(200))) != SR_OK)
-			return ret;
-	}
-
-	/* Enter trigger programming mode. */
-	sigma_set_register(WRITE_TRIGGER_SELECT1, 0x20, devc);
-
-	/* 100 and 200 MHz mode. */
-	if (devc->cur_samplerate >= SR_MHZ(100)) {
-		sigma_set_register(WRITE_TRIGGER_SELECT1, 0x81, devc);
-
-		/* Find which pin to trigger on from mask. */
-		for (triggerpin = 0; triggerpin < 8; ++triggerpin)
-			if ((devc->trigger.risingmask | devc->trigger.fallingmask) &
-			    (1 << triggerpin))
-				break;
-
-		/* Set trigger pin and light LED on trigger. */
-		triggerselect = (1 << LEDSEL1) | (triggerpin & 0x7);
-
-		/* Default rising edge. */
-		if (devc->trigger.fallingmask)
-			triggerselect |= 1 << 3;
-
-	/* All other modes. */
-	} else if (devc->cur_samplerate <= SR_MHZ(50)) {
-		build_basic_trigger(&lut, devc);
-
-		sigma_write_trigger_lut(&lut, devc);
-
-		triggerselect = (1 << LEDSEL1) | (1 << LEDSEL0);
-	}
-
-	/* Setup trigger in and out pins to default values. */
-	memset(&triggerinout_conf, 0, sizeof(struct triggerinout));
-	triggerinout_conf.trgout_bytrigger = 1;
-	triggerinout_conf.trgout_enable = 1;
-
-	sigma_write_register(WRITE_TRIGGER_OPTION,
-			     (uint8_t *) &triggerinout_conf,
-			     sizeof(struct triggerinout), devc);
-
-	/* Go back to normal mode. */
-	sigma_set_register(WRITE_TRIGGER_SELECT1, triggerselect, devc);
-
-	/* Set clock select register. */
-	if (devc->cur_samplerate == SR_MHZ(200))
-		/* Enable 4 channels. */
-		sigma_set_register(WRITE_CLOCK_SELECT, 0xf0, devc);
-	else if (devc->cur_samplerate == SR_MHZ(100))
-		/* Enable 8 channels. */
-		sigma_set_register(WRITE_CLOCK_SELECT, 0x00, devc);
-	else {
-		/*
-		 * 50 MHz mode (or fraction thereof). Any fraction down to
-		 * 50 MHz / 256 can be used, but is not supported by sigrok API.
-		 */
-		frac = SR_MHZ(50) / devc->cur_samplerate - 1;
-
-		clockselect.async = 0;
-		clockselect.fraction = frac;
-		clockselect.disabled_channels = 0;
-
-		sigma_write_register(WRITE_CLOCK_SELECT,
-				     (uint8_t *) &clockselect,
-				     sizeof(clockselect), devc);
-	}
-
-	/* Setup maximum post trigger time. */
-	sigma_set_register(WRITE_POST_TRIGGER,
-			   (devc->capture_ratio * 255) / 100, devc);
-
-	/* Start acqusition. */
-	gettimeofday(&devc->start_tv, 0);
-	sigma_set_register(WRITE_MODE, 0x0d, devc);
-
-	devc->cb_data = cb_data;
-
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(cb_data, LOG_PREFIX);
-
-	/* Add capture source. */
-	sr_source_add(0, G_IO_IN, 10, receive_data, (void *)sdi);
-
-	devc->state.state = SIGMA_CAPTURE;
-
-	return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
-	struct dev_context *devc;
-
-	(void)cb_data;
-
-	devc = sdi->priv;
-	devc->state.state = SIGMA_IDLE;
-
-	sr_source_remove(0);
-
-	return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver asix_sigma_driver_info = {
-	.name = "asix-sigma",
-	.longname = "ASIX SIGMA/SIGMA2",
-	.api_version = 1,
-	.init = init,
-	.cleanup = cleanup,
-	.scan = scan,
-	.dev_list = dev_list,
-	.dev_clear = dev_clear,
-	.config_get = config_get,
-	.config_set = config_set,
-	.config_list = config_list,
-	.dev_open = dev_open,
-	.dev_close = dev_close,
-	.dev_acquisition_start = dev_acquisition_start,
-	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
-};
diff --git a/hardware/asix-sigma/asix-sigma.h b/src/hardware/asix-sigma/protocol.h
similarity index 78%
rename from hardware/asix-sigma/asix-sigma.h
rename to src/hardware/asix-sigma/protocol.h
index 4c9deff..d654e60 100644
--- a/hardware/asix-sigma/asix-sigma.h
+++ b/src/hardware/asix-sigma/protocol.h
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2010 Håvard Espeland <gus at ping.uio.no>,
+ * Copyright (C) 2010-2012 Håvard Espeland <gus at ping.uio.no>,
  * Copyright (C) 2010 Martin Stensgård <mastensg at ping.uio.no>
  * Copyright (C) 2010 Carl Henrik Lunde <chlunde at ping.uio.no>
  *
@@ -19,11 +19,24 @@
  * 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
+#ifndef LIBSIGROK_HARDWARE_ASIX_SIGMA_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_ASIX_SIGMA_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <ftdi.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
 
 #define LOG_PREFIX "asix-sigma"
 
+#define USB_VENDOR			0xa600
+#define USB_PRODUCT			0xa000
+#define USB_DESCRIPTION			"ASIX SIGMA"
+#define USB_VENDOR_NAME			"ASIX"
+#define USB_MODEL_NAME			"SIGMA"
+
 enum sigma_write_register {
 	WRITE_CLOCK_SELECT	= 0,
 	WRITE_TRIGGER_SELECT0	= 1,
@@ -128,7 +141,7 @@ struct triggerlut {
 	uint16_t m0d[4], m1d[4], m2d[4];
 	uint16_t m3, m3s, m4;
 
-	/* Paramters should be sent as a single register write. */
+	/* Parameters should be sent as a single register write. */
 	struct {
 		uint8_t selc : 2;
 		uint8_t selpresc : 6;
@@ -208,4 +221,17 @@ struct dev_context {
 	void *cb_data;
 };
 
+extern SR_PRIV const uint64_t samplerates[];
+extern SR_PRIV const int SAMPLERATES_COUNT;
+
+SR_PRIV int sigma_write_register(uint8_t reg, uint8_t *data, size_t len, 
+				 struct dev_context *devc);
+SR_PRIV int sigma_set_register(uint8_t reg, uint8_t value, struct dev_context *devc);
+SR_PRIV int sigma_write_trigger_lut(struct triggerlut *lut, struct dev_context *devc);
+SR_PRIV void sigma_clear_helper(void *priv);
+SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
+SR_PRIV int sigma_convert_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV int sigma_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int sigma_build_basic_trigger(struct triggerlut *lut, struct dev_context *devc);
+
 #endif
diff --git a/hardware/atten-pps3xxx/api.c b/src/hardware/atten-pps3xxx/api.c
similarity index 77%
rename from hardware/atten-pps3xxx/api.c
rename to src/hardware/atten-pps3xxx/api.c
index 3b9f002..efe06ac 100644
--- a/hardware/atten-pps3xxx/api.c
+++ b/src/hardware/atten-pps3xxx/api.c
@@ -17,8 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
-#include <errno.h>
 #include "protocol.h"
 
 /*
@@ -31,24 +31,27 @@
  */
 #define SERIALCOMM "9600/8n2"
 
-static const int32_t scanopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t devopts[] = {
+static const uint32_t drvopts[] = {
 	SR_CONF_POWER_SUPPLY,
-	SR_CONF_CONTINUOUS,
-	SR_CONF_OUTPUT_CHANNEL,
-	SR_CONF_OVER_CURRENT_PROTECTION,
 };
 
-static const int32_t devopts_cg[] = {
-	SR_CONF_OUTPUT_VOLTAGE,
-	SR_CONF_OUTPUT_VOLTAGE_MAX,
-	SR_CONF_OUTPUT_CURRENT,
-	SR_CONF_OUTPUT_CURRENT_MAX,
-	SR_CONF_OUTPUT_ENABLED,
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t devopts_cg[] = {
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const char *channel_modes[] = {
@@ -57,7 +60,7 @@ static const char *channel_modes[] = {
 	"Parallel",
 };
 
-static struct pps_model models[] = {
+static const struct pps_model models[] = {
 	{ PPS_3203T_3S, "PPS3203T-3S",
 		CHANMODE_INDEPENDENT | CHANMODE_SERIES | CHANMODE_PARALLEL,
 		3,
@@ -72,16 +75,14 @@ static struct pps_model models[] = {
 	},
 };
 
-
 SR_PRIV struct sr_dev_driver atten_pps3203_driver_info;
-static struct sr_dev_driver *di = &atten_pps3203_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options, int modelid)
+static GSList *scan(struct sr_dev_driver *di, GSList *options, int modelid)
 {
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
@@ -91,15 +92,15 @@ static GSList *scan(GSList *options, int modelid)
 	struct sr_channel_group *cg;
 	struct sr_serial_dev_inst *serial;
 	GSList *l, *devices;
-	struct pps_model *model;
+	const struct pps_model *model;
 	uint8_t packet[PACKET_SIZE];
 	unsigned int i;
-	int ret;
+	int delay_ms, ret;
 	const char *conn, *serialcomm;
 	char channel[10];
 
 	devices = NULL;
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	conn = serialcomm = NULL;
@@ -119,20 +120,20 @@ static GSList *scan(GSList *options, int modelid)
 	if (!serialcomm)
 		serialcomm = SERIALCOMM;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
+
 	serial_flush(serial);
 
-	/* This is how the vendor software channels for hardware. */
+	/* This is how the vendor software scans for hardware. */
 	memset(packet, 0, PACKET_SIZE);
 	packet[0] = 0xaa;
 	packet[1] = 0xaa;
-	if (serial_write(serial, packet, PACKET_SIZE) == -1) {
-		sr_err("Unable to write while probing for hardware: %s",
-				strerror(errno));
+	delay_ms = serial_timeout(serial, PACKET_SIZE);
+	if (serial_write_blocking(serial, packet, PACKET_SIZE, delay_ms) < PACKET_SIZE) {
+		sr_err("Unable to write while probing for hardware.");
 		return NULL;
 	}
 	/* The device responds with a 24-byte packet when it receives a packet.
@@ -141,7 +142,7 @@ static GSList *scan(GSList *options, int modelid)
 	memset(packet, 0, PACKET_SIZE);
 	if ((ret = serial_read_nonblocking(serial, packet, PACKET_SIZE)) < 0) {
 		sr_err("Unable to read while probing for hardware: %s",
-				strerror(errno));
+				sr_strerror(ret));
 		return NULL;
 	}
 	if (ret != PACKET_SIZE || packet[0] != 0xaa || packet[1] != 0xaa) {
@@ -161,14 +162,16 @@ static GSList *scan(GSList *options, int modelid)
 		return NULL;
 	}
 
-	sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Atten", model->name, NULL);
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("Atten");
+	sdi->model = g_strdup(model->name);
 	sdi->driver = di;
 	sdi->inst_type = SR_INST_SERIAL;
 	sdi->conn = serial;
 	for (i = 0; i < MAX_CHANNELS; i++) {
 		snprintf(channel, 10, "CH%d", i + 1);
-		ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channel);
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel);
 		cg = g_malloc(sizeof(struct sr_channel_group));
 		cg->name = g_strdup(channel);
 		cg->channels = g_slist_append(NULL, ch);
@@ -179,6 +182,7 @@ static GSList *scan(GSList *options, int modelid)
 	devc = g_malloc0(sizeof(struct dev_context));
 	devc->model = model;
 	devc->config = g_malloc0(sizeof(struct per_channel_config) * model->num_channels);
+	devc->delay_ms = delay_ms;
 	sdi->priv = devc;
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
@@ -190,22 +194,22 @@ static GSList *scan(GSList *options, int modelid)
 	return devices;
 }
 
-static GSList *scan_3203(GSList *options)
+static GSList *scan_3203(struct sr_dev_driver *di, GSList *options)
 {
-	return scan(options, PPS_3203T_3S);
+	return scan(di, options, PPS_3203T_3S);
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -221,10 +225,10 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	if (!cg) {
 		/* No channel group: global options. */
 		switch (key) {
-		case SR_CONF_OUTPUT_CHANNEL:
+		case SR_CONF_CHANNEL_CONFIG:
 			*data = g_variant_new_string(channel_modes[devc->channel_mode]);
 			break;
-		case SR_CONF_OVER_CURRENT_PROTECTION:
+		case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
 			*data = g_variant_new_boolean(devc->over_current_protection);
 			break;
 		default:
@@ -236,19 +240,19 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		channel = ch->index;
 
 		switch (key) {
-		case SR_CONF_OUTPUT_VOLTAGE:
+		case SR_CONF_VOLTAGE:
 			*data = g_variant_new_double(devc->config[channel].output_voltage_last);
 			break;
-		case SR_CONF_OUTPUT_VOLTAGE_MAX:
+		case SR_CONF_VOLTAGE_TARGET:
 			*data = g_variant_new_double(devc->config[channel].output_voltage_max);
 			break;
-		case SR_CONF_OUTPUT_CURRENT:
+		case SR_CONF_CURRENT:
 			*data = g_variant_new_double(devc->config[channel].output_current_last);
 			break;
-		case SR_CONF_OUTPUT_CURRENT_MAX:
+		case SR_CONF_CURRENT_LIMIT:
 			*data = g_variant_new_double(devc->config[channel].output_current_max);
 			break;
-		case SR_CONF_OUTPUT_ENABLED:
+		case SR_CONF_ENABLED:
 			*data = g_variant_new_boolean(devc->config[channel].output_enabled);
 			break;
 		default:
@@ -274,7 +278,7 @@ static int find_str(const char *str, const char **strings, int array_size)
 	return idx;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -292,7 +296,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	if (!cg) {
 		/* No channel group: global options. */
 		switch (key) {
-		case SR_CONF_OUTPUT_CHANNEL:
+		case SR_CONF_CHANNEL_CONFIG:
 			sval = g_variant_get_string(data, NULL);
 			if ((ival = find_str(sval, channel_modes,
 							ARRAY_SIZE(channel_modes))) == -1) {
@@ -309,7 +313,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 			devc->channel_mode_set = ival;
 			devc->config_dirty = TRUE;
 			break;
-		case SR_CONF_OVER_CURRENT_PROTECTION:
+		case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
 			bval = g_variant_get_boolean(data);
 			if (bval == devc->over_current_protection_set)
 				/* Nothing to do. */
@@ -327,21 +331,21 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 		channel = ch->index;
 
 		switch (key) {
-		case SR_CONF_OUTPUT_VOLTAGE_MAX:
+		case SR_CONF_VOLTAGE_TARGET:
 			dval = g_variant_get_double(data);
 			if (dval < 0 || dval > devc->model->channels[channel].voltage[1])
 				ret = SR_ERR_ARG;
 			devc->config[channel].output_voltage_max = dval;
 			devc->config_dirty = TRUE;
 			break;
-		case SR_CONF_OUTPUT_CURRENT_MAX:
+		case SR_CONF_CURRENT_LIMIT:
 			dval = g_variant_get_double(data);
 			if (dval < 0 || dval > devc->model->channels[channel].current[1])
 				ret = SR_ERR_ARG;
 			devc->config[channel].output_current_max = dval;
 			devc->config_dirty = TRUE;
 			break;
-		case SR_CONF_OUTPUT_ENABLED:
+		case SR_CONF_ENABLED:
 			bval = g_variant_get_boolean(data);
 			if (bval == devc->config[channel].output_enabled_set)
 				/* Nothing to do. */
@@ -354,11 +358,10 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 		}
 	}
 
-
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -367,26 +370,32 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	GVariantBuilder gvb;
 	int channel, ret, i;
 
-	/* Always available, even without sdi. */
+	/* Always available. */
 	if (key == SR_CONF_SCAN_OPTIONS) {
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
 		return SR_OK;
 	}
 
 	if (!sdi)
 		return SR_ERR_ARG;
-	devc = sdi->priv;
 
+	devc = sdi->priv;
 	ret = SR_OK;
 	if (!cg) {
 		/* No channel group: global options. */
 		switch (key) {
 		case SR_CONF_DEVICE_OPTIONS:
-			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-					devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 			break;
-		case SR_CONF_OUTPUT_CHANNEL:
+		case SR_CONF_CHANNEL_CONFIG:
 			if (devc->model->channel_modes == CHANMODE_INDEPENDENT) {
 				/* The 1-channel models. */
 				*data = g_variant_new_strv(channel_modes, 1);
@@ -408,10 +417,10 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 
 		switch (key) {
 		case SR_CONF_DEVICE_OPTIONS:
-			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-					devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(int32_t));
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
 			break;
-		case SR_CONF_OUTPUT_VOLTAGE_MAX:
+		case SR_CONF_VOLTAGE_TARGET:
 			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
 			/* Min, max, step. */
 			for (i = 0; i < 3; i++) {
@@ -420,7 +429,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 			}
 			*data = g_variant_builder_end(&gvb);
 			break;
-		case SR_CONF_OUTPUT_CURRENT_MAX:
+		case SR_CONF_CURRENT_LIMIT:
 			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
 			/* Min, max, step. */
 			for (i = 0; i < 3; i++) {
@@ -470,7 +479,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	devc->acquisition_running = TRUE;
 
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 50, atten_pps3xxx_receive_data, (void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 50,
+			atten_pps3xxx_receive_data, (void *)sdi);
 	std_session_send_df_header(cb_data, LOG_PREFIX);
 
 	/* Send a "channel" configuration packet now. */
@@ -513,5 +523,5 @@ SR_PRIV struct sr_dev_driver atten_pps3203_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/atten-pps3xxx/protocol.c b/src/hardware/atten-pps3xxx/protocol.c
similarity index 91%
rename from hardware/atten-pps3xxx/protocol.c
rename to src/hardware/atten-pps3xxx/protocol.c
index 5658d5d..34e0af4 100644
--- a/hardware/atten-pps3xxx/protocol.c
+++ b/src/hardware/atten-pps3xxx/protocol.c
@@ -17,11 +17,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
-#include <errno.h>
 #include "protocol.h"
 
-static void dump_packet(char *msg, uint8_t *packet)
+static void dump_packet(const char *msg, uint8_t *packet)
 {
 	int i;
 	char str[128];
@@ -37,13 +37,13 @@ static void handle_packet(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	float value, data[MAX_CHANNELS];
 	int offset, i;
 
 	devc = sdi->priv;
 	dump_packet("received", devc->packet);
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	analog.channels = sdi->channels;
 	analog.num_samples = 1;
@@ -83,11 +83,13 @@ static void handle_packet(const struct sr_dev_inst *sdi)
 
 SR_PRIV void send_packet(const struct sr_dev_inst *sdi, uint8_t *packet)
 {
+	struct dev_context *devc;
 	struct sr_serial_dev_inst *serial;
 
+	devc = sdi->priv;
 	serial = sdi->conn;
-	if (serial_write(serial, packet, PACKET_SIZE) == -1)
-		sr_dbg("Failed to send packet: %s", strerror(errno));
+	if (serial_write_blocking(serial, packet, PACKET_SIZE, devc->delay_ms) < PACKET_SIZE)
+		sr_dbg("Failed to send packet.");
 	dump_packet("sent", packet);
 }
 
@@ -153,7 +155,7 @@ SR_PRIV int atten_pps3xxx_receive_data(int fd, int revents, void *cb_data)
 			if (devc->acquisition_running)
 				send_config(sdi);
 			else {
-				serial_source_remove(serial);
+				serial_source_remove(sdi->session, serial);
 				packet.type = SR_DF_END;
 				sr_session_send(sdi, &packet);
 			}
@@ -162,4 +164,3 @@ SR_PRIV int atten_pps3xxx_receive_data(int fd, int revents, void *cb_data)
 
 	return TRUE;
 }
-
diff --git a/hardware/atten-pps3xxx/protocol.h b/src/hardware/atten-pps3xxx/protocol.h
similarity index 94%
rename from hardware/atten-pps3xxx/protocol.h
rename to src/hardware/atten-pps3xxx/protocol.h
index 1441305..b9a256d 100644
--- a/hardware/atten-pps3xxx/protocol.h
+++ b/src/hardware/atten-pps3xxx/protocol.h
@@ -22,7 +22,7 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "atten-pps3xxx"
@@ -54,7 +54,7 @@ struct channel_spec {
 
 struct pps_model {
 	int modelid;
-	char *name;
+	const char *name;
 	int channel_modes;
 	int num_channels;
 	struct channel_spec channels[MAX_CHANNELS];
@@ -74,7 +74,7 @@ struct per_channel_config {
 /** Private, per-device-instance driver context. */
 struct dev_context {
 	/* Model-specific information */
-	struct pps_model *model;
+	const struct pps_model *model;
 
 	/* Acquisition state */
 	gboolean acquisition_running;
@@ -82,6 +82,8 @@ struct dev_context {
 	/* Operational state */
 	gboolean config_dirty;
 	struct per_channel_config *config;
+	/* Blocking write timeout for packet. */
+	int delay_ms;
 	/* Received from device. */
 	int channel_mode;
 	gboolean over_current_protection;
diff --git a/src/hardware/baylibre-acme/api.c b/src/hardware/baylibre-acme/api.c
new file mode 100644
index 0000000..c116cbb
--- /dev/null
+++ b/src/hardware/baylibre-acme/api.c
@@ -0,0 +1,447 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Bartosz Golaszewski <bgolaszewski at baylibre.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+#include <time.h>
+#include <sys/timerfd.h>
+
+SR_PRIV struct sr_dev_driver baylibre_acme_driver_info;
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+/*
+ * Currently there are two channel-group/probe options for ACME:
+ *   - SR_CONF_PROBE_FACTOR - allows to modify current shunt resistance
+ *     calibration
+ *   - SR_CONF_POWER_OFF - allows to remotely cut-off/restore power to
+ *     measured devices
+ *
+ * They are not static - we have to check each probe's capabilities in
+ * config_list().
+ */
+#define MAX_DEVOPTS_CG		2
+#define HAS_PROBE_FACTOR	(SR_CONF_PROBE_FACTOR | SR_CONF_GET | SR_CONF_SET)
+#define HAS_POWER_OFF		(SR_CONF_POWER_OFF | SR_CONF_GET | SR_CONF_SET)
+
+#define MAX_SAMPLE_RATE 500 /* In Hz */
+
+static const uint64_t samplerates[] = {
+	SR_HZ(1),
+	SR_HZ(MAX_SAMPLE_RATE),
+	SR_HZ(1),
+};
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_dev_inst *sdi;
+	GSList *devices;
+	gboolean status;
+	int i;
+
+	(void)options;
+
+	drvc = di->context;
+	devices = NULL;
+
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->samplerate = SR_HZ(10);
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("BayLibre");
+	sdi->model = g_strdup("ACME");
+	sdi->driver = di;
+	sdi->priv = devc;
+
+	status = bl_acme_is_sane();
+	if (!status)
+		goto err_out;
+
+	/*
+	 * Iterate over all ACME connectors and check if any probes
+	 * are present.
+	 */
+	for (i = 0; i < MAX_PROBES; i++) {
+		/*
+		 * First check if there's an energy probe on this connector. If
+		 * not, and we're already at the fifth probe - see if we can
+		 * detect a temperature probe.
+		 */
+		status = bl_acme_detect_probe(bl_acme_get_enrg_addr(i),
+					      PROBE_NUM(i), ENRG_PROBE_NAME);
+		if (status) {
+			/* Energy probe detected. */
+			status = bl_acme_register_probe(sdi, PROBE_ENRG,
+					bl_acme_get_enrg_addr(i), PROBE_NUM(i));
+			if (!status) {
+				sr_err("Error registering power probe %d",
+				       PROBE_NUM(i));
+				continue;
+			}
+		} else if (i >= TEMP_PRB_START_INDEX) {
+			status = bl_acme_detect_probe(bl_acme_get_temp_addr(i),
+					      PROBE_NUM(i), TEMP_PROBE_NAME);
+			if (status) {
+				/* Temperature probe detected. */
+				status = bl_acme_register_probe(sdi,PROBE_TEMP,
+					bl_acme_get_temp_addr(i), PROBE_NUM(i));
+				if (!status) {
+					sr_err("Error registering temp "
+					       "probe %d", PROBE_NUM(i));
+					continue;
+				}
+			}
+		}
+	}
+
+	/*
+	 * Let's assume there's no ACME device present if no probe
+	 * has been registered.
+	 */
+	if (!sdi->channel_groups)
+		goto err_out;
+
+	devices = g_slist_append(devices, sdi);
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+
+	return devices;
+
+err_out:
+	g_free(devc);
+	sr_dev_inst_free(sdi);
+
+	return NULL;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	(void)sdi;
+
+	/* Nothing to do here. */
+	sdi->status = SR_ST_ACTIVE;
+
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	(void)sdi;
+
+	/* Nothing to do here. */
+	sdi->status = SR_ST_INACTIVE;
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	dev_clear(di);
+
+	return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+		      const struct sr_dev_inst *sdi,
+		      const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	int ret;
+	uint64_t shunt;
+	gboolean power_off;
+
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	switch (key) {
+	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_SAMPLERATE:
+		*data = g_variant_new_uint64(devc->samplerate);
+		break;
+	case SR_CONF_PROBE_FACTOR:
+		if (!cg)
+			return SR_ERR_CHANNEL_GROUP;
+		ret = bl_acme_get_shunt(cg, &shunt);
+		if (ret == SR_OK)
+			*data = g_variant_new_uint64(shunt);
+		break;
+	case SR_CONF_POWER_OFF:
+		if (!cg)
+			return SR_ERR_CHANNEL_GROUP;
+		ret = bl_acme_read_power_state(cg, &power_off);
+		if (ret == SR_OK)
+			*data = g_variant_new_boolean(power_off);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+		      const struct sr_dev_inst *sdi,
+		      const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	uint64_t samplerate;
+	int ret;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_LIMIT_SAMPLES:
+		devc->limit_samples = g_variant_get_uint64(data);
+		devc->limit_msec = 0;
+		break;
+	case SR_CONF_LIMIT_MSEC:
+		devc->limit_msec = g_variant_get_uint64(data) * 1000;
+		devc->limit_samples = 0;
+		break;
+	case SR_CONF_SAMPLERATE:
+		samplerate = g_variant_get_uint64(data);
+		if (samplerate > MAX_SAMPLE_RATE) {
+			sr_err("Maximum sample rate is %d", MAX_SAMPLE_RATE);
+			ret = SR_ERR_SAMPLERATE;
+			break;
+		}
+		devc->samplerate = samplerate;
+		bl_acme_maybe_set_update_interval(sdi, samplerate);
+		break;
+	case SR_CONF_PROBE_FACTOR:
+		if (!cg)
+			return SR_ERR_CHANNEL_GROUP;
+		ret = bl_acme_set_shunt(cg, g_variant_get_uint64(data));
+		break;
+	case SR_CONF_POWER_OFF:
+		if (!cg)
+			return SR_ERR_CHANNEL_GROUP;
+		ret = bl_acme_set_power_off(cg, g_variant_get_boolean(data));
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+		       const struct sr_dev_inst *sdi,
+		       const struct sr_channel_group *cg)
+{
+	uint32_t devopts_cg[MAX_DEVOPTS_CG];
+	GVariant *gvar;
+	GVariantBuilder gvb;
+	int ret, num_devopts_cg = 0;
+
+	(void)sdi;
+	(void)cg;
+
+	ret = SR_OK;
+	if (!cg) {
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+			break;
+		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;
+		default:
+			return SR_ERR_NA;
+		}
+	} else {
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			if (bl_acme_get_probe_type(cg) == PROBE_ENRG)
+				devopts_cg[num_devopts_cg++] = HAS_PROBE_FACTOR;
+			if (bl_acme_probe_has_pws(cg))
+				devopts_cg[num_devopts_cg++] = HAS_POWER_OFF;
+
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts_cg, num_devopts_cg, sizeof(uint32_t));
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	}
+
+	return ret;
+}
+
+static void dev_acquisition_close(const struct sr_dev_inst *sdi)
+{
+	GSList *chl;
+	struct sr_channel *ch;
+
+	for (chl = sdi->channels; chl; chl = chl->next) {
+		ch = chl->data;
+		bl_acme_close_channel(ch);
+	}
+}
+
+static int dev_acquisition_open(const struct sr_dev_inst *sdi)
+{
+	GSList *chl;
+	struct sr_channel *ch;
+
+	for (chl = sdi->channels; chl; chl = chl->next) {
+		ch = chl->data;
+		if (bl_acme_open_channel(ch)) {
+			sr_err("Error opening channel %s", ch->name);
+			dev_acquisition_close(sdi);
+			return SR_ERR;
+		}
+	}
+
+	return 0;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc;
+	struct itimerspec tspec = {
+		.it_interval = { 0, 0 },
+		.it_value = { 0, 0 }
+	};
+
+	(void)cb_data;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (dev_acquisition_open(sdi))
+		return SR_ERR;
+
+	devc = sdi->priv;
+	devc->samples_read = 0;
+	devc->samples_missed = 0;
+	devc->timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
+	if (devc->timer_fd < 0) {
+		sr_err("Error creating timer fd");
+		return SR_ERR;
+	}
+
+	tspec.it_interval.tv_sec = 0;
+	tspec.it_interval.tv_nsec = SR_HZ_TO_NS(devc->samplerate);
+	tspec.it_value = tspec.it_interval;
+
+	if (timerfd_settime(devc->timer_fd, 0, &tspec, NULL)) {
+		sr_err("Failed to set timer");
+		close(devc->timer_fd);
+		return SR_ERR;
+	}
+
+	devc->channel = g_io_channel_unix_new(devc->timer_fd);
+	g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL);
+	g_io_channel_set_encoding(devc->channel, NULL, NULL);
+	g_io_channel_set_buffered(devc->channel, FALSE);
+
+	sr_session_source_add_channel(sdi->session, devc->channel,
+		G_IO_IN | G_IO_ERR, 1000, bl_acme_receive_data, (void *)sdi);
+
+	/* Send header packet to the session bus. */
+	std_session_send_df_header(sdi, LOG_PREFIX);
+	devc->start_time = g_get_monotonic_time();
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_datafeed_packet packet;
+	struct dev_context *devc;
+
+	(void)cb_data;
+
+	devc = sdi->priv;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	dev_acquisition_close(sdi);
+	sr_session_source_remove_channel(sdi->session, devc->channel);
+	g_io_channel_shutdown(devc->channel, FALSE, NULL);
+	g_io_channel_unref(devc->channel);
+	devc->channel = NULL;
+
+	/* Send last packet. */
+	packet.type = SR_DF_END;
+	sr_session_send(sdi, &packet);
+
+	if (devc->samples_missed > 0)
+		sr_warn("%" PRIu64 " samples missed", devc->samples_missed);
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver baylibre_acme_driver_info = {
+	.name = "baylibre-acme",
+	.longname = "BayLibre ACME (Another Cute Measurement Equipment)",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/baylibre-acme/gpio.c b/src/hardware/baylibre-acme/gpio.c
new file mode 100644
index 0000000..d86d241
--- /dev/null
+++ b/src/hardware/baylibre-acme/gpio.c
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Bartosz Golaszewski <bgolaszewski at baylibre.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "gpio.h"
+
+#define LOG_PREFIX "gpio"
+
+static int open_and_write(const gchar *path, const gchar *buf)
+{
+	FILE *fd;
+	ssize_t wr;
+
+	fd = g_fopen(path, "w");
+	if (!fd) {
+		sr_err("Error opening %s: %s", path, g_strerror(errno));
+		return -1;
+	}
+
+	wr = g_fprintf(fd, "%s", buf);
+	fclose(fd);
+	if (wr < 0) {
+		sr_err("Error writing to %s: %s", path, g_strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+SR_PRIV int sr_gpio_export(unsigned gpio)
+{
+	GString *path, *buf;
+	gboolean exported;
+	int status;
+
+	path = g_string_sized_new(128);
+	g_string_printf(path, "/sys/class/gpio/gpio%d", gpio);
+	exported = g_file_test(path->str, G_FILE_TEST_IS_DIR);
+	g_string_free(path, TRUE);
+	if (exported)
+		return 0; /* Already exported. */
+
+	status = sr_gpio_set_direction(gpio, GPIO_DIR_OUT);
+	if (status < 0)
+		return status;
+
+	buf = g_string_sized_new(16);
+	g_string_printf(buf, "%u\n", gpio);
+	status = open_and_write("/sys/class/gpio/export", buf->str);
+	g_string_free(buf, TRUE);
+
+	return status;
+}
+
+SR_PRIV int sr_gpio_set_direction(unsigned gpio, unsigned direction)
+{
+	GString *path, *buf;
+	int status;
+
+	path = g_string_sized_new(128);
+	buf = g_string_sized_new(16);
+	g_string_printf(path, "/sys/class/gpio/gpio%d/direction", gpio);
+	g_string_printf(buf, "%s\n", direction == GPIO_DIR_IN ? "in" : "out");
+
+	status = open_and_write(path->str, buf->str);
+
+	g_string_free(path, TRUE);
+	g_string_free(buf, TRUE);
+
+	return status;
+}
+
+SR_PRIV int sr_gpio_set_value(unsigned gpio, unsigned value)
+{
+	GString *path, *buf;
+	int status;
+
+	path = g_string_sized_new(128);
+	buf = g_string_sized_new(16);
+	g_string_printf(path, "/sys/class/gpio/gpio%d/value", gpio);
+	g_string_printf(buf, "%d\n", value);
+
+	status = open_and_write(path->str, buf->str);
+
+	g_string_free(path, TRUE);
+	g_string_free(buf, TRUE);
+
+	return status;
+}
+
+SR_PRIV int sr_gpio_get_value(int gpio)
+{
+	FILE *fd;
+	GString *path;
+	int ret, status;
+
+	path = g_string_sized_new(128);
+	g_string_printf(path, "/sys/class/gpio/gpio%d/value", gpio);
+	fd = g_fopen(path->str, "r");
+	if (!fd) {
+		sr_err("Error opening %s: %s", path->str, g_strerror(errno));
+		g_string_free(path, TRUE);
+		return -1;
+	}
+
+	status = fscanf(fd, "%d", &ret);
+	fclose(fd);
+	if (status != 1) {
+		sr_err("Error reading from %s: %s", path->str, g_strerror(errno));
+		g_string_free(path, TRUE);
+		return -1;
+	}
+
+	g_string_free(path, TRUE);
+	return ret;
+}
+
+SR_PRIV int sr_gpio_setval_export(int gpio, int value)
+{
+	int status;
+
+	status = sr_gpio_export(gpio);
+	if (status < 0)
+		return status;
+
+	status = sr_gpio_set_value(gpio, value);
+	if (status < 0)
+		return status;
+
+	return 0;
+}
+
+SR_PRIV int sr_gpio_getval_export(int gpio)
+{
+	int status;
+
+	status = sr_gpio_export(gpio);
+	if (status < 0)
+		return status;
+
+	return sr_gpio_get_value(gpio);
+}
diff --git a/src/hardware/baylibre-acme/gpio.h b/src/hardware/baylibre-acme/gpio.h
new file mode 100644
index 0000000..8e91fde
--- /dev/null
+++ b/src/hardware/baylibre-acme/gpio.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Bartosz Golaszewski <bgolaszewski at baylibre.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/>.
+ */
+
+/*
+ * Linux-specific GPIO interface helpers. These functions could be moved out
+ * of this directory if any other driver would happen to want to use them.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_BAYLIBRE_ACME_GPIO_H
+#define LIBSIGROK_HARDWARE_BAYLIBRE_ACME_GPIO_H
+
+enum {
+	GPIO_DIR_IN,
+	GPIO_DIR_OUT,
+};
+
+SR_PRIV int sr_gpio_export(unsigned gpio);
+SR_PRIV int sr_gpio_set_direction(unsigned gpio, unsigned direction);
+SR_PRIV int sr_gpio_set_value(unsigned gpio, unsigned value);
+SR_PRIV int sr_gpio_get_value(int gpio);
+/* These functions export given GPIO if it's not already exported. */
+SR_PRIV int sr_gpio_setval_export(int gpio, int value);
+SR_PRIV int sr_gpio_getval_export(int gpio);
+
+#endif
diff --git a/src/hardware/baylibre-acme/protocol.c b/src/hardware/baylibre-acme/protocol.c
new file mode 100644
index 0000000..0bef0b2
--- /dev/null
+++ b/src/hardware/baylibre-acme/protocol.c
@@ -0,0 +1,804 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Bartosz Golaszewski <bgolaszewski at baylibre.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <glib/gstdio.h>
+#include "protocol.h"
+#include "gpio.h"
+
+#define ACME_REV_A		1
+#define ACME_REV_B		2
+
+enum channel_type {
+	ENRG_PWR = 1,
+	ENRG_CURR,
+	ENRG_VOL,
+	TEMP_IN,
+	TEMP_OUT,
+};
+
+struct channel_group_priv {
+	uint8_t rev;
+	int hwmon_num;
+	int probe_type;
+	int index;
+	int has_pws;
+	uint32_t pws_gpio;
+};
+
+struct channel_priv {
+	int ch_type;
+	int fd;
+	float val;
+	struct channel_group_priv *probe;
+};
+
+#define EEPROM_SERIAL_SIZE		16
+#define EEPROM_TAG_SIZE			32
+
+#define EEPROM_PROBE_TYPE_USB		1
+#define EEPROM_PROBE_TYPE_JACK		2
+#define EEPROM_PROBE_TYPE_HE10		3
+
+struct probe_eeprom {
+	uint32_t type;
+	uint32_t rev;
+	uint32_t shunt;
+	uint8_t pwr_sw;
+	uint8_t serial[EEPROM_SERIAL_SIZE];
+	int8_t tag[EEPROM_TAG_SIZE];
+};
+
+#define EEPROM_SIZE (3 * sizeof(uint32_t) + 1 + EEPROM_SERIAL_SIZE + EEPROM_TAG_SIZE)
+
+#define EEPROM_OFF_TYPE		0
+#define EEPROM_OFF_REV		sizeof(uint32_t)
+#define EEPROM_OFF_SHUNT	(2 * sizeof(uint32_t))
+#define EEPROM_OFF_PWR_SW	(3 * sizeof(uint32_t))
+#define EEPROM_OFF_SERIAL	(3 * sizeof(uint32_t) + 1)
+#define EEPROM_OFF_TAG		(EEPROM_OFF_SERIAL + EEPROM_SERIAL_SIZE)
+
+static const uint8_t enrg_i2c_addrs[] = {
+	0x40, 0x41, 0x44, 0x45, 0x42, 0x43, 0x46, 0x47,
+};
+
+static const uint8_t temp_i2c_addrs[] = {
+	0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x4f, 0x4b,
+};
+
+static const uint32_t revA_pws_gpios[] = {
+	486, 498, 502, 482, 478, 506, 510, 474,
+};
+
+static const uint32_t revA_pws_info_gpios[] = {
+	487, 499, 503, 483, 479, 507, 511, 475,
+};
+
+static const uint32_t revB_pws_gpios[] = {
+	489, 491, 493, 495, 497, 499, 501, 503,
+};
+
+#define MOHM_TO_UOHM(x) ((x) * 1000)
+#define UOHM_TO_MOHM(x) ((x) / 1000)
+
+SR_PRIV uint8_t bl_acme_get_enrg_addr(int index)
+{
+	return enrg_i2c_addrs[index];
+}
+
+SR_PRIV uint8_t bl_acme_get_temp_addr(int index)
+{
+	return temp_i2c_addrs[index];
+}
+
+SR_PRIV gboolean bl_acme_is_sane(void)
+{
+	gboolean status;
+
+	/*
+	 * We expect sysfs to be present and mounted at /sys, ina226 and
+	 * tmp435 sensors detected by the system and their appropriate
+	 * drivers loaded and functional.
+	 */
+	status = g_file_test("/sys", G_FILE_TEST_IS_DIR);
+	if (!status) {
+		sr_err("/sys/ directory not found - sysfs not mounted?");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void probe_name_path(unsigned int addr, GString *path)
+{
+	g_string_printf(path,
+			"/sys/class/i2c-adapter/i2c-1/1-00%02x/name", addr);
+}
+
+/*
+ * For given address fill buf with the path to appropriate hwmon entry.
+ */
+static void probe_hwmon_path(unsigned int addr, GString *path)
+{
+	g_string_printf(path,
+			"/sys/class/i2c-adapter/i2c-1/1-00%02x/hwmon", addr);
+}
+
+static void probe_eeprom_path(unsigned int addr, GString *path)
+{
+	g_string_printf(path,
+			"/sys/class/i2c-dev/i2c-1/device/1-00%02x/eeprom",
+			addr + 0x10);
+}
+
+SR_PRIV gboolean bl_acme_detect_probe(unsigned int addr,
+				      int prb_num, const char *prb_name)
+{
+	gboolean ret = FALSE, status;
+	char *buf = NULL;
+	GString *path = g_string_sized_new(64);
+	GError *err = NULL;
+	gsize size;
+
+	probe_name_path(addr, path);
+	status = g_file_get_contents(path->str, &buf, &size, &err);
+	if (!status) {
+		sr_dbg("Name for probe %d can't be read: %s",
+		       prb_num, err->message);
+		g_string_free(path, TRUE);
+		return ret;
+	}
+
+	if (!strncmp(buf, prb_name, strlen(prb_name))) {
+		/*
+		 * Correct driver registered on this address - but is
+		 * there an actual probe connected?
+		 */
+		probe_hwmon_path(addr, path);
+		status = g_file_test(path->str, G_FILE_TEST_IS_DIR);
+		if (status) {
+			/* We have found an ACME probe. */
+			ret = TRUE;
+		}
+	}
+
+	g_free(buf);
+	g_string_free(path, TRUE);
+
+	return ret;
+}
+
+static int get_hwmon_index(unsigned int addr)
+{
+	int status, hwmon;
+	GString *path = g_string_sized_new(64);
+	GError *err = NULL;
+	GDir *dir;
+
+	probe_hwmon_path(addr, path);
+	dir = g_dir_open(path->str, 0, &err);
+	if (!dir) {
+		sr_err("Error opening %s: %s", path->str, err->message);
+		g_string_free(path, TRUE);
+		return -1;
+	}
+
+	g_string_free(path, TRUE);
+
+	/*
+	 * The directory should contain a single file named hwmonX where X
+	 * is the hwmon index.
+	 */
+	status = sscanf(g_dir_read_name(dir), "hwmon%d", &hwmon);
+	g_dir_close(dir);
+	if (status != 1) {
+		sr_err("Unable to determine the hwmon entry");
+		return -1;
+	}
+
+	return hwmon;
+}
+
+static void append_channel(struct sr_dev_inst *sdi, struct sr_channel_group *cg,
+			   int index, int type)
+{
+	struct channel_priv *cp;
+	struct dev_context *devc;
+	struct sr_channel *ch;
+	char *name;
+
+	devc = sdi->priv;
+
+	switch (type) {
+	case ENRG_PWR:
+		name = g_strdup_printf("P%d_ENRG_PWR", index);
+		break;
+	case ENRG_CURR:
+		name = g_strdup_printf("P%d_ENRG_CURR", index);
+		break;
+	case ENRG_VOL:
+		name = g_strdup_printf("P%d_ENRG_VOL", index);
+		break;
+	case TEMP_IN:
+		name = g_strdup_printf("P%d_TEMP_IN", index);
+		break;
+	case TEMP_OUT:
+		name = g_strdup_printf("P%d_TEMP_OUT", index);
+		break;
+	default:
+		sr_err("Invalid channel type: %d.", type);
+		return;
+	}
+
+	cp = g_malloc0(sizeof(struct channel_priv));
+	cp->ch_type = type;
+	cp->probe = cg->priv;
+
+	ch = sr_channel_new(sdi, devc->num_channels++,
+			    SR_CHANNEL_ANALOG, TRUE, name);
+	g_free(name);
+
+	ch->priv = cp;
+	cg->channels = g_slist_append(cg->channels, ch);
+}
+
+static int read_probe_eeprom(unsigned int addr, struct probe_eeprom *eeprom)
+{
+	GString *path = g_string_sized_new(64);
+	char eeprom_buf[EEPROM_SIZE];
+	ssize_t rd;
+	int fd;
+
+	probe_eeprom_path(addr, path);
+	fd = g_open(path->str, O_RDONLY);
+	g_string_free(path, TRUE);
+	if (fd < 0)
+		return -1;
+
+	rd = read(fd, eeprom_buf, EEPROM_SIZE);
+	close(fd);
+	if (rd != EEPROM_SIZE)
+		return -1;
+
+	eeprom->type = RB32(eeprom_buf + EEPROM_OFF_TYPE);
+	eeprom->rev = RB32(eeprom_buf + EEPROM_OFF_REV);
+	eeprom->shunt = RB32(eeprom_buf + EEPROM_OFF_SHUNT);
+	eeprom->pwr_sw = R8(eeprom_buf + EEPROM_OFF_PWR_SW);
+	/* Don't care about the serial number and tag for now. */
+
+	/* Check if we have some sensible values. */
+	if (eeprom->rev != 'B')
+		/* 'B' is the only supported revision with EEPROM for now. */
+		return -1;
+
+	if (eeprom->type != EEPROM_PROBE_TYPE_USB &&
+	    eeprom->type != EEPROM_PROBE_TYPE_JACK &&
+	    eeprom->type != EEPROM_PROBE_TYPE_HE10)
+		return -1;
+
+	return 0;
+}
+
+/* Some i2c slave addresses on revision B probes differ from revision A. */
+static int revB_addr_to_num(unsigned int addr)
+{
+	switch (addr) {
+	case 0x44:	return 5;
+	case 0x45:	return 6;
+	case 0x42:	return 3;
+	case 0x43:	return 4;
+	default:	return addr - 0x3f;
+	}
+}
+
+SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type,
+					unsigned int addr, int prb_num)
+{
+	struct sr_channel_group *cg;
+	struct channel_group_priv *cgp;
+	struct probe_eeprom eeprom;
+	int hwmon, status;
+	uint32_t gpio;
+
+	/* Obtain the hwmon index. */
+	hwmon = get_hwmon_index(addr);
+	if (hwmon < 0)
+		return FALSE;
+
+	cg = g_malloc0(sizeof(struct sr_channel_group));
+	cgp = g_malloc0(sizeof(struct channel_group_priv));
+	cg->priv = cgp;
+
+	/*
+	 * See if we can read the EEPROM contents. If not, assume it's
+	 * a revision A probe.
+	 */
+	memset(&eeprom, 0, sizeof(struct probe_eeprom));
+	status = read_probe_eeprom(addr, &eeprom);
+	cgp->rev = status < 0 ? ACME_REV_A : ACME_REV_B;
+
+	prb_num = cgp->rev == ACME_REV_A ? prb_num : revB_addr_to_num(addr);
+
+	cgp->hwmon_num = hwmon;
+	cgp->probe_type = type;
+	cgp->index = prb_num - 1;
+	cg->name = g_strdup_printf("Probe_%d", prb_num);
+
+	if (cgp->rev == ACME_REV_A) {
+		gpio = revA_pws_info_gpios[cgp->index];
+		cgp->has_pws = sr_gpio_getval_export(gpio);
+		cgp->pws_gpio = revA_pws_gpios[cgp->index];
+	} else {
+		cgp->has_pws = eeprom.pwr_sw;
+		cgp->pws_gpio = revB_pws_gpios[cgp->index];
+
+		/*
+		 * For revision B we can already try to set the shunt
+		 * resistance according to the EEPROM contents.
+		 *
+		 * Keep the default value if shunt in EEPROM == 0.
+		 */
+		if (eeprom.shunt > 0)
+			bl_acme_set_shunt(cg, UOHM_TO_MOHM(eeprom.shunt));
+	}
+
+	if (type == PROBE_ENRG) {
+		append_channel(sdi, cg, prb_num, ENRG_PWR);
+		append_channel(sdi, cg, prb_num, ENRG_CURR);
+		append_channel(sdi, cg, prb_num, ENRG_VOL);
+	} else if (type == PROBE_TEMP) {
+		append_channel(sdi, cg, prb_num, TEMP_IN);
+		append_channel(sdi, cg, prb_num, TEMP_OUT);
+	} else {
+		sr_err("Invalid probe type: %d.", type);
+	}
+
+	sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+
+	return TRUE;
+}
+
+SR_PRIV int bl_acme_get_probe_type(const struct sr_channel_group *cg)
+{
+	struct channel_group_priv *cgp = cg->priv;
+
+	return cgp->probe_type;
+}
+
+SR_PRIV int bl_acme_probe_has_pws(const struct sr_channel_group *cg)
+{
+	struct channel_group_priv *cgp = cg->priv;
+
+	return cgp->has_pws;
+}
+
+/*
+ * Sets path to the hwmon attribute if this channel group
+ * supports shunt resistance setting. The caller has to supply
+ * a valid GString.
+ */
+static int get_shunt_path(const struct sr_channel_group *cg, GString *path)
+{
+	struct channel_group_priv *cgp;
+	int ret = SR_OK, status;
+
+	cgp = cg->priv;
+
+	if (cgp->probe_type != PROBE_ENRG) {
+		sr_err("Probe doesn't support shunt resistance setting");
+		return SR_ERR_ARG;
+	}
+
+	g_string_append_printf(path,
+			       "/sys/class/hwmon/hwmon%d/shunt_resistor",
+			       cgp->hwmon_num);
+
+	/*
+	 * The shunt_resistor sysfs attribute is available
+	 * in the Linux kernel since version 3.20. We have
+	 * to notify the user if this attribute is not present.
+	 */
+	status = g_file_test(path->str, G_FILE_TEST_EXISTS);
+	if (!status) {
+		sr_err("shunt_resistance attribute not present, please update "
+		       "your kernel to version >=3.20");
+		return SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+/*
+ * Try setting the update_interval sysfs attribute for each probe according
+ * to samplerate.
+ */
+SR_PRIV void bl_acme_maybe_set_update_interval(const struct sr_dev_inst *sdi,
+					       uint64_t samplerate)
+{
+	struct sr_channel_group *cg;
+	struct channel_group_priv *cgp;
+	GString *hwmon;
+	GSList *l;
+	FILE *fd;
+
+	for (l = sdi->channel_groups; l != NULL; l = l->next) {
+		cg = l->data;
+		cgp = cg->priv;
+
+		hwmon = g_string_sized_new(64);
+		g_string_append_printf(hwmon,
+				"/sys/class/hwmon/hwmon%d/update_interval",
+				cgp->hwmon_num);
+
+		if (g_file_test(hwmon->str, G_FILE_TEST_EXISTS)) {
+			fd = g_fopen(hwmon->str, "w");
+			if (!fd) {
+				g_string_free(hwmon, TRUE);
+				continue;
+			}
+
+			g_fprintf(fd, "%" PRIu64 "\n", 1000 / samplerate);
+			fclose(fd);
+		}
+
+		g_string_free(hwmon, TRUE);
+	}
+}
+
+SR_PRIV int bl_acme_get_shunt(const struct sr_channel_group *cg,
+			      uint64_t *shunt)
+{
+	GString *path = g_string_sized_new(64);
+	gchar *contents;
+	int status, ret = SR_OK;
+	GError *err = NULL;
+
+	status = get_shunt_path(cg, path);
+	if (status != SR_OK) {
+		ret = status;
+		goto out;
+	}
+
+	status = g_file_get_contents(path->str, &contents, NULL, &err);
+	if (!status) {
+		sr_err("Error reading shunt resistance: %s", err->message);
+		ret = SR_ERR_IO;
+		goto out;
+	}
+
+	*shunt = UOHM_TO_MOHM(strtol(contents, NULL, 10));
+
+out:
+	g_string_free(path, TRUE);
+	return ret;
+}
+
+SR_PRIV int bl_acme_set_shunt(const struct sr_channel_group *cg, uint64_t shunt)
+{
+	GString *path = g_string_sized_new(64);;
+	int status, ret = SR_OK;
+	FILE *fd;
+
+	status = get_shunt_path(cg, path);
+	if (status != SR_OK) {
+		ret = status;
+		goto out;
+	}
+
+	/*
+	 * Can't use g_file_set_contents() here, as it calls open() with
+	 * O_EXEC flag in a sysfs directory thus failing with EACCES.
+	 */
+	fd = g_fopen(path->str, "w");
+	if (!fd) {
+		sr_err("Error opening %s: %s", path->str, g_strerror(errno));
+		ret = SR_ERR_IO;
+		goto out;
+	}
+
+	g_fprintf(fd, "%" PRIu64 "\n", MOHM_TO_UOHM(shunt));
+	fclose(fd);
+
+out:
+	g_string_free(path, TRUE);
+	return ret;
+}
+
+SR_PRIV int bl_acme_read_power_state(const struct sr_channel_group *cg,
+				     gboolean *off)
+{
+	struct channel_group_priv *cgp;
+	int val;
+
+	cgp = cg->priv;
+
+	if (!bl_acme_probe_has_pws(cg)) {
+		sr_err("Probe has no power-switch");
+		return SR_ERR_ARG;
+	}
+
+	val = sr_gpio_getval_export(cgp->pws_gpio);
+	*off = val ? FALSE : TRUE;
+
+	return SR_OK;
+}
+
+SR_PRIV int bl_acme_set_power_off(const struct sr_channel_group *cg,
+				  gboolean off)
+{
+	struct channel_group_priv *cgp;
+	int val;
+
+	cgp = cg->priv;
+
+	if (!bl_acme_probe_has_pws(cg)) {
+		sr_err("Probe has no power-switch");
+		return SR_ERR_ARG;
+	}
+
+	val = sr_gpio_setval_export(cgp->pws_gpio, off ? 0 : 1);
+	if (val < 0) {
+		sr_err("Error setting power-off state: gpio: %d",
+		       cgp->pws_gpio);
+		return SR_ERR_IO;
+	}
+
+	return SR_OK;
+}
+
+static int channel_to_mq(struct sr_channel *ch)
+{
+	struct channel_priv *chp;
+
+	chp = ch->priv;
+
+	switch (chp->ch_type) {
+	case ENRG_PWR:
+		return SR_MQ_POWER;
+	case ENRG_CURR:
+		return SR_MQ_CURRENT;
+	case ENRG_VOL:
+		return SR_MQ_VOLTAGE;
+	case TEMP_IN: /* Fallthrough */
+	case TEMP_OUT:
+		return SR_MQ_TEMPERATURE;
+	default:
+		return -1;
+	}
+}
+
+static int channel_to_unit(struct sr_channel *ch)
+{
+	struct channel_priv *chp;
+
+	chp = ch->priv;
+
+	switch (chp->ch_type) {
+	case ENRG_PWR:
+		return SR_UNIT_WATT;
+	case ENRG_CURR:
+		return SR_UNIT_AMPERE;
+	case ENRG_VOL:
+		return SR_UNIT_VOLT;
+	case TEMP_IN: /* Fallthrough */
+	case TEMP_OUT:
+		return SR_UNIT_CELSIUS;
+	default:
+		return -1;
+	}
+}
+
+/* We need to scale measurements down from the units used by the drivers. */
+static float adjust_data(int val, int type)
+{
+	switch (type) {
+	case ENRG_PWR:
+		return ((float)val) / 1000000.0;
+	case ENRG_CURR: /* Fallthrough */
+	case ENRG_VOL: /* Fallthrough */
+	case TEMP_IN: /* Fallthrough */
+	case TEMP_OUT:
+		return ((float)val) / 1000.0;
+	default:
+		return 0.0;
+	}
+}
+
+static float read_sample(struct sr_channel *ch)
+{
+	struct channel_priv *chp;
+	char buf[16];
+	ssize_t len;
+	int fd;
+
+	chp = ch->priv;
+	fd = chp->fd;
+
+	lseek(fd, 0, SEEK_SET);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0) {
+		sr_err("Error reading from channel %s (hwmon: %d): %s",
+			ch->name, chp->probe->hwmon_num, g_strerror(errno));
+		ch->enabled = FALSE;
+		return -1.0;
+	}
+
+	return adjust_data(strtol(buf, NULL, 10), chp->ch_type);
+}
+
+SR_PRIV int bl_acme_open_channel(struct sr_channel *ch)
+{
+	struct channel_priv *chp;
+	char path[64];
+	const char *file;
+	int fd;
+
+	chp = ch->priv;
+
+	switch (chp->ch_type) {
+	case ENRG_PWR:	file = "power1_input";	break;
+	case ENRG_CURR:	file = "curr1_input";	break;
+	case ENRG_VOL:	file = "in1_input";	break;
+	case TEMP_IN:	file = "temp1_input";	break;
+	case TEMP_OUT:	file = "temp2_input";	break;
+	default:
+		sr_err("Invalid channel type: %d.", chp->ch_type);
+		return SR_ERR;
+	}
+
+	snprintf(path, sizeof(path), "/sys/class/hwmon/hwmon%d/%s",
+		 chp->probe->hwmon_num, file);
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		sr_err("Error opening %s: %s", path, g_strerror(errno));
+		ch->enabled = FALSE;
+		return SR_ERR;
+	}
+
+	chp->fd = fd;
+
+	return 0;
+}
+
+SR_PRIV void bl_acme_close_channel(struct sr_channel *ch)
+{
+	struct channel_priv *chp;
+
+	chp = ch->priv;
+	close(chp->fd);
+	chp->fd = -1;
+}
+
+SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
+{
+	uint32_t cur_time, elapsed_time;
+	uint64_t nrexpiration;
+	struct sr_datafeed_packet packet, framep;
+	struct sr_datafeed_analog_old analog;
+	struct sr_dev_inst *sdi;
+	struct sr_channel *ch;
+	struct channel_priv *chp;
+	struct dev_context *devc;
+	GSList *chl, chonly;
+	unsigned i;
+
+	(void)fd;
+	(void)revents;
+
+	sdi = cb_data;
+	if (!sdi)
+		return TRUE;
+
+	devc = sdi->priv;
+	if (!devc)
+		return TRUE;
+
+	packet.type = SR_DF_ANALOG_OLD;
+	packet.payload = &analog;
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
+
+	if (read(devc->timer_fd, &nrexpiration, sizeof(nrexpiration)) < 0) {
+		sr_warn("Failed to read timer information");
+		return TRUE;
+	}
+
+	/*
+	 * We were not able to process the previous timer expiration, we are
+	 * overloaded.
+	 */
+	if (nrexpiration > 1)
+		devc->samples_missed += nrexpiration - 1;
+
+	/*
+	 * XXX This is a nasty workaround...
+	 *
+	 * At high sampling rates and maximum channels we are not able to
+	 * acquire samples fast enough, even though frontends still think
+	 * that samples arrive on time. This causes shifts in frontend
+	 * plots.
+	 *
+	 * To compensate for the delay we check if any clock events were
+	 * missed and - if so - don't really read the next value, but send
+	 * the same sample as fast as possible. We do it until we are back
+	 * on schedule.
+	 *
+	 * At high sampling rate this doesn't seem to visibly reduce the
+	 * accuracy.
+	 */
+	for (i = 0; i < nrexpiration; i++) {
+		framep.type = SR_DF_FRAME_BEGIN;
+		sr_session_send(cb_data, &framep);
+
+		/*
+		 * Due to different units used in each channel we're sending
+		 * samples one-by-one.
+		 */
+		for (chl = sdi->channels; chl; chl = chl->next) {
+			ch = chl->data;
+			chp = ch->priv;
+
+			if (!ch->enabled)
+				continue;
+			chonly.next = NULL;
+			chonly.data = ch;
+			analog.channels = &chonly;
+			analog.num_samples = 1;
+			analog.mq = channel_to_mq(chl->data);
+			analog.unit = channel_to_unit(ch);
+
+			if (i < 1)
+				chp->val = read_sample(ch);
+
+			analog.data = &chp->val;
+			sr_session_send(cb_data, &packet);
+		}
+
+		framep.type = SR_DF_FRAME_END;
+		sr_session_send(cb_data, &framep);
+	}
+
+	devc->samples_read++;
+	if (devc->limit_samples > 0 &&
+	    devc->samples_read >= devc->limit_samples) {
+		sr_info("Requested number of samples reached.");
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+		devc->last_sample_fin = g_get_monotonic_time();
+		return TRUE;
+	} else if (devc->limit_msec > 0) {
+		cur_time = g_get_monotonic_time();
+		elapsed_time = cur_time - devc->start_time;
+
+		if (elapsed_time >= devc->limit_msec) {
+			sr_info("Sampling time limit reached.");
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			devc->last_sample_fin = g_get_monotonic_time();
+			return TRUE;
+		}
+	}
+
+	devc->last_sample_fin = g_get_monotonic_time();
+	return TRUE;
+}
diff --git a/src/hardware/baylibre-acme/protocol.h b/src/hardware/baylibre-acme/protocol.h
new file mode 100644
index 0000000..d0c9761
--- /dev/null
+++ b/src/hardware/baylibre-acme/protocol.h
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Bartosz Golaszewski <bgolaszewski at baylibre.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_BAYLIBRE_ACME_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_BAYLIBRE_ACME_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <unistd.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "baylibre-acme"
+
+/* We support up to 8 energy/temperature probes. */
+#define MAX_PROBES		8
+
+/*
+ * Temperature probes can be connected to the last four ports on the
+ * ACME cape. When scanning, first look for temperature probes starting
+ * from this index.
+ */
+#define TEMP_PRB_START_INDEX	4
+
+#define ENRG_PROBE_NAME		"ina226"
+#define TEMP_PROBE_NAME		"tmp435"
+
+/* For the user we number the probes starting from 1. */
+#define PROBE_NUM(n) ((n) + 1)
+
+enum probe_type {
+	PROBE_ENRG = 1,
+	PROBE_TEMP,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	uint64_t samplerate;
+	uint64_t limit_samples;
+	uint64_t limit_msec;
+
+	uint32_t num_channels;
+	uint64_t samples_read;
+	uint64_t samples_missed;
+	int64_t start_time;
+	int64_t last_sample_fin;
+	int timer_fd;
+	GIOChannel *channel;
+};
+
+SR_PRIV uint8_t bl_acme_get_enrg_addr(int index);
+SR_PRIV uint8_t bl_acme_get_temp_addr(int index);
+
+SR_PRIV gboolean bl_acme_is_sane(void);
+
+SR_PRIV gboolean bl_acme_detect_probe(unsigned int addr,
+				      int prb_num, const char *prb_name);
+SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type,
+					unsigned int addr, int prb_num);
+
+SR_PRIV int bl_acme_get_probe_type(const struct sr_channel_group *cg);
+SR_PRIV int bl_acme_probe_has_pws(const struct sr_channel_group *cg);
+
+SR_PRIV void bl_acme_maybe_set_update_interval(const struct sr_dev_inst *sdi,
+					       uint64_t samplerate);
+
+SR_PRIV int bl_acme_get_shunt(const struct sr_channel_group *cg,
+			      uint64_t *shunt);
+SR_PRIV int bl_acme_set_shunt(const struct sr_channel_group *cg,
+			      uint64_t shunt);
+SR_PRIV int bl_acme_read_power_state(const struct sr_channel_group *cg,
+				     gboolean *off);
+SR_PRIV int bl_acme_set_power_off(const struct sr_channel_group *cg,
+				  gboolean off);
+
+SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data);
+
+SR_PRIV int bl_acme_open_channel(struct sr_channel *ch);
+
+SR_PRIV void bl_acme_close_channel(struct sr_channel *ch);
+#endif
diff --git a/src/hardware/beaglelogic/api.c b/src/hardware/beaglelogic/api.c
new file mode 100644
index 0000000..b974010
--- /dev/null
+++ b/src/hardware/beaglelogic/api.c
@@ -0,0 +1,426 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Kumar Abhishek <abhishek at theembeddedkitchen.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+#include "beaglelogic.h"
+
+SR_PRIV struct sr_dev_driver beaglelogic_driver_info;
+
+/* Scan options */
+static const uint32_t scanopts[] = {
+	SR_CONF_NUM_LOGIC_CHANNELS,
+};
+
+/* Hardware capabilities */
+static const uint32_t devopts[] = {
+	SR_CONF_LOGIC_ANALYZER,
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_NUM_LOGIC_CHANNELS | SR_CONF_GET,
+};
+
+/* Trigger matching capabilities */
+static const int32_t soft_trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
+	SR_TRIGGER_EDGE,
+};
+
+SR_PRIV const char *channel_names[] = {
+	"P8_45", "P8_46", "P8_43", "P8_44", "P8_41", "P8_42", "P8_39",
+	"P8_40", "P8_27", "P8_29", "P8_28", "P8_30", "P8_21", "P8_20",
+};
+
+/* Possible sample rates : 10 Hz to 100 MHz = (100 / x) MHz */
+static const uint64_t samplerates[] = {
+	SR_HZ(10),
+	SR_MHZ(100),
+	SR_HZ(1),
+};
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct dev_context *beaglelogic_devc_alloc(void)
+{
+	struct dev_context *devc;
+
+	devc = g_malloc0(sizeof(struct dev_context));
+
+	/* Default non-zero values (if any) */
+	devc->fd = -1;
+	devc->limit_samples = (uint64_t)-1;
+
+	return devc;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct drv_context *drvc;
+	GSList *devices, *l;
+	struct sr_config *src;
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	int i, maxch;
+
+	devices = NULL;
+	drvc = di->context;
+	drvc->instances = NULL;
+
+	/* Probe for /dev/beaglelogic */
+	if (!g_file_test(BEAGLELOGIC_DEV_NODE, G_FILE_TEST_EXISTS))
+		return NULL;
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->model = g_strdup("BeagleLogic");
+	sdi->version = g_strdup("1.0");
+	sdi->driver = di;
+
+	/* Unless explicitly specified, keep max channels to 8 only */
+	maxch = 8;
+	for (l = options; l; l = l->next) {
+		src = l->data;
+		if (src->key == SR_CONF_NUM_LOGIC_CHANNELS)
+			maxch = g_variant_get_int32(src->data);
+	}
+
+	/* We need to test for number of channels by opening the node */
+	devc = beaglelogic_devc_alloc();
+
+	if (beaglelogic_open_nonblock(devc) != SR_OK) {
+		g_free(devc);
+		sr_dev_inst_free(sdi);
+
+		return NULL;
+	}
+
+	if (maxch > 8) {
+		maxch = NUM_CHANNELS;
+		devc->sampleunit = BL_SAMPLEUNIT_16_BITS;
+	} else {
+		maxch = 8;
+		devc->sampleunit = BL_SAMPLEUNIT_8_BITS;
+	}
+
+	beaglelogic_set_sampleunit(devc);
+	beaglelogic_close(devc);
+
+	/* Signal */
+	sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE);
+
+	/* Fill the channels */
+	for (i = 0; i < maxch; i++)
+		sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE,
+				channel_names[i]);
+
+	sdi->priv = devc;
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+	devices = g_slist_append(devices, sdi);
+
+	return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+
+	/* Open BeagleLogic */
+	if (beaglelogic_open_nonblock(devc))
+		return SR_ERR;
+
+	/* Set fd and local attributes */
+	devc->pollfd.fd = devc->fd;
+	devc->pollfd.events = G_IO_IN;
+	devc->pollfd.revents = 0;
+
+	/* Get the default attributes */
+	beaglelogic_get_samplerate(devc);
+	beaglelogic_get_sampleunit(devc);
+	beaglelogic_get_triggerflags(devc);
+	beaglelogic_get_buffersize(devc);
+	beaglelogic_get_bufunitsize(devc);
+
+	/* Map the kernel capture FIFO for reads, saves 1 level of memcpy */
+	if (beaglelogic_mmap(devc) != SR_OK) {
+		sr_err("Unable to map capture buffer");
+		beaglelogic_close(devc);
+		return SR_ERR;
+	}
+
+	/* We're good to go now */
+	sdi->status = SR_ST_ACTIVE;
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+
+	if (sdi->status == SR_ST_ACTIVE) {
+		/* Close the memory mapping and the file */
+		beaglelogic_munmap(devc);
+		beaglelogic_close(devc);
+	}
+	sdi->status = SR_ST_INACTIVE;
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	struct drv_context *drvc;
+	struct sr_dev_inst *sdi;
+	GSList *l;
+
+	/* unused driver */
+	if (!(drvc = di->context))
+		return SR_OK;
+
+	/* Clean up the instances */
+	for (l = drvc->instances; l; l = l->next) {
+		sdi = l->data;
+		di->dev_close(sdi);
+		g_free(sdi->priv);
+		sr_dev_inst_free(sdi);
+	}
+	g_slist_free(drvc->instances);
+	drvc->instances = NULL;
+
+	return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc = sdi->priv;
+
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_LIMIT_SAMPLES:
+		*data = g_variant_new_uint64(devc->limit_samples);
+		break;
+	case SR_CONF_SAMPLERATE:
+		*data = g_variant_new_uint64(devc->cur_samplerate);
+		break;
+	case SR_CONF_CAPTURE_RATIO:
+		*data = g_variant_new_uint64(devc->capture_ratio);
+		break;
+	case SR_CONF_NUM_LOGIC_CHANNELS:
+		*data = g_variant_new_uint32(g_slist_length(sdi->channels));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc = sdi->priv;
+	uint64_t tmp_u64;
+
+	(void)cg;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		devc->cur_samplerate = g_variant_get_uint64(data);
+		return beaglelogic_set_samplerate(devc);
+	case SR_CONF_LIMIT_SAMPLES:
+		tmp_u64 = g_variant_get_uint64(data);
+		devc->limit_samples = tmp_u64;
+		devc->triggerflags = BL_TRIGGERFLAGS_ONESHOT;
+
+		/* Check if we have sufficient buffer size */
+		tmp_u64 *= SAMPLEUNIT_TO_BYTES(devc->sampleunit);
+		if (tmp_u64 > devc->buffersize) {
+			sr_warn("Insufficient buffer space has been allocated.");
+			sr_warn("Please use \'echo <size in bytes> > "\
+				BEAGLELOGIC_SYSFS_ATTR(memalloc) \
+				"\' as root to increase the buffer size, this"\
+				" capture is now truncated to %d Msamples",
+				devc->buffersize /
+				(SAMPLEUNIT_TO_BYTES(devc->sampleunit) * 1000000));
+		}
+		return beaglelogic_set_triggerflags(devc);
+	case SR_CONF_CAPTURE_RATIO:
+		devc->capture_ratio = g_variant_get_uint64(data);
+		if (devc->capture_ratio > 100)
+			return SR_ERR;
+		return SR_OK;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	int ret;
+	GVariant *gvar;
+	GVariantBuilder gvb;
+
+	(void)sdi;
+	(void)cg;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		break;
+	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_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
+				sizeof(int32_t));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+/* get a sane timeout for poll() */
+#define BUFUNIT_TIMEOUT_MS(devc)	(100 + ((devc->bufunitsize * 1000) / \
+				(uint32_t)(devc->cur_samplerate)))
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+				    void *cb_data)
+{
+	(void)cb_data;
+	struct dev_context *devc = sdi->priv;
+	struct sr_trigger *trigger;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	/* Save user pointer */
+	devc->cb_data = cb_data;
+
+	/* Clear capture state */
+	devc->bytes_read = 0;
+	devc->offset = 0;
+
+	/* Configure channels */
+	devc->sampleunit = g_slist_length(sdi->channels) > 8 ?
+			BL_SAMPLEUNIT_16_BITS : BL_SAMPLEUNIT_8_BITS;
+	beaglelogic_set_sampleunit(devc);
+
+	/* Configure triggers & send header packet */
+	if ((trigger = sr_session_trigger_get(sdi->session))) {
+		int pre_trigger_samples = 0;
+		if (devc->limit_samples > 0)
+			pre_trigger_samples = devc->capture_ratio * devc->limit_samples/100;
+		devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
+		if (!devc->stl)
+			return SR_ERR_MALLOC;
+		devc->trigger_fired = FALSE;
+	} else
+		devc->trigger_fired = TRUE;
+	std_session_send_df_header(cb_data, LOG_PREFIX);
+
+	/* Trigger and add poll on file */
+	beaglelogic_start(devc);
+	sr_session_source_add_pollfd(sdi->session, &devc->pollfd,
+			BUFUNIT_TIMEOUT_MS(devc), beaglelogic_receive_data,
+			(void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc = sdi->priv;
+	struct sr_datafeed_packet pkt;
+
+	(void)cb_data;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	/* Execute a stop on BeagleLogic */
+	beaglelogic_stop(devc);
+
+	/* lseek to offset 0, flushes the cache */
+	lseek(devc->fd, 0, SEEK_SET);
+
+	/* Remove session source and send EOT packet */
+	sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
+	pkt.type = SR_DF_END;
+	pkt.payload = NULL;
+	sr_session_send(sdi, &pkt);
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver beaglelogic_driver_info = {
+	.name = "beaglelogic",
+	.longname = "BeagleLogic",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/beaglelogic/beaglelogic.h b/src/hardware/beaglelogic/beaglelogic.h
new file mode 100644
index 0000000..9015c61
--- /dev/null
+++ b/src/hardware/beaglelogic/beaglelogic.h
@@ -0,0 +1,210 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Kumar Abhishek <abhishek at theembeddedkitchen.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BEAGLELOGIC_H_
+#define BEAGLELOGIC_H_
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* BeagleLogic device node name */
+#define BEAGLELOGIC_DEV_NODE        "/dev/beaglelogic"
+#define BEAGLELOGIC_SYSFS_ATTR(a)   "/sys/devices/virtual/misc/beaglelogic/"\
+					__STRING(a)
+
+/* Reproduced verbatim from beaglelogic.h in the kernel tree until the kernel
+ * module hits the mainline. Contains the ABI, so DO NOT TOUCH this section */
+
+/* ioctl calls that can be issued on /dev/beaglelogic */
+#define IOCTL_BL_GET_VERSION        _IOR('k', 0x20, uint32_t)
+
+#define IOCTL_BL_GET_SAMPLE_RATE    _IOR('k', 0x21, uint32_t)
+#define IOCTL_BL_SET_SAMPLE_RATE    _IOW('k', 0x21, uint32_t)
+
+#define IOCTL_BL_GET_SAMPLE_UNIT    _IOR('k', 0x22, uint32_t)
+#define IOCTL_BL_SET_SAMPLE_UNIT    _IOW('k', 0x22, uint32_t)
+
+#define IOCTL_BL_GET_TRIGGER_FLAGS  _IOR('k', 0x23, uint32_t)
+#define IOCTL_BL_SET_TRIGGER_FLAGS  _IOW('k', 0x23, uint32_t)
+
+#define IOCTL_BL_CACHE_INVALIDATE    _IO('k', 0x25)
+
+#define IOCTL_BL_GET_BUFFER_SIZE    _IOR('k', 0x26, uint32_t)
+#define IOCTL_BL_SET_BUFFER_SIZE    _IOW('k', 0x26, uint32_t)
+
+#define IOCTL_BL_GET_BUFUNIT_SIZE   _IOR('k', 0x27, uint32_t)
+
+#define IOCTL_BL_FILL_TEST_PATTERN   _IO('k', 0x28)
+
+#define IOCTL_BL_START               _IO('k', 0x29)
+#define IOCTL_BL_STOP                _IO('k', 0x2A)
+
+/* Possible States of BeagleLogic */
+enum beaglelogic_states {
+	STATE_BL_DISABLED,	/* Powered off (at module start) */
+	STATE_BL_INITIALIZED,	/* Powered on */
+	STATE_BL_MEMALLOCD,	/* Buffers allocated */
+	STATE_BL_ARMED,		/* All Buffers DMA-mapped and configuration done */
+	STATE_BL_RUNNING,	/* Data being captured */
+	STATE_BL_REQUEST_STOP,	/* Stop requested */
+	STATE_BL_ERROR   	/* Buffer overrun */
+};
+
+/* Setting attributes */
+enum beaglelogic_triggerflags {
+	BL_TRIGGERFLAGS_ONESHOT = 0,
+	BL_TRIGGERFLAGS_CONTINUOUS
+};
+
+/* Possible sample unit / formats */
+enum beaglelogic_sampleunit {
+	BL_SAMPLEUNIT_16_BITS = 0,
+	BL_SAMPLEUNIT_8_BITS
+};
+/* END beaglelogic.h */
+
+/* For all the functions below:
+ * Parameters:
+ * 	devc : Device context structure to operate on
+ * Returns:
+ * 	SR_OK or SR_ERR
+ */
+
+SR_PRIV int beaglelogic_open_nonblock(struct dev_context *devc);
+SR_PRIV int beaglelogic_close(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_get_buffersize(struct dev_context *devc);
+SR_PRIV int beaglelogic_set_buffersize(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_get_samplerate(struct dev_context *devc);
+SR_PRIV int beaglelogic_set_samplerate(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_get_sampleunit(struct dev_context *devc);
+SR_PRIV int beaglelogic_set_sampleunit(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_get_triggerflags(struct dev_context *devc);
+SR_PRIV int beaglelogic_set_triggerflags(struct dev_context *devc);
+
+/* Start and stop the capture operation */
+SR_PRIV int beaglelogic_start(struct dev_context *devc);
+SR_PRIV int beaglelogic_stop(struct dev_context *devc);
+
+/* Get the last error size */
+SR_PRIV int beaglelogic_getlasterror(struct dev_context *devc);
+
+/* Gets the unit size of the capture buffer (usually 4 or 8 MB) */
+SR_PRIV int beaglelogic_get_bufunitsize(struct dev_context *devc);
+
+SR_PRIV int beaglelogic_mmap(struct dev_context *devc);
+SR_PRIV int beaglelogic_munmap(struct dev_context *devc);
+
+/* Sources */
+SR_PRIV inline int beaglelogic_open_nonblock(struct dev_context *devc) {
+	devc->fd = open(BEAGLELOGIC_DEV_NODE, O_RDONLY | O_NONBLOCK);
+	return (devc->fd == -1 ? SR_ERR : SR_OK);
+}
+
+SR_PRIV inline int beaglelogic_close(struct dev_context *devc) {
+	return close(devc->fd);
+}
+
+SR_PRIV inline int beaglelogic_get_buffersize(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_GET_BUFFER_SIZE, &devc->buffersize);
+}
+
+SR_PRIV inline int beaglelogic_set_buffersize(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_SET_BUFFER_SIZE, devc->buffersize);
+}
+
+/* This is treated differently as it gets a uint64_t while a uint32_t is read */
+SR_PRIV inline int beaglelogic_get_samplerate(struct dev_context *devc) {
+	uint32_t arg, err;
+	err = ioctl(devc->fd, IOCTL_BL_GET_SAMPLE_RATE, &arg);
+	devc->cur_samplerate = arg;
+	return err;
+}
+
+SR_PRIV inline int beaglelogic_set_samplerate(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_RATE,
+			(uint32_t)devc->cur_samplerate);
+}
+
+SR_PRIV inline int beaglelogic_get_sampleunit(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_GET_SAMPLE_UNIT, &devc->sampleunit);
+}
+
+SR_PRIV inline int beaglelogic_set_sampleunit(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_UNIT, devc->sampleunit);
+}
+
+SR_PRIV inline int beaglelogic_get_triggerflags(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_GET_TRIGGER_FLAGS, &devc->triggerflags);
+}
+
+SR_PRIV inline int beaglelogic_set_triggerflags(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_SET_TRIGGER_FLAGS, devc->triggerflags);
+}
+
+SR_PRIV int beaglelogic_getlasterror(struct dev_context *devc) {
+	int fd;
+	char buf[16];
+	int ret;
+
+	if ((fd = open(BEAGLELOGIC_SYSFS_ATTR(lasterror), O_RDONLY)) == -1)
+		return SR_ERR;
+
+	if ((ret = read(fd, buf, 16)) < 0)
+		return SR_ERR;
+
+	close(fd);
+	devc->last_error = strtoul(buf, NULL, 10);
+
+	return SR_OK;
+}
+
+SR_PRIV inline int beaglelogic_start(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_START);
+}
+
+SR_PRIV inline int beaglelogic_stop(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_STOP);
+}
+
+SR_PRIV int beaglelogic_get_bufunitsize(struct dev_context *devc) {
+	return ioctl(devc->fd, IOCTL_BL_GET_BUFUNIT_SIZE, &devc->bufunitsize);
+}
+
+SR_PRIV int beaglelogic_mmap(struct dev_context *devc) {
+	if (!devc->buffersize)
+		beaglelogic_get_buffersize(devc);
+	devc->sample_buf = mmap(NULL, devc->buffersize,
+			PROT_READ, MAP_SHARED, devc->fd, 0);
+	return (devc->sample_buf == MAP_FAILED ? -1 : SR_OK);
+}
+
+SR_PRIV int beaglelogic_munmap(struct dev_context *devc) {
+	return munmap(devc->sample_buf, devc->buffersize);
+}
+
+#endif
diff --git a/src/hardware/beaglelogic/protocol.c b/src/hardware/beaglelogic/protocol.c
new file mode 100644
index 0000000..55e2b64
--- /dev/null
+++ b/src/hardware/beaglelogic/protocol.c
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Kumar Abhishek <abhishek at theembeddedkitchen.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "protocol.h"
+
+/* Define data packet size independent of packet (bufunitsize bytes) size
+ * from the BeagleLogic kernel module */
+#define PACKET_SIZE	(512 * 1024)
+
+/* This implementation is zero copy from the libsigrok side.
+ * It does not copy any data, just passes a pointer from the mmap'ed
+ * kernel buffers appropriately. It is up to the application which is
+ * using libsigrok to decide how to deal with the data.
+ */
+SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data)
+{
+	const struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_logic logic;
+
+	int trigger_offset;
+	int pre_trigger_samples;
+	uint32_t packetsize;
+	uint64_t bytes_remaining;
+
+	if (!(sdi = cb_data) || !(devc = sdi->priv))
+		return TRUE;
+
+	packetsize = PACKET_SIZE;
+	logic.unitsize = SAMPLEUNIT_TO_BYTES(devc->sampleunit);
+
+	if (revents == G_IO_IN) {
+		sr_info("In callback G_IO_IN, offset=%d", devc->offset);
+
+		bytes_remaining = (devc->limit_samples * logic.unitsize) -
+				devc->bytes_read;
+
+		/* Configure data packet */
+		packet.type = SR_DF_LOGIC;
+		packet.payload = &logic;
+		logic.data = devc->sample_buf + devc->offset;
+		logic.length = MIN(packetsize, bytes_remaining);
+
+		if (devc->trigger_fired) {
+			/* Send the incoming transfer to the session bus. */
+			sr_session_send(devc->cb_data, &packet);
+		} else {
+			/* Check for trigger */
+			trigger_offset = soft_trigger_logic_check(devc->stl,
+					logic.data, packetsize, &pre_trigger_samples);
+			if (trigger_offset > -1) {
+				devc->bytes_read += pre_trigger_samples * logic.unitsize;
+				trigger_offset *= logic.unitsize;
+				logic.length = MIN(packetsize - trigger_offset,
+						bytes_remaining);
+				logic.data += trigger_offset;
+
+				sr_session_send(devc->cb_data, &packet);
+
+				devc->trigger_fired = TRUE;
+			}
+		}
+
+		/* Move the read pointer forward */
+		lseek(fd, packetsize, SEEK_CUR);
+
+		/* Update byte count and offset (roll over if needed) */
+		devc->bytes_read += logic.length;
+		if ((devc->offset += packetsize) >= devc->buffersize) {
+			/* One shot capture, we abort and settle with less than
+			 * the required number of samples */
+			if (devc->triggerflags)
+				devc->offset = 0;
+			else
+				packetsize = 0;
+		}
+	}
+
+	/* EOF Received or we have reached the limit */
+	if (devc->bytes_read >= devc->limit_samples * logic.unitsize ||
+			packetsize == 0) {
+		/* Send EOA Packet, stop polling */
+		packet.type = SR_DF_END;
+		packet.payload = NULL;
+		sr_session_send(devc->cb_data, &packet);
+
+		sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
+	}
+
+	return TRUE;
+}
diff --git a/src/hardware/beaglelogic/protocol.h b/src/hardware/beaglelogic/protocol.h
new file mode 100644
index 0000000..75e7472
--- /dev/null
+++ b/src/hardware/beaglelogic/protocol.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Kumar Abhishek <abhishek at theembeddedkitchen.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_BEAGLELOGIC_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_BEAGLELOGIC_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "beaglelogic"
+
+/* Maximum possible input channels */
+#define NUM_CHANNELS            14
+
+#define SAMPLEUNIT_TO_BYTES(x)	((x) == 1 ? 1 : 2)
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/* Model-specific information */
+	int max_channels;
+	uint32_t fw_ver;
+
+	/* Acquisition settings: see beaglelogic.h */
+	uint64_t cur_samplerate;
+	uint64_t limit_samples;
+	uint32_t sampleunit;
+	uint32_t triggerflags;
+	uint64_t capture_ratio;
+
+	/* Buffers: size of each buffer block and the total buffer area */
+	uint32_t bufunitsize;
+	uint32_t buffersize;
+
+	/* Operational state */
+	int fd;
+	GPollFD pollfd;
+	int last_error;
+
+	uint64_t bytes_read;
+	uint64_t sent_samples;
+	uint32_t offset;
+	uint8_t *sample_buf;	/* mmap'd kernel buffer here */
+
+	void *cb_data;
+
+	/* Trigger logic */
+	struct soft_trigger_logic *stl;
+	gboolean trigger_fired;
+};
+
+SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/hardware/brymen-bm86x/api.c b/src/hardware/brymen-bm86x/api.c
similarity index 75%
rename from hardware/brymen-bm86x/api.c
rename to src/hardware/brymen-bm86x/api.c
index 169721d..64e492c 100644
--- a/hardware/brymen-bm86x/api.c
+++ b/src/hardware/brymen-bm86x/api.c
@@ -17,30 +17,30 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
 
 #define BRYMEN_BC86X "0820.0001"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_MULTIMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
 };
 
 SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info;
-static struct sr_dev_driver *di = &brymen_bm86x_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	GSList *usb_devices, *devices, *l;
 	struct drv_context *drvc;
@@ -48,10 +48,9 @@ static GSList *scan(GSList *options)
 	struct sr_dev_inst *sdi;
 	struct sr_usb_dev_inst *usb;
 	struct sr_config *src;
-	struct sr_channel *ch;
 	const char *conn;
 
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	conn = BRYMEN_BC86X;
@@ -73,25 +72,15 @@ static GSList *scan(GSList *options)
 	for (l = usb_devices; l; l = l->next) {
 		usb = l->data;
 
-		if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
-		                            "Brymen", "BM869", NULL))) {
-			sr_err("sr_dev_inst_new returned NULL.");
-			return NULL;
-		}
-
-		if (!(devc = g_try_malloc0(sizeof(*devc)))) {
-			sr_err("Device context malloc failed.");
-			return NULL;
-		}
-
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup("Brymen");
+		sdi->model = g_strdup("BM869");
+		devc = g_malloc0(sizeof(struct dev_context));
 		sdi->priv = devc;
 		sdi->driver = di;
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P2")))
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P2");
 
 		sdi->inst_type = SR_INST_USB;
 		sdi->conn = usb;
@@ -103,14 +92,15 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
-	struct drv_context *drvc = di->priv;
+	struct sr_dev_driver *di = sdi->driver;
+	struct drv_context *drvc = di->context;
 	struct sr_usb_dev_inst *usb;
 	struct dev_context *devc;
 	int ret;
@@ -120,6 +110,8 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 	if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
 		sdi->status = SR_ST_ACTIVE;
+	else
+		return SR_ERR;
 
 	/* Detach kernel drivers which grabbed this device (if any). */
 	if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
@@ -152,6 +144,9 @@ static int dev_close(struct sr_dev_inst *sdi)
 	struct dev_context *devc;
 	int ret;
 
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
 	usb = sdi->conn;
 	devc = sdi->priv;
 
@@ -177,12 +172,12 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return ret;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc = sdi->priv;
@@ -203,7 +198,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -221,11 +216,9 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_samples = g_variant_get_uint64(data);
-		sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
 		break;
 	case SR_CONF_LIMIT_MSEC:
 		devc->limit_msec = g_variant_get_uint64(data);
-		sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
 		break;
 	default:
 		return SR_ERR_NA;
@@ -234,7 +227,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -242,12 +235,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -261,17 +254,19 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 {
 	struct dev_context *devc;
 
+	(void)cb_data;
+
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
 	devc = sdi->priv;
-	devc->session_cb_data = cb_data;
 	devc->start_time = g_get_monotonic_time();
 
 	/* Send header packet to the session bus. */
-	std_session_send_df_header(cb_data, LOG_PREFIX);
+	std_session_send_df_header(sdi, LOG_PREFIX);
 
-	sr_source_add(0, 0, 10, brymen_bm86x_receive_data, (void *)sdi);
+	sr_session_source_add(sdi->session, -1, 0, 10,
+			brymen_bm86x_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -280,14 +275,16 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
 	struct sr_datafeed_packet packet;
 
+	(void)cb_data;
+
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
 	/* Send end packet to the session bus. */
 	packet.type = SR_DF_END;
-	sr_session_send(cb_data, &packet);
+	sr_session_send(sdi, &packet);
 
-	sr_source_remove(0);
+	sr_session_source_remove(sdi->session, -1);
 
 	return SR_OK;
 }
@@ -308,5 +305,5 @@ SR_PRIV struct sr_dev_driver brymen_bm86x_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/brymen-bm86x/protocol.c b/src/hardware/brymen-bm86x/protocol.c
similarity index 94%
rename from hardware/brymen-bm86x/protocol.c
rename to src/hardware/brymen-bm86x/protocol.c
index b8fdcaa..17d1a90 100644
--- a/hardware/brymen-bm86x/protocol.c
+++ b/src/hardware/brymen-bm86x/protocol.c
@@ -17,13 +17,14 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include <math.h>
 #include "protocol.h"
 
 #define USB_TIMEOUT 500
 
-static char char_map[128] = {
+static const char char_map[128] = {
 	[0x20] = '-',
 	[0x5F] = '0',
 	[0x50] = '1',
@@ -72,7 +73,7 @@ static int brymen_bm86x_parse_digits(const unsigned char *buf, int length,
 }
 
 static void brymen_bm86x_parse(unsigned char *buf, float *floatval,
-                               struct sr_datafeed_analog *analog)
+                               struct sr_datafeed_analog_old *analog)
 {
 	char str[16], temp_unit;
 	int ret1, ret2, over_limit;
@@ -161,6 +162,9 @@ static void brymen_bm86x_parse(unsigned char *buf, float *floatval,
 		} else if (buf[9] & 0x04) {
 			analog[1].mq = SR_MQ_CURRENT;
 			analog[1].unit = SR_UNIT_AMPERE;
+		} else if (buf[9] & 0x08) {
+			analog[1].mq = SR_MQ_CURRENT;
+			analog[1].unit = SR_UNIT_PERCENTAGE;
 		} else if (buf[14] & 0x04) {
 			analog[1].mq = SR_MQ_FREQUENCY;
 			analog[1].unit = SR_UNIT_HERTZ;
@@ -191,7 +195,7 @@ static void brymen_bm86x_handle_packet(const struct sr_dev_inst *sdi,
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog[2];
+	struct sr_datafeed_analog_old analog[2];
 	float floatval[2];
 
 	devc = sdi->priv;
@@ -209,9 +213,9 @@ static void brymen_bm86x_handle_packet(const struct sr_dev_inst *sdi,
 		analog[0].num_samples = 1;
 		analog[0].data = &floatval[0];
 		analog[0].channels = g_slist_append(NULL, sdi->channels->data);
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = &analog[0];
-		sr_session_send(devc->session_cb_data, &packet);
+		sr_session_send(sdi, &packet);
 		g_slist_free(analog[0].channels);
 	}
 
@@ -220,9 +224,9 @@ static void brymen_bm86x_handle_packet(const struct sr_dev_inst *sdi,
 		analog[1].num_samples = 1;
 		analog[1].data = &floatval[1];
 		analog[1].channels = g_slist_append(NULL, sdi->channels->next->data);
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = &analog[1];
-		sr_session_send(devc->session_cb_data, &packet);
+		sr_session_send(sdi, &packet);
 		g_slist_free(analog[1].channels);
 	}
 
@@ -254,7 +258,7 @@ static int brymen_bm86x_send_command(const struct sr_dev_inst *sdi)
 	}
 
 	if (ret != sizeof(buf)) {
-		sr_err("Short packet: sent %d/%ld bytes.", ret, sizeof(buf));
+		sr_err("Short packet: sent %d/%zu bytes.", ret, sizeof(buf));
 		return SR_ERR;
 	}
 
@@ -290,7 +294,7 @@ static int brymen_bm86x_read_interrupt(const struct sr_dev_inst *sdi)
 	}
 
 	if (transferred != sizeof(buf)) {
-		sr_err("Short packet: received %d/%d bytes.", transferred, sizeof(buf));
+		sr_err("Short packet: received %d/%zu bytes.", transferred, sizeof(buf));
 		return SR_ERR;
 	}
 
diff --git a/hardware/brymen-bm86x/protocol.h b/src/hardware/brymen-bm86x/protocol.h
similarity index 94%
rename from hardware/brymen-bm86x/protocol.h
rename to src/hardware/brymen-bm86x/protocol.h
index d3d8955..6e33e8b 100644
--- a/hardware/brymen-bm86x/protocol.h
+++ b/src/hardware/brymen-bm86x/protocol.h
@@ -22,7 +22,7 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "brymen-bm86x"
@@ -32,7 +32,6 @@ struct dev_context {
 	/* Acquisition settings */
 	uint64_t limit_samples;    /**< The sampling limit (in number of samples).*/
 	uint64_t limit_msec;       /**< The time limit (in milliseconds). */
-	void *session_cb_data;     /**< Opaque pointer passed in by the frontend. */
 
 	/* Operational state */
 	int detached_kernel_driver;/**< Whether kernel driver was detached or not */
diff --git a/hardware/brymen-dmm/api.c b/src/hardware/brymen-dmm/api.c
similarity index 77%
rename from hardware/brymen-dmm/api.c
rename to src/hardware/brymen-dmm/api.c
index 41a3181..911789f 100644
--- a/hardware/brymen-dmm/api.c
+++ b/src/hardware/brymen-dmm/api.c
@@ -17,24 +17,25 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_MULTIMETER,
-	SR_CONF_LIMIT_SAMPLES,
 	SR_CONF_CONTINUOUS,
-	SR_CONF_LIMIT_MSEC,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
 };
 
 SR_PRIV struct sr_dev_driver brymen_bm857_driver_info;
 static struct sr_dev_driver *di = &brymen_bm857_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
@@ -44,17 +45,15 @@ 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_channel *ch;
 	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;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
 
 	sr_info("Probing port %s.", conn);
@@ -75,24 +74,17 @@ static GSList *brymen_scan(const char *conn, const char *serialcomm)
 
 	sr_info("Found device on port %s.", conn);
 
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Brymen", "BM85x", NULL)))
-		goto scan_cleanup;
-
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		goto scan_cleanup;
-	}
-
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("Brymen");
+	sdi->model = g_strdup("BM85x");
+	devc = g_malloc0(sizeof(struct dev_context));
 	sdi->inst_type = SR_INST_SERIAL;
 	sdi->conn = serial;
-	drvc = di->priv;
+	drvc = di->context;
 	sdi->priv = devc;
 	sdi->driver = di;
-
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-		goto scan_cleanup;
-
-	sdi->channels = g_slist_append(sdi->channels, ch);
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
 
@@ -102,7 +94,7 @@ scan_cleanup:
 	return devices;
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct sr_config *src;
@@ -110,7 +102,7 @@ static GSList *scan(GSList *options)
 	const char *conn, *serialcomm;
 
 	devices = NULL;
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	conn = serialcomm = NULL;
@@ -139,17 +131,17 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -166,7 +158,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	}
 
 	ret = SR_OK;
-	switch (id) {
+	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_samples = g_variant_get_uint64(data);
 		break;
@@ -180,7 +172,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -188,12 +180,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -230,8 +222,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 	/* Poll every 50ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 50,
-		      brymen_dmm_receive_data, (void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 50,
+			brymen_dmm_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -258,5 +250,5 @@ SR_PRIV struct sr_dev_driver brymen_bm857_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/brymen-dmm/parser.c b/src/hardware/brymen-dmm/parser.c
similarity index 97%
rename from hardware/brymen-dmm/parser.c
rename to src/hardware/brymen-dmm/parser.c
index e4b1227..aaa2c3f 100644
--- a/hardware/brymen-dmm/parser.c
+++ b/src/hardware/brymen-dmm/parser.c
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
 
 #define MAX_PACKET_LEN 22
@@ -78,7 +79,8 @@ static int bm_send_command(uint8_t command, uint8_t arg1, uint8_t arg2,
 	/* TODO: How to compute the checksum? Hardware seems to ignore it. */
 
 	/* Request reading. */
-	written = serial_write(serial, &cmdout, sizeof(cmdout));
+	written = serial_write_blocking(serial, &cmdout, sizeof(cmdout),
+			serial_timeout(serial, sizeof(cmdout)));
 	if (written != sizeof(cmdout))
 		return SR_ERR;
 
@@ -190,7 +192,7 @@ static void parse_flags(const uint8_t *buf, struct brymen_flags *info)
 }
 
 SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
-		struct sr_datafeed_analog *analog, void *info)
+		struct sr_datafeed_analog_old *analog, void *info)
 {
 	struct brymen_flags flags;
 	struct brymen_header *hdr;
diff --git a/hardware/brymen-dmm/protocol.c b/src/hardware/brymen-dmm/protocol.c
similarity index 92%
rename from hardware/brymen-dmm/protocol.c
rename to src/hardware/brymen-dmm/protocol.c
index dff7b98..4db460a 100644
--- a/hardware/brymen-dmm/protocol.c
+++ b/src/hardware/brymen-dmm/protocol.c
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
 
 static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi)
@@ -24,7 +25,7 @@ static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi)
 	float floatval;
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 
 	devc = sdi->priv;
 
@@ -39,7 +40,7 @@ static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi)
 
 	if (analog.mq != -1) {
 		/* Got a measurement. */
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = &analog;
 		sr_session_send(devc->cb_data, &packet);
 		devc->num_samples++;
@@ -57,7 +58,7 @@ static void handle_new_data(struct sr_dev_inst *sdi)
 
 	/* 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);
+	len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
 	if (len < 1) {
 		sr_err("Serial port read error: %d.", len);
 		return;
@@ -179,7 +180,9 @@ SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
 {
 	int64_t start, time, byte_delay_us;
 	size_t ibuf, i, maxlen;
-	int status, len, packet_len, stream_len;
+	ssize_t len, stream_len;
+	int packet_len;
+	int status;
 
 	maxlen = *buflen;
 
@@ -187,15 +190,15 @@ SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
 	       "ms, baudrate = %d).", serial->port, timeout_ms, baudrate);
 
 	/* Assume 8n1 transmission. That is 10 bits for every byte. */
-	byte_delay_us = 10 * (1000000 / baudrate);
+	byte_delay_us = 10 * ((1000 * 1000) / baudrate);
 	start = g_get_monotonic_time();
 
 	packet_len = i = ibuf = len = 0;
 	while (ibuf < maxlen) {
-		len = serial_read(serial, &buf[ibuf], maxlen - ibuf);
+		len = serial_read_nonblocking(serial, &buf[ibuf], maxlen - ibuf);
 		if (len > 0) {
 			ibuf += len;
-			sr_spew("Read %d bytes.", len);
+			sr_spew("Read %zd bytes.", len);
 		}
 
 		time = g_get_monotonic_time() - start;
@@ -206,7 +209,7 @@ SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
 			/* How large of a packet are we expecting? */
 			packet_len = stream_len;
 			status = get_packet_size(&buf[i], &packet_len);
-			switch(status) {
+			switch (status) {
 			case PACKET_HEADER_OK:
 				/* We know how much data we need to wait for. */
 				break;
@@ -247,14 +250,14 @@ SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
 
 		if (time >= (int64_t)timeout_ms) {
 			/* Timeout */
-			sr_dbg("Detection timed out after %dms.", time);
+			sr_dbg("Detection timed out after %" PRIi64 "ms.", time);
 			break;
 		}
 		g_usleep(byte_delay_us);
 	}
 
 	*buflen = ibuf;
-	sr_err("Didn't find a valid packet (read %d bytes).", ibuf);
+	sr_err("Didn't find a valid packet (read %zu bytes).", ibuf);
 
 	return SR_ERR;
 }
diff --git a/hardware/brymen-dmm/protocol.h b/src/hardware/brymen-dmm/protocol.h
similarity index 96%
rename from hardware/brymen-dmm/protocol.h
rename to src/hardware/brymen-dmm/protocol.h
index 7c9aaae..24fe63d 100644
--- a/hardware/brymen-dmm/protocol.h
+++ b/src/hardware/brymen-dmm/protocol.h
@@ -25,7 +25,7 @@
 #include <stdlib.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "brymen-dmm"
@@ -77,7 +77,7 @@ SR_PRIV int brymen_packet_length(const uint8_t *buf, int *len);
 SR_PRIV gboolean brymen_packet_is_valid(const uint8_t *buf);
 
 SR_PRIV int brymen_parse(const uint8_t *buf, float *floatval,
-		struct sr_datafeed_analog *analog, void *info);
+		struct sr_datafeed_analog_old *analog, void *info);
 
 SR_PRIV int brymen_stream_detect(struct sr_serial_dev_inst *serial,
 				 uint8_t *buf, size_t *buflen,
diff --git a/hardware/cem-dt-885x/api.c b/src/hardware/cem-dt-885x/api.c
similarity index 71%
rename from hardware/cem-dt-885x/api.c
rename to src/hardware/cem-dt-885x/api.c
index 34853a0..8a9a6e1 100644
--- a/hardware/cem-dt-885x/api.c
+++ b/src/hardware/cem-dt-885x/api.c
@@ -17,29 +17,34 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include "protocol.h"
 
 #define SERIALCOMM "9600/8n1"
+
 /* 23ms is the longest interval between tokens. */
-#define MAX_SCAN_TIME 25 * 1000
+#define MAX_SCAN_TIME_US (25 * 1000)
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t drvopts[] = {
 	SR_CONF_SOUNDLEVELMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_CONTINUOUS,
-	SR_CONF_SPL_WEIGHT_FREQ,
-	SR_CONF_SPL_WEIGHT_TIME,
-	SR_CONF_SPL_MEASUREMENT_RANGE,
-	SR_CONF_DATALOG,
-	SR_CONF_HOLD_MAX,
-	SR_CONF_HOLD_MIN,
-	SR_CONF_POWER_OFF,
-	SR_CONF_DATA_SOURCE,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SPL_WEIGHT_FREQ | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SPL_WEIGHT_TIME | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SPL_MEASUREMENT_RANGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_DATALOG | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_HOLD_MAX | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_HOLD_MIN | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_POWER_OFF | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
 static const char *weight_freq[] = {
@@ -63,23 +68,21 @@ static const char *data_sources[] = {
 	"Live",
 	"Memory",
 };
-SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info;
-static struct sr_dev_driver *di = &cem_dt_885x_driver_info;
 
+SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_config *src;
 	struct sr_serial_dev_inst *serial;
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	GSList *l, *devices;
 	gint64 start;
 	const char *conn;
@@ -94,41 +97,32 @@ static GSList *scan(GSList *options)
 	if (!conn)
 		return NULL;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, SERIALCOMM)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, SERIALCOMM);
 
-	if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDONLY) != SR_OK)
 		return NULL;
 
 	devices = NULL;
-	drvc = di->priv;
+	drvc = di->context;
 	start = g_get_monotonic_time();
-	while (g_get_monotonic_time() - start < MAX_SCAN_TIME) {
-		if (serial_read(serial, &c, 1) == 1 && c == 0xa5) {
+	while (g_get_monotonic_time() - start < MAX_SCAN_TIME_US) {
+		if (serial_read_nonblocking(serial, &c, 1) == 1 && c == 0xa5) {
 			/* Found one. */
-			if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "CEM",
-					"DT-885x", NULL)))
-				return NULL;
-
-			if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-				sr_dbg("Device context malloc failed.");
-				return NULL;
-			}
+			sdi = g_malloc0(sizeof(struct sr_dev_inst));
+			sdi->status = SR_ST_INACTIVE;
+			sdi->vendor = g_strdup("CEM");
+			sdi->model = g_strdup("DT-885x");
+			devc = g_malloc0(sizeof(struct dev_context));
 			devc->cur_mqflags = 0;
 			devc->recording = -1;
 			devc->cur_meas_range = 0;
 			devc->cur_data_source = DATA_SOURCE_LIVE;
 			devc->enable_data_source_memory = FALSE;
-
-			if (!(sdi->conn = sr_serial_dev_inst_new(conn, SERIALCOMM)))
-				return NULL;
-
+			sdi->conn = sr_serial_dev_inst_new(conn, SERIALCOMM);
 			sdi->inst_type = SR_INST_SERIAL;
 			sdi->priv = devc;
 			sdi->driver = di;
-			if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "SPL")))
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "SPL");
 			drvc->instances = g_slist_append(drvc->instances, sdi);
 			devices = g_slist_append(devices, sdi);
 			break;
@@ -142,9 +136,9 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -160,12 +154,12 @@ static int dev_open(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -237,7 +231,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -327,7 +321,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	GVariant *tuple, *range[2];
@@ -335,40 +329,50 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	unsigned int i;
 	int ret;
 
-	(void)sdi;
 	(void)cg;
 
 	ret = SR_OK;
-	switch (key) {
-	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_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;
-	case SR_CONF_SPL_WEIGHT_FREQ:
-		*data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq));
-		break;
-	case SR_CONF_SPL_WEIGHT_TIME:
-		*data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time));
-		break;
-	case SR_CONF_SPL_MEASUREMENT_RANGE:
-		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-		for (i = 0; i < ARRAY_SIZE(meas_ranges); i++) {
-			range[0] = g_variant_new_uint64(meas_ranges[i][0]);
-			range[1] = g_variant_new_uint64(meas_ranges[i][1]);
-			tuple = g_variant_new_tuple(range, 2);
-			g_variant_builder_add_value(&gvb, tuple);
+	if (!sdi) {
+		switch (key) {
+		case SR_CONF_SCAN_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+			break;
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	} else {
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+			break;
+		case SR_CONF_SPL_WEIGHT_FREQ:
+			*data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq));
+			break;
+		case SR_CONF_SPL_WEIGHT_TIME:
+			*data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time));
+			break;
+		case SR_CONF_SPL_MEASUREMENT_RANGE:
+			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+			for (i = 0; i < ARRAY_SIZE(meas_ranges); i++) {
+				range[0] = g_variant_new_uint64(meas_ranges[i][0]);
+				range[1] = g_variant_new_uint64(meas_ranges[i][1]);
+				tuple = g_variant_new_tuple(range, 2);
+				g_variant_builder_add_value(&gvb, tuple);
+			}
+			*data = g_variant_builder_end(&gvb);
+			break;
+		case SR_CONF_DATA_SOURCE:
+			*data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+			break;
+		default:
+			return SR_ERR_NA;
 		}
-		*data = g_variant_builder_end(&gvb);
-		break;
-	case SR_CONF_DATA_SOURCE:
-		*data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
-		break;
-	default:
-		return SR_ERR_NA;
 	}
 
 	return ret;
@@ -397,8 +401,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 	/* Poll every 100ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 150, cem_dt_885x_receive_data,
-			(void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 150,
+			cem_dt_885x_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -428,5 +432,5 @@ SR_PRIV struct sr_dev_driver cem_dt_885x_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/cem-dt-885x/protocol.c b/src/hardware/cem-dt-885x/protocol.c
similarity index 96%
rename from hardware/cem-dt-885x/protocol.c
rename to src/hardware/cem-dt-885x/protocol.c
index 19e0830..68d003a 100644
--- a/hardware/cem-dt-885x/protocol.c
+++ b/src/hardware/cem-dt-885x/protocol.c
@@ -17,11 +17,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include "protocol.h"
 
 /* Length of expected payload for each token. */
-static int token_payloads[][2] = {
+static const int token_payloads[][2] = {
 	{ TOKEN_WEIGHT_TIME_FAST, 0 },
 	{ TOKEN_WEIGHT_TIME_SLOW, 0 },
 	{ TOKEN_HOLD_MAX, 0 },
@@ -65,7 +66,7 @@ static void process_mset(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	GString *dbg;
 	float fvalue;
 	int i;
@@ -84,7 +85,7 @@ static void process_mset(const struct sr_dev_inst *sdi)
 		g_string_free(dbg, TRUE);
 	}
 
-	switch(devc->token) {
+	switch (devc->token) {
 	case TOKEN_WEIGHT_TIME_FAST:
 		devc->cur_mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_F;
 		devc->cur_mqflags &= ~SR_MQFLAG_SPL_TIME_WEIGHT_S;
@@ -130,14 +131,14 @@ static void process_mset(const struct sr_dev_inst *sdi)
 				break;
 			}
 		}
-		memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+		memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 		analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
 		analog.mqflags = devc->cur_mqflags;
 		analog.unit = SR_UNIT_DECIBEL_SPL;
 		analog.channels = sdi->channels;
 		analog.num_samples = 1;
 		analog.data = &devc->last_spl;
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = &analog;
 		sr_session_send(devc->cb_data, &packet);
 
@@ -166,7 +167,7 @@ static void process_mset(const struct sr_dev_inst *sdi)
 	case TOKEN_MEAS_RANGE_OK:
 	case TOKEN_MEAS_RANGE_OVER:
 	case TOKEN_MEAS_RANGE_UNDER:
-		/* Not useful, or not expressable in sigrok. */
+		/* Not useful, or not expressible in sigrok. */
 		break;
 	}
 
@@ -177,26 +178,26 @@ static void send_data(const struct sr_dev_inst *sdi, unsigned char *data,
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	float fbuf[SAMPLES_PER_PACKET];
 	unsigned int i;
 
 	devc = sdi->priv;
 
-	for (i = 0; i < num_samples; i ++) {
+	for (i = 0; i < num_samples; i++) {
 		fbuf[i] = ((data[i * 2] & 0xf0) >> 4) * 100;
 		fbuf[i] += (data[i * 2] & 0x0f) * 10;
 		fbuf[i] += ((data[i * 2 + 1] & 0xf0) >> 4);
 		fbuf[i] += (data[i * 2 + 1] & 0x0f) / 10.0;
 	}
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 	analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
 	analog.mqflags = devc->cur_mqflags;
 	analog.unit = SR_UNIT_DECIBEL_SPL;
 	analog.channels = sdi->channels;
 	analog.num_samples = num_samples;
 	analog.data = fbuf;
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 
@@ -385,7 +386,7 @@ SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data)
 	devc = sdi->priv;
 	serial = sdi->conn;
 	if (revents == G_IO_IN) {
-		if (serial_read(serial, &c, 1) != 1)
+		if (serial_read_nonblocking(serial, &c, 1) != 1)
 			return TRUE;
 		process_byte(sdi, c, TRUE);
 
@@ -396,7 +397,7 @@ SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data)
 			} else {
 				/* Tell device to start transferring from memory. */
 				cmd = CMD_TRANSFER_MEMORY;
-				serial_write(serial, &cmd, 1);
+				serial_write_nonblocking(serial, &cmd, 1);
 			}
 		}
 	}
@@ -404,7 +405,6 @@ SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data)
 	return TRUE;
 }
 
-
 static int wait_for_token(const struct sr_dev_inst *sdi, int8_t *tokens, int timeout)
 {
 	struct dev_context *devc;
@@ -418,7 +418,7 @@ static int wait_for_token(const struct sr_dev_inst *sdi, int8_t *tokens, int tim
 	devc->state = ST_INIT;
 	start_time = g_get_monotonic_time() / 1000;
 	while (TRUE) {
-		if (serial_read(serial, &c, 1) != 1)
+		if (serial_read_nonblocking(serial, &c, 1) != 1)
 			/* Device might have gone away. */
 			return SR_ERR;
 		process_byte(sdi, c, FALSE);
@@ -453,7 +453,7 @@ static int cem_dt_885x_toggle(const struct sr_dev_inst *sdi, uint8_t cmd,
 	 * only thing to do is wait for the token that will confirm
 	 * whether the command worked or not, and resend if needed. */
 	while (TRUE) {
-		if (serial_write(serial, (const void *)&cmd, 1) != 1)
+		if (serial_write_nonblocking(serial, (const void *)&cmd, 1) != 1)
 			return SR_ERR;
 		if (wait_for_token(sdi, tokens, timeout) == SR_ERR)
 			return SR_ERR;
@@ -811,20 +811,14 @@ SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi)
 
 	serial = sdi->conn;
 
-	/* Reopen the port in non-blocking mode, so we can properly
-	 * detect when the device stops communicating. */
-	serial_close(serial);
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
-		return SR_ERR;
-
 	cmd = CMD_TOGGLE_POWER_OFF;
 	while (TRUE) {
 		serial_flush(serial);
-		if (serial_write(serial, (const void *)&cmd, 1) != 1)
+		if (serial_write_nonblocking(serial, (const void *)&cmd, 1) != 1)
 			return SR_ERR;
 		/* It never takes more than 23ms for the next token to arrive. */
 		g_usleep(25 * 1000);
-		if (serial_read(serial, &c, 1) != 1)
+		if (serial_read_nonblocking(serial, &c, 1) != 1)
 			/* Device is no longer responding. Good! */
 			break;
 	}
diff --git a/hardware/cem-dt-885x/protocol.h b/src/hardware/cem-dt-885x/protocol.h
similarity index 97%
rename from hardware/cem-dt-885x/protocol.h
rename to src/hardware/cem-dt-885x/protocol.h
index 233ef86..69afaca 100644
--- a/hardware/cem-dt-885x/protocol.h
+++ b/src/hardware/cem-dt-885x/protocol.h
@@ -22,7 +22,7 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "cem-dt-885x"
@@ -32,11 +32,11 @@
 #define SAMPLES_PER_PACKET 50
 
 /* Various temporary storage, at least 8 bytes. */
-#define BUF_SIZE SAMPLES_PER_PACKET * 2
+#define BUF_SIZE (SAMPLES_PER_PACKET * 2)
 
 /* When in hold mode, force the last measurement out at this interval.
  * We're using 50ms, which duplicates the non-hold 20Hz update rate. */
-#define HOLD_REPEAT_INTERVAL 50 * 1000
+#define HOLD_REPEAT_INTERVAL (50 * 1000)
 
 enum {
 	TOKEN_WEIGHT_TIME_FAST = 0x02,
diff --git a/hardware/center-3xx/api.c b/src/hardware/center-3xx/api.c
similarity index 74%
rename from hardware/center-3xx/api.c
rename to src/hardware/center-3xx/api.c
index ddac154..7c459ba 100644
--- a/hardware/center-3xx/api.c
+++ b/src/hardware/center-3xx/api.c
@@ -18,23 +18,26 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include "protocol.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t drvopts[] = {
 	SR_CONF_THERMOMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
-	SR_CONF_CONTINUOUS,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
 };
 
 static const char *channel_names[] = {
 	"T1", "T2", "T3", "T4",
-	NULL,
 };
 
 SR_PRIV struct sr_dev_driver center_309_driver_info;
@@ -60,8 +63,6 @@ static int dev_clear(int idx)
 
 static int init(struct sr_context *sr_ctx, int idx)
 {
-	sr_dbg("Selected '%s' subdriver.", center_devs[idx].di->name);
-
 	return std_init(sr_ctx, center_devs[idx].di, LOG_PREFIX);
 }
 
@@ -71,48 +72,36 @@ static GSList *center_scan(const char *conn, const char *serialcomm, int idx)
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
-	struct sr_channel *ch;
 	struct sr_serial_dev_inst *serial;
 	GSList *devices;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
 
-	drvc = center_devs[idx].di->priv;
+	drvc = center_devs[idx].di->context;
 	devices = NULL;
 	serial_flush(serial);
 
 	sr_info("Found device on port %s.", conn);
 
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, center_devs[idx].vendor,
-				    center_devs[idx].device, NULL)))
-		goto scan_cleanup;
-
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		goto scan_cleanup;
-	}
-
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(center_devs[idx].vendor);
+	sdi->model = g_strdup(center_devs[idx].device);
+	devc = g_malloc0(sizeof(struct dev_context));
 	sdi->inst_type = SR_INST_SERIAL;
 	sdi->conn = serial;
-
 	sdi->priv = devc;
 	sdi->driver = center_devs[idx].di;
 
-	for (i = 0; i <  center_devs[idx].num_channels; i++) {
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG,
-					   TRUE, channel_names[i])))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-	}
+	for (i = 0; i < center_devs[idx].num_channels; i++)
+		sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_names[i]);
 
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
 
-scan_cleanup:
 	serial_close(serial);
 
 	return devices;
@@ -152,7 +141,7 @@ static GSList *scan(GSList *options, int idx)
 
 static GSList *dev_list(int idx)
 {
-	return ((struct drv_context *)(center_devs[idx].di->priv))->instances;
+	return ((struct drv_context *)(center_devs[idx].di->context))->instances;
 }
 
 static int cleanup(int idx)
@@ -160,7 +149,7 @@ static int cleanup(int idx)
 	return dev_clear(idx);
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -172,7 +161,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
 	devc = sdi->priv;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		if (g_variant_get_uint64(data) == 0)
 			return SR_ERR_ARG;
@@ -190,20 +179,23 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
-	(void)sdi;
 	(void)cg;
 
 	switch (key) {
 	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -231,7 +223,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 
 	/* Poll every 500ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 500,
+	serial_source_add(sdi->session, serial, G_IO_IN, 500,
 		      center_devs[idx].receive_data, (void *)sdi);
 
 	return SR_OK;
@@ -245,15 +237,20 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 
 /* Driver-specific API function wrappers */
 #define HW_INIT(X) \
-static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
+static int init_##X(struct sr_dev_driver *d, \
+	struct sr_context *sr_ctx) { (void)d; return init(sr_ctx, X); }
 #define HW_CLEANUP(X) \
-static int cleanup_##X(void) { return cleanup(X); }
+static int cleanup_##X(const struct sr_dev_driver *d) { \
+	(void)d; return cleanup(X); }
 #define HW_SCAN(X) \
-static GSList *scan_##X(GSList *options) { return scan(options, X); }
+static GSList *scan_##X(struct sr_dev_driver *d, GSList *options) { \
+	(void)d; return scan(options, X); }
 #define HW_DEV_LIST(X) \
-static GSList *dev_list_##X(void) { return dev_list(X); }
+static GSList *dev_list_##X(const struct sr_dev_driver *d) { \
+	(void)d; return dev_list(X); }
 #define HW_DEV_CLEAR(X) \
-static int dev_clear_##X(void) { return dev_clear(X); }
+static int dev_clear_##X(const struct sr_dev_driver *d) { \
+	(void)d; return dev_clear(X); }
 #define HW_DEV_ACQUISITION_START(X) \
 static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
 void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
@@ -282,7 +279,7 @@ SR_PRIV struct sr_dev_driver ID##_driver_info = { \
 	.dev_close = std_serial_dev_close, \
 	.dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
 	.dev_acquisition_stop = dev_acquisition_stop, \
-	.priv = NULL, \
+	.context = NULL, \
 };
 
 DRV(center_309, CENTER_309, "center-309", "Center 309")
diff --git a/hardware/center-3xx/protocol.c b/src/hardware/center-3xx/protocol.c
similarity index 92%
rename from hardware/center-3xx/protocol.c
rename to src/hardware/center-3xx/protocol.c
index ef8b9dc..34478fa 100644
--- a/hardware/center-3xx/protocol.c
+++ b/src/hardware/center-3xx/protocol.c
@@ -18,10 +18,13 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include "protocol.h"
 
+#define NUM_CHANNELS 4
+
 struct center_info {
-	float temp[4];
+	float temp[NUM_CHANNELS];
 	gboolean rec, std, max, min, maxmin, t1t2, rel, hold, lowbat, celsius;
 	gboolean memfull, autooff;
 	gboolean mode_std, mode_rel, mode_max, mode_min, mode_maxmin;
@@ -31,7 +34,8 @@ static int center_send(struct sr_serial_dev_inst *serial, const char *cmd)
 {
 	int ret;
 
-	if ((ret = serial_write(serial, cmd, strlen(cmd))) < 0) {
+	if ((ret = serial_write_blocking(serial, cmd, strlen(cmd),
+			serial_timeout(serial, strlen(cmd)))) < 0) {
 		sr_err("Error sending '%s' command: %d.", cmd, ret);
 		return SR_ERR;
 	}
@@ -84,21 +88,21 @@ static int packet_parse(const uint8_t *buf, int idx, struct center_info *info)
 	info->autooff     = (buf[2] & (1 << 7)) != 0;
 
 	/* Byte 7+8/9+10/11+12/13+14: channel T1/T2/T3/T4 temperature. */
-	for (i = 0; i < 4; i++) {
+	for (i = 0; i < NUM_CHANNELS; i++) {
 		temp_u16 = buf[8 + (i * 2)];
 		temp_u16 |= ((uint16_t)buf[7 + (i * 2)] << 8);
 		info->temp[i] = (float)temp_u16;
 	}
 
 	/* Byte 43: Specifies whether we need to divide the value(s) by 10. */
-	for (i = 0; i < 4; i++) {
+	for (i = 0; i < NUM_CHANNELS; i++) {
 		/* Bit = 0: Divide by 10. Bit = 1: Don't divide by 10. */
 		if ((buf[43] & (1 << i)) == 0)
 			info->temp[i] /= 10;
 	}
 
 	/* Bytes 39-42: Overflow/overlimit bits, depending on mode. */
-	for (i = 0; i < 4; i++) {
+	for (i = 0; i < NUM_CHANNELS; i++) {
 		if (info->mode_std && ((buf[39] & (1 << i)) != 0))
 			info->temp[i] = INFINITY;
 		/* TODO: Rel. Not available on all models. */
@@ -119,7 +123,7 @@ static int packet_parse(const uint8_t *buf, int idx, struct center_info *info)
 static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
 {
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct dev_context *devc;
 	struct center_info info;
 	GSList *l;
@@ -127,7 +131,7 @@ static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
 
 	devc = sdi->priv;
 
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 	memset(&info, 0, sizeof(struct center_info));
 
 	ret = packet_parse(buf, idx, &info);
@@ -137,14 +141,14 @@ static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
 	}
 
 	/* Common values for all 4 channels. */
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	analog.mq = SR_MQ_TEMPERATURE;
 	analog.unit = (info.celsius) ? SR_UNIT_CELSIUS : SR_UNIT_FAHRENHEIT;
 	analog.num_samples = 1;
 
 	/* Send the values for T1 - T4. */
-	for (i = 0; i < 4; i++) {
+	for (i = 0; i < NUM_CHANNELS; i++) {
 		l = NULL;
 		l = g_slist_append(l, g_slist_nth_data(sdi->channels, i));
 		analog.channels = l;
@@ -170,7 +174,7 @@ static gboolean handle_new_data(struct sr_dev_inst *sdi, int idx)
 
 	/* Try to get as much data as the buffer can hold. */
 	len = SERIAL_BUFSIZE - devc->buflen;
-	len = serial_read(serial, devc->buf + devc->buflen, len);
+	len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
 	if (len < 1) {
 		sr_err("Serial port read error: %d.", len);
 		return FALSE;
diff --git a/hardware/center-3xx/protocol.h b/src/hardware/center-3xx/protocol.h
similarity index 89%
copy from hardware/center-3xx/protocol.h
copy to src/hardware/center-3xx/protocol.h
index 71e2630..acad71d 100644
--- a/hardware/center-3xx/protocol.h
+++ b/src/hardware/center-3xx/protocol.h
@@ -26,23 +26,20 @@
 #include <ctype.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "center-3xx"
 
-/* Note: When adding entries here, don't forget to update CENTER_DEV_COUNT. */
 enum {
 	CENTER_309,
 	VOLTCRAFT_K204,
 };
 
-#define CENTER_DEV_COUNT 2
-
 struct center_dev_info {
-	char *vendor;
-	char *device;
-	char *conn;
+	const char *vendor;
+	const char *device;
+	const char *conn;
 	int num_channels;
 	uint32_t max_sample_points;
 	uint8_t packet_size;
@@ -51,7 +48,7 @@ struct center_dev_info {
 	int (*receive_data)(int, int, void *);
 };
 
-extern SR_PRIV const struct center_dev_info center_devs[CENTER_DEV_COUNT];
+extern SR_PRIV const struct center_dev_info center_devs[];
 
 #define SERIAL_BUFSIZE 256
 
diff --git a/hardware/chronovu-la/api.c b/src/hardware/chronovu-la/api.c
similarity index 65%
rename from hardware/chronovu-la/api.c
rename to src/hardware/chronovu-la/api.c
index b232a10..1410458 100644
--- a/hardware/chronovu-la/api.c
+++ b/src/hardware/chronovu-la/api.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2011-2014 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2011-2015 Uwe Hermann <uwe at 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
@@ -18,30 +18,33 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include "protocol.h"
 
 SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
 static struct sr_dev_driver *di = &chronovu_la_driver_info;
 
-static const int32_t hwcaps[] = {
+static const uint32_t drvopts[] = {
 	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_TRIGGER_TYPE,
-	SR_CONF_LIMIT_MSEC, /* TODO: Not yet implemented. */
-	SR_CONF_LIMIT_SAMPLES, /* TODO: Not yet implemented. */
 };
 
-/* The ChronoVu LA8/LA16 can have multiple VID/PID pairs. */
-static struct {
-	uint16_t vid;
-	uint16_t pid;
-	int model;
-	const char *iproduct;
-} vid_pid[] = {
-	{ 0x0403, 0x6001, CHRONOVU_LA8,  "ChronoVu LA8"  },
-	{ 0x0403, 0x8867, CHRONOVU_LA8,  "ChronoVu LA8"  },
-	{ 0x0403, 0x6001, CHRONOVU_LA16, "ChronoVu LA16" },
-	{ 0x0403, 0x8867, CHRONOVU_LA16, "ChronoVu LA16" },
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CONN | SR_CONF_GET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+};
+
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
 };
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
@@ -56,31 +59,32 @@ static void clear_helper(void *priv)
 	g_free(devc->final_buf);
 }
 
-static int dev_clear(void)
+static int dev_clear(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, clear_helper);
 }
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static int add_device(int idx, int model, GSList **devices)
+static int add_device(int model, struct libusb_device_descriptor *des,
+	const char *serial_num, const char *connection_id,
+	libusb_device *usbdev, GSList **devices)
 {
 	int ret;
 	unsigned int i;
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
-	struct sr_channel *ch;
 
 	ret = SR_OK;
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	/* Allocate memory for our private device context. */
-	devc = g_try_malloc(sizeof(struct dev_context));
+	devc = g_malloc0(sizeof(struct dev_context));
 
 	/* Set some sane defaults. */
 	devc->prof = &cv_profiles[model];
@@ -98,8 +102,8 @@ static int add_device(int idx, int model, GSList **devices)
 	devc->done = 0;
 	devc->block_counter = 0;
 	devc->divcount = 0;
-	devc->usb_vid = vid_pid[idx].vid;
-	devc->usb_pid = vid_pid[idx].pid;
+	devc->usb_vid = des->idVendor;
+	devc->usb_pid = des->idProduct;
 	memset(devc->samplerates, 0, sizeof(uint64_t) * 255);
 
 	/* Allocate memory where we'll store the de-mangled data. */
@@ -113,89 +117,138 @@ static int add_device(int idx, int model, GSList **devices)
 	devc->cur_samplerate = devc->prof->max_samplerate;
 
 	/* Register the device with libsigrok. */
-	sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
-			      "ChronoVu", devc->prof->modelname, NULL);
-	if (!sdi) {
-		sr_err("Failed to create device instance.");
-		ret = SR_ERR;
-		goto err_free_final_buf;
-	}
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INITIALIZING;
+	sdi->vendor = g_strdup("ChronoVu");
+	sdi->model = g_strdup(devc->prof->modelname);
+	sdi->serial_num = g_strdup(serial_num);
+	sdi->connection_id = g_strdup(connection_id);
+	sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(usbdev),
+		libusb_get_device_address(usbdev), NULL);
 	sdi->driver = di;
 	sdi->priv = devc;
 
-	for (i = 0; i < devc->prof->num_channels; i++) {
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-					  cv_channel_names[i]))) {
-			ret = SR_ERR;
-			goto err_free_dev_inst;
-		}
-		sdi->channels = g_slist_append(sdi->channels, ch);
-	}
+	for (i = 0; i < devc->prof->num_channels; i++)
+		sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE,
+				cv_channel_names[i]);
 
 	*devices = g_slist_append(*devices, sdi);
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 
-	return SR_OK;
+	if (ret == SR_OK)
+		return SR_OK;
 
-err_free_dev_inst:
-	sr_dev_inst_free(sdi);
-err_free_final_buf:
-	g_free(devc->final_buf);
 err_free_devc:
 	g_free(devc);
 
 	return ret;
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-	int ret;
-	unsigned int i;
-	GSList *devices;
-	struct ftdi_context *ftdic;
-
-	(void)options;
+	int i, ret, model;
+	struct drv_context *drvc;
+	GSList *devices, *conn_devices, *l;
+	struct sr_usb_dev_inst *usb;
+	struct sr_config *src;
+	struct libusb_device_descriptor des;
+	libusb_device **devlist;
+	struct libusb_device_handle *hdl;
+	const char *conn;
+	char product[64], serial_num[64], connection_id[64];
+
+	drvc = di->context;
+	drvc->instances = NULL;
+
+	conn = NULL;
+	for (l = options; l; l = l->next) {
+		src = l->data;
+		switch (src->key) {
+		case SR_CONF_CONN:
+			conn = g_variant_get_string(src->data, NULL);
+			break;
+		}
+	}
+	if (conn)
+		conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+	else
+		conn_devices = NULL;
 
 	devices = NULL;
+	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+
+	for (i = 0; devlist[i]; i++) {
+		if (conn) {
+			for (l = conn_devices; l; l = l->next) {
+				usb = l->data;
+				if (usb->bus == libusb_get_bus_number(devlist[i])
+					&& usb->address == libusb_get_device_address(devlist[i]))
+					break;
+			}
+			if (!l)
+				/* This device matched none of the ones that
+				 * matched the conn specification. */
+				continue;
+		}
 
-	/* Allocate memory for the FTDI context and initialize it. */
-	if (!(ftdic = ftdi_new())) {
-		sr_err("Failed to initialize libftdi.");
-		return NULL;
-	}
+		libusb_get_device_descriptor(devlist[i], &des);
 
-	/* Check for LA8 and/or LA16 devices with various VID/PIDs. */
-	for (i = 0; i < ARRAY_SIZE(vid_pid); i++) {
-		ret = ftdi_usb_open_desc(ftdic, vid_pid[i].vid,
-			vid_pid[i].pid, vid_pid[i].iproduct, NULL);
-		/* Show errors other than "device not found". */
-		if (ret < 0 && ret != -3)
-			sr_dbg("Error finding/opening device (%d): %s.",
-			       ret, ftdi_get_error_string(ftdic));
-		if (ret < 0)
-			continue; /* No device found, or not usable. */
-
-		sr_dbg("Found %s device (%04x:%04x).",
-		       vid_pid[i].iproduct, vid_pid[i].vid, vid_pid[i].pid);
-
-		if ((ret = add_device(i, vid_pid[i].model, &devices)) < 0)
-			sr_dbg("Failed to add device: %d.", ret);
+		if ((ret = libusb_open(devlist[i], &hdl)) < 0)
+			continue;
 
-		if ((ret = ftdi_usb_close(ftdic)) < 0)
-			sr_dbg("Failed to close FTDI device (%d): %s.",
-			       ret, ftdi_get_error_string(ftdic));
+		if (des.iProduct == 0) {
+			product[0] = '\0';
+		} else if ((ret = libusb_get_string_descriptor_ascii(hdl,
+				des.iProduct, (unsigned char *)product,
+				sizeof(product))) < 0) {
+			sr_warn("Failed to get product string descriptor: %s.",
+				libusb_error_name(ret));
+			continue;
+		}
+
+		if (des.iSerialNumber == 0) {
+			serial_num[0] = '\0';
+		} else if ((ret = libusb_get_string_descriptor_ascii(hdl,
+				des.iSerialNumber, (unsigned char *)serial_num,
+				sizeof(serial_num))) < 0) {
+			sr_warn("Failed to get serial number string descriptor: %s.",
+				libusb_error_name(ret));
+			continue;
+		}
+
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+
+		libusb_close(hdl);
+
+		if (!strcmp(product, "ChronoVu LA8")) {
+			model = 0;
+		} else if (!strcmp(product, "ChronoVu LA16")) {
+			model = 1;
+		} else {
+			sr_spew("Unknown iProduct string '%s'.", product);
+			continue;
+		}
+
+		sr_dbg("Found %s (%04x:%04x, %d.%d, %s).",
+		       product, des.idVendor, des.idProduct,
+		       libusb_get_bus_number(devlist[i]),
+		       libusb_get_device_address(devlist[i]), connection_id);
+
+		if ((ret = add_device(model, &des, serial_num, connection_id,
+					devlist[i], &devices)) < 0) {
+			sr_dbg("Failed to add device: %d.", ret);
+		}
 	}
 
-	/* Close USB device, deinitialize and free the FTDI context. */
-	ftdi_free(ftdic);
-	ftdic = NULL;
+	libusb_free_device_list(devlist, 1);
+	g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
 
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -203,8 +256,6 @@ static int dev_open(struct sr_dev_inst *sdi)
 	struct dev_context *devc;
 	int ret;
 
-	ret = SR_ERR;
-
 	if (!(devc = sdi->priv))
 		return SR_ERR_BUG;
 
@@ -247,7 +298,8 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 	sdi->status = SR_ST_ACTIVE;
 
-	return SR_OK;
+	if (ret == SR_OK)
+		return SR_OK;
 
 err_ftdi_free:
 	ftdi_free(devc->ftdic); /* Close device (if open), free FTDI context. */
@@ -273,19 +325,27 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
-	return dev_clear();
+	return dev_clear(di);
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	char str[128];
 
 	(void)cg;
 
-	switch (id) {
+	switch (key) {
+	case SR_CONF_CONN:
+		if (!sdi || !(usb = sdi->conn))
+			return SR_ERR_ARG;
+		snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+		*data = g_variant_new_string(str);
+		break;
 	case SR_CONF_SAMPLERATE:
 		if (!sdi || !(devc = sdi->priv))
 			return SR_ERR_BUG;
@@ -298,7 +358,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -311,7 +371,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	if (!(devc = sdi->priv))
 		return SR_ERR_BUG;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		if (cv_set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
 			return SR_ERR;
@@ -333,7 +393,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	GVariant *gvar, *grange[2];
@@ -343,9 +403,17 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	(void)cg;
 
 	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLERATE:
 		if (!sdi || !sdi->priv || !(devc = sdi->priv))
@@ -369,10 +437,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 			grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES / 2);
 		*data = g_variant_new_tuple(grange, 2);
 		break;
-	case SR_CONF_TRIGGER_TYPE:
+	case SR_CONF_TRIGGER_MATCH:
 		if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
 			return SR_ERR_BUG;
-		*data = g_variant_new_string(devc->prof->trigger_type);
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				trigger_matches, devc->prof->num_trigger_matches,
+				sizeof(int32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -461,8 +531,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		return SR_ERR;
 	}
 
-	if (cv_configure_channels(sdi) != SR_OK) {
-		sr_err("Failed to configure channels.");
+	if (cv_convert_trigger(sdi) != SR_OK) {
+		sr_err("Failed to configure trigger.");
 		return SR_ERR;
 	}
 
@@ -498,7 +568,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	devc->cb_data = cb_data;
 
 	/* Send header packet to the session bus. */
-	std_session_send_df_header(cb_data, LOG_PREFIX);
+	std_session_send_df_header(sdi, LOG_PREFIX);
 
 	/* Time when we should be done (for detecting trigger timeouts). */
 	devc->done = (devc->divcount + 1) * devc->prof->trigger_constant +
@@ -507,7 +577,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	devc->trigger_found = 0;
 
 	/* Hook up a dummy handler to receive data from the device. */
-	sr_source_add(-1, G_IO_IN, 0, receive_data, (void *)sdi);
+	sr_session_source_add(sdi->session, -1, 0, 0, receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -516,15 +586,15 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
 	struct sr_datafeed_packet packet;
 
-	(void)sdi;
+	(void)cb_data;
 
 	sr_dbg("Stopping acquisition.");
-	sr_source_remove(-1);
+	sr_session_source_remove(sdi->session, -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);
+	sr_session_send(sdi, &packet);
 
 	return SR_OK;
 }
@@ -545,5 +615,5 @@ SR_PRIV struct sr_dev_driver chronovu_la_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/chronovu-la/protocol.c b/src/hardware/chronovu-la/protocol.c
similarity index 89%
rename from hardware/chronovu-la/protocol.c
rename to src/hardware/chronovu-la/protocol.c
index 5b4723c..a4689fa 100644
--- a/hardware/chronovu-la/protocol.c
+++ b/src/hardware/chronovu-la/protocol.c
@@ -18,14 +18,13 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include "protocol.h"
 
 SR_PRIV const struct cv_profile cv_profiles[] = {
-	{ CHRONOVU_LA8,  "LA8",  "ChronoVu LA8",  8,  SR_MHZ(100), "01",
-	  0.8388608 },
-	{ CHRONOVU_LA16, "LA16", "ChronoVu LA16", 16, SR_MHZ(200), "01rf",
-	  0.042 },
-	{ 0, NULL, NULL, 0, 0, NULL, 0.0 },
+	{ CHRONOVU_LA8,  "LA8",  "ChronoVu LA8",  8,  SR_MHZ(100), 2, 0.8388608 },
+	{ CHRONOVU_LA16, "LA16", "ChronoVu LA16", 16, SR_MHZ(200), 4, 0.042 },
+	ALL_ZERO
 };
 
 /* LA8: channels are numbered 0-7. LA16: channels are numbered 0-15. */
@@ -260,73 +259,60 @@ static int reset_device(struct dev_context *devc)
 	return SR_OK;
 }
 
-SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi)
+SR_PRIV int cv_convert_trigger(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
-	const struct sr_channel *ch;
-	const GSList *l;
+	struct sr_trigger *trigger;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	const GSList *l, *m;
 	uint16_t channel_bit;
-	char *tc;
 
 	devc = sdi->priv;
 	devc->trigger_pattern = 0x0000; /* Default to "low" trigger. */
 	devc->trigger_mask = 0x0000; /* Default to "don't care". */
 	devc->trigger_edgemask = 0x0000; /* Default to "state triggered". */
 
-	for (l = sdi->channels; l; l = l->next) {
-		ch = (struct sr_channel *)l->data;
+	if (!(trigger = sr_session_trigger_get(sdi->session)))
+		return SR_OK;
 
-		if (!ch) {
-			sr_err("%s: channel was NULL.", __func__);
-			return SR_ERR;
-		}
-
-		/* Skip disabled channels. */
-		if (!ch->enabled)
-			continue;
-
-		/* Skip (enabled) channels with no configured trigger. */
-		if (!ch->trigger)
-			continue;
-
-		/* Note: Must only be run if ch->trigger != NULL. */
-		if (ch->index < 0 || ch->index > (int)devc->prof->num_channels - 1) {
-			sr_err("Invalid channel index %d, must be "
-			       "between 0 and %d.", ch->index,
-			       devc->prof->num_channels - 1);
-			return SR_ERR;
-		}
-
-		channel_bit = (1 << (ch->index));
-
-		/* Configure the channel's trigger pattern/mask/edgemask. */
-		for (tc = ch->trigger; tc && *tc; tc++) {
-			devc->trigger_mask |= channel_bit;
+	if (g_slist_length(trigger->stages) > 1) {
+		sr_err("This device only supports 1 trigger stage.");
+		return SR_ERR;
+	}
 
-			/* Sanity check, LA8 only supports low/high trigger. */
-			if ((devc->prof->model == CHRONOVU_LA8) &&
-			    (*tc != '0' && *tc != '1')) {
-				sr_err("Invalid trigger '%c', only "
-				       "'0'/'1' supported.", *tc);
+	for (l = trigger->stages; l; l = l->next) {
+		stage = l->data;
+		for (m = stage->matches; m; m = m->next) {
+			match = m->data;
+			if (!match->channel->enabled)
+				/* Ignore disabled channels with a trigger. */
+				continue;
+			if (devc->prof->model == CHRONOVU_LA8 &&
+					(match->match == SR_TRIGGER_RISING
+					|| match->match == SR_TRIGGER_FALLING)) {
+				sr_err("This model supports only simple triggers.");
 				return SR_ERR;
 			}
+			channel_bit = (1 << (match->channel->index));
 
 			/* state: 1 == high, edge: 1 == rising edge. */
-			if (*tc == '1' || *tc == 'r')
+			if (match->match == SR_TRIGGER_ONE
+					|| match->match == SR_TRIGGER_RISING)
 				devc->trigger_pattern |= channel_bit;
 
 			/* LA16 (but not LA8) supports edge triggering. */
 			if ((devc->prof->model == CHRONOVU_LA16)) {
-				if (*tc == 'r' || *tc == 'f')
-					devc->trigger_edgemask |= channel_bit;
+				if (match->match == SR_TRIGGER_RISING
+						|| match->match == SR_TRIGGER_FALLING)
+						devc->trigger_edgemask |= channel_bit;
 			}
-
 		}
 	}
 
 	sr_dbg("Trigger pattern/mask/edgemask = 0x%04x / 0x%04x / 0x%04x.",
-	       devc->trigger_pattern, devc->trigger_mask,
-	       devc->trigger_edgemask);
+			devc->trigger_pattern, devc->trigger_mask,
+			devc->trigger_edgemask);
 
 	return SR_OK;
 }
@@ -386,7 +372,7 @@ SR_PRIV int cv_read_block(struct dev_context *devc)
 		} while ((devc->done > now) && (bytes_read == 0));
 	}
 
-	/* Check if block read was successful or a timeout occured. */
+	/* Check if block read was successful or a timeout occurred. */
 	if (bytes_read != BS) {
 		sr_err("Trigger timed out. Bytes read: %d.", bytes_read);
 		(void) reset_device(devc); /* Ignore errors. */
diff --git a/hardware/chronovu-la/protocol.h b/src/hardware/chronovu-la/protocol.h
similarity index 96%
rename from hardware/chronovu-la/protocol.h
rename to src/hardware/chronovu-la/protocol.h
index 6941661..bd1efff 100644
--- a/hardware/chronovu-la/protocol.h
+++ b/src/hardware/chronovu-la/protocol.h
@@ -22,10 +22,11 @@
 #define LIBSIGROK_HARDWARE_CHRONOVU_LA_PROTOCOL_H
 
 #include <glib.h>
+#include <libusb.h>
 #include <ftdi.h>
 #include <stdint.h>
 #include <string.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "la8/la16"
@@ -47,7 +48,7 @@ struct cv_profile {
 	const char *iproduct; /* USB iProduct string */
 	unsigned int num_channels;
 	uint64_t max_samplerate;
-	const char *trigger_type;
+	const int num_trigger_matches;
 	float trigger_constant;
 };
 
@@ -133,7 +134,7 @@ SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi);
 SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
 					  uint64_t samplerate);
 SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size);
-SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi);
+SR_PRIV int cv_convert_trigger(const struct sr_dev_inst *sdi);
 SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
 SR_PRIV int cv_read_block(struct dev_context *devc);
 SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block);
diff --git a/hardware/colead-slm/api.c b/src/hardware/colead-slm/api.c
similarity index 71%
rename from hardware/colead-slm/api.c
rename to src/hardware/colead-slm/api.c
index f658827..cd8647d 100644
--- a/hardware/colead-slm/api.c
+++ b/src/hardware/colead-slm/api.c
@@ -17,50 +17,48 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #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>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
 
 /* The Colead SL-5868P uses this. */
 #define SERIALCOMM "2400/8n1"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_SOUNDLEVELMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
 };
 
 SR_PRIV struct sr_dev_driver colead_slm_driver_info;
-static struct sr_dev_driver *di = &colead_slm_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
 	struct sr_config *src;
-	struct sr_channel *ch;
 	GSList *devices, *l;
 	const char *conn, *serialcomm;
 
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	devices = NULL;
@@ -82,33 +80,25 @@ static GSList *scan(GSList *options)
 	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 = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("Colead");
+	sdi->model = g_strdup("SL-5868P");
+	devc = g_malloc0(sizeof(struct dev_context));
+	sdi->conn = sr_serial_dev_inst_new(conn, serialcomm);
 	sdi->inst_type = SR_INST_SERIAL;
 	sdi->priv = devc;
 	sdi->driver = di;
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-		return NULL;
-	sdi->channels = g_slist_append(sdi->channels, ch);
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
 
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -124,12 +114,12 @@ static int dev_open(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -144,21 +134,13 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		return SR_ERR_BUG;
 	}
 
-	switch (id) {
+	switch (key) {
 	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;
@@ -167,7 +149,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -175,12 +157,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -209,8 +191,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 	/* Poll every 150ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 150, colead_slm_receive_data,
-			(void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 150,
+			colead_slm_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -237,5 +219,5 @@ SR_PRIV struct sr_dev_driver colead_slm_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/colead-slm/protocol.c b/src/hardware/colead-slm/protocol.c
similarity index 56%
rename from hardware/colead-slm/protocol.c
rename to src/hardware/colead-slm/protocol.c
index 4abcdd5..481a6dd 100644
--- a/hardware/colead-slm/protocol.c
+++ b/src/hardware/colead-slm/protocol.c
@@ -17,19 +17,19 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <string.h>
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
-#include <errno.h>
-#include <string.h>
 
 static void process_packet(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	GString *dbg;
 	float fvalue;
 	int checksum, mode, i;
@@ -71,7 +71,7 @@ static void process_packet(const struct sr_dev_inst *sdi)
 	}
 	fvalue /= 10;
 
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 	analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
 	analog.unit = SR_UNIT_DECIBEL_SPL;
 	analog.channels = sdi->channels;
@@ -92,87 +92,89 @@ static void process_packet(const struct sr_dev_inst *sdi)
 	 * weighting. */
 	mode = devc->buf[2] & 0x0f;
 	switch (mode) {
-		case 0x0:
-			analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_F;
-			break;
-		case 0x1:
-			analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_S;
-			break;
-		case 0x2:
-			analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_F;
-			break;
-		case 0x3:
-			analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_S;
-			break;
-		case 0x4:
-			analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_F;
-			break;
-		case 0x5:
-			analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_S;
-			break;
-		case 0x6:
-			analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
-					| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_F;
-			break;
-		case 0x7:
-			analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
-					| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_S;
-			break;
-		case 0x8:
-			/* 10-second mean, but we don't have MQ flags to express it. */
-			analog.mqflags |= SR_MQFLAG_SPL_LAT \
-					| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_F;
-			break;
-		case 0x9:
-			/* Mean over a time period between 11 seconds and 24 hours.
-			 * Which is so silly that there's no point in expressing
-			 * either this or the previous case.  */
-			analog.mqflags |= SR_MQFLAG_SPL_LAT \
-					| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_F;
-			break;
-		case 0xa:
-			/* 10-second mean. */
-			analog.mqflags |= SR_MQFLAG_SPL_LAT \
-					| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_S;
-			break;
-		case 0xb:
-			/* Mean over a time period between 11 seconds and 24 hours. */
-			analog.mqflags |= SR_MQFLAG_SPL_LAT \
-					| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
-					| SR_MQFLAG_SPL_TIME_WEIGHT_S;
-			break;
-		case 0xc:
-			/* Internal calibration on 1kHz sine at 94dB, not useful
-			 * to anything but the device. */
-			analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
-			break;
-		case 0xd:
-			/* Internal calibration on 1kHz sine at 94dB, not useful
-			 * to anything but the device. */
-			analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
-			break;
-		default:
-			sr_dbg("unknown configuration 0x%.2x", mode);
-			return;
-			break;
+	case 0x0:
+		analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_F;
+		break;
+	case 0x1:
+		analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_S;
+		break;
+	case 0x2:
+		analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_F;
+		break;
+	case 0x3:
+		analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_S;
+		break;
+	case 0x4:
+		analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_F;
+		break;
+	case 0x5:
+		analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_S;
+		break;
+	case 0x6:
+		analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
+				| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_F;
+		break;
+	case 0x7:
+		analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
+				| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_S;
+		break;
+	case 0x8:
+		/* 10-second mean, but we don't have MQ flags to express it. */
+		analog.mqflags |= SR_MQFLAG_SPL_LAT \
+				| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_F;
+		break;
+	case 0x9:
+		/* Mean over a time period between 11 seconds and 24 hours.
+		 * Which is so silly that there's no point in expressing
+		 * either this or the previous case.  */
+		analog.mqflags |= SR_MQFLAG_SPL_LAT \
+				| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_F;
+		break;
+	case 0xa:
+		/* 10-second mean. */
+		analog.mqflags |= SR_MQFLAG_SPL_LAT \
+				| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_S;
+		break;
+	case 0xb:
+		/* Mean over a time period between 11 seconds and 24 hours. */
+		analog.mqflags |= SR_MQFLAG_SPL_LAT \
+				| SR_MQFLAG_SPL_FREQ_WEIGHT_A \
+				| SR_MQFLAG_SPL_TIME_WEIGHT_S;
+		break;
+	case 0xc:
+		/* Internal calibration on 1kHz sine at 94dB, not useful
+		 * to anything but the device. */
+		analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
+		break;
+	case 0xd:
+		/* Internal calibration on 1kHz sine at 94dB, not useful
+		 * to anything but the device. */
+		analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
+		break;
+	default:
+		sr_dbg("unknown configuration 0x%.2x", mode);
+		return;
 	}
 
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 
 	devc->num_samples++;
+	if (devc->num_samples >= devc->limit_samples)
+		sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+				devc->cb_data);
 
 }
 
@@ -181,7 +183,7 @@ SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
 	const struct sr_dev_inst *sdi;
 	struct dev_context *devc;
 	struct sr_serial_dev_inst *serial;
-	int len;
+	int delay_ms, len;
 	char buf[128];
 
 	(void)fd;
@@ -198,20 +200,21 @@ SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
 
 	serial = sdi->conn;
 	if (devc->state == IDLE) {
-		if (serial_read(serial, buf, 128) != 1 || buf[0] != 0x10)
+		if (serial_read_nonblocking(serial, buf, 128) != 1 || buf[0] != 0x10)
 			/* Nothing there, or caught the tail end of a previous packet,
 			 * or some garbage. Unless it's a single "data ready" byte,
 			 * we don't want it. */
 			return TRUE;
 		/* Got 0x10, "measurement ready". */
-		if (serial_write(serial, "\x20", 1) == -1)
-			sr_err("unable to send command: %s", strerror(errno));
+		delay_ms = serial_timeout(serial, 1);
+		if (serial_write_blocking(serial, "\x20", 1, delay_ms) < 1)
+			sr_err("unable to send command");
 		else {
 			devc->state = COMMAND_SENT;
 			devc->buflen = 0;
 		}
 	} else {
-		len = serial_read(serial, devc->buf + devc->buflen,
+		len = serial_read_nonblocking(serial, devc->buf + devc->buflen,
 				10 - devc->buflen);
 		if (len < 1)
 			return TRUE;
diff --git a/hardware/colead-slm/protocol.h b/src/hardware/colead-slm/protocol.h
similarity index 97%
rename from hardware/colead-slm/protocol.h
rename to src/hardware/colead-slm/protocol.h
index 8942ac8..4cc4997 100644
--- a/hardware/colead-slm/protocol.h
+++ b/src/hardware/colead-slm/protocol.h
@@ -21,7 +21,7 @@
 #define LIBSIGROK_HARDWARE_COLEAD_SLM_PROTOCOL_H
 
 #include <stdint.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "colead-slm"
diff --git a/hardware/conrad-digi-35-cpu/api.c b/src/hardware/conrad-digi-35-cpu/api.c
similarity index 73%
rename from hardware/conrad-digi-35-cpu/api.c
rename to src/hardware/conrad-digi-35-cpu/api.c
index 01adcef..1f3abe8 100644
--- a/hardware/conrad-digi-35-cpu/api.c
+++ b/src/hardware/conrad-digi-35-cpu/api.c
@@ -22,43 +22,41 @@
  *  @internal
  */
 
+#include <config.h>
 #include "protocol.h"
 
 #define SERIALCOMM "9600/8n1"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_POWER_SUPPLY,
-	SR_CONF_OUTPUT_VOLTAGE,
-	SR_CONF_OUTPUT_CURRENT,
-	/* There's no SR_CONF_OUTPUT_ENABLED; can't know/set status remotely. */
-	SR_CONF_OVER_CURRENT_PROTECTION,
+	SR_CONF_VOLTAGE | SR_CONF_SET,
+	SR_CONF_CURRENT | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_SET,
 };
 
 SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info;
-static struct sr_dev_driver *di = &conrad_digi_35_cpu_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct sr_config *src;
-	struct sr_channel *ch;
 	struct sr_serial_dev_inst *serial;
 	GSList *l, *devices;
 	const char *conn, *serialcomm;
 
 	devices = NULL;
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 	conn = serialcomm = NULL;
 
@@ -84,10 +82,9 @@ static GSList *scan(GSList *options)
 	 * the device is there.
 	 */
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
 
 	serial_flush(serial);
@@ -95,33 +92,31 @@ static GSList *scan(GSList *options)
 
 	sr_spew("Conrad DIGI 35 CPU assumed at %s.", conn);
 
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, "Conrad", "DIGI 35 CPU", NULL)))
-		return NULL;
-
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_ACTIVE;
+	sdi->vendor = g_strdup("Conrad");
+	sdi->model = g_strdup("DIGI 35 CPU");
 	sdi->conn = serial;
 	sdi->priv = NULL;
 	sdi->driver = di;
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CH1")))
-		return NULL;
-	sdi->channels = g_slist_append(sdi->channels, ch);
-
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
 
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	int ret;
@@ -132,9 +127,8 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	ret = SR_OK;
 	switch (key) {
-	case SR_CONF_OUTPUT_VOLTAGE:
+	case SR_CONF_VOLTAGE:
 		dblval = g_variant_get_double(data);
 		if ((dblval < 0.0) || (dblval > 35.0)) {
 			sr_err("Voltage out of range (0 - 35.0)!");
@@ -142,7 +136,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 		}
 		ret = send_msg1(sdi, 'V', (int) (dblval * 10 + 0.5));
 		break;
-	case SR_CONF_OUTPUT_CURRENT:
+	case SR_CONF_CURRENT:
 		dblval = g_variant_get_double(data);
 		if ((dblval < 0.01) || (dblval > 2.55)) {
 			sr_err("Current out of range (0 - 2.55)!");
@@ -150,8 +144,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 		}
 		ret = send_msg1(sdi, 'C', (int) (dblval * 100 + 0.5));
 		break;
-	/* No SR_CONF_OUTPUT_ENABLED :-( . */
-	case SR_CONF_OVER_CURRENT_PROTECTION:
+	case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
 		if (g_variant_get_boolean(data))
 			ret = send_msg1(sdi, 'V', 900);
 		else /* Constant current mode */
@@ -164,7 +157,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	int ret;
@@ -175,12 +168,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	ret = SR_OK;
 	switch (key) {
 	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -225,5 +218,5 @@ SR_PRIV struct sr_dev_driver conrad_digi_35_cpu_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/conrad-digi-35-cpu/protocol.c b/src/hardware/conrad-digi-35-cpu/protocol.c
similarity index 88%
rename from hardware/conrad-digi-35-cpu/protocol.c
rename to src/hardware/conrad-digi-35-cpu/protocol.c
index 7657b31..732bec6 100644
--- a/hardware/conrad-digi-35-cpu/protocol.c
+++ b/src/hardware/conrad-digi-35-cpu/protocol.c
@@ -23,6 +23,7 @@
  * @internal
  */
 
+#include <config.h>
 #include "protocol.h"
 
 /**
@@ -48,8 +49,9 @@ SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param)
 
 	sr_spew("send_msg1(): %c%c%c%c\\r", buf[0], buf[1], buf[2], buf[3]);
 
-	if (serial_write(serial, buf, sizeof(buf)) == -1) {
-		sr_err("Write error for cmd=%c: %d %s", cmd, errno, strerror(errno));
+	if (serial_write_blocking(serial, buf, sizeof(buf),
+			serial_timeout(serial, sizeof(buf))) < (int)sizeof(buf)) {
+		sr_err("Write error for cmd=%c", cmd);
 		return SR_ERR;
 	}
 
@@ -57,7 +59,7 @@ SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param)
 	 * Wait 50ms to ensure that the device does not swallow any of the
 	 * following commands.
 	 */
-	g_usleep(50000);
+	g_usleep(50 * 1000);
 
 	return SR_OK;
 }
diff --git a/hardware/conrad-digi-35-cpu/protocol.h b/src/hardware/conrad-digi-35-cpu/protocol.h
similarity index 96%
rename from hardware/conrad-digi-35-cpu/protocol.h
rename to src/hardware/conrad-digi-35-cpu/protocol.h
index fcde852..340cc23 100644
--- a/hardware/conrad-digi-35-cpu/protocol.h
+++ b/src/hardware/conrad-digi-35-cpu/protocol.h
@@ -27,10 +27,9 @@
 #define LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
 
 #include <stdint.h>
-#include <errno.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "conrad-digi-35-cpu"
diff --git a/hardware/demo/demo.c b/src/hardware/demo/demo.c
similarity index 51%
rename from hardware/demo/demo.c
rename to src/hardware/demo/demo.c
index 1718f81..6042cfd 100644
--- a/hardware/demo/demo.c
+++ b/src/hardware/demo/demo.c
@@ -4,6 +4,7 @@
  * Copyright (C) 2010 Uwe Hermann <uwe at hermann-uwe.de>
  * Copyright (C) 2011 Olivier Fauchon <olivier at aixmarseille.com>
  * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ * Copyright (C) 2015 Bartosz Golaszewski <bgolaszewski at baylibre.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
@@ -20,16 +21,11 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
 #include <math.h>
-#ifdef _WIN32
-#include <io.h>
-#include <fcntl.h>
-#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
-#endif
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "demo"
@@ -42,7 +38,7 @@
 /* Size of the analog pattern space per channel. */
 #define ANALOG_BUFSIZE       4096
 
-#define ANALOG_AMPLITUDE 25
+#define DEFAULT_ANALOG_AMPLITUDE 25
 #define ANALOG_SAMPLES_PER_PERIOD 20
 
 /* Logic patterns we can generate. */
@@ -99,21 +95,22 @@ static const char *analog_pattern_str[] = {
 
 struct analog_gen {
 	int pattern;
+	float amplitude;
 	float pattern_data[ANALOG_BUFSIZE];
 	unsigned int num_samples;
-	struct sr_datafeed_analog packet;
+	struct sr_datafeed_analog_old packet;
+	float avg_val; /* Average value */
+	unsigned num_avgs; /* Number of samples averaged */
 };
 
 /* Private, per-device-instance driver context. */
 struct dev_context {
-	int pipe_fds[2];
-	GIOChannel *channel;
 	uint64_t cur_samplerate;
 	uint64_t limit_samples;
 	uint64_t limit_msec;
-	uint64_t logic_counter;
-	uint64_t analog_counter;
-	int64_t starttime;
+	uint64_t sent_samples;
+	int64_t start_us;
+	int64_t spent_us;
 	uint64_t step;
 	/* Logic */
 	int32_t num_logic_channels;
@@ -123,24 +120,42 @@ struct dev_context {
 	unsigned char logic_data[LOGIC_BUFSIZE];
 	/* Analog */
 	int32_t num_analog_channels;
-	GSList *analog_channel_groups;
+	GHashTable *ch_ag;
+	gboolean avg; /* True if averaging is enabled */
+	uint64_t avg_samples;
 };
 
-static const int32_t scanopts[] = {
+static const uint32_t drvopts[] = {
+	SR_CONF_DEMO_DEV,
+	SR_CONF_LOGIC_ANALYZER,
+	SR_CONF_OSCILLOSCOPE,
+};
+
+static const uint32_t scanopts[] = {
 	SR_CONF_NUM_LOGIC_CHANNELS,
 	SR_CONF_NUM_ANALOG_CHANNELS,
 };
 
-static const int devopts[] = {
-	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_DEMO_DEV,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_AVERAGING | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t devopts_cg_logic[] = {
+	SR_CONF_PATTERN_MODE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint32_t devopts_cg_analog_group[] = {
+	SR_CONF_AMPLITUDE | SR_CONF_GET | SR_CONF_SET,
 };
 
-static const int devopts_cg[] = {
-	SR_CONF_PATTERN_MODE,
+static const uint32_t devopts_cg_analog_channel[] = {
+	SR_CONF_PATTERN_MODE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_AMPLITUDE | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const uint64_t samplerates[] = {
@@ -149,7 +164,7 @@ static const uint64_t samplerates[] = {
 	SR_HZ(1),
 };
 
-static uint8_t pattern_sigrok[] = {
+static const 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,
@@ -161,63 +176,56 @@ static uint8_t pattern_sigrok[] = {
 };
 
 SR_PRIV struct sr_dev_driver demo_driver_info;
-static struct sr_dev_driver *di = &demo_driver_info;
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
 
-
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static void generate_analog_pattern(const struct sr_channel_group *cg, uint64_t sample_rate)
+static void generate_analog_pattern(struct analog_gen *ag, uint64_t sample_rate)
 {
-	struct analog_gen *ag;
 	double t, frequency;
 	float value;
 	unsigned int num_samples, i;
 	int last_end;
 
-	ag = cg->priv;
-	num_samples = ANALOG_BUFSIZE / sizeof(float);
+	sr_dbg("Generating %s pattern.", analog_pattern_str[ag->pattern]);
 
-	sr_dbg("Generating %s pattern for channel group %s",
-	       analog_pattern_str[ag->pattern], cg->name);
+	num_samples = ANALOG_BUFSIZE / sizeof(float);
 
 	switch (ag->pattern) {
 	case PATTERN_SQUARE:
-		value = ANALOG_AMPLITUDE;
+		value = ag->amplitude;
 		last_end = 0;
 		for (i = 0; i < num_samples; i++) {
 			if (i % 5 == 0)
 				value = -value;
 			if (i % 10 == 0)
-				last_end = i - 1;
+				last_end = i;
 			ag->pattern_data[i] = value;
 		}
 		ag->num_samples = last_end;
 		break;
-
 	case PATTERN_SINE:
 		frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
 
 		/* Make sure the number of samples we put out is an integer
 		 * multiple of our period size */
 		/* FIXME we actually need only one period. A ringbuffer would be
-		 * usefull here.*/
+		 * useful here. */
 		while (num_samples % ANALOG_SAMPLES_PER_PERIOD != 0)
 			num_samples--;
 
 		for (i = 0; i < num_samples; i++) {
 			t = (double) i / (double) sample_rate;
-			ag->pattern_data[i] = ANALOG_AMPLITUDE *
-						sin(2 * M_PI * frequency * t);
+			ag->pattern_data[i] = ag->amplitude *
+						sin(2 * G_PI * frequency * t);
 		}
 
 		ag->num_samples = num_samples;
 		break;
-
 	case PATTERN_TRIANGLE:
 		frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
 
@@ -226,13 +234,12 @@ static void generate_analog_pattern(const struct sr_channel_group *cg, uint64_t
 
 		for (i = 0; i < num_samples; i++) {
 			t = (double) i / (double) sample_rate;
-			ag->pattern_data[i] = (2 * ANALOG_AMPLITUDE / M_PI) *
-						asin(sin(2 * M_PI * frequency * t));
+			ag->pattern_data[i] = (2 * ag->amplitude / G_PI) *
+						asin(sin(2 * G_PI * frequency * t));
 		}
 
 		ag->num_samples = num_samples;
 		break;
-
 	case PATTERN_SAWTOOTH:
 		frequency = (double) sample_rate / ANALOG_SAMPLES_PER_PERIOD;
 
@@ -241,7 +248,7 @@ static void generate_analog_pattern(const struct sr_channel_group *cg, uint64_t
 
 		for (i = 0; i < num_samples; i++) {
 			t = (double) i / (double) sample_rate;
-			ag->pattern_data[i] = 2 * ANALOG_AMPLITUDE *
+			ag->pattern_data[i] = 2 * ag->amplitude *
 						((t * frequency) - floor(0.5f + t * frequency));
 		}
 
@@ -250,20 +257,20 @@ static void generate_analog_pattern(const struct sr_channel_group *cg, uint64_t
 	}
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
 	struct sr_channel *ch;
-	struct sr_channel_group *cg;
+	struct sr_channel_group *cg, *acg;
 	struct sr_config *src;
 	struct analog_gen *ag;
 	GSList *devices, *l;
 	int num_logic_channels, num_analog_channels, pattern, i;
 	char channel_name[16];
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	num_logic_channels = DEFAULT_NUM_LOGIC_CHANNELS;
 	num_analog_channels = DEFAULT_NUM_ANALOG_CHANNELS;
@@ -280,71 +287,61 @@ static GSList *scan(GSList *options)
 	}
 
 	devices = NULL;
-	sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, "Demo device", NULL, NULL);
-	if (!sdi) {
-		sr_err("Device instance creation failed.");
-		return NULL;
-	}
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_ACTIVE;
+	sdi->model = g_strdup("Demo device");
 	sdi->driver = di;
 
-	if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		return NULL;
-	}
+	devc = g_malloc0(sizeof(struct dev_context));
 	devc->cur_samplerate = SR_KHZ(200);
-	devc->limit_samples = 0;
-	devc->limit_msec = 0;
-	devc->step = 0;
 	devc->num_logic_channels = num_logic_channels;
 	devc->logic_unitsize = (devc->num_logic_channels + 7) / 8;
 	devc->logic_pattern = PATTERN_SIGROK;
 	devc->num_analog_channels = num_analog_channels;
-	devc->analog_channel_groups = NULL;
 
 	/* Logic channels, all in one channel group. */
-	if (!(cg = g_try_malloc(sizeof(struct sr_channel_group))))
-		return NULL;
+	cg = g_malloc0(sizeof(struct sr_channel_group));
 	cg->name = g_strdup("Logic");
-	cg->channels = NULL;
-	cg->priv = NULL;
 	for (i = 0; i < num_logic_channels; i++) {
 		sprintf(channel_name, "D%d", i);
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name)))
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
 		cg->channels = g_slist_append(cg->channels, ch);
 	}
 	sdi->channel_groups = g_slist_append(NULL, cg);
 
 	/* Analog channels, channel groups and pattern generators. */
-
 	pattern = 0;
+	/* An "Analog" channel group with all analog channels in it. */
+	acg = g_malloc0(sizeof(struct sr_channel_group));
+	acg->name = g_strdup("Analog");
+	sdi->channel_groups = g_slist_append(sdi->channel_groups, acg);
+
+	devc->ch_ag = g_hash_table_new(g_direct_hash, g_direct_equal);
 	for (i = 0; i < num_analog_channels; i++) {
-		sprintf(channel_name, "A%d", i);
-		if (!(ch = sr_channel_new(i + num_logic_channels,
-				SR_CHANNEL_ANALOG, TRUE, channel_name)))
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-
-		/* Every analog channel gets its own channel group. */
-		if (!(cg = g_try_malloc(sizeof(struct sr_channel_group))))
-			return NULL;
+		snprintf(channel_name, 16, "A%d", i);
+		ch = sr_channel_new(sdi, i + num_logic_channels, SR_CHANNEL_ANALOG,
+				TRUE, channel_name);
+		acg->channels = g_slist_append(acg->channels, ch);
+
+		/* Every analog channel gets its own channel group as well. */
+		cg = g_malloc0(sizeof(struct sr_channel_group));
 		cg->name = g_strdup(channel_name);
 		cg->channels = g_slist_append(NULL, ch);
+		sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
 
-		/* Every channel group gets a generator struct. */
-		if (!(ag = g_try_malloc(sizeof(struct analog_gen))))
-			return NULL;
+		/* Every channel gets a generator struct. */
+		ag = g_malloc(sizeof(struct analog_gen));
+		ag->amplitude = DEFAULT_ANALOG_AMPLITUDE;
 		ag->packet.channels = cg->channels;
 		ag->packet.mq = 0;
 		ag->packet.mqflags = 0;
 		ag->packet.unit = SR_UNIT_VOLT;
 		ag->packet.data = ag->pattern_data;
 		ag->pattern = pattern;
-		cg->priv = ag;
-
-		sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
-		devc->analog_channel_groups = g_slist_append(devc->analog_channel_groups, cg);
+		ag->avg_val = 0.0f;
+		ag->num_avgs = 0;
+		g_hash_table_insert(devc->ch_ag, ch, ag);
 
 		if (++pattern == ARRAY_SIZE(analog_pattern_str))
 			pattern = 0;
@@ -357,9 +354,9 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -376,12 +373,28 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static void clear_helper(void *priv)
 {
-	return std_dev_clear(di, NULL);
+	struct dev_context *devc;
+	GHashTableIter iter;
+	void *value;
+
+	devc = priv;
+
+	/* Analog generators. */
+	g_hash_table_iter_init(&iter, devc->ch_ag);
+	while (g_hash_table_iter_next(&iter, NULL, &value))
+		g_free(value);
+	g_hash_table_unref(devc->ch_ag);
+	g_free(devc);
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, clear_helper);
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -393,7 +406,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 		return SR_ERR_ARG;
 
 	devc = sdi->priv;
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		*data = g_variant_new_uint64(devc->cur_samplerate);
 		break;
@@ -403,25 +416,36 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	case SR_CONF_LIMIT_MSEC:
 		*data = g_variant_new_uint64(devc->limit_msec);
 		break;
+	case SR_CONF_AVERAGING:
+		*data = g_variant_new_boolean(devc->avg);
+		break;
+	case SR_CONF_AVG_SAMPLES:
+		*data = g_variant_new_uint64(devc->avg_samples);
+		break;
 	case SR_CONF_PATTERN_MODE:
 		if (!cg)
 			return SR_ERR_CHANNEL_GROUP;
+		/* Any channel in the group will do. */
 		ch = cg->channels->data;
 		if (ch->type == SR_CHANNEL_LOGIC) {
 			pattern = devc->logic_pattern;
 			*data = g_variant_new_string(logic_pattern_str[pattern]);
 		} else if (ch->type == SR_CHANNEL_ANALOG) {
-			ag = cg->priv;
+			ag = g_hash_table_lookup(devc->ch_ag, ch);
 			pattern = ag->pattern;
 			*data = g_variant_new_string(analog_pattern_str[pattern]);
 		} else
 			return SR_ERR_BUG;
 		break;
-	case SR_CONF_NUM_LOGIC_CHANNELS:
-		*data = g_variant_new_int32(devc->num_logic_channels);
-		break;
-	case SR_CONF_NUM_ANALOG_CHANNELS:
-		*data = g_variant_new_int32(devc->num_analog_channels);
+	case SR_CONF_AMPLITUDE:
+		if (!cg)
+			return SR_ERR_CHANNEL_GROUP;
+		/* Any channel in the group will do. */
+		ch = cg->channels->data;
+		if (ch->type != SR_CHANNEL_ANALOG)
+			return SR_ERR_ARG;
+		ag = g_hash_table_lookup(devc->ch_ag, ch);
+		*data = g_variant_new_double(ag->amplitude);
 		break;
 	default:
 		return SR_ERR_NA;
@@ -430,13 +454,14 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
 	struct analog_gen *ag;
 	struct sr_channel *ch;
-	int pattern, ret;
+	GSList *l;
+	int logic_pattern, analog_pattern, ret;
 	unsigned int i;
 	const char *stropt;
 
@@ -446,60 +471,79 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		return SR_ERR_DEV_CLOSED;
 
 	ret = SR_OK;
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		devc->cur_samplerate = g_variant_get_uint64(data);
-		sr_dbg("Setting samplerate to %" PRIu64, devc->cur_samplerate);
 		break;
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_msec = 0;
 		devc->limit_samples = g_variant_get_uint64(data);
-		sr_dbg("Setting sample limit to %" PRIu64, devc->limit_samples);
 		break;
 	case SR_CONF_LIMIT_MSEC:
 		devc->limit_msec = g_variant_get_uint64(data);
 		devc->limit_samples = 0;
-		sr_dbg("Setting time limit to %" PRIu64"ms", devc->limit_msec);
+		break;
+	case SR_CONF_AVERAGING:
+		devc->avg = g_variant_get_boolean(data);
+		sr_dbg("%s averaging", devc->avg ? "Enabling" : "Disabling");
+		break;
+	case SR_CONF_AVG_SAMPLES:
+		devc->avg_samples = g_variant_get_uint64(data);
+		sr_dbg("Setting averaging rate to %" PRIu64, devc->avg_samples);
 		break;
 	case SR_CONF_PATTERN_MODE:
 		if (!cg)
 			return SR_ERR_CHANNEL_GROUP;
 		stropt = g_variant_get_string(data, NULL);
-		ch = cg->channels->data;
-		pattern = -1;
-		if (ch->type == SR_CHANNEL_LOGIC) {
-			for (i = 0; i < ARRAY_SIZE(logic_pattern_str); i++) {
-				if (!strcmp(stropt, logic_pattern_str[i])) {
-					pattern = i;
-					break;
-				}
+		logic_pattern = analog_pattern = -1;
+		for (i = 0; i < ARRAY_SIZE(logic_pattern_str); i++) {
+			if (!strcmp(stropt, logic_pattern_str[i])) {
+				logic_pattern = i;
+				break;
 			}
-			if (pattern == -1)
-				return SR_ERR_ARG;
-			devc->logic_pattern = pattern;
-
-			/* Might as well do this now, these are static. */
-			if (pattern == PATTERN_ALL_LOW)
-				memset(devc->logic_data, 0x00, LOGIC_BUFSIZE);
-			else if (pattern == PATTERN_ALL_HIGH)
-				memset(devc->logic_data, 0xff, LOGIC_BUFSIZE);
-			sr_dbg("Setting logic pattern to %s",
-					logic_pattern_str[pattern]);
-		} else if (ch->type == SR_CHANNEL_ANALOG) {
-			for (i = 0; i < ARRAY_SIZE(analog_pattern_str); i++) {
-				if (!strcmp(stropt, analog_pattern_str[i])) {
-					pattern = i;
-					break;
-				}
+		}
+		for (i = 0; i < ARRAY_SIZE(analog_pattern_str); i++) {
+			if (!strcmp(stropt, analog_pattern_str[i])) {
+				analog_pattern = i;
+				break;
 			}
-			if (pattern == -1)
+		}
+		if (logic_pattern == -1 && analog_pattern == -1)
+			return SR_ERR_ARG;
+		for (l = cg->channels; l; l = l->next) {
+			ch = l->data;
+			if (ch->type == SR_CHANNEL_LOGIC) {
+				if (logic_pattern == -1)
+					return SR_ERR_ARG;
+				sr_dbg("Setting logic pattern to %s",
+						logic_pattern_str[logic_pattern]);
+				devc->logic_pattern = logic_pattern;
+				/* Might as well do this now, these are static. */
+				if (logic_pattern == PATTERN_ALL_LOW)
+					memset(devc->logic_data, 0x00, LOGIC_BUFSIZE);
+				else if (logic_pattern == PATTERN_ALL_HIGH)
+					memset(devc->logic_data, 0xff, LOGIC_BUFSIZE);
+			} else if (ch->type == SR_CHANNEL_ANALOG) {
+				if (analog_pattern == -1)
+					return SR_ERR_ARG;
+				sr_dbg("Setting analog pattern for channel %s to %s",
+						ch->name, analog_pattern_str[analog_pattern]);
+				ag = g_hash_table_lookup(devc->ch_ag, ch);
+				ag->pattern = analog_pattern;
+			} else
+				return SR_ERR_BUG;
+		}
+		break;
+	case SR_CONF_AMPLITUDE:
+		if (!cg)
+			return SR_ERR_CHANNEL_GROUP;
+		for (l = cg->channels; l; l = l->next) {
+			ch = l->data;
+			if (ch->type != SR_CHANNEL_ANALOG)
 				return SR_ERR_ARG;
-			sr_dbg("Setting analog pattern for channel group %s to %s",
-					cg->name, analog_pattern_str[pattern]);
-			ag = cg->priv;
-			ag->pattern = pattern;
-		} else
-			return SR_ERR_BUG;
+			ag = g_hash_table_lookup(devc->ch_ag, ch);
+			ag->amplitude = g_variant_get_double(data);
+		}
 		break;
 	default:
 		ret = SR_ERR_NA;
@@ -508,18 +552,22 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct sr_channel *ch;
 	GVariant *gvar;
 	GVariantBuilder gvb;
 
-	(void)sdi;
-
 	if (key == SR_CONF_SCAN_OPTIONS) {
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
 		return SR_OK;
 	}
 
@@ -529,8 +577,8 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	if (!cg) {
 		switch (key) {
 		case SR_CONF_DEVICE_OPTIONS:
-			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-					devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 			break;
 		case SR_CONF_SAMPLERATE:
 			g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
@@ -546,10 +594,28 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		ch = cg->channels->data;
 		switch (key) {
 		case SR_CONF_DEVICE_OPTIONS:
-			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-					devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(int32_t));
+			if (ch->type == SR_CHANNEL_LOGIC)
+				*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+						devopts_cg_logic, ARRAY_SIZE(devopts_cg_logic),
+						sizeof(uint32_t));
+			else if (ch->type == SR_CHANNEL_ANALOG) {
+				if (strcmp(cg->name, "Analog") == 0)
+					*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+							devopts_cg_analog_group, ARRAY_SIZE(devopts_cg_analog_group),
+							sizeof(uint32_t));
+				else
+					*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+							devopts_cg_analog_channel, ARRAY_SIZE(devopts_cg_analog_channel),
+							sizeof(uint32_t));
+			}
+			else
+				return SR_ERR_BUG;
 			break;
 		case SR_CONF_PATTERN_MODE:
+			/* The analog group (with all 4 channels) shall not have a pattern property. */
+			if (strcmp(cg->name, "Analog") == 0)
+				return SR_ERR_NA;
+
 			if (ch->type == SR_CHANNEL_LOGIC)
 				*data = g_variant_new_strv(logic_pattern_str,
 						ARRAY_SIZE(logic_pattern_str));
@@ -608,6 +674,66 @@ static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
 	}
 }
 
+static void send_analog_packet(struct analog_gen *ag,
+			       struct sr_dev_inst *sdi,
+			       uint64_t *analog_sent,
+			       uint64_t analog_pos,
+			       uint64_t analog_todo)
+{
+	struct sr_datafeed_packet packet;
+	struct dev_context *devc;
+	uint64_t sending_now, to_avg;
+	int ag_pattern_pos;
+	unsigned int i;
+
+	devc = sdi->priv;
+	packet.type = SR_DF_ANALOG_OLD;
+	packet.payload = &ag->packet;
+
+	if (!devc->avg) {
+		ag_pattern_pos = analog_pos % ag->num_samples;
+		sending_now = MIN(analog_todo, ag->num_samples-ag_pattern_pos);
+		ag->packet.data = ag->pattern_data + ag_pattern_pos;
+		ag->packet.num_samples = sending_now;
+		sr_session_send(sdi, &packet);
+
+		/* Whichever channel group gets there first. */
+		*analog_sent = MAX(*analog_sent, sending_now);
+	} else {
+		ag_pattern_pos = analog_pos % ag->num_samples;
+		to_avg = MIN(analog_todo, ag->num_samples-ag_pattern_pos);
+
+		for (i = 0; i < to_avg; i++) {
+			ag->avg_val = (ag->avg_val +
+					*(ag->pattern_data +
+					  ag_pattern_pos + i)) / 2;
+			ag->num_avgs++;
+			/* Time to send averaged data? */
+			if (devc->avg_samples > 0 &&
+			    ag->num_avgs >= devc->avg_samples)
+				goto do_send;
+		}
+
+		if (devc->avg_samples == 0) {
+			/* We're averaging all the samples, so wait with
+			 * sending until the very end.
+			 */
+			*analog_sent = ag->num_avgs;
+			return;
+		}
+
+do_send:
+		ag->packet.data = &ag->avg_val;
+		ag->packet.num_samples = 1;
+
+		sr_session_send(sdi, &packet);
+		*analog_sent = ag->num_avgs;
+
+		ag->num_avgs = 0;
+		ag->avg_val = 0.0f;
+	}
+}
+
 /* Callback handling data */
 static int prepare_data(int fd, int revents, void *cb_data)
 {
@@ -615,11 +741,11 @@ static int prepare_data(int fd, int revents, void *cb_data)
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
 	struct sr_datafeed_logic logic;
-	struct sr_channel_group *cg;
 	struct analog_gen *ag;
-	GSList *l;
-	uint64_t logic_todo, analog_todo, expected_samplenum, analog_samples, sending_now;
-	int64_t time, elapsed;
+	GHashTableIter iter;
+	void *value;
+	uint64_t samples_todo, logic_done, analog_done, analog_sent, sending_now;
+	int64_t elapsed_us, limit_us, todo_us;
 
 	(void)fd;
 	(void)revents;
@@ -627,19 +753,44 @@ static int prepare_data(int fd, int revents, void *cb_data)
 	sdi = cb_data;
 	devc = sdi->priv;
 
-	/* 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;
+	/* Just in case. */
+	if (devc->cur_samplerate <= 0 || devc->logic_unitsize <= 0
+			|| (devc->num_logic_channels <= 0
+			&& devc->num_analog_channels <= 0)) {
+		dev_acquisition_stop(sdi, sdi);
+		return G_SOURCE_CONTINUE;
+	}
 
-	/* Of those, how many do we still have to send? */
-	logic_todo = MIN(expected_samplenum, devc->limit_samples) - devc->logic_counter;
-	analog_todo = MIN(expected_samplenum, devc->limit_samples) - devc->analog_counter;
+	/* What time span should we send samples for? */
+	elapsed_us = g_get_monotonic_time() - devc->start_us;
+	limit_us = 1000 * devc->limit_msec;
+	if (limit_us > 0 && limit_us < elapsed_us)
+		todo_us = MAX(0, limit_us - devc->spent_us);
+	else
+		todo_us = MAX(0, elapsed_us - devc->spent_us);
+
+	/* How many samples are outstanding since the last round? */
+	samples_todo = (todo_us * devc->cur_samplerate + G_USEC_PER_SEC - 1)
+			/ G_USEC_PER_SEC;
+	if (devc->limit_samples > 0) {
+		if (devc->limit_samples < devc->sent_samples)
+			samples_todo = 0;
+		else if (devc->limit_samples - devc->sent_samples < samples_todo)
+			samples_todo = devc->limit_samples - devc->sent_samples;
+	}
+	/* Calculate the actual time covered by this run back from the sample
+	 * count, rounded towards zero. This avoids getting stuck on a too-low
+	 * time delta with no samples being sent due to round-off.
+	 */
+	todo_us = samples_todo * G_USEC_PER_SEC / devc->cur_samplerate;
 
-	while (logic_todo || analog_todo) {
+	logic_done  = devc->num_logic_channels  > 0 ? 0 : samples_todo;
+	analog_done = devc->num_analog_channels > 0 ? 0 : samples_todo;
+
+	while (logic_done < samples_todo || analog_done < samples_todo) {
 		/* Logic */
-		if (devc->num_logic_channels > 0 && logic_todo > 0) {
-			sending_now = MIN(logic_todo,
+		if (logic_done < samples_todo) {
+			sending_now = MIN(samples_todo - logic_done,
 					LOGIC_BUFSIZE / devc->logic_unitsize);
 			logic_generator(sdi, sending_now * devc->logic_unitsize);
 			packet.type = SR_DF_LOGIC;
@@ -648,110 +799,94 @@ static int prepare_data(int fd, int revents, void *cb_data)
 			logic.unitsize = devc->logic_unitsize;
 			logic.data = devc->logic_data;
 			sr_session_send(sdi, &packet);
-			logic_todo -= sending_now;
-			devc->logic_counter += sending_now;
+			logic_done += sending_now;
 		}
 
 		/* Analog, one channel at a time */
-		if (devc->num_analog_channels > 0 && analog_todo > 0) {
-			sending_now = 0;
-			for (l = devc->analog_channel_groups; l; l = l->next) {
-				cg = l->data;
-				ag = cg->priv;
-				packet.type = SR_DF_ANALOG;
+		if (analog_done < samples_todo) {
+			analog_sent = 0;
+
+			g_hash_table_iter_init(&iter, devc->ch_ag);
+			while (g_hash_table_iter_next(&iter, NULL, &value)) {
+				send_analog_packet(value, sdi, &analog_sent,
+						devc->sent_samples + analog_done,
+						samples_todo - analog_done);
+			}
+			analog_done += analog_sent;
+		}
+	}
+	/* At this point, both logic_done and analog_done should be
+	 * exactly equal to samples_todo, or else.
+	 */
+	if (logic_done != samples_todo || analog_done != samples_todo) {
+		sr_err("BUG: Sample count mismatch.");
+		return G_SOURCE_REMOVE;
+	}
+	devc->sent_samples += samples_todo;
+	devc->spent_us += todo_us;
+
+	if ((devc->limit_samples > 0 && devc->sent_samples >= devc->limit_samples)
+			|| (limit_us > 0 && devc->spent_us >= limit_us)) {
+
+		/* If we're averaging everything - now is the time to send data */
+		if (devc->avg_samples == 0) {
+			g_hash_table_iter_init(&iter, devc->ch_ag);
+			while (g_hash_table_iter_next(&iter, NULL, &value)) {
+				ag = value;
+				packet.type = SR_DF_ANALOG_OLD;
 				packet.payload = &ag->packet;
-
-				/* FIXME we should make sure we output a whole
-				 * period of data before we send out again the
-				 * beginning of our buffer. A ring buffer would
-				 * help here as well */
-
-				analog_samples = MIN(analog_todo, ag->num_samples);
-				/* Whichever channel group gets there first. */
-				sending_now = MAX(sending_now, analog_samples);
-				ag->packet.num_samples = analog_samples;
+				ag->packet.data = &ag->avg_val;
+				ag->packet.num_samples = 1;
 				sr_session_send(sdi, &packet);
 			}
-			analog_todo -= sending_now;
-			devc->analog_counter += sending_now;
 		}
-	}
-
-	if (devc->logic_counter >= devc->limit_samples &&
-			devc->analog_counter >= devc->limit_samples) {
 		sr_dbg("Requested number of samples reached.");
-		dev_acquisition_stop(sdi, cb_data);
-		return TRUE;
+		dev_acquisition_stop(sdi, sdi);
 	}
 
-	return TRUE;
+	return G_SOURCE_CONTINUE;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
-	GSList *l;
 	struct dev_context *devc;
+	GHashTableIter iter;
+	void *value;
+
+	(void)cb_data;
 
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
 	devc = sdi->priv;
-	if (devc->limit_samples == 0)
-		return SR_ERR;
-	devc->logic_counter = devc->analog_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)) {
-		sr_err("%s: pipe() failed", __func__);
-		return SR_ERR;
-	}
+	devc->sent_samples = 0;
 
-	for (l = devc->analog_channel_groups; l; l = l->next) {
-		generate_analog_pattern(l->data, devc->cur_samplerate);
-	}
+	g_hash_table_iter_init(&iter, devc->ch_ag);
+	while (g_hash_table_iter_next(&iter, NULL, &value))
+		generate_analog_pattern(value, devc->cur_samplerate);
 
-	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, prepare_data, (void *)sdi);
+	sr_session_source_add(sdi->session, -1, 0, 100,
+			prepare_data, (struct sr_dev_inst *)sdi);
 
 	/* Send header packet to the session bus. */
-	std_session_send_df_header(cb_data, LOG_PREFIX);
+	std_session_send_df_header(sdi, LOG_PREFIX);
 
 	/* We use this timestamp to decide how many more samples to send. */
-	devc->starttime = g_get_monotonic_time();
+	devc->start_us = g_get_monotonic_time();
+	devc->spent_us = 0;
 
 	return SR_OK;
 }
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
-	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
 
 	(void)cb_data;
 
-	devc = sdi->priv;
 	sr_dbg("Stopping acquisition.");
 
-	sr_session_source_remove_channel(devc->channel);
-	g_io_channel_shutdown(devc->channel, FALSE, NULL);
-	g_io_channel_unref(devc->channel);
-	devc->channel = NULL;
+	sr_session_source_remove(sdi->session, -1);
 
 	/* Send last packet. */
 	packet.type = SR_DF_END;
@@ -776,5 +911,5 @@ SR_PRIV struct sr_dev_driver demo_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/src/hardware/deree-de5000/api.c b/src/hardware/deree-de5000/api.c
new file mode 100644
index 0000000..b03c3a6
--- /dev/null
+++ b/src/hardware/deree-de5000/api.c
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Janne Huttunen <jahuttun at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+static void std_dev_attach(struct sr_dev_driver *di, struct sr_dev_inst *sdi)
+{
+	struct drv_context *drvc;
+
+	drvc = di->context;
+
+	sdi->driver = di;
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)di->context)->instances;
+}
+
+#define LOG_PREFIX "deree-de5000"
+
+SR_PRIV struct sr_dev_driver deree_de5000_driver_info;
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, es51919_serial_clean);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct sr_dev_inst *sdi;
+
+	if (!(sdi = es51919_serial_scan(options, "DER EE", "DE-5000")))
+		return NULL;
+
+	std_dev_attach(di, sdi);
+
+	return g_slist_append(NULL, sdi);
+}
+
+SR_PRIV struct sr_dev_driver deree_de5000_driver_info = {
+	.name = "deree-de5000",
+	.longname = "DER EE DE-5000",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = NULL,
+	.config_get = es51919_serial_config_get,
+	.config_set = es51919_serial_config_set,
+	.config_list = es51919_serial_config_list,
+	.dev_open = std_serial_dev_open,
+	.dev_close = std_serial_dev_close,
+	.dev_acquisition_start = es51919_serial_acquisition_start,
+	.dev_acquisition_stop = es51919_serial_acquisition_stop,
+	.context = NULL,
+};
diff --git a/hardware/fluke-dmm/api.c b/src/hardware/fluke-dmm/api.c
similarity index 73%
rename from hardware/fluke-dmm/api.c
rename to src/hardware/fluke-dmm/api.c
index e0dee04..808778d 100644
--- a/hardware/fluke-dmm/api.c
+++ b/src/hardware/fluke-dmm/api.c
@@ -17,32 +17,31 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <glib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <string.h>
-#include <errno.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "fluke-dmm.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_MULTIMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
 };
 
 SR_PRIV struct sr_dev_driver flukedmm_driver_info;
-static struct sr_dev_driver *di = &flukedmm_driver_info;
 
-static char *scan_conn[] = {
+static const char *scan_conn[] = {
 	/* 287/289 */
 	"115200/8n1",
 	/* 187/189 */
@@ -59,29 +58,28 @@ static const struct flukedmm_profile supported_flukedmm[] = {
 	{ FLUKE_190, "199B", 1000, 3500 },
 };
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *fluke_scan(const char *conn, const char *serialcomm)
+static GSList *fluke_scan(struct sr_dev_driver *di, const char *conn,
+		const char *serialcomm)
 {
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
-	struct sr_channel *ch;
 	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;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
 
-	drvc = di->priv;
+	drvc = di->context;
 	b = buf;
 	retry = 0;
 	devices = NULL;
@@ -90,9 +88,8 @@ static GSList *fluke_scan(const char *conn, const char *serialcomm)
 	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));
+		if (serial_write_blocking(serial, "ID\r", 3, SERIAL_WRITE_TIMEOUT_MS) < 0) {
+			sr_err("Unable to send ID string");
 			continue;
 		}
 
@@ -123,21 +120,18 @@ static GSList *fluke_scan(const char *conn, const char *serialcomm)
 					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;
-				}
+				sdi = g_malloc0(sizeof(struct sr_dev_inst));
+				sdi->status = SR_ST_INACTIVE;
+				sdi->vendor = g_strdup("Fluke");
+				sdi->model = g_strdup(tokens[0] + 6);
+				sdi->version = g_strdup(tokens[1] + s);
+				devc = g_malloc0(sizeof(struct dev_context));
 				devc->profile = &supported_flukedmm[i];
 				sdi->inst_type = SR_INST_SERIAL;
 				sdi->conn = serial;
 				sdi->priv = devc;
 				sdi->driver = di;
-				if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-					return NULL;
-				sdi->channels = g_slist_append(sdi->channels, ch);
+				sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 				drvc->instances = g_slist_append(drvc->instances, sdi);
 				devices = g_slist_append(devices, sdi);
 				break;
@@ -155,7 +149,7 @@ static GSList *fluke_scan(const char *conn, const char *serialcomm)
 	return devices;
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct sr_config *src;
 	GSList *l, *devices;
@@ -177,33 +171,34 @@ static GSList *scan(GSList *options)
 	if (!conn)
 		return NULL;
 
+	devices = NULL;
 	if (serialcomm) {
 		/* Use the provided comm specs. */
-		devices = fluke_scan(conn, serialcomm);
+		devices = fluke_scan(di, conn, serialcomm);
 	} else {
 		for (i = 0; scan_conn[i]; i++) {
-			if ((devices = fluke_scan(conn, scan_conn[i])))
+			if ((devices = fluke_scan(di, conn, scan_conn[i])))
 				break;
 			/* The Scopemeter 199B, at least, requires this
 			 * after all the 115k/9.6k confusion. */
-			g_usleep(5000);
+			g_usleep(5 * 1000);
 		}
 	}
 
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -218,21 +213,13 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		return SR_ERR_BUG;
 	}
 
-	switch (id) {
+	switch (key) {
 	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;
@@ -241,7 +228,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -249,12 +236,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -283,10 +270,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 	/* Poll every 100ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 50, fluke_receive_data, (void *)sdi);
+	serial_source_add(sdi->session, serial, 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));
+	if (serial_write_blocking(serial, "QM\r", 3, SERIAL_WRITE_TIMEOUT_MS) < 0) {
+		sr_err("Unable to send QM.");
 		return SR_ERR;
 	}
 	devc->cmd_sent_at = g_get_monotonic_time() / 1000;
@@ -317,5 +305,5 @@ SR_PRIV struct sr_dev_driver flukedmm_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/fluke-dmm/fluke-dmm.h b/src/hardware/fluke-dmm/fluke-dmm.h
similarity index 95%
rename from hardware/fluke-dmm/fluke-dmm.h
rename to src/hardware/fluke-dmm/fluke-dmm.h
index d162bdd..664d76d 100644
--- a/hardware/fluke-dmm/fluke-dmm.h
+++ b/src/hardware/fluke-dmm/fluke-dmm.h
@@ -24,6 +24,9 @@
 
 #define FLUKEDMM_BUFSIZE  256
 
+/* Always USB-serial, 1ms is plenty. */
+#define SERIAL_WRITE_TIMEOUT_MS 1
+
 /* Supported models */
 enum {
 	FLUKE_187 = 1,
diff --git a/hardware/fluke-dmm/fluke.c b/src/hardware/fluke-dmm/fluke.c
similarity index 92%
rename from hardware/fluke-dmm/fluke.c
rename to src/hardware/fluke-dmm/fluke.c
index edb6734..c6ab4b0 100644
--- a/hardware/fluke-dmm/fluke.c
+++ b/src/hardware/fluke-dmm/fluke.c
@@ -17,19 +17,19 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
-#include <errno.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "fluke-dmm.h"
 
-static struct sr_datafeed_analog *handle_qm_18x(const struct sr_dev_inst *sdi,
+static struct sr_datafeed_analog_old *handle_qm_18x(const struct sr_dev_inst *sdi,
 		char **tokens)
 {
-	struct sr_datafeed_analog *analog;
+	struct sr_datafeed_analog_old *analog;
 	float fvalue;
 	char *e, *u;
 	gboolean is_oor;
@@ -40,14 +40,14 @@ static struct sr_datafeed_analog *handle_qm_18x(const struct sr_dev_inst *sdi,
 	if ((e = strstr(tokens[1], "Out of range"))) {
 		is_oor = TRUE;
 		fvalue = -1;
-		while(*e && *e != '.')
+		while (*e && *e != '.')
 			e++;
 	} else {
 		is_oor = FALSE;
 		/* Delimit the float, since sr_atof_ascii() wants only
 		 * a valid float here. */
 		e = tokens[1];
-		while(*e && *e != ' ')
+		while (*e && *e != ' ')
 			e++;
 		*e++ = '\0';
 		if (sr_atof_ascii(tokens[1], &fvalue) != SR_OK || fvalue == 0.0) {
@@ -56,13 +56,11 @@ static struct sr_datafeed_analog *handle_qm_18x(const struct sr_dev_inst *sdi,
 			return NULL;
 		}
 	}
-	while(*e && *e == ' ')
+	while (*e && *e == ' ')
 		e++;
 
-	if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))))
-		return NULL;
-	if (!(analog->data = g_try_malloc(sizeof(float))))
-		return NULL;
+	analog = g_malloc0(sizeof(struct sr_datafeed_analog_old));
+	analog->data = g_malloc(sizeof(float));
 	analog->channels = sdi->channels;
 	analog->num_samples = 1;
 	if (is_oor)
@@ -156,10 +154,10 @@ static struct sr_datafeed_analog *handle_qm_18x(const struct sr_dev_inst *sdi,
 	return analog;
 }
 
-static struct sr_datafeed_analog *handle_qm_28x(const struct sr_dev_inst *sdi,
+static struct sr_datafeed_analog_old *handle_qm_28x(const struct sr_dev_inst *sdi,
 		char **tokens)
 {
-	struct sr_datafeed_analog *analog;
+	struct sr_datafeed_analog_old *analog;
 	float fvalue;
 
 	if (!tokens[1])
@@ -170,10 +168,8 @@ static struct sr_datafeed_analog *handle_qm_28x(const struct sr_dev_inst *sdi,
 		return NULL;
 	}
 
-	if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog))))
-		return NULL;
-	if (!(analog->data = g_try_malloc(sizeof(float))))
-		return NULL;
+	analog = g_malloc0(sizeof(struct sr_datafeed_analog_old));
+	analog->data = g_malloc(sizeof(float));
 	analog->channels = sdi->channels;
 	analog->num_samples = 1;
 	*analog->data = fvalue;
@@ -366,7 +362,7 @@ static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens)
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	float fvalue;
 
 	if (!strcmp(tokens[0], "9.9E+37")) {
@@ -386,7 +382,6 @@ static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens)
 		/* Don't have valid metadata yet. */
 		return;
 
-
 	if (devc->mq == SR_MQ_RESISTANCE && isnan(fvalue))
 		fvalue = INFINITY;
 	else if (devc->mq == SR_MQ_CONTINUITY) {
@@ -402,7 +397,7 @@ static void handle_qm_19x_data(const struct sr_dev_inst *sdi, char **tokens)
 	analog.mq = devc->mq;
 	analog.unit = devc->unit;
 	analog.mqflags = 0;
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 	devc->num_samples++;
@@ -414,7 +409,7 @@ static void handle_line(const struct sr_dev_inst *sdi)
 	struct dev_context *devc;
 	struct sr_serial_dev_inst *serial;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog *analog;
+	struct sr_datafeed_analog_old *analog;
 	int num_tokens, n, i;
 	char cmd[16], **tokens;
 
@@ -456,9 +451,8 @@ static void handle_line(const struct sr_dev_inst *sdi)
 					/* Slip the request in now, before the main
 					 * timer loop asks for metadata again. */
 					n = sprintf(cmd, "QM %d\r", devc->meas_type);
-					if (serial_write(serial, cmd, n) == -1)
-						sr_err("Unable to send QM (measurement): %s.",
-								strerror(errno));
+					if (serial_write_blocking(serial, cmd, n, SERIAL_WRITE_TIMEOUT_MS) < 0)
+						sr_err("Unable to send QM (measurement).");
 				}
 			} else {
 				/* Response to QM <n> measurement request. */
@@ -471,7 +465,7 @@ static void handle_line(const struct sr_dev_inst *sdi)
 
 	if (analog) {
 		/* Got a measurement. */
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = analog;
 		sr_session_send(devc->cb_data, &packet);
 		devc->num_samples++;
@@ -500,8 +494,8 @@ SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
 	serial = sdi->conn;
 	if (revents == G_IO_IN) {
 		/* Serial data arrived. */
-		while(FLUKEDMM_BUFSIZE - devc->buflen - 1 > 0) {
-			len = serial_read(serial, devc->buf + devc->buflen, 1);
+		while (FLUKEDMM_BUFSIZE - devc->buflen - 1 > 0) {
+			len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
 			if (len < 1)
 				break;
 			devc->buflen++;
@@ -526,8 +520,8 @@ SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data)
 	 * out-of-sync or temporary disconnect issues. */
 	if ((devc->expect_response == FALSE && elapsed > devc->profile->poll_period)
 			|| elapsed > devc->profile->timeout) {
-		if (serial_write(serial, "QM\r", 3) == -1)
-			sr_err("Unable to send QM: %s.", strerror(errno));
+		if (serial_write_blocking(serial, "QM\r", 3, SERIAL_WRITE_TIMEOUT_MS) < 0)
+			sr_err("Unable to send QM.");
 		devc->cmd_sent_at = now;
 		devc->expect_response = TRUE;
 	}
diff --git a/hardware/fx2lafw/api.c b/src/hardware/fx2lafw/api.c
similarity index 54%
rename from hardware/fx2lafw/api.c
rename to src/hardware/fx2lafw/api.c
index 6b7f9fa..4a80b57 100644
--- a/hardware/fx2lafw/api.c
+++ b/src/hardware/fx2lafw/api.c
@@ -18,7 +18,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
+#include "dslogic.h"
 
 static const struct fx2lafw_profile supported_fx2[] = {
 	/*
@@ -27,23 +29,50 @@ static const struct fx2lafw_profile supported_fx2[] = {
 	 * ARMFLY AX-Pro
 	 */
 	{ 0x08a9, 0x0014, "CWAV", "USBee AX", NULL,
-		FIRMWARE_DIR "/fx2lafw-cwav-usbeeax.fw",
+		"fx2lafw-cwav-usbeeax.fw",
 		0, NULL, NULL},
 	/*
 	 * CWAV USBee DX
 	 * XZL-Studio DX
 	 */
 	{ 0x08a9, 0x0015, "CWAV", "USBee DX", NULL,
-		FIRMWARE_DIR "/fx2lafw-cwav-usbeedx.fw",
+		"fx2lafw-cwav-usbeedx.fw",
 		DEV_CAPS_16BIT, NULL, NULL },
 
 	/*
 	 * CWAV USBee SX
 	 */
 	{ 0x08a9, 0x0009, "CWAV", "USBee SX", NULL,
-		FIRMWARE_DIR "/fx2lafw-cwav-usbeesx.fw",
+		"fx2lafw-cwav-usbeesx.fw",
 		0, NULL, NULL},
 
+	/* DreamSourceLab DSLogic (before FW upload) */
+	{ 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL,
+		"dreamsourcelab-dslogic-fx2.fw",
+		DEV_CAPS_16BIT, NULL, NULL},
+	/* DreamSourceLab DSLogic (after FW upload) */
+	{ 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL,
+		"dreamsourcelab-dslogic-fx2.fw",
+		DEV_CAPS_16BIT, "DreamSourceLab", "DSLogic"},
+
+	/* DreamSourceLab DSCope (before FW upload) */
+	{ 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL,
+		"dreamsourcelab-dscope-fx2.fw",
+		DEV_CAPS_16BIT, NULL, NULL},
+	/* DreamSourceLab DSCope (after FW upload) */
+	{ 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL,
+		"dreamsourcelab-dscope-fx2.fw",
+		DEV_CAPS_16BIT, "DreamSourceLab", "DSCope"},
+
+	/* DreamSourceLab DSLogic Pro (before FW upload) */
+	{ 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL,
+		"dreamsourcelab-dslogic-pro-fx2.fw",
+		DEV_CAPS_16BIT, NULL, NULL},
+	/* DreamSourceLab DSLogic Pro (after FW upload) */
+	{ 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL,
+		"dreamsourcelab-dslogic-pro-fx2.fw",
+		DEV_CAPS_16BIT, "DreamSourceLab", "DSLogic"},
+
 	/*
 	 * Saleae Logic
 	 * EE Electronics ESLA100
@@ -51,7 +80,7 @@ static const struct fx2lafw_profile supported_fx2[] = {
 	 * Robomotic BugLogic 3
 	 */
 	{ 0x0925, 0x3881, "Saleae", "Logic", NULL,
-		FIRMWARE_DIR "/fx2lafw-saleae-logic.fw",
+		"fx2lafw-saleae-logic.fw",
 		0, NULL, NULL},
 
 	/*
@@ -60,37 +89,61 @@ static const struct fx2lafw_profile supported_fx2[] = {
 	 * Braintechnology USB Interface V2.x
 	 */
 	{ 0x04B4, 0x8613, "Cypress", "FX2", NULL,
-		FIRMWARE_DIR "/fx2lafw-cypress-fx2.fw",
+		"fx2lafw-cypress-fx2.fw",
 		DEV_CAPS_16BIT, NULL, NULL },
 
 	/*
 	 * Braintechnology USB-LPS
 	 */
 	{ 0x16d0, 0x0498, "Braintechnology", "USB-LPS", NULL,
-		FIRMWARE_DIR "/fx2lafw-braintechnology-usb-lps.fw",
+		"fx2lafw-braintechnology-usb-lps.fw",
 		DEV_CAPS_16BIT, NULL, NULL },
 
-	{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }
-};
+	/*
+	 * sigrok FX2 based 8-channel logic analyzer
+	 */
+	{ 0x1d50, 0x608c, "sigrok", "FX2 LA (8ch)", NULL,
+		"fx2lafw-sigrok-fx2-8ch.fw",
+		0, NULL, NULL},
 
-static const int32_t hwopts[] = {
-	SR_CONF_CONN,
+	/*
+	 * sigrok FX2 based 16-channel logic analyzer
+	 */
+	{ 0x1d50, 0x608d, "sigrok", "FX2 LA (16ch)", NULL,
+		"fx2lafw-sigrok-fx2-16ch.fw",
+		DEV_CAPS_16BIT, NULL, NULL },
+
+	ALL_ZERO
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t drvopts[] = {
 	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_TRIGGER_TYPE,
-	SR_CONF_SAMPLERATE,
+};
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+};
 
-	/* These are really implemented in the driver, not the hardware. */
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_CONTINUOUS,
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_CONN | SR_CONF_GET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
 };
 
 static const char *channel_names[] = {
-	"0",  "1",  "2",  "3",  "4",  "5",  "6",  "7",
-	"8",  "9", "10", "11", "12", "13", "14", "15",
-	NULL,
+	"0", "1", "2", "3", "4", "5", "6", "7",
+	"8", "9", "10", "11", "12", "13", "14", "15",
+};
+
+static const int32_t soft_trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
+	SR_TRIGGER_EDGE,
 };
 
 static const uint64_t samplerates[] = {
@@ -112,32 +165,50 @@ static const uint64_t samplerates[] = {
 	SR_MHZ(24),
 };
 
+static const uint64_t dslogic_samplerates[] = {
+	SR_KHZ(10),
+	SR_KHZ(20),
+	SR_KHZ(50),
+	SR_KHZ(100),
+	SR_KHZ(200),
+	SR_KHZ(500),
+	SR_MHZ(1),
+	SR_MHZ(2),
+	SR_MHZ(5),
+	SR_MHZ(10),
+	SR_MHZ(20),
+	SR_MHZ(25),
+	SR_MHZ(50),
+	SR_MHZ(100),
+	SR_MHZ(200),
+	SR_MHZ(400),
+};
+
 SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
-static struct sr_dev_driver *di = &fx2lafw_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
 	struct sr_usb_dev_inst *usb;
-	struct sr_channel *ch;
 	struct sr_config *src;
 	const struct fx2lafw_profile *prof;
 	GSList *l, *devices, *conn_devices;
+	gboolean has_firmware;
 	struct libusb_device_descriptor des;
 	libusb_device **devlist;
 	struct libusb_device_handle *hdl;
-	int devcnt, num_logic_channels, ret, i, j;
+	int num_logic_channels, ret, i, j;
 	const char *conn;
-	char manufacturer[64], product[64];
+	char manufacturer[64], product[64], serial_num[64], connection_id[64];
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	conn = NULL;
 	for (l = options; l; l = l->next) {
@@ -171,11 +242,7 @@ static GSList *scan(GSList *options)
 				continue;
 		}
 
-		if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) {
-			sr_warn("Failed to get device descriptor: %s.",
-				libusb_error_name(ret));
-			continue;
-		}
+		libusb_get_device_descriptor( devlist[i], &des);
 
 		if ((ret = libusb_open(devlist[i], &hdl)) < 0)
 			continue;
@@ -200,6 +267,18 @@ static GSList *scan(GSList *options)
 			continue;
 		}
 
+		if (des.iSerialNumber == 0) {
+			serial_num[0] = '\0';
+		} else if ((ret = libusb_get_string_descriptor_ascii(hdl,
+				des.iSerialNumber, (unsigned char *) serial_num,
+				sizeof(serial_num))) < 0) {
+			sr_warn("Failed to get serial number string descriptor: %s.",
+				libusb_error_name(ret));
+			continue;
+		}
+
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+
 		libusb_close(hdl);
 
 		prof = NULL;
@@ -219,29 +298,45 @@ static GSList *scan(GSList *options)
 		if (!prof)
 			continue;
 
-		devcnt = g_slist_length(drvc->instances);
-		sdi = sr_dev_inst_new(devcnt, SR_ST_INITIALIZING,
-			prof->vendor, prof->model, prof->model_version);
-		if (!sdi)
-			return NULL;
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INITIALIZING;
+		sdi->vendor = g_strdup(prof->vendor);
+		sdi->model = g_strdup(prof->model);
+		sdi->version = g_strdup(prof->model_version);
 		sdi->driver = di;
+		sdi->serial_num = g_strdup(serial_num);
+		sdi->connection_id = g_strdup(connection_id);
 
 		/* Fill in channellist according to this device's profile. */
 		num_logic_channels = prof->dev_caps & DEV_CAPS_16BIT ? 16 : 8;
-		for (j = 0; j < num_logic_channels; j++) {
-			if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
-					channel_names[j])))
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
-		}
+		for (j = 0; j < num_logic_channels; j++)
+			sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE,
+					channel_names[j]);
 
 		devc = fx2lafw_dev_new();
 		devc->profile = prof;
+		devc->sample_wide = (prof->dev_caps & DEV_CAPS_16BIT) != 0;
 		sdi->priv = devc;
 		drvc->instances = g_slist_append(drvc->instances, sdi);
 		devices = g_slist_append(devices, sdi);
 
-		if (fx2lafw_check_conf_profile(devlist[i])) {
+		if (!strcmp(prof->model, "DSLogic")
+				|| !strcmp(prof->model, "DSLogic Pro")
+				|| !strcmp(prof->model, "DSCope")) {
+			devc->dslogic = TRUE;
+			devc->samplerates = dslogic_samplerates;
+			devc->num_samplerates = ARRAY_SIZE(dslogic_samplerates);
+			has_firmware = match_manuf_prod(devlist[i], "DreamSourceLab", "DSLogic")
+					|| match_manuf_prod(devlist[i], "DreamSourceLab", "DSCope");
+		} else {
+			devc->dslogic = FALSE;
+			devc->samplerates = samplerates;
+			devc->num_samplerates = ARRAY_SIZE(samplerates);
+			has_firmware = match_manuf_prod(devlist[i],
+					"sigrok", "fx2lafw");
+		}
+
+		if (has_firmware) {
 			/* Already has the firmware, so fix the new address. */
 			sr_dbg("Found an fx2lafw device.");
 			sdi->status = SR_ST_INACTIVE;
@@ -249,13 +344,15 @@ static GSList *scan(GSList *options)
 			sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
 					libusb_get_device_address(devlist[i]), NULL);
 		} else {
-			if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
-				prof->firmware) == SR_OK)
+			if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
+					USB_CONFIGURATION, prof->firmware) == SR_OK)
 				/* Store when this device's FW was updated. */
 				devc->fw_updated = g_get_monotonic_time();
 			else
 				sr_err("Firmware upload failed for "
-				       "device %d.", devcnt);
+				       "device %d.%d (logical).",
+				       libusb_get_bus_number(devlist[i]),
+				       libusb_get_device_address(devlist[i]));
 			sdi->inst_type = SR_INST_USB;
 			sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
 					0xff, NULL);
@@ -267,15 +364,17 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct sr_usb_dev_inst *usb;
 	struct dev_context *devc;
+	const char *fpga_firmware = NULL;
 	int ret;
 	int64_t timediff_us, timediff_ms;
 
@@ -335,9 +434,23 @@ static int dev_open(struct sr_dev_inst *sdi)
 		return SR_ERR;
 	}
 
+	if (devc->dslogic) {
+		if (!strcmp(devc->profile->model, "DSLogic")) {
+			fpga_firmware = DSLOGIC_FPGA_FIRMWARE;
+		} else if (!strcmp(devc->profile->model, "DSLogic Pro")) {
+			fpga_firmware = DSLOGIC_PRO_FPGA_FIRMWARE;
+		} else if (!strcmp(devc->profile->model, "DSCope")) {
+			fpga_firmware = DSCOPE_FPGA_FIRMWARE;
+		}
+
+		if ((ret = dslogic_fpga_firmware_upload(sdi,
+				fpga_firmware)) != SR_OK)
+			return ret;
+	}
+
 	if (devc->cur_samplerate == 0) {
 		/* Samplerate hasn't been set; default to the slowest one. */
-		devc->cur_samplerate = samplerates[0];
+		devc->cur_samplerate = devc->samplerates[0];
 	}
 
 	return SR_OK;
@@ -348,11 +461,11 @@ static int dev_close(struct sr_dev_inst *sdi)
 	struct sr_usb_dev_inst *usb;
 
 	usb = sdi->conn;
-	if (usb->devhdl == NULL)
+	if (!usb->devhdl)
 		return SR_ERR;
 
-	sr_info("fx2lafw: Closing device %d on %d.%d interface %d.",
-		sdi->index, usb->bus, usb->address, USB_INTERFACE);
+	sr_info("fx2lafw: Closing device on %d.%d (logical) / %s (physical) interface %d.",
+		usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
 	libusb_release_interface(usb->devhdl, USB_INTERFACE);
 	libusb_close(usb->devhdl);
 	usb->devhdl = NULL;
@@ -361,24 +474,22 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	int ret;
 	struct drv_context *drvc;
 
-	if (!(drvc = di->priv))
+	if (!(drvc = di->context))
 		return SR_OK;
 
-
 	ret = std_dev_clear(di, NULL);
 
 	g_free(drvc);
-	di->priv = NULL;
 
 	return ret;
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -392,7 +503,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 
 	devc = sdi->priv;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_CONN:
 		if (!sdi->conn)
 			return SR_ERR_ARG;
@@ -410,6 +521,9 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	case SR_CONF_SAMPLERATE:
 		*data = g_variant_new_uint64(devc->cur_samplerate);
 		break;
+	case SR_CONF_CAPTURE_RATIO:
+		*data = g_variant_new_uint64(devc->capture_ratio);
+		break;
 	default:
 		return SR_ERR_NA;
 	}
@@ -417,11 +531,12 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
-	int ret;
+	uint64_t arg;
+	int i, ret;
 
 	(void)cg;
 
@@ -435,48 +550,68 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
 	ret = SR_OK;
 
-	switch (id)
-	{
-		case SR_CONF_SAMPLERATE:
-			devc->cur_samplerate = g_variant_get_uint64(data);
-			break;
-		case SR_CONF_LIMIT_SAMPLES:
-			devc->limit_samples = g_variant_get_uint64(data);
-			break;
-		default:
-			ret = SR_ERR_NA;
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		arg = g_variant_get_uint64(data);
+		for (i = 0; i < devc->num_samplerates; i++) {
+			if (devc->samplerates[i] == arg) {
+				devc->cur_samplerate = arg;
+				break;
+			}
+		}
+		if (i == devc->num_samplerates)
+			ret = SR_ERR_ARG;
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		devc->limit_samples = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_CAPTURE_RATIO:
+		devc->capture_ratio = g_variant_get_uint64(data);
+		ret = (devc->capture_ratio > 100) ? SR_ERR : SR_OK;
+		break;
+	default:
+		ret = SR_ERR_NA;
 	}
 
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
+	struct dev_context *devc;
 	GVariant *gvar;
 	GVariantBuilder gvb;
 
-	(void)sdi;
 	(void)cg;
 
 	switch (key) {
 	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLERATE:
+		if (!sdi->priv)
+			return SR_ERR_ARG;
+		devc = sdi->priv;
 		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));
+		gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), devc->samplerates,
+				devc->num_samplerates, sizeof(uint64_t));
 		g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
 		*data = g_variant_builder_end(&gvb);
 		break;
-	case SR_CONF_TRIGGER_TYPE:
-		*data = g_variant_new_string(TRIGGER_TYPE);
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
+				sizeof(int32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -492,9 +627,8 @@ static int receive_data(int fd, int revents, void *cb_data)
 
 	(void)fd;
 	(void)revents;
-	(void)cb_data;
 
-	drvc = di->priv;
+	drvc = (struct drv_context *)cb_data;
 
 	tv.tv_sec = tv.tv_usec = 0;
 	libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
@@ -502,36 +636,35 @@ static int receive_data(int fd, int revents, void *cb_data)
 	return TRUE;
 }
 
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+static int start_transfers(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
-	struct drv_context *drvc;
 	struct sr_usb_dev_inst *usb;
+	struct sr_trigger *trigger;
 	struct libusb_transfer *transfer;
-	unsigned int i, timeout, num_transfers;
-	int ret;
+	unsigned int i, num_transfers;
+	int endpoint, timeout, ret;
 	unsigned char *buf;
 	size_t size;
 
-	if (sdi->status != SR_ST_ACTIVE)
-		return SR_ERR_DEV_CLOSED;
-
-	drvc = di->priv;
 	devc = sdi->priv;
 	usb = sdi->conn;
 
-	/* Configures devc->trigger_* and devc->sample_wide */
-	if (fx2lafw_configure_channels(sdi) != SR_OK) {
-		sr_err("Failed to configure channels.");
-		return SR_ERR;
-	}
-
-	devc->cb_data = cb_data;
 	devc->sent_samples = 0;
 	devc->acq_aborted = FALSE;
 	devc->empty_transfer_count = 0;
 
-	timeout = fx2lafw_get_timeout(devc);
+	if ((trigger = sr_session_trigger_get(sdi->session))) {
+		int pre_trigger_samples = 0;
+		if (devc->limit_samples > 0)
+			pre_trigger_samples = devc->capture_ratio * devc->limit_samples/100;
+		devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
+		if (!devc->stl)
+			return SR_ERR_MALLOC;
+		devc->trigger_fired = FALSE;
+	} else
+		devc->trigger_fired = TRUE;
+
 	num_transfers = fx2lafw_get_number_of_transfers(devc);
 	size = fx2lafw_get_buffer_size(devc);
 	devc->submitted_transfers = 0;
@@ -542,6 +675,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		return SR_ERR_MALLOC;
 	}
 
+	timeout = fx2lafw_get_timeout(devc);
+	endpoint = devc->dslogic ? 6 : 2;
 	devc->num_transfers = num_transfers;
 	for (i = 0; i < num_transfers; i++) {
 		if (!(buf = g_try_malloc(size))) {
@@ -550,8 +685,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		}
 		transfer = libusb_alloc_transfer(0);
 		libusb_fill_bulk_transfer(transfer, usb->devhdl,
-				2 | LIBUSB_ENDPOINT_IN, buf, size,
-				fx2lafw_receive_transfer, devc, timeout);
+				endpoint | LIBUSB_ENDPOINT_IN, buf, size,
+				fx2lafw_receive_transfer, (void *)sdi, timeout);
 		if ((ret = libusb_submit_transfer(transfer)) != 0) {
 			sr_err("Failed to submit transfer: %s.",
 			       libusb_error_name(ret));
@@ -564,16 +699,96 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		devc->submitted_transfers++;
 	}
 
-	devc->ctx = drvc->sr_ctx;
+	/* Send header packet to the session bus. */
+	std_session_send_df_header(sdi, LOG_PREFIX);
 
-	usb_source_add(devc->ctx, timeout, receive_data, NULL);
+	return SR_OK;
+}
 
-	/* Send header packet to the session bus. */
-	std_session_send_df_header(cb_data, LOG_PREFIX);
+static void LIBUSB_CALL dslogic_trigger_receive(struct libusb_transfer *transfer)
+{
+	const struct sr_dev_inst *sdi;
+	struct dslogic_trigger_pos *tpos;
+
+	sdi = transfer->user_data;
+
+	if (transfer->status == LIBUSB_TRANSFER_COMPLETED
+			&& transfer->actual_length == sizeof(struct dslogic_trigger_pos)) {
+		tpos = (struct dslogic_trigger_pos *)transfer->buffer;
+		sr_dbg("tpos real_pos %.8x ram_saddr %.8x", tpos->real_pos, tpos->ram_saddr);
+		g_free(tpos);
+		start_transfers(sdi);
+	}
+
+	libusb_free_transfer(transfer);
+
+}
+
+static int dslogic_trigger_request(const struct sr_dev_inst *sdi)
+{
+	struct sr_usb_dev_inst *usb;
+	struct libusb_transfer *transfer;
+	struct dslogic_trigger_pos *tpos;
+	int ret;
+
+	usb = sdi->conn;
+
+	if ((ret = dslogic_stop_acquisition(sdi)) != SR_OK)
+		return ret;
+
+	if ((ret = dslogic_fpga_configure(sdi)) != SR_OK)
+		return ret;
 
-	if ((ret = fx2lafw_command_start_acquisition(sdi)) != SR_OK) {
-		fx2lafw_abort_acquisition(devc);
+	if ((ret = dslogic_start_acquisition(sdi)) != SR_OK)
 		return ret;
+
+	sr_dbg("Getting trigger.");
+	tpos = g_malloc(sizeof(struct dslogic_trigger_pos));
+	transfer = libusb_alloc_transfer(0);
+	libusb_fill_bulk_transfer(transfer, usb->devhdl, 6 | LIBUSB_ENDPOINT_IN,
+			(unsigned char *)tpos, sizeof(struct dslogic_trigger_pos),
+			dslogic_trigger_receive, (void *)sdi, 0);
+	if ((ret = libusb_submit_transfer(transfer)) < 0) {
+		sr_err("Failed to request trigger: %s.", libusb_error_name(ret));
+		libusb_free_transfer(transfer);
+		g_free(tpos);
+		return SR_ERR;
+	}
+
+	return ret;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_dev_driver *di;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	int timeout, ret;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	di = sdi->driver;
+	drvc = di->context;
+	devc = sdi->priv;
+
+	devc->ctx = drvc->sr_ctx;
+	devc->cb_data = cb_data;
+	devc->sent_samples = 0;
+	devc->empty_transfer_count = 0;
+	devc->acq_aborted = FALSE;
+
+	timeout = fx2lafw_get_timeout(devc);
+	usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc);
+
+	if (devc->dslogic) {
+		dslogic_trigger_request(sdi);
+	} else {
+		start_transfers(sdi);
+		if ((ret = fx2lafw_command_start_acquisition(sdi)) != SR_OK) {
+			fx2lafw_abort_acquisition(devc);
+			return ret;
+		}
 	}
 
 	return SR_OK;
@@ -604,5 +819,5 @@ SR_PRIV struct sr_dev_driver fx2lafw_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/src/hardware/fx2lafw/dslogic.c b/src/hardware/fx2lafw/dslogic.c
new file mode 100644
index 0000000..ec8d534
--- /dev/null
+++ b/src/hardware/fx2lafw/dslogic.c
@@ -0,0 +1,238 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "protocol.h"
+#include "dslogic.h"
+
+#define FW_BUFSIZE (4 * 1024)
+
+#define FPGA_UPLOAD_DELAY (10 * 1000)
+
+#define USB_TIMEOUT (3 * 1000)
+
+SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi,
+		const char *name)
+{
+	uint64_t sum;
+	struct sr_resource bitstream;
+	struct drv_context *drvc;
+	struct sr_usb_dev_inst *usb;
+	unsigned char *buf;
+	ssize_t chunksize;
+	int transferred;
+	int result, ret;
+	uint8_t cmd[3];
+
+	drvc = sdi->driver->context;
+	usb = sdi->conn;
+
+	sr_dbg("Uploading FPGA firmware '%s'.", name);
+
+	result = sr_resource_open(drvc->sr_ctx, &bitstream,
+			SR_RESOURCE_FIRMWARE, name);
+	if (result != SR_OK)
+		return result;
+
+	/* Tell the device firmware is coming. */
+	memset(cmd, 0, sizeof(cmd));
+	if ((ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+			LIBUSB_ENDPOINT_OUT, DS_CMD_FPGA_FW, 0x0000, 0x0000,
+			(unsigned char *)&cmd, sizeof(cmd), USB_TIMEOUT)) < 0) {
+		sr_err("Failed to upload FPGA firmware: %s.", libusb_error_name(ret));
+		sr_resource_close(drvc->sr_ctx, &bitstream);
+		return SR_ERR;
+	}
+
+	/* Give the FX2 time to get ready for FPGA firmware upload. */
+	g_usleep(FPGA_UPLOAD_DELAY);
+
+	buf = g_malloc(FW_BUFSIZE);
+	sum = 0;
+	result = SR_OK;
+	while (1) {
+		chunksize = sr_resource_read(drvc->sr_ctx, &bitstream,
+				buf, FW_BUFSIZE);
+		if (chunksize < 0)
+			result = SR_ERR;
+		if (chunksize <= 0)
+			break;
+
+		if ((ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT,
+				buf, chunksize, &transferred, USB_TIMEOUT)) < 0) {
+			sr_err("Unable to configure FPGA firmware: %s.",
+					libusb_error_name(ret));
+			result = SR_ERR;
+			break;
+		}
+		sum += transferred;
+		sr_spew("Uploaded %" PRIu64 "/%" PRIu64 " bytes.",
+			sum, bitstream.size);
+
+		if (transferred != chunksize) {
+			sr_err("Short transfer while uploading FPGA firmware.");
+			result = SR_ERR;
+			break;
+		}
+	}
+	g_free(buf);
+	sr_resource_close(drvc->sr_ctx, &bitstream);
+
+	if (result == SR_OK)
+		sr_dbg("FPGA firmware upload done.");
+
+	return result;
+}
+
+SR_PRIV int dslogic_start_acquisition(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	struct dslogic_mode mode;
+	int ret;
+
+	devc = sdi->priv;
+	mode.flags = 0;
+	mode.sample_delay_h = mode.sample_delay_l = 0;
+	if (devc->sample_wide)
+		mode.flags |= DS_START_FLAGS_SAMPLE_WIDE;
+
+	usb = sdi->conn;
+	ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+			LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000,
+			(unsigned char *)&mode, sizeof(mode), USB_TIMEOUT);
+	if (ret < 0) {
+		sr_err("Failed to send start command: %s.", libusb_error_name(ret));
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV int dslogic_stop_acquisition(const struct sr_dev_inst *sdi)
+{
+	struct sr_usb_dev_inst *usb;
+	struct dslogic_mode mode;
+	int ret;
+
+	mode.flags = DS_START_FLAGS_STOP;
+	mode.sample_delay_h = mode.sample_delay_l = 0;
+
+	usb = sdi->conn;
+	ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+			LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000,
+			(unsigned char *)&mode, sizeof(struct dslogic_mode), USB_TIMEOUT);
+	if (ret < 0) {
+		sr_err("Failed to send stop command: %s.", libusb_error_name(ret));
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV int dslogic_fpga_configure(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	uint8_t c[3];
+	struct dslogic_fpga_config cfg;
+	uint16_t v16;
+	uint32_t v32;
+	int transferred, len, ret;
+
+	sr_dbg("Configuring FPGA.");
+	usb = sdi->conn;
+	devc = sdi->priv;
+
+	WL32(&cfg.sync, DS_CFG_START);
+	WL16(&cfg.mode_header, DS_CFG_MODE);
+	WL32(&cfg.divider_header, DS_CFG_DIVIDER);
+	WL32(&cfg.count_header, DS_CFG_COUNT);
+	WL32(&cfg.trig_pos_header, DS_CFG_TRIG_POS);
+	WL16(&cfg.trig_glb_header, DS_CFG_TRIG_GLB);
+	WL32(&cfg.trig_adp_header, DS_CFG_TRIG_ADP);
+	WL32(&cfg.trig_sda_header, DS_CFG_TRIG_SDA);
+	WL32(&cfg.trig_mask0_header, DS_CFG_TRIG_MASK0);
+	WL32(&cfg.trig_mask1_header, DS_CFG_TRIG_MASK1);
+	WL32(&cfg.trig_value0_header, DS_CFG_TRIG_VALUE0);
+	WL32(&cfg.trig_value1_header, DS_CFG_TRIG_VALUE1);
+	WL32(&cfg.trig_edge0_header, DS_CFG_TRIG_EDGE0);
+	WL32(&cfg.trig_edge1_header, DS_CFG_TRIG_EDGE1);
+	WL32(&cfg.trig_count0_header, DS_CFG_TRIG_COUNT0);
+	WL32(&cfg.trig_count1_header, DS_CFG_TRIG_COUNT1);
+	WL32(&cfg.trig_logic0_header, DS_CFG_TRIG_LOGIC0);
+	WL32(&cfg.trig_logic1_header, DS_CFG_TRIG_LOGIC1);
+	WL32(&cfg.end_sync, DS_CFG_END);
+
+	/* Pass in the length of a fixed-size struct. Really. */
+	len = sizeof(struct dslogic_fpga_config) / 2;
+	c[0] = len & 0xff;
+	c[1] = (len >> 8) & 0xff;
+	c[2] = (len >> 16) & 0xff;
+
+	ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+			LIBUSB_ENDPOINT_OUT, DS_CMD_CONFIG, 0x0000, 0x0000,
+			c, 3, USB_TIMEOUT);
+	if (ret < 0) {
+		sr_err("Failed to send FPGA configure command: %s.", libusb_error_name(ret));
+		return SR_ERR;
+	}
+
+	/*
+	 * 15	1 = internal test mode
+	 * 14	1 = external test mode
+	 * 13	1 = loopback test mode
+	 * 8-12	unused
+	 * 7	1 = analog mode
+	 * 6	1 = samplerate 400MHz
+	 * 5	1 = samplerate 200MHz or analog mode
+	 * 4	0 = logic, 1 = dso or analog
+	 * 2-3	unused
+	 * 1	0 = internal clock, 1 = external clock
+	 * 0	1 = trigger enabled
+	 */
+	v16 = 0x0000;
+	if (devc->dslogic_mode == DS_OP_INTERNAL_TEST)
+		v16 = 1 << 15;
+	else if (devc->dslogic_mode == DS_OP_EXTERNAL_TEST)
+		v16 = 1 << 14;
+	else if (devc->dslogic_mode == DS_OP_LOOPBACK_TEST)
+		v16 = 1 << 13;
+	if (devc->dslogic_external_clock)
+		v16 |= 1 << 2;
+	WL16(&cfg.mode, v16);
+
+	v32 = ceil(SR_MHZ(100) * 1.0 / devc->cur_samplerate);
+	WL32(&cfg.divider, v32);
+	WL32(&cfg.count, devc->limit_samples);
+
+	len = sizeof(struct dslogic_fpga_config);
+	ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT,
+			(unsigned char *)&cfg, len, &transferred, USB_TIMEOUT);
+	if (ret < 0 || transferred != len) {
+		sr_err("Failed to send FPGA configuration: %s.", libusb_error_name(ret));
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
diff --git a/src/hardware/fx2lafw/dslogic.h b/src/hardware/fx2lafw/dslogic.h
new file mode 100644
index 0000000..5406769
--- /dev/null
+++ b/src/hardware/fx2lafw/dslogic.h
@@ -0,0 +1,132 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_FX2LAFW_DSLOGIC_H
+#define LIBSIGROK_HARDWARE_FX2LAFW_DSLOGIC_H
+
+/* Modified protocol commands & flags used by DSLogic */
+#define DS_CMD_GET_FW_VERSION		0xb0
+#define DS_CMD_GET_REVID_VERSION	0xb1
+#define DS_CMD_START			0xb2
+#define DS_CMD_FPGA_FW			0xb3
+#define DS_CMD_CONFIG			0xb4
+
+#define DS_NUM_TRIGGER_STAGES		16
+#define DS_START_FLAGS_STOP		(1 << 7)
+#define DS_START_FLAGS_CLK_48MHZ	(1 << 6)
+#define DS_START_FLAGS_SAMPLE_WIDE	(1 << 5)
+
+enum dslogic_operation_modes {
+	DS_OP_NORMAL,
+	DS_OP_INTERNAL_TEST,
+	DS_OP_EXTERNAL_TEST,
+	DS_OP_LOOPBACK_TEST,
+};
+
+struct dslogic_version {
+	uint8_t major;
+	uint8_t minor;
+};
+
+struct dslogic_mode {
+	uint8_t flags;
+	uint8_t sample_delay_h;
+	uint8_t sample_delay_l;
+};
+
+struct dslogic_trigger_pos {
+	uint32_t real_pos;
+	uint32_t ram_saddr;
+	uint8_t first_block[504];
+};
+
+/*
+ * The FPGA is configured with TLV tuples. Length is specified as the
+ * number of 16-bit words, and the (type, length) header is in some
+ * cases padded with 0xffff.
+ */
+#define _DS_CFG(variable, wordcnt) ((variable << 8) | wordcnt)
+#define _DS_CFG_PAD(variable, wordcnt) ((_DS_CFG(variable, wordcnt) << 16) | 0xffff)
+#define DS_CFG_START		0xffffffff
+#define DS_CFG_MODE		_DS_CFG(0, 1)
+#define DS_CFG_DIVIDER		_DS_CFG_PAD(1, 2)
+#define DS_CFG_COUNT		_DS_CFG_PAD(3, 2)
+#define DS_CFG_TRIG_POS		_DS_CFG_PAD(5, 2)
+#define DS_CFG_TRIG_GLB		_DS_CFG(7, 1)
+#define DS_CFG_TRIG_ADP		_DS_CFG_PAD(10, 2)
+#define DS_CFG_TRIG_SDA		_DS_CFG_PAD(12, 2)
+#define DS_CFG_TRIG_MASK0	_DS_CFG_PAD(16, 16)
+#define DS_CFG_TRIG_MASK1	_DS_CFG_PAD(17, 16)
+#define DS_CFG_TRIG_VALUE0	_DS_CFG_PAD(20, 16)
+#define DS_CFG_TRIG_VALUE1	_DS_CFG_PAD(21, 16)
+#define DS_CFG_TRIG_EDGE0	_DS_CFG_PAD(24, 16)
+#define DS_CFG_TRIG_EDGE1	_DS_CFG_PAD(25, 16)
+#define DS_CFG_TRIG_COUNT0	_DS_CFG_PAD(28, 16)
+#define DS_CFG_TRIG_COUNT1	_DS_CFG_PAD(29, 16)
+#define DS_CFG_TRIG_LOGIC0	_DS_CFG_PAD(32, 16)
+#define DS_CFG_TRIG_LOGIC1	_DS_CFG_PAD(33, 16)
+#define DS_CFG_END		0x00000000
+
+struct dslogic_fpga_config {
+	uint32_t sync;
+	uint16_t mode_header;
+	uint16_t mode;
+	uint32_t divider_header;
+	uint32_t divider;
+	uint32_t count_header;
+	uint32_t count;
+	uint32_t trig_pos_header;
+	uint32_t trig_pos;
+	uint16_t trig_glb_header;
+	uint16_t trig_glb;
+	uint32_t trig_adp_header;
+	uint32_t trig_adp;
+	uint32_t trig_sda_header;
+	uint32_t trig_sda;
+	uint32_t trig_mask0_header;
+	uint16_t trig_mask0[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_mask1_header;
+	uint16_t trig_mask1[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_value0_header;
+	uint16_t trig_value0[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_value1_header;
+	uint16_t trig_value1[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_edge0_header;
+	uint16_t trig_edge0[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_edge1_header;
+	uint16_t trig_edge1[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_count0_header;
+	uint16_t trig_count0[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_count1_header;
+	uint16_t trig_count1[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_logic0_header;
+	uint16_t trig_logic0[DS_NUM_TRIGGER_STAGES];
+	uint32_t trig_logic1_header;
+	uint16_t trig_logic1[DS_NUM_TRIGGER_STAGES];
+	uint32_t end_sync;
+};
+
+SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi,
+		const char *name);
+SR_PRIV int dslogic_start_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int dslogic_stop_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int dslogic_fpga_configure(const struct sr_dev_inst *sdi);
+
+#endif
diff --git a/hardware/fx2lafw/protocol.c b/src/hardware/fx2lafw/protocol.c
similarity index 58%
rename from hardware/fx2lafw/protocol.c
rename to src/hardware/fx2lafw/protocol.c
index 45e7837..12fc9e2 100644
--- a/hardware/fx2lafw/protocol.c
+++ b/src/hardware/fx2lafw/protocol.c
@@ -18,21 +18,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
+#include <glib.h>
+#include <glib/gstdio.h>
 #include "protocol.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)
+#include "dslogic.h"
 
 #pragma pack(push, 1)
 
@@ -49,6 +39,8 @@ struct cmd_start_acquisition {
 
 #pragma pack(pop)
 
+#define USB_TIMEOUT 100
+
 static int command_get_fw_version(libusb_device_handle *devhdl,
 				  struct version_info *vi)
 {
@@ -56,7 +48,7 @@ static int command_get_fw_version(libusb_device_handle *devhdl,
 
 	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);
+		(unsigned char *)vi, sizeof(struct version_info), USB_TIMEOUT);
 
 	if (ret < 0) {
 		sr_err("Unable to get version info: %s.",
@@ -69,13 +61,14 @@ static int command_get_fw_version(libusb_device_handle *devhdl,
 
 static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid)
 {
+	struct dev_context *devc = sdi->priv;
 	struct sr_usb_dev_inst *usb = sdi->conn;
 	libusb_device_handle *devhdl = usb->devhdl;
-	int ret;
+	int cmd, ret;
 
+	cmd = devc->dslogic ? DS_CMD_GET_REVID_VERSION : CMD_GET_REVID_VERSION;
 	ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
-		LIBUSB_ENDPOINT_IN, CMD_GET_REVID_VERSION, 0x0000, 0x0000,
-		revid, 1, 100);
+		LIBUSB_ENDPOINT_IN, cmd, 0x0000, 0x0000, revid, 1, USB_TIMEOUT);
 
 	if (ret < 0) {
 		sr_err("Unable to get REVID: %s.", libusb_error_name(ret));
@@ -87,21 +80,25 @@ static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid)
 
 SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
 {
-	struct dev_context *devc = sdi->priv;
-	struct sr_usb_dev_inst *usb = sdi->conn;
-	libusb_device_handle *devhdl = usb->devhdl;
-	uint64_t samplerate = devc->cur_samplerate;
-	gboolean samplewide = devc->sample_wide;
-	struct cmd_start_acquisition cmd = { 0 };
-	int delay = 0, ret;
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	uint64_t samplerate;
+	struct cmd_start_acquisition cmd;
+	int delay, ret;
+
+	devc = sdi->priv;
+	usb = sdi->conn;
+	samplerate = devc->cur_samplerate;
 
 	/* Compute the sample rate. */
-	if (samplewide && samplerate > MAX_16BIT_SAMPLE_RATE) {
+	if (devc->sample_wide && samplerate > MAX_16BIT_SAMPLE_RATE) {
 		sr_err("Unable to sample at %" PRIu64 "Hz "
 		       "when collecting 16-bit samples.", samplerate);
 		return SR_ERR;
 	}
 
+	delay = 0;
+	cmd.flags = cmd.sample_delay_h = cmd.sample_delay_l = 0;
 	if ((SR_MHZ(48) % samplerate) == 0) {
 		cmd.flags = CMD_START_FLAGS_CLK_48MHZ;
 		delay = SR_MHZ(48) / samplerate - 1;
@@ -114,7 +111,7 @@ SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
 		delay = SR_MHZ(30) / samplerate - 1;
 	}
 
-	sr_info("GPIF delay = %d, clocksource = %sMHz.", delay,
+	sr_dbg("GPIF delay = %d, clocksource = %sMHz.", delay,
 		(cmd.flags & CMD_START_FLAGS_CLK_48MHZ) ? "48" : "30");
 
 	if (delay <= 0 || delay > MAX_SAMPLE_DELAY) {
@@ -126,13 +123,13 @@ SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
 	cmd.sample_delay_l = delay & 0xff;
 
 	/* Select the sampling width. */
-	cmd.flags |= samplewide ? CMD_START_FLAGS_SAMPLE_16BIT :
+	cmd.flags |= devc->sample_wide ? CMD_START_FLAGS_SAMPLE_16BIT :
 		CMD_START_FLAGS_SAMPLE_8BIT;
 
 	/* Send the control message. */
-	ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+	ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
 			LIBUSB_ENDPOINT_OUT, CMD_START, 0x0000, 0x0000,
-			(unsigned char *)&cmd, sizeof(cmd), 100);
+			(unsigned char *)&cmd, sizeof(cmd), USB_TIMEOUT);
 	if (ret < 0) {
 		sr_err("Unable to send start command: %s.",
 		       libusb_error_name(ret));
@@ -145,10 +142,11 @@ SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
 /**
  * Check the USB configuration to determine if this is an fx2lafw device.
  *
- * @return TRUE if the device's configuration profile match fx2lafw
+ * @return TRUE if the device's configuration profile matches fx2lafw
  *         configuration, FALSE otherwise.
  */
-SR_PRIV gboolean fx2lafw_check_conf_profile(libusb_device *dev)
+SR_PRIV gboolean match_manuf_prod(libusb_device *dev, const char *manufacturer,
+		const char *product)
 {
 	struct libusb_device_descriptor des;
 	struct libusb_device_handle *hdl;
@@ -159,25 +157,23 @@ SR_PRIV gboolean fx2lafw_check_conf_profile(libusb_device *dev)
 	ret = FALSE;
 	while (!ret) {
 		/* Assume the FW has not been loaded, unless proven wrong. */
-		if (libusb_get_device_descriptor(dev, &des) != 0)
-			break;
+		libusb_get_device_descriptor(dev, &des);
 
 		if (libusb_open(dev, &hdl) != 0)
 			break;
 
 		if (libusb_get_string_descriptor_ascii(hdl,
-		    des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
+				des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
 			break;
-		if (strncmp((const char *)strdesc, "sigrok", 6))
+		if (strcmp((const char *)strdesc, manufacturer))
 			break;
 
 		if (libusb_get_string_descriptor_ascii(hdl,
 				des.iProduct, strdesc, sizeof(strdesc)) < 0)
 			break;
-		if (strncmp((const char *)strdesc, "fx2lafw", 7))
+		if (strcmp((const char *)strdesc, product))
 			break;
 
-		/* If we made it here, it must be an fx2lafw. */
 		ret = TRUE;
 	}
 	if (hdl)
@@ -194,10 +190,11 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
 	struct dev_context *devc;
 	struct drv_context *drvc;
 	struct version_info vi;
-	int ret, skip, i, device_count;
+	int ret, i, device_count;
 	uint8_t revid;
+	char connection_id[64];
 
-	drvc = di->priv;
+	drvc = di->context;
 	devc = sdi->priv;
 	usb = sdi->conn;
 
@@ -205,7 +202,6 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
 		/* Device is already in use. */
 		return SR_ERR;
 
-	skip = 0;
 	device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
 	if (device_count < 0) {
 		sr_err("Failed to get device list: %s.",
@@ -214,29 +210,19 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
 	}
 
 	for (i = 0; i < device_count; i++) {
-		if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
-			sr_err("Failed to get device descriptor: %s.",
-			       libusb_error_name(ret));
-			continue;
-		}
+		libusb_get_device_descriptor(devlist[i], &des);
 
 		if (des.idVendor != devc->profile->vid
 		    || des.idProduct != devc->profile->pid)
 			continue;
 
-		if (sdi->status == SR_ST_INITIALIZING) {
-			if (skip != sdi->index) {
-				/* Skip devices of this type that aren't the one we want. */
-				skip += 1;
-				continue;
-			}
-		} else if (sdi->status == SR_ST_INACTIVE) {
+		if ((sdi->status == SR_ST_INITIALIZING) ||
+				(sdi->status == SR_ST_INACTIVE)) {
 			/*
-			 * This device is fully enumerated, so we need to find
-			 * this device by vendor, product, bus and address.
+			 * Check device by its physical USB bus/port address.
 			 */
-			if (libusb_get_bus_number(devlist[i]) != usb->bus
-				|| libusb_get_device_address(devlist[i]) != usb->address)
+			usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+			if (strcmp(sdi->connection_id, connection_id))
 				/* This is not the one. */
 				continue;
 		}
@@ -254,6 +240,16 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
 			break;
 		}
 
+		if (libusb_has_capability(LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) {
+			if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) {
+				if ((ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE)) < 0) {
+					sr_err("Failed to detach kernel driver: %s.",
+						libusb_error_name(ret));
+					return SR_ERR;
+				}
+			}
+		}
+
 		ret = command_get_fw_version(usb->devhdl, &vi);
 		if (ret != SR_OK) {
 			sr_err("Failed to get firmware version.");
@@ -279,9 +275,9 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
 		}
 
 		sdi->status = SR_ST_ACTIVE;
-		sr_info("Opened device %d on %d.%d, "
+		sr_info("Opened device on %d.%d (logical) / %s (physical), "
 			"interface %d, firmware %d.%d.",
-			sdi->index, usb->bus, usb->address,
+			usb->bus, usb->address, connection_id,
 			USB_INTERFACE, vi.major, vi.minor);
 
 		sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.",
@@ -297,72 +293,18 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
 	return SR_OK;
 }
 
-SR_PRIV int fx2lafw_configure_channels(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	struct sr_channel *ch;
-	GSList *l;
-	int channel_bit, stage, i;
-	char *tc;
-
-	devc = sdi->priv;
-	for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
-		devc->trigger_mask[i] = 0;
-		devc->trigger_value[i] = 0;
-	}
-
-	stage = -1;
-	for (l = sdi->channels; l; l = l->next) {
-		ch = (struct sr_channel *)l->data;
-		if (ch->enabled == FALSE)
-			continue;
-
-		if (ch->index > 7)
-			devc->sample_wide = TRUE;
-
-		channel_bit = 1 << (ch->index);
-		if (!(ch->trigger))
-			continue;
-
-		stage = 0;
-		for (tc = ch->trigger; *tc; tc++) {
-			devc->trigger_mask[stage] |= channel_bit;
-			if (*tc == '1')
-				devc->trigger_value[stage] |= channel_bit;
-			stage++;
-			if (stage > NUM_TRIGGER_STAGES)
-				return SR_ERR;
-		}
-	}
-
-	if (stage == -1) {
-		/*
-		 * We didn't configure any triggers, make sure acquisition
-		 * doesn't wait for any.
-		 */
-		devc->trigger_fired = TRUE;
-	} else {
-		devc->trigger_fired = FALSE;
-		devc->trigger_stage = 0;
-	}
-
-	return SR_OK;
-}
-
 SR_PRIV struct dev_context *fx2lafw_dev_new(void)
 {
 	struct dev_context *devc;
 
-	if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		return NULL;
-	}
-
+	devc = g_malloc0(sizeof(struct dev_context));
 	devc->profile = NULL;
 	devc->fw_updated = 0;
 	devc->cur_samplerate = 0;
 	devc->limit_samples = 0;
+	devc->capture_ratio = 0;
 	devc->sample_wide = FALSE;
+	devc->stl = NULL;
 
 	return devc;
 }
@@ -379,27 +321,37 @@ SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc)
 	}
 }
 
-static void finish_acquisition(struct dev_context *devc)
+static void finish_acquisition(struct sr_dev_inst *sdi)
 {
 	struct sr_datafeed_packet packet;
+	struct dev_context *devc;
+
+	devc = sdi->priv;
 
 	/* Terminate session. */
 	packet.type = SR_DF_END;
-	sr_session_send(devc->cb_data, &packet);
+	sr_session_send(sdi, &packet);
 
 	/* Remove fds from polling. */
-	usb_source_remove(devc->ctx);
+	usb_source_remove(sdi->session, devc->ctx);
 
 	devc->num_transfers = 0;
 	g_free(devc->transfers);
+
+	if (devc->stl) {
+		soft_trigger_logic_free(devc->stl);
+		devc->stl = NULL;
+	}
 }
 
 static void free_transfer(struct libusb_transfer *transfer)
 {
+	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
 	unsigned int i;
 
-	devc = transfer->user_data;
+	sdi = transfer->user_data;
+	devc = sdi->priv;
 
 	g_free(transfer->buffer);
 	transfer->buffer = NULL;
@@ -414,7 +366,7 @@ static void free_transfer(struct libusb_transfer *transfer)
 
 	devc->submitted_transfers--;
 	if (devc->submitted_transfers == 0)
-		finish_acquisition(devc);
+		finish_acquisition(sdi);
 }
 
 static void resubmit_transfer(struct libusb_transfer *transfer)
@@ -424,24 +376,24 @@ static void resubmit_transfer(struct libusb_transfer *transfer)
 	if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS)
 		return;
 
+	sr_err("%s: %s", __func__, libusb_error_name(ret));
 	free_transfer(transfer);
-	/* TODO: Stop session? */
 
-	sr_err("%s: %s", __func__, libusb_error_name(ret));
 }
 
-SR_PRIV void fx2lafw_receive_transfer(struct libusb_transfer *transfer)
+SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transfer)
 {
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
 	gboolean packet_has_error = FALSE;
 	struct sr_datafeed_packet packet;
 	struct sr_datafeed_logic logic;
-	struct dev_context *devc;
-	unsigned int trigger_offset, num_samples;
-	int cur_sample_count, sample_width, i;
-	uint8_t *cur_buf;
-	uint16_t cur_sample;
+	unsigned int num_samples;
+	int trigger_offset, cur_sample_count, unitsize;
+	int pre_trigger_samples;
 
-	devc = transfer->user_data;
+	sdi = transfer->user_data;
+	devc = sdi->priv;
 
 	/*
 	 * If acquisition has already ended, just free any queued up
@@ -452,13 +404,12 @@ SR_PRIV void fx2lafw_receive_transfer(struct libusb_transfer *transfer)
 		return;
 	}
 
-	sr_info("receive_transfer(): status %d received %d bytes.",
-		transfer->status, transfer->actual_length);
+	sr_dbg("receive_transfer(): status %s received %d bytes.",
+		libusb_error_name(transfer->status), transfer->actual_length);
 
 	/* Save incoming transfer before reusing the transfer struct. */
-	cur_buf = transfer->buffer;
-	sample_width = devc->sample_wide ? 2 : 1;
-	cur_sample_count = transfer->actual_length / sample_width;
+	unitsize = devc->sample_wide ? 2 : 1;
+	cur_sample_count = transfer->actual_length / unitsize;
 
 	switch (transfer->status) {
 	case LIBUSB_TRANSFER_NO_DEVICE:
@@ -490,88 +441,47 @@ SR_PRIV void fx2lafw_receive_transfer(struct libusb_transfer *transfer)
 		devc->empty_transfer_count = 0;
 	}
 
-	trigger_offset = 0;
-	if (!devc->trigger_fired) {
-		for (i = 0; i < cur_sample_count; i++) {
-			cur_sample = devc->sample_wide ?
-				*((uint16_t *)cur_buf + i) :
-				*((uint8_t *)cur_buf + i);
-
-			if ((cur_sample & devc->trigger_mask[devc->trigger_stage]) ==
-				devc->trigger_value[devc->trigger_stage]) {
-				/* Match on this trigger stage. */
-				devc->trigger_buffer[devc->trigger_stage] = cur_sample;
-				devc->trigger_stage++;
-
-				if (devc->trigger_stage == NUM_TRIGGER_STAGES ||
-					devc->trigger_mask[devc->trigger_stage] == 0) {
-					/* Match on all trigger stages, we're done. */
-					trigger_offset = i;
-
-					/*
-					 * TODO: Send pre-trigger buffer to session bus.
-					 * Tell the frontend we hit the trigger here.
-					 */
-					packet.type = SR_DF_TRIGGER;
-					packet.payload = NULL;
-					sr_session_send(devc->cb_data, &packet);
-
-					/*
-					 * Send the samples that triggered it,
-					 * since we're skipping past them.
-					 */
-					packet.type = SR_DF_LOGIC;
-					packet.payload = &logic;
-					num_samples = cur_sample_count - trigger_offset;
-					if (devc->limit_samples &&
-							num_samples > devc->limit_samples - devc->sent_samples)
-						num_samples = devc->limit_samples - devc->sent_samples;
-					logic.length = num_samples * sample_width;
-					logic.unitsize = sample_width;
-					logic.data = cur_buf + trigger_offset * sample_width;
-					sr_session_send(devc->cb_data, &packet);
-					devc->sent_samples += num_samples;
-
-					devc->trigger_fired = TRUE;
-					break;
-				}
-			} else if (devc->trigger_stage > 0) {
-				/*
-				 * We had a match before, but not in the next sample. However, we may
-				 * have a match on this stage in the next bit -- trigger on 0001 will
-				 * fail on seeing 00001, so we need to go back to stage 0 -- but at
-				 * the next sample from the one that matched originally, which the
-				 * counter increment at the end of the loop takes care of.
-				 */
-				i -= devc->trigger_stage;
-				if (i < -1)
-					i = -1; /* Oops, went back past this buffer. */
-				/* Reset trigger stage. */
-				devc->trigger_stage = 0;
-			}
+	if (devc->trigger_fired) {
+		if (!devc->limit_samples || devc->sent_samples < devc->limit_samples) {
+			/* Send the incoming transfer to the session bus. */
+			packet.type = SR_DF_LOGIC;
+			packet.payload = &logic;
+			if (devc->limit_samples && devc->sent_samples + cur_sample_count > devc->limit_samples)
+				num_samples = devc->limit_samples - devc->sent_samples;
+			else
+				num_samples = cur_sample_count;
+			logic.length = num_samples * unitsize;
+			logic.unitsize = unitsize;
+			logic.data = transfer->buffer;
+			sr_session_send(devc->cb_data, &packet);
+			devc->sent_samples += num_samples;
+		}
+	} else {
+		trigger_offset = soft_trigger_logic_check(devc->stl,
+			transfer->buffer, transfer->actual_length, &pre_trigger_samples);
+		if (trigger_offset > -1) {
+			devc->sent_samples += pre_trigger_samples;
+			packet.type = SR_DF_LOGIC;
+			packet.payload = &logic;
+			num_samples = cur_sample_count - trigger_offset;
+			if (devc->limit_samples &&
+					num_samples > devc->limit_samples - devc->sent_samples)
+				num_samples = devc->limit_samples - devc->sent_samples;
+			logic.length = num_samples * unitsize;
+			logic.unitsize = unitsize;
+			logic.data = transfer->buffer + trigger_offset * unitsize;
+			sr_session_send(devc->cb_data, &packet);
+			devc->sent_samples += num_samples;
+
+			devc->trigger_fired = TRUE;
 		}
-	} else if (devc->sent_samples < devc->limit_samples) {
-		/* Send the incoming transfer to the session bus. */
-		packet.type = SR_DF_LOGIC;
-		packet.payload = &logic;
-		if (devc->sent_samples + cur_sample_count > devc->limit_samples)
-			num_samples = devc->limit_samples - devc->sent_samples;
-		else
-			num_samples = cur_sample_count;
-		logic.length = num_samples * sample_width;
-		logic.unitsize = sample_width;
-		logic.data = cur_buf;
-		sr_session_send(devc->cb_data, &packet);
-		devc->sent_samples += cur_sample_count;
 	}
 
 	if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) {
 		fx2lafw_abort_acquisition(devc);
 		free_transfer(transfer);
-		return;
-	}
-
-	resubmit_transfer(transfer);
+	} else
+		resubmit_transfer(transfer);
 }
 
 static unsigned int to_bytes_per_ms(unsigned int samplerate)
diff --git a/hardware/fx2lafw/protocol.h b/src/hardware/fx2lafw/protocol.h
similarity index 72%
rename from hardware/fx2lafw/protocol.h
rename to src/hardware/fx2lafw/protocol.h
index dfb6ef8..373770d 100644
--- a/hardware/fx2lafw/protocol.h
+++ b/src/hardware/fx2lafw/protocol.h
@@ -26,7 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "fx2lafw"
@@ -34,7 +34,6 @@
 #define USB_INTERFACE		0
 #define USB_CONFIGURATION	1
 #define NUM_TRIGGER_STAGES	4
-#define TRIGGER_TYPE 		"01"
 
 #define MAX_RENUM_DELAY_MS	3000
 #define NUM_SIMUL_TRANSFERS	32
@@ -52,6 +51,24 @@
 
 #define DEV_CAPS_16BIT		(1 << DEV_CAPS_16BIT_POS)
 
+#define DSLOGIC_FPGA_FIRMWARE "dreamsourcelab-dslogic-fpga.fw"
+#define DSCOPE_FPGA_FIRMWARE "dreamsourcelab-dscope-fpga.fw"
+#define DSLOGIC_PRO_FPGA_FIRMWARE "dreamsourcelab-dslogic-pro-fpga.fw"
+
+/* Protocol commands */
+#define CMD_GET_FW_VERSION		0xb0
+#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)
+
 struct fx2lafw_profile {
 	uint16_t vid;
 	uint16_t pid;
@@ -78,18 +95,20 @@ struct dev_context {
 	 */
 	int64_t fw_updated;
 
+	/* Supported samplerates */
+	const uint64_t *samplerates;
+	int num_samplerates;
+
 	/* Device/capture settings */
 	uint64_t cur_samplerate;
 	uint64_t limit_samples;
+	uint64_t capture_ratio;
 
 	/* Operational settings */
 	gboolean trigger_fired;
 	gboolean acq_aborted;
 	gboolean sample_wide;
-	uint16_t trigger_mask[NUM_TRIGGER_STAGES];
-	uint16_t trigger_value[NUM_TRIGGER_STAGES];
-	unsigned int trigger_stage;
-	uint16_t trigger_buffer[NUM_TRIGGER_STAGES];
+	struct soft_trigger_logic *stl;
 
 	unsigned int sent_samples;
 	int submitted_transfers;
@@ -99,15 +118,20 @@ struct dev_context {
 	unsigned int num_transfers;
 	struct libusb_transfer **transfers;
 	struct sr_context *ctx;
+
+	/* Is this a DSLogic? */
+	gboolean dslogic;
+	uint16_t dslogic_mode;
+	int dslogic_external_clock;
 };
 
 SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV gboolean fx2lafw_check_conf_profile(libusb_device *dev);
+SR_PRIV gboolean match_manuf_prod(libusb_device *dev, const char *manufacturer,
+		const char *product);
 SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di);
-SR_PRIV int fx2lafw_configure_channels(const struct sr_dev_inst *sdi);
 SR_PRIV struct dev_context *fx2lafw_dev_new(void);
 SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc);
-SR_PRIV void fx2lafw_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transfer);
 SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc);
 SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc);
 SR_PRIV unsigned int fx2lafw_get_timeout(struct dev_context *devc);
diff --git a/hardware/gmc-mh-1x-2x/api.c b/src/hardware/gmc-mh-1x-2x/api.c
similarity index 73%
rename from hardware/gmc-mh-1x-2x/api.c
rename to src/hardware/gmc-mh-1x-2x/api.c
index 3c0a7aa..85077df 100644
--- a/hardware/gmc-mh-1x-2x/api.c
+++ b/src/hardware/gmc-mh-1x-2x/api.c
@@ -22,6 +22,7 @@
  *  @internal
  */
 
+#include <config.h>
 #include <string.h>
 #include "protocol.h"
 
@@ -34,31 +35,30 @@
 SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info;
 SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info;
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
 /** Hardware capabilities for Metrahit 1x/2x devices in send mode. */
-static const int32_t hwcaps_sm[] = {
+static const uint32_t devopts_sm[] = {
 	SR_CONF_MULTIMETER,
 	SR_CONF_THERMOMETER,    /**< All GMC 1x/2x multimeters seem to support this */
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
 };
 
 /** Hardware capabilities for Metrahit 2x devices in bidirectional Mode. */
-static const int32_t hwcaps_bd[] = {
+static const uint32_t devopts_bd[] = {
 	SR_CONF_MULTIMETER,
 	SR_CONF_THERMOMETER,    /**< All GMC 1x/2x multimeters seem to support this */
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
 	SR_CONF_CONTINUOUS,
-	SR_CONF_POWER_OFF,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_POWER_OFF | SR_CONF_GET | SR_CONF_SET,
 };
 
-
 /* TODO:
  * - For the 29S SR_CONF_ENERGYMETER, too.
  * - SR_CONF_PATTERN_MODE for some 2x devices
@@ -66,16 +66,9 @@ static const int32_t hwcaps_bd[] = {
  * Need to implement device-specific lists.
  */
 
-/** Init driver gmc_mh_1x_2x_rs232. */
-static int init_1x_2x_rs232(struct sr_context *sr_ctx)
-{
-	return std_init(sr_ctx, &gmc_mh_1x_2x_rs232_driver_info, LOG_PREFIX);
-}
-
-/** Init driver gmc_mh_2x_bd232. */
-static int init_2x_bd232(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
-	return std_init(sr_ctx, &gmc_mh_2x_bd232_driver_info, LOG_PREFIX);
+	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
 /**
@@ -90,7 +83,7 @@ static int read_byte(struct sr_serial_dev_inst *serial, gint64 timeout)
 	int rc = 0;
 
 	for (;;) {
-		rc = serial_read(serial, &result, 1);
+		rc = serial_read_nonblocking(serial, &result, 1);
 		if (rc == 1) {
 			sr_spew("read: 0x%02x/%d", result, result);
 			return result;
@@ -116,7 +109,7 @@ static enum model scan_model_sm(struct sr_serial_dev_inst *serial)
 	gint64 timeout_us;
 
 	model = METRAHIT_NONE;
-	timeout_us = g_get_monotonic_time() + 1 * 1000 * 1000;
+	timeout_us = g_get_monotonic_time() + (1 * 1000 * 1000);
 
 	/*
 	 * Try to find message consisting of device code and several
@@ -156,13 +149,12 @@ static enum model scan_model_sm(struct sr_serial_dev_inst *serial)
  * on configuration and measurement mode the intervals can be much larger and
  * then the detection might not work.
  */
-static GSList *scan_1x_2x_rs232(GSList *options)
+static GSList *scan_1x_2x_rs232(struct sr_dev_driver *di, GSList *options)
 {
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_config *src;
-	struct sr_channel *ch;
 	struct sr_serial_dev_inst *serial;
 	GSList *l, *devices;
 	const char *conn, *serialcomm;
@@ -170,10 +162,9 @@ static GSList *scan_1x_2x_rs232(GSList *options)
 	gboolean serialcomm_given;
 
 	devices = NULL;
-	drvc = (&gmc_mh_1x_2x_rs232_driver_info)->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 	conn = serialcomm = NULL;
-	model = METRAHIT_NONE;
 	serialcomm_given = FALSE;
 
 	sr_spew("scan_1x_2x_rs232() called!");
@@ -195,10 +186,9 @@ static GSList *scan_1x_2x_rs232(GSList *options)
 	if (!serialcomm)
 		serialcomm = SERIALCOMM_2X_RS232;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK) {
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK) {
 		sr_serial_dev_inst_free(serial);
 		return NULL;
 	}
@@ -223,26 +213,21 @@ static GSList *scan_1x_2x_rs232(GSList *options)
 
 	if (model != METRAHIT_NONE) {
 		sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(model));
-		if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC,
-				gmc_model_str(model), NULL)))
-			return NULL;
-		if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-			sr_err("Device context malloc failed.");
-			return NULL;
-		}
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup(VENDOR_GMC);
+		sdi->model = g_strdup(gmc_model_str(model));
+		devc = g_malloc0(sizeof(struct dev_context));
 		devc->model = model;
 		devc->limit_samples = 0;
 		devc->limit_msec = 0;
 		devc->num_samples = 0;
 		devc->elapsed_msec = g_timer_new();
 		devc->settings_ok = FALSE;
-
 		sdi->conn = serial;
 		sdi->priv = devc;
-		sdi->driver = &gmc_mh_1x_2x_rs232_driver_info;
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		sdi->driver = di;
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 		drvc->instances = g_slist_append(drvc->instances, sdi);
 		devices = g_slist_append(devices, sdi);
 	}
@@ -250,16 +235,16 @@ static GSList *scan_1x_2x_rs232(GSList *options)
 	return devices;
 }
 
-/** Scan for Metrahit 2x in a bidirectional mode using Gossen Metrawatt 'BD 232' interface.
- *
+/**
+ * Scan for Metrahit 2x in a bidirectional mode using Gossen Metrawatt
+ * 'BD 232' interface.
  */
-static GSList *scan_2x_bd232(GSList *options)
+static GSList *scan_2x_bd232(struct sr_dev_driver *di, GSList *options)
 {
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_config *src;
-	struct sr_channel *ch;
 	struct sr_serial_dev_inst *serial;
 	GSList *l, *devices;
 	const char *conn, *serialcomm;
@@ -271,7 +256,7 @@ static GSList *scan_2x_bd232(GSList *options)
 	conn = serialcomm = NULL;
 	devices = NULL;
 
-	drvc = (&gmc_mh_2x_bd232_driver_info)->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	sr_spew("scan_2x_bd232() called!");
@@ -292,20 +277,16 @@ static GSList *scan_2x_bd232(GSList *options)
 	if (!serialcomm)
 		serialcomm = SERIALCOMM_2X;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		goto exit_err;
 
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		goto exit_err;
-	}
-
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC, NULL, NULL)))
-		goto exit_err;
+	devc = g_malloc0(sizeof(struct dev_context));
 
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(VENDOR_GMC);
 	sdi->priv = devc;
 
 	/* Send message 03 "Query multimeter version and status" */
@@ -315,18 +296,18 @@ static GSList *scan_2x_bd232(GSList *options)
 		goto exit_err;
 
 	/* Wait for reply from device(s) for up to 2s. */
-	timeout_us = g_get_monotonic_time() + 2*1000*1000;
+	timeout_us = g_get_monotonic_time() + (2 * 1000 * 1000);
 
 	while (timeout_us > g_get_monotonic_time()) {
 		/* Receive reply (14 bytes) */
 		devc->buflen = 0;
-		for (cnt = 0; cnt < 14; cnt++) {
+		for (cnt = 0; cnt < GMC_REPLY_SIZE; cnt++) {
 			byte = read_byte(serial, timeout_us);
 			if (byte != -1)
 				devc->buf[devc->buflen++] = (byte & MASK_6BITS);
 		}
 
-		if (devc->buflen != 14)
+		if (devc->buflen != GMC_REPLY_SIZE)
 			continue;
 
 		devc->addr = devc->buf[0];
@@ -335,27 +316,19 @@ static GSList *scan_2x_bd232(GSList *options)
 
 		if (devc->model != METRAHIT_NONE) {
 			sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(devc->model));
-
 			devc->elapsed_msec = g_timer_new();
-
 			sdi->model = g_strdup(gmc_model_str(devc->model));
 			sdi->version = g_strdup_printf("Firmware %d.%d", devc->fw_ver_maj, devc->fw_ver_min);
 			sdi->conn = serial;
 			sdi->priv = devc;
-			sdi->driver = &gmc_mh_2x_bd232_driver_info;
-			if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-				goto exit_err;
-			sdi->channels = g_slist_append(sdi->channels, ch);
+			sdi->driver = di;
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 			drvc->instances = g_slist_append(drvc->instances, sdi);
 			devices = g_slist_append(devices, sdi);
-
-			if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-				sr_err("Device context malloc failed.");
-				goto exit_err;
-			}
-
-			if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR_GMC, NULL, NULL)))
-				goto exit_err;
+			devc = g_malloc0(sizeof(struct dev_context));
+			sdi = g_malloc0(sizeof(struct sr_dev_inst));
+			sdi->status = SR_ST_INACTIVE;
+			sdi->vendor = g_strdup(VENDOR_GMC);
 		}
 	};
 
@@ -372,25 +345,16 @@ exit_err:
 
 	if (serial)
 		sr_serial_dev_inst_free(serial);
-	if (devc)
-		g_free(devc);
+	g_free(devc);
 	if (sdi)
 		sr_dev_inst_free(sdi);
 
 	return NULL;
 }
 
-/** Driver device list function */
-static GSList *dev_list_1x_2x_rs232(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(gmc_mh_1x_2x_rs232_driver_info.priv))->instances;
-}
-
-/** Driver device list function */
-static GSList *dev_list_2x_bd232(void)
-{
-	return ((struct drv_context *)(gmc_mh_2x_bd232_driver_info.priv))
-			->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
@@ -411,27 +375,19 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup_sm_rs232(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
-	return std_dev_clear(&gmc_mh_1x_2x_rs232_driver_info, NULL);
+	return std_dev_clear(di, NULL);
 }
 
-static int cleanup_2x_bd232(void)
-{
-	return std_dev_clear(&gmc_mh_2x_bd232_driver_info, NULL);
-}
-
-/** Get value of configuration item */
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
-		      const struct sr_channel_group *cg)
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
 {
 	int ret;
 	struct dev_context *devc;
 
 	(void)cg;
 
-	ret = SR_OK;
-
 	if (!sdi || !(devc = sdi->priv))
 		return SR_ERR_ARG;
 
@@ -453,17 +409,17 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-/** Implementation of config_list, auxiliary function for common parts, */
-static int config_list_common(int key, GVariant **data, const struct sr_dev_inst *sdi,
-			      const struct sr_channel_group *cg)
+/** Implementation of config_list, auxiliary function for common parts. */
+static int config_list_common(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
 {
 	(void)sdi;
 	(void)cg;
 
 	switch (key) {
 	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-						  hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -473,13 +429,13 @@ static int config_list_common(int key, GVariant **data, const struct sr_dev_inst
 }
 
 /** Implementation of config_list for Metrahit 1x/2x send mode */
-static int config_list_sm(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list_sm(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 			  const struct sr_channel_group *cg)
 {
 	switch (key) {
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-						  hwcaps_sm, ARRAY_SIZE(hwcaps_sm), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts_sm, ARRAY_SIZE(devopts_sm), sizeof(uint32_t));
 		break;
 	default:
 		return config_list_common(key, data, sdi, cg);
@@ -489,13 +445,13 @@ static int config_list_sm(int key, GVariant **data, const struct sr_dev_inst *sd
 }
 
 /** Implementation of config_list for Metrahit 2x bidirectional mode */
-static int config_list_bd(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list_bd(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 			  const struct sr_channel_group *cg)
 {
 	switch (key) {
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-						  hwcaps_bd, ARRAY_SIZE(hwcaps_bd), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts_bd, ARRAY_SIZE(devopts_bd), sizeof(uint32_t));
 		break;
 	default:
 		return config_list_common(key, data, sdi, cg);
@@ -529,8 +485,8 @@ static int dev_acquisition_start_1x_2x_rs232(const struct sr_dev_inst *sdi,
 
 	/* Poll every 40ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 40, gmc_mh_1x_2x_receive_data,
-			  (void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 40,
+			gmc_mh_1x_2x_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -560,8 +516,8 @@ static int dev_acquisition_start_2x_bd232(const struct sr_dev_inst *sdi,
 
 	/* Poll every 40ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 40, gmc_mh_2x_receive_data,
-			  (void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 40,
+			gmc_mh_2x_receive_data, (void *)sdi);
 
 	/* Send start message */
 	return req_meas14(sdi);
@@ -583,10 +539,10 @@ SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info = {
 	.name = "gmc-mh-1x-2x-rs232",
 	.longname = "Gossen Metrawatt Metrahit 1x/2x, RS232 interface",
 	.api_version = 1,
-	.init = init_1x_2x_rs232,
-	.cleanup = cleanup_sm_rs232,
+	.init = init,
+	.cleanup = cleanup,
 	.scan = scan_1x_2x_rs232,
-	.dev_list = dev_list_1x_2x_rs232,
+	.dev_list = dev_list,
 	.dev_clear = NULL,
 	.config_get = config_get,
 	.config_set = config_set,
@@ -595,17 +551,17 @@ SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start_1x_2x_rs232,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
 
 SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info = {
 	.name = "gmc-mh-2x-bd232",
 	.longname = "Gossen Metrawatt Metrahit 2x, BD232/SI232-II interface",
 	.api_version = 1,
-	.init = init_2x_bd232,
-	.cleanup = cleanup_2x_bd232,
+	.init = init,
+	.cleanup = cleanup,
 	.scan = scan_2x_bd232,
-	.dev_list = dev_list_2x_bd232,
+	.dev_list = dev_list,
 	.dev_clear = NULL,
 	.config_get = config_get,
 	.config_set = config_set,
@@ -614,5 +570,5 @@ SR_PRIV struct sr_dev_driver gmc_mh_2x_bd232_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start_2x_bd232,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/gmc-mh-1x-2x/protocol.c b/src/hardware/gmc-mh-1x-2x/protocol.c
similarity index 90%
rename from hardware/gmc-mh-1x-2x/protocol.c
rename to src/hardware/gmc-mh-1x-2x/protocol.c
index fc086a7..7851f0e 100644
--- a/hardware/gmc-mh-1x-2x/protocol.c
+++ b/src/hardware/gmc-mh-1x-2x/protocol.c
@@ -22,12 +22,13 @@
  *  @internal
  */
 
+#include <config.h>
 #include <math.h>
 #include <string.h>
 #include "protocol.h"
 
 /* Internal Headers */
-static guchar calc_chksum_14(guchar* dta);
+static guchar calc_chksum_14(guchar *dta);
 static int chk_msg14(struct sr_dev_inst *sdi);
 
 /** Set or clear flags in devc->mqflags. */
@@ -257,7 +258,7 @@ static void decode_ctmv_18(uint8_t ctmv, struct dev_context *devc)
 /**
  * Decode range/sign/acdc byte special chars, Metrahit 18.
  *
- * @param[in] rs Rance/sign byte.
+ * @param[in] rs Range/sign byte.
  */
 static void decode_rs_18(uint8_t rs, struct dev_context *devc)
 {
@@ -282,8 +283,6 @@ static void decode_rs_18(uint8_t rs, struct dev_context *devc)
 			 * identify relative mode.
 			 */
 		}
-		else if (devc->vmains_29S)
-			devc->scale *= pow(10.0, range - 2);
 		else
 			devc->scale *= pow(10.0, range - 5);
 		break;
@@ -346,8 +345,8 @@ static void decode_spc_18(uint8_t spc, struct dev_context *devc)
  */
 static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
 {
-	if ((ctmv > 0x1c) || (!devc)) {
-		sr_err("decode_ctmv_2x(%d): invalid param(s)!", ctmv);
+	if ((ctmv > 0x20) || (!devc)) {
+		sr_err("decode_ctmv_2x(0x%x): invalid param(s)!", ctmv);
 		return;
 	}
 
@@ -373,6 +372,7 @@ static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
 	case 0x04: /* 00100 mA DC */
 	case 0x05: /* 00101 mA AC+DC */
 		devc->scale1000 = -1;
+		/* Fall through! */
 	case 0x06: /* 00110 A DC */
 	case 0x07: /* 00111 A AC+DC */
 		devc->mq = SR_MQ_CURRENT;
@@ -409,11 +409,14 @@ static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
 			devc->mqflags |= SR_MQFLAG_DC;
 		break;
 	case 0x0d: /* 01101 W on power, mA range (29S only) */
-		devc->scale *= 0.001;
+		devc->scale *= 0.1;
 		/* Fall through! */
 	case 0x0e: /* 01110 W on power, A range (29S only) */
+		devc->scale *= 0.1;
+		devc->scale1000 = -1;
 		devc->mq = SR_MQ_POWER;
 		devc->unit = SR_UNIT_WATT;
+		devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
 		break;
 	case 0x0f: /* 01111 Diode */
 	case 0x10: /* 10000 Diode with buzzer (actually cont. with voltage) */
@@ -437,7 +440,8 @@ static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
 		devc->unit = SR_UNIT_CELSIUS;
 		/* This can be Fahrenheit. That is detected by range=4 later. */
 		break;
-	/* 0x13 10011, 0x14 10100 unsed */
+	/* 0x13 10011 unused */
+	/* 0x14 10100 unused */
 	case 0x15: /* 10101 Press (29S only) */
 		/* TODO: What does that mean? Possibly phase shift?
 		   Then we need a unit/flag for it. */
@@ -452,8 +456,7 @@ static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
 	case 0x17: /* 10111 TRMS V on mains (29S only) */
 		devc->mq = SR_MQ_VOLTAGE;
 		devc->unit = SR_UNIT_VOLT;
-		devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_RMS);
-		devc->vmains_29S = TRUE;
+		devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
 		break;
 	case 0x18: /* 11000 Counter (zero crossings of a signal) */
 		devc->mq = SR_MQ_VOLTAGE;
@@ -468,11 +471,11 @@ static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
 		if (ctmv <= 0x19)
 			devc->mqflags |= SR_MQFLAG_DC;
 		break;
-	case 0x1b: /* 11011 pulse on mains (29S only) */
-		/* TODO: No unit or flags for this yet! */
-		devc->mq = SR_MQ_VOLTAGE;
-		devc->unit = SR_UNIT_UNITLESS;
-		devc->mqflags |= SR_MQFLAG_AC;
+	case 0x1b: /* 11011 Milliamperes in power mode (29S only); error in docs, "pulse on mains" */
+		devc->mq = SR_MQ_CURRENT;
+		devc->unit = SR_UNIT_AMPERE;
+		devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
+		devc->scale1000 = -1;
 		break;
 	case 0x1c: /* 11100 dropout on mains (29S only) */
 		/* TODO: No unit or flags for this yet! */
@@ -480,14 +483,20 @@ static void decode_ctmv_2x(uint8_t ctmv, struct dev_context *devc)
 		devc->unit = SR_UNIT_UNITLESS;
 		devc->mqflags |= SR_MQFLAG_AC;
 		break;
-	case 0x1f: /* 11111 Undocumented: 25S in stopwatch mode.
+	case 0x1d: /* 11101 Voltage in power mode (29S); undocumented! */
+		devc->mq = SR_MQ_VOLTAGE;
+		devc->unit = SR_UNIT_VOLT;
+		devc->mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
+		break;
+	/* 0x1e: 11110 Undocumented */
+	case 0x1f: /* 11111 25S in stopwatch mode; undocumented!
 			The value is voltage, not time, so treat it such. */
 		devc->mq = SR_MQ_VOLTAGE;
 		devc->unit = SR_UNIT_VOLT;
 		devc->mqflags |= SR_MQFLAG_DC;
 		break;
-	case 0x20: /* 100000 Undocumented: 25S in event count mode.
-		Value is 0 anyway. */
+	case 0x20: /* 100000 25S in event count mode; undocumented!
+			Value is 0 anyway. */
 		devc->mq = SR_MQ_VOLTAGE;
 		devc->unit = SR_UNIT_UNITLESS;
 		break;
@@ -517,8 +526,6 @@ static void decode_rs_2x(uint8_t rs, struct dev_context *devc)
 	case SR_MQ_VOLTAGE:
 		if (devc->unit == SR_UNIT_DECIBEL_VOLT)
 			devc->scale *= pow(10.0, -3);
-		else if (devc->vmains_29S)
-			devc->scale *= pow(10.0, range - 2);
 		else
 			devc->scale *= pow(10.0, range - 6);
 		break;
@@ -561,23 +568,28 @@ static void decode_rs_2x_TR2(uint8_t rs, struct dev_context *devc)
 	switch (devc->mq) {
 	case SR_MQ_CURRENT:
 		if (devc->scale1000 == -1) /* mA */
-			switch(range) {
-			case 0: case 1:	/* 100, 300 µA */
+			switch (range) {
+			case 0:
+			case 1:	/* 100, 300 µA */
 				devc->scale *= pow(10.0, -6);
 				break;
-			case 2: case 3:	/* 1, 3 mA */
+			case 2:
+			case 3:	/* 1, 3 mA */
 				devc->scale *= pow(10.0, -5);
 				break;
-			case 4: case 5:	/* 10, 30 mA */
+			case 4:
+			case 5:	/* 10, 30 mA */
 				devc->scale *= pow(10.0, -4);
 				break;
-			case 6: case 7:	/* 100, 300 mA */
+			case 6:
+			case 7:	/* 100, 300 mA */
 				devc->scale *= pow(10.0, -3);
 				break;
 			}
 		else /* A */
-			switch(range) {
-			case 0: case 1:	/* 1, 3 A */
+			switch (range) {
+			case 0:
+			case 1:	/* 1, 3 A */
 				devc->scale *= pow(10.0, -5);
 				break;
 			case 2: /* 10 A */
@@ -596,7 +608,6 @@ static void decode_rs_2x_TR2(uint8_t rs, struct dev_context *devc)
 		devc->scale *= -1.0;
 }
 
-
 /**
  * Decode special chars (Metrahit 2x).
  *
@@ -634,7 +645,6 @@ static void clean_ctmv_rs_v(struct dev_context *devc)
 	devc->unit = 0;
 	devc->mqflags = 0;
 	devc->scale1000 = 0;
-	devc->vmains_29S = FALSE;
 	clean_rs_v(devc);
 }
 
@@ -642,12 +652,12 @@ static void clean_ctmv_rs_v(struct dev_context *devc)
 static void send_value(struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct sr_datafeed_packet packet;
 
 	devc = sdi->priv;
 
-	memset(&analog, 0, sizeof(analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 	analog.channels = sdi->channels;
 	analog.num_samples = 1;
 	analog.mq = devc->mq;
@@ -655,8 +665,8 @@ static void send_value(struct sr_dev_inst *sdi)
 	analog.mqflags = devc->mqflags;
 	analog.data = &devc->value;
 
-	memset(&packet, 0, sizeof(packet));
-	packet.type = SR_DF_ANALOG;
+	memset(&packet, 0, sizeof(struct sr_datafeed_packet));
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 
@@ -847,7 +857,7 @@ static void process_msg_inf_13(struct sr_dev_inst *sdi)
 		devc->value += pow(10.0, cnt) * dgt;
 	}
 	sr_spew("process_msg_inf_13() value=%f scale=%f scale1000=%d mq=%d "
-		"unit=%d mqflags=0x%02llx", devc->value, devc->scale,
+		"unit=%d mqflags=0x%02" PRIx64, devc->value, devc->scale,
 		devc->scale1000, devc->mq, devc->unit, devc->mqflags);
 	if (devc->value != NAN)
 		devc->value *= devc->scale * pow(1000.0, devc->scale1000);
@@ -863,7 +873,7 @@ static void process_msg_inf_13(struct sr_dev_inst *sdi)
  *  @param buf Pointer to array of 14 data bytes.
  *  @param[in] raw Write only data bytes, no interpretation.
  */
-void dump_msg14(guchar* buf, gboolean raw)
+void dump_msg14(guchar *buf, gboolean raw)
 {
 	if (!buf)
 		return;
@@ -887,11 +897,11 @@ void dump_msg14(guchar* buf, gboolean raw)
  *  @param[in] dta Pointer to array of 13 data bytes.
  *  @return Checksum.
  */
-static guchar calc_chksum_14(guchar* dta)
+static guchar calc_chksum_14(guchar *dta)
 {
 	guchar cnt, chs;
 
-	for (chs = 0, cnt = 0; cnt < 13; cnt++)
+	for (chs = 0, cnt = 0; cnt < (GMC_REPLY_SIZE - 1); cnt++)
 		chs += dta[cnt];
 
 	return (64 - chs) & MASK_6BITS;
@@ -928,7 +938,6 @@ static int chk_msg14(struct sr_dev_inst *sdi)
 	}
 
 	if (devc->buf[1] == 0) { /* Error msg from device! */
-		retc = SR_ERR_ARG;
 		switch (devc->buf[2]) {
 		case 1: /* Not used */
 			sr_err("Device: Illegal error code!");
@@ -993,7 +1002,7 @@ SR_PRIV int process_msg14(struct sr_dev_inst *sdi)
 		sr_spew("Cmd %d unimplemented!", devc->buf[3]);
 		break;
 	case 3: /* Read firmware version and status */
-		sr_spew("Cmd 3, Read firmware and status", devc->buf[3]);
+		sr_spew("Cmd 3, Read firmware and status");
 		switch (devc->cmd_idx) {
 		case 0:
 			devc->fw_ver_maj = devc->buf[5];
@@ -1064,7 +1073,7 @@ SR_PRIV int process_msg14(struct sr_dev_inst *sdi)
 			devc->value += pow(10.0, cnt) * dgt;
 		}
 		sr_spew("process_msg14() value=%f scale=%f scale1000=%d mq=%d "
-			"unit=%d mqflags=0x%02llx", devc->value, devc->scale,
+			"unit=%d mqflags=0x%02" PRIx64, devc->value, devc->scale,
 			devc->scale1000, devc->mq, devc->unit, devc->mqflags);
 		if (devc->value != NAN)
 			devc->value *= devc->scale * pow(1000.0, devc->scale1000);
@@ -1102,7 +1111,7 @@ SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data)
 
 	if (revents == G_IO_IN) { /* Serial data arrived. */
 		while (GMC_BUFSIZE - devc->buflen - 1 > 0) {
-			len = serial_read(serial, devc->buf + devc->buflen, 1);
+			len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
 			if (len < 1)
 				break;
 			buf = *(devc->buf + devc->buflen);
@@ -1196,7 +1205,7 @@ SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data)
 
 	if (revents == G_IO_IN) { /* Serial data arrived. */
 		while (GMC_BUFSIZE - devc->buflen - 1 > 0) {
-			len = serial_read(serial, devc->buf + devc->buflen, 1);
+			len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
 			if (len < 1)
 				break;
 			buf = *(devc->buf + devc->buflen);
@@ -1227,7 +1236,7 @@ SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data)
 	if (sdi->status == SR_ST_ACTIVE) {
 		if (devc->response_pending) {
 			gint64 elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
-			if (elapsed_us > 1*1000*1000) /* Timeout! */
+			if (elapsed_us > (1 * 1000 * 1000)) /* Timeout! */
 				devc->response_pending = FALSE;
 		}
 		if (!devc->response_pending) {
@@ -1252,9 +1261,9 @@ SR_PRIV int gmc_mh_2x_receive_data(int fd, int revents, void *cb_data)
  *  @param[in] params Further parameters (9 bytes)
  *  @param[out] buf Buffer to create msg in (42 bytes).
  */
-void create_cmd_14(guchar addr, guchar func, guchar* params, guchar* buf)
+void create_cmd_14(guchar addr, guchar func, guchar *params, guchar *buf)
 {
-	uint8_t dta[14];	/* Unencoded message */
+	uint8_t dta[GMC_REPLY_SIZE];	/* Unencoded message */
 	int cnt;
 
 	if (!params || !buf)
@@ -1270,17 +1279,17 @@ void create_cmd_14(guchar addr, guchar func, guchar* params, guchar* buf)
 
 	/* 4-12: Copy further parameters */
 	for (cnt = 0; cnt < 9; cnt++)
-		dta[cnt+4] = (params[cnt] & MASK_6BITS);
+		dta[cnt + 4] = (params[cnt] & MASK_6BITS);
 
 	/* 13: Checksum (b complement) */
 	dta[13] = calc_chksum_14(dta);
 
 	/* The whole message is packed into 3 bytes per byte now (lower 6 bits only) the most
 	 * peculiar way I have ever seen. Possibly to improve IR communication? */
-	for (cnt = 0; cnt < 14; cnt++) {
-		buf[3*cnt] = (dta[cnt] & 0x01 ? 0x0f : 0) | (dta[cnt] & 0x02 ? 0xf0 : 0);
-		buf[3*cnt + 1] = (dta[cnt] & 0x04 ? 0x0f : 0) | (dta[cnt] & 0x08 ? 0xf0 : 0);
-		buf[3*cnt + 2] = (dta[cnt] & 0x10 ? 0x0f : 0) | (dta[cnt] & 0x20 ? 0xf0 : 0);
+	for (cnt = 0; cnt < GMC_REPLY_SIZE; cnt++) {
+		buf[(3 * cnt) + 0] = (dta[cnt] & 0x01 ? 0x0f : 0) | (dta[cnt] & 0x02 ? 0xf0 : 0);
+		buf[(3 * cnt) + 1] = (dta[cnt] & 0x04 ? 0x0f : 0) | (dta[cnt] & 0x08 ? 0xf0 : 0);
+		buf[(3 * cnt) + 2] = (dta[cnt] & 0x10 ? 0x0f : 0) | (dta[cnt] & 0x20 ? 0xf0 : 0);
 	}
 }
 
@@ -1302,7 +1311,8 @@ int req_meas14(const struct sr_dev_inst *sdi)
 	devc->cmd_idx = 0;
 	create_cmd_14(devc->addr, 8, params, msg);
 	devc->req_sent_at = g_get_monotonic_time();
-	if (serial_write(serial, msg, sizeof(msg)) == -1) {
+	if (serial_write_blocking(serial, msg, sizeof(msg),
+			serial_timeout(serial, sizeof(msg))) < (int)sizeof(msg)) {
 		return SR_ERR;
 	}
 
@@ -1331,21 +1341,25 @@ int req_stat14(const struct sr_dev_inst *sdi, gboolean power_on)
 
 	if (power_on) {
 		sr_info("Write some data and wait 3s to turn on powered off device...");
-		if (serial_write(serial, msg, sizeof(msg)) < 0)
+		if (serial_write_blocking(serial, msg, sizeof(msg),
+				serial_timeout(serial, sizeof(msg))) < 0)
 			return SR_ERR;
-		g_usleep(1*1000*1000);
-		if (serial_write(serial, msg, sizeof(msg)) < 0)
+		g_usleep(1 * 1000 * 1000);
+		if (serial_write_blocking(serial, msg, sizeof(msg),
+				serial_timeout(serial, sizeof(msg))) < 0)
 			return SR_ERR;
-		g_usleep(1*1000*1000);
-		if (serial_write(serial, msg, sizeof(msg)) < 0)
+		g_usleep(1 * 1000 * 1000);
+		if (serial_write_blocking(serial, msg, sizeof(msg),
+				serial_timeout(serial, sizeof(msg))) < 0)
 			return SR_ERR;
-		g_usleep(1*1000*1000);
+		g_usleep(1 * 1000 * 1000);
 		serial_flush(serial);
 	}
 
 	/* Write message and wait for reply */
 	devc->req_sent_at = g_get_monotonic_time();
-	if (serial_write(serial, msg, sizeof(msg)) == -1) {
+	if (serial_write_blocking(serial, msg, sizeof(msg),
+			serial_timeout(serial, sizeof(msg))) < (int)sizeof(msg)) {
 		return SR_ERR;
 	}
 
@@ -1366,7 +1380,7 @@ SR_PRIV int gmc_decode_model_sm(uint8_t mcode)
 		return METRAHIT_NONE;
 	}
 
-	switch(mcode) {
+	switch (mcode) {
 	case 0x04: /* 0100b */
 		return METRAHIT_12S;
 	case 0x08: /* 1000b */
@@ -1379,21 +1393,23 @@ SR_PRIV int gmc_decode_model_sm(uint8_t mcode)
 		return METRAHIT_16S;
 	case 0x06: /* 0110b (undocumented by GMC!) */
 		return METRAHIT_16I;
+	case 0x07: /* 0111b (undocumented by GMC!) */
+		return METRAHIT_16T;
 	case 0x0D: /* 1101b */
 		return METRAHIT_18S;
 	case 0x02: /* 0010b */
 		return METRAHIT_22SM;
 	case 0x03: /* 0011b */
 		return METRAHIT_23S;
-	case 0x0f: /* 1111b */
+	case 0x0F: /* 1111b */
 		return METRAHIT_24S;
 	case 0x05: /* 0101b */
 		return METRAHIT_25S;
 	case 0x01: /* 0001b */
 		return METRAHIT_26SM;
-	case 0x0c: /* 1100b */
+	case 0x0C: /* 1100b */
 		return METRAHIT_28S;
-	case 0x0e: /* 1110b */
+	case 0x0E: /* 1110b */
 		return METRAHIT_29S;
 	default:
 		sr_err("Unknown model code %d!", mcode);
@@ -1458,7 +1474,9 @@ SR_PRIV const char *gmc_model_str(enum model mcode)
 	case METRAHIT_16S:
 		return "METRAHit 16S";
 	case METRAHIT_16I:
-		return "METRAHit 16I";
+		return "METRAHit 16I/16L";
+	case METRAHIT_16T:
+		return "METRAHit 16T/16U/KMM2002";
 	case METRAHIT_18S:
 		return "METRAHit 18S";
 	case METRAHIT_22SM:
@@ -1488,11 +1506,9 @@ SR_PRIV const char *gmc_model_str(enum model mcode)
 	}
 }
 
-
-/** @copydoc sr_dev_driver.config_set
- */
-SR_PRIV int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
-		       const struct sr_channel_group *cg)
+/** @copydoc sr_dev_driver.config_set */
+SR_PRIV int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
 	uint8_t params[9];
@@ -1520,24 +1536,17 @@ SR_PRIV int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 		params[0] = 5;
 		params[1] = 5;
 		create_cmd_14(devc->addr, 6, params, msg);
-		if (serial_write(sdi->conn, msg, sizeof(msg)) == -1)
+		if (serial_write_blocking(sdi->conn, msg, sizeof(msg),
+				serial_timeout(sdi->conn, sizeof(msg))) < 0)
 			return SR_ERR;
 		else
-			g_usleep(2000000); /* Wait to ensure transfer before interface switched off. */
+			g_usleep(2 * 1000 * 1000); /* Wait to ensure transfer before interface switched off. */
 		break;
 	case SR_CONF_LIMIT_MSEC:
-		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;
diff --git a/hardware/gmc-mh-1x-2x/protocol.h b/src/hardware/gmc-mh-1x-2x/protocol.h
similarity index 88%
rename from hardware/gmc-mh-1x-2x/protocol.h
rename to src/hardware/gmc-mh-1x-2x/protocol.h
index 3d34d9a..b4869ad 100644
--- a/hardware/gmc-mh-1x-2x/protocol.h
+++ b/src/hardware/gmc-mh-1x-2x/protocol.h
@@ -27,12 +27,13 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "gmc-mh-1x-2x"
 
-#define GMC_BUFSIZE  266
+#define GMC_BUFSIZE 266
+#define GMC_REPLY_SIZE 14
 
 /** Message ID bits 4, 5 */
 #define MSGID_MASK  0x30 /**< Mask to get message ID bits */
@@ -60,10 +61,11 @@ enum model {
 	METRAHIT_14S		= 14,
 	METRAHIT_15S		= 15,
 	METRAHIT_16S		= 16,
-	METRAHIT_16I		= 17,
-	METRAHIT_16X = METRAHIT_16I,  /**< All Metrahit 16 */
+	METRAHIT_16I		= 17, /**< Metrahit 16I, L */
+	METRAHIT_16T		= 18, /**< Metrahit 16T, U, KMM2002 */
+	METRAHIT_16X = METRAHIT_16T,  /**< All Metrahit 16 */
 	/* A Metrahit 17 exists, but seems not to have an IR interface. */
-	METRAHIT_18S		= 18,
+	METRAHIT_18S		= 19,
 	METRAHIT_2X		= 20, /**< For model type comparisons */
 	METRAHIT_22SM		= METRAHIT_2X + 1,	/**< Send mode */
 	METRAHIT_22S		= METRAHIT_22SM + 1,	/**< Bidi mode */
@@ -74,6 +76,7 @@ enum model {
 	METRAHIT_26SM		= METRAHIT_25S + 1,	/**< Send mode */
 	METRAHIT_26S		= METRAHIT_26SM + 1,	/**< Bidi mode */
 	METRAHIT_26M		= METRAHIT_26S + 1,	/**< Bidi mode */
+	/* The Metrahit 27x and 28Cx have a totally different protocol */
 	METRAHIT_28S		= METRAHIT_26M + 1,
 	METRAHIT_29S		= METRAHIT_28S + 1,
 };
@@ -93,14 +96,13 @@ struct dev_context {
 	/* Operational state */
 	gboolean settings_ok;	/**< Settings msg received yet. */
 	int msg_type;       /**< Message type (MSGID_INF, ...). */
-	int msg_len;        /**< Message lengh (valid when msg, curr. type known).*/
+	int msg_len;        /**< Message length (valid when msg, curr. type known).*/
 	int mq;             /**< Measured quantity */
 	int unit;           /**< Measured unit */
 	uint64_t mqflags;	/**< Measured quantity flags */
 	float value;		/**< Measured value */
 	float scale;		/**< Scale for value. */
 	int8_t scale1000;   /**< Additional scale factor 1000x. */
-	gboolean vmains_29S;	/**< Measured ctmv is V mains (29S only). */
 	int addr;           /**< Device address (1..15). */
 	int cmd_idx;        /**< Parameter "Idx" (Index) of current command, if required. */
 	int cmd_seq;        /**< Command sequence. Used to query status every n messages. */
@@ -119,10 +121,10 @@ struct dev_context {
 };
 
 /* Forward declarations */
-SR_PRIV int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+SR_PRIV int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg);
-SR_PRIV void create_cmd_14(guchar addr, guchar func, guchar* params, guchar* buf);
-SR_PRIV void dump_msg14(guchar* buf, gboolean raw);
+SR_PRIV void create_cmd_14(guchar addr, guchar func, guchar *params, guchar *buf);
+SR_PRIV void dump_msg14(guchar *buf, gboolean raw);
 SR_PRIV int gmc_decode_model_bd(uint8_t mcode);
 SR_PRIV int gmc_decode_model_sm(uint8_t mcode);
 SR_PRIV int gmc_mh_1x_2x_receive_data(int fd, int revents, void *cb_data);
diff --git a/src/hardware/gwinstek-gds-800/api.c b/src/hardware/gwinstek-gds-800/api.c
new file mode 100644
index 0000000..cfec15e
--- /dev/null
+++ b/src/hardware/gwinstek-gds-800/api.c
@@ -0,0 +1,282 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Martin Lederhilger <martin.lederhilger at gmx.at>
+ *
+ * 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 "protocol.h"
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_OSCILLOSCOPE,
+	SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET,
+};
+
+SR_PRIV struct sr_dev_driver gwinstek_gds_800_driver_info;
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
+{
+	struct dev_context *devc;
+	struct sr_dev_inst *sdi;
+	struct sr_scpi_hw_info *hw_info;
+	struct sr_channel_group *cg;
+
+	if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+		sr_info("Couldn't get IDN response.");
+		return NULL;
+	}
+
+	if (strcmp(hw_info->manufacturer, "GW") != 0 ||
+	    strncmp(hw_info->model, "GDS-8", 5) != 0) {
+		sr_scpi_hw_info_free(hw_info);
+		return NULL;
+	}
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->vendor = g_strdup(hw_info->manufacturer);
+	sdi->model = g_strdup(hw_info->model);
+	sdi->version = g_strdup(hw_info->firmware_version);
+	sdi->conn = scpi;
+	sdi->driver = &gwinstek_gds_800_driver_info;
+	sdi->inst_type = SR_INST_SCPI;
+	sdi->serial_num = g_strdup(hw_info->serial_number);
+	sdi->channels = NULL;
+	sdi->channel_groups = NULL;
+
+	sr_scpi_hw_info_free(hw_info);
+
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->frame_limit = 1;
+	devc->sample_rate = 0.;
+	devc->df_started  = FALSE;
+	sdi->priv = devc;
+
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
+	sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "CH2");
+
+	cg = g_malloc0(sizeof(struct sr_channel_group));
+	cg->name = g_strdup("");
+	cg->channels = g_slist_append(cg->channels, g_slist_nth_data(sdi->channels, 0));
+	cg->channels = g_slist_append(cg->channels, g_slist_nth_data(sdi->channels, 1));
+	cg->priv = NULL;
+	sdi->channel_groups = g_slist_append(NULL, cg);
+
+	return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	return sr_scpi_scan(di->context, options, probe_device);
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	int ret;
+	struct sr_scpi_dev_inst *scpi = sdi->conn;
+
+	if ((ret = sr_scpi_open(scpi)) < 0) {
+		sr_err("Failed to open SCPI device: %s.", sr_strerror(ret));
+		return SR_ERR;
+	}
+
+	sdi->status = SR_ST_ACTIVE;
+
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct sr_scpi_dev_inst *scpi;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	scpi = sdi->conn;
+	if (scpi) {
+		if (sr_scpi_close(scpi) < 0)
+			return SR_ERR;
+		sdi->status = SR_ST_INACTIVE;
+	}
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	dev_clear(di);
+
+	return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (!sdi || !(devc = sdi->priv))
+		return SR_ERR_ARG;
+
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		*data = g_variant_new_uint64(devc->sample_rate);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (!sdi || !(devc = sdi->priv))
+		return SR_ERR_ARG;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	switch (key) {
+	case SR_CONF_LIMIT_FRAMES:
+		devc->frame_limit = g_variant_get_uint64(data);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	(void)sdi;
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		return SR_OK;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_scpi_dev_inst *scpi;
+	struct dev_context *devc;
+
+	(void)cb_data;
+
+	scpi = sdi->conn;
+	devc = sdi->priv;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc->state = START_ACQUISITION;
+	devc->cur_acq_frame = 0;
+
+	sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50,
+			gwinstek_gds_800_receive_data, (void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_scpi_dev_inst *scpi;
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+
+	(void)cb_data;
+
+	scpi = sdi->conn;
+	devc = sdi->priv;
+
+	if (sdi->status != SR_ST_ACTIVE) {
+		sr_err("Device inactive, can't stop acquisition.");
+		return SR_ERR;
+	}
+
+	if (devc->df_started) {
+		packet.type = SR_DF_FRAME_END;
+		sr_session_send(sdi, &packet);
+
+		packet.type = SR_DF_END;
+		sr_session_send(sdi, &packet);
+
+		devc->df_started = FALSE;
+	}
+
+	sr_scpi_source_remove(sdi->session, scpi);
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver gwinstek_gds_800_driver_info = {
+	.name = "gwinstek-gds-800",
+	.longname = "GW Instek GDS-800 series",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/gwinstek-gds-800/protocol.c b/src/hardware/gwinstek-gds-800/protocol.c
new file mode 100644
index 0000000..358e962
--- /dev/null
+++ b/src/hardware/gwinstek-gds-800/protocol.c
@@ -0,0 +1,283 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Martin Lederhilger <martin.lederhilger at gmx.at>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "protocol.h"
+#include <string.h>
+
+#define ANALOG_CHANNELS 2
+#define VERTICAL_DIVISIONS 10
+
+static int read_data(struct sr_dev_inst *sdi, void *cb_data,
+		struct sr_scpi_dev_inst *scpi, struct dev_context *devc,
+		int data_size)
+{
+	int len;
+
+	len = sr_scpi_read_data(scpi,
+		&devc->rcv_buffer[devc->cur_rcv_buffer_position],
+		data_size - devc->cur_rcv_buffer_position);
+	if (len < 0) {
+		sr_err("Read data error.");
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+		devc->cur_rcv_buffer_position = 0;
+		return SR_ERR;
+	}
+
+	devc->cur_rcv_buffer_position += len;
+
+	/* Handle the case where sr_scpi_read_data stopped at the newline. */
+	if (len < data_size && sr_scpi_read_complete(scpi)) {
+		devc->rcv_buffer[devc->cur_rcv_buffer_position] = '\n';
+		devc->cur_rcv_buffer_position++;
+	}
+
+	if (devc->cur_rcv_buffer_position < data_size)
+		return SR_ERR; /* Not finished yet. */
+	else if (devc->cur_rcv_buffer_position == data_size) {
+		 devc->cur_rcv_buffer_position = 0;
+		return SR_OK;
+	} else {
+		sr_err("Too many bytes read.");
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+		devc->cur_rcv_buffer_position = 0;
+		return SR_ERR;
+	}
+}
+
+SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct sr_scpi_dev_inst *scpi;
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+	char command[32];
+	char *response;
+	float volts_per_division;
+	int num_samples, i;
+	float samples[MAX_SAMPLES];
+	uint32_t sample_rate;
+	char *end_ptr;
+
+	(void)fd;
+
+	if (!(sdi = cb_data))
+		return TRUE;
+
+	if (!(devc = sdi->priv))
+		return TRUE;
+
+	scpi = sdi->conn;
+
+	if (!(revents == G_IO_IN || revents == 0))
+		return TRUE;
+
+	switch (devc->state) {
+	case START_ACQUISITION:
+		if (sr_scpi_send(scpi, ":TRIG:MOD 3") != SR_OK) {
+			sr_err("Failed to set trigger mode to SINGLE.");
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			return TRUE;
+		}
+		if (sr_scpi_send(scpi, ":STOP") != SR_OK) {
+			sr_err("Failed to put the trigger system into STOP state.");
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			return TRUE;
+		}
+		if (sr_scpi_send(scpi, ":RUN") != SR_OK) {
+			sr_err("Failed to put the trigger system into RUN state.");
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			return TRUE;
+		}
+
+		devc->cur_acq_channel = 0;
+		devc->state = START_TRANSFER_OF_CHANNEL_DATA;
+		break;
+	case START_TRANSFER_OF_CHANNEL_DATA:
+		if (((struct sr_channel *)g_slist_nth_data(sdi->channels, devc->cur_acq_channel))->enabled) {
+			if (sr_scpi_send(scpi, ":ACQ%d:MEM?", devc->cur_acq_channel+1) != SR_OK) {
+				sr_err("Failed to acquire memory.");
+				sdi->driver->dev_acquisition_stop(sdi, cb_data);
+				return TRUE;
+			}
+			if (sr_scpi_read_begin(scpi) != SR_OK) {
+				sr_err("Could not begin reading SCPI response.");
+				sdi->driver->dev_acquisition_stop(sdi, cb_data);
+				return TRUE;
+			}
+			devc->state = WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE;
+			devc->cur_rcv_buffer_position = 0;
+		} else {
+			/* All channels acquired. */
+			if (devc->cur_acq_channel == ANALOG_CHANNELS - 1) {
+				sr_spew("All channels acquired.");
+
+				if (devc->cur_acq_frame == devc->frame_limit - 1) {
+					/* All frames accquired. */
+					sr_spew("All frames acquired.");
+					
+					sdi->driver->dev_acquisition_stop(sdi, cb_data);
+					return TRUE;
+				} else {
+					/* Start acquiring next frame. */
+					if (devc->df_started) {
+						packet.type = SR_DF_FRAME_END;
+						sr_session_send(sdi, &packet);
+						
+						packet.type = SR_DF_FRAME_BEGIN;
+						sr_session_send(sdi, &packet);
+					}
+
+					devc->cur_acq_frame++;
+					devc->state = START_ACQUISITION;
+				}
+			} else {
+				/* Start acquiring next channel. */
+				devc->cur_acq_channel++;
+			}
+		}
+		break;
+	case WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE:
+		if (read_data(sdi, cb_data, scpi, devc, 1) == SR_OK) {
+			if (devc->rcv_buffer[0] == '#')
+				devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE;
+		}
+		break;
+	case WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE:
+		if (read_data(sdi, cb_data, scpi, devc, 1) == SR_OK) {
+			if (devc->rcv_buffer[0] != '4' &&
+				devc->rcv_buffer[0] != '5' &&
+				devc->rcv_buffer[0] != '6') {
+				sr_err("Data size digits is not 4, 5 or 6 but "
+				       "'%c'.", devc->rcv_buffer[0]);
+				sdi->driver->dev_acquisition_stop(sdi, cb_data);
+				return TRUE;
+			} else {
+				devc->data_size_digits = devc->rcv_buffer[0] - '0';
+				devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE;
+			}
+		}
+		break;
+	case WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE:
+		if (read_data(sdi, cb_data, scpi, devc, devc->data_size_digits) == SR_OK) {
+			devc->rcv_buffer[devc->data_size_digits] = 0;
+			if (sr_atoi(devc->rcv_buffer, &devc->data_size) != SR_OK) {
+				sr_err("Could not parse data size '%s'", devc->rcv_buffer);
+				sdi->driver->dev_acquisition_stop(sdi, cb_data);
+				return TRUE;
+			} else
+				devc->state = WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE;
+		}
+		break;
+	case WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE:
+		if (read_data(sdi, cb_data, scpi, devc, sizeof(float)) == SR_OK) {
+			/*
+			 * Contrary to the documentation, this field is
+			 * transfered with most significant byte first!
+			 */
+			sample_rate = RB32(devc->rcv_buffer);
+			memcpy(&devc->sample_rate, &sample_rate, sizeof(float));
+			devc->state = WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE;
+
+			if (!devc->df_started) {
+				std_session_send_df_header(sdi, LOG_PREFIX);
+
+				packet.type = SR_DF_FRAME_BEGIN;
+				sr_session_send(sdi, &packet);
+
+				devc->df_started = TRUE;
+			}
+		}
+		break;
+	case WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE:
+		if (read_data(sdi, cb_data, scpi, devc, 1) == SR_OK)
+			devc->state = WAIT_FOR_TRANSFER_OF_RESERVED_DATA_COMPLETE;
+		break;
+	case WAIT_FOR_TRANSFER_OF_RESERVED_DATA_COMPLETE:
+		if (read_data(sdi, cb_data, scpi, devc, 3) == SR_OK)
+			devc->state = WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE;
+		break;
+	case WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE:
+		if (read_data(sdi, cb_data, scpi, devc, devc->data_size - 8) == SR_OK) {
+			/* Fetch data needed for conversion from device. */
+			snprintf(command, sizeof(command), ":CHAN%d:SCAL?",
+					devc->cur_acq_channel + 1);
+			if (sr_scpi_get_string(scpi, command, &response) != SR_OK) {
+				sr_err("Failed to get volts per division.");
+				sdi->driver->dev_acquisition_stop(sdi, cb_data);
+				return TRUE;
+			}
+			volts_per_division = g_ascii_strtod(response, &end_ptr);
+			if (!strcmp(end_ptr, "mV"))
+				volts_per_division *= 1.e-3;
+			g_free(response);
+
+			num_samples = (devc->data_size - 8) / 2;
+			sr_spew("Received %d number of samples from channel "
+				"%d.", num_samples, devc->cur_acq_channel + 1);
+
+			/* Convert data. */
+			for (i = 0; i < num_samples; i++)
+				samples[i] = ((float) ((int16_t) (RB16(&devc->rcv_buffer[i*2])))) / 256. * VERTICAL_DIVISIONS * volts_per_division;
+
+			/* Fill frame. */
+			analog.channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, devc->cur_acq_channel));
+			analog.num_samples = num_samples;
+			analog.data = samples;
+			analog.mq = SR_MQ_VOLTAGE;
+			analog.unit = SR_UNIT_VOLT;
+			analog.mqflags = 0;
+			packet.type = SR_DF_ANALOG_OLD;
+			packet.payload = &analog;
+			sr_session_send(cb_data, &packet);
+			g_slist_free(analog.channels);
+
+			/* All channels acquired. */
+			if (devc->cur_acq_channel == ANALOG_CHANNELS - 1) {
+				sr_spew("All channels acquired.");
+
+				if (devc->cur_acq_frame == devc->frame_limit - 1) {
+					/* All frames acquired. */
+					sr_spew("All frames acquired.");
+					sdi->driver->dev_acquisition_stop(sdi, cb_data);
+					return TRUE;
+				} else {
+					/* Start acquiring next frame. */
+					if (devc->df_started) {
+						packet.type = SR_DF_FRAME_END;
+						sr_session_send(sdi, &packet);
+						
+						packet.type = SR_DF_FRAME_BEGIN;
+						sr_session_send(sdi, &packet);
+					}
+					devc->cur_acq_frame++;
+					devc->state = START_ACQUISITION;
+				}
+			} else {
+				/* Start acquiring next channel. */
+				devc->state = START_TRANSFER_OF_CHANNEL_DATA;
+				devc->cur_acq_channel++;
+				return TRUE;
+			}
+		}
+		break;
+	}
+
+	return TRUE;
+}
diff --git a/src/hardware/gwinstek-gds-800/protocol.h b/src/hardware/gwinstek-gds-800/protocol.h
new file mode 100644
index 0000000..382e87b
--- /dev/null
+++ b/src/hardware/gwinstek-gds-800/protocol.h
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Martin Lederhilger <martin.lederhilger at gmx.at>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_GWINSTEK_GDS_800_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_GWINSTEK_GDS_800_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+
+#define LOG_PREFIX "gwinstek-gds-800"
+
+#define MAX_SAMPLES 125000
+#define MAX_RCV_BUFFER_SIZE (MAX_SAMPLES * 2)
+
+enum gds_state
+{
+	START_ACQUISITION,
+	START_TRANSFER_OF_CHANNEL_DATA,
+	WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE,
+	WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE,
+	WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE,
+	WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE,
+	WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE,
+	WAIT_FOR_TRANSFER_OF_RESERVED_DATA_COMPLETE,
+	WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	enum gds_state state;
+	uint64_t cur_acq_frame;
+	uint64_t frame_limit;
+	int cur_acq_channel;
+	int cur_rcv_buffer_position;
+	char rcv_buffer[MAX_RCV_BUFFER_SIZE];
+	int data_size_digits;
+	int data_size;
+	float sample_rate;
+	gboolean df_started;
+};
+
+SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/hardware/hameg-hmo/api.c b/src/hardware/hameg-hmo/api.c
similarity index 83%
rename from hardware/hameg-hmo/api.c
rename to src/hardware/hameg-hmo/api.c
index b5b3bc2..1b36f04 100644
--- a/hardware/hameg-hmo/api.c
+++ b/src/hardware/hameg-hmo/api.c
@@ -17,19 +17,24 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
+#include "scpi.h"
 #include "protocol.h"
 
 #define SERIALCOMM "115200/8n1/flow=1"
 
 SR_PRIV struct sr_dev_driver hameg_hmo_driver_info;
-static struct sr_dev_driver *di = &hameg_hmo_driver_info;
 
 static const char *manufacturers[] = {
 	"HAMEG",
 };
 
-static const int32_t hwopts[] = {
+static const uint32_t drvopts[] = {
+	SR_CONF_OSCILLOSCOPE,
+};
+
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
@@ -41,7 +46,7 @@ enum {
 	CG_DIGITAL,
 };
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
@@ -50,7 +55,7 @@ static int check_manufacturer(const char *manufacturer)
 {
 	unsigned int i;
 
-	for (i = 0; i < ARRAY_SIZE(manufacturers); ++i)
+	for (i = 0; i < ARRAY_SIZE(manufacturers); i++)
 		if (!strcmp(manufacturer, manufacturers[i]))
 			return SR_OK;
 
@@ -75,29 +80,25 @@ static struct sr_dev_inst *hmo_probe_serial_device(struct sr_scpi_dev_inst *scpi
 	if (check_manufacturer(hw_info->manufacturer) != SR_OK)
 		goto fail;
 
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
-				    hw_info->manufacturer, hw_info->model,
-				    hw_info->firmware_version))) {
-		goto fail;
-	}
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->vendor = g_strdup(hw_info->manufacturer);
+	sdi->model = g_strdup(hw_info->model);
+	sdi->version = g_strdup(hw_info->firmware_version);
+	sdi->serial_num = g_strdup(hw_info->serial_number);
+	sdi->driver = &hameg_hmo_driver_info;
+	sdi->inst_type = SR_INST_SCPI;
+	sdi->conn = scpi;
+
 	sr_scpi_hw_info_free(hw_info);
 	hw_info = NULL;
 
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
-		goto fail;
+	devc = g_malloc0(sizeof(struct dev_context));
 
-	sdi->driver = di;
 	sdi->priv = devc;
-	sdi->inst_type = SR_INST_SCPI;
-	sdi->conn = scpi;
 
 	if (hmo_init_device(sdi) != SR_OK)
 		goto fail;
 
-	sr_scpi_close(sdi->conn);
-
-	sdi->status = SR_ST_INACTIVE;
-
 	return sdi;
 
 fail:
@@ -105,48 +106,36 @@ fail:
 		sr_scpi_hw_info_free(hw_info);
 	if (sdi)
 		sr_dev_inst_free(sdi);
-	if (devc)
-		g_free(devc);
+	g_free(devc);
 
 	return NULL;
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-	return sr_scpi_scan(di->priv, options, hmo_probe_serial_device);
+	return sr_scpi_scan(di->context, options, hmo_probe_serial_device);
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static void clear_helper(void *priv)
 {
-	unsigned int i;
 	struct dev_context *devc;
-	struct scope_config *model;
 
 	devc = priv;
-	model = devc->model_config;
 
 	hmo_scope_state_free(devc->model_state);
 
-	for (i = 0; i < model->analog_channels; ++i)
-		g_slist_free(devc->analog_groups[i].channels);
-
-	for (i = 0; i < model->digital_pods; ++i) {
-		g_slist_free(devc->digital_groups[i].channels);
-		g_free(devc->digital_groups[i].name);
-	}
-
 	g_free(devc->analog_groups);
 	g_free(devc->digital_groups);
 
 	g_free(devc);
 }
 
-static int dev_clear(void)
+static int dev_clear(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, clear_helper);
 }
@@ -176,9 +165,9 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
-	dev_clear();
+	dev_clear(di);
 
 	return SR_OK;
 }
@@ -187,19 +176,19 @@ static int check_channel_group(struct dev_context *devc,
 			     const struct sr_channel_group *cg)
 {
 	unsigned int i;
-	struct scope_config *model;
+	const struct scope_config *model;
 
 	model = devc->model_config;
 
 	if (!cg)
 		return CG_NONE;
 
-	for (i = 0; i < model->analog_channels; ++i)
-		if (cg == &devc->analog_groups[i])
+	for (i = 0; i < model->analog_channels; i++)
+		if (cg == devc->analog_groups[i])
 			return CG_ANALOG;
 
-	for (i = 0; i < model->digital_pods; ++i)
-		if (cg == &devc->digital_groups[i])
+	for (i = 0; i < model->digital_pods; i++)
+		if (cg == devc->digital_groups[i])
 			return CG_DIGITAL;
 
 	sr_err("Invalid channel group specified.");
@@ -207,13 +196,13 @@ static int check_channel_group(struct dev_context *devc,
 	return CG_INVALID;
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		      const struct sr_channel_group *cg)
 {
 	int ret, cg_type;
 	unsigned int i;
 	struct dev_context *devc;
-	struct scope_config *model;
+	const struct scope_config *model;
 	struct scope_state *state;
 
 	if (!sdi || !(devc = sdi->priv))
@@ -227,7 +216,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	state = devc->model_state;
 
 	switch (key) {
-	case SR_CONF_NUM_TIMEBASE:
+	case SR_CONF_NUM_HDIV:
 		*data = g_variant_new_int32(model->num_xdivs);
 		ret = SR_OK;
 		break;
@@ -241,8 +230,8 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 			sr_err("No channel group specified.");
 			return SR_ERR_CHANNEL_GROUP;
 		} else if (cg_type == CG_ANALOG) {
-			for (i = 0; i < model->analog_channels; ++i) {
-				if (cg != &devc->analog_groups[i])
+			for (i = 0; i < model->analog_channels; i++) {
+				if (cg != devc->analog_groups[i])
 					continue;
 				*data = g_variant_new_int32(model->num_ydivs);
 				ret = SR_OK;
@@ -258,8 +247,8 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 			sr_err("No channel group specified.");
 			return SR_ERR_CHANNEL_GROUP;
 		} else if (cg_type == CG_ANALOG) {
-			for (i = 0; i < model->analog_channels; ++i) {
-				if (cg != &devc->analog_groups[i])
+			for (i = 0; i < model->analog_channels; i++) {
+				if (cg != devc->analog_groups[i])
 					continue;
 				*data = g_variant_new("(tt)",
 						      (*model->vdivs)[state->analog_channels[i].vdiv][0],
@@ -289,8 +278,8 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 			sr_err("No channel group specified.");
 			return SR_ERR_CHANNEL_GROUP;
 		} else if (cg_type == CG_ANALOG) {
-			for (i = 0; i < model->analog_channels; ++i) {
-				if (cg != &devc->analog_groups[i])
+			for (i = 0; i < model->analog_channels; i++) {
+				if (cg != devc->analog_groups[i])
 					continue;
 				*data = g_variant_new_string((*model->coupling_options)[state->analog_channels[i].coupling]);
 				ret = SR_OK;
@@ -331,14 +320,14 @@ static GVariant *build_tuples(const uint64_t (*array)[][2], unsigned int n)
 	return g_variant_builder_end(&gvb);
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		      const struct sr_channel_group *cg)
 {
 	int ret, cg_type;
 	unsigned int i, j;
 	char command[MAX_COMMAND_SIZE], float_str[30];
 	struct dev_context *devc;
-	struct scope_config *model;
+	const struct scope_config *model;
 	struct scope_state *state;
 	const char *tmp;
 	uint64_t p, q;
@@ -388,8 +377,8 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 			if (p != (*model->vdivs)[i][0] ||
 			    q != (*model->vdivs)[i][1])
 				continue;
-			for (j = 1; j <= model->analog_channels; ++j) {
-				if (cg != &devc->analog_groups[j - 1])
+			for (j = 1; j <= model->analog_channels; j++) {
+				if (cg != devc->analog_groups[j - 1])
 					continue;
 				state->analog_channels[j - 1].vdiv = i;
 				g_ascii_formatd(float_str, sizeof(float_str), "%E", (float) p / q);
@@ -470,8 +459,8 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 		for (i = 0; (*model->coupling_options)[i]; i++) {
 			if (strcmp(tmp, (*model->coupling_options)[i]) != 0)
 				continue;
-			for (j = 1; j <= model->analog_channels; ++j) {
-				if (cg != &devc->analog_groups[j - 1])
+			for (j = 1; j <= model->analog_channels; j++) {
+				if (cg != devc->analog_groups[j - 1])
 					continue;
 				state->analog_channels[j-1].coupling = i;
 
@@ -503,34 +492,40 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		       const struct sr_channel_group *cg)
 {
-	int cg_type;
-	struct dev_context *devc;
-	struct scope_config *model;
-
-	if (!sdi || !(devc = sdi->priv))
-		return SR_ERR_ARG;
+	int cg_type = CG_NONE;
+	struct dev_context *devc = NULL;
+	const struct scope_config *model = NULL;
 
-	if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
-		return SR_ERR;
+	if (sdi && (devc = sdi->priv)) {
+		if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
+			return SR_ERR;
 
-	model = devc->model_config;
+		model = devc->model_config;
+	}
 
 	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		break;
 	case SR_CONF_DEVICE_OPTIONS:
 		if (cg_type == CG_NONE) {
-			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				model->hw_caps, model->num_hwcaps,
-				sizeof(int32_t));
+			if (model)
+				*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					model->devopts, model->num_devopts, sizeof(uint32_t));
+			else
+				*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
 		} else if (cg_type == CG_ANALOG) {
-			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				model->analog_hwcaps, model->num_analog_hwcaps,
-				sizeof(int32_t));
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				model->analog_devopts, model->num_analog_devopts,
+				sizeof(uint32_t));
 		} else {
-			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				NULL, 0, sizeof(int32_t));
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				NULL, 0, sizeof(uint32_t));
 		}
 		break;
 	case SR_CONF_COUPLING:
@@ -540,14 +535,20 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 			   g_strv_length((char **)*model->coupling_options));
 		break;
 	case SR_CONF_TRIGGER_SOURCE:
+		if (!model)
+			return SR_ERR_ARG;
 		*data = g_variant_new_strv(*model->trigger_sources,
 			   g_strv_length((char **)*model->trigger_sources));
 		break;
 	case SR_CONF_TRIGGER_SLOPE:
+		if (!model)
+			return SR_ERR_ARG;
 		*data = g_variant_new_strv(*model->trigger_slopes,
 			   g_strv_length((char **)*model->trigger_slopes));
 		break;
 	case SR_CONF_TIMEBASE:
+		if (!model)
+			return SR_ERR_ARG;
 		*data = build_tuples(model->timebases, model->num_timebases);
 		break;
 	case SR_CONF_VDIV:
@@ -567,7 +568,7 @@ SR_PRIV int hmo_request_data(const struct sr_dev_inst *sdi)
 	char command[MAX_COMMAND_SIZE];
 	struct sr_channel *ch;
 	struct dev_context *devc;
-	struct scope_config *model;
+	const struct scope_config *model;
 
 	devc = sdi->priv;
 	model = devc->model_config;
@@ -635,7 +636,7 @@ static int hmo_setup_channels(const struct sr_dev_inst *sdi)
 	gboolean *pod_enabled, setup_changed;
 	char command[MAX_COMMAND_SIZE];
 	struct scope_state *state;
-	struct scope_config *model;
+	const struct scope_config *model;
 	struct sr_channel *ch;
 	struct dev_context *devc;
 	struct sr_scpi_dev_inst *scpi;
@@ -688,7 +689,7 @@ static int hmo_setup_channels(const struct sr_dev_inst *sdi)
 		}
 	}
 
-	for (i = 1; i <= model->digital_pods; ++i) {
+	for (i = 1; i <= model->digital_pods; i++) {
 		if (state->digital_pods[i - 1] == pod_enabled[i - 1])
 			continue;
 		g_snprintf(command, sizeof(command),
@@ -723,6 +724,9 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	devc = sdi->priv;
 	digital_added = FALSE;
 
+	g_slist_free(devc->enabled_channels);
+	devc->enabled_channels = NULL;
+
 	for (l = sdi->channels; l; l = l->next) {
 		ch = l->data;
 		if (!ch->enabled)
@@ -749,7 +753,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		return SR_ERR;
 	}
 
-	sr_scpi_source_add(scpi, G_IO_IN, 50, hmo_receive_data, (void *)sdi);
+	sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50,
+			hmo_receive_data, (void *)sdi);
 
 	/* Send header packet to the session bus. */
 	std_session_send_df_header(cb_data, LOG_PREFIX);
@@ -780,7 +785,7 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 	g_slist_free(devc->enabled_channels);
 	devc->enabled_channels = NULL;
 	scpi = sdi->conn;
-	sr_scpi_source_remove(scpi);
+	sr_scpi_source_remove(sdi->session, scpi);
 
 	return SR_OK;
 }
@@ -801,5 +806,5 @@ SR_PRIV struct sr_dev_driver hameg_hmo_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/hameg-hmo/protocol.c b/src/hardware/hameg-hmo/protocol.c
similarity index 63%
rename from hardware/hameg-hmo/protocol.c
rename to src/hardware/hameg-hmo/protocol.c
index afa6efe..de93e74 100644
--- a/hardware/hameg-hmo/protocol.c
+++ b/src/hardware/hameg-hmo/protocol.c
@@ -17,6 +17,10 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+#include "scpi.h"
 #include "protocol.h"
 
 static const char *hameg_scpi_dialect[] = {
@@ -45,21 +49,21 @@ static const char *hameg_scpi_dialect[] = {
 	[SCPI_CMD_SET_ANALOG_CHAN_STATE]    = ":CHAN%d:STAT %d",
 };
 
-static const int32_t hmo_hwcaps[] = {
+static const uint32_t hmo_devopts[] = {
 	SR_CONF_OSCILLOSCOPE,
-	SR_CONF_TRIGGER_SOURCE,
-	SR_CONF_TIMEBASE,
-	SR_CONF_NUM_TIMEBASE,
-	SR_CONF_TRIGGER_SLOPE,
-	SR_CONF_HORIZ_TRIGGERPOS,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_LIMIT_FRAMES,
+	SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_NUM_HDIV | SR_CONF_GET,
+	SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET,
 };
 
-static const int32_t hmo_analog_caps[] = {
-	SR_CONF_NUM_VDIV,
-	SR_CONF_COUPLING,
-	SR_CONF_VDIV,
+static const uint32_t hmo_analog_devopts[] = {
+	SR_CONF_NUM_VDIV | SR_CONF_GET,
+	SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
 static const char *hmo_coupling_options[] = {
@@ -166,6 +170,8 @@ static const uint64_t hmo_vdivs[][2] = {
 	{ 2, 1 },
 	{ 5, 1 },
 	{ 10, 1 },
+	{ 20, 1 },
+	{ 50, 1 },
 };
 
 static const char *scope_analog_channel_names[] = {
@@ -194,9 +200,11 @@ static const char *scope_digital_channel_names[] = {
 	"D15",
 };
 
-static struct scope_config scope_models[] = {
+static const struct scope_config scope_models[] = {
 	{
-		.name = {"HMO722", "HMO1022", "HMO1522", "HMO2022", NULL},
+		/* HMO2522/3032/3042/3052 support 16 digital channels but they're not supported yet. */
+		.name = {"HMO722", "HMO1022", "HMO1522", "HMO2022", "HMO2522",
+				"HMO3032", "HMO3042", "HMO3052", NULL},
 		.analog_channels = 2,
 		.digital_channels = 8,
 		.digital_pods = 1,
@@ -204,11 +212,11 @@ static struct scope_config scope_models[] = {
 		.analog_names = &scope_analog_channel_names,
 		.digital_names = &scope_digital_channel_names,
 
-		.hw_caps = &hmo_hwcaps,
-		.num_hwcaps = ARRAY_SIZE(hmo_hwcaps),
+		.devopts = &hmo_devopts,
+		.num_devopts = ARRAY_SIZE(hmo_devopts),
 
-		.analog_hwcaps = &hmo_analog_caps,
-		.num_analog_hwcaps = ARRAY_SIZE(hmo_analog_caps),
+		.analog_devopts = &hmo_analog_devopts,
+		.num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
 
 		.coupling_options = &hmo_coupling_options,
 		.trigger_sources = &hmo_compact2_trigger_sources,
@@ -226,7 +234,9 @@ static struct scope_config scope_models[] = {
 		.scpi_dialect = &hameg_scpi_dialect,
 	},
 	{
-		.name = {"HMO724", "HMO1024", "HMO1524", "HMO2024", NULL},
+		/* HMO2524/3034/3044/3054 support 16 digital channels but they're not supported yet. */
+		.name = {"HMO724", "HMO1024", "HMO1524", "HMO2024", "HMO2524",
+				"HMO3034", "HMO3044", "HMO3054", NULL},
 		.analog_channels = 4,
 		.digital_channels = 8,
 		.digital_pods = 1,
@@ -234,11 +244,11 @@ static struct scope_config scope_models[] = {
 		.analog_names = &scope_analog_channel_names,
 		.digital_names = &scope_digital_channel_names,
 
-		.hw_caps = &hmo_hwcaps,
-		.num_hwcaps = ARRAY_SIZE(hmo_hwcaps),
+		.devopts = &hmo_devopts,
+		.num_devopts = ARRAY_SIZE(hmo_devopts),
 
-		.analog_hwcaps = &hmo_analog_caps,
-		.num_analog_hwcaps = ARRAY_SIZE(hmo_analog_caps),
+		.analog_devopts = &hmo_analog_devopts,
+		.num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
 
 		.coupling_options = &hmo_coupling_options,
 		.trigger_sources = &hmo_compact4_trigger_sources,
@@ -257,13 +267,13 @@ static struct scope_config scope_models[] = {
 	},
 };
 
-static void scope_state_dump(struct scope_config *config,
+static void scope_state_dump(const struct scope_config *config,
 			     struct scope_state *state)
 {
 	unsigned int i;
 	char *tmp;
 
-	for (i = 0; i < config->analog_channels; ++i) {
+	for (i = 0; i < config->analog_channels; i++) {
 		tmp = sr_voltage_string((*config->vdivs)[state->analog_channels[i].vdiv][0],
 					     (*config->vdivs)[state->analog_channels[i].vdiv][1]);
 		sr_info("State of analog channel  %d -> %s : %s (coupling) %s (vdiv) %2.2e (offset)",
@@ -272,12 +282,12 @@ static void scope_state_dump(struct scope_config *config,
 			tmp, state->analog_channels[i].vertical_offset);
 	}
 
-	for (i = 0; i < config->digital_channels; ++i) {
+	for (i = 0; i < config->digital_channels; i++) {
 		sr_info("State of digital channel %d -> %s", i,
 			state->digital_channels[i] ? "On" : "Off");
 	}
 
-	for (i = 0; i < config->digital_pods; ++i) {
+	for (i = 0; i < config->digital_pods; i++) {
 		sr_info("State of digital POD %d -> %s", i,
 			state->digital_pods[i] ? "On" : "Off");
 	}
@@ -308,7 +318,7 @@ static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi,
 		return SR_ERR;
 	}
 
-	for (i = 0; (*array)[i]; ++i) {
+	for (i = 0; (*array)[i]; i++) {
 		if (!g_strcmp0(tmp, (*array)[i])) {
 			*result = i;
 			g_free(tmp);
@@ -325,15 +335,85 @@ static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi,
 	return SR_OK;
 }
 
+/**
+ * This function takes a value of the form "2.000E-03", converts it to a
+ * significand / factor pair and returns the index of an array where
+ * a matching pair was found.
+ *
+ * It's a bit convoluted because of floating-point issues. The value "10.00E-09"
+ * is parsed by g_ascii_strtod() as 0.000000009999999939, for example.
+ * Therefore it's easier to break the number up into two strings and handle
+ * them separately.
+ *
+ * @param value The string to be parsed.
+ * @param array The array of s/f pairs.
+ * @param array_len The number of pairs in the array.
+ * @param result The index at which a matching pair was found.
+ *
+ * @return SR_ERR on any parsing error, SR_OK otherwise.
+ */
+static int array_float_get(gchar *value, const uint64_t array[][2],
+		int array_len, unsigned int *result)
+{
+	int i, e;
+	size_t pos;
+	uint64_t f;
+	float s;
+	unsigned int s_int;
+	gchar ss[10], es[10];
+
+	memset(ss, 0, sizeof(ss));
+	memset(es, 0, sizeof(es));
+
+	/* Get index of the separating 'E' character and break up the string. */
+	pos = strcspn(value, "E");
+
+	strncpy(ss, value, pos);
+	strncpy(es, &(value[pos+1]), 3);
+
+	if (sr_atof_ascii(ss, &s) != SR_OK)
+		return SR_ERR;
+	if (sr_atoi(es, &e) != SR_OK)
+		return SR_ERR;
+
+	/* Transform e.g. 10^-03 to 1000 as the array stores the inverse. */
+	f = pow(10, abs(e));
+
+	/*
+	 * Adjust the significand/factor pair to make sure
+	 * that f is a multiple of 1000.
+	 */
+	while ((int)fmod(log10(f), 3) > 0) {
+		s *= 10;
+
+		if (e < 0)
+			f *= 10;
+		else
+			f /= 10;
+	}
+
+	/* Truncate s to circumvent rounding errors. */
+	s_int = (unsigned int)s;
+
+	for (i = 0; i < array_len; i++) {
+		if ((s_int == array[i][0]) && (f == array[i][1])) {
+			*result = i;
+			return SR_OK;
+		}
+	}
+
+	return SR_ERR;
+}
+
 static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
-				    struct scope_config *config,
+				    const struct scope_config *config,
 				    struct scope_state *state)
 {
 	unsigned int i, j;
-	float tmp_float;
 	char command[MAX_COMMAND_SIZE];
+	char *tmp_str;
 
-	for (i = 0; i < config->analog_channels; ++i) {
+	for (i = 0; i < config->analog_channels; i++) {
 		g_snprintf(command, sizeof(command),
 			   (*config->scpi_dialect)[SCPI_CMD_GET_ANALOG_CHAN_STATE],
 			   i + 1);
@@ -346,17 +426,18 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
 			   (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_DIV],
 			   i + 1);
 
-		if (sr_scpi_get_float(scpi, command, &tmp_float) != SR_OK)
+		if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK)
 			return SR_ERR;
-		for (j = 0; j < config->num_vdivs; j++) {
-			if (tmp_float == ((float) (*config->vdivs)[j][0] /
-					  (*config->vdivs)[j][1])) {
-				state->analog_channels[i].vdiv = j;
-				break;
-			}
-		}
-		if (i == config->num_vdivs)
+
+		if (array_float_get(tmp_str, hmo_vdivs, ARRAY_SIZE(hmo_vdivs),
+				&j) != SR_OK) {
+			g_free(tmp_str);
+			sr_err("Could not determine array index for vertical div scale.");
 			return SR_ERR;
+		}
+
+		g_free(tmp_str);
+		state->analog_channels[i].vdiv = j;
 
 		g_snprintf(command, sizeof(command),
 			   (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_OFFSET],
@@ -379,13 +460,13 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
 }
 
 static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi,
-				     struct scope_config *config,
+				     const struct scope_config *config,
 				     struct scope_state *state)
 {
 	unsigned int i;
 	char command[MAX_COMMAND_SIZE];
 
-	for (i = 0; i < config->digital_channels; ++i) {
+	for (i = 0; i < config->digital_channels; i++) {
 		g_snprintf(command, sizeof(command),
 			   (*config->scpi_dialect)[SCPI_CMD_GET_DIG_CHAN_STATE],
 			   i);
@@ -395,7 +476,7 @@ static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi,
 			return SR_ERR;
 	}
 
-	for (i = 0; i < config->digital_pods; ++i) {
+	for (i = 0; i < config->digital_pods; i++) {
 		g_snprintf(command, sizeof(command),
 			   (*config->scpi_dialect)[SCPI_CMD_GET_DIG_POD_STATE],
 			   i + 1);
@@ -412,7 +493,7 @@ SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
 	struct scope_state *state;
-	struct scope_config *config;
+	const struct scope_config *config;
 
 	int tmp;
 	unsigned int i;
@@ -426,7 +507,7 @@ SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi)
 	state = devc->model_state;
 	channel_found = FALSE;
 
-	for (i = 0; i < config->analog_channels; ++i) {
+	for (i = 0; i < config->analog_channels; i++) {
 		if (state->analog_channels[i].state) {
 			g_snprintf(chan_name, sizeof(chan_name), "CHAN%d", i + 1);
 			g_snprintf(tmp_str, sizeof(tmp_str),
@@ -474,9 +555,10 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
 	struct scope_state *state;
-	struct scope_config *config;
+	const struct scope_config *config;
 	float tmp_float;
 	unsigned int i;
+	char *tmp_str;
 
 	devc = sdi->priv;
 	config = devc->model_config;
@@ -495,16 +577,20 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
 			&tmp_float) != SR_OK)
 		return SR_ERR;
 
-	for (i = 0; i < config->num_timebases; i++) {
-		if (tmp_float == ((float) (*config->timebases)[i][0] /
-				  (*config->timebases)[i][1])) {
-			state->timebase = i;
-			break;
-		}
-	}
-	if (i == config->num_timebases)
+	if (sr_scpi_get_string(sdi->conn,
+			(*config->scpi_dialect)[SCPI_CMD_GET_TIMEBASE],
+			&tmp_str) != SR_OK)
 		return SR_ERR;
 
+	if (array_float_get(tmp_str, hmo_timebases, ARRAY_SIZE(hmo_timebases),
+			&i) != SR_OK) {
+		g_free(tmp_str);
+		sr_err("Could not determine array index for time base.");
+		return SR_ERR;
+	}
+
+	state->timebase = i;
+
 	if (sr_scpi_get_float(sdi->conn,
 			(*config->scpi_dialect)[SCPI_CMD_GET_HORIZ_TRIGGERPOS],
 			&tmp_float) != SR_OK)
@@ -535,37 +621,19 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static struct scope_state *scope_state_new(struct scope_config *config)
+static struct scope_state *scope_state_new(const struct scope_config *config)
 {
 	struct scope_state *state;
 
-	if (!(state = g_try_malloc0(sizeof(struct scope_state))))
-		return NULL;
-
-	if (!(state->analog_channels = g_try_malloc0_n(config->analog_channels,
-				    sizeof(struct analog_channel_state))))
-	    goto fail;
-
-	if (!(state->digital_channels = g_try_malloc0_n(
-			config->digital_channels, sizeof(gboolean))))
-	    goto fail;
-
-	if (!(state->digital_pods = g_try_malloc0_n(config->digital_pods,
-						     sizeof(gboolean))))
-	    goto fail;
+	state = g_malloc0(sizeof(struct scope_state));
+	state->analog_channels = g_malloc0_n(config->analog_channels,
+			sizeof(struct analog_channel_state));
+	state->digital_channels = g_malloc0_n(
+			config->digital_channels, sizeof(gboolean));
+	state->digital_pods = g_malloc0_n(config->digital_pods,
+			sizeof(gboolean));
 
 	return state;
-
-fail:
-	if (state->analog_channels)
-		g_free(state->analog_channels);
-	if (state->digital_channels)
-		g_free(state->digital_channels);
-	if (state->digital_pods)
-		g_free(state->digital_pods);
-	g_free(state);
-
-	return NULL;
 }
 
 SR_PRIV void hmo_scope_state_free(struct scope_state *state)
@@ -604,46 +672,45 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
 		return SR_ERR_NA;
 	}
 
-	if (!(devc->analog_groups = g_try_malloc0(sizeof(struct sr_channel_group) *
-						  scope_models[model_index].analog_channels)))
-			return SR_ERR_MALLOC;
+	devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) *
+					scope_models[model_index].analog_channels);
 
-	if (!(devc->digital_groups = g_try_malloc0(sizeof(struct sr_channel_group) *
-						   scope_models[model_index].digital_pods)))
-			return SR_ERR_MALLOC;
+	devc->digital_groups = g_malloc0(sizeof(struct sr_channel_group*) *
+					 scope_models[model_index].digital_pods);
 
 	/* Add analog channels. */
 	for (i = 0; i < scope_models[model_index].analog_channels; i++) {
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
-			   (*scope_models[model_index].analog_names)[i])))
-			return SR_ERR_MALLOC;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE,
+			   (*scope_models[model_index].analog_names)[i]);
 
-		devc->analog_groups[i].name =
-			(char *)(*scope_models[model_index].analog_names)[i];
-		devc->analog_groups[i].channels = g_slist_append(NULL, ch);
+		devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
+
+		devc->analog_groups[i]->name = g_strdup(
+			(char *)(*scope_models[model_index].analog_names)[i]);
+		devc->analog_groups[i]->channels = g_slist_append(NULL, ch);
 
 		sdi->channel_groups = g_slist_append(sdi->channel_groups,
-						   &devc->analog_groups[i]);
+						   devc->analog_groups[i]);
 	}
 
 	/* Add digital channel groups. */
-	for (i = 0; i < scope_models[model_index].digital_pods; ++i) {
+	for (i = 0; i < scope_models[model_index].digital_pods; i++) {
 		g_snprintf(tmp, 25, "POD%d", i);
-		devc->digital_groups[i].name = g_strdup(tmp);
+
+		devc->digital_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
+
+		devc->digital_groups[i]->name = g_strdup(tmp);
 		sdi->channel_groups = g_slist_append(sdi->channel_groups,
-				   &devc->digital_groups[i < 8 ? 0 : 1]);
+				   devc->digital_groups[i < 8 ? 0 : 1]);
 	}
 
 	/* Add digital channels. */
 	for (i = 0; i < scope_models[model_index].digital_channels; i++) {
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-			   (*scope_models[model_index].digital_names)[i])))
-			return SR_ERR_MALLOC;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE,
+			   (*scope_models[model_index].digital_names)[i]);
 
-		devc->digital_groups[i < 8 ? 0 : 1].channels = g_slist_append(
-			devc->digital_groups[i < 8 ? 0 : 1].channels, ch);
+		devc->digital_groups[i < 8 ? 0 : 1]->channels = g_slist_append(
+			devc->digital_groups[i < 8 ? 0 : 1]->channels, ch);
 	}
 
 	devc->model_config = &scope_models[model_index];
@@ -662,79 +729,83 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
 	GArray *data;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct sr_datafeed_logic logic;
 
 	(void)fd;
 
+	data = NULL;
+
 	if (!(sdi = cb_data))
 		return TRUE;
 
 	if (!(devc = sdi->priv))
 		return TRUE;
 
-	if (revents == G_IO_IN) {
-		ch = devc->current_channel->data;
+	if (revents != G_IO_IN)
+		return TRUE;
 
-		switch (ch->type) {
-		case SR_CHANNEL_ANALOG:
-			if (sr_scpi_get_floatv(sdi->conn, NULL, &data) != SR_OK) {
-				if (data)
-					g_array_free(data, TRUE);
+	ch = devc->current_channel->data;
 
-				return TRUE;
-			}
+	switch (ch->type) {
+	case SR_CHANNEL_ANALOG:
+		if (sr_scpi_get_floatv(sdi->conn, NULL, &data) != SR_OK) {
+			if (data)
+				g_array_free(data, TRUE);
 
-			packet.type = SR_DF_FRAME_BEGIN;
-			sr_session_send(sdi, &packet);
-
-			analog.channels = g_slist_append(NULL, ch);
-			analog.num_samples = data->len;
-			analog.data = (float *) data->data;
-			analog.mq = SR_MQ_VOLTAGE;
-			analog.unit = SR_UNIT_VOLT;
-			analog.mqflags = 0;
-			packet.type = SR_DF_ANALOG;
-			packet.payload = &analog;
-			sr_session_send(cb_data, &packet);
-			g_slist_free(analog.channels);
-			g_array_free(data, TRUE);
-			break;
-		case SR_CHANNEL_LOGIC:
-			if (sr_scpi_get_uint8v(sdi->conn, NULL, &data) != SR_OK) {
-				if (data)
-					g_free(data);
-				return TRUE;
-			}
+			return TRUE;
+		}
 
-			packet.type = SR_DF_FRAME_BEGIN;
-			sr_session_send(sdi, &packet);
+		packet.type = SR_DF_FRAME_BEGIN;
+		sr_session_send(sdi, &packet);
 
-			logic.length = data->len;
-			logic.unitsize = 1;
-			logic.data = data->data;
-			packet.type = SR_DF_LOGIC;
-			packet.payload = &logic;
-			sr_session_send(cb_data, &packet);
-			g_array_free(data, TRUE);
-			break;
-		default:
-			sr_err("Invalid channel type.");
-			break;
+		analog.channels = g_slist_append(NULL, ch);
+		analog.num_samples = data->len;
+		analog.data = (float *) data->data;
+		analog.mq = SR_MQ_VOLTAGE;
+		analog.unit = SR_UNIT_VOLT;
+		analog.mqflags = 0;
+		packet.type = SR_DF_ANALOG_OLD;
+		packet.payload = &analog;
+		sr_session_send(cb_data, &packet);
+		g_slist_free(analog.channels);
+		g_array_free(data, TRUE);
+		data = NULL;
+		break;
+	case SR_CHANNEL_LOGIC:
+		if (sr_scpi_get_uint8v(sdi->conn, NULL, &data) != SR_OK) {
+			g_free(data);
+			return TRUE;
 		}
 
-		packet.type = SR_DF_FRAME_END;
+		packet.type = SR_DF_FRAME_BEGIN;
 		sr_session_send(sdi, &packet);
 
-		if (devc->current_channel->next) {
-			devc->current_channel = devc->current_channel->next;
-			hmo_request_data(sdi);
-		} else if (++devc->num_frames == devc->frame_limit) {
-			sdi->driver->dev_acquisition_stop(sdi, cb_data);
-		} else {
-			devc->current_channel = devc->enabled_channels;
-			hmo_request_data(sdi);
-		}
+		logic.length = data->len;
+		logic.unitsize = 1;
+		logic.data = data->data;
+		packet.type = SR_DF_LOGIC;
+		packet.payload = &logic;
+		sr_session_send(cb_data, &packet);
+		g_array_free(data, TRUE);
+		data = NULL;
+		break;
+	default:
+		sr_err("Invalid channel type.");
+		break;
+	}
+
+	packet.type = SR_DF_FRAME_END;
+	sr_session_send(sdi, &packet);
+
+	if (devc->current_channel->next) {
+		devc->current_channel = devc->current_channel->next;
+		hmo_request_data(sdi);
+	} else if (++devc->num_frames == devc->frame_limit) {
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+	} else {
+		devc->current_channel = devc->enabled_channels;
+		hmo_request_data(sdi);
 	}
 
 	return TRUE;
diff --git a/hardware/hameg-hmo/protocol.h b/src/hardware/hameg-hmo/protocol.h
similarity index 90%
rename from hardware/hameg-hmo/protocol.h
rename to src/hardware/hameg-hmo/protocol.h
index bdf70fc..3747d62 100644
--- a/hardware/hameg-hmo/protocol.h
+++ b/src/hardware/hameg-hmo/protocol.h
@@ -23,7 +23,7 @@
 #include <glib.h>
 #include <stdint.h>
 #include <string.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "hameg-hmo"
@@ -40,11 +40,11 @@ struct scope_config {
 	const char *(*analog_names)[];
 	const char *(*digital_names)[];
 
-	const int32_t (*hw_caps)[];
-	const uint8_t num_hwcaps;
+	const uint32_t (*devopts)[];
+	const uint8_t num_devopts;
 
-	const int32_t (*analog_hwcaps)[];
-	const uint8_t num_analog_hwcaps;
+	const uint32_t (*analog_devopts)[];
+	const uint8_t num_analog_devopts;
 
 	const char *(*coupling_options)[];
 	const uint8_t num_coupling_options;
@@ -90,11 +90,11 @@ struct scope_state {
 
 /** Private, per-device-instance driver context. */
 struct dev_context {
-	void *model_config;
+	const void *model_config;
 	void *model_state;
 
-	struct sr_channel_group *analog_groups;
-	struct sr_channel_group *digital_groups;
+	struct sr_channel_group **analog_groups;
+	struct sr_channel_group **digital_groups;
 
 	GSList *enabled_channels;
 	GSList *current_channel;
diff --git a/hardware/hantek-dso/api.c b/src/hardware/hantek-dso/api.c
similarity index 62%
rename from hardware/hantek-dso/api.c
rename to src/hardware/hantek-dso/api.c
index 76696d5..5fe431d 100644
--- a/hardware/hantek-dso/api.c
+++ b/src/hardware/hantek-dso/api.c
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -29,7 +30,7 @@
 #include <inttypes.h>
 #include <glib.h>
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "dso.h"
 
@@ -40,63 +41,71 @@
 #define NUM_TIMEBASE  10
 #define NUM_VDIV      8
 
-static const int32_t scanopts[] = {
+#define NUM_BUFFER_SIZES 2
+
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 };
 
-static const int32_t devopts[] = {
+static const uint32_t drvopts[] = {
 	SR_CONF_OSCILLOSCOPE,
-	SR_CONF_LIMIT_FRAMES,
-	SR_CONF_CONTINUOUS,
-	SR_CONF_TIMEBASE,
-	SR_CONF_BUFFERSIZE,
-	SR_CONF_TRIGGER_SOURCE,
-	SR_CONF_TRIGGER_SLOPE,
-	SR_CONF_HORIZ_TRIGGERPOS,
-	SR_CONF_FILTER,
-	SR_CONF_VDIV,
-	SR_CONF_COUPLING,
-	SR_CONF_NUM_TIMEBASE,
-	SR_CONF_NUM_VDIV,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
+	SR_CONF_CONN | SR_CONF_GET,
+	SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_BUFFERSIZE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_NUM_HDIV | SR_CONF_GET,
+	SR_CONF_NUM_VDIV | SR_CONF_GET,
+};
+
+static const uint32_t devopts_cg[] = {
+	SR_CONF_FILTER | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
 static const char *channel_names[] = {
 	"CH1", "CH2",
-	NULL,
 };
 
 static const uint64_t buffersizes_32k[] = {
-	10240, 32768,
+	(10 * 1024), (32 * 1024),
 };
 static const uint64_t buffersizes_512k[] = {
-	10240, 524288,
+	(10 * 1024), (512 * 1024),
 };
 static const uint64_t buffersizes_14k[] = {
-	10240, 14336,
+	(10 * 1024), (14 * 1024),
 };
 
 static const struct dso_profile dev_profiles[] = {
 	{	0x04b4, 0x2090, 0x04b5, 0x2090,
 		"Hantek", "DSO-2090",
 		buffersizes_32k,
-		FIRMWARE_DIR "/hantek-dso-2090.fw" },
+		"hantek-dso-2090.fw" },
 	{	0x04b4, 0x2150, 0x04b5, 0x2150,
 		"Hantek", "DSO-2150",
 		buffersizes_32k,
-		FIRMWARE_DIR "/hantek-dso-2150.fw" },
+		"hantek-dso-2150.fw" },
 	{	0x04b4, 0x2250, 0x04b5, 0x2250,
 		"Hantek", "DSO-2250",
 		buffersizes_512k,
-		FIRMWARE_DIR "/hantek-dso-2250.fw" },
+		"hantek-dso-2250.fw" },
 	{	0x04b4, 0x5200, 0x04b5, 0x5200,
 		"Hantek", "DSO-5200",
 		buffersizes_14k,
-		FIRMWARE_DIR "/hantek-dso-5200.fw" },
+		"hantek-dso-5200.fw" },
 	{	0x04b4, 0x520a, 0x04b5, 0x520a,
 		"Hantek", "DSO-5200A",
 		buffersizes_512k,
-		FIRMWARE_DIR "/hantek-dso-5200A.fw" },
-	{ 0, 0, 0, 0, 0, 0, 0, 0 },
+		"hantek-dso-5200A.fw" },
+	ALL_ZERO
 };
 
 static const uint64_t timebases[][2] = {
@@ -140,10 +149,9 @@ static const char *trigger_sources[] = {
 	/* TODO: forced */
 };
 
-static const char *filter_targets[] = {
-	"CH1",
-	"CH2",
-	/* TODO: "TRIGGER", */
+static const char *trigger_slopes[] = {
+	"r",
+	"f",
 };
 
 static const char *coupling[] = {
@@ -153,49 +161,46 @@ static const char *coupling[] = {
 };
 
 SR_PRIV struct sr_dev_driver hantek_dso_driver_info;
-static struct sr_dev_driver *di = &hantek_dso_driver_info;
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
 
-static struct sr_dev_inst *dso_dev_new(int index, const struct dso_profile *prof)
+static struct sr_dev_inst *dso_dev_new(const struct dso_profile *prof)
 {
 	struct sr_dev_inst *sdi;
 	struct sr_channel *ch;
+	struct sr_channel_group *cg;
 	struct drv_context *drvc;
 	struct dev_context *devc;
-	int i;
+	unsigned int i;
 
-	sdi = sr_dev_inst_new(index, SR_ST_INITIALIZING,
-		prof->vendor, prof->model, NULL);
-	if (!sdi)
-		return NULL;
-	sdi->driver = di;
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INITIALIZING;
+	sdi->vendor = g_strdup(prof->vendor);
+	sdi->model = g_strdup(prof->model);
+	sdi->driver = &hantek_dso_driver_info;
 
 	/*
 	 * Add only the real channels -- EXT isn't a source of data, only
 	 * a trigger source internal to the device.
 	 */
-	for (i = 0; channel_names[i]; i++) {
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
-				channel_names[i])))
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-	}
-
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		return NULL;
+	for (i = 0; i < ARRAY_SIZE(channel_names); i++) {
+		ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_names[i]);
+		cg = g_malloc0(sizeof(struct sr_channel_group));
+		cg->name = g_strdup(channel_names[i]);
+		cg->channels = g_slist_append(cg->channels, ch);
+		sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
 	}
 
+	devc = g_malloc0(sizeof(struct dev_context));
 	devc->profile = prof;
 	devc->dev_state = IDLE;
 	devc->timebase = DEFAULT_TIMEBASE;
 	devc->ch1_enabled = TRUE;
 	devc->ch2_enabled = TRUE;
-	devc->voltage_ch1 = DEFAULT_VOLTAGE;
-	devc->voltage_ch2 = DEFAULT_VOLTAGE;
-	devc->coupling_ch1 = DEFAULT_COUPLING;
-	devc->coupling_ch2 = DEFAULT_COUPLING;
+	devc->voltage[0] = DEFAULT_VOLTAGE;
+	devc->voltage[1] = DEFAULT_VOLTAGE;
+	devc->coupling[0] = DEFAULT_COUPLING;
+	devc->coupling[1] = DEFAULT_COUPLING;
 	devc->voffset_ch1 = DEFAULT_VERT_OFFSET;
 	devc->voffset_ch2 = DEFAULT_VERT_OFFSET;
 	devc->voffset_trigger = DEFAULT_VERT_TRIGGERPOS;
@@ -204,7 +209,7 @@ static struct sr_dev_inst *dso_dev_new(int index, const struct dso_profile *prof
 	devc->triggersource = g_strdup(DEFAULT_TRIGGER_SOURCE);
 	devc->triggerposition = DEFAULT_HORIZ_TRIGGERPOS;
 	sdi->priv = devc;
-	drvc = di->priv;
+	drvc = hantek_dso_driver_info.context;
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 
 	return sdi;
@@ -244,17 +249,17 @@ static void clear_dev_context(void *priv)
 
 }
 
-static int dev_clear(void)
+static int dev_clear(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, clear_dev_context);
 }
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
@@ -265,12 +270,12 @@ static GSList *scan(GSList *options)
 	GSList *l, *devices, *conn_devices;
 	struct libusb_device_descriptor des;
 	libusb_device **devlist;
-	int devcnt, ret, i, j;
+	int i, j;
 	const char *conn;
+	char connection_id[64];
 
-	drvc = di->priv;
+	drvc = di->context;
 
-	devcnt = 0;
 	devices = 0;
 
 	conn = NULL;
@@ -303,11 +308,9 @@ static GSList *scan(GSList *options)
 				continue;
 		}
 
-		if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
-			sr_err("Failed to get device descriptor: %s.",
-					libusb_error_name(ret));
-			continue;
-		}
+		libusb_get_device_descriptor(devlist[i], &des);
+
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
 
 		prof = NULL;
 		for (j = 0; dev_profiles[j].orig_vid; j++) {
@@ -316,35 +319,33 @@ static GSList *scan(GSList *options)
 				/* Device matches the pre-firmware profile. */
 				prof = &dev_profiles[j];
 				sr_dbg("Found a %s %s.", prof->vendor, prof->model);
-				sdi = dso_dev_new(devcnt, prof);
+				sdi = dso_dev_new(prof);
+				sdi->connection_id = g_strdup(connection_id);
 				devices = g_slist_append(devices, sdi);
 				devc = sdi->priv;
-				if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
-						prof->firmware) == SR_OK)
+				if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
+						USB_CONFIGURATION, prof->firmware) == SR_OK)
 					/* Remember when the firmware on this device was updated */
 					devc->fw_updated = g_get_monotonic_time();
 				else
-					sr_err("Firmware upload failed for "
-					        "device %d.", devcnt);
+					sr_err("Firmware upload failed");
 				/* Dummy USB address of 0xff will get overwritten later. */
 				sdi->conn = sr_usb_dev_inst_new(
 						libusb_get_bus_number(devlist[i]), 0xff, NULL);
-				devcnt++;
 				break;
 			} else if (des.idVendor == dev_profiles[j].fw_vid
 				&& des.idProduct == dev_profiles[j].fw_pid) {
 				/* Device matches the post-firmware profile. */
 				prof = &dev_profiles[j];
 				sr_dbg("Found a %s %s.", prof->vendor, prof->model);
-				sdi = dso_dev_new(devcnt, prof);
+				sdi = dso_dev_new(prof);
+				sdi->connection_id = g_strdup(connection_id);
 				sdi->status = SR_ST_INACTIVE;
 				devices = g_slist_append(devices, sdi);
-				devc = sdi->priv;
 				sdi->inst_type = SR_INST_USB;
 				sdi->conn = sr_usb_dev_inst_new(
 						libusb_get_bus_number(devlist[i]),
 						libusb_get_device_address(devlist[i]), NULL);
-				devcnt++;
 				break;
 			}
 		}
@@ -357,9 +358,9 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -390,7 +391,7 @@ static int dev_open(struct sr_dev_inst *sdi)
 			timediff_ms = timediff_us / 1000;
 			sr_spew("Waited %" PRIi64 " ms.", timediff_ms);
 		}
-		sr_info("Device came back after %d ms.", timediff_ms);
+		sr_info("Device came back after %" PRIi64 " ms.", timediff_ms);
 	} else {
 		err = dso_open(sdi);
 	}
@@ -417,176 +418,212 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
-	return dev_clear();
+	return dev_clear(di);
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
+	struct dev_context *devc;
 	struct sr_usb_dev_inst *usb;
 	char str[128];
+	const char *s;
+	const uint64_t *vdiv;
+	int ch_idx;
 
-	(void)cg;
-
-	switch (id) {
-	case SR_CONF_CONN:
-		if (!sdi || !sdi->conn)
-			return SR_ERR_ARG;
-		usb = sdi->conn;
-		if (usb->address == 255)
-			/* Device still needs to re-enumerate after firmware
-			 * upload, so we don't know its (future) address. */
-			return SR_ERR;
-		snprintf(str, 128, "%d.%d", usb->bus, usb->address);
-		*data = g_variant_new_string(str);
-		break;
-	case SR_CONF_NUM_TIMEBASE:
+	switch (key) {
+	case SR_CONF_NUM_HDIV:
 		*data = g_variant_new_int32(NUM_TIMEBASE);
 		break;
 	case SR_CONF_NUM_VDIV:
 		*data = g_variant_new_int32(NUM_VDIV);
 		break;
-	default:
-		return SR_ERR_NA;
+	}
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+	if (!cg) {
+		switch (key) {
+		case SR_CONF_CONN:
+			if (!sdi->conn)
+				return SR_ERR_ARG;
+			usb = sdi->conn;
+			if (usb->address == 255)
+				/* Device still needs to re-enumerate after firmware
+				 * upload, so we don't know its (future) address. */
+				return SR_ERR;
+			snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+			*data = g_variant_new_string(str);
+			break;
+		case SR_CONF_TIMEBASE:
+			*data = g_variant_new("(tt)", timebases[devc->timebase][0],
+					timebases[devc->timebase][1]);
+			break;
+		case SR_CONF_BUFFERSIZE:
+			*data = g_variant_new_uint64(devc->framesize);
+			break;
+		case SR_CONF_TRIGGER_SOURCE:
+			*data = g_variant_new_string(devc->triggersource);
+			break;
+		case SR_CONF_TRIGGER_SLOPE:
+			s = (devc->triggerslope == SLOPE_POSITIVE) ? "r" : "f";
+			*data = g_variant_new_string(s);
+			break;
+		case SR_CONF_HORIZ_TRIGGERPOS:
+			*data = g_variant_new_double(devc->triggerposition);
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	} else {
+		if (sdi->channel_groups->data == cg)
+			ch_idx = 0;
+		else if (sdi->channel_groups->next->data == cg)
+			ch_idx = 1;
+		else
+			return SR_ERR_ARG;
+		switch (key) {
+		case SR_CONF_FILTER:
+			*data = g_variant_new_boolean(devc->filter[ch_idx]);
+			break;
+		case SR_CONF_VDIV:
+			vdiv = vdivs[devc->voltage[ch_idx]];
+			*data = g_variant_new("(tt)", vdiv[0], vdiv[1]);
+			break;
+		case SR_CONF_COUPLING:
+			*data = g_variant_new_string(coupling[devc->coupling[ch_idx]]);
+			break;
+		}
 	}
 
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
 	double tmp_double;
 	uint64_t tmp_u64, p, q;
-	int tmp_int, ret;
+	int tmp_int, ch_idx, ret;
 	unsigned int i;
 	const char *tmp_str;
-	char **targets;
-
-	(void)cg;
 
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
 	ret = SR_OK;
 	devc = sdi->priv;
-	switch (id) {
-	case SR_CONF_LIMIT_FRAMES:
-		devc->limit_frames = g_variant_get_uint64(data);
-		break;
-	case SR_CONF_TRIGGER_SLOPE:
-		tmp_str = g_variant_get_string(data, NULL);
-		if (!tmp_str || !(tmp_str[0] == 'f' || tmp_str[0] == 'r'))
-			return SR_ERR_ARG;
-		devc->triggerslope = (tmp_str[0] == 'r')
-			? SLOPE_POSITIVE : SLOPE_NEGATIVE;
-		break;
-	case SR_CONF_HORIZ_TRIGGERPOS:
-		tmp_double = g_variant_get_double(data);
-		if (tmp_double < 0.0 || tmp_double > 1.0) {
-			sr_err("Trigger position should be between 0.0 and 1.0.");
-			ret = SR_ERR_ARG;
-		} else
-			devc->triggerposition = tmp_double;
-		break;
-	case SR_CONF_BUFFERSIZE:
-		tmp_u64 = g_variant_get_uint64(data);
-		for (i = 0; i < 2; i++) {
-			if (devc->profile->buffersizes[i] == tmp_u64) {
-				devc->framesize = tmp_u64;
-				break;
-			}
-		}
-		if (i == 2)
-			ret = SR_ERR_ARG;
-		break;
-	case SR_CONF_TIMEBASE:
-		g_variant_get(data, "(tt)", &p, &q);
-		tmp_int = -1;
-		for (i = 0; i < ARRAY_SIZE(timebases); i++) {
-			if (timebases[i][0] == p && timebases[i][1] == q) {
-				tmp_int = i;
-				break;
+	if (!cg) {
+		switch (key) {
+		case SR_CONF_LIMIT_FRAMES:
+			devc->limit_frames = g_variant_get_uint64(data);
+			break;
+		case SR_CONF_TRIGGER_SLOPE:
+			tmp_str = g_variant_get_string(data, NULL);
+			if (!tmp_str || !(tmp_str[0] == 'f' || tmp_str[0] == 'r'))
+				return SR_ERR_ARG;
+			devc->triggerslope = (tmp_str[0] == 'r')
+				? SLOPE_POSITIVE : SLOPE_NEGATIVE;
+			break;
+		case SR_CONF_HORIZ_TRIGGERPOS:
+			tmp_double = g_variant_get_double(data);
+			if (tmp_double < 0.0 || tmp_double > 1.0) {
+				sr_err("Trigger position should be between 0.0 and 1.0.");
+				ret = SR_ERR_ARG;
+			} else
+				devc->triggerposition = tmp_double;
+			break;
+		case SR_CONF_BUFFERSIZE:
+			tmp_u64 = g_variant_get_uint64(data);
+			for (i = 0; i < NUM_BUFFER_SIZES; i++) {
+				if (devc->profile->buffersizes[i] == tmp_u64) {
+					devc->framesize = tmp_u64;
+					break;
+				}
 			}
-		}
-		if (tmp_int >= 0)
-			devc->timebase = tmp_int;
-		else
-			ret = SR_ERR_ARG;
-		break;
-	case SR_CONF_TRIGGER_SOURCE:
-		tmp_str = g_variant_get_string(data, NULL);
-		for (i = 0; trigger_sources[i]; i++) {
-			if (!strcmp(tmp_str, trigger_sources[i])) {
-				devc->triggersource = g_strdup(tmp_str);
-				break;
+			if (i == NUM_BUFFER_SIZES)
+				ret = SR_ERR_ARG;
+			break;
+		case SR_CONF_TIMEBASE:
+			g_variant_get(data, "(tt)", &p, &q);
+			tmp_int = -1;
+			for (i = 0; i < ARRAY_SIZE(timebases); i++) {
+				if (timebases[i][0] == p && timebases[i][1] == q) {
+					tmp_int = i;
+					break;
+				}
 			}
-		}
-		if (trigger_sources[i] == 0)
-			ret = SR_ERR_ARG;
-		break;
-	case SR_CONF_FILTER:
-		tmp_str = g_variant_get_string(data, NULL);
-		devc->filter_ch1 = devc->filter_ch2 = devc->filter_trigger = 0;
-		targets = g_strsplit(tmp_str, ",", 0);
-		for (i = 0; targets[i]; i++) {
-			if (targets[i] == '\0')
-				/* Empty filter string can be used to clear them all. */
-				;
-			else if (!strcmp(targets[i], "CH1"))
-				devc->filter_ch1 = TRUE;
-			else if (!strcmp(targets[i], "CH2"))
-				devc->filter_ch2 = TRUE;
-			else if (!strcmp(targets[i], "TRIGGER"))
-				devc->filter_trigger = TRUE;
-			else {
-				sr_err("Invalid filter target %s.", targets[i]);
+			if (tmp_int >= 0)
+				devc->timebase = tmp_int;
+			else
 				ret = SR_ERR_ARG;
+			break;
+		case SR_CONF_TRIGGER_SOURCE:
+			tmp_str = g_variant_get_string(data, NULL);
+			for (i = 0; trigger_sources[i]; i++) {
+				if (!strcmp(tmp_str, trigger_sources[i])) {
+					devc->triggersource = g_strdup(tmp_str);
+					break;
+				}
 			}
+			if (trigger_sources[i] == 0)
+				ret = SR_ERR_ARG;
+			break;
+		default:
+			ret = SR_ERR_NA;
+			break;
 		}
-		g_strfreev(targets);
-		break;
-	case SR_CONF_VDIV:
-		/* TODO: Not supporting vdiv per channel yet. */
-		g_variant_get(data, "(tt)", &p, &q);
-		tmp_int = -1;
-		for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
-			if (vdivs[i][0] == p && vdivs[i][1] == q) {
-				tmp_int = i;
-				break;
+	} else {
+		if (sdi->channel_groups->data == cg)
+			ch_idx = 0;
+		else if (sdi->channel_groups->next->data == cg)
+			ch_idx = 1;
+		else
+			return SR_ERR_ARG;
+		switch (key) {
+		case SR_CONF_FILTER:
+			devc->filter[ch_idx] = g_variant_get_boolean(data);
+			break;
+		case SR_CONF_VDIV:
+			g_variant_get(data, "(tt)", &p, &q);
+			tmp_int = -1;
+			for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+				if (vdivs[i][0] == p && vdivs[i][1] == q) {
+					tmp_int = i;
+					break;
+				}
 			}
-		}
-		if (tmp_int >= 0) {
-			devc->voltage_ch1 = tmp_int;
-			devc->voltage_ch2 = tmp_int;
-		} else
-			ret = SR_ERR_ARG;
-		break;
-	case SR_CONF_COUPLING:
-		tmp_str = g_variant_get_string(data, NULL);
-		/* TODO: Not supporting coupling per channel yet. */
-		for (i = 0; coupling[i]; i++) {
-			if (!strcmp(tmp_str, coupling[i])) {
-				devc->coupling_ch1 = i;
-				devc->coupling_ch2 = i;
-				break;
+			if (tmp_int >= 0) {
+				devc->voltage[ch_idx] = tmp_int;
+			} else
+				ret = SR_ERR_ARG;
+			break;
+		case SR_CONF_COUPLING:
+			tmp_str = g_variant_get_string(data, NULL);
+			for (i = 0; coupling[i]; i++) {
+				if (!strcmp(tmp_str, coupling[i])) {
+					devc->coupling[ch_idx] = i;
+					break;
+				}
 			}
+			if (coupling[i] == 0)
+				ret = SR_ERR_ARG;
+			break;
+		default:
+			ret = SR_ERR_NA;
+			break;
 		}
-		if (coupling[i] == 0)
-			ret = SR_ERR_ARG;
-		break;
-	default:
-		ret = SR_ERR_NA;
-		break;
 	}
 
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -594,57 +631,75 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	GVariantBuilder gvb;
 	unsigned int i;
 
-	(void)cg;
+	if (key == SR_CONF_SCAN_OPTIONS) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	} else if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		return SR_OK;
+	}
 
-	switch (key) {
-	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				scanopts, ARRAY_SIZE(scanopts), sizeof(int32_t));
-		break;
-	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				devopts, ARRAY_SIZE(devopts), sizeof(int32_t));
-		break;
-	case SR_CONF_BUFFERSIZE:
-		if (!sdi)
-			return SR_ERR_ARG;
-		devc = sdi->priv;
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
-				devc->profile->buffersizes, 2, sizeof(uint64_t));
-		break;
-	case SR_CONF_COUPLING:
-		*data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
-		break;
-	case SR_CONF_VDIV:
-		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-		for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
-			rational[0] = g_variant_new_uint64(vdivs[i][0]);
-			rational[1] = g_variant_new_uint64(vdivs[i][1]);
-			tuple = g_variant_new_tuple(rational, 2);
-			g_variant_builder_add_value(&gvb, tuple);
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	if (!cg) {
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+			break;
+		case SR_CONF_BUFFERSIZE:
+			if (!sdi)
+				return SR_ERR_ARG;
+			devc = sdi->priv;
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
+					devc->profile->buffersizes, NUM_BUFFER_SIZES, sizeof(uint64_t));
+			break;
+		case SR_CONF_TIMEBASE:
+			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+			for (i = 0; i < ARRAY_SIZE(timebases); i++) {
+				rational[0] = g_variant_new_uint64(timebases[i][0]);
+				rational[1] = g_variant_new_uint64(timebases[i][1]);
+				tuple = g_variant_new_tuple(rational, 2);
+				g_variant_builder_add_value(&gvb, tuple);
+			}
+			*data = g_variant_builder_end(&gvb);
+			break;
+		case SR_CONF_TRIGGER_SOURCE:
+			*data = g_variant_new_strv(trigger_sources,
+					ARRAY_SIZE(trigger_sources));
+			break;
+		case SR_CONF_TRIGGER_SLOPE:
+			*data = g_variant_new_strv(trigger_slopes,
+					ARRAY_SIZE(trigger_slopes));
+			break;
+		default:
+			return SR_ERR_NA;
 		}
-		*data = g_variant_builder_end(&gvb);
-		break;
-	case SR_CONF_FILTER:
-		*data = g_variant_new_strv(filter_targets,
-				ARRAY_SIZE(filter_targets));
-		break;
-	case SR_CONF_TIMEBASE:
-		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-		for (i = 0; i < ARRAY_SIZE(timebases); i++) {
-			rational[0] = g_variant_new_uint64(timebases[i][0]);
-			rational[1] = g_variant_new_uint64(timebases[i][1]);
-			tuple = g_variant_new_tuple(rational, 2);
-			g_variant_builder_add_value(&gvb, tuple);
+	} else {
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
+			break;
+		case SR_CONF_COUPLING:
+			*data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+			break;
+		case SR_CONF_VDIV:
+			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+			for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+				rational[0] = g_variant_new_uint64(vdivs[i][0]);
+				rational[1] = g_variant_new_uint64(vdivs[i][1]);
+				tuple = g_variant_new_tuple(rational, 2);
+				g_variant_builder_add_value(&gvb, tuple);
+			}
+			*data = g_variant_builder_end(&gvb);
+			break;
+		default:
+			return SR_ERR_NA;
 		}
-		*data = g_variant_builder_end(&gvb);
-		break;
-	case SR_CONF_TRIGGER_SOURCE:
-		*data = g_variant_new_strv(trigger_sources,
-				ARRAY_SIZE(trigger_sources));
-		break;
-	default:
-		return SR_ERR_NA;
 	}
 
 	return SR_OK;
@@ -654,20 +709,21 @@ static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
 		int num_samples)
 {
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct dev_context *devc;
 	float ch1, ch2, range;
 	int num_channels, data_offset, i;
 
 	devc = sdi->priv;
 	num_channels = (devc->ch1_enabled && devc->ch2_enabled) ? 2 : 1;
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	/* TODO: support for 5xxx series 9-bit samples */
 	analog.channels = devc->enabled_channels;
 	analog.num_samples = num_samples;
 	analog.mq = SR_MQ_VOLTAGE;
 	analog.unit = SR_UNIT_VOLT;
+	analog.mqflags = 0;
 	/* TODO: Check malloc return value. */
 	analog.data = g_try_malloc(analog.num_samples * sizeof(float) * num_channels);
 	data_offset = 0;
@@ -686,20 +742,21 @@ static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
 		 */
 		/* TODO: Support for DSO-5xxx series 9-bit samples. */
 		if (devc->ch1_enabled) {
-			range = ((float)vdivs[devc->voltage_ch1][0] / vdivs[devc->voltage_ch1][1]) * 8;
+			range = ((float)vdivs[devc->voltage[0]][0] / vdivs[devc->voltage[0]][1]) * 8;
 			ch1 = range / 255 * *(buf + i * 2 + 1);
 			/* Value is centered around 0V. */
 			ch1 -= range / 2;
 			analog.data[data_offset++] = ch1;
 		}
 		if (devc->ch2_enabled) {
-			range = ((float)vdivs[devc->voltage_ch2][0] / vdivs[devc->voltage_ch2][1]) * 8;
+			range = ((float)vdivs[devc->voltage[1]][0] / vdivs[devc->voltage[1]][1]) * 8;
 			ch2 = range / 255 * *(buf + i * 2);
 			ch2 -= range / 2;
 			analog.data[data_offset++] = ch2;
 		}
 	}
 	sr_session_send(devc->cb_data, &packet);
+	g_free(analog.data);
 }
 
 /*
@@ -708,7 +765,7 @@ static void send_chunk(struct sr_dev_inst *sdi, unsigned char *buf,
  * queued up beforehand, so this just needs to chuck the incoming data onto
  * the libsigrok session bus.
  */
-static void receive_transfer(struct libusb_transfer *transfer)
+static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
 {
 	struct sr_datafeed_packet packet;
 	struct sr_dev_inst *sdi;
@@ -717,8 +774,8 @@ static void receive_transfer(struct libusb_transfer *transfer)
 
 	sdi = transfer->user_data;
 	devc = sdi->priv;
-	sr_spew("receive_transfer(): status %d received %d bytes.",
-		   transfer->status, transfer->actual_length);
+	sr_spew("receive_transfer(): status %s received %d bytes.",
+		libusb_error_name(transfer->status), transfer->actual_length);
 
 	if (transfer->actual_length == 0)
 		/* Nothing to send to the bus. */
@@ -768,8 +825,7 @@ static void receive_transfer(struct libusb_transfer *transfer)
 		}
 	} else {
 		/* Already past the trigger point, just send it all out. */
-		send_chunk(sdi, transfer->buffer,
-				num_samples);
+		send_chunk(sdi, transfer->buffer, num_samples);
 	}
 
 	devc->samp_received += num_samples;
@@ -804,8 +860,9 @@ static int handle_event(int fd, int revents, void *cb_data)
 	const struct sr_dev_inst *sdi;
 	struct sr_datafeed_packet packet;
 	struct timeval tv;
+	struct sr_dev_driver *di;
 	struct dev_context *devc;
-	struct drv_context *drvc = di->priv;
+	struct drv_context *drvc;
 	int num_channels;
 	uint32_t trigger_offset;
 	uint8_t capturestate;
@@ -814,6 +871,8 @@ static int handle_event(int fd, int revents, void *cb_data)
 	(void)revents;
 
 	sdi = cb_data;
+	di = sdi->driver;
+	drvc = di->context;
 	devc = sdi->priv;
 	if (devc->dev_state == STOPPING) {
 		/* We've been told to wind up the acquisition. */
@@ -822,7 +881,7 @@ static int handle_event(int fd, int revents, void *cb_data)
 		 * TODO: Doesn't really cancel pending transfers so they might
 		 * come in after SR_DF_END is sent.
 		 */
-		usb_source_remove(drvc->sr_ctx);
+		usb_source_remove(sdi->session, drvc->sr_ctx);
 
 		packet.type = SR_DF_END;
 		sr_session_send(sdi, &packet);
@@ -877,8 +936,7 @@ static int handle_event(int fd, int revents, void *cb_data)
 		devc->trigger_offset = trigger_offset;
 
 		num_channels = (devc->ch1_enabled && devc->ch2_enabled) ? 2 : 1;
-		/* TODO: Check malloc return value. */
-		devc->framebuf = g_try_malloc(devc->framesize * num_channels * 2);
+		devc->framebuf = g_malloc(devc->framesize * num_channels * 2);
 		devc->samp_buffered = devc->samp_received = 0;
 
 		/* Tell the scope to send us the first frame. */
@@ -913,7 +971,8 @@ static int handle_event(int fd, int revents, void *cb_data)
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
 	struct dev_context *devc;
-	struct drv_context *drvc = di->priv;
+	struct sr_dev_driver *di = sdi->driver;
+	struct drv_context *drvc = di->context;
 
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
@@ -933,7 +992,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		return SR_ERR;
 
 	devc->dev_state = CAPTURE;
-	usb_source_add(drvc->sr_ctx, TICK, handle_event, (void *)sdi);
+	usb_source_add(sdi->session, drvc->sr_ctx, TICK, handle_event, (void *)sdi);
 
 	/* Send header packet to the session bus. */
 	std_session_send_df_header(cb_data, LOG_PREFIX);
@@ -972,5 +1031,5 @@ SR_PRIV struct sr_dev_driver hantek_dso_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/hantek-dso/dso.c b/src/hardware/hantek-dso/dso.c
similarity index 90%
rename from hardware/hantek-dso/dso.c
rename to src/hardware/hantek-dso/dso.c
index da5eb12..8561ac3 100644
--- a/hardware/hantek-dso/dso.c
+++ b/src/hardware/hantek-dso/dso.c
@@ -19,12 +19,15 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-#include "dso.h"
+#include <config.h>
 #include <string.h>
 #include <glib.h>
 #include <libusb.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "dso.h"
+
+#define NUM_CHANNELS 2
 
 extern struct sr_dev_driver hantek_dso_driver_info;
 
@@ -73,8 +76,7 @@ static int dso_getmps(libusb_device *dev)
 	const struct libusb_interface_descriptor *intf_dsc;
 	int mps;
 
-	if (libusb_get_device_descriptor(dev, &des) != 0)
-		return 0;
+	libusb_get_device_descriptor(dev, &des);
 
 	if (des.bNumConfigurations != 1)
 		return 0;
@@ -109,11 +111,12 @@ err:
 SR_PRIV int dso_open(struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
-	struct drv_context *drvc = hantek_dso_driver_info.priv;
+	struct drv_context *drvc = hantek_dso_driver_info.context;
 	struct sr_usb_dev_inst *usb;
 	struct libusb_device_descriptor des;
 	libusb_device **devlist;
-	int err, skip, i;
+	int err, i;
+	char connection_id[64];
 
 	devc = sdi->priv;
 	usb = sdi->conn;
@@ -122,33 +125,22 @@ SR_PRIV int dso_open(struct sr_dev_inst *sdi)
 		/* already in use */
 		return SR_ERR;
 
-	skip = 0;
 	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
 	for (i = 0; devlist[i]; i++) {
-		if ((err = libusb_get_device_descriptor(devlist[i], &des))) {
-			sr_err("Failed to get device descriptor: %s.",
-			       libusb_error_name(err));
-			continue;
-		}
+		libusb_get_device_descriptor(devlist[i], &des);
 
 		if (des.idVendor != devc->profile->fw_vid
 		    || des.idProduct != devc->profile->fw_pid)
 			continue;
 
-		if (sdi->status == SR_ST_INITIALIZING) {
-			if (skip != sdi->index) {
-				/* Skip devices of this type that aren't the one we want. */
-				skip += 1;
-				continue;
-			}
-		} else if (sdi->status == SR_ST_INACTIVE) {
+		if ((sdi->status == SR_ST_INITIALIZING) ||
+				(sdi->status == SR_ST_INACTIVE)) {
 			/*
-			 * This device is fully enumerated, so we need to find
-			 * this device by vendor, product, bus and address.
+			 * Check device by its physical USB bus/port address.
 			 */
-			if (libusb_get_bus_number(devlist[i]) != usb->bus
-				|| libusb_get_device_address(devlist[i]) != usb->address)
-				/* this is not the one */
+			usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+			if (strcmp(sdi->connection_id, connection_id))
+				/* This is not the one. */
 				continue;
 		}
 
@@ -164,9 +156,10 @@ SR_PRIV int dso_open(struct sr_dev_inst *sdi)
 				sr_err("Wrong endpoint profile.");
 			else {
 				sdi->status = SR_ST_ACTIVE;
-				sr_info("Opened device %d on %d.%d interface %d.",
-					sdi->index, usb->bus,
-					usb->address, USB_INTERFACE);
+				sr_info("Opened device on %d.%d (logical) / "
+						"%s (physical) interface %d.",
+					usb->bus, usb->address,
+					sdi->connection_id, USB_INTERFACE);
 			}
 		} else {
 			sr_err("Failed to open device: %s.",
@@ -190,11 +183,11 @@ SR_PRIV void dso_close(struct sr_dev_inst *sdi)
 
 	usb = sdi->conn;
 
-	if (usb->devhdl == NULL)
+	if (!usb->devhdl)
 		return;
 
-	sr_info("Closing device %d on %d.%d interface %d.", sdi->index,
-		usb->bus, usb->address, USB_INTERFACE);
+	sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
+			usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
 	libusb_release_interface(usb->devhdl, USB_INTERFACE);
 	libusb_close(usb->devhdl);
 	usb->devhdl = NULL;
@@ -230,7 +223,7 @@ static int get_channel_offsets(const struct sr_dev_inst *sdi)
 	 * since that's how voltage offsets are submitted back to the DSO.
 	 * Convert to host order now, so we can use them natively.
 	 */
-	for (chan = 0; chan < 2; chan++) {
+	for (chan = 0; chan < NUM_CHANNELS; chan++) {
 		for (v = 0; v < 9; v++) {
 			devc->channel_levels[chan][v][0] =
 				g_ntohs(devc->channel_levels[chan][v][0]);
@@ -241,7 +234,7 @@ static int get_channel_offsets(const struct sr_dev_inst *sdi)
 
 	if (sr_log_loglevel_get() >= SR_LOG_DBG) {
 		gs = g_string_sized_new(128);
-		for (chan = 0; chan < 2; chan++) {
+		for (chan = 0; chan < NUM_CHANNELS; chan++) {
 			g_string_printf(gs, "CH%d:", chan + 1);
 			for (v = 0; v < 9; v++) {
 				g_string_append_printf(gs, " %.4x-%.4x",
@@ -388,19 +381,18 @@ static int dso_set_filters(const struct sr_dev_inst *sdi)
 	memset(cmdstring, 0, sizeof(cmdstring));
 	cmdstring[0] = CMD_SET_FILTERS;
 	cmdstring[1] = 0x0f;
-	if (devc->filter_ch1) {
+	if (devc->filter[0]) {
 		sr_dbg("Turning on CH1 filter.");
 		cmdstring[2] |= 0x80;
 	}
-	if (devc->filter_ch2) {
+	if (devc->filter[1]) {
 		sr_dbg("Turning on CH2 filter.");
 		cmdstring[2] |= 0x40;
 	}
-	if (devc->filter_trigger) {
-		/* TODO: supported on the DSO-2090? */
-		sr_dbg("Turning on trigger filter.");
-		cmdstring[2] |= 0x20;
-	}
+	/*
+	 * Not supported: filtering on the trigger
+	 * cmdstring[2] |= 0x20;
+	 */
 
 	if (send_begin(sdi) != SR_OK)
 		return SR_ERR;
@@ -433,8 +425,8 @@ static int dso_set_voltage(const struct sr_dev_inst *sdi)
 	cmdstring[2] = 0x30;
 
 	/* CH1 volts/div is encoded in bits 0-1 */
-	sr_dbg("CH1 vdiv index: %d.", devc->voltage_ch1);
-	switch (devc->voltage_ch1) {
+	sr_dbg("CH1 vdiv index: %d.", devc->voltage[0]);
+	switch (devc->voltage[0]) {
 	case VDIV_1V:
 	case VDIV_100MV:
 	case VDIV_10MV:
@@ -453,8 +445,8 @@ static int dso_set_voltage(const struct sr_dev_inst *sdi)
 	}
 
 	/* CH2 volts/div is encoded in bits 2-3 */
-	sr_dbg("CH2 vdiv index: %d.", devc->voltage_ch2);
-	switch (devc->voltage_ch2) {
+	sr_dbg("CH2 vdiv index: %d.", devc->voltage[1]);
+	switch (devc->voltage[1]) {
 	case VDIV_1V:
 	case VDIV_100MV:
 	case VDIV_10MV:
@@ -499,24 +491,24 @@ static int dso_set_relays(const struct sr_dev_inst *sdi)
 	devc = sdi->priv;
 	usb = sdi->conn;
 
-	if (devc->voltage_ch1 < VDIV_1V)
+	if (devc->voltage[0] < VDIV_1V)
 		relays[1] = ~relays[1];
 
-	if (devc->voltage_ch1 < VDIV_100MV)
+	if (devc->voltage[0] < VDIV_100MV)
 		relays[2] = ~relays[2];
 
-	sr_dbg("CH1 coupling: %d.", devc->coupling_ch1);
-	if (devc->coupling_ch1 != COUPLING_AC)
+	sr_dbg("CH1 coupling: %d.", devc->coupling[0]);
+	if (devc->coupling[0] != COUPLING_AC)
 		relays[3] = ~relays[3];
 
-	if (devc->voltage_ch2 < VDIV_1V)
+	if (devc->voltage[1] < VDIV_1V)
 		relays[4] = ~relays[4];
 
-	if (devc->voltage_ch2 < VDIV_100MV)
+	if (devc->voltage[1] < VDIV_100MV)
 		relays[5] = ~relays[5];
 
-	sr_dbg("CH2 coupling: %d.", devc->coupling_ch1);
-	if (devc->coupling_ch2 != COUPLING_AC)
+	sr_dbg("CH2 coupling: %d.", devc->coupling[1]);
+	if (devc->coupling[1] != COUPLING_AC)
 		relays[6] = ~relays[6];
 
 	if (!strcmp(devc->triggersource, "EXT"))
@@ -557,7 +549,7 @@ static int dso_set_voffsets(const struct sr_dev_inst *sdi)
 
 	memset(offsets, 0, sizeof(offsets));
 	/* Channel 1 */
-	ch_levels = devc->channel_levels[0][devc->voltage_ch1];
+	ch_levels = devc->channel_levels[0][devc->voltage[0]];
 	offset = (ch_levels[1] - ch_levels[0]) * devc->voffset_ch1 + ch_levels[0];
 	offsets[0] = (offset >> 8) | 0x20;
 	offsets[1] = offset & 0xff;
@@ -565,7 +557,7 @@ static int dso_set_voffsets(const struct sr_dev_inst *sdi)
 	       offsets[0], offsets[1]);
 
 	/* Channel 2 */
-	ch_levels = devc->channel_levels[1][devc->voltage_ch2];
+	ch_levels = devc->channel_levels[1][devc->voltage[1]];
 	offset = (ch_levels[1] - ch_levels[0]) * devc->voffset_ch2 + ch_levels[0];
 	offsets[2] = (offset >> 8) | 0x20;
 	offsets[3] = offset & 0xff;
@@ -709,7 +701,7 @@ SR_PRIV int dso_get_capturestate(const struct sr_dev_inst *sdi,
 	bitvalue = 1;
 	for (i = 0; i < 24; i++) {
 		/* Each set bit inverts all bits with a lower value. */
-		if(toff & bitvalue)
+		if (toff & bitvalue)
 			toff ^= bitvalue - 1;
 		bitvalue <<= 1;
 	}
diff --git a/hardware/hantek-dso/dso.h b/src/hardware/hantek-dso/dso.h
similarity index 95%
rename from hardware/hantek-dso/dso.h
rename to src/hardware/hantek-dso/dso.h
index edccb72..44cced4 100644
--- a/hardware/hantek-dso/dso.h
+++ b/src/hardware/hantek-dso/dso.h
@@ -49,7 +49,7 @@
 #define EEPROM_CHANNEL_OFFSETS  0x08
 
 /* All models have this for their "fast" mode. */
-#define FRAMESIZE_SMALL         10240
+#define FRAMESIZE_SMALL         (10 * 1024)
 
 enum control_requests {
 	CTRL_READ_EEPROM = 0xa2,
@@ -154,10 +154,10 @@ struct dso_profile {
 	/* VID/PID after firmware upload */
 	uint16_t fw_vid;
 	uint16_t fw_pid;
-	char *vendor;
-	char *model;
+	const char *vendor;
+	const char *model;
 	const uint64_t *buffersizes;
-	char *firmware;
+	const char *firmware;
 };
 
 struct dev_context {
@@ -180,19 +180,15 @@ struct dev_context {
 	int timebase;
 	gboolean ch1_enabled;
 	gboolean ch2_enabled;
-	int voltage_ch1;
-	int voltage_ch2;
-	int coupling_ch1;
-	int coupling_ch2;
+	int voltage[2];
+	int coupling[2];
 	// voltage offset (vertical position)
 	float voffset_ch1;
 	float voffset_ch2;
 	float voffset_trigger;
 	uint16_t channel_levels[2][9][2];
 	unsigned int framesize;
-	gboolean filter_ch1;
-	gboolean filter_ch2;
-	gboolean filter_trigger;
+	gboolean filter[2];
 	int triggerslope;
 	char *triggersource;
 	float triggerposition;
diff --git a/src/hardware/hung-chang-dso-2100/api.c b/src/hardware/hung-chang-dso-2100/api.c
new file mode 100644
index 0000000..45fff2b
--- /dev/null
+++ b/src/hardware/hung-chang-dso-2100/api.c
@@ -0,0 +1,764 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Daniel Glöckner <daniel-gl at gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <ieee1284.h>
+#include <string.h>
+#include "protocol.h"
+
+SR_PRIV struct sr_dev_driver hung_chang_dso_2100_driver_info;
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+	SR_CONF_OSCILLOSCOPE,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONN | SR_CONF_GET,
+	SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_BUFFERSIZE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint32_t cgopts[] = {
+	SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_PROBE_FACTOR | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint64_t samplerates[] = {
+	SR_MHZ(100), SR_MHZ(50),  SR_MHZ(25),   SR_MHZ(20),
+	SR_MHZ(10),  SR_MHZ(5),   SR_KHZ(2500), SR_MHZ(2),
+	SR_MHZ(1),   SR_KHZ(500), SR_KHZ(250),  SR_KHZ(200),
+	SR_KHZ(100), SR_KHZ(50),  SR_KHZ(25),   SR_KHZ(20),
+	SR_KHZ(10),  SR_KHZ(5),   SR_HZ(2500),  SR_KHZ(2),
+	SR_KHZ(1),   SR_HZ(500),  SR_HZ(250),   SR_HZ(200),
+	SR_HZ(100),  SR_HZ(50),   SR_HZ(25),    SR_HZ(20)
+};
+
+/* must be in sync with readout_steps[] in protocol.c */
+static const uint64_t buffersizes[] = {
+	2 * 500, 3 * 500, 4 * 500, 5 * 500,
+	6 * 500, 7 * 500, 8 * 500, 9 * 500, 10 * 500,
+	12 * 500 - 2, 14 * 500 - 2, 16 * 500 - 2,
+	18 * 500 - 2, 20 * 500 - 2, 10240 - 2
+};
+
+static const uint64_t vdivs[][2] = {
+	{ 10, 1000 },
+	{ 20, 1000 },
+	{ 50, 1000 },
+	{ 100, 1000 },
+	{ 200, 1000 },
+	{ 500, 1000 },
+	{ 1, 1 },
+	{ 2, 1 },
+	{ 5, 1 },
+};
+
+/* Bits 4 and 5 enable relays that add /10 filters to the chain
+ * Bits 0 and 1 select an output from a resistor array */
+static const uint8_t vdivs_map[] = {
+	0x01, 0x02, 0x03, 0x21, 0x22, 0x23, 0x31, 0x32, 0x33
+};
+
+
+static const char *trigger_sources[] = {
+	"A", "B", "EXT"
+};
+
+static const uint8_t trigger_sources_map[] = {
+	0x00, 0x80, 0x40
+};
+
+static const char *trigger_slopes[] = {
+	"f", "r"
+};
+
+static const char *coupling[] = {
+	"DC", "AC", "GND"
+};
+
+static const uint8_t coupling_map[] = {
+	0x00, 0x08, 0x04
+};
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan_port(GSList *devices, struct sr_dev_driver *di,
+			 struct parport *port)
+{
+	struct sr_dev_inst *sdi;
+	struct sr_channel *ch;
+	struct sr_channel_group *cg;
+	struct dev_context *devc;
+	struct drv_context *drvc;
+	int i;
+
+	if (ieee1284_open(port, 0, &i) != E1284_OK) {
+		sr_err("Can't open parallel port %s", port->name);
+		goto fail1;
+	}
+
+	if ((i & (CAP1284_RAW | CAP1284_BYTE)) != (CAP1284_RAW | CAP1284_BYTE)) {
+		sr_err("Parallel port %s does not provide low-level bidirection access",
+		       port->name);
+		goto fail2;
+	}
+
+	if (ieee1284_claim(port) != E1284_OK) {
+		sr_err("Parallel port %s already in use", port->name);
+		goto fail2;
+	}
+
+	if (!hung_chang_dso_2100_check_id(port))
+		goto fail3;
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("Hung-Chang");
+	sdi->model = g_strdup("DSO-2100");
+	sdi->driver = di;
+	drvc = di->context;
+	sdi->inst_type = 0; /* FIXME */
+	sdi->conn = port;
+	ieee1284_ref(port);
+
+	for (i = 0; i < NUM_CHANNELS; i++) {
+		cg = g_malloc0(sizeof(struct sr_channel_group));
+		cg->name = g_strdup(trigger_sources[i]);
+		ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, FALSE, trigger_sources[i]);
+		cg->channels = g_slist_append(cg->channels, ch);
+		sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+	}
+
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->enabled_channel = g_slist_append(NULL, NULL);
+	devc->channel = 0;
+	devc->rate = 0;
+	devc->probe[0] = 10;
+	devc->probe[1] = 10;
+	devc->cctl[0] = 0x31; /* 1V/div, DC coupling, trigger on channel A*/
+	devc->cctl[1] = 0x31; /* 1V/div, DC coupling, no tv sync trigger */
+	devc->edge = 0;
+	devc->tlevel = 0x80;
+	devc->pos[0] = 0x80;
+	devc->pos[1] = 0x80;
+	devc->offset[0] = 0x80;
+	devc->offset[1] = 0x80;
+	devc->gain[0] = 0x80;
+	devc->gain[1] = 0x80;
+	devc->frame_limit = 0;
+	devc->last_step = 0; /* buffersize = 1000 */
+	sdi->priv = devc;
+
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+	devices = g_slist_append(devices, sdi);
+
+fail3:
+	ieee1284_release(port);
+fail2:
+	ieee1284_close(port);
+fail1:
+	return devices;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct parport_list ports;
+	struct sr_config *src;
+	const char *conn = NULL;
+	GSList *devices, *option;
+	gboolean port_found;
+	int i;
+
+
+	for (option = options; option; option = option->next) {
+		src = option->data;
+		if (src->key == SR_CONF_CONN) {
+			conn = g_variant_get_string(src->data, NULL);
+			break;
+		}
+	}
+
+	if (!conn)
+		return NULL;
+
+	if (ieee1284_find_ports(&ports, 0) != E1284_OK)
+		return NULL;
+
+	devices = NULL;
+	port_found = FALSE;
+	for (i = 0; i < ports.portc; i++)
+		if (!strcmp(ports.portv[i]->name, conn)) {
+			port_found = TRUE;
+			devices = scan_port(devices, di, ports.portv[i]);
+		}
+
+	if (!port_found) {
+		sr_err("Parallel port %s not found. Valid names are:", conn);
+		for (i = 0; i < ports.portc; i++)
+			sr_err("\t%s", ports.portv[i]->name);
+	}
+
+	ieee1284_free_ports(&ports);
+
+	return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static void clear_private(void *priv)
+{
+	struct dev_context *devc = priv;
+
+	g_slist_free(devc->enabled_channel);
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	struct drv_context *drvc = di->context;
+	struct sr_dev_inst *sdi;
+	GSList *l;
+
+	if (drvc) {
+		for (l = drvc->instances; l; l = l->next) {
+			sdi = l->data;
+			ieee1284_unref(sdi->conn);
+		}
+	}
+
+	return std_dev_clear(di, clear_private);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+	int i;
+
+	if (sdi->status != SR_ST_INACTIVE)
+		goto fail1;
+
+	if (ieee1284_open(sdi->conn, 0, &i) != E1284_OK)
+		goto fail1;
+
+	if (ieee1284_claim(sdi->conn) != E1284_OK)
+		goto fail2;
+
+	if (ieee1284_data_dir(sdi->conn, 1) != E1284_OK)
+		goto fail3;
+
+	if (hung_chang_dso_2100_move_to(sdi, 1))
+		goto fail3;
+
+	devc->samples = g_try_malloc(1000 * sizeof(*devc->samples));
+	if (!devc->samples)
+		goto fail3;
+
+	sdi->status = SR_ST_ACTIVE;
+
+	return SR_OK;
+
+fail3:
+	hung_chang_dso_2100_reset_port(sdi->conn);
+	ieee1284_release(sdi->conn);
+fail2:
+	ieee1284_close(sdi->conn);
+fail1:
+	return SR_ERR;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_OK;
+
+	g_free(devc->samples);
+	hung_chang_dso_2100_reset_port(sdi->conn);
+	ieee1284_release(sdi->conn);
+	ieee1284_close(sdi->conn);
+	sdi->status = SR_ST_INACTIVE;
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	struct drv_context *drvc = di->context;
+	int ret;
+
+	ret = dev_clear(di);
+
+	g_free(drvc);
+
+	return ret;
+}
+
+static int find_in_array(GVariant *data, const GVariantType *type,
+			 const void *arr, int n)
+{
+	const char * const *sarr;
+	const char *s;
+	const uint64_t *u64arr;
+	const uint8_t *u8arr;
+	uint64_t u64;
+	uint8_t u8;
+	int i;
+
+	if (!g_variant_is_of_type(data, type))
+		return -1;
+
+	switch (g_variant_classify(data)) {
+	case G_VARIANT_CLASS_STRING:
+		s = g_variant_get_string(data, NULL);
+		sarr = arr;
+
+		for (i = 0; i < n; i++)
+			if (!strcmp(s, sarr[i]))
+				return i;
+		break;
+	case G_VARIANT_CLASS_UINT64:
+		u64 = g_variant_get_uint64(data);
+		u64arr = arr;
+
+		for (i = 0; i < n; i++)
+			if (u64 == u64arr[i])
+				return i;
+		break;
+	case G_VARIANT_CLASS_BYTE:
+		u8 = g_variant_get_byte(data);
+		u8arr = arr;
+
+		for (i = 0; i < n; i++)
+			if (u8 == u8arr[i])
+				return i;
+	default:
+		break;
+	}
+
+	return -1;
+}
+
+static int reverse_map(uint8_t u, const uint8_t *arr, int n)
+{
+	GVariant *v = g_variant_new_byte(u);
+	int i = find_in_array(v, G_VARIANT_TYPE_BYTE, arr, n);
+	g_variant_unref(v);
+	return i;
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc = sdi->priv;
+	struct parport *port;
+	int ret, i, ch = -1;
+
+	if (cg) /* sr_config_get will validate cg using config_list */
+		ch = ((struct sr_channel *)cg->channels->data)->index;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_CONN:
+		port = sdi->conn;
+		*data = g_variant_new_string(port->name);
+		break;
+	case SR_CONF_LIMIT_FRAMES:
+		*data = g_variant_new_uint64(devc->frame_limit);
+		break;
+	case SR_CONF_SAMPLERATE:
+		*data = g_variant_new_uint64(samplerates[devc->rate]);
+		break;
+	case SR_CONF_TRIGGER_SOURCE:
+		i = reverse_map(devc->cctl[0] & 0xC0, trigger_sources_map,
+				ARRAY_SIZE(trigger_sources_map));
+		if (i == -1)
+			ret = SR_ERR;
+		else
+			*data = g_variant_new_string(trigger_sources[i]);
+		break;
+	case SR_CONF_TRIGGER_SLOPE:
+		if (devc->edge >= ARRAY_SIZE(trigger_slopes))
+			ret = SR_ERR;
+		else
+			*data = g_variant_new_string(trigger_slopes[devc->edge]);
+		break;
+	case SR_CONF_BUFFERSIZE:
+		*data = g_variant_new_uint64(buffersizes[devc->last_step]);
+		break;
+	case SR_CONF_VDIV:
+		if (ch == -1) {
+			ret = SR_ERR_CHANNEL_GROUP;
+		} else {
+			i = reverse_map(devc->cctl[ch] & 0x33, vdivs_map,
+					ARRAY_SIZE(vdivs_map));
+			if (i == -1)
+				ret = SR_ERR;
+			else
+				*data = g_variant_new("(tt)", vdivs[i][0],
+						      vdivs[i][1]);
+		}
+		break;
+	case SR_CONF_COUPLING:
+		if (ch == -1) {
+			ret = SR_ERR_CHANNEL_GROUP;
+		} else {
+			i = reverse_map(devc->cctl[ch] & 0x0C, coupling_map,
+					ARRAY_SIZE(coupling_map));
+			if (i == -1)
+				ret = SR_ERR;
+			else
+				*data = g_variant_new_string(coupling[i]);
+		}
+		break;
+	case SR_CONF_PROBE_FACTOR:
+		if (ch == -1)
+			ret = SR_ERR_CHANNEL_GROUP;
+		else
+			*data = g_variant_new_uint64(devc->probe[ch]);
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc = sdi->priv;
+	int ret, i, ch = -1;
+	uint64_t u, v;
+
+	if (cg) /* sr_config_set will validate cg using config_list */
+		ch = ((struct sr_channel *)cg->channels->data)->index;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_LIMIT_FRAMES:
+		devc->frame_limit = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_SAMPLERATE:
+		i = find_in_array(data, G_VARIANT_TYPE_UINT64,
+				  samplerates, ARRAY_SIZE(samplerates));
+		if (i == -1)
+			ret = SR_ERR_ARG;
+		else
+			devc->rate = i;
+		break;
+	case SR_CONF_TRIGGER_SOURCE:
+		i = find_in_array(data, G_VARIANT_TYPE_STRING,
+				  trigger_sources, ARRAY_SIZE(trigger_sources));
+		if (i == -1)
+			ret = SR_ERR_ARG;
+		else
+			devc->cctl[0] = (devc->cctl[0] & 0x3F)
+				      | trigger_sources_map[i];
+		break;
+	case SR_CONF_TRIGGER_SLOPE:
+		i = find_in_array(data, G_VARIANT_TYPE_STRING,
+				  trigger_slopes, ARRAY_SIZE(trigger_slopes));
+		if (i == -1)
+			ret = SR_ERR_ARG;
+		else
+			devc->edge = i;
+		break;
+	case SR_CONF_BUFFERSIZE:
+		i = find_in_array(data, G_VARIANT_TYPE_UINT64,
+				  buffersizes, ARRAY_SIZE(buffersizes));
+		if (i == -1)
+			ret = SR_ERR_ARG;
+		else
+			devc->last_step = i;
+		break;
+	case SR_CONF_VDIV:
+		if (ch == -1) {
+			ret = SR_ERR_CHANNEL_GROUP;
+		} else if (!g_variant_is_of_type(data, G_VARIANT_TYPE("(tt)"))) {
+			ret = SR_ERR_ARG;
+		} else {
+			g_variant_get(data, "(tt)", &u, &v);
+			for (i = 0; i < (int)ARRAY_SIZE(vdivs); i++)
+				if (vdivs[i][0] == u && vdivs[i][1] == v)
+					break;
+			if (i == ARRAY_SIZE(vdivs))
+				ret = SR_ERR_ARG;
+			else
+				devc->cctl[ch] = (devc->cctl[ch] & 0xCC)
+					       | vdivs_map[i];
+		}
+		break;
+	case SR_CONF_COUPLING:
+		if (ch == -1) {
+			ret = SR_ERR_CHANNEL_GROUP;
+		} else {
+			i = find_in_array(data, G_VARIANT_TYPE_STRING,
+					  coupling, ARRAY_SIZE(coupling));
+			if (i == -1)
+				ret = SR_ERR_ARG;
+			else
+				devc->cctl[ch] = (devc->cctl[ch] & 0xF3)
+					       | coupling_map[i];
+		}
+		break;
+	case SR_CONF_PROBE_FACTOR:
+		if (ch == -1) {
+			ret = SR_ERR_CHANNEL_GROUP;
+		} else {
+			u = g_variant_get_uint64(data);
+			if (!u)
+				ret = SR_ERR_ARG;
+			else
+				devc->probe[ch] = u;
+		}
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_channel_set(const struct sr_dev_inst *sdi,
+			      struct sr_channel *ch,
+			      unsigned int changes)
+{
+	struct dev_context *devc = sdi->priv;
+	uint8_t v;
+
+	if (changes & SR_CHANNEL_SET_ENABLED) {
+		if (ch->enabled) {
+			v = devc->channel | (1 << ch->index);
+			if (v & (v - 1))
+				return SR_ERR;
+			devc->channel = v;
+			devc->enabled_channel->data = ch;
+		} else {
+			devc->channel &= ~(1 << ch->index);
+		}
+	}
+	return SR_OK;
+}
+
+static int config_commit(const struct sr_dev_inst *sdi)
+{
+	uint8_t state = hung_chang_dso_2100_read_mbox(sdi->conn, 0.02);
+	int ret;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	switch (state) {
+	case 0x03:
+	case 0x14:
+	case 0x21:
+		/* we will travel the complete config path on our way to state 1 */
+		break;
+	case 0x00:
+		state = 0x01;
+	default:
+		ret = hung_chang_dso_2100_move_to(sdi, 1);
+		if (ret != SR_OK)
+			return ret;
+	case 0x01:
+		hung_chang_dso_2100_write_mbox(sdi->conn, 4);
+	}
+	ret = hung_chang_dso_2100_move_to(sdi, 1);
+	if (ret != SR_OK)
+		return ret;
+	return hung_chang_dso_2100_move_to(sdi, state);
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	GVariantBuilder gvb;
+	GVariant *gvar, *rational[2];
+	GSList *l;
+	int i;
+
+	switch (key) {
+		case SR_CONF_SCAN_OPTIONS:
+	case SR_CONF_DEVICE_OPTIONS:
+		break;
+	case SR_CONF_SAMPLERATE:
+	case SR_CONF_TRIGGER_SOURCE:
+	case SR_CONF_TRIGGER_SLOPE:
+	case SR_CONF_BUFFERSIZE:
+		if (!sdi || cg)
+			return SR_ERR_NA;
+		break;
+	case SR_CONF_VDIV:
+	case SR_CONF_COUPLING:
+		if (!sdi)
+			return SR_ERR_NA;
+		if (!cg)
+			return SR_ERR_CHANNEL_GROUP;
+		l = g_slist_find(sdi->channel_groups, cg);
+		if (!l)
+			return SR_ERR_ARG;
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_DEVICE_OPTIONS:
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else if (!cg)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					cgopts, ARRAY_SIZE(cgopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_SAMPLERATE:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+		gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+				samplerates, ARRAY_SIZE(samplerates), sizeof(uint64_t));
+		g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+		*data = g_variant_builder_end(&gvb);
+		break;
+	case SR_CONF_TRIGGER_SOURCE:
+		*data = g_variant_new_strv(trigger_sources, ARRAY_SIZE(trigger_sources));
+		break;
+	case SR_CONF_TRIGGER_SLOPE:
+		*data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes));
+		break;
+	case SR_CONF_BUFFERSIZE:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
+				buffersizes, ARRAY_SIZE(buffersizes), sizeof(uint64_t));
+		break;
+	case SR_CONF_VDIV:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+		for (i = 0; i < (int)ARRAY_SIZE(vdivs); i++) {
+			rational[0] = g_variant_new_uint64(vdivs[i][0]);
+			rational[1] = g_variant_new_uint64(vdivs[i][1]);
+			gvar = g_variant_new_tuple(rational, 2);
+			g_variant_builder_add_value(&gvb, gvar);
+		}
+		*data = g_variant_builder_end(&gvb);
+		break;
+	case SR_CONF_COUPLING:
+		*data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+		break;
+	}
+
+	return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+		void *cb_data)
+{
+	struct dev_context *devc = sdi->priv;
+	int ret;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (devc->channel) {
+		static const float res_array[] = {0.5, 1, 2, 5};
+		static const uint8_t relays[] = {100, 10, 10, 1};
+		devc->factor = devc->probe[devc->channel - 1] / 32.0;
+		devc->factor *= res_array[devc->cctl[devc->channel - 1] & 0x03];
+		devc->factor /= relays[(devc->cctl[devc->channel - 1] >> 4) & 0x03];
+	}
+	devc->frame = 0;
+	devc->cb_data = cb_data;
+	devc->state_known = TRUE;
+	devc->step = 0;
+	devc->adc2 = FALSE;
+	devc->retries = MAX_RETRIES;
+
+	ret = hung_chang_dso_2100_move_to(sdi, 0x21);
+	if (ret != SR_OK)
+		return ret;
+
+	std_session_send_df_header(cb_data, LOG_PREFIX);
+
+	sr_session_source_add(sdi->session, -1, 0, 8,
+			      hung_chang_dso_2100_poll, (void *)sdi);
+
+	return SR_OK;
+}
+
+SR_PRIV int hung_chang_dso_2100_dev_acquisition_stop(const struct sr_dev_inst *sdi,
+		void *cb_data)
+{
+	struct sr_datafeed_packet packet = { .type = SR_DF_END };
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	sr_session_send(cb_data, &packet);
+	sr_session_source_remove(sdi->session, -1);
+
+	hung_chang_dso_2100_move_to(sdi, 1);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	return hung_chang_dso_2100_dev_acquisition_stop(sdi, cb_data);
+}
+
+SR_PRIV struct sr_dev_driver hung_chang_dso_2100_driver_info = {
+	.name = "hung-chang-dso-2100",
+	.longname = "Hung-Chang DSO-2100",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_channel_set = config_channel_set,
+	.config_commit = config_commit,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/hung-chang-dso-2100/protocol.c b/src/hardware/hung-chang-dso-2100/protocol.c
new file mode 100644
index 0000000..c1c8dac
--- /dev/null
+++ b/src/hardware/hung-chang-dso-2100/protocol.c
@@ -0,0 +1,464 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Daniel Glöckner <daniel-gl at gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <ieee1284.h>
+#include "protocol.h"
+
+/* The firmware can be in the following states:
+ *  0x00	Temporary state during initialization
+ *  		Automatically transitions to state 0x01
+ *  0x01	Idle, this state updates calibration caps
+ *		Send 0x02 to go to state 0x21
+ *		Send 0x03 to go to state 0x03
+ *		Send 0x04 to go to state 0x14
+ *  0x21	Trigger is armed, caps are _not_ updated
+ *  		Send 0x99 to check if trigger event occured
+ *  			if triggered, goes to state 0x03
+ *  			else stays in state 0x21
+ *  		Send 0xFE to generate artificial trigger event
+ *			returns to state 0x21
+ *			but next 0x99 will succeed
+ *		Send 0xFF to go to state 0x03 (abort capture)
+ *  0x03	Extracts two 500 sample subsets from the 5000
+ *  		sample capture buffer for readout
+ *  		When reading samples, the FPGA starts at the
+ *  		first of the 1000 samples and automatically
+ *  		advances to the next.
+ *  		Send 0x04 to go to state 0x0F
+ *  0x14	Scroll acquisition mode, update calib caps
+ *  		When reading samples, the FPGA provides the
+ *  		current value of the ADCs
+ *  		Send 0xFF to go to state 0x0F
+ *  0x0F	Send channel number (1 or 2) to go to next state
+ *  		There are actually two 0x0F states in series
+ *  		which both expect the channel number.
+ *  		If the values don't match, they are discarded.
+ *  		The next state 0x05 is entered anyway
+ *  0x05	Same as state 0x0F but expects sample rate index.
+ *  		The next state is 0x08
+ *  0x08	Same as state 0x0F but expects step size + 1 for
+ *  		the second 500 sample subset
+ *  		The next state is 0x09
+ *  0x09	Same as state 0x0F but expects step size + 1 for
+ *  		the first 500 sample subset
+ *  		The next state is 0x06
+ *  0x06	Same as state 0x0F but expects vdiv and coupling
+ *  		configuration for the first channel and trigger
+ *  		source selection.
+ *  		(U46 in the schematics)
+ *  		The next state is 0x07
+ *  0x07	Same as state 0x0F but expects vdiv and coupling
+ *  		configuration for the first channel and trigger
+ *  		type (edge, TV hsync, TV vsync).
+ *  		(U47 in the schematics)
+ *  		The next state is 0x0A
+ *  0x0A	Same as state 0x0F but expects a parameter X + 1
+ *  		that determines the offset of the second 500 sample
+ *  		subset
+ *  		Offset = 5 * X * step size for first subset
+ *  		The next state is 0x0B
+ *  0x0B	Same as state 0x0F but expects the type of edge to
+ *  		trigger on (rising or falling)
+ *  		The next state is 0x0C
+ *  0x0C	Same as state 0x0F but expects the calibration
+ *  		value for the first channel's  position
+ *  		(POS1 in the schematics)
+ *  		The next state is 0x0D
+ *  0x0D	Same as state 0x0F but expects the calibration
+ *  		value for the second channel's  position
+ *  		(POS2 in the schematics)
+ *  		The next state is 0x0E
+ *  0x0E	Same as state 0x0F but expects the trigger level
+ *  		(TRIGLEVEL in the schematics)
+ *  		Keep in mind that trigger sources are AC coupled
+ *  		The next state is 0x10
+ *  0x10	Same as state 0x0F but expects the calibration
+ *  		value for the first channel's offset
+ *  		(OFFSET1 in the schematics)
+ *  		The next state is 0x11
+ *  0x11	Same as state 0x0F but expects the calibration
+ *  		value for the first channel's gain
+ *  		(GAIN1 in the schematics)
+ *  		The next state is 0x12
+ *  0x12	Same as state 0x0F but expects the calibration
+ *  		value for the second channel's offset
+ *  		(OFFSET2 in the schematics)
+ *  		The next state is 0x13
+ *  0x13	Same as state 0x0F but expects the calibration
+ *  		value for the second channel's gain
+ *  		(GAIN2 in the schematics)
+ *  		The next state is 0x01
+ *
+ * The Mailbox appears to be half duplex.
+ * If one side writes a byte into the mailbox, it
+ * reads 0 until the other side has written a byte.
+ * So you can't transfer 0.
+ *
+ * As the status signals are unconnected, the device is not
+ * IEEE1284 compliant and can't make use of EPP or ECP transfers.
+ * It drives the data lines when control is set to:
+ *                0                => Channel A data
+ *          C1284_NAUTOFD          => Channel B data
+ *         C1284_NSELECTIN         => Mailbox
+ * C1284_NSELECTIN | C1284_NAUTOFD => 0x55
+ *
+ * It takes about 200ns for the data lines to become stable after
+ * the control lines have been changed. This driver assumes that
+ * parallel port access is slow enough to not require additional
+ * delays.
+ *
+ * Channel values in state 0x14 and the mailbox can change their
+ * value while they are selected, the latter of course only from
+ * 0 to a valid state. Beware of intermediate values.
+ *
+ * SRAM N layout (N = 1 or 2):
+ * 0x0000-0x13ff	samples captured from ADC N
+ * 0x4000-0x41f3	bytes extracted from 0x6000 with step1
+ *			(both ADCs but only channel N)
+ * 0x41f4-0x43e7	bytes extracted from 0x6000+5*step1*shift
+ *			with step2 (both ADCs but only channel N)
+ * 0x43e8-0x43ea	{0x01, 0xfe, 0x80}
+ * 0x43eb-0x444e	copy of bytes from 0x4320
+ * 0x6000-0x7387	interleaved SRAM 1 and SRAM 2 bytes from
+ * 			0x0001 to 0x09c5 after channel N was captured
+ *
+ * On a trigger event the FPGA directs the ADC samples to the region
+ * at 0x0000. The microcontroller then copies 5000 samples from 0x0001
+ * to 0x6000. Each time state 0x03 is entered, the bytes from 0x4000
+ * to 0x444e are filled and the start address for readout is reset to
+ * 0x4000. Readout will wrap around back to 0x4000 after reaching 0x7fff.
+ *
+ * As you can see from the layout, it was probably intended to capture
+ * 5000 samples for both probes before they are read out. We don't do that
+ * to be able to read the full 10k samples captured by the FPGA. It would
+ * be useless anyway if you don't capture repetitive signals. We're also
+ * not reading the two samples at 0x0000 to save a few milliseconds.
+ */
+
+static const struct {
+	uint16_t num;
+	uint8_t step1;
+	uint8_t shift;
+	uint8_t interleave;
+} readout_steps[] = {
+	{ 1000, 1, 100, 0 },
+	{ 500, 100, 2, 0 },
+	{ 500, 100, 3, 0 },
+	{ 500, 100, 4, 0 },
+	{ 500, 100, 5, 0 },
+	{ 500, 100, 6, 0 },
+	{ 500, 100, 7, 0 },
+	{ 500, 100, 8, 0 },
+	{ 500, 100, 9, 0 },
+	{ 499, 212, 41, 1 },
+	{ 500, 157, 56, 1 },
+	{ 500, 247, 36, 1 },
+	{ 500, 232, 180, 1 },
+	{ 500, 230, 182, 1 },
+	{ 120, 212, 43, 1 }
+};
+
+SR_PRIV void hung_chang_dso_2100_reset_port(struct parport *port)
+{
+	ieee1284_write_control(port,
+			C1284_NSTROBE | C1284_NAUTOFD | C1284_NSELECTIN);
+	ieee1284_data_dir(port, 0);
+}
+
+SR_PRIV gboolean hung_chang_dso_2100_check_id(struct parport *port)
+{
+	gboolean ret = FALSE;
+
+	if (ieee1284_data_dir(port, 1) != E1284_OK)
+		goto fail;
+
+	ieee1284_write_control(port, C1284_NSTROBE | C1284_NAUTOFD | C1284_NSELECTIN);
+	ieee1284_write_control(port, C1284_NAUTOFD | C1284_NSELECTIN);
+
+	if (ieee1284_read_data(port) != 0x55)
+		goto fail;
+
+	ret = TRUE;
+fail:
+	hung_chang_dso_2100_reset_port(port);
+
+	return ret;
+}
+
+SR_PRIV void hung_chang_dso_2100_write_mbox(struct parport *port, uint8_t val)
+{
+	sr_dbg("mbox <= %X", val);
+	ieee1284_write_control(port,
+			C1284_NSTROBE | C1284_NINIT | C1284_NSELECTIN);
+	ieee1284_data_dir(port, 0);
+	ieee1284_write_data(port, val);
+	ieee1284_write_control(port, C1284_NINIT | C1284_NSELECTIN);
+	ieee1284_write_control(port,
+			C1284_NSTROBE | C1284_NINIT | C1284_NSELECTIN);
+	ieee1284_data_dir(port, 1);
+	ieee1284_write_control(port,
+		C1284_NSTROBE | C1284_NAUTOFD | C1284_NINIT | C1284_NSELECTIN);
+}
+
+SR_PRIV uint8_t hung_chang_dso_2100_read_mbox(struct parport *port, float timeout)
+{
+	GTimer *timer = NULL;
+	uint8_t val;
+
+	ieee1284_write_control(port, C1284_NSTROBE | C1284_NSELECTIN);
+	ieee1284_write_control(port, C1284_NSELECTIN);
+
+	for (;;) {
+		if (ieee1284_read_data(port)) {
+			/* Always read the value a second time.
+			 * The first one may be unstable. */
+			val = ieee1284_read_data(port);
+			break;
+		}
+		if (!timer) {
+			timer = g_timer_new();
+		} else if (g_timer_elapsed(timer, NULL) > timeout) {
+			val = 0;
+			break;
+		}
+	}
+
+	ieee1284_write_control(port, C1284_NSTROBE | C1284_NSELECTIN);
+	ieee1284_write_control(port,
+		C1284_NSTROBE | C1284_NAUTOFD | C1284_NINIT | C1284_NSELECTIN);
+
+	if (timer)
+		g_timer_destroy(timer);
+	sr_dbg("mbox == %X", val);
+	return val;
+}
+
+SR_PRIV int hung_chang_dso_2100_move_to(const struct sr_dev_inst *sdi, uint8_t target)
+{
+	struct dev_context *devc = sdi->priv;
+	int timeout = 40;
+	uint8_t c;
+
+	while (timeout--) {
+		c = hung_chang_dso_2100_read_mbox(sdi->conn, 0.1);
+		if (c == target)
+			return SR_OK;
+
+		switch (c) {
+		case 0x00:
+			/* Can happen if someone wrote something into
+			 * the mbox that was not expected by the uC.
+			 * Alternating between 0xff and 4 helps in
+			 * all states. */
+			c = (timeout & 1) ? 0xFF : 0x04;
+			break;
+		case 0x01:
+			switch (target) {
+			case 0x21: c = 2; break;
+			case 0x03: c = 3; break;
+			default: c = 4;
+			}
+			break;
+		case 0x03: c = 4; break;
+		case 0x05: c = devc->rate + 1; break;
+		case 0x06: c = devc->cctl[0]; break;
+		case 0x07: c = devc->cctl[1]; break;
+		case 0x08: c = 1 /* step 2 */ + 1 ; break;
+		case 0x09: c = readout_steps[devc->step].step1 + 1; break;
+		case 0x0A: c = readout_steps[devc->step].shift + 1; break;
+		case 0x0B: c = devc->edge + 1; break;
+		case 0x0C: c = devc->pos[0]; break;
+		case 0x0D: c = devc->pos[1]; break;
+		case 0x0E: c = devc->tlevel; break;
+		case 0x0F:
+			if (!devc->channel)
+				c = 1;
+			else if (readout_steps[devc->step].interleave)
+				c = devc->adc2 ? 2 : 1;
+			else
+				c = devc->channel;
+			break;
+		case 0x10: c = devc->offset[0]; break;
+		case 0x11: c = devc->gain[0]; break;
+		case 0x12: c = devc->offset[1]; break;
+		case 0x13: c = devc->gain[1]; break;
+		case 0x14:
+		case 0x21: c = 0xFF; break;
+		default:
+			return SR_ERR_DATA;
+		}
+		hung_chang_dso_2100_write_mbox(sdi->conn, c);
+	}
+	return SR_ERR_TIMEOUT;
+}
+
+static void skip_samples(struct parport *port, uint8_t ctrl, size_t num)
+{
+	while (num--) {
+		ieee1284_write_control(port, ctrl & ~C1284_NSTROBE);
+		ieee1284_write_control(port, ctrl);
+	}
+}
+
+static void read_samples(struct parport *port, uint8_t ctrl, uint8_t *buf, size_t num, size_t stride)
+{
+	while (num--) {
+		ieee1284_write_control(port, ctrl & ~C1284_NSTROBE);
+		*buf = ieee1284_read_data(port);
+		buf += stride;
+		ieee1284_write_control(port, ctrl);
+	}
+}
+
+static void push_samples(const struct sr_dev_inst *sdi, uint8_t *buf, size_t num)
+{
+	struct dev_context *devc = sdi->priv;
+	float *data = devc->samples;
+	struct sr_datafeed_analog_old analog = {
+		.channels = devc->enabled_channel,
+		.num_samples = num,
+		.mq = SR_MQ_VOLTAGE,
+		.unit = SR_UNIT_VOLT,
+		.mqflags = 0,
+		.data = data,
+	};
+	struct sr_datafeed_packet packet = {
+		.type = SR_DF_ANALOG_OLD,
+		.payload = &analog,
+	};
+	float factor = devc->factor;
+
+	while (num--)
+		data[num] = (buf[num] - 0x80) * factor;
+
+	sr_session_send(devc->cb_data, &packet);
+}
+
+static int read_subframe(const struct sr_dev_inst *sdi, uint8_t *buf)
+{
+	struct dev_context *devc = sdi->priv;
+	uint8_t sig[3], ctrl;
+	unsigned int num;
+	gboolean interleave;
+
+	interleave = readout_steps[devc->step].interleave;
+	ctrl = C1284_NSTROBE;
+	if ((interleave && devc->adc2) || (!interleave && devc->channel == 2))
+		ctrl |= C1284_NAUTOFD;
+
+	ieee1284_write_control(sdi->conn, ctrl);
+	num = readout_steps[devc->step].num;
+	if (num < 1000)
+		skip_samples(sdi->conn, ctrl, 1000 - num);
+	read_samples(sdi->conn, ctrl, buf + (devc->adc2 ? 1 : 0), num,
+		     interleave ? 2 : 1);
+	read_samples(sdi->conn, ctrl, sig, 3, 1);
+	if (sig[0] != 0x01 || sig[1] != 0xfe || sig[2] != 0x80) {
+		if (--devc->retries) {
+			sr_dbg("Missing signature at end of buffer, %i tries remaining",
+			       devc->retries);
+			return TRUE;
+		} else {
+			sr_err("Failed to read frame without transfer errors");
+			devc->step = 0;
+		}
+	} else {
+		if (interleave && !devc->adc2) {
+			devc->adc2 = TRUE;
+			devc->retries = MAX_RETRIES;
+			return TRUE;
+		} else {
+			if (interleave)
+				num *= 2;
+			if (!devc->step) {
+				struct sr_datafeed_packet packet = {
+					.type = SR_DF_TRIGGER
+				};
+
+				push_samples(sdi, buf, 6);
+				sr_session_send(devc->cb_data, &packet);
+				buf += 6;
+				num -= 6;
+			}
+			push_samples(sdi, buf, num);
+			if (++devc->step > devc->last_step)
+				devc->step = 0;
+		}
+	}
+
+	devc->adc2 = FALSE;
+	devc->retries = MAX_RETRIES;
+
+	return devc->step > 0;
+}
+
+SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data)
+{
+	struct sr_datafeed_packet packet = { .type = SR_DF_FRAME_BEGIN };
+	const struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	uint8_t state, buf[1000];
+
+	(void)fd;
+	(void)revents;
+
+	if (!(sdi = cb_data))
+		return TRUE;
+
+	if (!(devc = sdi->priv))
+		return TRUE;
+
+	if (devc->state_known)
+		hung_chang_dso_2100_write_mbox(sdi->conn, 0x99);
+
+	state = hung_chang_dso_2100_read_mbox(sdi->conn, 0.00025);
+	devc->state_known = (state != 0x00);
+
+	if (!devc->state_known || state == 0x21)
+		return TRUE;
+
+	if (state != 0x03) {
+		sr_err("Unexpected state 0x%X while checking for trigger", state);
+		return FALSE;
+	}
+
+	sr_session_send(devc->cb_data, &packet);
+
+	if (devc->channel) {
+		while (read_subframe(sdi, buf)) {
+			if (hung_chang_dso_2100_move_to(sdi, 1) != SR_OK)
+				break;
+			hung_chang_dso_2100_write_mbox(sdi->conn, 3);
+			g_usleep(1700);
+			if (hung_chang_dso_2100_read_mbox(sdi->conn, 0.02) != 0x03)
+				break;
+		}
+	}
+
+	packet.type = SR_DF_FRAME_END;
+	sr_session_send(devc->cb_data, &packet);
+
+	if (++devc->frame >= devc->frame_limit)
+		hung_chang_dso_2100_dev_acquisition_stop(sdi, devc->cb_data);
+	else
+		hung_chang_dso_2100_move_to(sdi, 0x21);
+
+	return TRUE;
+}
diff --git a/src/hardware/hung-chang-dso-2100/protocol.h b/src/hardware/hung-chang-dso-2100/protocol.h
new file mode 100644
index 0000000..5636699
--- /dev/null
+++ b/src/hardware/hung-chang-dso-2100/protocol.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Daniel Glöckner <daniel-gl at gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_HUNG_CHANG_DSO_2100_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_HUNG_CHANG_DSO_2100_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "hung-chang-dso-2100"
+#define MAX_RETRIES 4
+#define NUM_CHANNELS 2
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/* Acquisition settings */
+	GSList *enabled_channel;
+	uint8_t channel;
+	uint8_t rate;
+	uint8_t cctl[2];
+	uint8_t edge;
+	uint8_t tlevel;
+	uint8_t pos[2];
+	uint8_t offset[2];
+	uint8_t gain[2];
+
+	/* Operational state */
+	uint64_t frame_limit;
+	uint64_t frame;
+	uint64_t probe[2];
+	uint8_t step;
+	uint8_t last_step;
+	uint8_t retries;
+	gboolean adc2;
+
+	/* Temporary state across callbacks */
+	void *cb_data;
+	float *samples;
+	float factor;
+	gboolean state_known;
+};
+
+SR_PRIV void hung_chang_dso_2100_reset_port(struct parport *port);
+SR_PRIV gboolean hung_chang_dso_2100_check_id(struct parport *port);
+SR_PRIV void hung_chang_dso_2100_write_mbox(struct parport *port, uint8_t val);
+SR_PRIV uint8_t hung_chang_dso_2100_read_mbox(struct parport *port, float timeout);
+SR_PRIV int hung_chang_dso_2100_move_to(const struct sr_dev_inst *sdi, uint8_t target);
+SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data);
+SR_PRIV int hung_chang_dso_2100_dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data);
+
+#endif
diff --git a/hardware/ikalogic-scanalogic2/api.c b/src/hardware/ikalogic-scanalogic2/api.c
similarity index 81%
rename from hardware/ikalogic-scanalogic2/api.c
rename to src/hardware/ikalogic-scanalogic2/api.c
index e586011..130addc 100644
--- a/hardware/ikalogic-scanalogic2/api.c
+++ b/src/hardware/ikalogic-scanalogic2/api.c
@@ -17,14 +17,21 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
 
-static const int hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_TRIGGER_TYPE,
-	SR_CONF_CAPTURE_RATIO,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
+	SR_TRIGGER_EDGE,
 };
 
 SR_PRIV const uint64_t sl2_samplerates[NUM_SAMPLERATES] = {
@@ -41,41 +48,37 @@ SR_PRIV const uint64_t sl2_samplerates[NUM_SAMPLERATES] = {
 	SR_MHZ(20),
 };
 
-static const char *channel_names[NUM_CHANNELS + 1] = {
+static const char *channel_names[] = {
 	"0", "1", "2", "3",
-	NULL,
 };
 
 SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info;
-static struct sr_dev_driver *di = &ikalogic_scanalogic2_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	GSList *usb_devices, *devices, *l;
 	struct drv_context *drvc;
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	struct dev_context *devc;
 	struct sr_usb_dev_inst *usb;
 	struct device_info dev_info;
-	int ret, device_index, i;
-	char *fw_ver_str;
+	unsigned int i;
+	int ret;
 
 	(void)options;
 
 	devices = NULL;
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
-	device_index = 0;
 
 	usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, USB_VID_PID);
 
-	if (usb_devices == NULL)
+	if (!usb_devices)
 		return NULL;
 
 	for (l = usb_devices; l; l = l->next) {
@@ -87,11 +90,7 @@ static GSList *scan(GSList *options)
 			continue;
 		}
 
-		if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
-			sr_err("Device instance malloc failed.");
-			sr_usb_dev_inst_free(usb);
-			continue;
-		}
+		devc = g_malloc0(sizeof(struct dev_context));
 
 		if (!(devc->xfer_in = libusb_alloc_transfer(0))) {
 			sr_err("Transfer malloc failed.");
@@ -108,40 +107,20 @@ static GSList *scan(GSList *options)
 			continue;
 		}
 
-		fw_ver_str = g_strdup_printf("%u.%u", dev_info.fw_ver_major,
-			dev_info.fw_ver_minor);
-		if (!fw_ver_str) {
-			sr_err("Firmware string malloc failed.");
-			sr_usb_dev_inst_free(usb);
-			libusb_free_transfer(devc->xfer_in);
-			libusb_free_transfer(devc->xfer_out);
-			g_free(devc);
-			continue;
-		}
-
-		sdi = sr_dev_inst_new(device_index, SR_ST_INACTIVE, VENDOR_NAME,
-			MODEL_NAME, fw_ver_str);
-		g_free(fw_ver_str);
-		if (!sdi) {
-			sr_err("sr_dev_inst_new failed.");
-			sr_usb_dev_inst_free(usb);
-			libusb_free_transfer(devc->xfer_in);
-			libusb_free_transfer(devc->xfer_out);
-			g_free(devc);
-			continue;
-		}
-
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup(VENDOR_NAME);
+		sdi->model = g_strdup(MODEL_NAME);
+		sdi->version = g_strdup_printf("%u.%u", dev_info.fw_ver_major, dev_info.fw_ver_minor);
+		sdi->serial_num = g_strdup_printf("%d", dev_info.serial);
 		sdi->priv = devc;
 		sdi->driver = di;
 		sdi->inst_type = SR_INST_USB;
 		sdi->conn = usb;
 
-		for (i = 0; channel_names[i]; i++) {
-			ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-				channel_names[i]);
-			sdi->channels = g_slist_append(sdi->channels, ch);
-			devc->channels[i] = ch;
-		}
+		for (i = 0; i < ARRAY_SIZE(channel_names); i++)
+			devc->channels[i] = sr_channel_new(sdi, i,
+				SR_CHANNEL_LOGIC, TRUE, channel_names[i]);
 
 		devc->state = STATE_IDLE;
 		devc->next_state = STATE_IDLE;
@@ -176,8 +155,6 @@ static GSList *scan(GSList *options)
 
 		drvc->instances = g_slist_append(drvc->instances, sdi);
 		devices = g_slist_append(devices, sdi);
-
-		device_index++;
 	}
 
 	g_slist_free(usb_devices);
@@ -185,9 +162,9 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static void clear_dev_context(void *priv)
@@ -203,20 +180,21 @@ static void clear_dev_context(void *priv)
 	g_free(devc);
 }
 
-static int dev_clear(void)
+static int dev_clear(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, &clear_dev_context);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_usb_dev_inst *usb;
 	uint8_t buffer[PACKET_LENGTH];
 	int ret;
 
-	if (!(drvc = di->priv)) {
+	if (!(drvc = di->context)) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -248,11 +226,11 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 	libusb_fill_control_transfer(devc->xfer_in, usb->devhdl,
 		devc->xfer_buf_in, sl2_receive_transfer_in,
-		sdi, USB_TIMEOUT);
+		sdi, USB_TIMEOUT_MS);
 
 	libusb_fill_control_transfer(devc->xfer_out, usb->devhdl,
 		devc->xfer_buf_out, sl2_receive_transfer_out,
-		sdi, USB_TIMEOUT);
+		sdi, USB_TIMEOUT_MS);
 
 	memset(buffer, 0, sizeof(buffer));
 
@@ -281,9 +259,10 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct sr_usb_dev_inst *usb;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -302,12 +281,12 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
-	return dev_clear();
+	return dev_clear(di);
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -332,7 +311,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	uint64_t samplerate, limit_samples, capture_ratio;
@@ -343,8 +322,6 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	ret = SR_OK;
-
 	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		limit_samples = g_variant_get_uint64(data);
@@ -365,7 +342,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	GVariant *gvar, *grange[2];
@@ -376,11 +353,10 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	(void)cg;
 
 	ret = SR_OK;
-
 	switch (key) {
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, hwcaps,
-			ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLERATE:
 		g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
@@ -390,8 +366,10 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		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_TYPES);
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				trigger_matches, ARRAY_SIZE(trigger_matches),
+				sizeof(int32_t));
 		break;
 	case SR_CONF_LIMIT_SAMPLES:
 		grange[0] = g_variant_new_uint64(0);
@@ -407,6 +385,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	uint16_t trigger_bytes, tmp;
@@ -417,7 +396,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		return SR_ERR_DEV_CLOSED;
 
 	devc = sdi->priv;
-	drvc = di->priv;
+	drvc = di->context;
 
 	devc->cb_data = cb_data;
 	devc->wait_data_ready_locked = TRUE;
@@ -431,7 +410,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	 * The trigger must be configured first because the calculation of the
 	 * pre and post trigger samples depends on a configured trigger.
 	 */
-	sl2_configure_trigger(sdi);
+	sl2_convert_trigger(sdi);
 	sl2_calculate_trigger_samples(sdi);
 
 	trigger_bytes = devc->pre_trigger_bytes + devc->post_trigger_bytes;
@@ -482,7 +461,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		return SR_ERR;
 	}
 
-	usb_source_add(drvc->sr_ctx, 100, ikalogic_scanalogic2_receive_data, (void *)sdi);
+	usb_source_add(sdi->session, drvc->sr_ctx, 100,
+			ikalogic_scanalogic2_receive_data, (void *)sdi);
 
 	sr_dbg("Acquisition started successfully.");
 
@@ -524,5 +504,5 @@ SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/ikalogic-scanalogic2/protocol.c b/src/hardware/ikalogic-scanalogic2/protocol.c
similarity index 91%
rename from hardware/ikalogic-scanalogic2/protocol.c
rename to src/hardware/ikalogic-scanalogic2/protocol.c
index b99709c..03d5d1b 100644
--- a/hardware/ikalogic-scanalogic2/protocol.c
+++ b/src/hardware/ikalogic-scanalogic2/protocol.c
@@ -17,23 +17,23 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
 
 extern struct sr_dev_driver ikalogic_scanalogic2_driver_info;
-static struct sr_dev_driver *di = &ikalogic_scanalogic2_driver_info;
 
 extern uint64_t sl2_samplerates[NUM_SAMPLERATES];
 
 static void stop_acquisition(struct sr_dev_inst *sdi)
 {
-	struct drv_context *drvc = sdi->driver->priv;
+	struct drv_context *drvc = sdi->driver->context;
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
 
 	devc = sdi->priv;
 
 	/* Remove USB file descriptors from polling. */
-	usb_source_remove(drvc->sr_ctx);
+	usb_source_remove(sdi->session, drvc->sr_ctx);
 
 	packet.type = SR_DF_END;
 	sr_session_send(devc->cb_data, &packet);
@@ -43,14 +43,14 @@ static void stop_acquisition(struct sr_dev_inst *sdi)
 
 static void abort_acquisition(struct sr_dev_inst *sdi)
 {
-	struct drv_context *drvc = sdi->driver->priv;
+	struct drv_context *drvc = sdi->driver->context;
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
 
 	devc = sdi->priv;
 
 	/* Remove USB file descriptors from polling. */
-	usb_source_remove(drvc->sr_ctx);
+	usb_source_remove(sdi->session, drvc->sr_ctx);
 
 	packet.type = SR_DF_END;
 	sr_session_send(devc->cb_data, &packet);
@@ -199,6 +199,7 @@ static void process_sample_data(const struct sr_dev_inst *sdi)
 SR_PRIV int ikalogic_scanalogic2_receive_data(int fd, int revents, void *cb_data)
 {
 	struct sr_dev_inst *sdi;
+	struct sr_dev_driver *di;
 	struct dev_context *devc;
 	struct drv_context *drvc;
 	struct timeval tv;
@@ -214,7 +215,8 @@ SR_PRIV int ikalogic_scanalogic2_receive_data(int fd, int revents, void *cb_data
 	if (!(devc = sdi->priv))
 		return TRUE;
 
-	drvc = di->priv;
+	di = sdi->driver;
+	drvc = di->context;
 	current_time = g_get_monotonic_time();
 
 	if (devc->state == STATE_WAIT_DATA_READY &&
@@ -257,7 +259,7 @@ SR_PRIV int ikalogic_scanalogic2_receive_data(int fd, int revents, void *cb_data
 	return TRUE;
 }
 
-SR_PRIV void sl2_receive_transfer_in( struct libusb_transfer *transfer)
+SR_PRIV void LIBUSB_CALL sl2_receive_transfer_in( struct libusb_transfer *transfer)
 {
 	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
@@ -268,7 +270,8 @@ SR_PRIV void sl2_receive_transfer_in( struct libusb_transfer *transfer)
 	devc = sdi->priv;
 
 	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		sr_err("Transfer to device failed: %i.", transfer->status);
+		sr_err("Transfer to device failed: %s.",
+			libusb_error_name(transfer->status));
 		devc->transfer_error = TRUE;
 		return;
 	}
@@ -388,7 +391,7 @@ SR_PRIV void sl2_receive_transfer_in( struct libusb_transfer *transfer)
 	}
 }
 
-SR_PRIV void sl2_receive_transfer_out( struct libusb_transfer *transfer)
+SR_PRIV void LIBUSB_CALL sl2_receive_transfer_out( struct libusb_transfer *transfer)
 {
 	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
@@ -398,7 +401,8 @@ SR_PRIV void sl2_receive_transfer_out( struct libusb_transfer *transfer)
 	devc = sdi->priv;
 
 	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-		sr_err("Transfer to device failed: %i.", transfer->status);
+		sr_err("Transfer to device failed: %s.",
+			libusb_error_name(transfer->status));
 		devc->transfer_error = TRUE;
 		return;
 	}
@@ -479,14 +483,14 @@ SR_PRIV int sl2_set_limit_samples(const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-SR_PRIV void sl2_configure_trigger(const struct sr_dev_inst *sdi)
+SR_PRIV int sl2_convert_trigger(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
-	struct sr_channel *ch;
-	uint8_t trigger_type;
-	int channel_index, num_triggers_anyedge;
-	char *trigger;
-	GSList *l;
+	struct sr_trigger *trigger;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	const GSList *l, *m;
+	int num_triggers_anyedge;
 
 	devc = sdi->priv;
 
@@ -494,32 +498,36 @@ SR_PRIV void sl2_configure_trigger(const struct sr_dev_inst *sdi)
 	devc->trigger_channel = TRIGGER_CHANNEL_0;
 	devc->trigger_type = TRIGGER_TYPE_NONE;
 
-	num_triggers_anyedge = 0;
-
-	for (l = sdi->channels, channel_index = 0; l; l = l->next, channel_index++) {
-		ch = l->data;
-		trigger = ch->trigger;
+	if (!(trigger = sr_session_trigger_get(sdi->session)))
+		return SR_OK;
 
-		if (!trigger || !ch->enabled)
-			continue;
+	if (g_slist_length(trigger->stages) > 1) {
+		sr_err("This device only supports 1 trigger stage.");
+		return SR_ERR;
+	}
 
-		switch (*trigger) {
-		case 'r':
-			trigger_type = TRIGGER_TYPE_POSEDGE;
-			break;
-		case 'f':
-			trigger_type = TRIGGER_TYPE_NEGEDGE;
-			break;
-		case 'c':
-			trigger_type = TRIGGER_TYPE_ANYEDGE;
-			num_triggers_anyedge++;
-			break;
-		default:
-			continue;
+	num_triggers_anyedge = 0;
+	for (l = trigger->stages; l; l = l->next) {
+		stage = l->data;
+		for (m = stage->matches; m; m = m->next) {
+			match = m->data;
+			if (!match->channel->enabled)
+				/* Ignore disabled channels with a trigger. */
+				continue;
+			devc->trigger_channel = match->channel->index + 1;
+			switch (match->match) {
+			case SR_TRIGGER_RISING:
+				devc->trigger_type = TRIGGER_TYPE_POSEDGE;
+				break;
+			case SR_TRIGGER_FALLING:
+				devc->trigger_type = TRIGGER_TYPE_NEGEDGE;
+				break;
+			case SR_TRIGGER_EDGE:
+				devc->trigger_type = TRIGGER_TYPE_ANYEDGE;
+				num_triggers_anyedge++;
+				break;
+			}
 		}
-
-		devc->trigger_channel = channel_index + 1;
-		devc->trigger_type = trigger_type;
 	}
 
 	/*
@@ -533,6 +541,8 @@ SR_PRIV void sl2_configure_trigger(const struct sr_dev_inst *sdi)
 
 	sr_dbg("Trigger set to channel 0x%02x and type 0x%02x.",
 		devc->trigger_channel, devc->trigger_type);
+
+	return SR_OK;
 }
 
 SR_PRIV int sl2_set_capture_ratio(const struct sr_dev_inst *sdi,
@@ -636,7 +646,7 @@ SR_PRIV int sl2_get_device_info(struct sr_usb_dev_inst usb,
 	uint8_t buffer[PACKET_LENGTH];
 	int ret;
 
-	drvc = di->priv;
+	drvc = ikalogic_scanalogic2_driver_info.context;
 
 	if (!dev_info)
 		return SR_ERR_ARG;
@@ -747,12 +757,12 @@ SR_PRIV int sl2_transfer_in(libusb_device_handle *dev_handle, uint8_t *data)
 {
 	return libusb_control_transfer(dev_handle, USB_REQUEST_TYPE_IN,
 		USB_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
-		(unsigned char *)data, PACKET_LENGTH, USB_TIMEOUT);
+		(unsigned char *)data, PACKET_LENGTH, USB_TIMEOUT_MS);
 }
 
 SR_PRIV int sl2_transfer_out(libusb_device_handle *dev_handle, uint8_t *data)
 {
 	return libusb_control_transfer(dev_handle, USB_REQUEST_TYPE_OUT,
 		USB_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE, USB_INTERFACE,
-		(unsigned char *)data, PACKET_LENGTH, USB_TIMEOUT);
+		(unsigned char *)data, PACKET_LENGTH, USB_TIMEOUT_MS);
 }
diff --git a/hardware/ikalogic-scanalogic2/protocol.h b/src/hardware/ikalogic-scanalogic2/protocol.h
similarity index 95%
rename from hardware/ikalogic-scanalogic2/protocol.h
rename to src/hardware/ikalogic-scanalogic2/protocol.h
index 3b411e6..66a5cec 100644
--- a/hardware/ikalogic-scanalogic2/protocol.h
+++ b/src/hardware/ikalogic-scanalogic2/protocol.h
@@ -24,7 +24,7 @@
 #include <string.h>
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "ikalogic-scanalogic2"
@@ -34,7 +34,7 @@
 
 #define USB_VID_PID			"20a0.4123"
 #define USB_INTERFACE			0
-#define USB_TIMEOUT			5000
+#define USB_TIMEOUT_MS			(5 * 1000)
 
 #define USB_REQUEST_TYPE_IN		(LIBUSB_REQUEST_TYPE_CLASS | \
 	LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN)
@@ -49,8 +49,6 @@
 #define NUM_SAMPLERATES			11
 #define NUM_CHANNELS			4
 
-#define TRIGGER_TYPES			"rfc"
-
 /*
  * Number of sample bytes and samples the device can acquire. Note that the
  * vendor software can acquire 32736 sample bytes only but the device is capable
@@ -219,13 +217,13 @@ struct dev_context {
 };
 
 SR_PRIV int ikalogic_scanalogic2_receive_data(int fd, int revents, void *cb_data);
-SR_PRIV void sl2_receive_transfer_in(struct libusb_transfer *transfer);
-SR_PRIV void sl2_receive_transfer_out(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL sl2_receive_transfer_in(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL sl2_receive_transfer_out(struct libusb_transfer *transfer);
 SR_PRIV int sl2_set_samplerate(const struct sr_dev_inst *sdi,
 		uint64_t samplerate);
 SR_PRIV int sl2_set_limit_samples(const struct sr_dev_inst *sdi,
 				  uint64_t limit_samples);
-SR_PRIV void sl2_configure_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV int sl2_convert_trigger(const struct sr_dev_inst *sdi);
 SR_PRIV int sl2_set_capture_ratio(const struct sr_dev_inst *sdi,
 				  uint64_t capture_ratio);
 SR_PRIV int sl2_set_after_trigger_delay(const struct sr_dev_inst *sdi,
diff --git a/hardware/ikalogic-scanaplus/api.c b/src/hardware/ikalogic-scanaplus/api.c
similarity index 85%
rename from hardware/ikalogic-scanaplus/api.c
rename to src/hardware/ikalogic-scanaplus/api.c
index e855097..ba4652e 100644
--- a/hardware/ikalogic-scanaplus/api.c
+++ b/src/hardware/ikalogic-scanaplus/api.c
@@ -18,6 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include "protocol.h"
 
 #define USB_VENDOR_ID			0x0403
@@ -28,25 +29,22 @@
 
 #define SAMPLE_BUF_SIZE			(8 * 1024 * 1024)
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_LIMIT_MSEC,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_CONTINUOUS, // TODO?
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
 /* Channels are numbered 1-9. */
 static const char *channel_names[] = {
 	"1", "2", "3", "4", "5", "6", "7", "8", "9",
-	NULL,
 };
 
 /* Note: The IKALOGIC ScanaPLUS always samples at 100MHz. */
-static uint64_t samplerates[1] = { SR_MHZ(100) };
+static const uint64_t samplerates[1] = { SR_MHZ(100) };
 
 SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info;
-static struct sr_dev_driver *di = &ikalogic_scanaplus_driver_info;
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
 
@@ -59,22 +57,22 @@ static void clear_helper(void *priv)
 	ftdi_free(devc->ftdic);
 	g_free(devc->compressed_buf);
 	g_free(devc->sample_buf);
+	g_free(devc);
 }
 
-static int dev_clear(void)
+static int dev_clear(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, clear_helper);
 }
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	GSList *devices;
@@ -83,15 +81,12 @@ static GSList *scan(GSList *options)
 
 	(void)options;
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	devices = NULL;
 
 	/* Allocate memory for our private device context. */
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		goto err_free_nothing;
-	}
+	devc = g_malloc0(sizeof(struct dev_context));
 
 	/* Allocate memory for the incoming compressed samples. */
 	if (!(devc->compressed_buf = g_try_malloc0(COMPRESSED_BUF_SIZE))) {
@@ -123,21 +118,15 @@ static GSList *scan(GSList *options)
 	}
 
 	/* Register the device with libsigrok. */
-	sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
-			USB_VENDOR_NAME, USB_MODEL_NAME, NULL);
-	if (!sdi) {
-		sr_err("Failed to create device instance.");
-		goto err_close_ftdic;
-	}
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INITIALIZING;
+	sdi->vendor = g_strdup(USB_VENDOR_NAME);
+	sdi->model = g_strdup(USB_MODEL_NAME);
 	sdi->driver = di;
 	sdi->priv = devc;
 
-	for (i = 0; channel_names[i]; i++) {
-		if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-					   channel_names[i])))
-			return NULL;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-	}
+	for (i = 0; i < ARRAY_SIZE(channel_names); i++)
+		sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_names[i]);
 
 	devices = g_slist_append(devices, sdi);
 	drvc->instances = g_slist_append(drvc->instances, sdi);
@@ -147,7 +136,6 @@ static GSList *scan(GSList *options)
 
 	return devices;
 
-err_close_ftdic:
 	scanaplus_close(devc);
 err_free_ftdic:
 	ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
@@ -157,14 +145,13 @@ err_free_compressed_buf:
 	g_free(devc->compressed_buf);
 err_free_devc:
 	g_free(devc);
-err_free_nothing:
 
 	return NULL;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
@@ -274,18 +261,18 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return ret;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
-	return dev_clear();
+	return dev_clear(di);
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
 	(void)cg;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		/* The ScanaPLUS samplerate is 100MHz and can't be changed. */
 		*data = g_variant_new_uint64(SR_MHZ(100));
@@ -297,7 +284,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -309,7 +296,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
 	devc = sdi->priv;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		if (g_variant_get_uint64(data) != SR_MHZ(100)) {
 			sr_err("ScanaPLUS only supports samplerate = 100MHz.");
@@ -334,7 +321,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	GVariant *gvar;
@@ -345,8 +332,8 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLERATE:
 		g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
@@ -393,10 +380,10 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		return ret;
 
 	/* Send header packet to the session bus. */
-	std_session_send_df_header(cb_data, LOG_PREFIX);
+	std_session_send_df_header(sdi, LOG_PREFIX);
 
 	/* Hook up a dummy handler to receive data from the device. */
-	sr_source_add(-1, G_IO_IN, 0, scanaplus_receive_data, (void *)sdi);
+	sr_session_source_add(sdi->session, -1, 0, 0, scanaplus_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -405,15 +392,15 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
 	struct sr_datafeed_packet packet;
 
-	(void)sdi;
+	(void)cb_data;
 
 	sr_dbg("Stopping acquisition.");
-	sr_source_remove(-1);
+	sr_session_source_remove(sdi->session, -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);
+	sr_session_send(sdi, &packet);
 
 	return SR_OK;
 }
@@ -434,5 +421,5 @@ SR_PRIV struct sr_dev_driver ikalogic_scanaplus_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/ikalogic-scanaplus/protocol.c b/src/hardware/ikalogic-scanaplus/protocol.c
similarity index 99%
rename from hardware/ikalogic-scanaplus/protocol.c
rename to src/hardware/ikalogic-scanaplus/protocol.c
index 3a834f7..0b81ebd 100644
--- a/hardware/ikalogic-scanaplus/protocol.c
+++ b/src/hardware/ikalogic-scanaplus/protocol.c
@@ -18,6 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include "protocol.h"
 
 /*
diff --git a/hardware/ikalogic-scanaplus/protocol.h b/src/hardware/ikalogic-scanaplus/protocol.h
similarity index 96%
rename from hardware/ikalogic-scanaplus/protocol.h
rename to src/hardware/ikalogic-scanaplus/protocol.h
index 1df0517..cdd684d 100644
--- a/hardware/ikalogic-scanaplus/protocol.h
+++ b/src/hardware/ikalogic-scanaplus/protocol.h
@@ -25,12 +25,12 @@
 #include <string.h>
 #include <glib.h>
 #include <ftdi.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "ikalogic-scanaplus"
 
-#define COMPRESSED_BUF_SIZE		(64 * 1024)
+#define COMPRESSED_BUF_SIZE (64 * 1024)
 
 /* Private, per-device-instance driver context. */
 struct dev_context {
diff --git a/hardware/kecheng-kc-330b/api.c b/src/hardware/kecheng-kc-330b/api.c
similarity index 86%
rename from hardware/kecheng-kc-330b/api.c
rename to src/hardware/kecheng-kc-330b/api.c
index 57dafdf..d294bf0 100644
--- a/hardware/kecheng-kc-330b/api.c
+++ b/src/hardware/kecheng-kc-330b/api.c
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include "protocol.h"
 
@@ -24,14 +25,15 @@
 #define VENDOR "Kecheng"
 #define USB_INTERFACE 0
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_SOUNDLEVELMETER,
-	SR_CONF_LIMIT_SAMPLES,
 	SR_CONF_CONTINUOUS,
-	SR_CONF_DATALOG,
-	SR_CONF_SPL_WEIGHT_FREQ,
-	SR_CONF_SPL_WEIGHT_TIME,
-	SR_CONF_DATA_SOURCE,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SAMPLE_INTERVAL | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_DATALOG | SR_CONF_GET,
+	SR_CONF_SPL_WEIGHT_FREQ | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SPL_WEIGHT_TIME | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
 SR_PRIV const uint64_t kecheng_kc_330b_sample_intervals[][2] = {
@@ -60,21 +62,20 @@ static const char *data_sources[] = {
 };
 
 SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info;
-static struct sr_dev_driver *di = &kecheng_kc_330b_driver_info;
 
-
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static int scan_kecheng(struct sr_usb_dev_inst *usb, char **model)
+static int scan_kecheng(struct sr_dev_driver *di,
+		struct sr_usb_dev_inst *usb, char **model)
 {
 	struct drv_context *drvc;
 	int len, ret;
 	unsigned char cmd, buf[32];
 
-	drvc = di->priv;
+	drvc = di->context;
 	if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
 		return SR_ERR;
 
@@ -107,18 +108,17 @@ static int scan_kecheng(struct sr_usb_dev_inst *usb, char **model)
 	return SR_OK;
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	GSList *usb_devices, *devices, *l;
 	char *model;
 
 	(void)options;
 
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	devices = NULL;
@@ -126,23 +126,17 @@ static GSList *scan(GSList *options)
 		/* We have a list of sr_usb_dev_inst matching the connection
 		 * string. Wrap them in sr_dev_inst and we're done. */
 		for (l = usb_devices; l; l = l->next) {
-			if (scan_kecheng(l->data, &model) != SR_OK)
+			if (scan_kecheng(di, l->data, &model) != SR_OK)
 				continue;
-			if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR,
-					model, NULL)))
-				return NULL;
-			g_free(model);
+			sdi = g_malloc0(sizeof(struct sr_dev_inst));
+			sdi->status = SR_ST_INACTIVE;
+			sdi->vendor = g_strdup(VENDOR);
+			sdi->model = model; /* Already g_strndup()'d. */
 			sdi->driver = di;
 			sdi->inst_type = SR_INST_USB;
 			sdi->conn = l->data;
-			if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "SPL")))
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
-
-			if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
-				sr_dbg("Device context malloc failed.");
-				return NULL;
-			}
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "SPL");
+			devc = g_malloc0(sizeof(struct dev_context));
 			sdi->priv = devc;
 			devc->limit_samples = 0;
 			/* The protocol provides no way to read the current
@@ -166,18 +160,19 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct drv_context *drvc;
 	struct sr_usb_dev_inst *usb;
 	int ret;
 
-	if (!(drvc = di->priv)) {
+	if (!(drvc = di->context)) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -203,10 +198,11 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct dev_context *devc;
 	struct sr_usb_dev_inst *usb;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -231,24 +227,22 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	int ret;
 	struct drv_context *drvc;
 
-	if (!(drvc = di->priv))
+	if (!(drvc = di->context))
 		/* Can get called on an unused driver, doesn't matter. */
 		return SR_OK;
 
-
 	ret = std_dev_clear(di, NULL);
 	g_free(drvc);
-	di->priv = NULL;
 
 	return ret;
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -297,9 +291,10 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct dev_context *devc;
 	uint64_t p, q;
 	unsigned int i;
@@ -311,7 +306,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -377,7 +372,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	GVariant *tuple, *rational[2];
@@ -389,8 +384,8 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLE_INTERVAL:
 		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
@@ -421,6 +416,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 		void *cb_data)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
@@ -429,13 +425,13 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	struct sr_usb_dev_inst *usb;
 	GVariant *gvar, *rational[2];
 	const uint64_t *si;
-	int stored_mqflags, req_len, buf_len, len, ret;
+	int req_len, buf_len, len, ret;
 	unsigned char buf[9];
 
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	drvc = di->priv;
+	drvc = di->context;
 	devc = sdi->priv;
 	usb = sdi->conn;
 
@@ -461,8 +457,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	} else {
 		if (kecheng_kc_330b_log_info_get(sdi, buf) != SR_OK)
 			return SR_ERR;
-		stored_mqflags = buf[4] ? SR_MQFLAG_SPL_TIME_WEIGHT_S : SR_MQFLAG_SPL_TIME_WEIGHT_F;
-		stored_mqflags |= buf[5] ? SR_MQFLAG_SPL_FREQ_WEIGHT_C : SR_MQFLAG_SPL_FREQ_WEIGHT_A;
+		devc->mqflags = buf[4] ? SR_MQFLAG_SPL_TIME_WEIGHT_S : SR_MQFLAG_SPL_TIME_WEIGHT_F;
+		devc->mqflags |= buf[5] ? SR_MQFLAG_SPL_FREQ_WEIGHT_C : SR_MQFLAG_SPL_FREQ_WEIGHT_A;
 		devc->stored_samples = (buf[7] << 8) | buf[8];
 		if (devc->stored_samples == 0) {
 			/* Notify frontend of empty log by sending start/end packets. */
@@ -489,7 +485,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	if (!(devc->xfer = libusb_alloc_transfer(0)))
 		return SR_ERR;
 
-	usb_source_add(drvc->sr_ctx, 10,
+	usb_source_add(sdi->session, drvc->sr_ctx, 10,
 		kecheng_kc_330b_handle_events, (void *)sdi);
 
 	if (devc->data_source == DATA_SOURCE_LIVE) {
@@ -571,5 +567,5 @@ SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/kecheng-kc-330b/protocol.c b/src/hardware/kecheng-kc-330b/protocol.c
similarity index 96%
rename from hardware/kecheng-kc-330b/protocol.c
rename to src/hardware/kecheng-kc-330b/protocol.c
index 072eb7e..e4550d2 100644
--- a/hardware/kecheng-kc-330b/protocol.c
+++ b/src/hardware/kecheng-kc-330b/protocol.c
@@ -17,15 +17,16 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include "protocol.h"
 
 extern struct sr_dev_driver kecheng_kc_330b_driver_info;
-static struct sr_dev_driver *di = &kecheng_kc_330b_driver_info;
 extern const uint64_t kecheng_kc_330b_sample_intervals[][2];
 
 SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data)
 {
+	struct sr_dev_driver *di;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
@@ -40,10 +41,11 @@ SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data)
 	(void)fd;
 	(void)revents;
 
-	drvc = di->priv;
 	sdi = cb_data;
 	devc = sdi->priv;
 	usb = sdi->conn;
+	di = sdi->driver;
+	drvc = di->context;
 
 	memset(&tv, 0, sizeof(struct timeval));
 	libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
@@ -51,7 +53,7 @@ SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data)
 
 	if (sdi->status == SR_ST_STOPPING) {
 		libusb_free_transfer(devc->xfer);
-		usb_source_remove(drvc->sr_ctx);
+		usb_source_remove(sdi->session, drvc->sr_ctx);
 		packet.type = SR_DF_END;
 		sr_session_send(cb_data, &packet);
 		sdi->status = SR_ST_ACTIVE;
@@ -106,24 +108,24 @@ static void send_data(const struct sr_dev_inst *sdi, void *buf, unsigned int buf
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 
 	devc = sdi->priv;
 
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 	analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
 	analog.mqflags = devc->mqflags;
 	analog.unit = SR_UNIT_DECIBEL_SPL;
 	analog.channels = sdi->channels;
 	analog.num_samples = buf_len;
 	analog.data = buf;
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 
 }
 
-SR_PRIV void kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer)
+SR_PRIV void LIBUSB_CALL kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer)
 {
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
diff --git a/hardware/kecheng-kc-330b/protocol.h b/src/hardware/kecheng-kc-330b/protocol.h
similarity index 94%
rename from hardware/kecheng-kc-330b/protocol.h
rename to src/hardware/kecheng-kc-330b/protocol.h
index 09152b9..a718bdb 100644
--- a/hardware/kecheng-kc-330b/protocol.h
+++ b/src/hardware/kecheng-kc-330b/protocol.h
@@ -22,12 +22,12 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "kecheng-kc-330b"
 
-#define EP_IN 0x80 | 1
+#define EP_IN (0x80 | 1)
 #define EP_OUT 2
 
 /* 500ms */
@@ -91,7 +91,7 @@ struct dev_context {
 };
 
 SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data);
-SR_PRIV void kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer);
 SR_PRIV int kecheng_kc_330b_configure(const struct sr_dev_inst *sdi);
 SR_PRIV int kecheng_kc_330b_set_date_time(struct sr_dev_inst *sdi);
 SR_PRIV int kecheng_kc_330b_recording_get(const struct sr_dev_inst *sdi,
diff --git a/src/hardware/kern-scale/api.c b/src/hardware/kern-scale/api.c
new file mode 100644
index 0000000..0443f5e
--- /dev/null
+++ b/src/hardware/kern-scale/api.c
@@ -0,0 +1,267 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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 <config.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_SCALE,
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+};
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct scale_info *scale;
+	struct sr_config *src;
+	GSList *l, *devices;
+	const char *conn, *serialcomm;
+	struct sr_dev_inst *sdi;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_serial_dev_inst *serial;
+	int ret;
+	size_t len;
+	uint8_t buf[128];
+
+	scale = (struct scale_info *)di;
+
+	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 = scale->conn;
+
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
+
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+		return NULL;
+
+	sr_info("Probing serial port %s.", conn);
+
+	drvc = di->context;
+	devices = NULL;
+	serial_flush(serial);
+
+	sr_spew("Set O1 mode (continuous values, stable and unstable ones).");
+	if (serial_write_nonblocking(serial, "O1\r\n", 4) != 4)
+		goto scan_cleanup;
+	/* Device replies with "A00\r\n" (OK) or "E01\r\n" (Error). Ignore. */
+
+	/* Let's get a bit of data and see if we can find a packet. */
+	len = sizeof(buf);
+	ret = serial_stream_detect(serial, buf, &len, scale->packet_size,
+				   scale->packet_valid, 3000, scale->baudrate);
+	if (ret != SR_OK)
+		goto scan_cleanup;
+
+	sr_info("Found device on port %s.", conn);
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(scale->vendor);
+	sdi->model = g_strdup(scale->device);
+	devc = g_malloc0(sizeof(struct dev_context));
+	sdi->inst_type = SR_INST_SERIAL;
+	sdi->conn = serial;
+	sdi->priv = devc;
+	sdi->driver = di;
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "Mass");
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+	devices = g_slist_append(devices, sdi);
+
+scan_cleanup:
+	serial_close(serial);
+
+	return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return dev_clear(di);
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (!(devc = sdi->priv))
+		return SR_ERR_BUG;
+
+	switch (key) {
+	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:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	(void)sdi;
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int 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))
+		return SR_ERR_BUG;
+
+	devc->cb_data = cb_data;
+
+	serial = sdi->conn;
+
+	sr_spew("Set O1 mode (continuous values, stable and unstable ones).");
+	if (serial_write_nonblocking(serial, "O1\r\n", 4) != 4)
+		return SR_ERR;
+	/* Device replies with "A00\r\n" (OK) or "E01\r\n" (Error). Ignore. */
+
+	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_source_add(sdi->session, serial, G_IO_IN, 50,
+		      kern_scale_receive_data, (void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+			sdi->conn, LOG_PREFIX);
+}
+
+#define SCALE(ID, CHIPSET, VENDOR, MODEL, CONN, BAUDRATE, PACKETSIZE, \
+			VALID, PARSE) \
+	&(struct scale_info) { \
+		{ \
+			.name = ID, \
+			.longname = VENDOR " " MODEL, \
+			.api_version = 1, \
+			.init = init, \
+			.cleanup = cleanup, \
+			.scan = scan, \
+			.dev_list = dev_list, \
+			.dev_clear = dev_clear, \
+			.config_get = NULL, \
+			.config_set = config_set, \
+			.config_list = config_list, \
+			.dev_open = std_serial_dev_open, \
+			.dev_close = std_serial_dev_close, \
+			.dev_acquisition_start = dev_acquisition_start, \
+			.dev_acquisition_stop = dev_acquisition_stop, \
+			.context = NULL, \
+		}, \
+		VENDOR, MODEL, CONN, BAUDRATE, PACKETSIZE, \
+		VALID, PARSE, sizeof(struct CHIPSET##_info) \
+	}
+
+/*
+ * Some scales have (user-configurable) 14-byte or 15-byte packets.
+ * We transparently support both variants by specifying the larger value
+ * below and due to the way the stream parser works.
+ *
+ * The scales have a standard baudrate (model dependent) as listed below,
+ * but serial parameters are user-configurable. We support that by letting
+ * the user override them via "serialcomm".
+ */
+
+SR_PRIV const struct scale_info *kern_scale_drivers[] = {
+	SCALE(
+		"kern-ew-6200-2nm", kern,
+		"KERN", "EW 6200-2NM", "1200/8n2", 1200,
+		15 /* (or 14) */, sr_kern_packet_valid, sr_kern_parse
+	),
+	NULL
+};
diff --git a/src/hardware/kern-scale/protocol.c b/src/hardware/kern-scale/protocol.c
new file mode 100644
index 0000000..8845ae3
--- /dev/null
+++ b/src/hardware/kern-scale/protocol.c
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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 <config.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi,
+			  void *info)
+{
+	struct scale_info *scale;
+	float floatval;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+	struct dev_context *devc;
+
+	scale = (struct scale_info *)sdi->driver;
+
+	devc = sdi->priv;
+
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
+
+	analog.channels = sdi->channels;
+	analog.num_samples = 1;
+	analog.mq = -1;
+
+	scale->packet_parse(buf, &floatval, &analog, info);
+	analog.data = &floatval;
+
+	if (analog.mq != -1) {
+		/* Got a measurement. */
+		packet.type = SR_DF_ANALOG_OLD;
+		packet.payload = &analog;
+		sr_session_send(devc->cb_data, &packet);
+		devc->num_samples++;
+	}
+}
+
+static void handle_new_data(struct sr_dev_inst *sdi, void *info)
+{
+	struct scale_info *scale;
+	struct dev_context *devc;
+	int len, i, offset = 0;
+	struct sr_serial_dev_inst *serial;
+
+	scale = (struct scale_info *)sdi->driver;
+
+	devc = sdi->priv;
+	serial = sdi->conn;
+
+	/* Try to get as much data as the buffer can hold. */
+	len = SCALE_BUFSIZE - devc->buflen;
+	len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
+	if (len == 0)
+		return; /* No new bytes, nothing to do. */
+	if (len < 0) {
+		sr_err("Serial port read error: %d.", len);
+		return;
+	}
+	devc->buflen += len;
+
+	/* Now look for packets in that data. */
+	while ((devc->buflen - offset) >= scale->packet_size) {
+		if (scale->packet_valid(devc->buf + offset)) {
+			handle_packet(devc->buf + offset, sdi, info);
+			offset += scale->packet_size;
+		} else {
+			offset++;
+		}
+	}
+
+	/* If we have any data left, move it to the beginning of our buffer. */
+	for (i = 0; i < devc->buflen - offset; i++)
+		devc->buf[i] = devc->buf[offset + i];
+	devc->buflen -= offset;
+}
+
+SR_PRIV int kern_scale_receive_data(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct scale_info *scale;
+	int64_t time;
+	void *info;
+
+	(void)fd;
+
+	if (!(sdi = cb_data))
+		return TRUE;
+
+	if (!(devc = sdi->priv))
+		return TRUE;
+
+	scale = (struct scale_info *)sdi->driver;
+
+	if (revents == G_IO_IN) {
+		/* Serial data arrived. */
+		info = g_malloc(scale->info_size);
+		handle_new_data(sdi, info);
+		g_free(info);
+	}
+
+	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;
+	}
+
+	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.");
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			return TRUE;
+		}
+	}
+
+	return TRUE;
+}
diff --git a/hardware/center-3xx/protocol.h b/src/hardware/kern-scale/protocol.h
similarity index 53%
rename from hardware/center-3xx/protocol.h
rename to src/hardware/kern-scale/protocol.h
index 71e2630..fce4700 100644
--- a/hardware/center-3xx/protocol.h
+++ b/src/hardware/kern-scale/protocol.h
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2013 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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
@@ -18,49 +18,41 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
-#ifndef LIBSIGROK_HARDWARE_CENTER_3XX_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_CENTER_3XX_PROTOCOL_H
-
-#include <stdint.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <glib.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "center-3xx"
-
-/* Note: When adding entries here, don't forget to update CENTER_DEV_COUNT. */
-enum {
-	CENTER_309,
-	VOLTCRAFT_K204,
-};
-
-#define CENTER_DEV_COUNT 2
-
-struct center_dev_info {
-	char *vendor;
-	char *device;
-	char *conn;
-	int num_channels;
-	uint32_t max_sample_points;
-	uint8_t packet_size;
+#ifndef LIBSIGROK_HARDWARE_KERN_SCALE_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_KERN_SCALE_PROTOCOL_H
+
+#define LOG_PREFIX "kern-scale"
+
+struct scale_info {
+	/** libsigrok driver info struct. */
+	struct sr_dev_driver di;
+	/** Manufacturer/brand. */
+	const char *vendor;
+	/** Model. */
+	const char *device;
+	/** serialconn string. */
+	const char *conn;
+	/** Baud rate. */
+	uint32_t baudrate;
+	/** Packet size in bytes. */
+	int packet_size;
+	/** Packet validation function. */
 	gboolean (*packet_valid)(const uint8_t *);
-	struct sr_dev_driver *di;
-	int (*receive_data)(int, int, void *);
+	/** Packet parsing function. */
+	int (*packet_parse)(const uint8_t *, float *,
+			    struct sr_datafeed_analog_old *, void *);
+	/** Size of chipset info struct. */
+	gsize info_size;
 };
 
-extern SR_PRIV const struct center_dev_info center_devs[CENTER_DEV_COUNT];
-
-#define SERIAL_BUFSIZE 256
+#define SCALE_BUFSIZE 256
 
 /** 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). */
+	/** The time limit (in milliseconds). */
 	uint64_t limit_msec;
 
 	/** Opaque pointer passed in by the frontend. */
@@ -69,16 +61,14 @@ struct dev_context {
 	/** The current number of already received samples. */
 	uint64_t num_samples;
 
+	/** The starting time of current sampling run. */
 	int64_t starttime;
 
-	uint8_t buf[SERIAL_BUFSIZE];
+	uint8_t buf[SCALE_BUFSIZE];
 	int bufoffset;
 	int buflen;
 };
 
-SR_PRIV gboolean center_3xx_packet_valid(const uint8_t *buf);
-
-SR_PRIV int receive_data_CENTER_309(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_K204(int fd, int revents, void *cb_data);
+SR_PRIV int kern_scale_receive_data(int fd, int revents, void *cb_data);
 
 #endif
diff --git a/src/hardware/korad-kaxxxxp/api.c b/src/hardware/korad-kaxxxxp/api.c
new file mode 100644
index 0000000..8753b1a
--- /dev/null
+++ b/src/hardware/korad-kaxxxxp/api.c
@@ -0,0 +1,436 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+
+static const uint32_t drvopts[] = {
+	/* Device class */
+	SR_CONF_POWER_SUPPLY,
+};
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t devopts[] = {
+	/* Device class */
+	SR_CONF_POWER_SUPPLY,
+	/* Acquisition modes. */
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+	/* Device configuration */
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_REGULATION | SR_CONF_GET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const struct korad_kaxxxxp_model models[] = {
+	/* Device enum, vendor, model, ID reply, channels, voltage, current */
+	{VELLEMAN_PS3005D, "Velleman", "PS3005D",
+		"VELLEMANPS3005DV2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+	{VELLEMAN_LABPS3005D, "Velleman", "LABPS3005D",
+		"VELLEMANLABPS3005DV2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+	{KORAD_KA3005P, "Korad", "KA3005P",
+		"KORADKA3005PV2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+	/* Sometimes the KA3005P has an extra 0x01 after the ID. */
+	{KORAD_KA3005P_0X01, "Korad", "KA3005P",
+		"KORADKA3005PV2.0\x01", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+	ALL_ZERO
+};
+
+SR_PRIV struct sr_dev_driver korad_kaxxxxp_driver_info;
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	GSList *devices, *l;
+	struct sr_dev_inst *sdi;
+	struct sr_config *src;
+	const char *conn, *serialcomm;
+	struct sr_serial_dev_inst *serial;
+	char reply[50];
+	int i, model_id;
+	unsigned int len;
+
+	devices = NULL;
+	conn = NULL;
+	serialcomm = NULL;
+	drvc = di->context;
+	drvc->instances = 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;
+		default:
+			sr_err("Unknown option %d, skipping.", src->key);
+			break;
+		}
+	}
+
+	if (!conn)
+		return NULL;
+	if (!serialcomm)
+		serialcomm = "9600/8n1";
+
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+		return NULL;
+
+	serial_flush(serial);
+
+	/* Get the device model. */
+	len = 0;
+	for (i = 0; models[i].id; i++) {
+		if (strlen(models[i].id) > len)
+			len = strlen(models[i].id);
+	}
+	memset(&reply, 0, sizeof(reply));
+	sr_dbg("Want max %d bytes.", len);
+	if ((korad_kaxxxxp_send_cmd(serial, "*IDN?") < 0))
+		return NULL;
+
+	/* i is used here for debug purposes only. */
+	if ((i = korad_kaxxxxp_read_chars(serial, len, reply)) < 0)
+		return NULL;
+	sr_dbg("Received: %d, %s", i, reply);
+	model_id = -1;
+	for (i = 0; models[i].id; i++) {
+		if (!strcmp(models[i].id, reply))
+			model_id = i;
+	}
+	if (model_id < 0) {
+		sr_err("Unknown model ID '%s' detected, aborting.", reply);
+		return NULL;
+	}
+	sr_dbg("Found: %s %s (idx %d, ID '%s').", models[model_id].vendor,
+		models[model_id].name, model_id, models[model_id].id);
+
+	/* Init device instance, etc. */
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(models[model_id].vendor);
+	sdi->model = g_strdup(models[model_id].name);
+	sdi->inst_type = SR_INST_SERIAL;
+	sdi->conn = serial;
+	sdi->driver = di;
+
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
+
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->model = &models[model_id];
+	devc->reply[5] = 0;
+	devc->req_sent_at = 0;
+	sdi->priv = devc;
+
+	/* Get current status of device. */
+	if (korad_kaxxxxp_get_all_values(serial, devc) < 0)
+		goto exit_err;
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+	devices = g_slist_append(devices, sdi);
+
+	serial_close(serial);
+	if (!devices)
+		sr_serial_dev_inst_free(serial);
+
+	return devices;
+
+exit_err:
+	sr_dev_inst_free(sdi);
+	g_free(devc);
+	sr_dbg("Scan failed.");
+
+	return NULL;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	dev_clear(di);
+	return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+	const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (!sdi || !data)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	switch (key) {
+	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_VOLTAGE:
+		*data = g_variant_new_double(devc->voltage);
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		*data = g_variant_new_double(devc->voltage_max);
+		break;
+	case SR_CONF_CURRENT:
+		*data = g_variant_new_double(devc->current);
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		*data = g_variant_new_double(devc->current_max);
+		break;
+	case SR_CONF_ENABLED:
+		*data = g_variant_new_boolean(devc->output_enabled);
+		break;
+	case SR_CONF_REGULATION:
+		/* Dual channel not supported. */
+		*data = g_variant_new_string((devc->cc_mode[0]) ? "CC" : "CV");
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+		*data = g_variant_new_boolean(devc->ocp_enabled);
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+		*data = g_variant_new_boolean(devc->ovp_enabled);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+	const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	double dval;
+	gboolean bval;
+
+	(void)cg;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+
+	switch (key) {
+	case SR_CONF_LIMIT_MSEC:
+		if (g_variant_get_uint64(data) == 0)
+			return SR_ERR_ARG;
+		devc->limit_msec = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		if (g_variant_get_uint64(data) == 0)
+			return SR_ERR_ARG;
+		devc->limit_samples = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		dval = g_variant_get_double(data);
+		if (dval < devc->model->voltage[0] || dval > devc->model->voltage[1])
+			return SR_ERR_ARG;
+		devc->voltage_max = dval;
+		devc->target = KAXXXXP_VOLTAGE_MAX;
+		if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+			return SR_ERR;
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		dval = g_variant_get_double(data);
+		if (dval < devc->model->current[0] || dval > devc->model->current[1])
+			return SR_ERR_ARG;
+		devc->current_max = dval;
+		devc->target = KAXXXXP_CURRENT_MAX;
+		if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+			return SR_ERR;
+		break;
+	case SR_CONF_ENABLED:
+		bval = g_variant_get_boolean(data);
+		/* Set always so it is possible turn off with sigrok-cli. */
+		devc->output_enabled = bval;
+		devc->target = KAXXXXP_OUTPUT;
+		if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+			return SR_ERR;
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+		bval = g_variant_get_boolean(data);
+		devc->ocp_enabled = bval;
+		devc->target = KAXXXXP_OCP;
+		if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+			return SR_ERR;
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+		bval = g_variant_get_boolean(data);
+		devc->ovp_enabled = bval;
+		devc->target = KAXXXXP_OVP;
+		if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0)
+			return SR_ERR;
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+	const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+
+	struct dev_context *devc;
+	GVariant *gvar;
+	GVariantBuilder gvb;
+	double dval;
+	int idx;
+
+	(void)cg;
+
+	/* Always available (with or without sdi). */
+	if (key == SR_CONF_SCAN_OPTIONS) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	/* Return drvopts without sdi (and devopts with sdi, see below). */
+	if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	/* Every other key needs an sdi. */
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	switch (key) {
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+		/* Min, max, step. */
+		for (idx = 0; idx < 3; idx++) {
+			dval = devc->model->voltage[idx];
+			gvar = g_variant_new_double(dval);
+			g_variant_builder_add_value(&gvb, gvar);
+		}
+		*data = g_variant_builder_end(&gvb);
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+		/* Min, max, step. */
+		for (idx = 0; idx < 3; idx++) {
+			dval = devc->model->current[idx];
+			gvar = g_variant_new_double(dval);
+			g_variant_builder_add_value(&gvb, gvar);
+		}
+		*data = g_variant_builder_end(&gvb);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int 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;
+
+	devc = sdi->priv;
+	devc->cb_data = cb_data;
+
+	/* Send header packet to the session bus. */
+	std_session_send_df_header(cb_data, LOG_PREFIX);
+
+	devc->starttime = g_get_monotonic_time();
+	devc->num_samples = 0;
+	devc->reply_pending = FALSE;
+	devc->req_sent_at = 0;
+	serial = sdi->conn;
+	serial_source_add(sdi->session, serial, G_IO_IN,
+			KAXXXXP_POLL_INTERVAL_MS,
+			korad_kaxxxxp_receive_data, (void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	return std_serial_dev_acquisition_stop(sdi, cb_data,
+		std_serial_dev_close, sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver korad_kaxxxxp_driver_info = {
+	.name = "korad-kaxxxxp",
+	.longname = "Korad KAxxxxP",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = std_serial_dev_open,
+	.dev_close = std_serial_dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/korad-kaxxxxp/protocol.c b/src/hardware/korad-kaxxxxp/protocol.c
new file mode 100644
index 0000000..60c4545
--- /dev/null
+++ b/src/hardware/korad-kaxxxxp/protocol.c
@@ -0,0 +1,406 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+
+#define REQ_TIMEOUT_MS 500
+#define DEVICE_PROCESSING_TIME_MS 80
+
+SR_PRIV int korad_kaxxxxp_send_cmd(struct sr_serial_dev_inst *serial,
+				const char *cmd)
+{
+	int ret;
+
+	sr_dbg("Sending '%s'.", cmd);
+	if ((ret = serial_write_blocking(serial, cmd, strlen(cmd), 0)) < 0) {
+		sr_err("Error sending command: %d.", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+SR_PRIV int korad_kaxxxxp_read_chars(struct sr_serial_dev_inst *serial,
+				int count, char *buf)
+{
+	int ret, received, turns;
+
+	received = 0;
+	turns = 0;
+
+	do {
+		if ((ret = serial_read_blocking(serial, buf + received,
+				count - received,
+				serial_timeout(serial, count))) < 0) {
+			sr_err("Error %d reading %d bytes from device.",
+			       ret, count);
+			return ret;
+		}
+		received += ret;
+		turns++;
+	} while ((received < count) && (turns < 100));
+
+	buf[count] = 0;
+
+	sr_spew("Received: '%s'.", buf);
+
+	return ret;
+}
+
+static void give_device_time_to_process(struct dev_context *devc)
+{
+	int64_t sleeping_time;
+
+	sleeping_time = devc->req_sent_at + (DEVICE_PROCESSING_TIME_MS * 1000);
+	sleeping_time -= g_get_monotonic_time();
+
+	if (sleeping_time > 0) {
+		g_usleep(sleeping_time);
+		sr_spew("Sleeping for processing %" PRIi64 " usec", sleeping_time);
+	}
+}
+
+SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial,
+				struct dev_context *devc)
+{
+	char msg[21];
+	const char *cmd;
+	float value;
+	int ret;
+
+	give_device_time_to_process(devc);
+
+	msg[20] = 0;
+	switch (devc->target) {
+	case KAXXXXP_CURRENT:
+	case KAXXXXP_VOLTAGE:
+	case KAXXXXP_STATUS:
+		sr_err("Can't set measurable parameter.");
+		return SR_ERR;
+	case KAXXXXP_CURRENT_MAX:
+		cmd = "ISET1:%05.3f";
+		value = devc->current_max;
+		break;
+	case KAXXXXP_VOLTAGE_MAX:
+		cmd = "VSET1:%05.2f";
+		value = devc->voltage_max;
+		break;
+	case KAXXXXP_OUTPUT:
+		cmd = "OUT%01.0f";
+		value = (devc->output_enabled) ? 1 : 0;
+		break;
+	case KAXXXXP_BEEP:
+		cmd = "BEEP%01.0f";
+		value = (devc->beep_enabled) ? 1 : 0;
+		break;
+	case KAXXXXP_OCP:
+		cmd = "OCP%01.0f";
+		value = (devc->ocp_enabled) ? 1 : 0;
+		break;
+	case KAXXXXP_OVP:
+		cmd = "OVP%01.0f";
+		value = (devc->ovp_enabled) ? 1 : 0;
+		break;
+	case KAXXXXP_SAVE:
+		cmd = "SAV%01.0f";
+		if (devc->program < 1 || devc->program > 5) {
+			sr_err("Only programs 1-5 supported and %d isn't "
+			       "between them.", devc->program);
+			return SR_ERR;
+		}
+		value = devc->program;
+		break;
+	case KAXXXXP_RECALL:
+		cmd = "RCL%01.0f";
+		if (devc->program < 1 || devc->program > 5) {
+			sr_err("Only programs 1-5 supported and %d isn't "
+			       "between them.", devc->program);
+			return SR_ERR;
+		}
+		value = devc->program;
+		break;
+	default:
+		sr_err("Don't know how to set %d.", devc->target);
+		return SR_ERR;
+	}
+
+	if (cmd)
+		snprintf(msg, 20, cmd, value);
+
+	ret = korad_kaxxxxp_send_cmd(serial, msg);
+	devc->req_sent_at = g_get_monotonic_time();
+	devc->reply_pending = FALSE;
+
+	return ret;
+}
+
+SR_PRIV int korad_kaxxxxp_query_value(struct sr_serial_dev_inst *serial,
+				struct dev_context *devc)
+{
+	int ret;
+
+	give_device_time_to_process(devc);
+
+	switch (devc->target) {
+	case KAXXXXP_CURRENT:
+		/* Read current from device. */
+		ret = korad_kaxxxxp_send_cmd(serial, "IOUT1?");
+		break;
+	case KAXXXXP_CURRENT_MAX:
+		/* Read set current from device. */
+		ret = korad_kaxxxxp_send_cmd(serial, "ISET1?");
+		break;
+	case KAXXXXP_VOLTAGE:
+		/* Read voltage from device. */
+		ret = korad_kaxxxxp_send_cmd(serial, "VOUT1?");
+		break;
+	case KAXXXXP_VOLTAGE_MAX:
+		/* Read set voltage from device. */
+		ret = korad_kaxxxxp_send_cmd(serial, "VSET1?");
+		break;
+	case KAXXXXP_STATUS:
+	case KAXXXXP_OUTPUT:
+		/* Read status from device. */
+		ret = korad_kaxxxxp_send_cmd(serial, "STATUS?");
+		break;
+	default:
+		sr_err("Don't know how to query %d.", devc->target);
+		return SR_ERR;
+	}
+
+	devc->req_sent_at = g_get_monotonic_time();
+	devc->reply_pending = TRUE;
+
+	return ret;
+}
+
+SR_PRIV int korad_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
+				struct dev_context *devc)
+{
+	int ret;
+
+	for (devc->target = KAXXXXP_CURRENT;
+			devc->target <= KAXXXXP_STATUS; devc->target++) {
+		if ((ret = korad_kaxxxxp_query_value(serial, devc)) < 0)
+			return ret;
+		if ((ret = korad_kaxxxxp_get_reply(serial, devc)) < 0)
+			return ret;
+	}
+
+	return ret;
+}
+
+SR_PRIV int korad_kaxxxxp_get_reply(struct sr_serial_dev_inst *serial,
+				struct dev_context *devc)
+{
+	double value;
+	int count, ret;
+	float *target;
+	char status_byte;
+
+	target = NULL;
+	count = 5;
+
+	switch (devc->target) {
+	case KAXXXXP_CURRENT:
+		/* Read current from device. */
+		target = &(devc->current);
+		break;
+	case KAXXXXP_CURRENT_MAX:
+		/* Read set current from device. */
+		target = &(devc->current_max);
+		break;
+	case KAXXXXP_VOLTAGE:
+		/* Read voltage from device. */
+		target = &(devc->voltage);
+		break;
+	case KAXXXXP_VOLTAGE_MAX:
+		/* Read set voltage from device. */
+		target = &(devc->voltage_max);
+		break;
+	case KAXXXXP_STATUS:
+	case KAXXXXP_OUTPUT:
+		/* Read status from device. */
+		count = 1;
+		break;
+	default:
+		sr_err("Don't know where to put repply %d.", devc->target);
+	}
+
+	if ((ret = korad_kaxxxxp_read_chars(serial, count, devc->reply)) < 0)
+		return ret;
+
+	devc->reply[count] = 0;
+
+	if (target) {
+		value = g_ascii_strtod(devc->reply, NULL);
+		*target = (float)value;
+		sr_dbg("value: %f",value);
+	} else {
+		/* We have status reply. */
+		status_byte = devc->reply[0];
+		/* Constant current */
+		devc->cc_mode[0] = !(status_byte & (1 << 0)); /* Channel one */
+		devc->cc_mode[1] = !(status_byte & (1 << 1)); /* Channel two */
+		/*
+		 * Tracking
+		 * status_byte & ((1 << 2) | (1 << 3))
+		 * 00 independent 01 series 11 parallel
+		 */
+		devc->beep_enabled = (1 << 4);
+		devc->ocp_enabled = (status_byte & (1 << 5));
+		devc->output_enabled = (status_byte & (1 << 6));
+		/* Velleman LABPS3005 quirk */
+		if (devc->output_enabled)
+			devc->ovp_enabled = (status_byte & (1 << 7));
+		sr_dbg("Status: 0x%02x", status_byte);
+		sr_spew("Status: CH1: constant %s CH2: constant %s. "
+			"Tracking would be %s. Device is "
+			"%s and %s. Buttons are %s. Output is %s "
+			"and extra byte is %s.",
+			(status_byte & (1 << 0)) ? "voltage" : "current",
+			(status_byte & (1 << 1)) ? "voltage" : "current",
+			(status_byte & (1 << 2)) ? "parallel" : "series",
+			(status_byte & (1 << 3)) ? "tracking" : "independent",
+			(status_byte & (1 << 4)) ? "beeping" : "silent",
+			(status_byte & (1 << 5)) ? "locked" : "unlocked",
+			(status_byte & (1 << 6)) ? "enabled" : "disabled",
+			(status_byte & (1 << 7)) ? "true" : "false");
+	}
+	/* Read the sixth byte from ISET? BUG workaround. */
+	if (devc->target == KAXXXXP_CURRENT_MAX)
+		serial_read_blocking(serial, &status_byte, 1, 10);
+	devc->reply_pending = FALSE;
+
+	return ret;
+}
+
+static void next_measurement(struct dev_context *devc)
+{
+	switch (devc->target) {
+	case KAXXXXP_CURRENT:
+		devc->target = KAXXXXP_VOLTAGE;
+		break;
+	case KAXXXXP_CURRENT_MAX:
+		devc->target = KAXXXXP_CURRENT;
+		break;
+	case KAXXXXP_VOLTAGE:
+		devc->target = KAXXXXP_STATUS;
+		break;
+	case KAXXXXP_VOLTAGE_MAX:
+		devc->target = KAXXXXP_CURRENT;
+		break;
+	/* Read back what was set. */
+	case KAXXXXP_BEEP:
+	case KAXXXXP_OCP:
+	case KAXXXXP_OVP:
+	case KAXXXXP_OUTPUT:
+		devc->target = KAXXXXP_STATUS;
+		break;
+	case KAXXXXP_STATUS:
+		devc->target = KAXXXXP_CURRENT;
+		break;
+	default:
+		devc->target = KAXXXXP_CURRENT;
+	}
+}
+
+SR_PRIV int korad_kaxxxxp_receive_data(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct sr_serial_dev_inst *serial;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+	int64_t t, elapsed_us;
+
+	(void)fd;
+
+	if (!(sdi = cb_data))
+		return TRUE;
+
+	if (!(devc = sdi->priv))
+		return TRUE;
+
+	serial = sdi->conn;
+
+	if (revents == G_IO_IN) {
+		/* Get the value. */
+		korad_kaxxxxp_get_reply(serial, devc);
+
+		/* Send the value forward. */
+		packet.type = SR_DF_ANALOG_OLD;
+		packet.payload = &analog;
+		analog.channels = sdi->channels;
+		analog.num_samples = 1;
+		if (devc->target == KAXXXXP_CURRENT) {
+			analog.mq = SR_MQ_CURRENT;
+			analog.unit = SR_UNIT_AMPERE;
+			analog.mqflags = 0;
+			analog.data = &devc->current;
+			sr_session_send(sdi, &packet);
+		}
+		if (devc->target == KAXXXXP_VOLTAGE) {
+			analog.mq = SR_MQ_VOLTAGE;
+			analog.unit = SR_UNIT_VOLT;
+			analog.mqflags = SR_MQFLAG_DC;
+			analog.data = &devc->voltage;
+			sr_session_send(sdi, &packet);
+			devc->num_samples++;
+		}
+		next_measurement(devc);
+	} else {
+		/* Time out */
+		if (!devc->reply_pending) {
+			if (korad_kaxxxxp_query_value(serial, devc) < 0)
+				return TRUE;
+			devc->req_sent_at = g_get_monotonic_time();
+			devc->reply_pending = TRUE;
+		}
+	}
+
+	if (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;
+	}
+
+	if (devc->limit_msec) {
+		t = (g_get_monotonic_time() - devc->starttime) / 1000;
+		if (t > (int64_t)devc->limit_msec) {
+			sr_info("Requested time limit reached.");
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			return TRUE;
+		}
+	}
+
+	/* Request next packet, if required. */
+	if (sdi->status == SR_ST_ACTIVE) {
+		if (devc->reply_pending) {
+			elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+			if (elapsed_us > (REQ_TIMEOUT_MS * 1000))
+				devc->reply_pending = FALSE;
+			return TRUE;
+		}
+
+	}
+
+	return TRUE;
+}
diff --git a/src/hardware/korad-kaxxxxp/protocol.h b/src/hardware/korad-kaxxxxp/protocol.h
new file mode 100644
index 0000000..77c4eb6
--- /dev/null
+++ b/src/hardware/korad-kaxxxxp/protocol.h
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae at 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/>.
+ */
+
+/**
+ * @file
+ * Korad KAxxxxP power supply driver
+ * @internal
+ */
+
+#ifndef LIBSIGROK_HARDWARE_KORAD_KAXXXXP_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_KORAD_KAXXXXP_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "korad-kaxxxxp"
+
+#define KAXXXXP_POLL_INTERVAL_MS 80
+
+enum {
+	VELLEMAN_PS3005D,
+	VELLEMAN_LABPS3005D,
+	KORAD_KA3005P,
+	KORAD_KA3005P_0X01,
+	/* Support for future devices with this protocol. */
+};
+
+/* Information on single model */
+struct korad_kaxxxxp_model {
+	int model_id; /**< Model info */
+	const char *vendor; /**< Vendor name */
+	const char *name; /**< Model name */
+	const char *id; /**< Model ID, as delivered by interface */
+	int channels; /**< Number of channels */
+	double voltage[3]; /**< Min, max, step */
+	double current[3]; /**< Min, max, step */
+};
+
+/* Reply targets */
+enum {
+	KAXXXXP_CURRENT,
+	KAXXXXP_CURRENT_MAX,
+	KAXXXXP_VOLTAGE,
+	KAXXXXP_VOLTAGE_MAX,
+	KAXXXXP_STATUS,
+	KAXXXXP_OUTPUT,
+	KAXXXXP_BEEP,
+	KAXXXXP_OCP,
+	KAXXXXP_OVP,
+	KAXXXXP_SAVE,
+	KAXXXXP_RECALL,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/* Model-specific information */
+	const struct korad_kaxxxxp_model *model; /**< Model information. */
+
+	/* Acquisition settings */
+	uint64_t limit_samples;
+	uint64_t limit_msec;
+	uint64_t num_samples;
+	int64_t starttime;
+	int64_t req_sent_at;
+	gboolean reply_pending;
+
+	void *cb_data;
+
+	/* Operational state */
+	float current;          /**< Last current value [A] read from device. */
+	float current_max;      /**< Output current set. */
+	float voltage;          /**< Last voltage value [V] read from device. */
+	float voltage_max;      /**< Output voltage set. */
+	gboolean cc_mode[2];    /**< Device is in CC mode (otherwise CV). */
+
+	gboolean output_enabled; /**< Is the output enabled? */
+	gboolean beep_enabled;   /**< Enable beeper. */
+	gboolean ocp_enabled;    /**< Output current protection enabled. */
+	gboolean ovp_enabled;    /**< Output voltage protection enabled. */
+
+	/* Temporary state across callbacks */
+	int target;              /**< What reply to expect. */
+	int program;             /**< Program to store or recall. */
+	char reply[6];
+};
+
+SR_PRIV int korad_kaxxxxp_send_cmd(struct sr_serial_dev_inst *serial,
+					const char *cmd);
+SR_PRIV int korad_kaxxxxp_read_chars(struct sr_serial_dev_inst *serial,
+					int count, char *buf);
+SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial,
+					struct dev_context *devc);
+SR_PRIV int korad_kaxxxxp_query_value(struct sr_serial_dev_inst *serial,
+					struct dev_context *devc);
+SR_PRIV int korad_kaxxxxp_get_reply(struct sr_serial_dev_inst *serial,
+					struct dev_context *devc);
+SR_PRIV int korad_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
+					struct dev_context *devc);
+SR_PRIV int korad_kaxxxxp_receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/hardware/lascar-el-usb/api.c b/src/hardware/lascar-el-usb/api.c
similarity index 84%
rename from hardware/lascar-el-usb/api.c
rename to src/hardware/lascar-el-usb/api.c
index 657d993..c8938f2 100644
--- a/hardware/lascar-el-usb/api.c
+++ b/src/hardware/lascar-el-usb/api.c
@@ -17,32 +17,33 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <glib.h>
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
 SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info;
-static struct sr_dev_driver *di = &lascar_el_usb_driver_info;
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_THERMOMETER,
 	SR_CONF_HYGROMETER,
-	SR_CONF_DATALOG,
-	SR_CONF_LIMIT_SAMPLES,
+	SR_CONF_CONN | SR_CONF_GET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_DATALOG | SR_CONF_GET | SR_CONF_SET,
 };
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct sr_dev_inst *sdi;
@@ -51,7 +52,7 @@ static GSList *scan(GSList *options)
 	GSList *usb_devices, *devices, *l;
 	const char *conn;
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	conn = NULL;
 	for (l = options; l; l = l->next) {
@@ -88,18 +89,19 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct drv_context *drvc;
 	struct sr_usb_dev_inst *usb;
 	int ret;
 
-	if (!(drvc = di->priv)) {
+	if (!(drvc = di->context)) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -120,9 +122,10 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct sr_usb_dev_inst *usb;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -141,24 +144,22 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	int ret;
 	struct drv_context *drvc;
 
-	if (!(drvc = di->priv))
+	if (!(drvc = di->context))
 		/* Can get called on an unused driver, doesn't matter. */
 		return SR_OK;
 
-
 	ret = std_dev_clear(di, NULL);
 	g_free(drvc);
-	di->priv = NULL;
 
 	return ret;
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -169,7 +170,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	(void)cg;
 
 	devc = sdi->priv;
-	switch (id) {
+	switch (key) {
 	case SR_CONF_CONN:
 		if (!sdi || !sdi->conn)
 			return SR_ERR_ARG;
@@ -194,9 +195,10 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct dev_context *devc;
 	int ret;
 
@@ -205,27 +207,22 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
 
 	devc = sdi->priv;
 	ret = SR_OK;
-	switch (id) {
+	switch (key) {
 	case SR_CONF_DATALOG:
-		if (g_variant_get_boolean(data)) {
-			/* Start logging. */
+		if (g_variant_get_boolean(data))
 			ret = lascar_start_logging(sdi);
-		} else {
-			/* Stop logging. */
+		else
 			ret = lascar_stop_logging(sdi);
-		}
 		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:
 		ret = SR_ERR_NA;
@@ -234,7 +231,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -242,12 +239,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -256,7 +253,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static void mark_xfer(struct libusb_transfer *xfer)
+static void LIBUSB_CALL mark_xfer(struct libusb_transfer *xfer)
 {
 
 	if (xfer->status == LIBUSB_TRANSFER_COMPLETED)
@@ -304,11 +301,11 @@ static int lascar_proc_config(const struct sr_dev_inst *sdi)
 		devc->temp_unit = devc->config[0x2e] | (devc->config[0x2f] << 8);
 		if (devc->temp_unit != 0 && devc->temp_unit != 1) {
 			sr_dbg("invalid temperature unit %d", devc->temp_unit);
-			/* Default to Celcius, we're all adults here. */
+			/* Default to Celsius, we're all adults here. */
 			devc->temp_unit = 0;
 		} else
 			sr_dbg("temperature unit is %s", devc->temp_unit
-					? "Fahrenheit" : "Celcius");
+					? "Fahrenheit" : "Celsius");
 		break;
 	case LOG_CO:
 		devc->sample_size = 2;
@@ -328,6 +325,7 @@ static int lascar_proc_config(const struct sr_dev_inst *sdi)
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct sr_datafeed_packet packet;
 	struct sr_datafeed_meta meta;
 	struct sr_config *src;
@@ -343,12 +341,12 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
 
-	drvc = di->priv;
+	drvc = di->context;
 	devc = sdi->priv;
 	usb = sdi->conn;
 	devc->cb_data = cb_data;
@@ -387,14 +385,13 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR,
 			0x02, 0x0001, 0x00, NULL, 0, 50);
 
-
 	/* Flush input. The F321 requires this. */
 	while (libusb_bulk_transfer(usb->devhdl, LASCAR_EP_IN, resp,
 			256, &ret, 5) == 0 && ret > 0)
 		;
 
 	libusb_fill_bulk_transfer(xfer_in, usb->devhdl, LASCAR_EP_IN,
-			resp, sizeof(resp), mark_xfer, 0, 10000);
+			resp, sizeof(resp), mark_xfer, 0, BULK_XFER_TIMEOUT);
 	if (libusb_submit_transfer(xfer_in) != 0) {
 		libusb_free_transfer(xfer_in);
 		libusb_free_transfer(xfer_out);
@@ -415,11 +412,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	tv.tv_sec = 0;
 	tv.tv_usec = 0;
 	while (!xfer_in->user_data || !xfer_out->user_data) {
-		g_usleep(5000);
+		g_usleep(SLEEP_US_LONG);
 		libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 	}
 	if (xfer_in->user_data != GINT_TO_POINTER(1) ||
-			xfer_in->user_data != GINT_TO_POINTER(1)) {
+			xfer_out->user_data != GINT_TO_POINTER(1)) {
 		sr_dbg("no response to log transfer request");
 		libusb_free_transfer(xfer_in);
 		libusb_free_transfer(xfer_out);
@@ -434,9 +431,10 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	devc->log_size = xfer_in->buffer[1] + (xfer_in->buffer[2] << 8);
 	libusb_free_transfer(xfer_out);
 
-	usb_source_add(drvc->sr_ctx, 100, lascar_el_usb_handle_events, (void *)sdi);
+	usb_source_add(sdi->session, drvc->sr_ctx, 100,
+			lascar_el_usb_handle_events, (void *)sdi);
 
-	buf = g_try_malloc(4096);
+	buf = g_malloc(4096);
 	libusb_fill_bulk_transfer(xfer_in, usb->devhdl, LASCAR_EP_IN,
 			buf, 4096, lascar_el_usb_receive_transfer, cb_data, 100);
 	if ((ret = libusb_submit_transfer(xfer_in) != 0)) {
@@ -451,9 +449,10 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 SR_PRIV int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	(void)cb_data;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -485,5 +484,5 @@ SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/lascar-el-usb/protocol.c b/src/hardware/lascar-el-usb/protocol.c
similarity index 88%
rename from hardware/lascar-el-usb/protocol.c
rename to src/hardware/lascar-el-usb/protocol.c
index 050b42b..2584e8b 100644
--- a/hardware/lascar-el-usb/protocol.c
+++ b/src/hardware/lascar-el-usb/protocol.c
@@ -17,17 +17,18 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <sys/time.h>
 #include <string.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
 extern struct sr_dev_driver lascar_el_usb_driver_info;
-static struct sr_dev_driver *di = &lascar_el_usb_driver_info;
+struct sr_dev_driver *di = &lascar_el_usb_driver_info;
 
 static const struct elusb_profile profiles[] = {
 	{ 1, "EL-USB-1", LOG_UNSUPPORTED },
@@ -51,10 +52,9 @@ static const struct elusb_profile profiles[] = {
 	{ 19, "EL-USB-1-LCD", LOG_UNSUPPORTED },
 	{ 20, "EL-OEM-3", LOG_UNSUPPORTED },
 	{ 21, "EL-USB-1-LCD", LOG_UNSUPPORTED },
-	{ 0, NULL, 0 }
+	ALL_ZERO
 };
 
-
 static libusb_device_handle *lascar_open(struct libusb_device *dev)
 {
 	libusb_device_handle *dev_hdl;
@@ -67,7 +67,7 @@ static libusb_device_handle *lascar_open(struct libusb_device *dev)
 	}
 
 	/* Some of these fail, but it needs doing -- some sort of mode
-	 * setup for the SILabs F32x. */
+	 * setup for the SiLabs F32x. */
 	libusb_control_transfer(dev_hdl, LIBUSB_REQUEST_TYPE_VENDOR,
 			0x00, 0xffff, 0x00, NULL, 0, 50);
 	libusb_control_transfer(dev_hdl, LIBUSB_REQUEST_TYPE_VENDOR,
@@ -78,7 +78,7 @@ static libusb_device_handle *lascar_open(struct libusb_device *dev)
 	return dev_hdl;
 }
 
-static void mark_xfer(struct libusb_transfer *xfer)
+static void LIBUSB_CALL mark_xfer(struct libusb_transfer *xfer)
 {
 
 	xfer->user_data = GINT_TO_POINTER(1);
@@ -97,7 +97,7 @@ SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
 
 	sr_spew("Reading config block.");
 
-	drvc = di->priv;
+	drvc = di->context;
 	*configlen = 0;
 
 	if (!(xfer_in = libusb_alloc_transfer(0)) ||
@@ -112,7 +112,7 @@ SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
 	/* Keep a read request waiting in the wings, ready to pounce
 	 * the moment the device sends something. */
 	libusb_fill_bulk_transfer(xfer_in, dev_hdl, LASCAR_EP_IN,
-			buf, 256, mark_xfer, 0, 10000);
+			buf, 256, mark_xfer, 0, BULK_XFER_TIMEOUT);
 	if (libusb_submit_transfer(xfer_in) != 0)
 		goto cleanup;
 
@@ -133,7 +133,7 @@ SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
 			start = 0;
 			break;
 		}
-		g_usleep(5000);
+		g_usleep(SLEEP_US_LONG);
 		libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 	}
 	if (!start) {
@@ -166,7 +166,7 @@ SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
 			start = 0;
 			break;
 		}
-		g_usleep(5000);
+		g_usleep(SLEEP_US_LONG);
 		libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 	}
 	if (!start) {
@@ -183,16 +183,16 @@ SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
 	*configlen = buflen;
 
 cleanup:
-	if (!xfer_in->user_data || !xfer_in->user_data) {
+	if (!xfer_in->user_data || !xfer_out->user_data) {
 		if (!xfer_in->user_data)
 			libusb_cancel_transfer(xfer_in);
 		if (!xfer_out->user_data)
 			libusb_cancel_transfer(xfer_out);
 		start = g_get_monotonic_time();
 		while (!xfer_in->user_data || !xfer_out->user_data) {
-			if (g_get_monotonic_time() - start > 10000)
+			if (g_get_monotonic_time() - start > EVENTS_TIMEOUT)
 				break;
-			g_usleep(1000);
+			g_usleep(SLEEP_US_SHORT);
 			libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 		}
 	}
@@ -214,7 +214,7 @@ static int lascar_save_config(libusb_device_handle *dev_hdl,
 
 	sr_spew("Writing config block.");
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	if (!(xfer_in = libusb_alloc_transfer(0)) ||
 			!(xfer_out = libusb_alloc_transfer(0)))
@@ -229,7 +229,7 @@ static int lascar_save_config(libusb_device_handle *dev_hdl,
 	/* Keep a read request waiting in the wings, ready to pounce
 	 * the moment the device sends something. */
 	libusb_fill_bulk_transfer(xfer_in, dev_hdl, LASCAR_EP_IN,
-			buf, 256, mark_xfer, 0, 10000);
+			buf, 256, mark_xfer, 0, BULK_XFER_TIMEOUT);
 	if (libusb_submit_transfer(xfer_in) != 0) {
 		ret = SR_ERR;
 		goto cleanup;
@@ -248,7 +248,7 @@ static int lascar_save_config(libusb_device_handle *dev_hdl,
 	tv.tv_sec = 0;
 	tv.tv_usec = 0;
 	while (!xfer_out->user_data) {
-		g_usleep(5000);
+		g_usleep(SLEEP_US_LONG);
 		libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 	}
 
@@ -259,7 +259,7 @@ static int lascar_save_config(libusb_device_handle *dev_hdl,
 		goto cleanup;
 	}
 	while (!xfer_in->user_data || !xfer_out->user_data) {
-		g_usleep(5000);
+		g_usleep(SLEEP_US_LONG);
 		libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 	}
 
@@ -269,16 +269,16 @@ static int lascar_save_config(libusb_device_handle *dev_hdl,
 	}
 
 cleanup:
-	if (!xfer_in->user_data || !xfer_in->user_data) {
+	if (!xfer_in->user_data || !xfer_out->user_data) {
 		if (!xfer_in->user_data)
 			libusb_cancel_transfer(xfer_in);
 		if (!xfer_out->user_data)
 			libusb_cancel_transfer(xfer_out);
 		start = g_get_monotonic_time();
 		while (!xfer_in->user_data || !xfer_out->user_data) {
-			if (g_get_monotonic_time() - start > 10000)
+			if (g_get_monotonic_time() - start > EVENTS_TIMEOUT)
 				break;
-			g_usleep(1000);
+			g_usleep(SLEEP_US_SHORT);
 			libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 		}
 	}
@@ -293,7 +293,6 @@ static struct sr_dev_inst *lascar_identify(unsigned char *config)
 	struct dev_context *devc;
 	const struct elusb_profile *profile;
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	int modelid, i;
 	char firmware[5];
 
@@ -323,31 +322,24 @@ static struct sr_dev_inst *lascar_identify(unsigned char *config)
 			return NULL;
 		}
 
-		if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, LASCAR_VENDOR,
-				profile->modelname, firmware)))
-			return NULL;
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup(LASCAR_VENDOR);
+		sdi->model = g_strdup(profile->modelname);
+		sdi->version = g_strdup(firmware);
 		sdi->driver = di;
 
 		if (profile->logformat == LOG_TEMP_RH) {
 			/* Model this as two channels: temperature and humidity. */
-			if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Temp")))
-				return NULL;
-			sdi->channels = g_slist_append(NULL, ch);
-			if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Hum")))
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "Temp");
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "Hum");
 		} else if (profile->logformat == LOG_CO) {
-			if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CO")))
-				return NULL;
-			sdi->channels = g_slist_append(NULL, ch);
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CO");
 		} else {
-			if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-				return NULL;
-			sdi->channels = g_slist_append(NULL, ch);
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 		}
 
-		if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
-			return NULL;
+		devc = g_malloc0(sizeof(struct dev_context));
 		sdi->priv = devc;
 		devc->profile = profile;
 	}
@@ -360,21 +352,15 @@ SR_PRIV struct sr_dev_inst *lascar_scan(int bus, int address)
 	struct drv_context *drvc;
 	struct sr_dev_inst *sdi;
 	struct libusb_device **devlist;
-	struct libusb_device_descriptor des;
 	libusb_device_handle *dev_hdl;
-	int dummy, ret, i;
+	int dummy, i;
 	unsigned char config[MAX_CONFIGBLOCK_SIZE];
 
-	drvc = di->priv;
+	drvc = di->context;
 	sdi = NULL;
 
 	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
 	for (i = 0; devlist[i]; i++) {
-		if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
-			sr_err("Failed to get device descriptor: %d.", ret);
-			continue;
-		}
-
 		if (libusb_get_bus_number(devlist[i]) != bus ||
 				libusb_get_device_address(devlist[i]) != address)
 			continue;
@@ -397,7 +383,7 @@ static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct sr_channel *ch;
 	float *temp, *rh;
 	uint16_t s;
@@ -411,7 +397,7 @@ static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
 		samples = samples_left;
 	switch (devc->profile->logformat) {
 	case LOG_TEMP_RH:
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = &analog;
 		analog.mqflags = 0;
 		if (!(temp = g_try_malloc(sizeof(float) * samples)))
@@ -419,9 +405,9 @@ static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
 		if (!(rh = g_try_malloc(sizeof(float) * samples)))
 			break;
 		for (i = 0, j = 0; i < samples; i++) {
-			/* Both Celcius and Fahrenheit stored at base -40. */
+			/* Both Celsius and Fahrenheit stored at base -40. */
 			if (devc->temp_unit == 0)
-				/* Celcius is stored in half-degree increments. */
+				/* Celsius is stored in half-degree increments. */
 				temp[j] = buf[i * 2] / 2 - 40;
 			else
 				temp[j] = buf[i * 2] - 40;
@@ -445,6 +431,7 @@ static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
 				analog.unit = SR_UNIT_CELSIUS;
 			analog.data = temp;
 			sr_session_send(devc->cb_data, &packet);
+			g_slist_free(analog.channels);
 		}
 
 		ch = sdi->channels->next->data;
@@ -454,13 +441,14 @@ static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
 			analog.unit = SR_UNIT_PERCENTAGE;
 			analog.data = rh;
 			sr_session_send(devc->cb_data, &packet);
+			g_slist_free(analog.channels);
 		}
 
 		g_free(temp);
 		g_free(rh);
 		break;
 	case LOG_CO:
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = &analog;
 		analog.channels = sdi->channels;
 		analog.num_samples = samples;
@@ -471,7 +459,7 @@ static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
 			break;
 		for (i = 0; i < samples; i++) {
 			s = (buf[i * 2] << 8) | buf[i * 2 + 1];
-			analog.data[i] = (s * devc->co_high + devc->co_low) / 1000000;
+			analog.data[i] = (s * devc->co_high + devc->co_low) / (1000 * 1000);
 			if (analog.data[i] < 0.0)
 				analog.data[i] = 0.0;
 		}
@@ -488,7 +476,7 @@ static void lascar_el_usb_dispatch(struct sr_dev_inst *sdi, unsigned char *buf,
 
 SR_PRIV int lascar_el_usb_handle_events(int fd, int revents, void *cb_data)
 {
-	struct drv_context *drvc = di->priv;
+	struct drv_context *drvc = di->context;
 	struct sr_datafeed_packet packet;
 	struct sr_dev_inst *sdi;
 	struct timeval tv;
@@ -499,7 +487,7 @@ SR_PRIV int lascar_el_usb_handle_events(int fd, int revents, void *cb_data)
 	sdi = cb_data;
 
 	if (sdi->status == SR_ST_STOPPING) {
-		usb_source_remove(drvc->sr_ctx);
+		usb_source_remove(sdi->session, drvc->sr_ctx);
 
 		packet.type = SR_DF_END;
 		sr_session_send(cb_data, &packet);
@@ -512,7 +500,7 @@ SR_PRIV int lascar_el_usb_handle_events(int fd, int revents, void *cb_data)
 	return TRUE;
 }
 
-SR_PRIV void lascar_el_usb_receive_transfer(struct libusb_transfer *transfer)
+SR_PRIV void LIBUSB_CALL lascar_el_usb_receive_transfer(struct libusb_transfer *transfer)
 {
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
diff --git a/hardware/lascar-el-usb/protocol.h b/src/hardware/lascar-el-usb/protocol.h
similarity index 88%
rename from hardware/lascar-el-usb/protocol.h
rename to src/hardware/lascar-el-usb/protocol.h
index a94bd8a..482c4a9 100644
--- a/hardware/lascar-el-usb/protocol.h
+++ b/src/hardware/lascar-el-usb/protocol.h
@@ -21,7 +21,7 @@
 #define LIBSIGROK_HARDWARE_LASCAR_EL_USB_PROTOCOL_H
 
 #include <stdint.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "lascar-el-usb"
@@ -30,10 +30,15 @@
 #define LASCAR_INTERFACE 0
 #define LASCAR_EP_IN 0x82
 #define LASCAR_EP_OUT 2
-/* Max 100ms for a device to positively identify. */
-#define SCAN_TIMEOUT 100000
 #define MAX_CONFIGBLOCK_SIZE 256
 
+/* Max 100ms for a device to positively identify. */
+#define SCAN_TIMEOUT (100 * 1000)
+#define BULK_XFER_TIMEOUT (10 * 1000)
+#define EVENTS_TIMEOUT (10 * 1000)
+#define SLEEP_US_LONG (5 * 1000)
+#define SLEEP_US_SHORT (1 * 1000)
+
 /** Private, per-device-instance driver context. */
 struct dev_context {
 	void *cb_data;
@@ -63,7 +68,7 @@ enum {
 
 struct elusb_profile {
 	int modelid;
-	char *modelname;
+	const char *modelname;
 	int logformat;
 };
 
@@ -71,7 +76,7 @@ SR_PRIV int lascar_get_config(libusb_device_handle *dev_hdl,
 		unsigned char *configblock, int *configlen);
 SR_PRIV struct sr_dev_inst *lascar_scan(int bus, int address);
 SR_PRIV int lascar_el_usb_handle_events(int fd, int revents, void *cb_data);
-SR_PRIV void lascar_el_usb_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL lascar_el_usb_receive_transfer(struct libusb_transfer *transfer);
 SR_PRIV int lascar_start_logging(const struct sr_dev_inst *sdi);
 SR_PRIV int lascar_stop_logging(const struct sr_dev_inst *sdi);
 SR_PRIV int lascar_is_logging(const struct sr_dev_inst *sdi);
diff --git a/src/hardware/lecroy-logicstudio/api.c b/src/hardware/lecroy-logicstudio/api.c
new file mode 100644
index 0000000..53e7b01
--- /dev/null
+++ b/src/hardware/lecroy-logicstudio/api.c
@@ -0,0 +1,559 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Tilman Sauerbeck <tilman at code-monkey.de>
+ * Copyright (C) 2012 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "protocol.h"
+
+#define LOGICSTUDIO16_VID 0x05ff
+#define LOGICSTUDIO16_PID_LACK_FIRMWARE 0xa001
+#define LOGICSTUDIO16_PID_HAVE_FIRMWARE 0xa002
+
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 0
+#define FX2_FIRMWARE "lecroy-logicstudio16-fx2lp.fw"
+
+#define UNKNOWN_ADDRESS 0xff
+#define MAX_RENUM_DELAY_MS 3000
+
+static const uint32_t devopts[] = {
+	SR_CONF_LOGIC_ANALYZER,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+};
+
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
+	SR_TRIGGER_EDGE,
+};
+
+static const uint64_t samplerates[] = {
+	SR_HZ(1000),
+	SR_HZ(2500),
+	SR_KHZ(5),
+	SR_KHZ(10),
+	SR_KHZ(25),
+	SR_KHZ(50),
+	SR_KHZ(100),
+	SR_KHZ(250),
+	SR_KHZ(500),
+	SR_KHZ(1000),
+	SR_KHZ(2500),
+	SR_MHZ(5),
+	SR_MHZ(10),
+	SR_MHZ(25),
+	SR_MHZ(50),
+	SR_MHZ(100),
+	SR_MHZ(250),
+	SR_MHZ(500),
+};
+
+SR_PRIV struct sr_dev_driver lecroy_logicstudio_driver_info;
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct sr_dev_inst *create_device(struct sr_dev_driver *di,
+		struct sr_usb_dev_inst *usb, enum sr_dev_inst_status status,
+		int64_t fw_updated)
+{
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	char channel_name[8];
+	int i;
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = status;
+	sdi->vendor = g_strdup("LeCroy");
+	sdi->model = g_strdup("LogicStudio16");
+	sdi->driver = di;
+	sdi->inst_type = SR_INST_USB;
+	sdi->conn = usb;
+
+	for (i = 0; i < 16; i++) {
+		snprintf(channel_name, sizeof(channel_name), "D%i", i);
+		sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
+	}
+
+	devc = g_malloc0(sizeof(struct dev_context));
+
+	sdi->priv = devc;
+
+	devc->fw_updated = fw_updated;
+	devc->capture_ratio = 50;
+
+	lls_set_samplerate(sdi, SR_MHZ(500));
+
+	return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct sr_dev_inst *sdi;
+	struct drv_context *drvc;
+	struct sr_usb_dev_inst *usb;
+	struct libusb_device_descriptor des;
+	libusb_device **devlist;
+	GSList *devices;
+	char connection_id[64];
+	size_t i;
+	int r;
+
+	(void)options;
+
+	drvc = di->context;
+	drvc->instances = NULL;
+
+	devices = NULL;
+
+	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+
+	for (i = 0; devlist[i]; i++) {
+		libusb_get_device_descriptor(devlist[i], &des);
+
+		if (des.idVendor != LOGICSTUDIO16_VID)
+			continue;
+
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+
+		usb = NULL;
+
+		switch (des.idProduct) {
+		case LOGICSTUDIO16_PID_HAVE_FIRMWARE:
+			usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+				libusb_get_device_address(devlist[i]), NULL);
+
+			sdi = create_device(di, usb, SR_ST_INACTIVE, 0);
+			break;
+		case LOGICSTUDIO16_PID_LACK_FIRMWARE:
+			r = ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
+				USB_CONFIGURATION, FX2_FIRMWARE);
+			if (r != SR_OK) {
+				/*
+				 * An error message has already been logged by
+				 * ezusb_upload_firmware().
+				 */
+				continue;
+			}
+
+			/*
+			 * Put unknown as the address so that we know we still
+			 * need to get the proper address after the device
+			 * renumerates.
+			 */
+			usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+				UNKNOWN_ADDRESS, NULL);
+
+			sdi = create_device(di, usb, SR_ST_INITIALIZING,
+				g_get_monotonic_time());
+			break;
+		default:
+			break;
+		}
+
+		/* Cannot handle this device? */
+		if (!usb)
+			continue;
+
+		sdi->connection_id = g_strdup(connection_id);
+
+		drvc->instances = g_slist_append(drvc->instances, sdi);
+		devices = g_slist_append(devices, sdi);
+	}
+
+	libusb_free_device_list(devlist, 1);
+
+	return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int open_device(struct sr_dev_inst *sdi)
+{
+	struct drv_context *drvc;
+	struct sr_usb_dev_inst *usb;
+	struct libusb_device_descriptor des;
+	libusb_device **devlist;
+	char connection_id[64];
+	bool is_opened;
+	size_t i;
+	int r;
+
+	if (sdi->status == SR_ST_ACTIVE)
+		return SR_ERR;
+
+	drvc = sdi->driver->context;
+	usb = sdi->conn;
+
+	is_opened = FALSE;
+
+	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+
+	for (i = 0; devlist[i]; i++) {
+		libusb_get_device_descriptor(devlist[i], &des);
+
+		if (des.idVendor != LOGICSTUDIO16_VID ||
+			des.idProduct != LOGICSTUDIO16_PID_HAVE_FIRMWARE)
+			continue;
+
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+
+		/*
+		 * Check if this device is the same one that we associated
+		 * with this sdi in scan() and bail if it isn't.
+		 */
+		if (strcmp(sdi->connection_id, connection_id))
+			continue;
+
+		r = libusb_open(devlist[i], &usb->devhdl);
+
+		if (r) {
+			sr_err("Failed to open device: %s.",
+				libusb_error_name(r));
+			break;
+		}
+
+		/* Fix up address after firmware upload. */
+		if (usb->address == UNKNOWN_ADDRESS)
+			usb->address = libusb_get_device_address(devlist[i]);
+
+		is_opened = TRUE;
+
+		break;
+	}
+
+	libusb_free_device_list(devlist, 1);
+
+	if (!is_opened)
+		return SR_ERR;
+
+	if ((r = libusb_claim_interface(usb->devhdl, USB_INTERFACE))) {
+		sr_err("Failed to claim interface: %s.",
+			libusb_error_name(r));
+		return SR_ERR;
+	}
+
+	sdi->status = SR_ST_ACTIVE;
+
+	return SR_OK;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	int64_t timediff_us, timediff_ms;
+	int ret;
+
+	drvc = sdi->driver->context;
+	devc = sdi->priv;
+
+	if (!drvc) {
+		sr_err("Driver was not initialized.");
+		return SR_ERR;
+	}
+
+	/*
+	 * If we didn't need to upload FX2 firmware in scan(), open the device
+	 * right away. Otherwise, wait up to MAX_RENUM_DELAY_MS ms for the
+	 * FX2 to renumerate.
+	 */
+	if (!devc->fw_updated) {
+		ret = open_device(sdi);
+	} else {
+		sr_info("Waiting for device to reset.");
+
+		/* Takes >= 300ms for the FX2 to be gone from the USB bus. */
+		g_usleep(300 * 1000);
+		timediff_ms = 0;
+
+		while (timediff_ms < MAX_RENUM_DELAY_MS) {
+			ret = open_device(sdi);
+
+			if (ret == SR_OK)
+				break;
+
+			g_usleep(100 * 1000);
+
+			timediff_us = g_get_monotonic_time() - devc->fw_updated;
+			timediff_ms = timediff_us / 1000;
+
+			sr_spew("Waited %" PRIi64 "ms.", timediff_ms);
+		}
+
+		if (ret != SR_OK) {
+			sr_err("Device failed to renumerate.");
+			return SR_ERR;
+		}
+
+		sr_info("Device came back after %" PRIi64 "ms.", timediff_ms);
+	}
+
+	if (ret != SR_OK) {
+		sr_err("Unable to open device.");
+		return ret;
+	}
+
+	/*
+	 * Only allocate the sample buffer now since it's rather large.
+	 * Don't want to allocate it before we know we are going to use it.
+	 */
+	devc->fetched_samples = g_malloc(SAMPLE_BUF_SIZE);
+
+	devc->conv8to16 = g_malloc(CONV_8TO16_BUF_SIZE);
+
+	devc->intr_xfer = libusb_alloc_transfer(0);
+	devc->bulk_xfer = libusb_alloc_transfer(0);
+
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct sr_usb_dev_inst *usb;
+	struct dev_context *devc;
+
+	usb = sdi->conn;
+	devc = sdi->priv;
+
+	g_free(devc->fetched_samples);
+	devc->fetched_samples = NULL;
+
+	g_free(devc->conv8to16);
+	devc->conv8to16 = NULL;
+
+	if (devc->intr_xfer) {
+		devc->intr_xfer->buffer = NULL; /* Points into devc. */
+		libusb_free_transfer(devc->intr_xfer);
+		devc->intr_xfer = NULL;
+	}
+
+	if (devc->bulk_xfer) {
+		devc->bulk_xfer->buffer = NULL; /* Points into devc. */
+		libusb_free_transfer(devc->bulk_xfer);
+		devc->bulk_xfer = NULL;
+	}
+
+	if (!usb->devhdl)
+		return SR_ERR;
+
+	libusb_release_interface(usb->devhdl, 0);
+
+	libusb_close(usb->devhdl);
+	usb->devhdl = NULL;
+
+	sdi->status = SR_ST_INACTIVE;
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	struct drv_context *drvc;
+	int ret;
+
+	drvc = di->context;
+
+	ret = dev_clear(di);
+
+	g_free(drvc);
+
+	return ret;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+	const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		*data = g_variant_new_uint64(lls_get_samplerate(sdi));
+		break;
+	case SR_CONF_CAPTURE_RATIO:
+		*data = g_variant_new_uint64(devc->capture_ratio);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+	const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		return lls_set_samplerate(sdi, g_variant_get_uint64(data));
+	case SR_CONF_CAPTURE_RATIO:
+		devc->capture_ratio = g_variant_get_uint64(data);
+		if (devc->capture_ratio > 100)
+			return SR_ERR;
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+	const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+	GVariantBuilder vb;
+	GVariant *var;
+
+	(void)sdi;
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts),
+				sizeof(uint32_t));
+		break;
+	case SR_CONF_SAMPLERATE:
+		g_variant_builder_init(&vb, G_VARIANT_TYPE("a{sv}"));
+		var = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+				samplerates, ARRAY_SIZE(samplerates),
+				sizeof(uint64_t));
+		g_variant_builder_add(&vb, "{sv}", "samplerates", var);
+		*data = g_variant_builder_end(&vb);
+		break;
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				trigger_matches, ARRAY_SIZE(trigger_matches),
+				sizeof(int32_t));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_commit(const struct sr_dev_inst *sdi)
+{
+	return lls_setup_acquisition(sdi);
+}
+
+static int receive_usb_data(int fd, int revents, void *cb_data)
+{
+	struct drv_context *drvc;
+	struct timeval tv;
+
+	(void)fd;
+	(void)revents;
+
+	drvc = (struct drv_context *) cb_data;
+
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+
+	libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx,
+		&tv, NULL);
+
+	return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct drv_context *drvc;
+	int ret;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	drvc = sdi->driver->context;
+
+	if ((ret = lls_start_acquisition(sdi)) < 0)
+		return ret;
+
+	std_session_send_df_header(cb_data, LOG_PREFIX);
+
+	return usb_source_add(sdi->session, drvc->sr_ctx, 100,
+		receive_usb_data, drvc);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	(void)cb_data;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	return lls_stop_acquisition(sdi);
+}
+
+SR_PRIV struct sr_dev_driver lecroy_logicstudio_driver_info = {
+	.name = "lecroy-logicstudio",
+	.longname = "LeCroy LogicStudio",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.config_commit = config_commit,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/lecroy-logicstudio/protocol.c b/src/hardware/lecroy-logicstudio/protocol.c
new file mode 100644
index 0000000..9c82599
--- /dev/null
+++ b/src/hardware/lecroy-logicstudio/protocol.c
@@ -0,0 +1,1189 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Tilman Sauerbeck <tilman at code-monkey.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <assert.h>
+#include "protocol.h"
+
+#define EP_INTR (LIBUSB_ENDPOINT_IN | 1)
+#define EP_BULK (LIBUSB_ENDPOINT_IN | 2)
+#define EP_BITSTREAM (LIBUSB_ENDPOINT_OUT | 6)
+
+#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
+#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT)
+
+#define USB_COMMAND_READ_WRITE_REGS 0xb1
+#define USB_COMMAND_WRITE_STATUS_REG 0xb2
+#define USB_COMMAND_START_UPLOAD 0xb3
+#define USB_COMMAND_VERIFY_UPLOAD 0xb4
+
+#define USB_TIMEOUT_MS 100
+
+/* Firmware for acquisition on 8 channels. */
+#define FPGA_FIRMWARE_8 "lecroy-logicstudio16-8.bitstream"
+/* Firmware for acquisition on 16 channels. */
+#define FPGA_FIRMWARE_16 "lecroy-logicstudio16-16.bitstream"
+
+#define FPGA_FIRMWARE_SIZE 464196
+#define FPGA_FIRMWARE_CHUNK_SIZE 2048
+
+#define NUM_TRIGGER_STAGES 2
+#define TRIGGER_CFG_SIZE 45
+
+#define ALIGN2_DOWN(n, p) ((((n) > (p)) ? ((n) - (p)) : (n)) & ~((p) - 1))
+
+#define TRIGGER_OP_A 0x1000
+#define TRIGGER_OP_B 0x2000
+#define TRIGGER_OP_A_OR_B 0x3000
+#define TRIGGER_OP_A_AND_B 0x4000
+#define TRIGGER_OP_A_THEN_B 0x8000
+
+#define REG_ACQUISITION_ID 0x00
+#define REG_SAMPLERATE 0x02
+#define REG_PRETRIG_LO 0x03
+#define REG_PRETRIG_HI 0x04
+#define REG_POSTTRIG_LO 0x05
+#define REG_POSTTRIG_HI 0x06
+#define REG_ARM_TRIGGER 0x07
+#define REG_FETCH_SAMPLES 0x08
+#define REG_UNK1_LO 0x09
+#define REG_UNK1_HI 0x0a
+#define REG_UNK2_LO 0x0b
+#define REG_UNK2_HI 0x0c
+#define REG_UNK3_LO 0x0d
+#define REG_UNK3_HI 0x0e
+#define REG_UNK4_LO 0x0f
+#define REG_UNK4_HI 0x10
+#define REG_UNK5_LO 0x11
+#define REG_UNK5_HI 0x12
+#define REG_UNK6_LO 0x13
+#define REG_UNK6_HI 0x14
+#define REG_UNK0_LO 0x15
+#define REG_UNK0_HI 0x16
+#define REG_TRIGGER_CFG 0x18
+#define REG_TRIGGER_COMBINE_OP 0x1b
+#define REG_SELECT_CHANNELS 0x21
+#define REG_VOLTAGE_THRESH_EXTERNAL 0x22
+#define REG_VOLTAGE_THRESH_LOWER_CHANNELS 0x23
+#define REG_VOLTAGE_THRESH_UPPER_CHANNELS 0x24
+
+struct samplerate_info {
+	/** The samplerate in Hz. */
+	uint64_t samplerate;
+
+	/**
+	 * The offset to add to the sample offset for when the trigger fired.
+	 *
+	 * @note The value stored here only applies to 8 channel mode.
+	 *       When acquiring 16 channels, subtract another 8 samples.
+	 */
+	int8_t trigger_sample_offset;
+
+	uint8_t cfg;
+};
+
+struct trigger_config {
+	uint16_t rising_edges;
+	uint16_t falling_edges;
+	uint16_t any_edges;
+
+	uint16_t ones;
+	uint16_t zeroes;
+};
+
+/** A register and its value. */
+struct regval {
+	uint8_t reg;
+	uint16_t val;
+};
+
+static void LIBUSB_CALL handle_fetch_samples_done(struct libusb_transfer *xfer);
+static void LIBUSB_CALL recv_bulk_transfer(struct libusb_transfer *xfer);
+
+static const struct samplerate_info samplerates[] = {
+	{ SR_GHZ(1),  -24, 0x1f },
+	{ SR_MHZ(500), -6, 0x00 },
+	{ SR_MHZ(250), -4, 0x01 },
+	{ SR_MHZ(100),  2, 0x03 },
+	{ SR_MHZ(50),   4, 0x04 },
+	{ SR_MHZ(25),   8, 0x05 },
+	{ SR_MHZ(10),   4, 0x07 },
+	{ SR_MHZ(5),    8, 0x08 },
+	{ SR_KHZ(2500), 8, 0x09 },
+	{ SR_KHZ(1000), 8, 0x0b },
+	{ SR_KHZ(500),  8, 0x0c },
+	{ SR_KHZ(250),  8, 0x0d },
+	{ SR_KHZ(100),  8, 0x0f },
+	{ SR_KHZ(50),   8, 0x10 },
+	{ SR_KHZ(25),   8, 0x11 },
+	{ SR_KHZ(10),   8, 0x13 },
+	{ SR_KHZ(5),    8, 0x14 },
+	{ SR_HZ(2500),  8, 0x15 },
+	{ SR_HZ(1000),  8, 0x17 },
+};
+
+static int read_register(const struct sr_dev_inst *sdi,
+	uint8_t reg, uint16_t *value)
+{
+	struct sr_usb_dev_inst *usb;
+	uint8_t data[2];
+	int r;
+
+	usb = sdi->conn;
+
+	r = libusb_control_transfer(usb->devhdl, CTRL_IN,
+		USB_COMMAND_READ_WRITE_REGS, reg, 5444,
+		data, sizeof(data), USB_TIMEOUT_MS);
+
+	if (r != sizeof(data)) {
+		sr_err("CTRL_IN failed: %i.", r);
+		return SR_ERR;
+	}
+
+	*value = RB16(data);
+
+	return SR_OK;
+}
+
+static int write_registers_sync(const struct sr_dev_inst *sdi,
+	unsigned int wValue, unsigned int wIndex,
+	const struct regval *regs, size_t num_regs)
+{
+	struct sr_usb_dev_inst *usb;
+	uint8_t *buf;
+	size_t i, bufsiz;
+	int r;
+
+	usb = sdi->conn;
+
+	/* Try to avoid overflowing the stack. */
+	if (num_regs > 32)
+		return SR_ERR;
+
+	bufsiz = num_regs * 3;
+	buf = alloca(bufsiz);
+
+	for (i = 0; i < num_regs; i++) {
+		W8(&buf[i * 3 + 0], regs[i].reg);
+		WB16(&buf[i * 3 + 1], regs[i].val);
+	}
+
+	r = libusb_control_transfer(usb->devhdl, CTRL_OUT,
+			USB_COMMAND_READ_WRITE_REGS, wValue, wIndex,
+			buf, bufsiz, USB_TIMEOUT_MS);
+
+	if (r != (int) bufsiz) {
+		sr_err("write_registers_sync(%u/%u) failed.", wValue, wIndex);
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+static int write_registers_async(const struct sr_dev_inst *sdi,
+	unsigned int wValue, unsigned int wIndex,
+	const struct regval *regs, size_t num_regs,
+	libusb_transfer_cb_fn callback)
+{
+	struct sr_usb_dev_inst *usb;
+	struct libusb_transfer *xfer;
+	uint8_t *buf, *xfer_buf;
+	size_t i;
+
+	usb = sdi->conn;
+
+	xfer = libusb_alloc_transfer(0);
+	xfer_buf = g_malloc(LIBUSB_CONTROL_SETUP_SIZE + (num_regs * 3));
+
+	libusb_fill_control_setup(xfer_buf, CTRL_OUT,
+		USB_COMMAND_READ_WRITE_REGS, wValue, wIndex, num_regs * 3);
+
+	buf = xfer_buf + LIBUSB_CONTROL_SETUP_SIZE;
+
+	for (i = 0; i < num_regs; i++) {
+		W8(&buf[i * 3 + 0], regs[i].reg);
+		WB16(&buf[i * 3 + 1], regs[i].val);
+	}
+
+	libusb_fill_control_transfer(xfer, usb->devhdl,
+		xfer_buf, callback, (void *) sdi, USB_TIMEOUT_MS);
+
+	if (libusb_submit_transfer(xfer) < 0) {
+		g_free(xfer->buffer);
+		xfer->buffer = NULL;
+		libusb_free_transfer(xfer);
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+static void prep_regw(struct regval *regval, uint8_t reg, uint16_t val)
+{
+	regval->reg = reg;
+	regval->val = val;
+}
+
+static void LIBUSB_CALL handle_fetch_samples_done(struct libusb_transfer *xfer)
+{
+	const struct sr_dev_inst *sdi;
+	struct sr_usb_dev_inst *usb;
+	struct dev_context *devc;
+
+	sdi = xfer->user_data;
+	usb = sdi->conn;
+	devc = sdi->priv;
+
+	g_free(xfer->buffer);
+	xfer->buffer = NULL;
+
+	libusb_free_transfer(xfer);
+
+	libusb_fill_bulk_transfer(devc->bulk_xfer, usb->devhdl, EP_BULK,
+		devc->fetched_samples, 17 << 10,
+		recv_bulk_transfer, (void *)sdi, USB_TIMEOUT_MS);
+
+	libusb_submit_transfer(devc->bulk_xfer);
+}
+
+static void calc_unk0(uint32_t *a, uint32_t *b)
+{
+	uint32_t t;
+
+	t = 20000 / 4;
+
+	if (a)
+		*a = (t + 63) | 63;
+	if (b)
+		*b = (t + 63) & ~63;
+}
+
+static int fetch_samples_async(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct regval cmd[12];
+	uint32_t unk1, unk2, unk3;
+	int i;
+
+	devc = sdi->priv;
+
+	unk1 = devc->earliest_sample % (devc->num_thousand_samples << 10);
+	unk1 = (unk1 * devc->num_enabled_channel_groups) / 8;
+
+	calc_unk0(&unk2, &unk3);
+
+	i = 0;
+
+	prep_regw(&cmd[i++], REG_UNK1_LO, (unk1 >>  0) & 0xffff);
+	prep_regw(&cmd[i++], REG_UNK1_HI, (unk1 >> 16) & 0xffff);
+
+	prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples);
+	prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples | 0x02);
+
+	prep_regw(&cmd[i++], REG_UNK1_LO, 0x0000);
+	prep_regw(&cmd[i++], REG_UNK1_HI, 0x0000);
+
+	prep_regw(&cmd[i++], REG_UNK2_LO, (unk2 >>  0) & 0xffff);
+	prep_regw(&cmd[i++], REG_UNK2_HI, (unk2 >> 16) & 0xffff);
+
+	prep_regw(&cmd[i++], REG_UNK3_LO, (unk3 >>  0) & 0xffff);
+	prep_regw(&cmd[i++], REG_UNK3_HI, (unk3 >> 16) & 0xffff);
+
+	devc->magic_fetch_samples = 0x01;
+	prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples + 0x01);
+	prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples | 0x02);
+
+	return write_registers_async(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd),
+			handle_fetch_samples_done);
+}
+
+static int handle_intr_data(const struct sr_dev_inst *sdi, uint8_t *buffer)
+{
+	struct dev_context *devc;
+	gboolean resubmit_intr_xfer;
+	uint64_t time_latest, time_trigger, sample_latest, sample_trigger;
+	uint32_t samplerate_divider;
+
+	resubmit_intr_xfer = TRUE;
+
+	if (!sdi)
+		goto out;
+
+	devc = sdi->priv;
+
+	if (!devc->want_trigger)
+		goto out;
+
+	/* Does this packet refer to our newly programmed trigger yet? */
+	if (RB16(&buffer[0x02]) != devc->acquisition_id)
+		goto out;
+
+	switch (buffer[0x1f]) {
+	case 0x09:
+		/* Storing pre-trigger samples. */
+		break;
+	case 0x0a:
+		/* Trigger armed? */
+		break;
+	case 0x0b:
+		/* Storing post-trigger samples. */
+		break;
+	case 0x04:
+		/* Acquisition complete. */
+		devc->total_received_sample_bytes = 0;
+
+		samplerate_divider = SR_GHZ(1) / devc->samplerate_info->samplerate;
+
+		/*
+		 * These timestamps seem to be in units of eight nanoseconds.
+		 * The first one refers to the time when the latest sample
+		 * was written to the device's sample buffer, and the second
+		 * one refers to the time when the trigger fired.
+		 *
+		 * They are stored as 48 bit integers in the packet and we
+		 * shift it to the right by 16 to make up for that.
+		 */
+		time_latest = RB64(&buffer[0x6]) >> 16;
+		time_trigger = RB64(&buffer[0xc]) >> 16;
+
+		/* Convert timestamps to sample offsets. */
+		sample_latest = time_latest * 8;
+		sample_latest /= samplerate_divider;
+
+		sample_latest = ALIGN2_DOWN(sample_latest,
+			8 / devc->num_enabled_channel_groups);
+
+		devc->earliest_sample = sample_latest -
+			(devc->num_thousand_samples * 1000);
+
+		sample_trigger = time_trigger * 8;
+
+		/* Fill the zero bits on the right. */
+		sample_trigger |= RB16(&buffer[0x12]) & 7;
+
+		sample_trigger += devc->samplerate_info->trigger_sample_offset;
+
+		if (devc->num_enabled_channel_groups > 1)
+			sample_trigger -= 8;
+
+		sample_trigger -= 0x18;
+
+		if (samplerate_divider > 1) {
+			/* FIXME: Underflow. */
+			sample_trigger -= samplerate_divider;
+			sample_trigger /= samplerate_divider;
+		}
+
+		/*
+		 * Seems the hardware reports one sample too early,
+		 * so make up for that.
+		 */
+		sample_trigger++;
+
+		devc->trigger_sample = sample_trigger;
+
+		fetch_samples_async(sdi);
+
+		/*
+		 * Don't re-submit the interrupt transfer;
+		 * we need to get the samples instead.
+		 */
+		resubmit_intr_xfer = FALSE;
+
+		break;
+	default:
+		break;
+	}
+
+out:
+	return resubmit_intr_xfer;
+}
+
+static int upload_fpga_bitstream(const struct sr_dev_inst *sdi,
+	const char *firmware_name)
+{
+	struct drv_context *drvc;
+	struct sr_usb_dev_inst *usb;
+	struct sr_resource firmware;
+	uint8_t firmware_chunk[FPGA_FIRMWARE_CHUNK_SIZE];
+	uint8_t upload_succeeded;
+	ssize_t chunk_size;
+	int i, r, ret, actual_length;
+
+	drvc = sdi->driver->context;
+	usb = sdi->conn;
+
+	ret = sr_resource_open(drvc->sr_ctx, &firmware,
+			SR_RESOURCE_FIRMWARE, firmware_name);
+
+	if (ret != SR_OK)
+		return ret;
+
+	ret = SR_ERR;
+
+	if (firmware.size != FPGA_FIRMWARE_SIZE) {
+		sr_err("Invalid FPGA firmware file size: %" PRIu64 " bytes.",
+			firmware.size);
+		goto out;
+	}
+
+	/* Initiate upload. */
+	r = libusb_control_transfer(usb->devhdl, CTRL_OUT,
+		USB_COMMAND_START_UPLOAD, 0x07, 5444,
+		NULL, 0, USB_TIMEOUT_MS);
+
+	if (r != 0) {
+		sr_err("Failed to initiate firmware upload: %s.",
+				libusb_error_name(ret));
+		goto out;
+	}
+
+	for (;;) {
+		chunk_size = sr_resource_read(drvc->sr_ctx, &firmware,
+			firmware_chunk, sizeof(firmware_chunk));
+
+		if (chunk_size < 0)
+			goto out;
+
+		if (chunk_size == 0)
+			break;
+
+		actual_length = chunk_size;
+
+		r = libusb_bulk_transfer(usb->devhdl, EP_BITSTREAM,
+			firmware_chunk, chunk_size, &actual_length, USB_TIMEOUT_MS);
+
+		if (r != 0 || (ssize_t)actual_length != chunk_size) {
+			sr_err("FPGA firmware upload failed.");
+			goto out;
+		}
+	}
+
+	/* Verify upload. */
+	for (i = 0; i < 4; i++) {
+		g_usleep(250000);
+
+		upload_succeeded = 0x00;
+
+		r = libusb_control_transfer(usb->devhdl, CTRL_IN,
+			USB_COMMAND_VERIFY_UPLOAD, 0x07, 5444,
+			&upload_succeeded, sizeof(upload_succeeded),
+			USB_TIMEOUT_MS);
+
+		if (r != sizeof(upload_succeeded)) {
+			sr_err("CTRL_IN failed: %i.", r);
+			return SR_ERR;
+		}
+
+		if (upload_succeeded == 0x01) {
+			ret = SR_OK;
+			break;
+		}
+	}
+
+out:
+	sr_resource_close(drvc->sr_ctx, &firmware);
+
+	return ret;
+}
+
+static int upload_trigger(const struct sr_dev_inst *sdi,
+	uint8_t reg_values[TRIGGER_CFG_SIZE], uint8_t reg_offset)
+{
+	struct regval regs[3 * 5];
+	uint16_t value;
+	int i, j, k;
+
+	for (i = 0; i < TRIGGER_CFG_SIZE; i += 5) {
+		k = 0;
+
+		for (j = 0; j < 5; j++) {
+			value = ((reg_offset + i + j) << 8) | reg_values[i + j];
+
+			prep_regw(&regs[k++], REG_TRIGGER_CFG, value);
+			prep_regw(&regs[k++], REG_TRIGGER_CFG, value | 0x8000);
+			prep_regw(&regs[k++], REG_TRIGGER_CFG, value);
+		}
+
+		if (write_registers_sync(sdi, 0x12, 5444, regs, ARRAY_SIZE(regs))) {
+			sr_err("Failed to upload trigger config.");
+			return SR_ERR;
+		}
+	}
+
+	return SR_OK;
+}
+
+static int program_trigger(const struct sr_dev_inst *sdi,
+	struct trigger_config *stages, int num_filled_stages)
+{
+	struct trigger_config *block;
+	struct regval combine_op;
+	uint8_t buf[TRIGGER_CFG_SIZE];
+	const uint8_t reg_offsets[] = { 0x00, 0x40 };
+	int i;
+
+	for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
+		block = &stages[i];
+
+		memset(buf, 0, sizeof(buf));
+
+		WL16(&buf[0x00], ~(block->rising_edges | block->falling_edges));
+		WL16(&buf[0x05], block->rising_edges | block->falling_edges | block->any_edges);
+
+		if (block->ones | block->zeroes)
+			buf[0x09] = 0x10;
+
+		WL16(&buf[0x0a], block->rising_edges);
+		WL16(&buf[0x0f], block->ones | block->zeroes);
+		buf[0x13] = 0x10;
+		WL16(&buf[0x14], block->ones | 0x8000);
+
+		if (block->ones == 0x01)
+			WL16(&buf[0x19], block->ones << 1);
+		else
+			WL16(&buf[0x19], block->ones | 0x0001);
+
+		/*
+		 * The final trigger has some special stuff.
+		 * Not sure of the meaning yet.
+		 */
+		if (i == NUM_TRIGGER_STAGES - 1) {
+			buf[0x09] = 0x10; /* This is most likely wrong. */
+
+			buf[0x28] = 0xff;
+			buf[0x29] = 0xff;
+			buf[0x2a] = 0xff;
+			buf[0x2b] = 0xff;
+			buf[0x2c] = 0x80;
+		}
+
+		if (upload_trigger(sdi, buf, reg_offsets[i]) < 0)
+			return SR_ERR;
+	}
+
+	/*
+	 * If both available stages are used, AND them in the trigger
+	 * criteria.
+	 *
+	 * Once sigrok learns to teach devices about the combination
+	 * that the user wants, this seems to be the best default since
+	 * edge triggers cannot be AND'ed otherwise
+	 * (they are always OR'd within a single stage).
+	 */
+	prep_regw(&combine_op, REG_TRIGGER_COMBINE_OP,
+		num_filled_stages > 1 ? TRIGGER_OP_A_AND_B : TRIGGER_OP_A);
+
+	return write_registers_sync(sdi, 0x12, 5444, &combine_op, 1);
+}
+
+static gboolean transform_trigger(struct sr_trigger_stage *stage,
+	struct trigger_config *config)
+{
+	GSList *l;
+	struct sr_trigger_match *match;
+	uint32_t channel_mask;
+	gboolean ret;
+
+	ret = FALSE;
+
+	for (l = stage->matches; l; l = l->next) {
+		match = l->data;
+
+		if (!match)
+			continue;
+
+		/* Ignore disabled channels. */
+		if (!match->channel->enabled)
+			continue;
+
+		channel_mask = 1 << match->channel->index;
+
+		switch (match->match) {
+		case SR_TRIGGER_RISING:
+			config->rising_edges |= channel_mask;
+			break;
+		case SR_TRIGGER_FALLING:
+			config->falling_edges |= channel_mask;
+			break;
+		case SR_TRIGGER_EDGE:
+			config->any_edges |= channel_mask;
+			break;
+		case SR_TRIGGER_ONE:
+			config->ones |= channel_mask;
+			break;
+		case SR_TRIGGER_ZERO:
+			config->zeroes |= channel_mask;
+			break;
+		}
+
+		ret = TRUE;
+	}
+
+	return ret;
+}
+
+static int configure_trigger(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_trigger *trigger;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	struct trigger_config blocks[NUM_TRIGGER_STAGES];
+	gboolean stage_has_matches;
+	int num_filled_stages;
+	GSList *l, *ll;
+
+	devc = sdi->priv;
+
+	trigger = sr_session_trigger_get(sdi->session);
+
+	num_filled_stages = 0;
+
+	memset(blocks, 0, sizeof(blocks));
+
+	for (l = trigger ? trigger->stages : NULL; l; l = l->next) {
+		stage = l->data;
+		stage_has_matches = FALSE;
+
+		/* Check if this stage has any interesting matches. */
+		for (ll = stage->matches; ll; ll = ll->next) {
+			match = ll->data;
+
+			if (!match)
+				continue;
+
+			/* Ignore disabled channels. */
+			if (match->channel->enabled) {
+				stage_has_matches = TRUE;
+				break;
+			}
+		}
+
+		if (stage_has_matches == FALSE)
+			continue;
+
+		if (num_filled_stages == NUM_TRIGGER_STAGES)
+			return SR_ERR;
+
+		if (transform_trigger(stage, &blocks[num_filled_stages]))
+			num_filled_stages++;
+	}
+
+	devc->want_trigger = num_filled_stages > 0;
+
+	return program_trigger(sdi, blocks, num_filled_stages);
+}
+
+/** Update the bit mask of enabled channels. */
+SR_PRIV void lls_update_channel_mask(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_channel *channel;
+	GSList *l;
+
+	devc = sdi->priv;
+
+	devc->channel_mask = 0;
+
+	for (l = sdi->channels; l; l = l->next) {
+		channel = l->data;
+		if (channel->enabled)
+			devc->channel_mask |= 1 << channel->index;
+	}
+}
+
+SR_PRIV int lls_set_samplerate(const struct sr_dev_inst *sdi,
+	uint64_t samplerate)
+{
+	struct dev_context *devc;
+	size_t i;
+
+	devc = sdi->priv;
+
+	for (i = 0; i < ARRAY_SIZE(samplerates); i++) {
+		if (samplerates[i].samplerate == samplerate) {
+			devc->samplerate_info = &samplerates[i];
+			return SR_OK;
+		}
+	}
+
+	return SR_ERR;
+}
+
+SR_PRIV uint64_t lls_get_samplerate(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+
+	return devc->samplerate_info->samplerate;
+}
+
+static int read_0f12(const struct sr_dev_inst *sdi, uint64_t *value)
+{
+	uint64_t u64;
+	uint16_t u16;
+	int r, reg;
+
+	u64 = 0;
+
+	/*
+	 * Read the 64 bit register spread over 4 16 bit registers.
+	 *
+	 * Note that these don't seem to be the same registers we're writing
+	 * when arming the trigger (ie REG_UNK4 and REG_UNK5).
+	 * Seems there's multiple register spaces?
+	 */
+	for (reg = 0x0f; reg <= 0x12; reg++) {
+		r = read_register(sdi, reg, &u16);
+		if (r != SR_OK)
+			return r;
+		u64 <<= 16;
+		u64 |= u16;
+	}
+
+	*value = u64;
+
+	return SR_OK;
+}
+
+static int wait_for_dev_to_settle(const struct sr_dev_inst *sdi)
+{
+	uint64_t old_value, new_value;
+	int r, i;
+
+	/* Get the initial value. */
+	r = read_0f12(sdi, &old_value);
+
+	if (r != SR_OK)
+		return r;
+
+	/*
+	 * We are looking for two consecutive reads that yield the
+	 * same value. Try a couple of times.
+	 */
+	for (i = 0; i < 100; i++) {
+		r = read_0f12(sdi, &new_value);
+		if (r != SR_OK)
+			return r;
+
+		if (old_value == new_value)
+			return SR_OK;
+
+		old_value = new_value;
+	}
+
+	return SR_ERR;
+}
+
+SR_PRIV int lls_setup_acquisition(const struct sr_dev_inst *sdi)
+{
+	uint8_t status_reg_value[] = {
+		0x1, 0x0, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc,
+		0xd, 0xe, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6
+	};
+	struct regval threshold[3];
+	struct regval channels;
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	gboolean lower_enabled, upper_enabled, upload_bitstream;
+	uint32_t num_thousand_samples, num_enabled_channel_groups;
+	int i, r;
+
+	usb = sdi->conn;
+	devc = sdi->priv;
+
+	prep_regw(&threshold[0], REG_VOLTAGE_THRESH_LOWER_CHANNELS, 0x00c3);
+	prep_regw(&threshold[1], REG_VOLTAGE_THRESH_UPPER_CHANNELS, 0x00c2);
+	prep_regw(&threshold[2], REG_VOLTAGE_THRESH_EXTERNAL, 0x003e);
+
+	lls_update_channel_mask(sdi);
+
+	lower_enabled = (devc->channel_mask & 0x00ff) != 0x00;
+	upper_enabled = (devc->channel_mask & 0xff00) != 0x00;
+
+	num_thousand_samples = 20;
+	num_enabled_channel_groups = 2;
+
+	if (lower_enabled != upper_enabled) {
+		num_thousand_samples <<= 1;
+		num_enabled_channel_groups >>= 1;
+	}
+
+	if (upper_enabled && !lower_enabled)
+		prep_regw(&channels, REG_SELECT_CHANNELS, 0x01);
+	else
+		prep_regw(&channels, REG_SELECT_CHANNELS, 0x00);
+
+	/*
+	 * If the number of enabled channel groups changed since
+	 * the last acquisition, we need to switch FPGA bitstreams.
+	 * This works for the initial bitstream upload because
+	 * devc->num_enabled_channel_groups is initialized to zero.
+	 */
+	upload_bitstream =
+		devc->num_enabled_channel_groups != num_enabled_channel_groups;
+
+	if (upload_bitstream) {
+		if (lls_stop_acquisition(sdi)) {
+			sr_err("Cannot stop acquisition for FPGA bitstream upload.");
+			return SR_ERR;
+		}
+
+		for (i = 0; i < 3; i++)
+			if (write_registers_sync(sdi, 0x0, 0x0, &threshold[i], 1))
+				return SR_ERR;
+
+		if (num_enabled_channel_groups == 1)
+			r = upload_fpga_bitstream(sdi, FPGA_FIRMWARE_8);
+		else
+			r = upload_fpga_bitstream(sdi, FPGA_FIRMWARE_16);
+
+		if (r != SR_OK) {
+			sr_err("Firmware not accepted by device.");
+			return SR_ERR;
+		}
+
+		r = wait_for_dev_to_settle(sdi);
+
+		if (r != SR_OK) {
+			sr_err("Device did not settle in time.");
+			return SR_ERR;
+		}
+
+		for (i = 0; i < 3; i++)
+			if (write_registers_sync(sdi, 0x12, 5444, &threshold[i], 1))
+				return SR_ERR;
+
+		devc->magic_arm_trigger = 0x00;
+		devc->magic_fetch_samples = 0x00;
+	}
+
+	if (write_registers_sync(sdi, 0x12, 5444, &channels, 1))
+		return SR_ERR;
+
+	if (configure_trigger(sdi) < 0)
+		return SR_ERR;
+
+	if (write_registers_sync(sdi, 0x12, 5444, &threshold[0], 1))
+		return SR_ERR;
+
+	if (write_registers_sync(sdi, 0x12, 5444, &threshold[1], 1))
+		return SR_ERR;
+
+	if (upload_bitstream) {
+		r = libusb_control_transfer(usb->devhdl, CTRL_OUT,
+			USB_COMMAND_WRITE_STATUS_REG, 0x12, 5444,
+			status_reg_value, sizeof(status_reg_value), USB_TIMEOUT_MS);
+
+		if (r != sizeof(status_reg_value)) {
+			sr_err("Failed to write status register: %s.",
+				libusb_error_name(r));
+			return SR_ERR;
+		}
+	}
+
+	devc->num_thousand_samples = num_thousand_samples;
+	devc->num_enabled_channel_groups = num_enabled_channel_groups;
+
+	return SR_OK;
+}
+
+static void LIBUSB_CALL recv_intr_transfer(struct libusb_transfer *xfer)
+{
+	const struct sr_dev_inst *sdi;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+
+	sdi = xfer->user_data;
+	drvc = sdi->driver->context;
+	devc = sdi->priv;
+
+	if (devc->abort_acquisition) {
+		packet.type = SR_DF_END;
+		sr_session_send(sdi, &packet);
+		usb_source_remove(sdi->session, drvc->sr_ctx);
+		return;
+	}
+
+	if (xfer->status == LIBUSB_TRANSFER_COMPLETED) {
+		if (xfer->actual_length != INTR_BUF_SIZE)
+			sr_err("Invalid size of interrupt transfer: %u.",
+				xfer->actual_length);
+		else if (handle_intr_data(sdi, xfer->buffer)) {
+			if (libusb_submit_transfer(xfer) < 0)
+				sr_err("Failed to submit interrupt transfer.");
+		}
+	}
+}
+
+static void send_samples(const struct sr_dev_inst *sdi,
+	uint8_t *samples, uint32_t length)
+{
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_logic logic;
+	gboolean lower_enabled, upper_enabled;
+	uint32_t shift, i;
+
+	devc = sdi->priv;
+
+	lower_enabled = (devc->channel_mask & 0x00ff) != 0x00;
+	upper_enabled = (devc->channel_mask & 0xff00) != 0x00;
+
+	logic.unitsize = 2;
+
+	packet.type = SR_DF_LOGIC;
+	packet.payload = &logic;
+
+	if (lower_enabled && upper_enabled) {
+		logic.length = length;
+		logic.data = samples;
+
+		sr_session_send(sdi, &packet);
+	} else {
+		/* Which channel group is enabled? */
+		shift = (lower_enabled) ? 0 : 8;
+
+		while (length >= (CONV_8TO16_BUF_SIZE / 2)) {
+			for (i = 0; i < (CONV_8TO16_BUF_SIZE / 2); i++) {
+				devc->conv8to16[i] = samples[i];
+				devc->conv8to16[i] <<= shift;
+			}
+
+			logic.length = CONV_8TO16_BUF_SIZE;
+			logic.data = devc->conv8to16;
+
+			sr_session_send(sdi, &packet);
+
+			samples += CONV_8TO16_BUF_SIZE / 2;
+			length -= CONV_8TO16_BUF_SIZE / 2;
+		}
+
+		/* Handle the remaining samples. */
+		for (i = 0; i < length; i++) {
+			devc->conv8to16[i] = samples[i];
+			devc->conv8to16[i] <<= shift;
+		}
+
+		logic.length = length * 2;
+		logic.data = devc->conv8to16;
+
+		sr_session_send(sdi, &packet);
+	}
+}
+
+static uint16_t sample_to_byte_offset(struct dev_context *devc, uint64_t o)
+{
+	o %= devc->num_thousand_samples << 10;
+
+	/* We have 8 bit per channel group, so this gets us a byte offset. */
+	return o * devc->num_enabled_channel_groups;
+}
+
+static void LIBUSB_CALL recv_bulk_transfer(struct libusb_transfer *xfer)
+{
+	const struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct drv_context *drvc;
+	struct sr_datafeed_packet packet;
+	uint32_t bytes_left, length;
+	uint16_t read_offset, trigger_offset;
+
+	sdi = xfer->user_data;
+
+	if (!sdi)
+		return;
+
+	drvc = sdi->driver->context;
+	devc = sdi->priv;
+
+	devc->total_received_sample_bytes += xfer->actual_length;
+
+	if (devc->total_received_sample_bytes < SAMPLE_BUF_SIZE) {
+		xfer->buffer = devc->fetched_samples
+			+ devc->total_received_sample_bytes;
+
+		xfer->length = MIN(16 << 10,
+			SAMPLE_BUF_SIZE - devc->total_received_sample_bytes);
+
+		libusb_submit_transfer(xfer);
+		return;
+	}
+
+	usb_source_remove(sdi->session, drvc->sr_ctx);
+
+	read_offset = sample_to_byte_offset(devc, devc->earliest_sample);
+	trigger_offset = sample_to_byte_offset(devc, devc->trigger_sample);
+
+	/*
+	 * The last few bytes seem to contain garbage data,
+	 * so ignore them.
+	 */
+	bytes_left = (SAMPLE_BUF_SIZE >> 10) * 1000;
+
+	sr_spew("Start reading at offset 0x%04hx.", read_offset);
+	sr_spew("Trigger offset 0x%04hx.", trigger_offset);
+
+	if (trigger_offset < read_offset) {
+		length = MIN(bytes_left, SAMPLE_BUF_SIZE - read_offset);
+
+		sr_spew("Sending %u pre-trigger bytes starting at 0x%04hx.",
+			length, read_offset);
+
+		send_samples(sdi, &devc->fetched_samples[read_offset], length);
+
+		bytes_left -= length;
+		read_offset = 0;
+	}
+
+	{
+		length = MIN(bytes_left, (uint32_t)(trigger_offset - read_offset));
+
+		sr_spew("Sending %u pre-trigger bytes starting at 0x%04hx.",
+			length, read_offset);
+
+		send_samples(sdi, &devc->fetched_samples[read_offset], length);
+
+		bytes_left -= length;
+
+		read_offset += length;
+		read_offset %= SAMPLE_BUF_SIZE;
+	}
+
+	/* Here comes the trigger. */
+	packet.type = SR_DF_TRIGGER;
+	packet.payload = NULL;
+
+	sr_session_send(sdi, &packet);
+
+	/* Send post-trigger samples. */
+	while (bytes_left > 0) {
+		length = MIN(bytes_left, SAMPLE_BUF_SIZE - read_offset);
+
+		sr_spew("Sending %u post-trigger bytes starting at 0x%04hx.",
+			length, read_offset);
+
+		send_samples(sdi, &devc->fetched_samples[read_offset], length);
+
+		bytes_left -= length;
+
+		read_offset += length;
+		read_offset %= SAMPLE_BUF_SIZE;
+	}
+
+	packet.type = SR_DF_END;
+	sr_session_send(sdi, &packet);
+}
+
+static uint32_t transform_sample_count(struct dev_context *devc,
+	uint32_t samples)
+{
+	uint32_t d = 8 / devc->num_enabled_channel_groups;
+
+	return (samples + 0x1c + d + d - 1) / d;
+}
+
+SR_PRIV int lls_start_acquisition(const struct sr_dev_inst *sdi)
+{
+	struct sr_usb_dev_inst *usb;
+	struct dev_context *devc;
+	struct regval cmd[17];
+	uint32_t unk0, total_samples, pre_trigger_samples, post_trigger_samples;
+	uint32_t pre_trigger_tr, post_trigger_tr;
+	int i;
+
+	usb = sdi->conn;
+	devc = sdi->priv;
+
+	devc->abort_acquisition = FALSE;
+
+	libusb_fill_interrupt_transfer(devc->intr_xfer, usb->devhdl, EP_INTR,
+		devc->intr_buf, INTR_BUF_SIZE,
+		recv_intr_transfer, (void *) sdi, USB_TIMEOUT_MS);
+
+	libusb_submit_transfer(devc->intr_xfer);
+
+	if (devc->want_trigger == FALSE)
+		return SR_OK;
+
+	calc_unk0(&unk0, NULL);
+
+	total_samples = devc->num_thousand_samples * 1000;
+
+	pre_trigger_samples = total_samples * devc->capture_ratio / 100;
+	post_trigger_samples = total_samples - pre_trigger_samples;
+
+	pre_trigger_tr = transform_sample_count(devc, pre_trigger_samples);
+	post_trigger_tr = transform_sample_count(devc, post_trigger_samples);
+
+	i = 0;
+
+	prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger);
+	prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger | 0x02);
+
+	prep_regw(&cmd[i++], REG_UNK6_LO, 0x0000);
+	prep_regw(&cmd[i++], REG_UNK6_HI, 0x0000);
+
+	prep_regw(&cmd[i++], REG_UNK0_LO, (unk0 >>  0) & 0xffff);
+	prep_regw(&cmd[i++], REG_UNK0_HI, (unk0 >> 16) & 0xffff);
+
+	prep_regw(&cmd[i++], REG_UNK4_LO, 0x0000);
+	prep_regw(&cmd[i++], REG_UNK4_HI, 0x0000);
+
+	prep_regw(&cmd[i++], REG_UNK5_LO, 0x0000);
+	prep_regw(&cmd[i++], REG_UNK5_HI, 0x0000);
+
+	prep_regw(&cmd[i++], REG_ACQUISITION_ID, ++devc->acquisition_id);
+	prep_regw(&cmd[i++], REG_SAMPLERATE, devc->samplerate_info->cfg);
+
+	prep_regw(&cmd[i++], REG_PRETRIG_LO, (pre_trigger_tr >>  0) & 0xffff);
+	prep_regw(&cmd[i++], REG_PRETRIG_HI, (pre_trigger_tr >> 16) & 0xffff);
+
+	prep_regw(&cmd[i++], REG_POSTTRIG_LO, (post_trigger_tr >>  0) & 0xffff);
+	prep_regw(&cmd[i++], REG_POSTTRIG_HI, (post_trigger_tr >> 16) & 0xffff);
+
+	devc->magic_arm_trigger = 0x0c;
+	prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger | 0x01);
+
+	return write_registers_sync(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd));
+}
+
+SR_PRIV int lls_stop_acquisition(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct regval cmd[2];
+	int i;
+
+	devc = sdi->priv;
+
+	devc->abort_acquisition = TRUE;
+
+	i = 0;
+
+	prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger);
+	prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger | 0x02);
+
+	assert(i == ARRAY_SIZE(cmd));
+
+	return write_registers_sync(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd));
+}
diff --git a/src/hardware/lecroy-logicstudio/protocol.h b/src/hardware/lecroy-logicstudio/protocol.h
new file mode 100644
index 0000000..1ac3d0f
--- /dev/null
+++ b/src/hardware/lecroy-logicstudio/protocol.h
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Tilman Sauerbeck <tilman at code-monkey.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_LECROY_LOGICSTUDIO_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_LECROY_LOGICSTUDIO_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "lecroy-logicstudio"
+
+#define SAMPLE_BUF_SIZE 40960u
+#define CONV_8TO16_BUF_SIZE 8192
+#define INTR_BUF_SIZE 32
+
+struct samplerate_info;
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	struct libusb_transfer *intr_xfer;
+	struct libusb_transfer *bulk_xfer;
+
+	const struct samplerate_info *samplerate_info;
+
+	/**
+	 * When the device is opened, this will point at a buffer
+	 * of SAMPLE_BUF_SIZE bytes.
+	 */
+	uint8_t *fetched_samples;
+
+	/**
+	 * Used to convert 8 bit samples (8 channels) to 16 bit samples
+	 * (16 channels), thus only used in 8 channel mode.
+	 * Holds CONV_8TO16_BUF_SIZE bytes.
+	 */
+	uint16_t *conv8to16;
+
+	int64_t fw_updated; /* Time of last FX2 firmware upload. */
+
+	/** The pre-trigger capture ratio in percent. */
+	uint64_t capture_ratio;
+
+	uint64_t earliest_sample;
+	uint64_t trigger_sample;
+
+	/** The number of eight-channel groups enabled (either 1 or 2). */
+	uint32_t num_enabled_channel_groups;
+
+	/**
+	 * The number of samples to acquire (in thousands).
+	 * This is not customizable, but depending on the number
+	 * of enabled channel groups.
+	 */
+	uint32_t num_thousand_samples;
+
+	uint32_t total_received_sample_bytes;
+
+	/** Mask of enabled channels. */
+	uint16_t channel_mask;
+
+	uint16_t acquisition_id;
+
+	gboolean want_trigger;
+	gboolean abort_acquisition;
+
+	/**
+	 * These two magic values are required in order to fix a sample
+	 * buffer corruption. Before the first acquisition is run, they
+	 * need to be set to 0.
+	 */
+	uint8_t magic_arm_trigger;
+	uint8_t magic_fetch_samples;
+
+	/**
+	 * Buffer for interrupt transfers (acquisition state notifications).
+	 */
+	uint8_t intr_buf[INTR_BUF_SIZE];
+};
+
+SR_PRIV void lls_update_channel_mask(const struct sr_dev_inst *sdi);
+
+SR_PRIV int lls_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
+SR_PRIV uint64_t lls_get_samplerate(const struct sr_dev_inst *sdi);
+
+SR_PRIV int lls_setup_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int lls_start_acquisition(const struct sr_dev_inst *sdi);
+SR_PRIV int lls_stop_acquisition(const struct sr_dev_inst *sdi);
+
+#endif
diff --git a/src/hardware/manson-hcs-3xxx/api.c b/src/hardware/manson-hcs-3xxx/api.c
new file mode 100644
index 0000000..917a7c0
--- /dev/null
+++ b/src/hardware/manson-hcs-3xxx/api.c
@@ -0,0 +1,440 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok at heidbrink.biz>
+ *
+ * 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
+ */
+
+/** @file
+  *  <em>Manson HCS-3xxx series</em> power supply driver
+  *  @internal
+  */
+
+#include <config.h>
+#include "protocol.h"
+
+static const uint32_t drvopts[] = {
+	/* Device class */
+	SR_CONF_POWER_SUPPLY,
+};
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t devopts[] = {
+	/* Device class */
+	SR_CONF_POWER_SUPPLY,
+	/* Acquisition modes. */
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+	/* Device configuration */
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+/* Note: All models have one power supply output only. */
+static const struct hcs_model models[] = {
+	{ MANSON_HCS_3100, "HCS-3100",     "3100", { 1, 18, 0.1 }, { 0, 10,   0.10 } },
+	{ MANSON_HCS_3102, "HCS-3102",     "3102", { 1, 36, 0.1 }, { 0,  5,   0.01 } },
+	{ MANSON_HCS_3104, "HCS-3104",     "3104", { 1, 60, 0.1 }, { 0,  2.5, 0.01 } },
+	{ MANSON_HCS_3150, "HCS-3150",     "3150", { 1, 18, 0.1 }, { 0, 15,   0.10 } },
+	{ MANSON_HCS_3200, "HCS-3200",     "3200", { 1, 18, 0.1 }, { 0, 20,   0.10 } },
+	{ MANSON_HCS_3202, "HCS-3202",     "3202", { 1, 36, 0.1 }, { 0, 10,   0.10 } },
+	{ MANSON_HCS_3204, "HCS-3204",     "3204", { 1, 60, 0.1 }, { 0,  5,   0.01 } },
+	{ MANSON_HCS_3300, "HCS-3300-USB", "3300", { 1, 16, 0.1 }, { 0, 30,   0.10 } },
+	{ MANSON_HCS_3302, "HCS-3302-USB", "3302", { 1, 32, 0.1 }, { 0, 15,   0.10 } },
+	{ MANSON_HCS_3304, "HCS-3304-USB", "3304", { 1, 60, 0.1 }, { 0,  8,   0.10 } },
+	{ MANSON_HCS_3400, "HCS-3400-USB", "3400", { 1, 16, 0.1 }, { 0, 40,   0.10 } },
+	{ MANSON_HCS_3402, "HCS-3402-USB", "3402", { 1, 32, 0.1 }, { 0, 20,   0.10 } },
+	{ MANSON_HCS_3404, "HCS-3404-USB", "3404", { 1, 60, 0.1 }, { 0, 10,   0.10 } },
+	{ MANSON_HCS_3600, "HCS-3600-USB", "3600", { 1, 16, 0.1 }, { 0, 60,   0.10 } },
+	{ MANSON_HCS_3602, "HCS-3602-USB", "3602", { 1, 32, 0.1 }, { 0, 30,   0.10 } },
+	{ MANSON_HCS_3604, "HCS-3604-USB", "3604", { 1, 60, 0.1 }, { 0, 15,   0.10 } },
+	ALL_ZERO
+};
+
+SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info;
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	int i, model_id;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_dev_inst *sdi;
+	struct sr_config *src;
+	GSList *devices, *l;
+	const char *conn, *serialcomm;
+	struct sr_serial_dev_inst *serial;
+	char reply[50], **tokens, *dummy;
+
+	drvc = di->context;
+	drvc->instances = NULL;
+	devices = NULL;
+	conn = NULL;
+	serialcomm = NULL;
+	devc = 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;
+		default:
+			sr_err("Unknown option %d, skipping.", src->key);
+			break;
+		}
+	}
+
+	if (!conn)
+		return NULL;
+	if (!serialcomm)
+		serialcomm = "9600/8n1";
+
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
+
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+		return NULL;
+
+	serial_flush(serial);
+
+	sr_info("Probing serial port %s.", conn);
+
+	/* Get the device model. */
+	memset(&reply, 0, sizeof(reply));
+	if ((hcs_send_cmd(serial, "GMOD\r") < 0) ||
+	    (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
+		return NULL;
+	tokens = g_strsplit((const gchar *)&reply, "\r", 2);
+
+	model_id = -1;
+	for (i = 0; models[i].id != NULL; i++) {
+		if (!strcmp(models[i].id, tokens[0]))
+			model_id = i;
+	}
+	if (model_id < 0) {
+		sr_err("Unknown model ID '%s' detected, aborting.", tokens[0]);
+		g_strfreev(tokens);
+		return NULL;
+	}
+	g_strfreev(tokens);
+
+	/* Init device instance, etc. */
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("Manson");
+	sdi->model = g_strdup(models[model_id].name);
+	sdi->inst_type = SR_INST_SERIAL;
+	sdi->conn = serial;
+	sdi->driver = di;
+
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
+
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->model = &models[model_id];
+
+	sdi->priv = devc;
+
+	/* Get current voltage, current, status. */
+	if ((hcs_send_cmd(serial, "GETD\r") < 0) ||
+	    (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
+		goto exit_err;
+	tokens = g_strsplit((const gchar *)&reply, "\r", 2);
+	if (hcs_parse_volt_curr_mode(sdi, tokens) < 0) {
+		g_strfreev(tokens);
+		goto exit_err;
+	}
+	g_strfreev(tokens);
+
+	/* Get max. voltage and current. */
+	if ((hcs_send_cmd(serial, "GMAX\r") < 0) ||
+	    (hcs_read_reply(serial, 2, reply, sizeof(reply)) < 0))
+		goto exit_err;
+	tokens = g_strsplit((const gchar *)&reply, "\r", 2);
+	devc->current_max_device = g_strtod(&tokens[0][3], &dummy) * devc->model->current[2];
+	tokens[0][3] = '\0';
+	devc->voltage_max_device = g_strtod(tokens[0], &dummy) * devc->model->voltage[2];
+	g_strfreev(tokens);
+
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+	devices = g_slist_append(devices, sdi);
+
+	serial_close(serial);
+	if (!devices)
+		sr_serial_dev_inst_free(serial);
+
+	return devices;
+
+exit_err:
+	sr_dev_inst_free(sdi);
+	g_free(devc);
+
+	return NULL;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return dev_clear(di);
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	switch (key) {
+	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_VOLTAGE:
+		*data = g_variant_new_double(devc->voltage);
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		*data = g_variant_new_double(devc->voltage_max);
+		break;
+	case SR_CONF_CURRENT:
+		*data = g_variant_new_double(devc->current);
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		*data = g_variant_new_double(devc->current_max);
+		break;
+	case SR_CONF_ENABLED:
+		*data = g_variant_new_boolean(devc->output_enabled);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	gboolean bval;
+	gdouble dval;
+
+	(void)cg;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+
+	switch (key) {
+	case SR_CONF_LIMIT_MSEC:
+		if (g_variant_get_uint64(data) == 0)
+			return SR_ERR_ARG;
+		devc->limit_msec = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		if (g_variant_get_uint64(data) == 0)
+			return SR_ERR_ARG;
+		devc->limit_samples = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		dval = g_variant_get_double(data);
+		if (dval < devc->model->voltage[0] || dval > devc->voltage_max_device)
+			return SR_ERR_ARG;
+
+		if ((hcs_send_cmd(sdi->conn, "VOLT%03.0f\r",
+			(dval / devc->model->voltage[2])) < 0) ||
+		    (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
+			return SR_ERR;
+		devc->voltage_max = dval;
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		dval = g_variant_get_double(data);
+		if (dval < devc->model->current[0] || dval > devc->current_max_device)
+			return SR_ERR_ARG;
+
+		if ((hcs_send_cmd(sdi->conn, "CURR%03.0f\r",
+			(dval / devc->model->current[2])) < 0) ||
+		    (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
+			return SR_ERR;
+		devc->current_max = dval;
+		break;
+	case SR_CONF_ENABLED:
+		bval = g_variant_get_boolean(data);
+		if (bval == devc->output_enabled) /* Nothing to do. */
+			break;
+		if ((hcs_send_cmd(sdi->conn, "SOUT%1d\r", !bval) < 0) ||
+		    (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0))
+			return SR_ERR;
+		devc->output_enabled = bval;
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	GVariant *gvar;
+	GVariantBuilder gvb;
+	double dval;
+	int idx;
+
+	(void)cg;
+
+	/* Always available (with or without sdi). */
+	if (key == SR_CONF_SCAN_OPTIONS) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	/* Return drvopts without sdi (and devopts with sdi, see below). */
+	if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	/* Every other key needs an sdi. */
+	if (!sdi)
+		return SR_ERR_ARG;
+	devc = sdi->priv;
+
+	switch (key) {
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+		/* Min, max, step. */
+		for (idx = 0; idx < 3; idx++) {
+			if (idx == 1)
+				dval = devc->voltage_max_device;
+			else
+				dval = devc->model->voltage[idx];
+			gvar = g_variant_new_double(dval);
+			g_variant_builder_add_value(&gvb, gvar);
+		}
+		*data = g_variant_builder_end(&gvb);
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+		/* Min, max, step. */
+		for (idx = 0; idx < 3; idx++) {
+			if (idx == 1)
+				dval = devc->current_max_device;
+			else
+				dval = devc->model->current[idx];
+			gvar = g_variant_new_double(dval);
+			g_variant_builder_add_value(&gvb, gvar);
+		}
+		*data = g_variant_builder_end(&gvb);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int 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;
+
+	devc = sdi->priv;
+	devc->cb_data = cb_data;
+
+	/* Send header packet to the session bus. */
+	std_session_send_df_header(cb_data, LOG_PREFIX);
+
+	devc->starttime = g_get_monotonic_time();
+	devc->num_samples = 0;
+	devc->reply_pending = FALSE;
+	devc->req_sent_at = 0;
+
+	/* Poll every 10ms, or whenever some data comes in. */
+	serial = sdi->conn;
+	serial_source_add(sdi->session, serial, G_IO_IN, 10,
+			hcs_receive_data, (void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	return std_serial_dev_acquisition_stop(sdi, cb_data,
+			std_serial_dev_close, sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info = {
+	.name = "manson-hcs-3xxx",
+	.longname = "Manson HCS-3xxx",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = std_serial_dev_open,
+	.dev_close = std_serial_dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/manson-hcs-3xxx/protocol.c b/src/hardware/manson-hcs-3xxx/protocol.c
new file mode 100644
index 0000000..657ae31
--- /dev/null
+++ b/src/hardware/manson-hcs-3xxx/protocol.c
@@ -0,0 +1,261 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok at heidbrink.biz>
+ *
+ * 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
+ */
+
+/** @file
+  *  <em>Manson HCS-3xxx Series</em> power supply driver
+  *  @internal
+  */
+
+#include <config.h>
+#include "protocol.h"
+
+#define REQ_TIMEOUT_MS 500
+
+SR_PRIV int hcs_send_cmd(struct sr_serial_dev_inst *serial, const char *cmd, ...)
+{
+	int ret;
+	char cmdbuf[50];
+	char *cmd_esc;
+	va_list args;
+
+	va_start(args, cmd);
+	vsnprintf(cmdbuf, sizeof(cmdbuf), cmd, args);
+	va_end(args);
+
+	cmd_esc = g_strescape(cmdbuf, NULL);
+	sr_dbg("Sending '%s'.", cmd_esc);
+	g_free(cmd_esc);
+
+	if ((ret = serial_write_blocking(serial, cmdbuf, strlen(cmdbuf),
+			serial_timeout(serial, strlen(cmdbuf)))) < 0) {
+		sr_err("Error sending command: %d.", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * Read data from interface into buffer blocking until @a lines number of \\r chars
+ * received.
+ * @param serial Previously initialized serial port structure.
+ * @param[in] lines Number of \\r-terminated lines to read (1-n).
+ * @param     buf Buffer for result. Contents is NUL-terminated on success.
+ * @param[in] buflen Buffer length (>0).
+ * @retval SR_OK Lines received and ending with "OK\r" (success).
+ * @retval SR_ERR Error.
+ * @retval SR_ERR_ARG Invalid argument.
+ */
+SR_PRIV int hcs_read_reply(struct sr_serial_dev_inst *serial, int lines, char *buf, int buflen)
+{
+	int l_recv = 0;
+	int bufpos = 0;
+	int retc;
+
+	if (!serial || (lines <= 0) || !buf || (buflen <= 0))
+		return SR_ERR_ARG;
+
+	while ((l_recv < lines) && (bufpos < (buflen + 1))) {
+		retc = serial_read_blocking(serial, &buf[bufpos], 1, 0);
+		if (retc != 1)
+			return SR_ERR;
+		if (buf[bufpos] == '\r')
+			l_recv++;
+		bufpos++;
+	}
+	buf[bufpos] = '\0';
+
+	if ((l_recv == lines) && (g_str_has_suffix(buf, "OK\r")))
+		return SR_OK;
+	else
+		return SR_ERR;
+}
+
+/** Interpret result of GETD command. */
+SR_PRIV int hcs_parse_volt_curr_mode(struct sr_dev_inst *sdi, char **tokens)
+{
+	char *str;
+	double val;
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+
+	/* Bytes 0-3: Voltage. */
+	str = g_strndup(tokens[0], 4);
+	val = g_ascii_strtod(str, NULL) / 100;
+	devc->voltage = val;
+	g_free(str);
+
+	/* Bytes 4-7: Current. */
+	str = g_strndup((tokens[0] + 4), 4);
+	val = g_ascii_strtod(str, NULL) / 100;
+	devc->current = val;
+	g_free(str);
+
+	/* Byte 8: Mode ('0' means CV, '1' means CC). */
+	devc->cc_mode = (tokens[0][8] == '1');
+
+	/* Output enabled? Works because voltage cannot be set to 0.0 directly. */
+	devc->output_enabled = devc->voltage != 0.0;
+
+	return SR_OK;
+}
+
+static void send_sample(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+
+	devc = sdi->priv;
+
+	packet.type = SR_DF_ANALOG_OLD;
+	packet.payload = &analog;
+	analog.channels = sdi->channels;
+	analog.num_samples = 1;
+
+	analog.mq = SR_MQ_VOLTAGE;
+	analog.unit = SR_UNIT_VOLT;
+	analog.mqflags = SR_MQFLAG_DC;
+	analog.data = &devc->voltage;
+	sr_session_send(sdi, &packet);
+
+	analog.mq = SR_MQ_CURRENT;
+	analog.unit = SR_UNIT_AMPERE;
+	analog.mqflags = 0;
+	analog.data = &devc->current;
+	sr_session_send(sdi, &packet);
+
+	devc->num_samples++;
+}
+
+static int parse_reply(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	char *reply_esc, **tokens;
+	int retc;
+
+	devc = sdi->priv;
+
+	reply_esc = g_strescape(devc->buf, NULL);
+	sr_dbg("Received '%s'.", reply_esc);
+	g_free(reply_esc);
+
+	tokens = g_strsplit(devc->buf, "\r", 0);
+	retc = hcs_parse_volt_curr_mode(sdi, tokens);
+	g_strfreev(tokens);
+	if (retc < 0)
+		return SR_ERR;
+
+	send_sample(sdi);
+
+	return SR_OK;
+}
+
+static int handle_new_data(struct sr_dev_inst *sdi)
+{
+	int len;
+	struct dev_context *devc;
+	struct sr_serial_dev_inst *serial;
+
+	devc = sdi->priv;
+	serial = sdi->conn;
+
+	len = serial_read_blocking(serial, devc->buf + devc->buflen, 1, 0);
+	if (len < 1)
+		return SR_ERR;
+
+	devc->buflen += len;
+	devc->buf[devc->buflen] = '\0';
+
+	/* Wait until we received an "OK\r" (among other bytes). */
+	if (!g_str_has_suffix(devc->buf, "OK\r"))
+		return SR_OK;
+
+	parse_reply(sdi);
+
+	devc->buf[0] = '\0';
+	devc->buflen = 0;
+
+	devc->reply_pending = FALSE;
+
+	return SR_OK;
+}
+
+/** Driver/serial data reception function. */
+SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct sr_serial_dev_inst *serial;
+	int64_t t, elapsed_us;
+
+	(void)fd;
+
+	if (!(sdi = cb_data))
+		return TRUE;
+
+	if (!(devc = sdi->priv))
+		return TRUE;
+
+	serial = sdi->conn;
+
+	if (revents == G_IO_IN) {
+		/* New data arrived. */
+		handle_new_data(sdi);
+	} else {
+		/* Timeout. */
+	}
+
+	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;
+	}
+
+	if (devc->limit_msec) {
+		t = (g_get_monotonic_time() - devc->starttime) / 1000;
+		if (t > (int64_t)devc->limit_msec) {
+			sr_info("Requested time limit reached.");
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			return TRUE;
+		}
+	}
+
+	/* Request next packet, if required. */
+	if (sdi->status == SR_ST_ACTIVE) {
+		if (devc->reply_pending) {
+			elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+			if (elapsed_us > (REQ_TIMEOUT_MS * 1000))
+				devc->reply_pending = FALSE;
+			return TRUE;
+		}
+
+		/* Send command to get voltage, current, and mode (CC or CV). */
+		if (hcs_send_cmd(serial, "GETD\r") < 0)
+			return TRUE;
+
+		devc->req_sent_at = g_get_monotonic_time();
+		devc->reply_pending = TRUE;
+	}
+
+	return TRUE;
+}
diff --git a/src/hardware/manson-hcs-3xxx/protocol.h b/src/hardware/manson-hcs-3xxx/protocol.h
new file mode 100644
index 0000000..8e614ce
--- /dev/null
+++ b/src/hardware/manson-hcs-3xxx/protocol.h
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok at heidbrink.biz>
+ *
+ * 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
+ */
+
+/** @file
+  *  <em>Manson HCS-3xxx Series</em> power supply driver
+  *  @internal
+  */
+
+#ifndef LIBSIGROK_HARDWARE_MANSON_HCS_3XXX_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_MANSON_HCS_3XXX_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "manson-hcs-3xxx"
+
+enum {
+	MANSON_HCS_3100,
+	MANSON_HCS_3102,
+	MANSON_HCS_3104,
+	MANSON_HCS_3150,
+	MANSON_HCS_3200,
+	MANSON_HCS_3202,
+	MANSON_HCS_3204,
+	MANSON_HCS_3300,
+	MANSON_HCS_3302,
+	MANSON_HCS_3304,
+	MANSON_HCS_3400,
+	MANSON_HCS_3402,
+	MANSON_HCS_3404,
+	MANSON_HCS_3600,
+	MANSON_HCS_3602,
+	MANSON_HCS_3604,
+};
+
+/** Information on a single model. */
+struct hcs_model {
+	int model_id;      /**< Model info */
+	const char *name;  /**< Model name */
+	const char *id;    /**< Model ID, like delivered by interface */
+	double voltage[3]; /**< Min, max, step */
+	double current[3]; /**< Min, max, step */
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	const struct hcs_model *model; /**< Model information. */
+
+	uint64_t limit_samples;
+	uint64_t limit_msec;
+	uint64_t num_samples;
+	int64_t starttime;
+	int64_t req_sent_at;
+	gboolean reply_pending;
+
+	void *cb_data;
+
+	float current;		/**< Last current value [A] read from device. */
+	float current_max;	/**< Output current set. */
+	float current_max_device;/**< Device-provided maximum output current. */
+	float voltage;		/**< Last voltage value [V] read from device. */
+	float voltage_max;	/**< Output voltage set. */
+	float voltage_max_device;/**< Device-provided maximum output voltage. */
+	gboolean cc_mode;	/**< Device is in constant current mode (otherwise constant voltage). */
+
+	gboolean output_enabled; /**< Is the output enabled? */
+
+	char buf[50];
+	int buflen;
+};
+
+SR_PRIV int hcs_parse_volt_curr_mode(struct sr_dev_inst *sdi, char **tokens);
+SR_PRIV int hcs_read_reply(struct sr_serial_dev_inst *serial, int lines, char *buf, int buflen);
+SR_PRIV int hcs_send_cmd(struct sr_serial_dev_inst *serial, const char *cmd, ...);
+SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/src/hardware/maynuo-m97/api.c b/src/hardware/maynuo-m97/api.c
new file mode 100644
index 0000000..919cb86
--- /dev/null
+++ b/src/hardware/maynuo-m97/api.c
@@ -0,0 +1,521 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Aurelien Jacobs <aurel at gnuage.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+	SR_CONF_MODBUSADDR
+};
+
+static const uint32_t drvopts[] = {
+	SR_CONF_ELECTRONIC_LOAD,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t devopts_cg[] = {
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_REGULATION | SR_CONF_GET,
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
+	SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET,
+	SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET,
+};
+
+/* The IDs in this list are only guessed and needs to be verified
+   against some real hardware. If at least a few of them matches,
+   it will probably be safe to enable the others. */
+static const struct maynuo_m97_model supported_models[] = {
+//	{  53, "M9711"     ,   30, 150,    150 },
+//	{  54, "M9712"     ,   30, 150,    300 },
+//	{  55, "M9712C"    ,   60, 150,    300 },
+//	{  56, "M9713"     ,  120, 150,    600 },
+//	{  57, "M9712B"    ,   15, 500,    300 },
+//	{  58, "M9713B"    ,   30, 500,    600 },
+//	{  59, "M9714"     ,  240, 150,   1200 },
+//	{  60, "M9714B"    ,   60, 500,   1200 },
+//	{  61, "M9715"     ,  240, 150,   1800 },
+//	{  62, "M9715B"    ,  120, 500,   1800 },
+//	{  63, "M9716"     ,  240, 150,   2400 },
+//	{  64, "M9716B"    ,  120, 500,   2400 },
+//	{  65, "M9717C"    ,  480, 150,   3600 },
+//	{  66, "M9717"     ,  240, 150,   3600 },
+//	{  67, "M9717B"    ,  120, 500,   3600 },
+//	{  68, "M9718"     ,  240, 150,   6000 },
+//	{  69, "M9718B"    ,  120, 500,   6000 },
+//	{  70, "M9718D"    ,  240, 500,   6000 },
+//	{  71, "M9836"     ,  500, 150,  20000 },
+//	{  72, "M9836B"    ,  240, 500,  20000 },
+//	{  73, "M9838B"    ,  240, 500,  50000 },
+//	{  74, "M9839B"    ,  240, 500, 100000 },
+//	{  75, "M9840B"    ,  500, 500, 200000 },
+//	{  76, "M9840"     , 1500, 150, 200000 },
+//	{  77, "M9712B30"  ,   30, 500,    300 },
+//	{  78, "M9718E"    ,  120, 600,   6000 },
+//	{  79, "M9718F"    ,  480, 150,   6000 },
+//	{  80, "M9716E"    ,  480, 150,   3000 },
+//	{  81, "M9710"     ,   30, 150,    150 },
+//	{  82, "M9834"     ,  500, 150,  10000 },
+//	{  83, "M9835"     ,  500, 150,  15000 },
+//	{  84, "M9835B"    ,  240, 500,  15000 },
+//	{  85, "M9837"     ,  500, 150,  35000 },
+//	{  86, "M9837B"    ,  240, 500,  35000 },
+//	{  87, "M9838"     ,  500, 150,  50000 },
+//	{  88, "M9839"     ,  500, 150, 100000 },
+//	{  89, "M9835C"    , 1000, 150,  15000 }, /* ?? */
+//	{  90, "M9836C"    , 1000, 150,  20000 }, /* ?? */
+//	{  91, "M9718F-300",  480, 300,   6000 }, /* ?? */
+//	{  92, "M9836F"    , 1000, 150,  20000 }, /* ?? */
+//	{  93, "M9836E"    ,  240, 600,  20000 }, /* ?? */
+//	{  94, "M9717D"    ,  240, 500,   3600 }, /* ?? */
+//	{  95, "M9836B-720",  240, 720,  20000 }, /* ?? */
+//	{  96, "M9834H"    ,  500, 150,  10000 }, /* ?? */
+//	{  97, "M9836H"    ,  500, 150,  20000 }, /* ?? */
+//	{  98, "M9718F-500",  480, 500,   6000 }, /* ?? */
+//	{  99, "M9834B"    ,  240, 500,  10000 }, /* ?? */
+//	{ 100, "M9811"     ,   30, 150,    200 },
+	{ 101, "M9812"     ,   30, 150,    300 },
+//	{ 102, "M9812B"    ,   15, 500,    300 },
+};
+
+SR_PRIV struct sr_dev_driver maynuo_m97_driver_info;
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus)
+{
+	const struct maynuo_m97_model *model = NULL;
+	struct dev_context *devc;
+	struct sr_dev_inst *sdi;
+	struct sr_channel_group *cg;
+	struct sr_channel *ch;
+	uint16_t id, version;
+	unsigned int i;
+
+	int ret = maynuo_m97_get_model_version(modbus, &id, &version);
+	if (ret != SR_OK)
+		return NULL;
+	for (i = 0; i < ARRAY_SIZE(supported_models); i++)
+		if (id == supported_models[i].id) {
+			model = &supported_models[i];
+			break;
+		}
+	if (model == NULL) {
+		sr_err("Unkown model: %d.", id);
+		return NULL;
+	}
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_ACTIVE;
+	sdi->vendor = g_strdup("Maynuo");
+	sdi->model = g_strdup(model->name);
+	sdi->version = g_strdup_printf("v%d.%d", version/10, version%10);
+	sdi->conn = modbus;
+	sdi->driver = &maynuo_m97_driver_info;
+	sdi->inst_type = SR_INST_MODBUS;
+
+	cg = g_malloc0(sizeof(struct sr_channel_group));
+	cg->name = g_strdup("1");
+	sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+
+	ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V1");
+	cg->channels = g_slist_append(cg->channels, ch);
+
+	ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "I1");
+	cg->channels = g_slist_append(cg->channels, ch);
+
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->model = model;
+
+	sdi->priv = devc;
+
+	return sdi;
+}
+
+static int config_compare(gconstpointer a, gconstpointer b)
+{
+	const struct sr_config *ac = a, *bc = b;
+	return ac->key != bc->key;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct sr_config default_serialcomm = {
+	    .key = SR_CONF_SERIALCOMM,
+	    .data = g_variant_new_string("9600/8n1"),
+	};
+	struct sr_config default_modbusaddr = {
+	    .key = SR_CONF_MODBUSADDR,
+	    .data = g_variant_new_uint64(1),
+	};
+	GSList *opts = options, *devices;
+
+	if (!g_slist_find_custom(options, &default_serialcomm, config_compare))
+		opts = g_slist_prepend(opts, &default_serialcomm);
+	if (!g_slist_find_custom(options, &default_modbusaddr, config_compare))
+		opts = g_slist_prepend(opts, &default_modbusaddr);
+
+	devices = sr_modbus_scan(di->context, opts, probe_device);
+
+	while (opts != options)
+		opts = g_slist_delete_link(opts, opts);
+	g_variant_unref(default_serialcomm.data);
+	g_variant_unref(default_modbusaddr.data);
+
+	return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, g_free);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct sr_modbus_dev_inst *modbus = sdi->conn;
+
+	if (sr_modbus_open(modbus) < 0)
+		return SR_ERR;
+
+	sdi->status = SR_ST_ACTIVE;
+
+	maynuo_m97_set_bit(modbus, PC1, 1);
+
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_modbus_dev_inst *modbus;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	modbus = sdi->conn;
+
+	if (modbus) {
+		devc = sdi->priv;
+		if (devc->expecting_registers) {
+			/* Wait for the last data that was requested from the device. */
+			uint16_t registers[devc->expecting_registers];
+			sr_modbus_read_holding_registers(modbus, -1,
+			                                 devc->expecting_registers,
+			                                 registers);
+		}
+
+		maynuo_m97_set_bit(modbus, PC1, 0);
+
+		if (sr_modbus_close(modbus) < 0)
+			return SR_ERR;
+		sdi->status = SR_ST_INACTIVE;
+	}
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return dev_clear(di);
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	struct sr_modbus_dev_inst *modbus;
+	enum maynuo_m97_mode mode;
+	int ret, ivalue;
+	float fvalue;
+
+	(void)cg;
+
+	modbus = sdi->conn;
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	switch (key) {
+	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_ENABLED:
+		if ((ret = maynuo_m97_get_bit(modbus, ISTATE, &ivalue)) == SR_OK)
+			*data = g_variant_new_boolean(ivalue);
+		break;
+	case SR_CONF_REGULATION:
+		if ((ret = maynuo_m97_get_bit(modbus, UNREG, &ivalue)) != SR_OK)
+			break;
+		if (ivalue)
+			*data = g_variant_new_string("UR");
+		else if ((ret = maynuo_m97_get_mode(modbus, &mode)) == SR_OK)
+			*data = g_variant_new_string(maynuo_m97_mode_to_str(mode));
+		break;
+	case SR_CONF_VOLTAGE:
+		if ((ret = maynuo_m97_get_float(modbus, U, &fvalue)) == SR_OK)
+			*data = g_variant_new_double(fvalue);
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		if ((ret = maynuo_m97_get_float(modbus, UFIX, &fvalue)) == SR_OK)
+			*data = g_variant_new_double(fvalue);
+		break;
+	case SR_CONF_CURRENT:
+		if ((ret = maynuo_m97_get_float(modbus, I, &fvalue)) == SR_OK)
+			*data = g_variant_new_double(fvalue);
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		if ((ret = maynuo_m97_get_float(modbus, IFIX, &fvalue)) == SR_OK)
+			*data = g_variant_new_double(fvalue);
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+		*data = g_variant_new_boolean(TRUE);
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE:
+		if ((ret = maynuo_m97_get_bit(modbus, UOVER, &ivalue)) == SR_OK)
+			*data = g_variant_new_boolean(ivalue);
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+		if ((ret = maynuo_m97_get_float(modbus, UMAX, &fvalue)) == SR_OK)
+			*data = g_variant_new_double(fvalue);
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+		*data = g_variant_new_boolean(TRUE);
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE:
+		if ((ret = maynuo_m97_get_bit(modbus, IOVER, &ivalue)) == SR_OK)
+			*data = g_variant_new_boolean(ivalue);
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+		if ((ret = maynuo_m97_get_float(modbus, IMAX, &fvalue)) == SR_OK)
+			*data = g_variant_new_double(fvalue);
+		break;
+	case SR_CONF_OVER_TEMPERATURE_PROTECTION:
+		*data = g_variant_new_boolean(TRUE);
+		break;
+	case SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE:
+		if ((ret = maynuo_m97_get_bit(modbus, HEAT, &ivalue)) == SR_OK)
+			*data = g_variant_new_boolean(ivalue);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	struct sr_modbus_dev_inst *modbus;
+	int ret;
+
+	(void)data;
+	(void)cg;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	modbus = sdi->conn;
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_LIMIT_SAMPLES:
+		devc->limit_samples = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_LIMIT_MSEC:
+		devc->limit_msec = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_ENABLED:
+		ret = maynuo_m97_set_input(modbus, g_variant_get_boolean(data));
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		ret = maynuo_m97_set_float(modbus, UFIX, g_variant_get_double(data));
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		ret = maynuo_m97_set_float(modbus, IFIX, g_variant_get_double(data));
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+		ret = maynuo_m97_set_float(modbus, UMAX, g_variant_get_double(data));
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+		ret = maynuo_m97_set_float(modbus, IMAX, g_variant_get_double(data));
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	GVariantBuilder gvb;
+	int ret;
+
+	/* Always available, even without sdi. */
+	if (key == SR_CONF_SCAN_OPTIONS) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	} else if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	if (!sdi)
+		return SR_ERR_ARG;
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	if (!cg) {
+		/* No channel group: global options. */
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	} else {
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
+			break;
+		case SR_CONF_VOLTAGE_TARGET:
+			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+			/* Min, max, write resolution. */
+			g_variant_builder_add_value(&gvb, g_variant_new_double(0.0));
+			g_variant_builder_add_value(&gvb, g_variant_new_double(devc->model->max_voltage));
+			g_variant_builder_add_value(&gvb, g_variant_new_double(0.001));
+			*data = g_variant_builder_end(&gvb);
+			break;
+		case SR_CONF_CURRENT_LIMIT:
+			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+			/* Min, max, step. */
+			g_variant_builder_add_value(&gvb, g_variant_new_double(0.0));
+			g_variant_builder_add_value(&gvb, g_variant_new_double(devc->model->max_current));
+			g_variant_builder_add_value(&gvb, g_variant_new_double(0.0001));
+			*data = g_variant_builder_end(&gvb);
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	}
+
+	return ret;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+		void *cb_data)
+{
+	struct dev_context *devc;
+	struct sr_modbus_dev_inst *modbus;
+	int ret;
+
+	(void)cb_data;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	modbus = sdi->conn;
+	devc = sdi->priv;
+
+	if ((ret = sr_modbus_source_add(sdi->session, modbus, G_IO_IN, 10,
+			maynuo_m97_receive_data, (void *)sdi)) != SR_OK)
+		return ret;
+
+	/* Send header packet to the session bus. */
+	std_session_send_df_header(sdi, LOG_PREFIX);
+
+	devc->num_samples = 0;
+	devc->starttime = g_get_monotonic_time();
+
+	return maynuo_m97_capture_start(sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_modbus_dev_inst *modbus;
+	struct sr_datafeed_packet packet;
+
+	(void)cb_data;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	/* End of last frame. */
+	packet.type = SR_DF_END;
+	sr_session_send(sdi, &packet);
+
+	modbus = sdi->conn;
+	sr_modbus_source_remove(sdi->session, modbus);
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver maynuo_m97_driver_info = {
+	.name = "maynuo-m97",
+	.longname = "maynuo M97/M98 series",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/maynuo-m97/protocol.c b/src/hardware/maynuo-m97/protocol.c
new file mode 100644
index 0000000..085d379
--- /dev/null
+++ b/src/hardware/maynuo-m97/protocol.c
@@ -0,0 +1,212 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Aurelien Jacobs <aurel at gnuage.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+
+SR_PRIV int maynuo_m97_get_bit(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_coil address, int *value)
+{
+	uint8_t coil;
+	int ret = sr_modbus_read_coils(modbus, address, 1, &coil);
+	*value = coil & 1;
+	return ret;
+}
+
+SR_PRIV int maynuo_m97_set_bit(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_coil address, int value)
+{
+	return sr_modbus_write_coil(modbus, address, value);
+}
+
+SR_PRIV int maynuo_m97_get_float(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_register address, float *value)
+{
+	uint16_t registers[2];
+	int ret = sr_modbus_read_holding_registers(modbus, address, 2, registers);
+	if (ret == SR_OK)
+		*value = RBFL(registers);
+	return ret;
+}
+
+SR_PRIV int maynuo_m97_set_float(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_register address, float value)
+{
+	uint16_t registers[2];
+	WBFL(registers, value);
+	return sr_modbus_write_multiple_registers(modbus, address, 2, registers);
+}
+
+
+static int maynuo_m97_cmd(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_mode cmd)
+{
+	uint16_t registers[1];
+	WB16(registers, cmd);
+	return sr_modbus_write_multiple_registers(modbus, CMD, 1, registers);
+}
+
+SR_PRIV int maynuo_m97_get_mode(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_mode *mode)
+{
+	uint16_t registers[1];
+	int ret;
+	ret = sr_modbus_read_holding_registers(modbus, SETMODE, 1, registers);
+	*mode = RB16(registers) & 0xFF;
+	return ret;
+}
+
+SR_PRIV int maynuo_m97_set_mode(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_mode mode)
+{
+	return maynuo_m97_cmd(modbus, mode);
+}
+
+SR_PRIV int maynuo_m97_set_input(struct sr_modbus_dev_inst *modbus, int enable)
+{
+	enum maynuo_m97_mode mode;
+	int ret;
+	if ((ret = maynuo_m97_get_mode(modbus, &mode)) != SR_OK)
+		return ret;
+	if ((ret = maynuo_m97_cmd(modbus, enable ? INPUT_ON : INPUT_OFF)) != SR_OK)
+		return ret;
+	return maynuo_m97_set_mode(modbus, mode);
+}
+
+SR_PRIV int maynuo_m97_get_model_version(struct sr_modbus_dev_inst *modbus,
+		uint16_t *model, uint16_t *version)
+{
+	uint16_t registers[2];
+	int ret;
+	ret = sr_modbus_read_holding_registers(modbus, MODEL, 2, registers);
+	*model   = RB16(registers+0);
+	*version = RB16(registers+1);
+	return ret;
+}
+
+
+SR_PRIV const char *maynuo_m97_mode_to_str(enum maynuo_m97_mode mode)
+{
+	switch (mode) {
+	case CC:             return "CC";
+	case CV:             return "CV";
+	case CW:             return "CP";
+	case CR:             return "CR";
+	case CC_SOFT_START:  return "CC Soft Start";
+	case DYNAMIC:        return "Dynamic";
+	case SHORT_CIRCUIT:  return "Short Circuit";
+	case LIST:           return "List Mode";
+	case CC_L_AND_UL:    return "CC Loading and Unloading";
+	case CV_L_AND_UL:    return "CV Loading and Unloading";
+	case CW_L_AND_UL:    return "CP Loading and Unloading";
+	case CR_L_AND_UL:    return "CR Loading and Unloading";
+	case CC_TO_CV:       return "CC + CV";
+	case CR_TO_CV:       return "CR + CV";
+	case BATTERY_TEST:   return "Battery Test";
+	case CV_SOFT_START:  return "CV Soft Start";
+	default:             return "UNKNOWN";
+	}
+}
+
+
+static void maynuo_m97_session_send_value(const struct sr_dev_inst *sdi, struct sr_channel *ch, float value, enum sr_mq mq, enum sr_unit unit)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+
+	analog.channels = g_slist_append(NULL, ch);
+	analog.num_samples = 1;
+	analog.data = &value;
+	analog.mq = mq;
+	analog.unit = unit;
+	analog.mqflags = SR_MQFLAG_DC;
+
+	packet.type = SR_DF_ANALOG_OLD;
+	packet.payload = &analog;
+	sr_session_send(sdi, &packet);
+	g_slist_free(analog.channels);
+}
+
+SR_PRIV int maynuo_m97_capture_start(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_modbus_dev_inst *modbus;
+	int ret;
+
+	modbus = sdi->conn;
+	devc = sdi->priv;
+
+	if ((ret = sr_modbus_read_holding_registers(modbus, U, 4, NULL)) == SR_OK)
+		devc->expecting_registers = 4;
+	return ret;
+}
+
+SR_PRIV int maynuo_m97_receive_data(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct sr_modbus_dev_inst *modbus;
+	struct sr_datafeed_packet packet;
+	uint16_t registers[4];
+	int64_t t;
+
+	(void)fd;
+	(void)revents;
+
+	if (!(sdi = cb_data))
+		return TRUE;
+
+	modbus = sdi->conn;
+	devc = sdi->priv;
+
+	devc->expecting_registers = 0;
+	if (sr_modbus_read_holding_registers(modbus, -1, 4, registers) == SR_OK) {
+		packet.type = SR_DF_FRAME_BEGIN;
+		sr_session_send(cb_data, &packet);
+
+		maynuo_m97_session_send_value(sdi, sdi->channels->data,
+		                              RBFL(registers + 0),
+		                              SR_MQ_VOLTAGE, SR_UNIT_VOLT);
+		maynuo_m97_session_send_value(sdi, sdi->channels->next->data,
+		                              RBFL(registers + 2),
+		                              SR_MQ_CURRENT, SR_UNIT_AMPERE);
+
+		packet.type = SR_DF_FRAME_END;
+		sr_session_send(cb_data, &packet);
+		devc->num_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;
+	}
+
+	if (devc->limit_msec) {
+		t = (g_get_monotonic_time() - devc->starttime) / 1000;
+		if (t > (int64_t)devc->limit_msec) {
+			sr_info("Requested time limit reached.");
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			return TRUE;
+		}
+	}
+
+	maynuo_m97_capture_start(sdi);
+	return TRUE;
+}
diff --git a/src/hardware/maynuo-m97/protocol.h b/src/hardware/maynuo-m97/protocol.h
new file mode 100644
index 0000000..62c1d47
--- /dev/null
+++ b/src/hardware/maynuo-m97/protocol.h
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Aurelien Jacobs <aurel at gnuage.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_MAYNUO_M97_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_MAYNUO_M97_PROTOCOL_H
+
+#include <stdint.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "maynuo-m97"
+
+struct maynuo_m97_model {
+	unsigned int id;
+	const char *name;
+	unsigned int max_current;
+	unsigned int max_voltage;
+	unsigned int max_power;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/* Model-specific information */
+	const struct maynuo_m97_model *model;
+
+	/* Acquisition settings */
+	uint64_t limit_samples;
+	uint64_t limit_msec;
+
+	/* Operational state */
+	uint64_t num_samples;
+	int64_t starttime;
+	int expecting_registers;
+};
+
+enum maynuo_m97_coil {
+	PC1        = 0x0500,
+	PC2        = 0X0501,
+	TRIG       = 0x0502,
+	REMOTE     = 0x0503,
+	ISTATE     = 0x0510,
+	TRACK      = 0x0511,
+	MEMORY     = 0x0512,
+	VOICEEN    = 0x0513,
+	CONNECT    = 0x0514,
+	ATEST      = 0x0515,
+	ATESTUN    = 0x0516,
+	ATESTPASS  = 0x0517,
+	IOVER      = 0x0520,
+	UOVER      = 0x0521,
+	POVER      = 0x0522,
+	HEAT       = 0x0523,
+	REVERSE    = 0x0524,
+	UNREG      = 0x0525,
+	ERREP      = 0x0526,
+	ERRCAL     = 0x0527,
+};
+
+enum maynuo_m97_register {
+	CMD        = 0x0A00,
+	IFIX       = 0x0A01,
+	UFIX       = 0x0A03,
+	PFIX       = 0x0A05,
+	RFIX       = 0x0A07,
+	TMCCS      = 0x0A09,
+	TMCVS      = 0x0A0B,
+	UCCONSET   = 0x0A0D,
+	UCCOFFSET  = 0x0A0F,
+	UCVONSET   = 0x0A11,
+	UCVOFFSET  = 0x0A13,
+	UCPONSET   = 0x0A15,
+	UCPOFFSET  = 0x0A17,
+	UCRONSET   = 0x0A19,
+	UCROFFSET  = 0x0A1B,
+	UCCCV      = 0x0A1D,
+	UCRCV      = 0x0A1F,
+	IA         = 0x0A21,
+	IB         = 0x0A23,
+	TMAWD      = 0x0A25,
+	TMBWD      = 0x0A27,
+	TMTRANSRIS = 0x0A29,
+	TMTRANSFAL = 0x0A2B,
+	MODETRAN   = 0x0A2D,
+	UBATTEND   = 0x0A2E,
+	BATT       = 0x0A30,
+	SERLIST    = 0x0A32,
+	SERATEST   = 0x0A33,
+	IMAX       = 0x0A34,
+	UMAX       = 0x0A36,
+	PMAX       = 0x0A38,
+	ILCAL      = 0x0A3A,
+	IHCAL      = 0x0A3C,
+	ULCAL      = 0x0A3E,
+	UHCAL      = 0x0A40,
+	TAGSCAL    = 0x0A42,
+	U          = 0x0B00,
+	I          = 0x0B02,
+	SETMODE    = 0x0B04,
+	INPUTMODE  = 0x0B05,
+	MODEL      = 0x0B06,
+	EDITION    = 0x0B07,
+};
+
+enum maynuo_m97_mode {
+	CC            =  1,
+	CV            =  2,
+	CW            =  3,
+	CR            =  4,
+	CC_SOFT_START = 20,
+	DYNAMIC       = 25,
+	SHORT_CIRCUIT = 26,
+	LIST          = 27,
+	CC_L_AND_UL   = 30,
+	CV_L_AND_UL   = 31,
+	CW_L_AND_UL   = 32,
+	CR_L_AND_UL   = 33,
+	CC_TO_CV      = 34,
+	CR_TO_CV      = 36,
+	BATTERY_TEST  = 38,
+	CV_SOFT_START = 39,
+	SYSTEM_PARAM  = 41,
+	INPUT_ON      = 42,
+	INPUT_OFF     = 43,
+};
+
+SR_PRIV int maynuo_m97_get_bit(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_coil address, int *value);
+SR_PRIV int maynuo_m97_set_bit(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_coil address, int value);
+SR_PRIV int maynuo_m97_get_float(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_register address, float *value);
+SR_PRIV int maynuo_m97_set_float(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_register address, float value);
+
+SR_PRIV int maynuo_m97_get_mode(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_mode *mode);
+SR_PRIV int maynuo_m97_set_mode(struct sr_modbus_dev_inst *modbus,
+		enum maynuo_m97_mode mode);
+SR_PRIV int maynuo_m97_set_input(struct sr_modbus_dev_inst *modbus, int enable);
+SR_PRIV int maynuo_m97_get_model_version(struct sr_modbus_dev_inst *modbus,
+		uint16_t *model, uint16_t *version);
+
+SR_PRIV const char *maynuo_m97_mode_to_str(enum maynuo_m97_mode mode);
+
+SR_PRIV int maynuo_m97_capture_start(const struct sr_dev_inst *sdi);
+SR_PRIV int maynuo_m97_receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/hardware/mic-985xx/api.c b/src/hardware/mic-985xx/api.c
similarity index 68%
rename from hardware/mic-985xx/api.c
rename to src/hardware/mic-985xx/api.c
index 7b0d680..775bcc0 100644
--- a/hardware/mic-985xx/api.c
+++ b/src/hardware/mic-985xx/api.c
@@ -18,19 +18,27 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include "protocol.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t drvopts_temp[] = {
+	SR_CONF_THERMOMETER,
+};
+
+static const uint32_t drvopts_temp_hum[] = {
 	SR_CONF_THERMOMETER,
 	SR_CONF_HYGROMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
-	SR_CONF_CONTINUOUS,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
 };
 
 SR_PRIV struct sr_dev_driver mic_98581_driver_info;
@@ -56,8 +64,6 @@ static int dev_clear(int idx)
 
 static int init(struct sr_context *sr_ctx, int idx)
 {
-	sr_dbg("Selected '%s' subdriver.", mic_devs[idx].di->name);
-
 	return std_init(sr_ctx, mic_devs[idx].di, LOG_PREFIX);
 }
 
@@ -66,17 +72,15 @@ static GSList *mic_scan(const char *conn, const char *serialcomm, int idx)
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
-	struct sr_channel *ch;
 	struct sr_serial_dev_inst *serial;
 	GSList *devices;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
 
-	drvc = mic_devs[idx].di->priv;
+	drvc = mic_devs[idx].di->context;
 	devices = NULL;
 	serial_flush(serial);
 
@@ -86,35 +90,24 @@ static GSList *mic_scan(const char *conn, const char *serialcomm, int idx)
 	sr_info("Found device on port %s.", conn);
 
 	/* TODO: Fill in version from protocol response. */
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, mic_devs[idx].vendor,
-				    mic_devs[idx].device, NULL)))
-		goto scan_cleanup;
-
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		goto scan_cleanup;
-	}
-
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(mic_devs[idx].vendor);
+	sdi->model = g_strdup(mic_devs[idx].device);
+	devc = g_malloc0(sizeof(struct dev_context));
 	sdi->inst_type = SR_INST_SERIAL;
 	sdi->conn = serial;
-
 	sdi->priv = devc;
 	sdi->driver = mic_devs[idx].di;
 
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "Temperature")))
-		goto scan_cleanup;
-	sdi->channels = g_slist_append(sdi->channels, ch);
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "Temperature");
 
-	if (mic_devs[idx].has_humidity) {
-		if (!(ch = sr_channel_new(1, SR_CHANNEL_ANALOG, TRUE, "Humidity")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-	}
+	if (mic_devs[idx].has_humidity)
+		sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "Humidity");
 
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
 
-scan_cleanup:
 	serial_close(serial);
 
 	return devices;
@@ -154,7 +147,7 @@ static GSList *scan(GSList *options, int idx)
 
 static GSList *dev_list(int idx)
 {
-	return ((struct drv_context *)(mic_devs[idx].di->priv))->instances;
+	return ((struct drv_context *)(mic_devs[idx].di->context))->instances;
 }
 
 static int cleanup(int idx)
@@ -162,7 +155,7 @@ static int cleanup(int idx)
 	return dev_clear(idx);
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -174,16 +167,12 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
 	devc = sdi->priv;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_samples = g_variant_get_uint64(data);
-		sr_dbg("Setting sample limit to %" PRIu64 ".",
-		       devc->limit_samples);
 		break;
 	case SR_CONF_LIMIT_MSEC:
 		devc->limit_msec = g_variant_get_uint64(data);
-		sr_dbg("Setting time limit to %" PRIu64 "ms.",
-		       devc->limit_msec);
 		break;
 	default:
 		return SR_ERR_NA;
@@ -192,20 +181,29 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
-		const struct sr_channel_group *cg)
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg, int idx)
 {
-	(void)sdi;
 	(void)cg;
 
 	switch (key) {
 	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		if (!sdi && !mic_devs[idx].has_humidity) {
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts_temp, ARRAY_SIZE(drvopts_temp),
+				sizeof(uint32_t));
+		} else if (!sdi && mic_devs[idx].has_humidity) {
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts_temp_hum, ARRAY_SIZE(drvopts_temp_hum),
+				sizeof(uint32_t));
+		} else {
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		}
 		break;
 	default:
 		return SR_ERR_NA;
@@ -233,7 +231,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 
 	/* Poll every 100ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 100,
+	serial_source_add(sdi->session, serial, G_IO_IN, 100,
 		      mic_devs[idx].receive_data, (void *)sdi);
 
 	return SR_OK;
@@ -247,15 +245,24 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 
 /* Driver-specific API function wrappers */
 #define HW_INIT(X) \
-static int init_##X(struct sr_context *sr_ctx) { return init(sr_ctx, X); }
+static int init_##X(struct sr_dev_driver *di, struct sr_context *sr_ctx) { \
+	(void)di; return init(sr_ctx, X); }
 #define HW_CLEANUP(X) \
-static int cleanup_##X(void) { return cleanup(X); }
+static int cleanup_##X(const struct sr_dev_driver *di) { \
+	(void)di; return cleanup(X); }
 #define HW_SCAN(X) \
-static GSList *scan_##X(GSList *options) { return scan(options, X); }
+static GSList *scan_##X(struct sr_dev_driver *di, GSList *options) { \
+	(void)di; return scan(options, X); }
 #define HW_DEV_LIST(X) \
-static GSList *dev_list_##X(void) { return dev_list(X); }
+static GSList *dev_list_##X(const struct sr_dev_driver *di) { \
+	(void)di; return dev_list(X); }
 #define HW_DEV_CLEAR(X) \
-static int dev_clear_##X(void) { return dev_clear(X); }
+static int dev_clear_##X(const struct sr_dev_driver *di) { \
+	(void)di; return dev_clear(X); }
+#define HW_CONFIG_LIST(X) \
+static int config_list_##X(uint32_t key, GVariant **data, \
+const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { \
+return config_list(key, data, sdi, cg, X); }
 #define HW_DEV_ACQUISITION_START(X) \
 static int dev_acquisition_start_##X(const struct sr_dev_inst *sdi, \
 void *cb_data) { return dev_acquisition_start(sdi, cb_data, X); }
@@ -267,6 +274,7 @@ HW_CLEANUP(ID_UPPER) \
 HW_SCAN(ID_UPPER) \
 HW_DEV_LIST(ID_UPPER) \
 HW_DEV_CLEAR(ID_UPPER) \
+HW_CONFIG_LIST(ID_UPPER) \
 HW_DEV_ACQUISITION_START(ID_UPPER) \
 SR_PRIV struct sr_dev_driver ID##_driver_info = { \
 	.name = NAME, \
@@ -279,12 +287,12 @@ SR_PRIV struct sr_dev_driver ID##_driver_info = { \
 	.dev_clear = dev_clear_##ID_UPPER, \
 	.config_get = NULL, \
 	.config_set = config_set, \
-	.config_list = config_list, \
+	.config_list = config_list_##ID_UPPER, \
 	.dev_open = std_serial_dev_open, \
 	.dev_close = std_serial_dev_close, \
 	.dev_acquisition_start = dev_acquisition_start_##ID_UPPER, \
 	.dev_acquisition_stop = dev_acquisition_stop, \
-	.priv = NULL, \
+	.context = NULL, \
 };
 
 DRV(mic_98581, MIC_98581, "mic-98581", "MIC 98581")
diff --git a/hardware/mic-985xx/protocol.c b/src/hardware/mic-985xx/protocol.c
similarity index 90%
rename from hardware/mic-985xx/protocol.c
rename to src/hardware/mic-985xx/protocol.c
index ce5f020..4ee11b7 100644
--- a/hardware/mic-985xx/protocol.c
+++ b/src/hardware/mic-985xx/protocol.c
@@ -18,13 +18,15 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include "protocol.h"
 
 static int mic_send(struct sr_serial_dev_inst *serial, const char *cmd)
 {
 	int ret;
 
-	if ((ret = serial_write(serial, cmd, strlen(cmd))) < 0) {
+	if ((ret = serial_write_blocking(serial, cmd, strlen(cmd),
+			serial_timeout(serial, strlen(cmd)))) < 0) {
 		sr_err("Error sending '%s' command: %d.", cmd, ret);
 		return SR_ERR;
 	}
@@ -94,6 +96,9 @@ static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
 	float temperature, humidity;
 	struct sr_datafeed_packet packet;
 	struct sr_datafeed_analog analog;
+	struct sr_analog_encoding encoding;
+	struct sr_analog_meaning meaning;
+	struct sr_analog_spec spec;
 	struct dev_context *devc;
 	GSList *l;
 	int ret;
@@ -108,8 +113,7 @@ static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
 		return SR_ERR;
 	}
 
-	/* Clear 'analog', otherwise it'll contain random garbage. */
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	sr_analog_init(&analog, &encoding, &meaning, &spec, 3);
 
 	/* Common values for both channels. */
 	packet.type = SR_DF_ANALOG;
@@ -119,9 +123,9 @@ static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
 	/* Temperature. */
 	l = g_slist_copy(sdi->channels);
 	l = g_slist_remove_link(l, g_slist_nth(l, 1));
-	analog.channels = l;
-	analog.mq = SR_MQ_TEMPERATURE;
-	analog.unit = SR_UNIT_CELSIUS; /* TODO: Use C/F correctly. */
+	meaning.channels = l;
+	meaning.mq = SR_MQ_TEMPERATURE;
+	meaning.unit = SR_UNIT_CELSIUS; /* TODO: Use C/F correctly. */
 	analog.data = &temperature;
 	sr_session_send(devc->cb_data, &packet);
 	g_slist_free(l);
@@ -130,9 +134,9 @@ static int handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi, int idx)
 	if (mic_devs[idx].has_humidity) {
 		l = g_slist_copy(sdi->channels);
 		l = g_slist_remove_link(l, g_slist_nth(l, 0));
-		analog.channels = l;
-		analog.mq = SR_MQ_RELATIVE_HUMIDITY;
-		analog.unit = SR_UNIT_PERCENTAGE;
+		meaning.channels = l;
+		meaning.mq = SR_MQ_RELATIVE_HUMIDITY;
+		meaning.unit = SR_UNIT_PERCENTAGE;
 		analog.data = &humidity;
 		sr_session_send(devc->cb_data, &packet);
 		g_slist_free(l);
@@ -154,7 +158,7 @@ static void handle_new_data(struct sr_dev_inst *sdi, int idx)
 
 	/* Try to get as much data as the buffer can hold. */
 	len = SERIAL_BUFSIZE - devc->buflen;
-	len = serial_read(serial, devc->buf + devc->buflen, len);
+	len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
 	if (len < 1) {
 		sr_err("Serial port read error: %d.", len);
 		return;
diff --git a/hardware/mic-985xx/protocol.h b/src/hardware/mic-985xx/protocol.h
similarity index 90%
rename from hardware/mic-985xx/protocol.h
rename to src/hardware/mic-985xx/protocol.h
index 279cca5..f0e31a6 100644
--- a/hardware/mic-985xx/protocol.h
+++ b/src/hardware/mic-985xx/protocol.h
@@ -25,23 +25,20 @@
 #include <string.h>
 #include <ctype.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "mic-985xx"
 
-/* Note: When adding entries here, don't forget to update MIC_DEV_COUNT. */
 enum {
 	MIC_98581,
 	MIC_98583,
 };
 
-#define MIC_DEV_COUNT 2
-
 struct mic_dev_info {
-	char *vendor;
-	char *device;
-	char *conn;
+	const char *vendor;
+	const char *device;
+	const char *conn;
 	uint32_t max_sample_points;
 	gboolean has_temperature;
 	gboolean has_humidity;
@@ -51,7 +48,7 @@ struct mic_dev_info {
 	int (*receive_data)(int, int, void *);
 };
 
-extern SR_PRIV const struct mic_dev_info mic_devs[MIC_DEV_COUNT];
+extern SR_PRIV const struct mic_dev_info mic_devs[];
 
 #define SERIAL_BUFSIZE 256
 
diff --git a/src/hardware/motech-lps-30x/api.c b/src/hardware/motech-lps-30x/api.c
new file mode 100644
index 0000000..0ecfcb0
--- /dev/null
+++ b/src/hardware/motech-lps-30x/api.c
@@ -0,0 +1,855 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok at heidbrink.biz>
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com> (code from atten-pps3xxx)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ *  <em>Motech LPS-30x series</em> power supply driver
+ *  @internal
+ */
+
+#include <config.h>
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+#include "protocol.h"
+
+/* Forward declarations */
+SR_PRIV struct sr_dev_driver motech_lps_301_driver_info;
+SR_PRIV int lps_read_reply(struct sr_serial_dev_inst *serial, char **buf, int *buflen);
+SR_PRIV int lps_send_va(struct sr_serial_dev_inst *serial, const char *fmt, va_list args);
+SR_PRIV int lps_cmd_ok(struct sr_serial_dev_inst *serial, const char *fmt, ...);
+SR_PRIV int lps_cmd_reply(char *reply, struct sr_serial_dev_inst *serial, const char *fmt, ...);
+SR_PRIV int lps_query_status(struct sr_dev_inst *sdi);
+
+/* Serial communication parameters */
+#define SERIALCOMM "2400/8n1/dtr=1/rts=1/flow=0"
+
+#define VENDOR_MOTECH "Motech"
+
+/** Driver capabilities generic. */
+static const uint32_t drvopts[] = {
+	/* Device class */
+	SR_CONF_POWER_SUPPLY,
+};
+
+/** Driver scanning options. */
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+};
+
+/** Hardware capabilities generic. */
+static const uint32_t devopts[] = {
+	/* Device class */
+	SR_CONF_POWER_SUPPLY,
+	/* Acquisition modes. */
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+	/* Device configuration */
+	SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+/** Hardware capabilities channel 1, 2. */
+static const uint32_t devopts_ch12[] = {
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+/** Hardware capabilities channel 3. (LPS-304/305 only). */
+static const uint32_t devopts_ch3[] = {
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const char *channel_modes[] = {
+	"Independent",
+	"Track1",
+	"Track2",
+};
+
+static const struct lps_modelspec models[] = {
+	{ LPS_UNKNOWN, "Dummy", 0,
+		{
+
+		}
+	},
+	{ LPS_301, "LPS-301", 1,
+		{
+			/* Channel 1 */
+			{ { 0, 32, 0.01 }, { 0.005, 2, 0.001 } },
+		},
+	},
+	{ LPS_302, "LPS-302", 1,
+		{
+			/* Channel 1 */
+			{ { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+		},
+	},
+	{ LPS_303, "LPS-303", 1,
+		{
+			/* Channel 1 */
+			{ { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+		},
+	},
+	{ LPS_304, "LPS-304", 3,
+		{
+			/* Channel 1 */
+			{ { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+			/* Channel 2 */
+			{ { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+			/* Channel 3 */
+			{ { 5, 5, 0.0 }, { 0.005, 3, 0.001 } },
+		},
+	},
+	{ LPS_305, "LPS-305", 3,
+		{
+			/* Channel 1 */
+			{ { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+			/* Channel 2 */
+			{ { 0, 32, 0.01 }, { 0.005, 3, 0.001 } },
+			/* Channel 3 */
+			{ { 3.3, 5, 1.7 }, { 0.005, 3, 0.001 } },
+		},
+	},
+};
+
+static int init_lps301(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+/** Send command to device with va_list.
+ */
+SR_PRIV int lps_send_va(struct sr_serial_dev_inst *serial, const char *fmt, va_list args)
+{
+	int retc;
+	char auxfmt[LINELEN_MAX];
+	char buf[LINELEN_MAX];
+
+	snprintf(auxfmt, sizeof(auxfmt), "%s\r\n", fmt);
+	vsnprintf(buf, sizeof(buf), auxfmt, args);
+
+	sr_spew("lps_send_va: \"%s\"", buf);
+
+	retc = serial_write_blocking(serial, buf, strlen(buf),
+			serial_timeout(serial, strlen(buf)));
+
+	if (retc < 0)
+		return SR_ERR;
+
+	return SR_OK;
+}
+
+/** Send command to device.
+ */
+SR_PRIV int lps_send_req(struct sr_serial_dev_inst *serial, const char *fmt, ...)
+{
+	int retc;
+	va_list args;
+
+	va_start(args, fmt);
+	retc = lps_send_va(serial, fmt, args);
+	va_end(args);
+
+	return retc;
+}
+
+/** Send command and consume simple OK reply. */
+SR_PRIV int lps_cmd_ok(struct sr_serial_dev_inst *serial, const char *fmt, ...)
+{
+	int retc;
+	va_list args;
+	char buf[LINELEN_MAX];
+	char *bufptr;
+	int  buflen;
+
+	/* Send command */
+	va_start(args, fmt);
+	retc = lps_send_va(serial, fmt, args);
+	va_end(args);
+
+	if (retc != SR_OK)
+		return SR_ERR;
+
+	/* Read reply */
+	buf[0] = '\0';
+	bufptr = buf;
+	buflen = sizeof(buf);
+	retc = lps_read_reply(serial, &bufptr, &buflen);
+	if ((retc == SR_OK) && (buflen == 0))
+		return SR_OK;
+
+	return SR_ERR;
+}
+
+/** Send command and read reply string.
+ *  @param reply Pointer to buffer of size LINELEN_MAX. Will be NUL-terminated.
+ */
+SR_PRIV int lps_cmd_reply(char *reply, struct sr_serial_dev_inst *serial, const char *fmt, ...)
+{
+	int retc;
+	va_list args;
+	char buf[LINELEN_MAX];
+	char *bufptr;
+	int  buflen;
+
+	reply[0] = '\0';
+
+	/* Send command */
+	va_start(args, fmt);
+	retc = lps_send_va(serial, fmt, args);
+	va_end(args);
+
+	if (retc != SR_OK)
+		return SR_ERR;
+
+	/* Read reply */
+	buf[0] = '\0';
+	bufptr = buf;
+	buflen = sizeof(buf);
+	retc = lps_read_reply(serial, &bufptr, &buflen);
+	if ((retc == SR_OK) && (buflen > 0)) {
+		strcpy(reply, buf);
+		return SR_OK;
+	}
+
+	return SR_ERR;
+}
+
+/** Process integer value returned by STATUS command. */
+SR_PRIV int lps_process_status(struct sr_dev_inst *sdi, int stat)
+{
+	struct dev_context *devc;
+	int tracking_mode;
+
+	devc = (struct dev_context *)sdi->priv;
+
+	sr_spew("Status: %d", stat);
+	devc->channel_status[0].cc_mode = (stat & 0x01) != 0;
+	sr_spew("Channel 1 %s mode", devc->channel_status[0].cc_mode?"CC":"CV");
+	if (devc->model->num_channels > 1) {
+		devc->channel_status[1].cc_mode = (stat & 0x02) != 0;
+		sr_spew("Channel 2 %s mode", devc->channel_status[1].cc_mode?"CC":"CV");
+
+		tracking_mode = (stat & 0x0c) >> 2;
+		switch (tracking_mode) {
+		case 0: devc->tracking_mode = 0;
+			break;
+		case 2: devc->tracking_mode = 1;
+			break;
+		case 3: devc->tracking_mode = 2;
+			break;
+		default:
+			sr_err("Illegal channel tracking mode %d!", tracking_mode);
+			devc->tracking_mode = 0;
+			break;
+		}
+
+		sr_spew("Channel tracking: %d", devc->tracking_mode);
+	}
+	devc->channel_status[0].output_enabled = devc->channel_status[1].output_enabled = stat&0x040?TRUE:FALSE;
+	sr_spew("Channel 1%s output: %s", devc->model->num_channels > 1?"+2":"", devc->channel_status[0].output_enabled?"ON":"OFF");
+	if (devc->model->num_channels > 2) {
+		devc->channel_status[2].output_enabled = stat&0x010?TRUE:FALSE;
+		devc->channel_status[2].output_voltage_last = stat&0x020?3.3:5;
+		sr_spew("Channel 3 output: %s, U=%02f V, overload=%d",
+			devc->channel_status[2].output_enabled?"ON":"OFF",
+			devc->channel_status[2].output_voltage_last,
+			stat&0x080?1:0);
+	}
+	sr_spew("Fan=%d, beep=%d, CC output compensated=%d", stat&0x0100?1:0, stat&0x0200?1:0, stat&0x0400?1:0);
+
+	return SR_OK;
+}
+
+/** Send STATUS commend and process status string. */
+SR_PRIV int lps_query_status(struct sr_dev_inst *sdi)
+{
+	char buf[LINELEN_MAX];
+	int stat, ret;
+	struct dev_context *devc;
+
+	devc = (struct dev_context *)sdi->priv;
+
+	devc->req_sent_at = g_get_real_time();
+
+	if ((ret = lps_cmd_reply(buf, sdi->conn, "STATUS")) < 0) {
+		sr_err("%s: Failed to read status: %s.", __func__,
+			sr_strerror(ret));
+		return SR_ERR;
+	}
+
+	if (sr_atoi(buf, &stat) != SR_OK)
+		return SR_ERR;
+
+	return lps_process_status(sdi, stat);
+}
+
+static gint64 calc_timeout_ms(gint64 start_us)
+{
+	gint64 result = REQ_TIMEOUT_MS - ((g_get_real_time() - start_us) / 1000);
+
+	if (result < 0)
+		return 0;
+
+	return result;
+}
+
+/** Read message into buf until "OK" received.
+ *  @retval SR_OK Msg received; buf and buflen contain result, if any except OK.
+ *  @retval SR_ERR Error, including timeout.
+*/
+SR_PRIV int lps_read_reply(struct sr_serial_dev_inst *serial, char **buf, int *buflen)
+{
+	int retries;
+	char buf2[LINELEN_MAX];
+	char *buf2ptr;
+	int buf2len;
+	gint64 timeout_start;
+
+	*buf[0] = '\0';
+
+	/* Read one line. It is either a data message or "OK". */
+	timeout_start = g_get_real_time();
+	buf2len = *buflen;
+	/* Up to 5 tries because serial_readline() will consume only one CR or LF per
+	 * call, but device sends up to 4 in a row. */
+	for (retries = 0; retries < 5; retries++) {
+		*buflen = buf2len;
+		if (serial_readline(serial, buf, buflen, calc_timeout_ms(timeout_start)) != SR_OK)
+			return SR_ERR;
+		if (!strcmp(*buf, "OK")) { /* We got an OK! */
+			*buf[0] = '\0';
+			*buflen = 0;
+			return SR_OK;
+		}
+		if (*buflen > 0) /* We got a msg! */
+			break;
+	}
+
+	/* A data msg is in buf (possibly ERROR), need to consume "OK". */
+	buf2[0] = '\0';
+	buf2ptr = buf2;
+	for (retries = 0; retries < 5; retries++) {
+		buf2len = sizeof(buf2);
+		if (serial_readline(serial, &buf2ptr, &buf2len, calc_timeout_ms(timeout_start)) != SR_OK)
+			return SR_ERR;
+
+		if (!strcmp(buf2ptr, "OK")) { /* We got an OK! */
+			if (!strcmp(*buf, "ERROR")) { /* OK came after msg ERROR! */
+				sr_spew("ERROR found!");
+				*buf[0] = '\0';
+				*buflen = 0;
+				return SR_ERR;
+			}
+			return SR_OK;
+		}
+	}
+
+	return SR_ERR; /* Timeout! */
+}
+
+/** Scan for LPS-300 series device.
+ */
+static GSList *do_scan(lps_modelid modelid, struct sr_dev_driver *drv, GSList *options)
+{
+	struct sr_dev_inst *sdi;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_serial_dev_inst *serial;
+	struct sr_channel *ch;
+	struct sr_channel_group *cg;
+	GSList *devices;
+	const char *conn, *serialcomm;
+	int cnt, ret;
+	gchar buf[LINELEN_MAX];
+	gchar channel[10];
+	char *verstr;
+
+	sdi = NULL;
+	devc = NULL;
+	conn = serialcomm = NULL;
+	devices = NULL;
+
+	drvc = drv->context;
+	drvc->instances = NULL;
+
+	sr_spew("scan() called!");
+
+	/* Process and check options. */
+	if (sr_serial_extract_options(options, &conn, &serialcomm) != SR_OK)
+		return NULL;
+	if (!serialcomm)
+		serialcomm = SERIALCOMM;
+
+	/* Init serial port. */
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
+
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+		goto exit_err;
+
+	/* Query and verify model string. */
+	serial_flush(serial);
+	if (lps_cmd_reply(buf, serial, "MODEL") != SR_OK)
+		return NULL;
+
+	/* Check model string. */
+	if (strncmp(buf, "LPS-", 4)) {
+		sr_spew("Unknown model code \"%s\"!", buf);
+		return NULL;
+	}
+
+	/* Bug in device FW 1.17, model number is empty, so this can't work with this FW! */
+	if (modelid == LPS_UNKNOWN) {
+		g_strstrip(buf);
+		for (cnt = LPS_301; cnt <= LPS_305; cnt++) {
+			if (!strcmp(buf, models[cnt].modelstr)) {
+				modelid = cnt;
+				break;
+			}
+		}
+		if (modelid == LPS_UNKNOWN) {
+			sr_err("Unable to detect model from model string '%s'!", buf);
+			return NULL;
+		}
+	}
+
+	/* Query version */
+	verstr = NULL;
+	if ((ret = lps_cmd_reply(buf, serial, "VERSION")) == SR_OK) {
+		if (strncmp(buf, "Ver-", 4)) {
+			sr_spew("Version string %s not recognized.", buf);
+			goto exit_err;
+		}
+
+		g_strstrip(buf);
+		verstr = buf + 4;
+	}
+	else  /* Bug in device FW 1.17: Querying version string fails while output is active.
+		Therefore just print an error message, but do not exit with error. */
+		sr_err("Failed to query for hardware version: %s.",
+			sr_strerror(ret));
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(VENDOR_MOTECH);
+	sdi->model = g_strdup(models[modelid].modelstr);
+	sdi->version = g_strdup(verstr);
+	sdi->driver = drv;
+	sdi->inst_type = SR_INST_SERIAL;
+	sdi->conn = serial;
+
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->model = &models[modelid];
+	devc->limit_samples = 0;
+	devc->limit_msec = 0;
+	devc->num_samples = 0;
+	devc->elapsed_msec = g_timer_new();
+
+	sdi->priv = devc;
+
+	/* Setup channels and channel groups. */
+	for (cnt = 0; cnt < models[modelid].num_channels; cnt++) {
+		snprintf(channel, sizeof(channel), "CH%d", cnt + 1);
+		ch = sr_channel_new(sdi, cnt, SR_CHANNEL_ANALOG, TRUE, channel);
+
+		devc->channel_status[cnt].info = g_slist_append(NULL, ch);
+
+		cg = g_malloc(sizeof(struct sr_channel_group));
+		snprintf(channel, sizeof(channel), "CG%d", cnt+1);
+		cg->name = g_strdup(channel);
+		cg->priv = NULL;
+		cg->channels = g_slist_append(NULL, ch);
+
+		sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+	}
+
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+	devices = g_slist_append(devices, sdi);
+
+	/* Query status */
+	if (lps_query_status(sdi) != SR_OK)
+		goto exit_err;
+
+	serial_close(serial);
+	if (!devices)
+		sr_serial_dev_inst_free(serial);
+
+	return devices;
+
+exit_err:
+	sr_info("%s: Error!", __func__);
+
+	if (serial) {
+		serial_close(serial);
+		sr_serial_dev_inst_free(serial);
+	}
+	g_free(devc);
+	if (sdi)
+		sr_dev_inst_free(sdi);
+
+	return NULL;
+}
+
+/** Scan for LPS-301 device. */
+static GSList *scan_lps301(struct sr_dev_driver *di, GSList *options)
+{
+	return do_scan(LPS_301, di, options);
+}
+
+static GSList *dev_list_lps301(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static void dev_clear_private(struct dev_context *devc)
+{
+	int ch_idx;
+
+	/* Free channel_status.info (list only, data owned by sdi). */
+	for (ch_idx = 0; ch_idx < devc->model->num_channels; ch_idx++)
+		g_slist_free(devc->channel_status[ch_idx].info);
+
+	g_timer_destroy(devc->elapsed_msec);
+}
+
+static int dev_clear_lps301(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, (std_dev_clear_callback)dev_clear_private);
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return dev_clear_lps301(di);
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	struct sr_channel *ch;
+	int ch_idx;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	if (!cg) {
+		/* No channel group: global options. */
+		switch (key) {
+		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_CHANNEL_CONFIG:
+			*data = g_variant_new_string(channel_modes[devc->tracking_mode]);
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	} else {
+		/* We only ever have one channel per channel group in this driver. */
+		ch = cg->channels->data;
+		ch_idx = ch->index;
+		switch (key) {
+		case SR_CONF_VOLTAGE:
+			*data = g_variant_new_double(devc->channel_status[ch_idx].output_voltage_last);
+			break;
+		case SR_CONF_VOLTAGE_TARGET:
+			*data = g_variant_new_double(devc->channel_status[ch_idx].output_voltage_max);
+			break;
+		case SR_CONF_CURRENT:
+			*data = g_variant_new_double(devc->channel_status[ch_idx].output_current_last);
+			break;
+		case SR_CONF_CURRENT_LIMIT:
+			*data = g_variant_new_double(devc->channel_status[ch_idx].output_current_max);
+			break;
+		case SR_CONF_ENABLED:
+			*data = g_variant_new_boolean(devc->channel_status[ch_idx].output_enabled);
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	}
+
+	return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	struct sr_channel *ch;
+	gdouble dval;
+	int ch_idx;
+	const char *sval;
+	gboolean bval;
+	int idx;
+	gboolean found;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+
+	/* Cannot change settings while acquisition active, would cause a mess with commands.
+	 * Changing this would be possible, but tricky. */
+	if (devc->acq_running)
+		return SR_ERR_NA;
+
+	if (!cg) {
+		/* No channel group: global options. */
+		switch (key) {
+		case SR_CONF_LIMIT_MSEC:
+			devc->limit_msec = g_variant_get_uint64(data);
+			break;
+		case SR_CONF_LIMIT_SAMPLES:
+			devc->limit_samples = g_variant_get_uint64(data);
+			break;
+		case SR_CONF_CHANNEL_CONFIG:
+			sval = g_variant_get_string(data, NULL);
+			found = FALSE;
+			for (idx = 0; idx < (int)ARRAY_SIZE(channel_modes); idx++) {
+				if (!strcmp(sval, channel_modes[idx])) {
+					found = TRUE;
+					if (devc->tracking_mode == idx)
+						break;	/* Nothing to do! */
+					devc->tracking_mode = idx;
+					if (devc->model->modelid >= LPS_304) /* No use to set anything in the smaller models. */
+						return lps_cmd_ok(sdi->conn, "TRACK%1d", devc->tracking_mode);
+				}
+				if (devc->model->modelid <= LPS_303) /* Only first setting possible for smaller models. */
+					break;
+			}
+			if (!found)
+				return SR_ERR_ARG;
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	} else {
+		/* Channel group specified: per-channel options. */
+		/* We only ever have one channel per channel group in this driver. */
+		ch = cg->channels->data;
+		ch_idx = ch->index;
+
+		switch (key) {
+		case SR_CONF_VOLTAGE_TARGET:
+			dval = g_variant_get_double(data);
+			if (dval < 0 || dval > devc->model->channels[ch_idx].voltage[1])
+				return SR_ERR_ARG;
+			if (ch_idx == 2) {
+				if (devc->model->modelid < LPS_304)
+					return SR_ERR_ARG;
+
+				if (fabs(dval - 5.000) <= 0.001)
+					dval = 5.0;
+				else if ((devc->model->modelid >= LPS_305) && (fabs(dval - 3.300) <= 0.001))
+					dval = 3.3;
+				else return SR_ERR_ARG;
+			}
+
+			devc->channel_status[ch_idx].output_voltage_max = dval;
+			if (ch_idx == 2)
+				return lps_cmd_ok(sdi->conn, "VDD%1.0f", trunc(dval));
+			else
+				return lps_cmd_ok(sdi->conn, "VSET%d %05.3f", ch_idx+1, dval);
+			break;
+		case SR_CONF_CURRENT_LIMIT:
+			dval = g_variant_get_double(data);
+			if (dval < 0 || dval > devc->model->channels[ch_idx].current[1])
+				return SR_ERR_ARG;
+			if (ch_idx == 2) /* No current setting for CH3. */
+				return SR_ERR_NA;
+			devc->channel_status[ch_idx].output_current_max = dval;
+			return lps_cmd_ok(sdi->conn, "ISET%d %05.4f", ch_idx+1, dval);
+			break;
+		case SR_CONF_ENABLED:
+			bval = g_variant_get_boolean(data);
+			if (bval == devc->channel_status[ch_idx].output_enabled) /* Nothing to do. */
+				break;
+			devc->channel_status[ch_idx].output_enabled = bval;
+			if (ch_idx != 2) { /* Channels 1,2 can be set only together. */
+				devc->channel_status[ch_idx^1].output_enabled = bval;
+				return lps_cmd_ok(sdi->conn, "OUT%1d", (int)bval);
+			} else { /* Channel 3: No command to disable output, set voltage to 0 instead. */
+				if (bval)
+					return lps_cmd_ok(sdi->conn, "VDD%1.0f", devc->channel_status[ch_idx].output_voltage_max);
+				else
+					return lps_cmd_ok(sdi->conn, "VDD0");
+			}
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	struct sr_channel *ch;
+	int ch_idx, i;
+	GVariant *gvar;
+	GVariantBuilder gvb;
+
+	/* Driver options, no device instance necessary. */
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	case SR_CONF_DEVICE_OPTIONS:
+		if (sdi != NULL)
+			break;
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		return SR_OK;
+	default:
+		if (!sdi)
+			return SR_ERR_ARG;
+		devc = sdi->priv;
+		break;
+	}
+
+	/* Device options, independent from channel groups. */
+	if (!cg) {
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+			return SR_OK;
+		case SR_CONF_CHANNEL_CONFIG:
+			if (devc->model->modelid <= LPS_303) {
+				/* The 1-channel models. */
+				*data = g_variant_new_strv(channel_modes, 1);
+			} else {
+				/* The other models support all modes. */
+				*data = g_variant_new_strv(channel_modes, ARRAY_SIZE(channel_modes));
+			}
+			return SR_OK;
+		default:
+			return SR_ERR_NA;
+		}
+	}
+
+	/* Device options, depending on channel groups. */
+	ch = cg->channels->data;
+	ch_idx = ch->index;
+	switch (key) {
+	case SR_CONF_DEVICE_OPTIONS:
+		if ((ch_idx == 0) || (ch_idx == 1)) /* CH1, CH2 */
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				  devopts_ch12, ARRAY_SIZE(devopts_ch12), sizeof(uint32_t));
+		else /* Must be CH3 */
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				  devopts_ch3, ARRAY_SIZE(devopts_ch3), sizeof(uint32_t));
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+		/* Min, max, step. */
+		for (i = 0; i < 3; i++) {
+			gvar = g_variant_new_double(devc->model->channels[ch_idx].voltage[i]);
+			g_variant_builder_add_value(&gvb, gvar);
+		}
+		*data = g_variant_builder_end(&gvb);
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+		/* Min, max, step. */
+		for (i = 0; i < 3; i++) {
+			gvar = g_variant_new_double(devc->model->channels[ch_idx].current[i]);
+			g_variant_builder_add_value(&gvb, gvar);
+		}
+		*data = g_variant_builder_end(&gvb);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int 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;
+
+	devc = sdi->priv;
+
+	devc->acq_running = TRUE;
+
+	serial = sdi->conn;
+	serial_source_add(sdi->session, serial, G_IO_IN, 50,
+			motech_lps_30x_receive_data, (void *)sdi);
+	std_session_send_df_header(cb_data, LOG_PREFIX);
+
+	/* Start timer, if required. */
+	if (devc->limit_msec)
+		g_timer_start(devc->elapsed_msec);
+
+	devc->acq_req = AQ_NONE;
+	/* Do not start polling device here, the read function will do it in 50 ms. */
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc;
+
+	/* Stop timer, if required. */
+	if (sdi && (devc = sdi->priv) && devc->limit_msec)
+		g_timer_stop(devc->elapsed_msec);
+
+	return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+			sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver motech_lps_301_driver_info = {
+	.name = "motech-lps-301",
+	.longname = "Motech LPS-301",
+	.api_version = 1,
+	.init = init_lps301,
+	.cleanup = cleanup,
+	.scan = scan_lps301,
+	.dev_list = dev_list_lps301,
+	.dev_clear = dev_clear_lps301,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = std_serial_dev_open,
+	.dev_close = std_serial_dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/motech-lps-30x/protocol.c b/src/hardware/motech-lps-30x/protocol.c
new file mode 100644
index 0000000..4b9da80
--- /dev/null
+++ b/src/hardware/motech-lps-30x/protocol.c
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok at heidbrink.biz>
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com> (code from atten-pps3xxx)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ *  <em>Motech LPS-30x series</em> power supply driver
+ *  @internal
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <string.h>
+#include "protocol.h"
+
+/** Send data packets for current measurements. */
+static void send_data(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+	int i;
+	float data[MAX_CHANNELS];
+
+	devc = sdi->priv;
+	packet.type = SR_DF_ANALOG_OLD;
+	packet.payload = &analog;
+	analog.channels = sdi->channels;
+	analog.num_samples = 1;
+
+	analog.mq = SR_MQ_VOLTAGE;
+	analog.unit = SR_UNIT_VOLT;
+	analog.mqflags = SR_MQFLAG_DC;
+	analog.data = data;
+	for (i = 0; i < devc->model->num_channels; i++)
+		analog.data[i] = devc->channel_status[i].output_voltage_last; /* Value always 3.3 or 5 for channel 3, if present! */
+	sr_session_send(sdi, &packet);
+
+	analog.mq = SR_MQ_CURRENT;
+	analog.unit = SR_UNIT_AMPERE;
+	analog.mqflags = 0;
+	analog.data = data;
+	for (i = 0; i < devc->model->num_channels; i++)
+		analog.data[i] = devc->channel_status[i].output_current_last; /* Value always 0 for channel 3, if present! */
+	sr_session_send(sdi, &packet);
+
+	devc->num_samples++;
+}
+
+/** Process a complete line (without CR/LF) in buf. */
+static void process_line(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	double dbl;
+	int auxint;
+
+	devc = sdi->priv;
+
+	switch (devc->acq_req_pending) {
+	case 0: /* Should not happen... */
+		break;
+	case 1: /* Waiting for data reply to request */
+		/* Convert numbers */
+		switch (devc->acq_req) {
+		case AQ_U1:
+		case AQ_U2:
+		case AQ_I1:
+		case AQ_I2:
+			if (sr_atod(devc->buf, &dbl) != SR_OK) {
+				sr_err("Failed to convert '%s' to double, errno=%d %s",
+					devc->buf, errno, g_strerror(errno));
+				dbl = 0.0;
+			}
+			break;
+		case AQ_STATUS:
+			if (sr_atoi(devc->buf, &auxint) != SR_OK) {
+				sr_err("Failed to convert '%s' to int, errno=%d %s",
+					devc->buf, errno, g_strerror(errno));
+				auxint = 0;
+			}
+			break;
+		default:
+			break;
+		}
+
+		switch (devc->acq_req) {
+		case AQ_U1:
+			devc->channel_status[0].output_voltage_last = dbl;
+			break;
+		case AQ_I1:
+			devc->channel_status[0].output_current_last = dbl;
+			break;
+		case AQ_U2:
+			devc->channel_status[1].output_voltage_last = dbl;
+			break;
+		case AQ_I2:
+			devc->channel_status[1].output_current_last = dbl;
+			break;
+		case AQ_STATUS: /* Process status and generate data. */
+			if (lps_process_status(sdi, auxint) == SR_OK) {
+				send_data(sdi);
+			}
+			break;
+		default:
+			break;
+		}
+
+		devc->acq_req_pending = 2;
+		break;
+	case 2: /* Waiting for OK after request */
+		if (strcmp(devc->buf, "OK")) {
+			sr_err("Unexpected reply while waiting for OK: '%s'", devc->buf);
+		}
+		devc->acq_req_pending = 0;
+		break;
+	}
+
+	devc->buf[0] = '\0';
+	devc->buflen = 0;
+}
+
+SR_PRIV int motech_lps_30x_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;
+	gdouble elapsed_s;
+
+	(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 (LINELEN_MAX - devc->buflen - 2 > 0) {
+			len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
+			if (len < 1)
+				break;
+
+			/* Eliminate whitespace at beginning of line. */
+			if (g_ascii_isspace(devc->buf[0])) {
+				devc->buf[0] = '\0';
+				devc->buflen = 0;
+				continue;
+			}
+
+			devc->buflen += len;
+			devc->buf[devc->buflen] = '\0';
+
+			/* If line complete, process msg. */
+			if ((devc->buflen > 0) && ((devc->buf[devc->buflen-1] == '\r') || devc->buf[devc->buflen-1] == '\n')) {
+				devc->buflen--;
+				devc->buf[devc->buflen] = '\0';
+
+				sr_spew("Line complete: \"%s\"", devc->buf);
+				process_line(sdi);
+			}
+		}
+	}
+
+	/* If number of samples or time limit reached, stop acquisition. */
+	if (devc->limit_samples && (devc->num_samples >= devc->limit_samples))
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+
+	if (devc->limit_msec) {
+		elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
+		if ((elapsed_s * 1000) >= devc->limit_msec)
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+	}
+
+	/* Only request the next packet if required. */
+	if (!((sdi->status == SR_ST_ACTIVE) && (devc->acq_running)))
+		return TRUE;
+
+	if (devc->acq_req_pending) {
+		int64_t elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+		if (elapsed_us > (REQ_TIMEOUT_MS * 1000)) {
+			sr_spew("Request timeout: req=%d t=%" PRIi64 "us",
+				(int)devc->acq_req, elapsed_us);
+			devc->acq_req_pending = 0;
+		}
+	}
+
+	if (devc->acq_req_pending == 0) {
+		switch (devc->acq_req) {
+		case AQ_NONE: /* Fall through */
+		case AQ_STATUS:
+			devc->acq_req = AQ_U1;
+			lps_send_req(serial, "VOUT1");
+			break;
+		case AQ_U1:
+			devc->acq_req = AQ_I1;
+			lps_send_req(serial, "IOUT1");
+			break;
+		case AQ_I1:
+			if (devc->model->num_channels == 1) {
+				devc->acq_req = AQ_STATUS;
+				lps_send_req(serial, "STATUS");
+			} else {
+				devc->acq_req = AQ_U2;
+				lps_send_req(serial, "VOUT2");
+			}
+			break;
+		case AQ_U2:
+			devc->acq_req = AQ_I2;
+			lps_send_req(serial, "IOUT2");
+			break;
+		case AQ_I2:
+			devc->acq_req = AQ_STATUS;
+			lps_send_req(serial, "STATUS");
+			break;
+		default:
+			sr_err("Illegal devc->acq_req=%d", devc->acq_req);
+			return SR_ERR;
+		}
+		devc->req_sent_at = g_get_real_time();
+		devc->acq_req_pending = 1;
+	}
+
+	return TRUE;
+}
diff --git a/src/hardware/motech-lps-30x/protocol.h b/src/hardware/motech-lps-30x/protocol.h
new file mode 100644
index 0000000..728c600
--- /dev/null
+++ b/src/hardware/motech-lps-30x/protocol.h
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Matthias Heidbrink <m-sigrok at heidbrink.biz>
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com> (code from atten-pps3xxx)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ *  <em>Motech LPS-30x series</em> power supply driver
+ *  @internal
+ */
+
+#ifndef LIBSIGROK_HARDWARE_MOTECH_LPS_30X_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_MOTECH_LPS_30X_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+SR_PRIV int lps_process_status(struct sr_dev_inst *sdi, int stat);
+SR_PRIV int lps_send_req(struct sr_serial_dev_inst *serial, const char *fmt, ...);
+
+#define LOG_PREFIX "motech-lps-30x"
+
+#define LINELEN_MAX 50	/**< Max. line length for requests */
+
+#define REQ_TIMEOUT_MS 250 /**< Timeout [ms] for single request. */
+
+#define MAX_CHANNELS 3
+
+typedef enum {
+	LPS_UNKNOWN = 0,/**< Unknown model (used during detection process) */
+	LPS_301,	/**< Motech/Amrel LPS-301, 1 output */
+	LPS_302,	/**< Motech/Amrel LPS-302, 1 output */
+	LPS_303,	/**< Motech/Amrel LPS-303, 1 output */
+	LPS_304,	/**< Motech/Amrel LPS-304, 3 outputs */
+	LPS_305,	/**< Motech/Amrel LPS-305, 3 outputs */
+} lps_modelid;
+
+/** Channel specification */
+struct channel_spec {
+	/* Min, max, step. */
+	gdouble voltage[3];
+	gdouble current[3];
+};
+
+/** Model properties specification */
+struct lps_modelspec {
+	lps_modelid modelid;
+	const char *modelstr;
+	uint8_t num_channels;
+	struct channel_spec channels[3];
+};
+
+/** Used to implement a little state machine to query all required values in a row. */
+typedef enum {
+	AQ_NONE,
+	AQ_U1,
+	AQ_I1,
+	AQ_I2,
+	AQ_U2,
+	AQ_STATUS,
+} acquisition_req;
+
+/** Status of a single channel. */
+struct channel_status {
+	/* Channel information (struct channel_info*). data (struct) owned by sdi, just a reference to address a single channel. */
+	GSList *info;
+	/* Received from device. */
+	gdouble output_voltage_last;
+	gdouble output_current_last;
+	gboolean output_enabled;	/**< Also used when set. */
+	gboolean cc_mode;		/**< Constant current mode. If false, constant voltage mode. */
+	/* Set by frontend. */
+	gdouble output_voltage_max;
+	gdouble output_current_max;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/* Model-specific information */
+	const struct lps_modelspec *model;
+
+	/* Acquisition status */
+	gboolean acq_running;		/**< Acquisition is running. */
+	uint64_t limit_samples;		/**< Target number of samples */
+	uint64_t limit_msec;		/**< Target sampling time */
+	acquisition_req acq_req;	/**< Current request. */
+	uint8_t	acq_req_pending;	/**< Request pending. 0=none, 1=reply, 2=OK */
+
+	/* Operational state */
+	struct channel_status channel_status[MAX_CHANNELS];
+	guint8 tracking_mode;		/**< 0=off, 1=Tracking from CH1, 2=Tracking from CH2. */
+
+	/* Temporary state across callbacks */
+	int64_t req_sent_at;    /**< Request sent. */
+	uint64_t num_samples;	/**< Current #samples for limit_samples */
+	GTimer *elapsed_msec;	/**< Used for sampling with limit_msec  */
+	gchar buf[LINELEN_MAX];	/**< Buffer for read callback */
+	int buflen;		/**< Data len in buf */
+};
+
+SR_PRIV int motech_lps_30x_receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/hardware/norma-dmm/api.c b/src/hardware/norma-dmm/api.c
similarity index 61%
rename from hardware/norma-dmm/api.c
rename to src/hardware/norma-dmm/api.c
index 6860a78..9e0be39 100644
--- a/hardware/norma-dmm/api.c
+++ b/src/hardware/norma-dmm/api.c
@@ -17,18 +17,24 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/** @file
+ *  Norma DM9x0/Siemens B102x DMMs driver.
+ *  @internal
+ */
+
+#include <config.h>
 #include "protocol.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_MULTIMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
 };
 
 #define BUF_MAX 50
@@ -36,31 +42,49 @@ static const int32_t hwcaps[] = {
 #define SERIALCOMM "4800/8n1/dtr=1/rts=0/flow=1"
 
 SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
-static struct sr_dev_driver *di = &norma_dmm_driver_info;
+SR_PRIV struct sr_dev_driver siemens_b102x_driver_info;
+
+static const char *get_brandstr(struct sr_dev_driver *drv)
+{
+	return (drv == &norma_dmm_driver_info) ? "Norma" : "Siemens";
+}
+
+static const char *get_typestr(int type, struct sr_dev_driver *drv)
+{
+	static const char *nameref[5][2] = {
+		{"DM910", "B1024"},
+		{"DM920", "B1025"},
+		{"DM930", "B1026"},
+		{"DM940", "B1027"},
+		{"DM950", "B1028"},
+	};
+
+	if ((type < 1) || (type > 5))
+		return "Unknown type!";
+
+	return nameref[type - 1][(drv == &siemens_b102x_driver_info)];
+}
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *drv, GSList *options)
 {
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_config *src;
-	struct sr_channel *ch;
 	struct sr_serial_dev_inst *serial;
 	GSList *l, *devices;
-	int len, cnt;
+	int len, cnt, auxtype;
 	const char *conn, *serialcomm;
 	char *buf;
-	char fmttype[10];
 	char req[10];
-	int auxtype;
 
 	devices = NULL;
-	drvc = di->priv;
+	drvc = drv->context;
 	drvc->instances = NULL;
 	conn = serialcomm = NULL;
 
@@ -80,58 +104,48 @@ static GSList *scan(GSList *options)
 	if (!serialcomm)
 		serialcomm = SERIALCOMM;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
 
 	serial_flush(serial);
 
-	if (!(buf = g_try_malloc(BUF_MAX))) {
-		sr_err("Serial buffer malloc failed.");
-		return NULL;
-	}
+	buf = g_malloc(BUF_MAX);
 
 	snprintf(req, sizeof(req), "%s\r\n",
 		 nmadmm_requests[NMADMM_REQ_IDN].req_str);
+	g_usleep(150 * 1000); /* Wait a little to allow serial port to settle. */
 	for (cnt = 0; cnt < 7; cnt++) {
-		if (serial_write(serial, req, strlen(req)) == -1) {
-			sr_err("Unable to send identification request: %d %s.",
-			       errno, strerror(errno));
+		if (serial_write_blocking(serial, req, strlen(req),
+				serial_timeout(serial, strlen(req))) < 0) {
+			sr_err("Unable to send identification request.");
 			return NULL;
 		}
 		len = BUF_MAX;
-		serial_readline(serial, &buf, &len, 1500);
+		serial_readline(serial, &buf, &len, NMADMM_TIMEOUT_MS);
 		if (!len)
 			continue;
 		buf[BUF_MAX - 1] = '\0';
 
-		/* Match ID string, e.g. "1834 065 V1.06,IF V1.02" (DM950) */
+		/* Match ID string, e.g. "1834 065 V1.06,IF V1.02" (DM950). */
 		if (g_regex_match_simple("^1834 [^,]*,IF V*", (char *)buf, 0, 0)) {
 			auxtype = xgittoint(buf[7]);
-				// TODO: Will this work with non-DM950?
-			snprintf(fmttype, sizeof(fmttype), "DM9%d0", auxtype);
-			sr_spew("Norma %s DMM %s detected!", fmttype, &buf[9]);
-
-			if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
-						"Norma", fmttype, buf + 9)))
-				return NULL;
-			if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-				sr_err("Device context malloc failed.");
-				return NULL;
-			}
+			sr_spew("%s %s DMM %s detected!", get_brandstr(drv), get_typestr(auxtype, drv), buf + 9);
+
+			sdi = g_malloc0(sizeof(struct sr_dev_inst));
+			sdi->status = SR_ST_INACTIVE;
+			sdi->vendor = g_strdup(get_brandstr(drv));
+			sdi->model = g_strdup(get_typestr(auxtype, drv));
+			sdi->version = g_strdup(buf + 9);
+			devc = g_malloc0(sizeof(struct dev_context));
 			devc->type = auxtype;
 			devc->version = g_strdup(&buf[9]);
 			devc->elapsed_msec = g_timer_new();
-
 			sdi->conn = serial;
 			sdi->priv = devc;
-			sdi->driver = di;
-			if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE,
-				"P1")))
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
+			sdi->driver = drv;
+			sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 			drvc->instances = g_slist_append(drvc->instances, sdi);
 			devices = g_slist_append(devices, sdi);
 			break;
@@ -158,9 +172,9 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
@@ -179,12 +193,12 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -201,19 +215,10 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 
 	switch (key) {
 	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;
@@ -222,7 +227,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -230,12 +235,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -244,8 +249,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int dev_acquisition_start(const struct sr_dev_inst *sdi,
-				    void *cb_data)
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
 	struct dev_context *devc;
 	struct sr_serial_dev_inst *serial;
@@ -267,8 +271,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 
 	/* Poll every 100ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 100, norma_dmm_receive_data,
-		      (void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 100,
+			norma_dmm_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -287,7 +291,26 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 
 SR_PRIV struct sr_dev_driver norma_dmm_driver_info = {
 	.name = "norma-dmm",
-	.longname = "Norma DM9x0 / Siemens B102x DMMs",
+	.longname = "Norma DM9x0 DMMs",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = NULL,
+	.config_get = NULL,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = std_serial_dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
+
+SR_PRIV struct sr_dev_driver siemens_b102x_driver_info = {
+	.name = "siemens-b102x",
+	.longname = "Siemens B102x DMMs",
 	.api_version = 1,
 	.init = init,
 	.cleanup = cleanup,
@@ -301,5 +324,5 @@ SR_PRIV struct sr_dev_driver norma_dmm_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/norma-dmm/protocol.c b/src/hardware/norma-dmm/protocol.c
similarity index 89%
rename from hardware/norma-dmm/protocol.c
rename to src/hardware/norma-dmm/protocol.c
index 8e1466a..4dc717c 100644
--- a/hardware/norma-dmm/protocol.c
+++ b/src/hardware/norma-dmm/protocol.c
@@ -17,12 +17,20 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/** @file
+ *  Norma DM9x0/Siemens B102x DMMs driver.
+ *  @internal
+ */
+
+#include <config.h>
 #include "protocol.h"
 
+#define LINE_LENGTH 20
+
 SR_PRIV const struct nmadmm_req nmadmm_requests[] = {
 	{ NMADMM_REQ_IDN, "IDN?" },
 	{ NMADMM_REQ_IDN, "STATUS?" },
-	{ 0, NULL },
+	ALL_ZERO
 };
 
 static int nma_send_req(const struct sr_dev_inst *sdi, int req, char *params)
@@ -43,13 +51,15 @@ static int nma_send_req(const struct sr_dev_inst *sdi, int req, char *params)
 	devc->last_req = req;
 	devc->last_req_pending = TRUE;
 
-	if (serial_write(serial, buf, len) == -1) {
-		sr_err("Unable to send request: %d %s.",
-			errno, strerror(errno));
+	if (serial_write_blocking(serial, buf, len,
+			serial_timeout(serial, len)) < 0) {
+		sr_err("Unable to send request.");
 		devc->last_req_pending = FALSE;
 		return SR_ERR;
 	}
 
+	devc->req_sent_at = g_get_monotonic_time();
+
 	return SR_OK;
 }
 
@@ -81,24 +91,24 @@ static void nma_process_line(const struct sr_dev_inst *sdi)
 	int mmode, devstat;	/* Measuring mode, device status */
 	float value;	/* Measured value */
 	float scale;	/* Scaling factor depending on range and function */
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct sr_datafeed_packet packet;
 
 	devc = sdi->priv;
 
-	devc->buf[20] = '\0';
+	devc->buf[LINE_LENGTH] = '\0';
 
 	sr_spew("Received line '%s'.", devc->buf);
 
 	/* Check line. */
-	if (strlen((const char *)devc->buf) != 20) {
+	if (strlen((const char *)devc->buf) != LINE_LENGTH) {
 		sr_err("line: Invalid status '%s', must be 20 hex digits.",
 		       devc->buf);
 		devc->buflen = 0;
 		return;
 	}
 
-	for (pos = 0; pos < 20; pos++) {
+	for (pos = 0; pos < LINE_LENGTH; pos++) {
 		if (!isxdigit(devc->buf[pos])) {
 			sr_err("line: Expected hex digit in '%s' at pos %d!",
 				devc->buf, pos);
@@ -110,7 +120,7 @@ static void nma_process_line(const struct sr_dev_inst *sdi)
 	/* Start decoding. */
 	value = 0.0;
 	scale = 1.0;
-	memset(&analog, 0, sizeof(analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 
 	/*
 	 * The numbers are hex digits, starting from 0.
@@ -311,7 +321,7 @@ static void nma_process_line(const struct sr_dev_inst *sdi)
 		sr_warn("Low battery, measurement quality degraded!");
 	}
 	/* 0x08: SCALED */
-	/* 0x04: RATE (=lower resolution, allows higher rata rate up to 10/s. */
+	/* 0x04: RATE (=lower resolution, allows higher data rate up to 10/s. */
 	/* 0x02: Current clamp */
 	if (flags & 0x01) { /* dB */
 		/*
@@ -353,8 +363,8 @@ static void nma_process_line(const struct sr_dev_inst *sdi)
 	analog.num_samples = 1;
 	analog.data = &value;
 
-	memset(&packet, 0, sizeof(packet));
-	packet.type = SR_DF_ANALOG;
+	memset(&packet, 0, sizeof(struct sr_datafeed_packet));
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 
@@ -384,7 +394,7 @@ SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data)
 	if (revents == G_IO_IN) {
 		/* Serial data arrived. */
 		while (NMADMM_BUFSIZE - devc->buflen - 1 > 0) {
-			len = serial_read(serial, devc->buf + devc->buflen, 1);
+			len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
 			if (len < 1)
 				break;
 			devc->buflen += len;
@@ -417,9 +427,18 @@ SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data)
 	}
 
 	/* Request next package. */
-	if (!terminating && !devc->last_req_pending) {
-		if (nma_send_req(sdi, NMADMM_REQ_STATUS, NULL) != SR_OK)
-			return FALSE;
+	if (!terminating) {
+		if (devc->last_req_pending) {
+			gint64 elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+			if (elapsed_us > NMADMM_TIMEOUT_MS * 1000) {/* Timeout! */
+				sr_spew("Request timeout!");
+				devc->last_req_pending = FALSE;
+			}
+		}
+		if (!devc->last_req_pending) {
+			if (nma_send_req(sdi, NMADMM_REQ_STATUS, NULL) != SR_OK)
+				return FALSE;
+		}
 	}
 
 	return TRUE;
diff --git a/hardware/norma-dmm/protocol.h b/src/hardware/norma-dmm/protocol.h
similarity index 92%
rename from hardware/norma-dmm/protocol.h
rename to src/hardware/norma-dmm/protocol.h
index 2ed0e25..7c52b47 100644
--- a/hardware/norma-dmm/protocol.h
+++ b/src/hardware/norma-dmm/protocol.h
@@ -23,16 +23,22 @@
 #include <stdint.h>
 #include <string.h>
 #include <ctype.h>
-#include <errno.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
+/** @file
+ *  Norma DM9x0/Siemens B102x DMMs driver.
+ *  @internal
+ */
+
 #define LOG_PREFIX "norma-dmm"
 
 #define NMADMM_BUFSIZE  256
 
+#define NMADMM_TIMEOUT_MS 2000 /**< Request timeout. */
+
 /** Norma DMM request types (used ones only, the DMMs support about 50). */
 enum {
 	NMADMM_REQ_IDN = 0,	/**< Request identity */
@@ -63,6 +69,7 @@ struct dev_context {
 
 	/* Operational state */
 	int last_req;			/**< Last request. */
+	int64_t req_sent_at;		/**< Request sent. */
 	gboolean last_req_pending;	/**< Last request not answered yet. */
 	int lowbatt;			/**< Low battery. 1=low, 2=critical. */
 
diff --git a/hardware/openbench-logic-sniffer/api.c b/src/hardware/openbench-logic-sniffer/api.c
similarity index 76%
copy from hardware/openbench-logic-sniffer/api.c
copy to src/hardware/openbench-logic-sniffer/api.c
index b99d8e7..ddd33e1 100644
--- a/hardware/openbench-logic-sniffer/api.c
+++ b/src/hardware/openbench-logic-sniffer/api.c
@@ -17,26 +17,34 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
-#include <libserialport.h>
 
 #define SERIALCOMM "115200/8n1"
 
-static const int32_t hwopts[] = {
+static const uint32_t drvopts[] = {
+	SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
-	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_TRIGGER_TYPE,
-	SR_CONF_CAPTURE_RATIO,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_EXTERNAL_CLOCK,
-	SR_CONF_PATTERN_MODE,
-	SR_CONF_SWAP,
-	SR_CONF_RLE,
+static const uint32_t devopts[] = {
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_EXTERNAL_CLOCK | SR_CONF_SET,
+	SR_CONF_PATTERN_MODE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SWAP | SR_CONF_SET,
+	SR_CONF_RLE | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
 };
 
 #define STR_PATTERN_NONE     "None"
@@ -62,11 +70,10 @@ static const char *patterns[] = {
 };
 
 /* Channels are numbered 0-31 (on the PCB silkscreen). */
-SR_PRIV const char *ols_channel_names[NUM_CHANNELS + 1] = {
+SR_PRIV const char *ols_channel_names[] = {
 	"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
 	"13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23",
 	"24", "25", "26", "27", "28", "29", "30", "31",
-	NULL,
 };
 
 /* Default supported samplerates, can be overridden by device metadata. */
@@ -76,29 +83,28 @@ static const uint64_t samplerates[] = {
 	SR_HZ(1),
 };
 
+#define RESPONSE_DELAY_US (10 * 1000)
+
 SR_PRIV struct sr_dev_driver ols_driver_info;
-static struct sr_dev_driver *di = &ols_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct sr_config *src;
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
-	struct dev_context *devc;
-	struct sr_channel *ch;
 	struct sr_serial_dev_inst *serial;
-	GPollFD probefd;
 	GSList *l, *devices;
-	int ret, i;
+	int ret;
+	unsigned int i;
 	const char *conn, *serialcomm;
 	char buf[8];
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	devices = NULL;
 
@@ -117,11 +123,10 @@ static GSList *scan(GSList *options)
 	if (!conn)
 		return NULL;
 
-	if (serialcomm == NULL)
+	if (!serialcomm)
 		serialcomm = SERIALCOMM;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
 	/* The discovery procedure is like this: first send the Reset
 	 * command (0x00) 5 times, since the device could be anywhere
@@ -130,7 +135,7 @@ static GSList *scan(GSList *options)
 	 * have a match.
 	 */
 	sr_info("Probing %s.", conn);
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
 
 	ret = SR_OK;
@@ -147,50 +152,53 @@ static GSList *scan(GSList *options)
 	}
 	send_shortcommand(serial, CMD_ID);
 
-	/* Wait 10ms for a response. */
-	g_usleep(10000);
+	g_usleep(RESPONSE_DELAY_US);
 
-	sp_get_port_handle(serial->data, &probefd.fd);
-	probefd.events = G_IO_IN;
-	g_poll(&probefd, 1, 1);
-
-	if (probefd.revents != G_IO_IN)
+	if (sp_input_waiting(serial->data) == 0) {
+		sr_dbg("Didn't get any reply.");
 		return NULL;
-	if (serial_read_blocking(serial, buf, 4) != 4)
+	}
+
+	ret = serial_read_blocking(serial, buf, 4, serial_timeout(serial, 4));
+	if (ret != 4) {
+		sr_err("Invalid reply (expected 4 bytes, got %d).", ret);
 		return NULL;
-	if (strncmp(buf, "1SLO", 4) && strncmp(buf, "1ALS", 4))
+	}
+
+	if (strncmp(buf, "1SLO", 4) && strncmp(buf, "1ALS", 4)) {
+		sr_err("Invalid reply (expected '1SLO' or '1ALS', got "
+		       "'%c%c%c%c').", buf[0], buf[1], buf[2], buf[3]);
 		return NULL;
+	}
 
 	/* Definitely using the OLS protocol, check if it supports
 	 * the metadata command.
 	 */
 	send_shortcommand(serial, CMD_METADATA);
-	if (g_poll(&probefd, 1, 10) > 0) {
+
+	g_usleep(RESPONSE_DELAY_US);
+
+	if (sp_input_waiting(serial->data) != 0) {
 		/* Got metadata. */
 		sdi = get_metadata(serial);
-		sdi->index = 0;
-		devc = sdi->priv;
 	} else {
 		/* Not an OLS -- some other board that uses the sump protocol. */
 		sr_info("Device does not support metadata.");
-		sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
-				"Sump", "Logic Analyzer", "v1.0");
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup("Sump");
+		sdi->model = g_strdup("Logic Analyzer");
+		sdi->version = g_strdup("v1.0");
 		sdi->driver = di;
-		for (i = 0; i < 32; i++) {
-			if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-					ols_channel_names[i])))
-				return 0;
-			sdi->channels = g_slist_append(sdi->channels, ch);
-		}
-		devc = ols_dev_new();
-		sdi->priv = devc;
+		for (i = 0; i < ARRAY_SIZE(ols_channel_names); i++)
+			sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE,
+					ols_channel_names[i]);
+		sdi->priv = ols_dev_new();
 	}
 	/* Configure samplerate and divider. */
 	if (ols_set_samplerate(sdi, DEFAULT_SAMPLERATE) != SR_OK)
 		sr_dbg("Failed to set default samplerate (%"PRIu64").",
 				DEFAULT_SAMPLERATE);
-	/* Clear trigger masks, values and stages. */
-	ols_configure_channels(sdi);
 	sdi->inst_type = SR_INST_SERIAL;
 	sdi->conn = serial;
 
@@ -202,17 +210,17 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -223,7 +231,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 		return SR_ERR_ARG;
 
 	devc = sdi->priv;
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		*data = g_variant_new_uint64(devc->cur_samplerate);
 		break;
@@ -251,7 +259,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -267,7 +275,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
 	devc = sdi->priv;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		tmp_u64 = g_variant_get_uint64(data);
 		if (tmp_u64 < samplerates[0] || tmp_u64 > samplerates[1])
@@ -283,10 +291,9 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		break;
 	case SR_CONF_CAPTURE_RATIO:
 		devc->capture_ratio = g_variant_get_uint64(data);
-		if (devc->capture_ratio < 0 || devc->capture_ratio > 100) {
-			devc->capture_ratio = 0;
+		if (devc->capture_ratio < 0 || devc->capture_ratio > 100)
 			ret = SR_ERR;
-		} else
+		else
 			ret = SR_OK;
 		break;
 	case SR_CONF_EXTERNAL_CLOCK:
@@ -348,24 +355,28 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
 	GVariant *gvar, *grange[2];
 	GVariantBuilder gvb;
-	int num_channels, i;
+	int num_ols_changrp, i;
 
 	(void)cg;
 
 	switch (key) {
 	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLERATE:
 		g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
@@ -374,8 +385,10 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
 		*data = g_variant_builder_end(&gvb);
 		break;
-	case SR_CONF_TRIGGER_TYPE:
-		*data = g_variant_new_string(TRIGGER_TYPE);
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				trigger_matches, ARRAY_SIZE(trigger_matches),
+				sizeof(int32_t));
 		break;
 	case SR_CONF_PATTERN_MODE:
 		*data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns));
@@ -393,20 +406,17 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		 * Channel groups are turned off if no channels in that group are
 		 * enabled, making more room for samples for the enabled group.
 		*/
-		ols_configure_channels(sdi);
-		num_channels = 0;
+		ols_channel_mask(sdi);
+		num_ols_changrp = 0;
 		for (i = 0; i < 4; i++) {
 			if (devc->channel_mask & (0xff << (i * 8)))
-				num_channels++;
-		}
-		if (num_channels == 0) {
-			/* This can happen, but shouldn't cause too much drama.
-			 * However we can't continue because the code below would
-			 * divide by zero. */
-			break;
+				num_ols_changrp++;
 		}
 		grange[0] = g_variant_new_uint64(MIN_NUM_SAMPLES);
-		grange[1] = g_variant_new_uint64(devc->max_samples / num_channels);
+		if (num_ols_changrp)
+			grange[1] = g_variant_new_uint64(devc->max_samples / num_ols_changrp);
+		else
+			grange[1] = g_variant_new_uint64(MIN_NUM_SAMPLES);
 		*data = g_variant_new_tuple(grange, 2);
 		break;
 	default:
@@ -459,8 +469,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	struct dev_context *devc;
 	struct sr_serial_dev_inst *serial;
 	uint16_t samplecount, readcount, delaycount;
-	uint8_t changrp_mask, arg[4];
-	int num_channels;
+	uint8_t ols_changrp_mask, arg[4];
+	int num_ols_changrp;
 	int ret, i;
 
 	if (sdi->status != SR_ST_ACTIVE)
@@ -469,22 +479,14 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	devc = sdi->priv;
 	serial = sdi->conn;
 
-	if (ols_configure_channels(sdi) != SR_OK) {
-		sr_err("Failed to configure channels.");
-		return SR_ERR;
-	}
+	ols_channel_mask(sdi);
 
-	/*
-	 * Enable/disable channel groups in the flag register according to the
-	 * channel mask. Calculate this here, because num_channels is needed
-	 * to limit readcount.
-	 */
-	changrp_mask = 0;
-	num_channels = 0;
+	num_ols_changrp = 0;
+	ols_changrp_mask = 0;
 	for (i = 0; i < 4; i++) {
 		if (devc->channel_mask & (0xff << (i * 8))) {
-			changrp_mask |= (1 << i);
-			num_channels++;
+			ols_changrp_mask |= (1 << i);
+			num_ols_changrp++;
 		}
 	}
 
@@ -492,7 +494,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	 * Limit readcount to prevent reading past the end of the hardware
 	 * buffer.
 	 */
-	samplecount = MIN(devc->max_samples / num_channels, devc->limit_samples);
+	samplecount = MIN(devc->max_samples / num_ols_changrp, devc->limit_samples);
 	readcount = samplecount / 4;
 
 	/* Rather read too many samples than too few. */
@@ -500,12 +502,15 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 		readcount++;
 
 	/* Basic triggers. */
-	if (devc->trigger_mask[0] != 0x00000000) {
-		/* At least one channel has a trigger on it. */
+	if (ols_convert_trigger(sdi) != SR_OK) {
+		sr_err("Failed to configure channels.");
+		return SR_ERR;
+	}
+	if (devc->num_stages > 0) {
 		delaycount = readcount * (1 - devc->capture_ratio / 100.0);
 		devc->trigger_at = (readcount - delaycount) * 4 - devc->num_stages;
 		for (i = 0; i <= devc->num_stages; i++) {
-			sr_dbg("Setting stage %d trigger.", i);
+			sr_dbg("Setting OLS stage %d trigger.", i);
 			if ((ret = set_trigger(sdi, i)) != SR_OK)
 				return ret;
 		}
@@ -544,8 +549,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 			devc->flag_reg & FLAG_RLE ? "on" : "off",
 			devc->flag_reg & FLAG_FILTER ? "on": "off",
 			devc->flag_reg & FLAG_DEMUX ? "on" : "off");
-	/* 1 means "disable channel". */
-	devc->flag_reg |= ~(changrp_mask << 2) & 0x3c;
+	/*
+	 * Enable/disable OLS channel groups in the flag register according
+	 * to the channel mask. 1 means "disable channel".
+	 */
+	devc->flag_reg |= ~(ols_changrp_mask << 2) & 0x3c;
 	arg[0] = devc->flag_reg & 0xff;
 	arg[1] = devc->flag_reg >> 8;
 	arg[2] = arg[3] = 0x00;
@@ -565,7 +573,11 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	/* Send header packet to the session bus. */
 	std_session_send_df_header(cb_data, LOG_PREFIX);
 
-	serial_source_add(serial, G_IO_IN, -1, ols_receive_data, cb_data);
+	/* If the device stops sending for longer than it takes to send a byte,
+	 * that means it's finished. But wait at least 100 ms to be safe.
+	 */
+	serial_source_add(sdi->session, serial, G_IO_IN, 100,
+			ols_receive_data, cb_data);
 
 	return SR_OK;
 }
@@ -595,5 +607,5 @@ SR_PRIV struct sr_dev_driver ols_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/openbench-logic-sniffer/protocol.c b/src/hardware/openbench-logic-sniffer/protocol.c
similarity index 80%
rename from hardware/openbench-logic-sniffer/protocol.c
rename to src/hardware/openbench-logic-sniffer/protocol.c
index 4530447..6fb6b44 100644
--- a/hardware/openbench-logic-sniffer/protocol.c
+++ b/src/hardware/openbench-logic-sniffer/protocol.c
@@ -17,11 +17,10 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
-#include <libserialport.h>
 
 extern SR_PRIV struct sr_dev_driver ols_driver_info;
-static struct sr_dev_driver *di = &ols_driver_info;
 
 SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial,
 		uint8_t command)
@@ -30,7 +29,10 @@ SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial,
 
 	sr_dbg("Sending cmd 0x%.2x.", command);
 	buf[0] = command;
-	if (serial_write_blocking(serial, buf, 1) != 1)
+	if (serial_write_blocking(serial, buf, 1, serial_timeout(serial, 1)) != 1)
+		return SR_ERR;
+
+	if (serial_drain(serial) != 0)
 		return SR_ERR;
 
 	return SR_OK;
@@ -48,62 +50,70 @@ SR_PRIV int send_longcommand(struct sr_serial_dev_inst *serial,
 	buf[2] = data[1];
 	buf[3] = data[2];
 	buf[4] = data[3];
-	if (serial_write_blocking(serial, buf, 5) != 5)
+	if (serial_write_blocking(serial, buf, 5, serial_timeout(serial, 1)) != 5)
+		return SR_ERR;
+
+	if (serial_drain(serial) != 0)
 		return SR_ERR;
 
 	return SR_OK;
 }
 
-SR_PRIV int ols_configure_channels(const struct sr_dev_inst *sdi)
+/* Configures the channel mask based on which channels are enabled. */
+SR_PRIV void ols_channel_mask(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
-	const struct sr_channel *ch;
+	struct sr_channel *channel;
 	const GSList *l;
-	int channel_bit, stage, i;
-	char *tc;
 
 	devc = sdi->priv;
 
 	devc->channel_mask = 0;
+	for (l = sdi->channels; l; l = l->next) {
+		channel = l->data;
+		if (channel->enabled)
+			devc->channel_mask |= 1 << channel->index;
+	}
+}
+
+SR_PRIV int ols_convert_trigger(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_trigger *trigger;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	const GSList *l, *m;
+	int i;
+
+	devc = sdi->priv;
+
+	devc->num_stages = 0;
 	for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
 		devc->trigger_mask[i] = 0;
 		devc->trigger_value[i] = 0;
 	}
 
-	devc->num_stages = 0;
-	for (l = sdi->channels; l; l = l->next) {
-		ch = (const struct sr_channel *)l->data;
-		if (!ch->enabled)
-			continue;
+	if (!(trigger = sr_session_trigger_get(sdi->session)))
+		return SR_OK;
 
-		if (ch->index >= devc->max_channels) {
-			sr_err("Channels over the limit of %d\n", devc->max_channels);
-			return SR_ERR;
-		}
+	devc->num_stages = g_slist_length(trigger->stages);
+	if (devc->num_stages > NUM_TRIGGER_STAGES) {
+		sr_err("This device only supports %d trigger stages.",
+				NUM_TRIGGER_STAGES);
+		return SR_ERR;
+	}
 
-		/*
-		 * Set up the channel mask for later configuration into the
-		 * flag register.
-		 */
-		channel_bit = 1 << (ch->index);
-		devc->channel_mask |= channel_bit;
-
-		if (!ch->trigger)
-			continue;
-
-		/* Configure trigger mask and value. */
-		stage = 0;
-		for (tc = ch->trigger; tc && *tc; tc++) {
-			devc->trigger_mask[stage] |= channel_bit;
-			if (*tc == '1')
-				devc->trigger_value[stage] |= channel_bit;
-			stage++;
-			/* Only supporting parallel mode, with up to 4 stages. */
-			if (stage > 4)
-				return SR_ERR;
+	for (l = trigger->stages; l; l = l->next) {
+		stage = l->data;
+		for (m = stage->matches; m; m = m->next) {
+			match = m->data;
+			if (!match->channel->enabled)
+				/* Ignore disabled channels with a trigger. */
+				continue;
+			devc->trigger_mask[stage->stage] |= 1 << match->channel->index;
+			if (match->match == SR_TRIGGER_ONE)
+				devc->trigger_value[stage->stage] |= 1 << match->channel->index;
 		}
-		if (stage > devc->num_stages)
-			devc->num_stages = stage - 1;
 	}
 
 	return SR_OK;
@@ -113,10 +123,7 @@ SR_PRIV struct dev_context *ols_dev_new(void)
 {
 	struct dev_context *devc;
 
-	if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		return NULL;
-	}
+	devc = g_malloc0(sizeof(struct dev_context));
 
 	/* Device-specific settings */
 	devc->max_samples = devc->max_samplerate = devc->protocol_version = 0;
@@ -134,14 +141,15 @@ SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
 {
 	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
-	struct sr_channel *ch;
 	uint32_t tmp_int, ui;
 	uint8_t key, type, token;
+	int delay_ms;
 	GString *tmp_str, *devname, *version;
 	guchar tmp_c;
 
-	sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, NULL, NULL, NULL);
-	sdi->driver = di;
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->driver = &ols_driver_info;
 	devc = ols_dev_new();
 	sdi->priv = devc;
 
@@ -150,7 +158,8 @@ SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
 
 	key = 0xff;
 	while (key) {
-		if (serial_read_blocking(serial, &key, 1) != 1)
+		delay_ms = serial_timeout(serial, 1);
+		if (serial_read_blocking(serial, &key, 1, delay_ms) != 1)
 			break;
 		if (key == 0x00) {
 			sr_dbg("Got metadata key 0x00, metadata ends.");
@@ -162,7 +171,8 @@ SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
 		case 0:
 			/* NULL-terminated string */
 			tmp_str = g_string_new("");
-			while (serial_read_blocking(serial, &tmp_c, 1) == 1 && tmp_c != '\0')
+			delay_ms = serial_timeout(serial, 1);
+			while (serial_read_blocking(serial, &tmp_c, 1, delay_ms) == 1 && tmp_c != '\0')
 				g_string_append_c(tmp_str, tmp_c);
 			sr_dbg("Got metadata key 0x%.2x value '%s'.",
 			       key, tmp_str->str);
@@ -194,7 +204,8 @@ SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
 			break;
 		case 1:
 			/* 32-bit unsigned integer */
-			if (serial_read_blocking(serial, &tmp_int, 4) != 4)
+			delay_ms = serial_timeout(serial, 4);
+			if (serial_read_blocking(serial, &tmp_int, 4, delay_ms) != 4)
 				break;
 			tmp_int = RB32(&tmp_int);
 			sr_dbg("Got metadata key 0x%.2x value 0x%.8x.",
@@ -202,12 +213,9 @@ SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
 			switch (token) {
 			case 0x00:
 				/* Number of usable channels */
-				for (ui = 0; ui < tmp_int; ui++) {
-					if (!(ch = sr_channel_new(ui, SR_CHANNEL_LOGIC, TRUE,
-							ols_channel_names[ui])))
-						return 0;
-					sdi->channels = g_slist_append(sdi->channels, ch);
-				}
+				for (ui = 0; ui < tmp_int; ui++)
+					sr_channel_new(sdi, ui, SR_CHANNEL_LOGIC, TRUE,
+							ols_channel_names[ui]);
 				break;
 			case 0x01:
 				/* Amount of sample memory available (bytes) */
@@ -218,7 +226,7 @@ SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
 				/* what is this for? */
 				break;
 			case 0x03:
-				/* Maximum sample rate (hz) */
+				/* Maximum sample rate (Hz) */
 				devc->max_samplerate = tmp_int;
 				break;
 			case 0x04:
@@ -233,19 +241,17 @@ SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial)
 			break;
 		case 2:
 			/* 8-bit unsigned integer */
-			if (serial_read_blocking(serial, &tmp_c, 1) != 1)
+			delay_ms = serial_timeout(serial, 1);
+			if (serial_read_blocking(serial, &tmp_c, 1, delay_ms) != 1)
 				break;
 			sr_dbg("Got metadata key 0x%.2x value 0x%.2x.",
 			       key, tmp_c);
 			switch (token) {
 			case 0x00:
 				/* Number of usable channels */
-				for (ui = 0; ui < tmp_c; ui++) {
-					if (!(ch = sr_channel_new(ui, SR_CHANNEL_LOGIC, TRUE,
-							ols_channel_names[ui])))
-						return 0;
-					sdi->channels = g_slist_append(sdi->channels, ch);
-				}
+				for (ui = 0; ui < tmp_c; ui++)
+					sr_channel_new(sdi, ui, SR_CHANNEL_LOGIC, TRUE,
+							ols_channel_names[ui]);
 				break;
 			case 0x01:
 				/* protocol version */
@@ -313,7 +319,7 @@ SR_PRIV void abort_acquisition(const struct sr_dev_inst *sdi)
 	struct sr_serial_dev_inst *serial;
 
 	serial = sdi->conn;
-	serial_source_remove(serial);
+	serial_source_remove(sdi->session, serial);
 
 	/* Terminate session */
 	packet.type = SR_DF_END;
@@ -328,7 +334,7 @@ SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data)
 	struct sr_datafeed_packet packet;
 	struct sr_datafeed_logic logic;
 	uint32_t sample;
-	int num_channels, offset, j;
+	int num_ols_changrp, offset, j;
 	unsigned int i;
 	unsigned char byte;
 
@@ -338,15 +344,12 @@ SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data)
 	serial = sdi->conn;
 	devc = sdi->priv;
 
+	if (devc->num_transfers == 0 && revents == 0) {
+		/* Ignore timeouts as long as we haven't received anything */
+		return TRUE;
+	}
+
 	if (devc->num_transfers++ == 0) {
-		/*
-		 * First time round, means the device started sending data,
-		 * and will not stop until done. If it stops sending for
-		 * longer than it takes to send a byte, that means it's
-		 * finished. We'll double that to 30ms to be sure...
-		 */
-		serial_source_remove(serial);
-		serial_source_add(serial, G_IO_IN, 30, ols_receive_data, cb_data);
 		devc->raw_sample_buf = g_try_malloc(devc->limit_samples * 4);
 		if (!devc->raw_sample_buf) {
 			sr_err("Sample buffer malloc failed.");
@@ -356,10 +359,10 @@ SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data)
 		memset(devc->raw_sample_buf, 0x82, devc->limit_samples * 4);
 	}
 
-	num_channels = 0;
+	num_ols_changrp = 0;
 	for (i = NUM_CHANNELS; i > 0x02; i /= 2) {
 		if ((devc->flag_reg & i) == 0) {
-			num_channels++;
+			num_ols_changrp++;
 		}
 	}
 
@@ -374,7 +377,7 @@ SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data)
 
 		devc->sample[devc->num_bytes++] = byte;
 		sr_spew("Received byte 0x%.2x.", byte);
-		if (devc->num_bytes == num_channels) {
+		if (devc->num_bytes == num_ols_changrp) {
 			devc->cnt_samples++;
 			devc->cnt_samples_rle++;
 			/*
@@ -407,7 +410,7 @@ SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data)
 				devc->num_samples = devc->limit_samples;
 			}
 
-			if (num_channels < 4) {
+			if (num_ols_changrp < 4) {
 				/*
 				 * Some channel groups may have been turned
 				 * off, to speed up transfer between the
diff --git a/hardware/openbench-logic-sniffer/protocol.h b/src/hardware/openbench-logic-sniffer/protocol.h
similarity index 85%
copy from hardware/openbench-logic-sniffer/protocol.h
copy to src/hardware/openbench-logic-sniffer/protocol.h
index 98831b5..1e6cd69 100644
--- a/hardware/openbench-logic-sniffer/protocol.h
+++ b/src/hardware/openbench-logic-sniffer/protocol.h
@@ -23,18 +23,17 @@
 #include <stdint.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "ols"
 
-#define NUM_CHANNELS             32
-#define NUM_TRIGGER_STAGES     4
-#define TRIGGER_TYPE           "01"
-#define SERIAL_SPEED           B115200
-#define CLOCK_RATE             SR_MHZ(100)
-#define MIN_NUM_SAMPLES        4
-#define DEFAULT_SAMPLERATE     SR_KHZ(200)
+#define NUM_CHANNELS               32
+#define NUM_TRIGGER_STAGES         4
+#define SERIAL_SPEED               B115200
+#define CLOCK_RATE                 SR_MHZ(100)
+#define MIN_NUM_SAMPLES            4
+#define DEFAULT_SAMPLERATE         SR_KHZ(200)
 
 /* Command opcodes */
 #define CMD_RESET                  0x00
@@ -56,7 +55,7 @@
 /* 12-13 unused, 14-15 RLE mode (we hardcode mode 0). */
 #define FLAG_INTERNAL_TEST_MODE    (1 << 11)
 #define FLAG_EXTERNAL_TEST_MODE    (1 << 10)
-#define FLAG_SWAP_CHANNELS           (1 << 9)
+#define FLAG_SWAP_CHANNELS         (1 << 9)
 #define FLAG_RLE                   (1 << 8)
 #define FLAG_SLOPE_FALLING         (1 << 7)
 #define FLAG_CLOCK_EXTERNAL        (1 << 6)
@@ -82,8 +81,8 @@ struct dev_context {
 	int capture_ratio;
 	int trigger_at;
 	uint32_t channel_mask;
-	uint32_t trigger_mask[4];
-	uint32_t trigger_value[4];
+	uint32_t trigger_mask[NUM_TRIGGER_STAGES];
+	uint32_t trigger_value[NUM_TRIGGER_STAGES];
 	int num_stages;
 	uint16_t flag_reg;
 
@@ -102,14 +101,14 @@ struct dev_context {
 	unsigned char *raw_sample_buf;
 };
 
-
-SR_PRIV extern const char *ols_channel_names[NUM_CHANNELS + 1];
+SR_PRIV extern const char *ols_channel_names[];
 
 SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial,
 		uint8_t command);
 SR_PRIV int send_longcommand(struct sr_serial_dev_inst *serial,
 		uint8_t command, uint8_t *data);
-SR_PRIV int ols_configure_channels(const struct sr_dev_inst *sdi);
+SR_PRIV void ols_channel_mask(const struct sr_dev_inst *sdi);
+SR_PRIV int ols_convert_trigger(const struct sr_dev_inst *sdi);
 SR_PRIV struct dev_context *ols_dev_new(void);
 SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial);
 SR_PRIV int ols_set_samplerate(const struct sr_dev_inst *sdi,
diff --git a/hardware/openbench-logic-sniffer/api.c b/src/hardware/pipistrello-ols/api.c
similarity index 51%
rename from hardware/openbench-logic-sniffer/api.c
rename to src/hardware/pipistrello-ols/api.c
index b99d8e7..44af401 100644
--- a/hardware/openbench-logic-sniffer/api.c
+++ b/src/hardware/pipistrello-ols/api.c
@@ -17,26 +17,26 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
-#include <libserialport.h>
 
-#define SERIALCOMM "115200/8n1"
-
-static const int32_t hwopts[] = {
-	SR_CONF_CONN,
-	SR_CONF_SERIALCOMM,
+static const uint32_t devopts[] = {
+	SR_CONF_LOGIC_ANALYZER,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_PATTERN_MODE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SWAP | SR_CONF_SET,
+	SR_CONF_RLE | SR_CONF_GET | SR_CONF_SET,
 };
 
-static const int32_t hwcaps[] = {
-	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_TRIGGER_TYPE,
-	SR_CONF_CAPTURE_RATIO,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_EXTERNAL_CLOCK,
-	SR_CONF_PATTERN_MODE,
-	SR_CONF_SWAP,
-	SR_CONF_RLE,
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
 };
 
 #define STR_PATTERN_NONE     "None"
@@ -62,11 +62,10 @@ static const char *patterns[] = {
 };
 
 /* Channels are numbered 0-31 (on the PCB silkscreen). */
-SR_PRIV const char *ols_channel_names[NUM_CHANNELS + 1] = {
+SR_PRIV const char *p_ols_channel_names[] = {
 	"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
 	"13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23",
 	"24", "25", "26", "27", "28", "29", "30", "31",
-	NULL,
 };
 
 /* Default supported samplerates, can be overridden by device metadata. */
@@ -76,143 +75,157 @@ static const uint64_t samplerates[] = {
 	SR_HZ(1),
 };
 
-SR_PRIV struct sr_dev_driver ols_driver_info;
-static struct sr_dev_driver *di = &ols_driver_info;
+SR_PRIV struct sr_dev_driver p_ols_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-	struct sr_config *src;
 	struct sr_dev_inst *sdi;
 	struct drv_context *drvc;
 	struct dev_context *devc;
-	struct sr_channel *ch;
-	struct sr_serial_dev_inst *serial;
-	GPollFD probefd;
-	GSList *l, *devices;
+	GSList *devices;
 	int ret, i;
-	const char *conn, *serialcomm;
-	char buf[8];
+	char buf[70];
+	int bytes_read;
+
+	(void)options;
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	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;
+	/* Allocate memory for our private device context. */
+	devc = g_malloc0(sizeof(struct dev_context));
 
-	if (serialcomm == NULL)
-		serialcomm = SERIALCOMM;
+	/* Device-specific settings */
+	devc->max_samplebytes = devc->max_samplerate = devc->protocol_version = 0;
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
+	/* Acquisition settings */
+	devc->limit_samples = devc->capture_ratio = 0;
+	devc->trigger_at = -1;
+	devc->channel_mask = 0xffffffff;
+	devc->flag_reg = 0;
 
+	/* Allocate memory for the incoming ftdi data. */
+	devc->ftdi_buf = g_malloc0(FTDI_BUF_SIZE);
+
+	/* Allocate memory for the FTDI context (ftdic) and initialize it. */
+	if (!(devc->ftdic = ftdi_new())) {
+		sr_err("Failed to initialize libftdi.");
+		goto err_free_ftdi_buf;;
+	}
+
+	/* Try to open the FTDI device */
+	if (p_ols_open(devc) != SR_OK) {
+		goto err_free_ftdic;
+	}
+	
 	/* The discovery procedure is like this: first send the Reset
 	 * command (0x00) 5 times, since the device could be anywhere
 	 * in a 5-byte command. Then send the ID command (0x02).
 	 * If the device responds with 4 bytes ("OLS1" or "SLA1"), we
 	 * have a match.
 	 */
-	sr_info("Probing %s.", conn);
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
-		return NULL;
 
 	ret = SR_OK;
 	for (i = 0; i < 5; i++) {
-		if ((ret = send_shortcommand(serial, CMD_RESET)) != SR_OK) {
-			sr_err("Port %s is not writable.", conn);
+		if ((ret = write_shortcommand(devc, CMD_RESET)) != SR_OK) {
 			break;
 		}
 	}
 	if (ret != SR_OK) {
-		serial_close(serial);
-		sr_err("Could not use port %s. Quitting.", conn);
-		return NULL;
+		sr_err("Could not reset device. Quitting.");
+		goto err_close_ftdic;
+	}
+	write_shortcommand(devc, CMD_ID);
+
+	/* Read the response data. */
+	bytes_read = ftdi_read_data(devc->ftdic, (uint8_t *)buf, 4);
+	if (bytes_read < 0) {
+		sr_err("Failed to read FTDI data (%d): %s.",
+		       bytes_read, ftdi_get_error_string(devc->ftdic));
+		goto err_close_ftdic;
+	}
+	if (bytes_read == 0) {
+		goto err_close_ftdic;
 	}
-	send_shortcommand(serial, CMD_ID);
-
-	/* Wait 10ms for a response. */
-	g_usleep(10000);
-
-	sp_get_port_handle(serial->data, &probefd.fd);
-	probefd.events = G_IO_IN;
-	g_poll(&probefd, 1, 1);
 
-	if (probefd.revents != G_IO_IN)
-		return NULL;
-	if (serial_read_blocking(serial, buf, 4) != 4)
-		return NULL;
 	if (strncmp(buf, "1SLO", 4) && strncmp(buf, "1ALS", 4))
-		return NULL;
+		goto err_close_ftdic;
 
 	/* Definitely using the OLS protocol, check if it supports
 	 * the metadata command.
 	 */
-	send_shortcommand(serial, CMD_METADATA);
-	if (g_poll(&probefd, 1, 10) > 0) {
-		/* Got metadata. */
-		sdi = get_metadata(serial);
-		sdi->index = 0;
-		devc = sdi->priv;
-	} else {
-		/* Not an OLS -- some other board that uses the sump protocol. */
-		sr_info("Device does not support metadata.");
-		sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
-				"Sump", "Logic Analyzer", "v1.0");
-		sdi->driver = di;
-		for (i = 0; i < 32; i++) {
-			if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-					ols_channel_names[i])))
-				return 0;
-			sdi->channels = g_slist_append(sdi->channels, ch);
-		}
-		devc = ols_dev_new();
-		sdi->priv = devc;
+	write_shortcommand(devc, CMD_METADATA);
+
+        /* Read the metadata. */
+	bytes_read = ftdi_read_data(devc->ftdic, (uint8_t *)buf, 64);
+	if (bytes_read < 0) {
+		sr_err("Failed to read FTDI data (%d): %s.",
+		       bytes_read, ftdi_get_error_string(devc->ftdic));
+		goto err_close_ftdic;
 	}
+	if (bytes_read == 0) {
+		goto err_close_ftdic;
+	}
+
+	/* Close device. We'll reopen it again when we need it. */
+	p_ols_close(devc);
+
+	/* Parse the metadata. */
+	sdi = p_ols_get_metadata((uint8_t *)buf, bytes_read, devc);
+
 	/* Configure samplerate and divider. */
-	if (ols_set_samplerate(sdi, DEFAULT_SAMPLERATE) != SR_OK)
+	if (p_ols_set_samplerate(sdi, DEFAULT_SAMPLERATE) != SR_OK)
 		sr_dbg("Failed to set default samplerate (%"PRIu64").",
 				DEFAULT_SAMPLERATE);
-	/* Clear trigger masks, values and stages. */
-	ols_configure_channels(sdi);
-	sdi->inst_type = SR_INST_SERIAL;
-	sdi->conn = serial;
 
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
 
-	serial_close(serial);
-
 	return devices;
+
+err_close_ftdic:
+	p_ols_close(devc);
+err_free_ftdic:
+	ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
+err_free_ftdi_buf:
+	g_free(devc->ftdi_buf);
+	g_free(devc);
+
+	return NULL;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static void clear_helper(void *priv)
 {
-	return std_dev_clear(di, NULL);
+	struct dev_context *devc;
+
+	devc = priv;
+
+	ftdi_free(devc->ftdic);
+	g_free(devc->ftdi_buf);
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, clear_helper);
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return dev_clear(di);
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -223,7 +236,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 		return SR_ERR_ARG;
 
 	devc = sdi->priv;
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		*data = g_variant_new_uint64(devc->cur_samplerate);
 		break;
@@ -244,6 +257,9 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	case SR_CONF_RLE:
 		*data = g_variant_new_boolean(devc->flag_reg & FLAG_RLE ? TRUE : FALSE);
 		break;
+	case SR_CONF_EXTERNAL_CLOCK:
+		*data = g_variant_new_boolean(devc->flag_reg & FLAG_CLOCK_EXTERNAL ? TRUE : FALSE);
+		break;
 	default:
 		return SR_ERR_NA;
 	}
@@ -251,7 +267,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -267,12 +283,12 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
 	devc = sdi->priv;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		tmp_u64 = g_variant_get_uint64(data);
 		if (tmp_u64 < samplerates[0] || tmp_u64 > samplerates[1])
 			return SR_ERR_SAMPLERATE;
-		ret = ols_set_samplerate(sdi, g_variant_get_uint64(data));
+		ret = p_ols_set_samplerate(sdi, g_variant_get_uint64(data));
 		break;
 	case SR_CONF_LIMIT_SAMPLES:
 		tmp_u64 = g_variant_get_uint64(data);
@@ -283,10 +299,9 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		break;
 	case SR_CONF_CAPTURE_RATIO:
 		devc->capture_ratio = g_variant_get_uint64(data);
-		if (devc->capture_ratio < 0 || devc->capture_ratio > 100) {
-			devc->capture_ratio = 0;
+		if (devc->capture_ratio < 0 || devc->capture_ratio > 100)
 			ret = SR_ERR;
-		} else
+		else
 			ret = SR_OK;
 		break;
 	case SR_CONF_EXTERNAL_CLOCK:
@@ -348,24 +363,20 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
 	GVariant *gvar, *grange[2];
 	GVariantBuilder gvb;
-	int num_channels, i;
+	int num_pols_changrp, i;
 
 	(void)cg;
 
 	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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLERATE:
 		g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
@@ -374,8 +385,10 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar);
 		*data = g_variant_builder_end(&gvb);
 		break;
-	case SR_CONF_TRIGGER_TYPE:
-		*data = g_variant_new_string(TRIGGER_TYPE);
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				trigger_matches, ARRAY_SIZE(trigger_matches),
+				sizeof(int32_t));
 		break;
 	case SR_CONF_PATTERN_MODE:
 		*data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns));
@@ -386,27 +399,27 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		devc = sdi->priv;
 		if (devc->flag_reg & FLAG_RLE)
 			return SR_ERR_NA;
-		if (devc->max_samples == 0)
+		if (devc->max_samplebytes == 0)
 			/* Device didn't specify sample memory size in metadata. */
 			return SR_ERR_NA;
 		/*
 		 * Channel groups are turned off if no channels in that group are
 		 * enabled, making more room for samples for the enabled group.
 		*/
-		ols_configure_channels(sdi);
-		num_channels = 0;
+		pols_channel_mask(sdi);
+		num_pols_changrp = 0;
 		for (i = 0; i < 4; i++) {
 			if (devc->channel_mask & (0xff << (i * 8)))
-				num_channels++;
-		}
-		if (num_channels == 0) {
-			/* This can happen, but shouldn't cause too much drama.
-			 * However we can't continue because the code below would
-			 * divide by zero. */
-			break;
+				num_pols_changrp++;
 		}
+		/* 3 channel groups takes as many bytes as 4 channel groups */
+		if (num_pols_changrp == 3)
+			num_pols_changrp = 4;
 		grange[0] = g_variant_new_uint64(MIN_NUM_SAMPLES);
-		grange[1] = g_variant_new_uint64(devc->max_samples / num_channels);
+		if (num_pols_changrp)
+			grange[1] = g_variant_new_uint64(devc->max_samplebytes / num_pols_changrp);
+		else
+			grange[1] = g_variant_new_uint64(MIN_NUM_SAMPLES);
 		*data = g_variant_new_tuple(grange, 2);
 		break;
 	default:
@@ -416,21 +429,53 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+
+	if (p_ols_open(devc) != SR_OK) {
+		return SR_ERR;
+	} else {
+		sdi->status = SR_ST_ACTIVE;
+		return SR_OK;
+	}
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	int ret;
+	struct dev_context *devc;
+
+	ret = SR_OK;
+	devc = sdi->priv;
+
+	if (sdi->status == SR_ST_ACTIVE) {
+		sr_dbg("Status ACTIVE, closing device.");
+		ret = p_ols_close(devc);
+	} else {
+		sr_spew("Status not ACTIVE, nothing to do.");
+	}
+
+	sdi->status = SR_ST_INACTIVE;
+
+	return ret;
+}
+
 static int set_trigger(const struct sr_dev_inst *sdi, int stage)
 {
 	struct dev_context *devc;
-	struct sr_serial_dev_inst *serial;
 	uint8_t cmd, arg[4];
 
 	devc = sdi->priv;
-	serial = sdi->conn;
 
 	cmd = CMD_SET_TRIGGER_MASK + stage * 4;
 	arg[0] = devc->trigger_mask[stage] & 0xff;
 	arg[1] = (devc->trigger_mask[stage] >> 8) & 0xff;
 	arg[2] = (devc->trigger_mask[stage] >> 16) & 0xff;
 	arg[3] = (devc->trigger_mask[stage] >> 24) & 0xff;
-	if (send_longcommand(serial, cmd, arg) != SR_OK)
+	if (write_longcommand(devc, cmd, arg) != SR_OK)
 		return SR_ERR;
 
 	cmd = CMD_SET_TRIGGER_VALUE + stage * 4;
@@ -438,7 +483,7 @@ static int set_trigger(const struct sr_dev_inst *sdi, int stage)
 	arg[1] = (devc->trigger_value[stage] >> 8) & 0xff;
 	arg[2] = (devc->trigger_value[stage] >> 16) & 0xff;
 	arg[3] = (devc->trigger_value[stage] >> 24) & 0xff;
-	if (send_longcommand(serial, cmd, arg) != SR_OK)
+	if (write_longcommand(devc, cmd, arg) != SR_OK)
 		return SR_ERR;
 
 	cmd = CMD_SET_TRIGGER_CONFIG + stage * 4;
@@ -447,7 +492,44 @@ static int set_trigger(const struct sr_dev_inst *sdi, int stage)
 	if (stage == devc->num_stages)
 		/* Last stage, fire when this one matches. */
 		arg[3] |= TRIGGER_START;
-	if (send_longcommand(serial, cmd, arg) != SR_OK)
+	if (write_longcommand(devc, cmd, arg) != SR_OK)
+		return SR_ERR;
+
+	cmd = CMD_SET_TRIGGER_EDGE + stage * 4;
+	arg[0] = devc->trigger_edge[stage] & 0xff;
+	arg[1] = (devc->trigger_edge[stage] >> 8) & 0xff;
+	arg[2] = (devc->trigger_edge[stage] >> 16) & 0xff;
+	arg[3] = (devc->trigger_edge[stage] >> 24) & 0xff;
+	if (write_longcommand(devc, cmd, arg) != SR_OK)
+		return SR_ERR;
+
+	return SR_OK;
+}
+
+static int disable_trigger(const struct sr_dev_inst *sdi, int stage)
+{
+	struct dev_context *devc;
+	uint8_t cmd, arg[4];
+
+	devc = sdi->priv;
+
+	cmd = CMD_SET_TRIGGER_MASK + stage * 4;
+	arg[0] = arg[1] = arg[2] = arg[3] = 0x00;
+	if (write_longcommand(devc, cmd, arg) != SR_OK)
+		return SR_ERR;
+
+	cmd = CMD_SET_TRIGGER_VALUE + stage * 4;
+	if (write_longcommand(devc, cmd, arg) != SR_OK)
+		return SR_ERR;
+
+	cmd = CMD_SET_TRIGGER_CONFIG + stage * 4;
+	arg[2] = 0x03;
+	if (write_longcommand(devc, cmd, arg) != SR_OK)
+		return SR_ERR;
+
+	cmd = CMD_SET_TRIGGER_EDGE + stage * 4;
+	arg[2] = 0x00;
+	if (write_longcommand(devc, cmd, arg) != SR_OK)
 		return SR_ERR;
 
 	return SR_OK;
@@ -457,57 +539,81 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 		void *cb_data)
 {
 	struct dev_context *devc;
-	struct sr_serial_dev_inst *serial;
-	uint16_t samplecount, readcount, delaycount;
-	uint8_t changrp_mask, arg[4];
-	int num_channels;
+	uint32_t samplecount, readcount, delaycount;
+	uint8_t pols_changrp_mask, arg[4];
+	uint16_t flag_tmp;
+	int num_pols_changrp, samplespercount;
 	int ret, i;
 
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
 	devc = sdi->priv;
-	serial = sdi->conn;
 
-	if (ols_configure_channels(sdi) != SR_OK) {
-		sr_err("Failed to configure channels.");
-		return SR_ERR;
-	}
+	pols_channel_mask(sdi);
 
 	/*
 	 * Enable/disable channel groups in the flag register according to the
-	 * channel mask. Calculate this here, because num_channels is needed
-	 * to limit readcount.
+	 * channel mask. Calculate this here, because num_pols_changrp is
+	 * needed to limit readcount.
 	 */
-	changrp_mask = 0;
-	num_channels = 0;
+	pols_changrp_mask = 0;
+	num_pols_changrp = 0;
 	for (i = 0; i < 4; i++) {
 		if (devc->channel_mask & (0xff << (i * 8))) {
-			changrp_mask |= (1 << i);
-			num_channels++;
+			pols_changrp_mask |= (1 << i);
+			num_pols_changrp++;
 		}
 	}
+	/* 3 channel groups takes as many bytes as 4 channel groups */
+	if (num_pols_changrp == 3)
+		num_pols_changrp = 4;
+	/* maximum number of samples (or RLE counts) the buffer memory can hold */
+	devc->max_samples = devc->max_samplebytes / num_pols_changrp;
 
 	/*
 	 * Limit readcount to prevent reading past the end of the hardware
 	 * buffer.
 	 */
-	samplecount = MIN(devc->max_samples / num_channels, devc->limit_samples);
-	readcount = samplecount / 4;
+	sr_dbg("max_samples = %d", devc->max_samples);
+	sr_dbg("limit_samples = %" PRIu64, devc->limit_samples);
+	samplecount = MIN(devc->max_samples, devc->limit_samples);
+	sr_dbg("Samplecount = %d", samplecount);
+
+	/* In demux mode the OLS is processing two samples per clock */
+	if (devc->flag_reg & FLAG_DEMUX) {
+		samplespercount = 8;
+	}
+	else {
+		samplespercount = 4;
+	}
+
+	readcount = samplecount / samplespercount;
 
 	/* Rather read too many samples than too few. */
-	if (samplecount % 4 != 0)
+	if (samplecount % samplespercount != 0)
 		readcount++;
 
 	/* Basic triggers. */
-	if (devc->trigger_mask[0] != 0x00000000) {
-		/* At least one channel has a trigger on it. */
+	if (pols_convert_trigger(sdi) != SR_OK) {
+		sr_err("Failed to configure channels.");
+		return SR_ERR;
+	}
+
+	if (devc->num_stages > 0) {
 		delaycount = readcount * (1 - devc->capture_ratio / 100.0);
-		devc->trigger_at = (readcount - delaycount) * 4 - devc->num_stages;
-		for (i = 0; i <= devc->num_stages; i++) {
-			sr_dbg("Setting stage %d trigger.", i);
-			if ((ret = set_trigger(sdi, i)) != SR_OK)
-				return ret;
+		devc->trigger_at = (readcount - delaycount) * samplespercount - devc->num_stages;
+		for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
+			if (i <= devc->num_stages) {
+				sr_dbg("Setting p-ols stage %d trigger.", i);
+				if ((ret = set_trigger(sdi, i)) != SR_OK)
+					return ret;
+			}
+			else {
+				sr_dbg("Disabling p-ols stage %d trigger.", i);
+				if ((ret = disable_trigger(sdi, i)) != SR_OK)
+					return ret;
+			}
 		}
 	} else {
 		/* No triggers configured, force trigger on first stage. */
@@ -524,17 +630,21 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	arg[1] = (devc->cur_samplerate_divider & 0xff00) >> 8;
 	arg[2] = (devc->cur_samplerate_divider & 0xff0000) >> 16;
 	arg[3] = 0x00;
-	if (send_longcommand(serial, CMD_SET_DIVIDER, arg) != SR_OK)
+	if (write_longcommand(devc, CMD_SET_DIVIDER, arg) != SR_OK)
 		return SR_ERR;
 
-	/* Send sample limit and pre/post-trigger capture ratio. */
-	sr_dbg("Setting sample limit %d, trigger point at %d",
-			(readcount - 1) * 4, (delaycount - 1) * 4);
+	/* Send extended sample limit and pre/post-trigger capture ratio. */
 	arg[0] = ((readcount - 1) & 0xff);
 	arg[1] = ((readcount - 1) & 0xff00) >> 8;
-	arg[2] = ((delaycount - 1) & 0xff);
-	arg[3] = ((delaycount - 1) & 0xff00) >> 8;
-	if (send_longcommand(serial, CMD_CAPTURE_SIZE, arg) != SR_OK)
+	arg[2] = ((readcount - 1) & 0xff0000) >> 16;
+	arg[3] = ((readcount - 1) & 0xff000000) >> 24;
+	if (write_longcommand(devc, CMD_CAPTURE_DELAY, arg) != SR_OK)
+		return SR_ERR;
+	arg[0] = ((delaycount - 1) & 0xff);
+	arg[1] = ((delaycount - 1) & 0xff00) >> 8;
+	arg[2] = ((delaycount - 1) & 0xff0000) >> 16;
+	arg[3] = ((delaycount - 1) & 0xff000000) >> 24;
+	if (write_longcommand(devc, CMD_CAPTURE_COUNT, arg) != SR_OK)
 		return SR_ERR;
 
 	/* Flag register. */
@@ -544,16 +654,33 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 			devc->flag_reg & FLAG_RLE ? "on" : "off",
 			devc->flag_reg & FLAG_FILTER ? "on": "off",
 			devc->flag_reg & FLAG_DEMUX ? "on" : "off");
-	/* 1 means "disable channel". */
-	devc->flag_reg |= ~(changrp_mask << 2) & 0x3c;
-	arg[0] = devc->flag_reg & 0xff;
-	arg[1] = devc->flag_reg >> 8;
+
+	/*
+	* Enable/disable OLS channel groups in the flag register according
+	* to the channel mask. 1 means "disable channel".
+	*/
+	devc->flag_reg &= ~0x3c;
+	devc->flag_reg |= ~(pols_changrp_mask << 2) & 0x3c;
+	sr_dbg("flag_reg = %x", devc->flag_reg);
+
+	/*
+	* In demux mode the OLS is processing two 8-bit or 16-bit samples 
+	* in parallel and for this to work the lower two bits of the four 
+	* "channel_disable" bits must be replicated to the upper two bits.
+	*/
+	flag_tmp = devc->flag_reg;
+	if (devc->flag_reg & FLAG_DEMUX) {
+		flag_tmp &= ~0x30;
+		flag_tmp |= ~(pols_changrp_mask << 4) & 0x30;
+	}
+	arg[0] = flag_tmp & 0xff;
+	arg[1] = flag_tmp >> 8;
 	arg[2] = arg[3] = 0x00;
-	if (send_longcommand(serial, CMD_SET_FLAGS, arg) != SR_OK)
+	if (write_longcommand(devc, CMD_SET_FLAGS, arg) != SR_OK)
 		return SR_ERR;
 
 	/* Start acquisition on the device. */
-	if (send_shortcommand(serial, CMD_RUN) != SR_OK)
+	if (write_shortcommand(devc, CMD_RUN) != SR_OK)
 		return SR_ERR;
 
 	/* Reset all operational states. */
@@ -565,35 +692,52 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	/* Send header packet to the session bus. */
 	std_session_send_df_header(cb_data, LOG_PREFIX);
 
-	serial_source_add(serial, G_IO_IN, -1, ols_receive_data, cb_data);
+	/* Hook up a dummy handler to receive data from the device. */
+	sr_session_source_add(sdi->session, -1, 0, 10, p_ols_receive_data,
+			cb_data);
 
 	return SR_OK;
 }
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
-	(void)cb_data;
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+
+	devc = sdi->priv;
+
+	sr_dbg("Stopping acquisition.");
+	write_shortcommand(devc, CMD_RESET);
+	write_shortcommand(devc, CMD_RESET);
+	write_shortcommand(devc, CMD_RESET);
+	write_shortcommand(devc, CMD_RESET);
+	write_shortcommand(devc, CMD_RESET);
+
+	sr_session_source_remove(sdi->session, -1);
 
-	abort_acquisition(sdi);
+	/* 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 ols_driver_info = {
-	.name = "ols",
-	.longname = "Openbench Logic Sniffer",
+SR_PRIV struct sr_dev_driver p_ols_driver_info = {
+	.name = "p-ols",
+	.longname = "Pipistrello OLS",
 	.api_version = 1,
 	.init = init,
 	.cleanup = cleanup,
 	.scan = scan,
 	.dev_list = dev_list,
-	.dev_clear = NULL,
+	.dev_clear = dev_clear,
 	.config_get = config_get,
 	.config_set = config_set,
 	.config_list = config_list,
-	.dev_open = std_serial_dev_open,
-	.dev_close = std_serial_dev_close,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/src/hardware/pipistrello-ols/protocol.c b/src/hardware/pipistrello-ols/protocol.c
new file mode 100644
index 0000000..17fe031
--- /dev/null
+++ b/src/hardware/pipistrello-ols/protocol.c
@@ -0,0 +1,682 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+
+extern SR_PRIV struct sr_dev_driver p_ols_driver_info;
+
+SR_PRIV int write_shortcommand(struct dev_context *devc, uint8_t command)
+{
+	uint8_t buf[1];
+	int bytes_written;
+
+	sr_dbg("Sending cmd 0x%.2x.", command);
+	buf[0] = command;
+	bytes_written = ftdi_write_data(devc->ftdic, buf, 1);
+	if (bytes_written < 0) {
+		sr_err("Failed to write FTDI data (%d): %s.",
+		       bytes_written, ftdi_get_error_string(devc->ftdic));
+		return SR_ERR;
+	} else if (bytes_written != 1) {
+		sr_err("FTDI write error, only %d/%d bytes written: %s.",
+		       bytes_written, 1, ftdi_get_error_string(devc->ftdic));
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV int write_longcommand(struct dev_context *devc, uint8_t command, uint8_t *data)
+{
+	uint8_t buf[5];
+	int bytes_written;
+
+	sr_dbg("Sending cmd 0x%.2x data 0x%.2x%.2x%.2x%.2x.", command,
+			data[0], data[1], data[2], data[3]);
+	buf[0] = command;
+	buf[1] = data[0];
+	buf[2] = data[1];
+	buf[3] = data[2];
+	buf[4] = data[3];
+	bytes_written = ftdi_write_data(devc->ftdic, buf, 5);
+	if (bytes_written < 0) {
+		sr_err("Failed to write FTDI data (%d): %s.",
+		       bytes_written, ftdi_get_error_string(devc->ftdic));
+		return SR_ERR;
+	} else if (bytes_written != 5) {
+		sr_err("FTDI write error, only %d/%d bytes written: %s.",
+		       bytes_written, 1, ftdi_get_error_string(devc->ftdic));
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV int p_ols_open(struct dev_context *devc)
+{
+	int ret;
+
+	/* Note: Caller checks devc and devc->ftdic. */
+
+	/* Select interface B, otherwise communication will fail. */
+	ret = ftdi_set_interface(devc->ftdic, INTERFACE_B);
+	if (ret < 0) {
+		sr_err("Failed to set FTDI interface B (%d): %s", ret,
+		       ftdi_get_error_string(devc->ftdic));
+		return SR_ERR;
+	}
+	sr_dbg("FTDI chip interface B set successfully.");
+
+	/* Check for the device and temporarily open it. */
+	ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID, USB_DEVICE_ID,
+				 USB_IPRODUCT, NULL);
+	if (ret < 0) {
+		/* Log errors, except for -3 ("device not found"). */
+		if (ret != -3)
+			sr_err("Failed to open device (%d): %s", ret,
+			       ftdi_get_error_string(devc->ftdic));
+		return SR_ERR;
+	}
+	sr_dbg("FTDI device opened successfully.");
+
+	/* Purge RX/TX buffers in the FTDI chip. */
+	if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
+		sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
+		       ret, ftdi_get_error_string(devc->ftdic));
+		goto err_open_close_ftdic;
+	}
+	sr_dbg("FTDI chip buffers purged successfully.");
+
+	/* Reset the FTDI bitmode. */
+	ret = ftdi_set_bitmode(devc->ftdic, 0xff, BITMODE_RESET);
+	if (ret < 0) {
+		sr_err("Failed to reset the FTDI chip bitmode (%d): %s.",
+		       ret, ftdi_get_error_string(devc->ftdic));
+		goto err_open_close_ftdic;
+	}
+	sr_dbg("FTDI chip bitmode reset successfully.");
+
+	/* Set the FTDI latency timer to 16. */
+	ret = ftdi_set_latency_timer(devc->ftdic, 16);
+	if (ret < 0) {
+		sr_err("Failed to set FTDI latency timer (%d): %s.",
+		       ret, ftdi_get_error_string(devc->ftdic));
+		goto err_open_close_ftdic;
+	}
+	sr_dbg("FTDI chip latency timer set successfully.");
+
+	/* Set the FTDI read data chunk size to 64kB. */
+	ret = ftdi_read_data_set_chunksize(devc->ftdic, 64 * 1024);
+	if (ret < 0) {
+		sr_err("Failed to set FTDI read data chunk size (%d): %s.",
+		       ret, ftdi_get_error_string(devc->ftdic));
+		goto err_open_close_ftdic;
+	}
+	sr_dbg("FTDI chip read data chunk size set successfully.");
+	
+	return SR_OK;
+
+err_open_close_ftdic:
+	ftdi_usb_close(devc->ftdic);
+	return SR_ERR;
+}
+
+SR_PRIV int p_ols_close(struct dev_context *devc)
+{
+	int ret;
+
+	/* Note: Caller checks devc and devc->ftdic. */
+
+	if ((ret = ftdi_usb_close(devc->ftdic)) < 0) {
+		sr_err("Failed to close FTDI device (%d): %s.",
+		       ret, ftdi_get_error_string(devc->ftdic));
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+/* Configures the channel mask based on which channels are enabled. */
+SR_PRIV void pols_channel_mask(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_channel *channel;
+	const GSList *l;
+
+	devc = sdi->priv;
+
+	devc->channel_mask = 0;
+	for (l = sdi->channels; l; l = l->next) {
+		channel = l->data;
+		if (channel->enabled)
+			devc->channel_mask |= 1 << channel->index;
+	}
+}
+
+SR_PRIV int pols_convert_trigger(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_trigger *trigger;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	const GSList *l, *m;
+	int i;
+
+	devc = sdi->priv;
+
+	devc->num_stages = 0;
+	for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
+		devc->trigger_mask[i] = 0;
+		devc->trigger_value[i] = 0;
+		devc->trigger_edge[i] = 0;
+	}
+
+	if (!(trigger = sr_session_trigger_get(sdi->session)))
+		return SR_OK;
+
+	devc->num_stages = g_slist_length(trigger->stages);
+	if (devc->num_stages > NUM_TRIGGER_STAGES) {
+		sr_err("This device only supports %d trigger stages.",
+				NUM_TRIGGER_STAGES);
+		return SR_ERR;
+	}
+
+	for (l = trigger->stages; l; l = l->next) {
+		stage = l->data;
+		for (m = stage->matches; m; m = m->next) {
+			match = m->data;
+			if (!match->channel->enabled)
+				/* Ignore disabled channels with a trigger. */
+				continue;
+			devc->trigger_mask[stage->stage] |= 1 << match->channel->index;
+			if (match->match == SR_TRIGGER_ONE || match->match == SR_TRIGGER_RISING)
+				devc->trigger_value[stage->stage] |= 1 << match->channel->index;
+			if (match->match == SR_TRIGGER_RISING || match->match == SR_TRIGGER_FALLING)
+				devc->trigger_edge[stage->stage] |= 1 << match->channel->index;
+		}
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_inst *p_ols_get_metadata(uint8_t *buf, int bytes_read, struct dev_context *devc)
+{
+	struct sr_dev_inst *sdi;
+	uint32_t tmp_int, ui;
+	uint8_t key, type, token;
+	GString *tmp_str, *devname, *version;
+	guchar tmp_c;
+	int index, i;
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->driver = &p_ols_driver_info;
+	sdi->priv = devc;
+
+	devname = g_string_new("");
+	version = g_string_new("");
+
+	index = 0;
+	while (index < bytes_read) {
+		key = buf[index++];
+		if (key == 0x00) {
+			sr_dbg("Got metadata key 0x00, metadata ends.");
+			break;
+		}
+		type = key >> 5;
+		token = key & 0x1f;
+		switch (type) {
+		case 0:
+			/* NULL-terminated string */
+			tmp_str = g_string_new("");
+			while ((index < bytes_read) && ((tmp_c = buf[index++]) != '\0'))
+				g_string_append_c(tmp_str, tmp_c);
+			sr_dbg("Got metadata key 0x%.2x value '%s'.",
+			       key, tmp_str->str);
+			switch (token) {
+			case 0x01:
+				/* Device name */
+				devname = g_string_append(devname, tmp_str->str);
+				break;
+			case 0x02:
+				/* FPGA firmware version */
+				if (version->len)
+					g_string_append(version, ", ");
+				g_string_append(version, "FPGA version ");
+				g_string_append(version, tmp_str->str);
+				break;
+			case 0x03:
+				/* Ancillary version */
+				if (version->len)
+					g_string_append(version, ", ");
+				g_string_append(version, "Ancillary version ");
+				g_string_append(version, tmp_str->str);
+				break;
+			default:
+				sr_info("Unknown token 0x%.2x: '%s'",
+					token, tmp_str->str);
+				break;
+			}
+			g_string_free(tmp_str, TRUE);
+			break;
+		case 1:
+			/* 32-bit unsigned integer */
+			tmp_int = 0;
+			for (i = 0; i < 4; i++) {
+				tmp_int = (tmp_int << 8) | buf[index++];
+			}
+			sr_dbg("Got metadata key 0x%.2x value 0x%.8x.",
+			       key, tmp_int);
+			switch (token) {
+			case 0x00:
+				/* Number of usable channels */
+				for (ui = 0; ui < tmp_int; ui++)
+					sr_channel_new(sdi, ui, SR_CHANNEL_LOGIC, TRUE,
+							p_ols_channel_names[ui]);
+				break;
+			case 0x01:
+				/* Amount of sample memory available (bytes) */
+				devc->max_samplebytes = tmp_int;
+				break;
+			case 0x02:
+				/* Amount of dynamic memory available (bytes) */
+				/* what is this for? */
+				break;
+			case 0x03:
+				/* Maximum sample rate (Hz) */
+				devc->max_samplerate = tmp_int;
+				break;
+			case 0x04:
+				/* protocol version */
+				devc->protocol_version = tmp_int;
+				break;
+			default:
+				sr_info("Unknown token 0x%.2x: 0x%.8x.",
+					token, tmp_int);
+				break;
+			}
+			break;
+		case 2:
+			/* 8-bit unsigned integer */
+			tmp_c = buf[index++];
+			sr_dbg("Got metadata key 0x%.2x value 0x%.2x.",
+			       key, tmp_c);
+			switch (token) {
+			case 0x00:
+				/* Number of usable channels */
+				for (ui = 0; ui < tmp_c; ui++)
+					sr_channel_new(sdi, ui, SR_CHANNEL_LOGIC, TRUE,
+							p_ols_channel_names[ui]);
+				break;
+			case 0x01:
+				/* protocol version */
+				devc->protocol_version = tmp_c;
+				break;
+			default:
+				sr_info("Unknown token 0x%.2x: 0x%.2x.",
+					token, tmp_c);
+				break;
+			}
+			break;
+		default:
+			/* unknown type */
+			break;
+		}
+	}
+
+	sdi->model = devname->str;
+	sdi->version = version->str;
+	g_string_free(devname, FALSE);
+	g_string_free(version, FALSE);
+
+	return sdi;
+}
+
+SR_PRIV int p_ols_set_samplerate(const struct sr_dev_inst *sdi,
+		const uint64_t samplerate)
+{
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+	if (devc->max_samplerate && samplerate > devc->max_samplerate)
+		return SR_ERR_SAMPLERATE;
+
+	if (samplerate > CLOCK_RATE) {
+		sr_info("Enabling demux mode.");
+		devc->flag_reg |= FLAG_DEMUX;
+		devc->flag_reg &= ~FLAG_FILTER;
+		devc->max_channels = NUM_CHANNELS / 2;
+		devc->cur_samplerate_divider = (CLOCK_RATE * 2 / samplerate) - 1;
+	} else {
+		sr_info("Disabling demux mode.");
+		devc->flag_reg &= ~FLAG_DEMUX;
+		devc->flag_reg |= FLAG_FILTER;
+		devc->max_channels = NUM_CHANNELS;
+		devc->cur_samplerate_divider = (CLOCK_RATE / samplerate) - 1;
+	}
+
+	/* Calculate actual samplerate used and complain if it is different
+	 * from the requested.
+	 */
+	devc->cur_samplerate = CLOCK_RATE / (devc->cur_samplerate_divider + 1);
+	if (devc->flag_reg & FLAG_DEMUX)
+		devc->cur_samplerate *= 2;
+	if (devc->cur_samplerate != samplerate)
+		sr_info("Can't match samplerate %" PRIu64 ", using %"
+		       PRIu64 ".", samplerate, devc->cur_samplerate);
+
+	return SR_OK;
+}
+
+SR_PRIV int p_ols_receive_data(int fd, int revents, void *cb_data)
+{
+	struct dev_context *devc;
+	struct sr_dev_inst *sdi;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_logic logic;
+	uint32_t sample;
+	int num_channels, offset, j;
+	int bytes_read, index;
+	unsigned int i;
+	unsigned char byte;
+
+	(void)fd;
+	(void)revents;
+
+	sdi = cb_data;
+	devc = sdi->priv;
+
+	if (devc->num_transfers++ == 0) {
+		devc->raw_sample_buf = g_try_malloc(devc->limit_samples * 4);
+		if (!devc->raw_sample_buf) {
+			sr_err("Sample buffer malloc failed.");
+			return FALSE;
+		}
+		/* fill with 1010... for debugging */
+		memset(devc->raw_sample_buf, 0x82, devc->limit_samples * 4);
+	}
+
+	if ((devc->num_samples < devc->limit_samples) && (devc->cnt_samples < devc->max_samples)) {
+
+		num_channels = 0;
+		for (i = NUM_CHANNELS; i > 0x02; i /= 2) {
+			if ((devc->flag_reg & i) == 0) {
+				num_channels++;
+			}
+		}
+
+		/* Get a block of data. */
+		bytes_read = ftdi_read_data(devc->ftdic, devc->ftdi_buf, FTDI_BUF_SIZE);
+		if (bytes_read < 0) {
+			sr_err("Failed to read FTDI data (%d): %s.",
+			       bytes_read, ftdi_get_error_string(devc->ftdic));
+			sdi->driver->dev_acquisition_stop(sdi, sdi);
+			return FALSE;
+		}
+		if (bytes_read == 0) {
+			sr_spew("Received 0 bytes, nothing to do.");
+			return TRUE;
+		}
+
+		sr_dbg("Received %d bytes", bytes_read);
+
+		index = 0;
+		while (index < bytes_read) {
+			byte = devc->ftdi_buf[index++];
+			devc->cnt_bytes++;
+
+			devc->sample[devc->num_bytes++] = byte;
+			sr_spew("Received byte 0x%.2x.", byte);
+
+			if ((devc->flag_reg & FLAG_DEMUX) && (devc->flag_reg & FLAG_RLE)) {
+				/* RLE in demux mode must be processed differently 
+				* since in this case the RLE encoder is operating on pairs of samples.
+				*/
+				if (devc->num_bytes == num_channels * 2) {
+					devc->cnt_samples += 2;
+					devc->cnt_samples_rle += 2;
+					/*
+					 * Got a sample pair. Convert from the OLS's little-endian
+					 * sample to the local format.
+					 */
+					sample = devc->sample[0] | (devc->sample[1] << 8) \
+							| (devc->sample[2] << 16) | (devc->sample[3] << 24);
+					sr_spew("Received sample pair 0x%.*x.", devc->num_bytes * 2, sample);
+
+					/*
+					 * In RLE mode the high bit of the sample pair is the
+					 * "count" flag, meaning this sample pair is the number
+					 * of times the previous sample pair occurred.
+					 */
+					if (devc->sample[devc->num_bytes - 1] & 0x80) {
+						/* Clear the high bit. */
+						sample &= ~(0x80 << (devc->num_bytes - 1) * 8);
+						devc->rle_count = sample;
+						devc->cnt_samples_rle += devc->rle_count * 2;
+						sr_dbg("RLE count: %u.", devc->rle_count * 2);
+						devc->num_bytes = 0;
+						continue;
+					}
+					devc->num_samples += (devc->rle_count + 1) * 2;
+					if (devc->num_samples > devc->limit_samples) {
+						/* Save us from overrunning the buffer. */
+						devc->rle_count -= (devc->num_samples - devc->limit_samples) / 2;
+						devc->num_samples = devc->limit_samples;
+						index = bytes_read;
+					}
+
+					/*
+					 * Some channel groups may have been turned
+					 * off, to speed up transfer between the
+					 * hardware and the PC. Expand that here before
+					 * submitting it over the session bus --
+					 * whatever is listening on the bus will be
+					 * expecting a full 32-bit sample, based on
+					 * the number of channels.
+					 */
+					j = 0;
+					/* expand first sample */
+					memset(devc->tmp_sample, 0, 4);
+					for (i = 0; i < 2; i++) {
+						if (((devc->flag_reg >> 2) & (1 << i)) == 0) {
+							/*
+							 * This channel group was
+							 * enabled, copy from received
+							 * sample.
+							 */
+							devc->tmp_sample[i] = devc->sample[j++];
+						} 
+					}
+					/* Clear out the most significant bit of the sample */
+					devc->tmp_sample[devc->num_bytes - 1] &= 0x7f;
+					sr_spew("Expanded sample 1: 0x%.2x%.2x%.2x%.2x.",
+						devc->tmp_sample[3], devc->tmp_sample[2],
+						devc->tmp_sample[1], devc->tmp_sample[0]);
+
+					/* expand second sample */
+					memset(devc->tmp_sample2, 0, 4);
+					for (i = 0; i < 2; i++) {
+						if (((devc->flag_reg >> 2) & (1 << i)) == 0) {
+							/*
+							 * This channel group was
+							 * enabled, copy from received
+							 * sample.
+							 */
+							devc->tmp_sample2[i] = devc->sample[j++];
+						} 
+					}
+					/* Clear out the most significant bit of the sample */
+					devc->tmp_sample2[devc->num_bytes - 1] &= 0x7f;
+					sr_spew("Expanded sample 2: 0x%.2x%.2x%.2x%.2x.",
+						devc->tmp_sample2[3], devc->tmp_sample2[2],
+						devc->tmp_sample2[1], devc->tmp_sample2[0]);
+
+					/*
+					 * OLS sends its sample buffer backwards.
+					 * store it in reverse order here, so we can dump
+					 * this on the session bus later.
+					 */
+					offset = (devc->limit_samples - devc->num_samples) * 4;
+					for (i = 0; i <= devc->rle_count; i++) {
+						memcpy(devc->raw_sample_buf + offset + (i * 8),
+									 devc->tmp_sample2, 4);
+						memcpy(devc->raw_sample_buf + offset + (4 + (i * 8)),
+									 devc->tmp_sample, 4);
+					}
+					memset(devc->sample, 0, 4);
+					devc->num_bytes = 0;
+					devc->rle_count = 0;
+				}
+			}
+			else {
+				if (devc->num_bytes == num_channels) {
+					devc->cnt_samples++;
+					devc->cnt_samples_rle++;
+					/*
+					 * Got a full sample. Convert from the OLS's little-endian
+					 * sample to the local format.
+					 */
+					sample = devc->sample[0] | (devc->sample[1] << 8) \
+							| (devc->sample[2] << 16) | (devc->sample[3] << 24);
+					sr_spew("Received sample 0x%.*x.", devc->num_bytes * 2, sample);
+					if (devc->flag_reg & FLAG_RLE) {
+						/*
+						 * In RLE mode the high bit of the sample is the
+						 * "count" flag, meaning this sample is the number
+						 * of times the previous sample occurred.
+						 */
+						if (devc->sample[devc->num_bytes - 1] & 0x80) {
+							/* Clear the high bit. */
+							sample &= ~(0x80 << (devc->num_bytes - 1) * 8);
+							devc->rle_count = sample;
+							devc->cnt_samples_rle += devc->rle_count;
+							sr_dbg("RLE count: %u.", devc->rle_count);
+							devc->num_bytes = 0;
+							continue;
+						}
+					}
+					devc->num_samples += devc->rle_count + 1;
+					if (devc->num_samples > devc->limit_samples) {
+						/* Save us from overrunning the buffer. */
+						devc->rle_count -= devc->num_samples - devc->limit_samples;
+						devc->num_samples = devc->limit_samples;
+						index = bytes_read;
+					}
+
+					if (num_channels < 4) {
+						/*
+						 * Some channel groups may have been turned
+						 * off, to speed up transfer between the
+						 * hardware and the PC. Expand that here before
+						 * submitting it over the session bus --
+						 * whatever is listening on the bus will be
+						 * expecting a full 32-bit sample, based on
+						 * the number of channels.
+						 */
+						j = 0;
+						memset(devc->tmp_sample, 0, 4);
+						for (i = 0; i < 4; i++) {
+							if (((devc->flag_reg >> 2) & (1 << i)) == 0) {
+								/*
+								 * This channel group was
+								 * enabled, copy from received
+								 * sample.
+								 */
+								devc->tmp_sample[i] = devc->sample[j++];
+							} 
+						}
+						memcpy(devc->sample, devc->tmp_sample, 4);
+						sr_spew("Expanded sample: 0x%.8x.", sample);
+					}
+
+					/*
+					 * Pipistrello OLS sends its sample buffer backwards.
+					 * store it in reverse order here, so we can dump
+					 * this on the session bus later.
+					 */
+					offset = (devc->limit_samples - devc->num_samples) * 4;
+					for (i = 0; i <= devc->rle_count; i++) {
+						memcpy(devc->raw_sample_buf + offset + (i * 4),
+									 devc->sample, 4);
+					}
+					memset(devc->sample, 0, 4);
+					devc->num_bytes = 0;
+					devc->rle_count = 0;
+				}
+			}
+		}
+		return TRUE;
+	} else {
+		do {
+			bytes_read = ftdi_read_data(devc->ftdic, devc->ftdi_buf, FTDI_BUF_SIZE);
+		} while (bytes_read > 0);
+
+		/*
+		 * We've acquired all the samples we asked for -- we're done.
+		 * Send the (properly-ordered) buffer to the frontend.
+		 */
+		sr_dbg("Received %d bytes, %d samples, %d decompressed samples.",
+				devc->cnt_bytes, devc->cnt_samples,
+				devc->cnt_samples_rle);
+		if (devc->trigger_at != -1) {
+			/*
+			 * A trigger was set up, so we need to tell the frontend
+			 * about it.
+			 */
+			if (devc->trigger_at > 0) {
+				/* There are pre-trigger samples, send those first. */
+				packet.type = SR_DF_LOGIC;
+				packet.payload = &logic;
+				logic.length = devc->trigger_at * 4;
+				logic.unitsize = 4;
+				logic.data = devc->raw_sample_buf +
+					(devc->limit_samples - devc->num_samples) * 4;
+				sr_session_send(cb_data, &packet);
+			}
+
+			/* Send the trigger. */
+			packet.type = SR_DF_TRIGGER;
+			sr_session_send(cb_data, &packet);
+
+			/* Send post-trigger samples. */
+			packet.type = SR_DF_LOGIC;
+			packet.payload = &logic;
+			logic.length = (devc->num_samples * 4) - (devc->trigger_at * 4);
+			logic.unitsize = 4;
+			logic.data = devc->raw_sample_buf + devc->trigger_at * 4 +
+				(devc->limit_samples - devc->num_samples) * 4;
+			sr_session_send(cb_data, &packet);
+		} else {
+			/* no trigger was used */
+			packet.type = SR_DF_LOGIC;
+			packet.payload = &logic;
+			logic.length = devc->num_samples * 4;
+			logic.unitsize = 4;
+			logic.data = devc->raw_sample_buf +
+				(devc->limit_samples - devc->num_samples) * 4;
+			sr_session_send(cb_data, &packet);
+		}
+		g_free(devc->raw_sample_buf);
+
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+	}
+
+	return TRUE;
+}
diff --git a/hardware/openbench-logic-sniffer/protocol.h b/src/hardware/pipistrello-ols/protocol.h
similarity index 62%
rename from hardware/openbench-logic-sniffer/protocol.h
rename to src/hardware/pipistrello-ols/protocol.h
index 98831b5..c2bd2cc 100644
--- a/hardware/openbench-logic-sniffer/protocol.h
+++ b/src/hardware/pipistrello-ols/protocol.h
@@ -17,24 +17,30 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef LIBSIGROK_HARDWARE_OPENBENCH_LOGIC_SNIFFER_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_OPENBENCH_LOGIC_SNIFFER_PROTOCOL_H
+#ifndef LIBSIGROK_HARDWARE_PIPISTRELLO_OLS_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_PIPISTRELLO_OLS_PROTOCOL_H
 
 #include <stdint.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <ftdi.h>
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-#define LOG_PREFIX "ols"
+#define LOG_PREFIX "p-ols"
 
-#define NUM_CHANNELS             32
+#define USB_VENDOR_ID		0x0403
+#define USB_DEVICE_ID		0x6010
+#define USB_VENDOR_NAME		"Saanlima"
+#define USB_IPRODUCT		"Pipistrello LX45"
+
+#define FTDI_BUF_SIZE          (16 * 1024)
+
+#define NUM_CHANNELS           32
 #define NUM_TRIGGER_STAGES     4
-#define TRIGGER_TYPE           "01"
-#define SERIAL_SPEED           B115200
 #define CLOCK_RATE             SR_MHZ(100)
 #define MIN_NUM_SAMPLES        4
-#define DEFAULT_SAMPLERATE     SR_KHZ(200)
+#define DEFAULT_SAMPLERATE     SR_MHZ(100)
 
 /* Command opcodes */
 #define CMD_RESET                  0x00
@@ -42,12 +48,14 @@
 #define CMD_TESTMODE               0x03
 #define CMD_ID                     0x02
 #define CMD_METADATA               0x04
-#define CMD_SET_FLAGS              0x82
 #define CMD_SET_DIVIDER            0x80
-#define CMD_CAPTURE_SIZE           0x81
+#define CMD_SET_FLAGS              0x82
+#define CMD_CAPTURE_COUNT          0x83
+#define CMD_CAPTURE_DELAY          0x84
 #define CMD_SET_TRIGGER_MASK       0xc0
 #define CMD_SET_TRIGGER_VALUE      0xc1
 #define CMD_SET_TRIGGER_CONFIG     0xc2
+#define CMD_SET_TRIGGER_EDGE       0xc3
 
 /* Trigger config */
 #define TRIGGER_START              (1 << 3)
@@ -56,7 +64,7 @@
 /* 12-13 unused, 14-15 RLE mode (we hardcode mode 0). */
 #define FLAG_INTERNAL_TEST_MODE    (1 << 11)
 #define FLAG_EXTERNAL_TEST_MODE    (1 << 10)
-#define FLAG_SWAP_CHANNELS           (1 << 9)
+#define FLAG_SWAP_CHANNELS         (1 << 9)
 #define FLAG_RLE                   (1 << 8)
 #define FLAG_SLOPE_FALLING         (1 << 7)
 #define FLAG_CLOCK_EXTERNAL        (1 << 6)
@@ -69,21 +77,27 @@
 
 /* Private, per-device-instance driver context. */
 struct dev_context {
+	/** FTDI device context (used by libftdi). */
+	struct ftdi_context *ftdic;
+	uint8_t *ftdi_buf;
+
 	/* Fixed device settings */
 	int max_channels;
-	uint32_t max_samples;
+	uint32_t max_samplebytes;
 	uint32_t max_samplerate;
 	uint32_t protocol_version;
 
 	/* Acquisition settings */
 	uint64_t cur_samplerate;
 	uint32_t cur_samplerate_divider;
+	uint32_t max_samples;
 	uint64_t limit_samples;
 	int capture_ratio;
 	int trigger_at;
 	uint32_t channel_mask;
-	uint32_t trigger_mask[4];
-	uint32_t trigger_value[4];
+	uint32_t trigger_mask[NUM_TRIGGER_STAGES];
+	uint32_t trigger_value[NUM_TRIGGER_STAGES];
+	uint32_t trigger_edge[NUM_TRIGGER_STAGES];
 	int num_stages;
 	uint16_t flag_reg;
 
@@ -92,29 +106,26 @@ struct dev_context {
 	unsigned int num_samples;
 	int num_bytes;
 	int cnt_bytes;
-	int cnt_samples;
+	unsigned int cnt_samples;
 	int cnt_samples_rle;
 
 	/* Temporary variables */
 	unsigned int rle_count;
 	unsigned char sample[4];
 	unsigned char tmp_sample[4];
+	unsigned char tmp_sample2[4];
 	unsigned char *raw_sample_buf;
 };
 
-
-SR_PRIV extern const char *ols_channel_names[NUM_CHANNELS + 1];
-
-SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial,
-		uint8_t command);
-SR_PRIV int send_longcommand(struct sr_serial_dev_inst *serial,
-		uint8_t command, uint8_t *data);
-SR_PRIV int ols_configure_channels(const struct sr_dev_inst *sdi);
-SR_PRIV struct dev_context *ols_dev_new(void);
-SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial);
-SR_PRIV int ols_set_samplerate(const struct sr_dev_inst *sdi,
-		uint64_t samplerate);
-SR_PRIV void abort_acquisition(const struct sr_dev_inst *sdi);
-SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV extern const char *p_ols_channel_names[];
+SR_PRIV int write_shortcommand(struct dev_context *devc, uint8_t command);
+SR_PRIV int write_longcommand(struct dev_context *devc, uint8_t command, uint8_t *data);
+SR_PRIV int p_ols_open(struct dev_context *devc);
+SR_PRIV int p_ols_close(struct dev_context *devc);
+SR_PRIV void pols_channel_mask(const struct sr_dev_inst *sdi);
+SR_PRIV int pols_convert_trigger(const struct sr_dev_inst *sdi);
+SR_PRIV struct sr_dev_inst *p_ols_get_metadata(uint8_t *buf, int bytes_read, struct dev_context *devc);
+SR_PRIV int p_ols_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
+SR_PRIV int p_ols_receive_data(int fd, int revents, void *cb_data);
 
 #endif
diff --git a/hardware/rigol-ds/api.c b/src/hardware/rigol-ds/api.c
similarity index 74%
rename from hardware/rigol-ds/api.c
rename to src/hardware/rigol-ds/api.c
index 4650010..1f784cf 100644
--- a/hardware/rigol-ds/api.c
+++ b/src/hardware/rigol-ds/api.c
@@ -19,37 +19,40 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
+#include "scpi.h"
 #include "protocol.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_OSCILLOSCOPE,
-	SR_CONF_TIMEBASE,
-	SR_CONF_TRIGGER_SOURCE,
-	SR_CONF_TRIGGER_SLOPE,
-	SR_CONF_HORIZ_TRIGGERPOS,
-	SR_CONF_NUM_TIMEBASE,
-	SR_CONF_LIMIT_FRAMES,
-	SR_CONF_SAMPLERATE,
+	SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
+	SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_SET,
+	SR_CONF_NUM_HDIV | SR_CONF_GET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET,
+	SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const int32_t analog_hwcaps[] = {
-	SR_CONF_NUM_VDIV,
-	SR_CONF_VDIV,
-	SR_CONF_COUPLING,
-	SR_CONF_DATA_SOURCE,
+static const uint32_t analog_devopts[] = {
+	SR_CONF_NUM_VDIV | SR_CONF_GET,
+	SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
 static const uint64_t timebases[][2] = {
@@ -113,6 +116,9 @@ static const uint64_t vdivs[][2] = {
 	{ 2, 1 },
 	{ 5, 1 },
 	{ 10, 1 },
+	{ 20, 1 },
+	{ 50, 1 },
+	{ 100, 1 },
 };
 
 #define NUM_TIMEBASE  ARRAY_SIZE(timebases)
@@ -172,12 +178,13 @@ enum series {
 	DS2000,
 	DS2000A,
 	DSO1000,
+	DS1000Z,
 };
 
 /* short name, full name */
 static const struct rigol_ds_vendor supported_vendors[] = {
 	[RIGOL] = {"Rigol", "Rigol Technologies"},
-	[AGILENT] = {"Agilent", "Rigol Technologies"},
+	[AGILENT] = {"Agilent", "Agilent Technologies"},
 };
 
 #define VENDOR(x) &supported_vendors[x]
@@ -189,11 +196,13 @@ static const struct rigol_ds_series supported_series[] = {
 	[DS1000] = {VENDOR(RIGOL), "DS1000", PROTOCOL_V2, FORMAT_IEEE488_2,
 		{50, 1}, {2, 1000}, 12, 600, 1048576},
 	[DS2000] = {VENDOR(RIGOL), "DS2000", PROTOCOL_V3, FORMAT_IEEE488_2,
-		{500, 1}, {2, 1000}, 14, 1400, 14000},
+		{500, 1}, {500, 1000000}, 14, 1400, 14000},
 	[DS2000A] = {VENDOR(RIGOL), "DS2000A", PROTOCOL_V3, FORMAT_IEEE488_2,
 		{1000, 1}, {500, 1000000}, 14, 1400, 14000},
 	[DSO1000] = {VENDOR(AGILENT), "DSO1000", PROTOCOL_V3, FORMAT_IEEE488_2,
 		{50, 1}, {2, 1000}, 12, 600, 20480},
+	[DS1000Z] = {VENDOR(RIGOL), "DS1000Z", PROTOCOL_V4, FORMAT_IEEE488_2,
+		{50, 1}, {1, 1000}, 12, 1200, 12000000},
 };
 
 #define SERIES(x) &supported_series[x]
@@ -229,33 +238,41 @@ static const struct rigol_ds_model supported_models[] = {
 	{SERIES(DSO1000), "DSO1014A", {2, 1000000000}, 4, false},
 	{SERIES(DSO1000), "DSO1022A", {2, 1000000000}, 2, false},
 	{SERIES(DSO1000), "DSO1024A", {2, 1000000000}, 4, false},
+	{SERIES(DS1000Z), "DS1054Z", {5, 1000000000}, 4, false},
+	{SERIES(DS1000Z), "DS1074Z", {5, 1000000000}, 4, false},
+	{SERIES(DS1000Z), "DS1104Z", {5, 1000000000}, 4, false},
+	{SERIES(DS1000Z), "DS1074Z-S", {5, 1000000000}, 4, false},
+	{SERIES(DS1000Z), "DS1104Z-S", {5, 1000000000}, 4, false},
+	{SERIES(DS1000Z), "MSO1074Z", {5, 1000000000}, 4, true},
+	{SERIES(DS1000Z), "MSO1104Z", {5, 1000000000}, 4, true},
+	{SERIES(DS1000Z), "MSO1074Z-S", {5, 1000000000}, 4, true},
+	{SERIES(DS1000Z), "MSO1104Z-S", {5, 1000000000}, 4, true},
 };
 
 SR_PRIV struct sr_dev_driver rigol_ds_driver_info;
-static struct sr_dev_driver *di = &rigol_ds_driver_info;
 
 static void clear_helper(void *priv)
 {
 	struct dev_context *devc;
+	unsigned int i;
 
 	devc = priv;
 	g_free(devc->data);
 	g_free(devc->buffer);
-	g_free(devc->coupling[0]);
-	g_free(devc->coupling[1]);
+	for (i = 0; i < ARRAY_SIZE(devc->coupling); i++)
+		g_free(devc->coupling[i]);
 	g_free(devc->trigger_source);
 	g_free(devc->trigger_slope);
-	g_slist_free(devc->analog_groups[0].channels);
-	g_slist_free(devc->analog_groups[1].channels);
-	g_slist_free(devc->digital_group.channels);
+	g_free(devc->analog_groups);
+	g_free(devc);
 }
 
-static int dev_clear(void)
+static int dev_clear(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, clear_helper);
 }
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
@@ -282,7 +299,7 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 	}
 
 	for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
-		if (!strcasecmp(hw_info->manufacturer,
+		if (!g_ascii_strcasecmp(hw_info->manufacturer,
 					supported_models[i].series->vendor->full_name) &&
 				!strcmp(hw_info->model, supported_models[i].name)) {
 			model = &supported_models[i];
@@ -290,22 +307,20 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 		}
 	}
 
-	if (!model || !(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
-					      model->series->vendor->name,
-						  model->name,
-						  hw_info->firmware_version))) {
+	if (!model) {
 		sr_scpi_hw_info_free(hw_info);
 		return NULL;
 	}
 
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->vendor = g_strdup(model->series->vendor->name);
+	sdi->model = g_strdup(model->name);
+	sdi->version = g_strdup(hw_info->firmware_version);
 	sdi->conn = scpi;
-
-	sdi->driver = di;
+	sdi->driver = &rigol_ds_driver_info;
 	sdi->inst_type = SR_INST_SCPI;
-
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
-		return NULL;
-
+	sdi->serial_num = g_strdup(hw_info->serial_number);
+	devc = g_malloc0(sizeof(struct dev_context));
 	devc->limit_frames = 0;
 	devc->model = model;
 	devc->format = model->series->format;
@@ -324,44 +339,46 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 			}
 			if (i != 3)
 				break;
-			if (n[0] != 0 || n[1] > 2)
-				break;
-			if (n[1] == 2 && n[2] > 3)
-				break;
-			sr_dbg("Found DS1000 firmware < 0.2.4, using raw data format.");
-			devc->format = FORMAT_RAW;
-		} while(0);
+			scpi->firmware_version = n[0] * 100 + n[1] * 10 + n[2];
+			if (scpi->firmware_version < 24) {
+				sr_dbg("Found DS1000 firmware < 0.2.4, using raw data format.");
+				devc->format = FORMAT_RAW;
+			}
+			break;
+		} while (0);
 		g_strfreev(version);
 	}
 
 	sr_scpi_hw_info_free(hw_info);
 
+	devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) *
+					model->analog_channels);
+
 	for (i = 0; i < model->analog_channels; i++) {
-		if (!(channel_name = g_strdup_printf("CH%d", i + 1)))
-			return NULL;
-		ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channel_name);
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		devc->analog_groups[i].name = channel_name;
-		devc->analog_groups[i].channels = g_slist_append(NULL, ch);
+		channel_name = g_strdup_printf("CH%d", i + 1);
+		ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_name);
+
+		devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
+
+		devc->analog_groups[i]->name = channel_name;
+		devc->analog_groups[i]->channels = g_slist_append(NULL, ch);
 		sdi->channel_groups = g_slist_append(sdi->channel_groups,
-				&devc->analog_groups[i]);
+				devc->analog_groups[i]);
 	}
 
 	if (devc->model->has_digital) {
-		for (i = 0; i < 16; i++) {
-			if (!(channel_name = g_strdup_printf("D%d", i)))
-				return NULL;
-			ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, channel_name);
+		devc->digital_group = g_malloc0(sizeof(struct sr_channel_group));
+
+		for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+			channel_name = g_strdup_printf("D%d", i);
+			ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
 			g_free(channel_name);
-			if (!ch)
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
-			devc->digital_group.channels = g_slist_append(
-					devc->digital_group.channels, ch);
+			devc->digital_group->channels = g_slist_append(
+					devc->digital_group->channels, ch);
 		}
-		devc->digital_group.name = "LA";
+		devc->digital_group->name = g_strdup("LA");
 		sdi->channel_groups = g_slist_append(sdi->channel_groups,
-				&devc->digital_group);
+				devc->digital_group);
 	}
 
 	for (i = 0; i < NUM_TIMEBASE; i++) {
@@ -371,14 +388,16 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 			devc->num_timebases = &timebases[i] - devc->timebases + 1;
 	}
 
-	for (i = 0; i < NUM_VDIV; i++)
-		if (!memcmp(&devc->model->series->min_vdiv, &vdivs[i], sizeof(uint64_t[2])))
+	for (i = 0; i < NUM_VDIV; i++) {
+		if (!memcmp(&devc->model->series->min_vdiv,
+					&vdivs[i], sizeof(uint64_t[2]))) {
 			devc->vdivs = &vdivs[i];
+			devc->num_vdivs = NUM_VDIV - i;
+		}
+	}
 
-	if (!(devc->buffer = g_try_malloc(ACQ_BUFFER_SIZE)))
-		return NULL;
-	if (!(devc->data = g_try_malloc(ACQ_BUFFER_SIZE * sizeof(float))))
-		return NULL;
+	devc->buffer = g_malloc(ACQ_BUFFER_SIZE);
+	devc->data = g_malloc(ACQ_BUFFER_SIZE * sizeof(float));
 
 	devc->data_source = DATA_SOURCE_LIVE;
 
@@ -387,25 +406,30 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 	return sdi;
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-	return sr_scpi_scan(di->priv, options, probe_device);
+	return sr_scpi_scan(di->context, options, probe_device);
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
+	int ret;
 	struct sr_scpi_dev_inst *scpi = sdi->conn;
 
-	if (sr_scpi_open(scpi) < 0)
+	if ((ret = sr_scpi_open(scpi)) < 0) {
+		sr_err("Failed to open SCPI device: %s.", sr_strerror(ret));
 		return SR_ERR;
+	}
 
-	if (rigol_ds_get_dev_cfg(sdi) != SR_OK)
+	if ((ret = rigol_ds_get_dev_cfg(sdi)) < 0) {
+		sr_err("Failed to get device config: %s.", sr_strerror(ret));
 		return SR_ERR;
+	}
 
 	sdi->status = SR_ST_ACTIVE;
 
@@ -435,9 +459,9 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
-	return dev_clear();
+	return dev_clear(di);
 }
 
 static int analog_frame_size(const struct sr_dev_inst *sdi)
@@ -480,7 +504,7 @@ static int digital_frame_size(const struct sr_dev_inst *sdi)
 	}
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -488,7 +512,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	const char *tmp_str;
 	uint64_t samplerate;
 	int analog_channel = -1;
-	float smallest_diff = 0.0000000001;
+	float smallest_diff = INFINITY;
 	int idx = -1;
 	unsigned i;
 
@@ -512,12 +536,13 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 		}
 	}
 
-	switch (id) {
-	case SR_CONF_NUM_TIMEBASE:
+	switch (key) {
+	case SR_CONF_NUM_HDIV:
 		*data = g_variant_new_int32(devc->model->series->num_horizontal_divs);
 		break;
 	case SR_CONF_NUM_VDIV:
-		*data = g_variant_new_int32(NUM_VDIV);
+		*data = g_variant_new_int32(devc->num_vdivs);
+		break;
 	case SR_CONF_DATA_SOURCE:
 		if (devc->data_source == DATA_SOURCE_LIVE)
 			*data = g_variant_new_string("Live");
@@ -532,6 +557,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 				(devc->timebase * devc->model->series->num_horizontal_divs);
 			*data = g_variant_new_uint64(samplerate);
 		} else {
+			sr_dbg("Unknown data source: %d.", devc->data_source);
 			return SR_ERR_NA;
 		}
 		break;
@@ -551,12 +577,14 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 		*data = g_variant_new_string(tmp_str);
 		break;
 	case SR_CONF_TRIGGER_SLOPE:
-		if (!strcmp(devc->trigger_slope, "POS"))
+		if (!strncmp(devc->trigger_slope, "POS", 3)) {
 			tmp_str = "r";
-		else if (!strcmp(devc->trigger_slope, "NEG"))
+		} else if (!strncmp(devc->trigger_slope, "NEG", 3)) {
 			tmp_str = "f";
-		else
+		} else {
+			sr_dbg("Unknown trigger slope: '%s'.", devc->trigger_slope);
 			return SR_ERR_NA;
+		}
 		*data = g_variant_new_string(tmp_str);
 		break;
 	case SR_CONF_TIMEBASE:
@@ -568,14 +596,18 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 				idx = i;
 			}
 		}
-		if (idx < 0)
+		if (idx < 0) {
+			sr_dbg("Negative timebase index: %d.", idx);
 			return SR_ERR_NA;
+		}
 		*data = g_variant_new("(tt)", devc->timebases[idx][0],
 		                              devc->timebases[idx][1]);
 		break;
 	case SR_CONF_VDIV:
-		if (analog_channel < 0)
+		if (analog_channel < 0) {
+			sr_dbg("Negative analog channel: %d.", analog_channel);
 			return SR_ERR_NA;
+		}
 		for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
 			float vdiv = (float)vdivs[i][0] / vdivs[i][1];
 			float diff = fabs(devc->vdiv[analog_channel] - vdiv);
@@ -584,13 +616,17 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 				idx = i;
 			}
 		}
-		if (idx < 0)
+		if (idx < 0) {
+			sr_dbg("Negative vdiv index: %d.", idx);
 			return SR_ERR_NA;
+		}
 		*data = g_variant_new("(tt)", vdivs[idx][0], vdivs[idx][1]);
 		break;
 	case SR_CONF_COUPLING:
-		if (analog_channel < 0)
+		if (analog_channel < 0) {
+			sr_dbg("Negative analog channel: %d.", analog_channel);
 			return SR_ERR_NA;
+		}
 		*data = g_variant_new_string(devc->coupling[analog_channel]);
 		break;
 	default:
@@ -600,7 +636,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -624,15 +660,18 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	}
 
 	ret = SR_OK;
-	switch (id) {
+	switch (key) {
 	case SR_CONF_LIMIT_FRAMES:
 		devc->limit_frames = g_variant_get_uint64(data);
 		break;
 	case SR_CONF_TRIGGER_SLOPE:
 		tmp_str = g_variant_get_string(data, NULL);
 
-		if (!tmp_str || !(tmp_str[0] == 'f' || tmp_str[0] == 'r'))
+		if (!tmp_str || !(tmp_str[0] == 'f' || tmp_str[0] == 'r')) {
+			sr_err("Unknown trigger slope: '%s'.",
+			       (tmp_str) ? tmp_str : "NULL");
 			return SR_ERR_ARG;
+		}
 
 		g_free(devc->trigger_slope);
 		devc->trigger_slope = g_strdup((tmp_str[0] == 'r') ? "POS" : "NEG");
@@ -640,8 +679,10 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		break;
 	case SR_CONF_HORIZ_TRIGGERPOS:
 		t_dbl = g_variant_get_double(data);
-		if (t_dbl < 0.0 || t_dbl > 1.0)
+		if (t_dbl < 0.0 || t_dbl > 1.0) {
+			sr_err("Invalid horiz. trigger position: %g.", t_dbl);
 			return SR_ERR;
+		}
 		devc->horiz_triggerpos = t_dbl;
 		/* We have the trigger offset as a percentage of the frame, but
 		 * need to express this in seconds. */
@@ -660,8 +701,10 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 				break;
 			}
 		}
-		if (i == devc->num_timebases)
+		if (i == devc->num_timebases) {
+			sr_err("Invalid timebase index: %d.", i);
 			ret = SR_ERR_ARG;
+		}
 		break;
 	case SR_CONF_TRIGGER_SOURCE:
 		tmp_str = g_variant_get_string(data, NULL);
@@ -685,8 +728,10 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 				break;
 			}
 		}
-		if (i == ARRAY_SIZE(trigger_sources))
+		if (i == ARRAY_SIZE(trigger_sources)) {
+			sr_err("Invalid trigger source index: %d.", i);
 			ret = SR_ERR_ARG;
+		}
 		break;
 	case SR_CONF_VDIV:
 		if (!cg) {
@@ -694,8 +739,8 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 			return SR_ERR_CHANNEL_GROUP;
 		}
 		g_variant_get(data, "(tt)", &p, &q);
-		for (i = 0; i < 2; i++) {
-			if (cg == &devc->analog_groups[i]) {
+		for (i = 0; i < devc->model->analog_channels; i++) {
+			if (cg == devc->analog_groups[i]) {
 				for (j = 0; j < ARRAY_SIZE(vdivs); j++) {
 					if (vdivs[j][0] != p || vdivs[j][1] != q)
 						continue;
@@ -705,9 +750,11 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 					return rigol_ds_config_set(sdi, ":CHAN%d:SCAL %s", i + 1,
 							buffer);
 				}
+				sr_err("Invalid vdiv index: %d.", j);
 				return SR_ERR_ARG;
 			}
 		}
+		sr_dbg("Didn't set vdiv, unknown channel(group).");
 		return SR_ERR_NA;
 	case SR_CONF_COUPLING:
 		if (!cg) {
@@ -715,8 +762,8 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 			return SR_ERR_CHANNEL_GROUP;
 		}
 		tmp_str = g_variant_get_string(data, NULL);
-		for (i = 0; i < 2; i++) {
-			if (cg == &devc->analog_groups[i]) {
+		for (i = 0; i < devc->model->analog_channels; i++) {
+			if (cg == devc->analog_groups[i]) {
 				for (j = 0; j < ARRAY_SIZE(coupling); j++) {
 					if (!strcmp(tmp_str, coupling[j])) {
 						g_free(devc->coupling[i]);
@@ -725,9 +772,11 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 								devc->coupling[i]);
 					}
 				}
+				sr_err("Invalid coupling index: %d.", j);
 				return SR_ERR_ARG;
 			}
 		}
+		sr_dbg("Didn't set coupling, unknown channel(group).");
 		return SR_ERR_NA;
 	case SR_CONF_DATA_SOURCE:
 		tmp_str = g_variant_get_string(data, NULL);
@@ -739,18 +788,19 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		else if (devc->model->series->protocol >= PROTOCOL_V3
 			 && !strcmp(tmp_str, "Segmented"))
 			devc->data_source = DATA_SOURCE_SEGMENTED;
-		else
+		else {
+			sr_err("Unknown data source: '%s'.", tmp_str);
 			return SR_ERR;
+		}
 		break;
 	default:
-		ret = SR_ERR_NA;
-		break;
+		return SR_ERR_NA;
 	}
 
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	GVariant *tuple, *rational[2];
@@ -758,16 +808,13 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	unsigned int i;
 	struct dev_context *devc = NULL;
 
-	if (sdi)
-		devc = sdi->priv;
-
 	if (key == SR_CONF_SCAN_OPTIONS) {
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		return SR_OK;
-	} else if (key == SR_CONF_DEVICE_OPTIONS && cg == NULL) {
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-			hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+	} else if (key == SR_CONF_DEVICE_OPTIONS && !cg) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		return SR_OK;
 	}
 
@@ -776,12 +823,9 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		return SR_ERR_ARG;
 
 	/* If a channel group is specified, it must be a valid one. */
-	if (cg) {
-		if (cg != &devc->analog_groups[0]
-				&& cg != &devc->analog_groups[1]) {
-			sr_err("Invalid channel group specified.");
-			return SR_ERR;
-		}
+	if (cg && !g_slist_find(sdi->channel_groups, cg)) {
+		sr_err("Invalid channel group specified.");
+		return SR_ERR;
 	}
 
 	switch (key) {
@@ -790,15 +834,15 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 			sr_err("No channel group specified.");
 			return SR_ERR_CHANNEL_GROUP;
 		}
-		if (cg == &devc->digital_group) {
-			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				NULL, 0, sizeof(int32_t));
+		if (cg == devc->digital_group) {
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				NULL, 0, sizeof(uint32_t));
 			return SR_OK;
 		} else {
-			for (i = 0; i < 2; i++) {
-				if (cg == &devc->analog_groups[i]) {
-					*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-						analog_hwcaps, ARRAY_SIZE(analog_hwcaps), sizeof(int32_t));
+			for (i = 0; i < devc->model->analog_channels; i++) {
+				if (cg == devc->analog_groups[i]) {
+					*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+						analog_devopts, ARRAY_SIZE(analog_devopts), sizeof(uint32_t));
 					return SR_OK;
 				}
 			}
@@ -821,7 +865,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 			return SR_ERR_CHANNEL_GROUP;
 		}
 		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
-		for (i = 0; i < NUM_VDIV; i++) {
+		for (i = 0; i < devc->num_vdivs; i++) {
 			rational[0] = g_variant_new_uint64(devc->vdivs[i][0]);
 			rational[1] = g_variant_new_uint64(devc->vdivs[i][1]);
 			tuple = g_variant_new_tuple(rational, 2);
@@ -883,6 +927,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	struct dev_context *devc;
 	struct sr_channel *ch;
 	struct sr_datafeed_packet packet;
+	gboolean some_digital;
 	GSList *l;
 
 	if (sdi->status != SR_ST_ACTIVE)
@@ -893,13 +938,14 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 	devc->num_frames = 0;
 
+	some_digital = FALSE;
 	for (l = sdi->channels; l; l = l->next) {
 		ch = l->data;
 		sr_dbg("handling channel %s", ch->name);
 		if (ch->type == SR_CHANNEL_ANALOG) {
 			if (ch->enabled)
-				devc->enabled_analog_channels = g_slist_append(
-						devc->enabled_analog_channels, ch);
+				devc->enabled_channels = g_slist_append(
+						devc->enabled_channels, ch);
 			if (ch->enabled != devc->analog_channels[ch->index]) {
 				/* Enabled channel is currently disabled, or vice versa. */
 				if (rigol_ds_config_set(sdi, ":CHAN%d:DISP %s", ch->index + 1,
@@ -908,19 +954,29 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 				devc->analog_channels[ch->index] = ch->enabled;
 			}
 		} else if (ch->type == SR_CHANNEL_LOGIC) {
+			/* Only one list entry for DS1000D series. All channels are retrieved
+			 * together when this entry is processed. */
+			if (ch->enabled && (
+						devc->model->series->protocol > PROTOCOL_V2 ||
+						!some_digital))
+				devc->enabled_channels = g_slist_append(
+						devc->enabled_channels, ch);
 			if (ch->enabled) {
-				devc->enabled_digital_channels = g_slist_append(
-						devc->enabled_digital_channels, ch);
+				some_digital = TRUE;
 				/* Turn on LA module if currently off. */
 				if (!devc->la_enabled) {
-					if (rigol_ds_config_set(sdi, ":LA:DISP ON") != SR_OK)
+					if (rigol_ds_config_set(sdi,
+							devc->model->series->protocol >= PROTOCOL_V4 ?
+								":LA:STAT ON" : ":LA:DISP ON") != SR_OK)
 						return SR_ERR;
 					devc->la_enabled = TRUE;
 				}
 			}
 			if (ch->enabled != devc->digital_channels[ch->index]) {
 				/* Enabled channel is currently disabled, or vice versa. */
-				if (rigol_ds_config_set(sdi, ":DIG%d:TURN %s", ch->index,
+				if (rigol_ds_config_set(sdi,
+						devc->model->series->protocol >= PROTOCOL_V4 ?
+							":LA:DIG%d:DISP %s" : ":DIG%d:TURN %s", ch->index,
 						ch->enabled ? "ON" : "OFF") != SR_OK)
 					return SR_ERR;
 				devc->digital_channels[ch->index] = ch->enabled;
@@ -928,12 +984,14 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		}
 	}
 
-	if (!devc->enabled_analog_channels && !devc->enabled_digital_channels)
+	if (!devc->enabled_channels)
 		return SR_ERR;
 
 	/* Turn off LA module if on and no digital channels selected. */
-	if (devc->la_enabled && !devc->enabled_digital_channels)
-		if (rigol_ds_config_set(sdi, ":LA:DISP OFF") != SR_OK)
+	if (devc->la_enabled && !some_digital)
+		if (rigol_ds_config_set(sdi,
+				devc->model->series->protocol >= PROTOCOL_V4 ?
+					":LA:STAT OFF" : ":LA:DISP OFF") != SR_OK)
 			return SR_ERR;
 
 	/* Set memory mode. */
@@ -970,15 +1028,13 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		if (rigol_ds_config_set(sdi, ":RUN") != SR_OK)
 			return SR_ERR;
 
-	sr_scpi_source_add(scpi, G_IO_IN, 50, rigol_ds_receive, (void *)sdi);
+	sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50,
+			rigol_ds_receive, (void *)sdi);
 
 	/* Send header packet to the session bus. */
 	std_session_send_df_header(cb_data, LOG_PREFIX);
 
-	if (devc->enabled_analog_channels)
-		devc->channel_entry = devc->enabled_analog_channels;
-	else
-		devc->channel_entry = devc->enabled_digital_channels;
+	devc->channel_entry = devc->enabled_channels;
 
 	if (rigol_ds_capture_start(sdi) != SR_OK)
 		return SR_ERR;
@@ -1009,12 +1065,10 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 	packet.type = SR_DF_END;
 	sr_session_send(sdi, &packet);
 
-	g_slist_free(devc->enabled_analog_channels);
-	g_slist_free(devc->enabled_digital_channels);
-	devc->enabled_analog_channels = NULL;
-	devc->enabled_digital_channels = NULL;
+	g_slist_free(devc->enabled_channels);
+	devc->enabled_channels = NULL;
 	scpi = sdi->conn;
-	sr_scpi_source_remove(scpi);
+	sr_scpi_source_remove(sdi->session, scpi);
 
 	return SR_OK;
 }
@@ -1035,5 +1089,5 @@ SR_PRIV struct sr_dev_driver rigol_ds_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/rigol-ds/protocol.c b/src/hardware/rigol-ds/protocol.c
similarity index 67%
rename from hardware/rigol-ds/protocol.c
rename to src/hardware/rigol-ds/protocol.c
index 97cb353..4a30717 100644
--- a/hardware/rigol-ds/protocol.c
+++ b/src/hardware/rigol-ds/protocol.c
@@ -19,6 +19,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <unistd.h>
@@ -28,8 +29,9 @@
 #include <ctype.h>
 #include <time.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
+#include "scpi.h"
 #include "protocol.h"
 
 /*
@@ -215,7 +217,7 @@ static int rigol_ds_check_stop(const struct sr_dev_inst *sdi)
 
 	ch = devc->channel_entry->data;
 
-	if (devc->model->series->protocol <= PROTOCOL_V2)
+	if (devc->model->series->protocol != PROTOCOL_V3)
 		return SR_OK;
 
 	if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
@@ -241,7 +243,7 @@ static int rigol_ds_check_stop(const struct sr_dev_inst *sdi)
 	if (tmp & 0x10) {
 		sr_warn("Single shot acquisition failed, retrying...");
 		/* Sleep a bit, otherwise the single shot will often fail */
-		g_usleep(500000);
+		g_usleep(500 * 1000);
 		rigol_ds_config_set(sdi, ":SING");
 		rigol_ds_set_wait_event(devc, WAIT_STOP);
 		return SR_ERR;
@@ -261,7 +263,7 @@ static int rigol_ds_block_wait(const struct sr_dev_inst *sdi)
 	if (!(devc = sdi->priv))
 		return SR_ERR;
 
-	if (devc->model->series->protocol >= PROTOCOL_V3) {
+	if (devc->model->series->protocol == PROTOCOL_V3) {
 
 		start = time(NULL);
 
@@ -277,7 +279,7 @@ static int rigol_ds_block_wait(const struct sr_dev_inst *sdi)
 			 * it too much with SCPI requests but don't wait too
 			 * long for short sample frame sizes.
 			 */
-			g_usleep(devc->analog_frame_size < 15000 ? 100000 : 1000000);
+			g_usleep(devc->analog_frame_size < (15 * 1000) ? (100 * 1000) : (1000 * 1000));
 
 			/* "READ,nnnn" (still working) or "IDLE,nnnn" (finished) */
 			if (sr_scpi_get_string(sdi->conn, ":WAV:STAT?", &buf) != SR_OK)
@@ -285,7 +287,7 @@ static int rigol_ds_block_wait(const struct sr_dev_inst *sdi)
 
 			if (parse_int(buf + 5, &len) != SR_OK)
 				return SR_ERR;
-		} while (buf[0] == 'R' && len < 1000000);
+		} while (buf[0] == 'R' && len < (1000 * 1000));
 	}
 
 	rigol_ds_set_wait_event(devc, WAIT_NONE);
@@ -310,7 +312,7 @@ SR_PRIV int rigol_ds_config_set(const struct sr_dev_inst *sdi, const char *forma
 	if (devc->model->series->protocol == PROTOCOL_V2) {
 		/* The DS1000 series needs this stupid delay, *OPC? doesn't work. */
 		sr_spew("delay %dms", 100);
-		g_usleep(100000);
+		g_usleep(100 * 1000);
 		return SR_OK;
 	} else {
 		return sr_scpi_get_opc(sdi->conn);
@@ -322,11 +324,12 @@ SR_PRIV int rigol_ds_capture_start(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
 	gchar *trig_mode;
+	unsigned int num_channels, i, j;
 
 	if (!(devc = sdi->priv))
 		return SR_ERR;
 
-	sr_dbg("Starting data capture for frameset %lu of %lu",
+	sr_dbg("Starting data capture for frameset %" PRIu64 " of %" PRIu64,
 	       devc->num_frames + 1, devc->limit_frames);
 
 	switch (devc->model->series->protocol) {
@@ -353,15 +356,44 @@ SR_PRIV int rigol_ds_capture_start(const struct sr_dev_inst *sdi)
 		}
 		break;
 	case PROTOCOL_V3:
+	case PROTOCOL_V4:
 		if (rigol_ds_config_set(sdi, ":WAV:FORM BYTE") != SR_OK)
 			return SR_ERR;
 		if (devc->data_source == DATA_SOURCE_LIVE) {
 			if (rigol_ds_config_set(sdi, ":WAV:MODE NORM") != SR_OK)
 				return SR_ERR;
+			devc->analog_frame_size = devc->model->series->live_samples;
+			devc->digital_frame_size = devc->model->series->live_samples;
 			rigol_ds_set_wait_event(devc, WAIT_TRIGGER);
 		} else {
-			if (rigol_ds_config_set(sdi, ":WAV:MODE RAW") != SR_OK)
-				return SR_ERR;
+			if (devc->model->series->protocol == PROTOCOL_V3) {
+				if (rigol_ds_config_set(sdi, ":WAV:MODE RAW") != SR_OK)
+					return SR_ERR;
+			} else if (devc->model->series->protocol == PROTOCOL_V4) {
+				num_channels = 0;
+
+				/* Channels 3 and 4 are multiplexed with D0-7 and D8-15 */
+				for (i = 0; i < devc->model->analog_channels; i++) {
+					if (devc->analog_channels[i]) {
+						num_channels++;
+					} else if (i >= 2 && devc->model->has_digital) {
+						for (j = 0; j < 8; j++) {
+							if (devc->digital_channels[8 * (i - 2) + j]) {
+								num_channels++;
+								break;
+							}
+						}
+					}
+				}
+
+				devc->analog_frame_size = devc->digital_frame_size =
+					num_channels == 1 ?
+						devc->model->series->buffer_samples :
+							num_channels == 2 ?
+								devc->model->series->buffer_samples / 2 :
+								devc->model->series->buffer_samples / 4;
+			}
+
 			if (rigol_ds_config_set(sdi, ":SING") != SR_OK)
 				return SR_ERR;
 			rigol_ds_set_wait_event(devc, WAIT_STOP);
@@ -385,7 +417,9 @@ SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi)
 
 	sr_dbg("Starting reading data from channel %d", ch->index + 1);
 
-	if (devc->model->series->protocol <= PROTOCOL_V2) {
+	switch (devc->model->series->protocol) {
+	case PROTOCOL_V1:
+	case PROTOCOL_V2:
 		if (ch->type == SR_CHANNEL_LOGIC) {
 			if (sr_scpi_send(sdi->conn, ":WAV:DATA? DIG") != SR_OK)
 				return SR_ERR;
@@ -395,7 +429,8 @@ SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi)
 				return SR_ERR;
 		}
 		rigol_ds_set_wait_event(devc, WAIT_NONE);
-	} else {
+		break;
+	case PROTOCOL_V3:
 		if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
 				  ch->index + 1) != SR_OK)
 			return SR_ERR;
@@ -405,6 +440,31 @@ SR_PRIV int rigol_ds_channel_start(const struct sr_dev_inst *sdi)
 			if (rigol_ds_config_set(sdi, ":WAV:BEG") != SR_OK)
 				return SR_ERR;
 		}
+		break;
+	case PROTOCOL_V4:
+		if (ch->type == SR_CHANNEL_ANALOG) {
+			if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
+					ch->index + 1) != SR_OK)
+				return SR_ERR;
+		} else {
+			if (rigol_ds_config_set(sdi, ":WAV:SOUR D%d",
+					ch->index) != SR_OK)
+				return SR_ERR;
+		}
+
+		if (rigol_ds_config_set(sdi,
+					devc->data_source == DATA_SOURCE_LIVE ?
+						":WAV:MODE NORM" :":WAV:MODE RAW") != SR_OK)
+			return SR_ERR;
+		break;
+	}
+
+	if (devc->model->series->protocol >= PROTOCOL_V3 &&
+			ch->type == SR_CHANNEL_ANALOG) {
+		/* Vertical reference. */
+		if (sr_scpi_get_int(sdi->conn, ":WAV:YREF?",
+				&devc->vert_reference[ch->index]) != SR_OK)
+			return SR_ERR;
 	}
 
 	rigol_ds_set_wait_event(devc, WAIT_BLOCK);
@@ -479,7 +539,7 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
 	struct sr_scpi_dev_inst *scpi;
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct sr_datafeed_logic logic;
 	double vdiv, offset;
 	int len, i, vref;
@@ -496,203 +556,206 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
 
 	scpi = sdi->conn;
 
-	if (revents == G_IO_IN || revents == 0) {
-		switch(devc->wait_event) {
-		case WAIT_NONE:
-			break;
-		case WAIT_TRIGGER:
-			if (rigol_ds_trigger_wait(sdi) != SR_OK)
-				return TRUE;
-			if (rigol_ds_channel_start(sdi) != SR_OK)
-				return TRUE;
+	if (!(revents == G_IO_IN || revents == 0))
+		return TRUE;
+
+	switch (devc->wait_event) {
+	case WAIT_NONE:
+		break;
+	case WAIT_TRIGGER:
+		if (rigol_ds_trigger_wait(sdi) != SR_OK)
 			return TRUE;
-		case WAIT_BLOCK:
-			if (rigol_ds_block_wait(sdi) != SR_OK)
-				return TRUE;
-			break;
-		case WAIT_STOP:
-			if (rigol_ds_stop_wait(sdi) != SR_OK)
-				return TRUE;
-			if (rigol_ds_check_stop(sdi) != SR_OK)
-				return TRUE;
-			if (rigol_ds_channel_start(sdi) != SR_OK)
-				return TRUE;
+		if (rigol_ds_channel_start(sdi) != SR_OK)
 			return TRUE;
-		default:
-			sr_err("BUG: Unknown event target encountered");
-		}
-
-		ch = devc->channel_entry->data;
+		return TRUE;
+	case WAIT_BLOCK:
+		if (rigol_ds_block_wait(sdi) != SR_OK)
+			return TRUE;
+		break;
+	case WAIT_STOP:
+		if (rigol_ds_stop_wait(sdi) != SR_OK)
+			return TRUE;
+		if (rigol_ds_check_stop(sdi) != SR_OK)
+			return TRUE;
+		if (rigol_ds_channel_start(sdi) != SR_OK)
+			return TRUE;
+		return TRUE;
+	default:
+		sr_err("BUG: Unknown event target encountered");
+		break;
+	}
 
-		expected_data_bytes = ch->type == SR_CHANNEL_ANALOG ?
-				devc->analog_frame_size : devc->digital_frame_size;
+	ch = devc->channel_entry->data;
 
-		if (devc->num_block_bytes == 0) {
-			if (devc->model->series->protocol >= PROTOCOL_V3)
-				if (sr_scpi_send(sdi->conn, ":WAV:DATA?") != SR_OK)
-					return TRUE;
+	expected_data_bytes = ch->type == SR_CHANNEL_ANALOG ?
+			devc->analog_frame_size : devc->digital_frame_size;
 
-			if (sr_scpi_read_begin(scpi) != SR_OK)
+	if (devc->num_block_bytes == 0) {
+		if (devc->model->series->protocol >= PROTOCOL_V4) {
+			if (sr_scpi_send(sdi->conn, ":WAV:START %d",
+					devc->num_channel_bytes + 1) != SR_OK)
+				return TRUE;
+			if (sr_scpi_send(sdi->conn, ":WAV:STOP %d",
+					MIN(devc->num_channel_bytes + ACQ_BLOCK_SIZE,
+						devc->analog_frame_size)) != SR_OK)
 				return TRUE;
-
-			if (devc->format == FORMAT_IEEE488_2) {
-				sr_dbg("New block header expected");
-				len = rigol_ds_read_header(sdi);
-				if (len == 0)
-					/* Still reading the header. */
-					return TRUE;
-				if (len == -1) {
-					sr_err("Read error, aborting capture.");
-					packet.type = SR_DF_FRAME_END;
-					sr_session_send(cb_data, &packet);
-					sdi->driver->dev_acquisition_stop(sdi, cb_data);
-					return TRUE;
-				}
-				/* At slow timebases in live capture the DS2072
-				 * sometimes returns "short" data blocks, with
-				 * apparently no way to get the rest of the data.
-				 * Discard these, the complete data block will
-				 * appear eventually.
-				 */
-				if (devc->data_source == DATA_SOURCE_LIVE
-						&& (unsigned)len < expected_data_bytes) {
-					sr_dbg("Discarding short data block");
-					sr_scpi_read_data(scpi, (char *)devc->buffer, len + 1);
-					return TRUE;
-				}
-				devc->num_block_bytes = len;
-			} else {
-				devc->num_block_bytes = expected_data_bytes;
-			}
-			devc->num_block_read = 0;
 		}
 
-		len = devc->num_block_bytes - devc->num_block_read;
-		if (len > ACQ_BUFFER_SIZE)
-			len = ACQ_BUFFER_SIZE;
-		sr_dbg("Requesting read of %d bytes", len);
-
-		len = sr_scpi_read_data(scpi, (char *)devc->buffer, len);
+		if (devc->model->series->protocol >= PROTOCOL_V3)
+			if (sr_scpi_send(sdi->conn, ":WAV:DATA?") != SR_OK)
+				return TRUE;
 
-		if (len == -1) {
-			sr_err("Read error, aborting capture.");
-			packet.type = SR_DF_FRAME_END;
-			sr_session_send(cb_data, &packet);
-			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+		if (sr_scpi_read_begin(scpi) != SR_OK)
 			return TRUE;
-		}
-
-		sr_dbg("Received %d bytes.", len);
-
-		devc->num_block_read += len;
-
-		if (ch->type == SR_CHANNEL_ANALOG) {
-			vref = devc->vert_reference[ch->index];
-			vdiv = devc->vdiv[ch->index] / 25.6;
-			offset = devc->vert_offset[ch->index];
-			if (devc->model->series->protocol >= PROTOCOL_V3)
-				for (i = 0; i < len; i++)
-					devc->data[i] = ((int)devc->buffer[i] - vref) * vdiv - offset;
-			else
-				for (i = 0; i < len; i++)
-					devc->data[i] = (128 - devc->buffer[i]) * vdiv - offset;
-			analog.channels = g_slist_append(NULL, ch);
-			analog.num_samples = len;
-			analog.data = devc->data;
-			analog.mq = SR_MQ_VOLTAGE;
-			analog.unit = SR_UNIT_VOLT;
-			analog.mqflags = 0;
-			packet.type = SR_DF_ANALOG;
-			packet.payload = &analog;
-			sr_session_send(cb_data, &packet);
-			g_slist_free(analog.channels);
-		} else {
-			logic.length = len;
-			logic.unitsize = 2;
-			logic.data = devc->buffer;
-			packet.type = SR_DF_LOGIC;
-			packet.payload = &logic;
-			sr_session_send(cb_data, &packet);
-		}
 
-		if (devc->num_block_read == devc->num_block_bytes) {
-			sr_dbg("Block has been completed");
-			if (devc->model->series->protocol >= PROTOCOL_V3) {
-				/* Discard the terminating linefeed */
-				sr_scpi_read_data(scpi, (char *)devc->buffer, 1);
-			}
-			if (devc->format == FORMAT_IEEE488_2) {
-				/* Prepare for possible next block */
-				devc->num_header_bytes = 0;
-				devc->num_block_bytes = 0;
-				if (devc->data_source != DATA_SOURCE_LIVE)
-					rigol_ds_set_wait_event(devc, WAIT_BLOCK);
-			}
-			if (!sr_scpi_read_complete(scpi)) {
-				sr_err("Read should have been completed");
+		if (devc->format == FORMAT_IEEE488_2) {
+			sr_dbg("New block header expected");
+			len = rigol_ds_read_header(sdi);
+			if (len == 0)
+				/* Still reading the header. */
+				return TRUE;
+			if (len == -1) {
+				sr_err("Read error, aborting capture.");
 				packet.type = SR_DF_FRAME_END;
 				sr_session_send(cb_data, &packet);
 				sdi->driver->dev_acquisition_stop(sdi, cb_data);
 				return TRUE;
 			}
-			devc->num_block_read = 0;
+			/* At slow timebases in live capture the DS2072
+			 * sometimes returns "short" data blocks, with
+			 * apparently no way to get the rest of the data.
+			 * Discard these, the complete data block will
+			 * appear eventually.
+			 */
+			if (devc->data_source == DATA_SOURCE_LIVE
+					&& (unsigned)len < expected_data_bytes) {
+				sr_dbg("Discarding short data block");
+				sr_scpi_read_data(scpi, (char *)devc->buffer, len + 1);
+				return TRUE;
+			}
+			devc->num_block_bytes = len;
 		} else {
-			sr_dbg("%d of %d block bytes read", devc->num_block_read, devc->num_block_bytes);
+			devc->num_block_bytes = expected_data_bytes;
 		}
+		devc->num_block_read = 0;
+	}
 
-		devc->num_channel_bytes += len;
+	len = devc->num_block_bytes - devc->num_block_read;
+	if (len > ACQ_BUFFER_SIZE)
+		len = ACQ_BUFFER_SIZE;
+	sr_dbg("Requesting read of %d bytes", len);
 
-		if (devc->num_channel_bytes < expected_data_bytes)
-			/* Don't have the full data for this channel yet, re-run. */
-			return TRUE;
+	len = sr_scpi_read_data(scpi, (char *)devc->buffer, len);
+
+	if (len == -1) {
+		sr_err("Read error, aborting capture.");
+		packet.type = SR_DF_FRAME_END;
+		sr_session_send(cb_data, &packet);
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+		return TRUE;
+	}
 
-		/* End of data for this channel. */
+	sr_dbg("Received %d bytes.", len);
+
+	devc->num_block_read += len;
+
+	if (ch->type == SR_CHANNEL_ANALOG) {
+		vref = devc->vert_reference[ch->index];
+		vdiv = devc->vdiv[ch->index] / 25.6;
+		offset = devc->vert_offset[ch->index];
+		if (devc->model->series->protocol >= PROTOCOL_V3)
+			for (i = 0; i < len; i++)
+				devc->data[i] = ((int)devc->buffer[i] - vref) * vdiv - offset;
+		else
+			for (i = 0; i < len; i++)
+				devc->data[i] = (128 - devc->buffer[i]) * vdiv - offset;
+		analog.channels = g_slist_append(NULL, ch);
+		analog.num_samples = len;
+		analog.data = devc->data;
+		analog.mq = SR_MQ_VOLTAGE;
+		analog.unit = SR_UNIT_VOLT;
+		analog.mqflags = 0;
+		packet.type = SR_DF_ANALOG_OLD;
+		packet.payload = &analog;
+		sr_session_send(cb_data, &packet);
+		g_slist_free(analog.channels);
+	} else {
+		logic.length = len;
+		// TODO: For the MSO1000Z series, we need a way to express that
+		// this data is in fact just for a single channel, with the valid
+		// data for that channel in the LSB of each byte.
+		logic.unitsize = devc->model->series->protocol == PROTOCOL_V4 ? 1 : 2;
+		logic.data = devc->buffer;
+		packet.type = SR_DF_LOGIC;
+		packet.payload = &logic;
+		sr_session_send(cb_data, &packet);
+	}
+
+	if (devc->num_block_read == devc->num_block_bytes) {
+		sr_dbg("Block has been completed");
 		if (devc->model->series->protocol >= PROTOCOL_V3) {
-			/* Signal end of data download to scope */
+			/* Discard the terminating linefeed */
+			sr_scpi_read_data(scpi, (char *)devc->buffer, 1);
+		}
+		if (devc->format == FORMAT_IEEE488_2) {
+			/* Prepare for possible next block */
+			devc->num_header_bytes = 0;
+			devc->num_block_bytes = 0;
 			if (devc->data_source != DATA_SOURCE_LIVE)
-				/*
-				 * This causes a query error, without it switching
-				 * to the next channel causes an error. Fun with
-				 * firmware...
-				 */
-				rigol_ds_config_set(sdi, ":WAV:END");
+				rigol_ds_set_wait_event(devc, WAIT_BLOCK);
 		}
+		if (!sr_scpi_read_complete(scpi)) {
+			sr_err("Read should have been completed");
+			packet.type = SR_DF_FRAME_END;
+			sr_session_send(cb_data, &packet);
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
+			return TRUE;
+		}
+		devc->num_block_read = 0;
+	} else {
+		sr_dbg("%" PRIu64 " of %" PRIu64 " block bytes read",
+			devc->num_block_read, devc->num_block_bytes);
+	}
+
+	devc->num_channel_bytes += len;
+
+	if (devc->num_channel_bytes < expected_data_bytes)
+		/* Don't have the full data for this channel yet, re-run. */
+		return TRUE;
+
+	/* End of data for this channel. */
+	if (devc->model->series->protocol == PROTOCOL_V3) {
+		/* Signal end of data download to scope */
+		if (devc->data_source != DATA_SOURCE_LIVE)
+			/*
+			 * This causes a query error, without it switching
+			 * to the next channel causes an error. Fun with
+			 * firmware...
+			 */
+			rigol_ds_config_set(sdi, ":WAV:END");
+	}
+
+	if (devc->channel_entry->next) {
+		/* We got the frame for this channel, now get the next channel. */
+		devc->channel_entry = devc->channel_entry->next;
+		rigol_ds_channel_start(sdi);
+	} else {
+		/* Done with this frame. */
+		packet.type = SR_DF_FRAME_END;
+		sr_session_send(cb_data, &packet);
 
-		if (ch->type == SR_CHANNEL_ANALOG
-				&& devc->channel_entry->next != NULL) {
-			/* We got the frame for this analog channel, but
-			 * there's another analog channel. */
-			devc->channel_entry = devc->channel_entry->next;
-			rigol_ds_channel_start(sdi);
+		if (++devc->num_frames == devc->limit_frames) {
+			/* Last frame, stop capture. */
+			sdi->driver->dev_acquisition_stop(sdi, cb_data);
 		} else {
-			/* Done with all analog channels in this frame. */
-			if (devc->enabled_digital_channels
-					&& devc->channel_entry != devc->enabled_digital_channels) {
-				/* Now we need to get the digital data. */
-				devc->channel_entry = devc->enabled_digital_channels;
-				rigol_ds_channel_start(sdi);
-			} else {
-				/* Done with this frame. */
-				packet.type = SR_DF_FRAME_END;
-				sr_session_send(cb_data, &packet);
+			/* Get the next frame, starting with the first channel. */
+			devc->channel_entry = devc->enabled_channels;
 
-				if (++devc->num_frames == devc->limit_frames) {
-					/* Last frame, stop capture. */
-					sdi->driver->dev_acquisition_stop(sdi, cb_data);
-				} else {
-					/* Get the next frame, starting with the first analog channel. */
-					if (devc->enabled_analog_channels)
-						devc->channel_entry = devc->enabled_analog_channels;
-					else
-						devc->channel_entry = devc->enabled_digital_channels;
-
-					rigol_ds_capture_start(sdi);
-
-					/* Start of next frame. */
-					packet.type = SR_DF_FRAME_BEGIN;
-					sr_session_send(cb_data, &packet);
-				}
-			}
+			rigol_ds_capture_start(sdi);
+
+			/* Start of next frame. */
+			packet.type = SR_DF_FRAME_BEGIN;
+			sr_session_send(cb_data, &packet);
 		}
 	}
 
@@ -702,7 +765,7 @@ SR_PRIV int rigol_ds_receive(int fd, int revents, void *cb_data)
 SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
-	char *t_s, *cmd;
+	char *cmd;
 	unsigned int i;
 	int res;
 
@@ -711,11 +774,10 @@ SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi)
 	/* Analog channel state. */
 	for (i = 0; i < devc->model->analog_channels; i++) {
 		cmd = g_strdup_printf(":CHAN%d:DISP?", i + 1);
-		res = sr_scpi_get_string(sdi->conn, cmd, &t_s);
+		res = sr_scpi_get_bool(sdi->conn, cmd, &devc->analog_channels[i]);
 		g_free(cmd);
 		if (res != SR_OK)
 			return SR_ERR;
-		devc->analog_channels[i] = !strcmp(t_s, "ON") || !strcmp(t_s, "1");
 	}
 	sr_dbg("Current analog channel state:");
 	for (i = 0; i < devc->model->analog_channels; i++)
@@ -723,19 +785,21 @@ SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi)
 
 	/* Digital channel state. */
 	if (devc->model->has_digital) {
-		if (sr_scpi_get_string(sdi->conn, ":LA:DISP?", &t_s) != SR_OK)
+		if (sr_scpi_get_bool(sdi->conn,
+				devc->model->series->protocol >= PROTOCOL_V4 ?
+					":LA:STAT?" : ":LA:DISP?",
+				&devc->la_enabled) != SR_OK)
 			return SR_ERR;
-		devc->la_enabled = !strcmp(t_s, "ON") ? TRUE : FALSE;
 		sr_dbg("Logic analyzer %s, current digital channel state:",
 				devc->la_enabled ? "enabled" : "disabled");
-		for (i = 0; i < 16; i++) {
-			cmd = g_strdup_printf(":DIG%d:TURN?", i);
-			res = sr_scpi_get_string(sdi->conn, cmd, &t_s);
+		for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+			cmd = g_strdup_printf(
+				devc->model->series->protocol >= PROTOCOL_V4 ?
+					":LA:DIG%d:DISP?" : ":DIG%d:TURN?", i);
+			res = sr_scpi_get_bool(sdi->conn, cmd, &devc->digital_channels[i]);
 			g_free(cmd);
 			if (res != SR_OK)
 				return SR_ERR;
-			devc->digital_channels[i] = !strcmp(t_s, "ON") ? TRUE : FALSE;
-			g_free(t_s);
 			sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "on" : "off");
 		}
 	}
@@ -757,18 +821,6 @@ SR_PRIV int rigol_ds_get_dev_cfg(const struct sr_dev_inst *sdi)
 	for (i = 0; i < devc->model->analog_channels; i++)
 		sr_dbg("CH%d %g", i + 1, devc->vdiv[i]);
 
-	sr_dbg("Current vertical reference:");
-	if (devc->model->series->protocol >= PROTOCOL_V3) {
-		/* Vertical reference - not certain if this is the place to read it. */
-		for (i = 0; i < devc->model->analog_channels; i++) {
-			if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d", i + 1) != SR_OK)
-				return SR_ERR;
-			if (sr_scpi_get_int(sdi->conn, ":WAV:YREF?", &devc->vert_reference[i]) != SR_OK)
-				return SR_ERR;
-			sr_dbg("CH%d %d", i + 1, devc->vert_reference[i]);
-		}
-	}
-
 	/* Vertical offset. */
 	for (i = 0; i < devc->model->analog_channels; i++) {
 		cmd = g_strdup_printf(":CHAN%d:OFFS?", i + 1);
diff --git a/hardware/rigol-ds/protocol.h b/src/hardware/rigol-ds/protocol.h
similarity index 93%
rename from hardware/rigol-ds/protocol.h
rename to src/hardware/rigol-ds/protocol.h
index 0117628..5e5f01b 100644
--- a/hardware/rigol-ds/protocol.h
+++ b/src/hardware/rigol-ds/protocol.h
@@ -23,13 +23,16 @@
 
 #include <stdint.h>
 #include <stdbool.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "rigol-ds"
 
 /* Size of acquisition buffers */
-#define ACQ_BUFFER_SIZE 32768
+#define ACQ_BUFFER_SIZE (32 * 1024)
+
+/* Maximum number of samples to retrieve at once. */
+#define ACQ_BLOCK_SIZE (30 * 1000)
 
 #define MAX_ANALOG_CHANNELS 4
 #define MAX_DIGITAL_CHANNELS 16
@@ -38,6 +41,7 @@ enum protocol_version {
 	PROTOCOL_V1, /* VS5000 */
 	PROTOCOL_V2, /* DS1000 */
 	PROTOCOL_V3, /* DS2000, DSO1000 */
+	PROTOCOL_V4, /* DS1000Z */
 };
 
 enum data_format {
@@ -98,12 +102,11 @@ struct dev_context {
 	uint64_t num_vdivs;
 
 	/* Channel groups */
-	struct sr_channel_group analog_groups[MAX_ANALOG_CHANNELS];
-	struct sr_channel_group digital_group;
+	struct sr_channel_group **analog_groups;
+	struct sr_channel_group *digital_group;
 
 	/* Acquisition settings */
-	GSList *enabled_analog_channels;
-	GSList *enabled_digital_channels;
+	GSList *enabled_channels;
 	uint64_t limit_frames;
 	void *cb_data;
 	enum data_source data_source;
diff --git a/hardware/saleae-logic16/api.c b/src/hardware/saleae-logic16/api.c
similarity index 80%
rename from hardware/saleae-logic16/api.c
rename to src/hardware/saleae-logic16/api.c
index 388d95a..bee0ea4 100644
--- a/hardware/saleae-logic16/api.c
+++ b/src/hardware/saleae-logic16/api.c
@@ -19,12 +19,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <glib.h>
 #include <libusb.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
@@ -33,32 +34,39 @@
 
 #define USB_INTERFACE		0
 #define USB_CONFIGURATION	1
-#define FX2_FIRMWARE		FIRMWARE_DIR "/saleae-logic16-fx2.fw"
+#define FX2_FIRMWARE		"saleae-logic16-fx2.fw"
 
 #define MAX_RENUM_DELAY_MS	3000
 #define NUM_SIMUL_TRANSFERS	32
 
 SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
-static struct sr_dev_driver *di = &saleae_logic16_driver_info;
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_VOLTAGE_THRESHOLD,
-
-	/* These are really implemented in the driver, not the hardware. */
-	SR_CONF_LIMIT_SAMPLES,
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_CONN | SR_CONF_GET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const int32_t soft_trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
+	SR_TRIGGER_EDGE,
 };
 
 static const char *channel_names[] = {
 	"0", "1", "2", "3", "4", "5", "6", "7", "8",
 	"9", "10", "11", "12", "13", "14", "15",
-	NULL,
 };
 
 static const struct {
@@ -87,7 +95,7 @@ static const uint64_t samplerates[] = {
 	SR_MHZ(100),
 };
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
@@ -103,8 +111,7 @@ static gboolean check_conf_profile(libusb_device *dev)
 	ret = FALSE;
 	while (!ret) {
 		/* Assume the FW has not been loaded, unless proven wrong. */
-		if (libusb_get_device_descriptor(dev, &des) != 0)
-			break;
+		libusb_get_device_descriptor(dev, &des);
 
 		if (libusb_open(dev, &hdl) != 0)
 			break;
@@ -130,21 +137,21 @@ static gboolean check_conf_profile(libusb_device *dev)
 	return ret;
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
 	struct sr_usb_dev_inst *usb;
-	struct sr_channel *ch;
 	struct sr_config *src;
 	GSList *l, *devices, *conn_devices;
 	struct libusb_device_descriptor des;
 	libusb_device **devlist;
-	int devcnt, ret, i, j;
+	unsigned int i, j;
 	const char *conn;
+	char connection_id[64];
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	conn = NULL;
 	for (l = options; l; l = l->next) {
@@ -178,31 +185,25 @@ static GSList *scan(GSList *options)
 				continue;
 		}
 
-		if ((ret = libusb_get_device_descriptor(devlist[i], &des)) != 0) {
-			sr_warn("Failed to get device descriptor: %s.",
-				libusb_error_name(ret));
-			continue;
-		}
+		libusb_get_device_descriptor(devlist[i], &des);
+
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
 
 		if (des.idVendor != LOGIC16_VID || des.idProduct != LOGIC16_PID)
 			continue;
 
-		devcnt = g_slist_length(drvc->instances);
-		sdi = sr_dev_inst_new(devcnt, SR_ST_INITIALIZING,
-				      "Saleae", "Logic16", NULL);
-		if (!sdi)
-			return NULL;
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INITIALIZING;
+		sdi->vendor = g_strdup("Saleae");
+		sdi->model = g_strdup("Logic16");
 		sdi->driver = di;
+		sdi->connection_id = g_strdup(connection_id);
 
-		for (j = 0; channel_names[j]; j++) {
-			if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
-						   channel_names[j])))
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
-		}
+		for (j = 0; j < ARRAY_SIZE(channel_names); j++)
+			sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE,
+					    channel_names[j]);
 
-		if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
-			return NULL;
+		devc = g_malloc0(sizeof(struct dev_context));
 		devc->selected_voltage_range = VOLTAGE_RANGE_18_33_V;
 		sdi->priv = devc;
 		drvc->instances = g_slist_append(drvc->instances, sdi);
@@ -217,13 +218,12 @@ static GSList *scan(GSList *options)
 				libusb_get_bus_number(devlist[i]),
 				libusb_get_device_address(devlist[i]), NULL);
 		} else {
-			if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
-						  FX2_FIRMWARE) == SR_OK)
+			if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
+					USB_CONFIGURATION, FX2_FIRMWARE) == SR_OK)
 				/* Store when this device's FW was updated. */
 				devc->fw_updated = g_get_monotonic_time();
 			else
-				sr_err("Firmware upload failed for "
-				       "device %d.", devcnt);
+				sr_err("Firmware upload failed.");
 			sdi->inst_type = SR_INST_USB;
 			sdi->conn = sr_usb_dev_inst_new(
 				libusb_get_bus_number(devlist[i]), 0xff, NULL);
@@ -235,27 +235,29 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int logic16_dev_open(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di;
 	libusb_device **devlist;
 	struct sr_usb_dev_inst *usb;
 	struct libusb_device_descriptor des;
 	struct drv_context *drvc;
-	int ret, skip, i, device_count;
+	int ret, i, device_count;
+	char connection_id[64];
 
-	drvc = di->priv;
+	di = sdi->driver;
+	drvc = di->context;
 	usb = sdi->conn;
 
 	if (sdi->status == SR_ST_ACTIVE)
 		/* Device is already in use. */
 		return SR_ERR;
 
-	skip = 0;
 	device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
 	if (device_count < 0) {
 		sr_err("Failed to get device list: %s.",
@@ -264,28 +266,18 @@ static int logic16_dev_open(struct sr_dev_inst *sdi)
 	}
 
 	for (i = 0; i < device_count; i++) {
-		if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
-			sr_err("Failed to get device descriptor: %s.",
-			       libusb_error_name(ret));
-			continue;
-		}
+		libusb_get_device_descriptor(devlist[i], &des);
 
 		if (des.idVendor != LOGIC16_VID || des.idProduct != LOGIC16_PID)
 			continue;
 
-		if (sdi->status == SR_ST_INITIALIZING) {
-			if (skip != sdi->index) {
-				/* Skip devices of this type that aren't the one we want. */
-				skip += 1;
-				continue;
-			}
-		} else if (sdi->status == SR_ST_INACTIVE) {
+		if ((sdi->status == SR_ST_INITIALIZING) ||
+				(sdi->status == SR_ST_INACTIVE)) {
 			/*
-			 * This device is fully enumerated, so we need to find
-			 * this device by vendor, product, bus and address.
+			 * Check device by its physical USB bus/port address.
 			 */
-			if (libusb_get_bus_number(devlist[i]) != usb->bus
-			    || libusb_get_device_address(devlist[i]) != usb->address)
+			usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+			if (strcmp(sdi->connection_id, connection_id))
 				/* This is not the one. */
 				continue;
 		}
@@ -323,8 +315,8 @@ static int logic16_dev_open(struct sr_dev_inst *sdi)
 		}
 
 		sdi->status = SR_ST_ACTIVE;
-		sr_info("Opened device %d on %d.%d, interface %d.",
-			sdi->index, usb->bus, usb->address, USB_INTERFACE);
+		sr_info("Opened device on %d.%d (logical) / %s (physical), interface %d.",
+			usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
 
 		break;
 	}
@@ -397,11 +389,11 @@ static int dev_close(struct sr_dev_inst *sdi)
 	struct sr_usb_dev_inst *usb;
 
 	usb = sdi->conn;
-	if (usb->devhdl == NULL)
+	if (!usb->devhdl)
 		return SR_ERR;
 
-	sr_info("Closing device %d on %d.%d interface %d.",
-		sdi->index, usb->bus, usb->address, USB_INTERFACE);
+	sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
+		usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
 	libusb_release_interface(usb->devhdl, USB_INTERFACE);
 	libusb_close(usb->devhdl);
 	usb->devhdl = NULL;
@@ -410,24 +402,22 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	int ret;
 	struct drv_context *drvc;
 
-	if (!(drvc = di->priv))
+	if (!(drvc = di->context))
 		/* Can get called on an unused driver, doesn't matter. */
 		return SR_OK;
 
-
 	ret = std_dev_clear(di, NULL);
 	g_free(drvc);
-	di->priv = NULL;
 
 	return ret;
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -458,6 +448,12 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		devc = sdi->priv;
 		*data = g_variant_new_uint64(devc->cur_samplerate);
 		break;
+	case SR_CONF_CAPTURE_RATIO:
+		if (!sdi)
+			return SR_ERR;
+		devc = sdi->priv;
+		*data = g_variant_new_uint64(devc->capture_ratio);
+		break;
 	case SR_CONF_VOLTAGE_THRESHOLD:
 		if (!sdi)
 			return SR_ERR;
@@ -481,7 +477,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -504,6 +500,10 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_samples = g_variant_get_uint64(data);
 		break;
+	case SR_CONF_CAPTURE_RATIO:
+		devc->capture_ratio = g_variant_get_uint64(data);
+		ret = (devc->capture_ratio > 100) ? SR_ERR : SR_OK;
+		break;
 	case SR_CONF_VOLTAGE_THRESHOLD:
 		g_variant_get(data, "(dd)", &low, &high);
 		ret = SR_ERR_ARG;
@@ -524,7 +524,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return ret;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	GVariant *gvar, *range[2];
@@ -538,12 +538,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	ret = SR_OK;
 	switch (key) {
 	case SR_CONF_SCAN_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLERATE:
 		g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
@@ -562,6 +562,11 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		}
 		*data = g_variant_builder_end(&gvb);
 		break;
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				soft_trigger_matches, ARRAY_SIZE(soft_trigger_matches),
+				sizeof(int32_t));
+		break;
 	default:
 		return SR_ERR_NA;
 	}
@@ -573,7 +578,7 @@ static void abort_acquisition(struct dev_context *devc)
 {
 	int i;
 
-	devc->num_samples = -1;
+	devc->sent_samples = -1;
 
 	for (i = devc->num_transfers - 1; i >= 0; i--) {
 		if (devc->transfers[i])
@@ -627,9 +632,6 @@ static int configure_channels(const struct sr_dev_inst *sdi)
 	struct sr_channel *ch;
 	GSList *l;
 	uint16_t channel_bit;
-#ifdef WORDS_BIGENDIAN
-	int i;
-#endif
 
 	devc = sdi->priv;
 
@@ -656,16 +658,6 @@ static int configure_channels(const struct sr_dev_inst *sdi)
 		devc->channel_masks[devc->num_channels++] = channel_bit;
 	}
 
-	if (devc->cur_channels & ~0xff) {
-		devc->unitsize = 2;
-	} else {
-#ifdef WORDS_BIGENDIAN
-		for (i = 0; i < devc->num_channels; i++)
-			devc->channel_masks[i] >>= 8;
-#endif
-		devc->unitsize = 1;
-	}
-
 	return SR_OK;
 }
 
@@ -675,18 +667,20 @@ static int receive_data(int fd, int revents, void *cb_data)
 	struct dev_context *devc;
 	struct drv_context *drvc;
 	const struct sr_dev_inst *sdi;
+	struct sr_dev_driver *di;
 
 	(void)fd;
 	(void)revents;
 
 	sdi = cb_data;
-	drvc = di->priv;
+	di = sdi->driver;
+	drvc = di->context;
 	devc = sdi->priv;
 
 	tv.tv_sec = tv.tv_usec = 0;
 	libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 
-	if (devc->num_samples == -2) {
+	if (devc->sent_samples == -2) {
 		logic16_abort_acquisition(sdi);
 		abort_acquisition(devc);
 	}
@@ -696,9 +690,11 @@ static int receive_data(int fd, int revents, void *cb_data)
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct dev_context *devc;
 	struct drv_context *drvc;
 	struct sr_usb_dev_inst *usb;
+	struct sr_trigger *trigger;
 	struct libusb_transfer *transfer;
 	unsigned int i, timeout, num_transfers;
 	int ret;
@@ -708,7 +704,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	drvc = di->priv;
+	drvc = di->context;
 	devc = sdi->priv;
 	usb = sdi->conn;
 
@@ -719,11 +715,22 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	}
 
 	devc->cb_data = cb_data;
-	devc->num_samples = 0;
+	devc->sent_samples = 0;
 	devc->empty_transfer_count = 0;
 	devc->cur_channel = 0;
 	memset(devc->channel_data, 0, sizeof(devc->channel_data));
 
+	if ((trigger = sr_session_trigger_get(sdi->session))) {
+		int pre_trigger_samples = 0;
+		if (devc->limit_samples > 0)
+			pre_trigger_samples = devc->capture_ratio * devc->limit_samples/100;
+		devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
+		if (!devc->stl)
+			return SR_ERR_MALLOC;
+		devc->trigger_fired = FALSE;
+	} else
+		devc->trigger_fired = TRUE;
+
 	timeout = get_timeout(devc);
 	num_transfers = get_number_of_transfers(devc);
 	size = get_buffer_size(devc);
@@ -765,7 +772,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 		transfer = libusb_alloc_transfer(0);
 		libusb_fill_bulk_transfer(transfer, usb->devhdl,
 				2 | LIBUSB_ENDPOINT_IN, buf, size,
-				logic16_receive_transfer, devc, timeout);
+				logic16_receive_transfer, (void *)sdi, timeout);
 		if ((ret = libusb_submit_transfer(transfer)) != 0) {
 			sr_err("Failed to submit transfer: %s.",
 			       libusb_error_name(ret));
@@ -780,7 +787,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 	devc->ctx = drvc->sr_ctx;
 
-	usb_source_add(devc->ctx, timeout, receive_data, (void *)sdi);
+	usb_source_add(sdi->session, devc->ctx, timeout, receive_data, (void *)sdi);
 
 	/* Send header packet to the session bus. */
 	std_session_send_df_header(cb_data, LOG_PREFIX);
@@ -825,5 +832,5 @@ SR_PRIV struct sr_dev_driver saleae_logic16_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/saleae-logic16/protocol.c b/src/hardware/saleae-logic16/protocol.c
similarity index 51%
rename from hardware/saleae-logic16/protocol.c
rename to src/hardware/saleae-logic16/protocol.c
index e2c5f88..5566b85 100644
--- a/hardware/saleae-logic16/protocol.c
+++ b/src/hardware/saleae-logic16/protocol.c
@@ -19,8 +19,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "protocol.h"
-
+#include <config.h>
 #include <stdint.h>
 #include <string.h>
 #include <glib.h>
@@ -28,11 +27,12 @@
 #include <stdio.h>
 #include <errno.h>
 #include <math.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
+#include "protocol.h"
 
-#define FPGA_FIRMWARE_18	FIRMWARE_DIR"/saleae-logic16-fpga-18.bitstream"
-#define FPGA_FIRMWARE_33	FIRMWARE_DIR"/saleae-logic16-fpga-33.bitstream"
+#define FPGA_FIRMWARE_18	"saleae-logic16-fpga-18.bitstream"
+#define FPGA_FIRMWARE_33	"saleae-logic16-fpga-33.bitstream"
 
 #define MAX_SAMPLE_RATE		SR_MHZ(100)
 #define MAX_4CH_SAMPLE_RATE	SR_MHZ(50)
@@ -66,6 +66,101 @@
 
 #define MAX_EMPTY_TRANSFERS		64
 
+/* Register mappings for old and new bitstream versions */
+
+enum fpga_register_id {
+	FPGA_REGISTER_VERSION,
+	FPGA_REGISTER_STATUS_CONTROL,
+	FPGA_REGISTER_CHANNEL_SELECT_LOW,
+	FPGA_REGISTER_CHANNEL_SELECT_HIGH,
+	FPGA_REGISTER_SAMPLE_RATE_DIVISOR,
+	FPGA_REGISTER_LED_BRIGHTNESS,
+	FPGA_REGISTER_PRIMER_DATA1,
+	FPGA_REGISTER_PRIMER_CONTROL,
+	FPGA_REGISTER_MODE,
+	FPGA_REGISTER_PRIMER_DATA2,
+	FPGA_REGISTER_MAX = FPGA_REGISTER_PRIMER_DATA2
+};
+
+enum fpga_status_control_bit {
+	FPGA_STATUS_CONTROL_BIT_RUNNING,
+	FPGA_STATUS_CONTROL_BIT_UPDATE,
+	FPGA_STATUS_CONTROL_BIT_UNKNOWN1,
+	FPGA_STATUS_CONTROL_BIT_OVERFLOW,
+	FPGA_STATUS_CONTROL_BIT_UNKNOWN2,
+	FPGA_STATUS_CONTROL_BIT_MAX = FPGA_STATUS_CONTROL_BIT_UNKNOWN2
+};
+
+enum fpga_mode_bit {
+	FPGA_MODE_BIT_CLOCK,
+	FPGA_MODE_BIT_UNKNOWN1,
+	FPGA_MODE_BIT_UNKNOWN2,
+	FPGA_MODE_BIT_MAX = FPGA_MODE_BIT_UNKNOWN2
+};
+
+static const uint8_t fpga_register_map_old[FPGA_REGISTER_MAX + 1] = {
+	[FPGA_REGISTER_VERSION]			= 0,
+	[FPGA_REGISTER_STATUS_CONTROL]		= 1,
+	[FPGA_REGISTER_CHANNEL_SELECT_LOW]	= 2,
+	[FPGA_REGISTER_CHANNEL_SELECT_HIGH]	= 3,
+	[FPGA_REGISTER_SAMPLE_RATE_DIVISOR]	= 4,
+	[FPGA_REGISTER_LED_BRIGHTNESS]		= 5,
+	[FPGA_REGISTER_PRIMER_DATA1]		= 6,
+	[FPGA_REGISTER_PRIMER_CONTROL]		= 7,
+	[FPGA_REGISTER_MODE]			= 10,
+	[FPGA_REGISTER_PRIMER_DATA2]		= 12,
+};
+
+static const uint8_t fpga_register_map_new[FPGA_REGISTER_MAX + 1] = {
+	[FPGA_REGISTER_VERSION]			= 7,
+	[FPGA_REGISTER_STATUS_CONTROL]		= 15,
+	[FPGA_REGISTER_CHANNEL_SELECT_LOW]	= 1,
+	[FPGA_REGISTER_CHANNEL_SELECT_HIGH]	= 6,
+	[FPGA_REGISTER_SAMPLE_RATE_DIVISOR]	= 11,
+	[FPGA_REGISTER_LED_BRIGHTNESS]		= 5,
+	[FPGA_REGISTER_PRIMER_DATA1]		= 14,
+	[FPGA_REGISTER_PRIMER_CONTROL]		= 2,
+	[FPGA_REGISTER_MODE]			= 4,
+	[FPGA_REGISTER_PRIMER_DATA2]		= 3,
+};
+
+static const uint8_t fpga_status_control_bit_map_old[FPGA_STATUS_CONTROL_BIT_MAX + 1] = {
+	[FPGA_STATUS_CONTROL_BIT_RUNNING]	= 0x01,
+	[FPGA_STATUS_CONTROL_BIT_UPDATE]	= 0x02,
+	[FPGA_STATUS_CONTROL_BIT_UNKNOWN1]	= 0x08,
+	[FPGA_STATUS_CONTROL_BIT_OVERFLOW]	= 0x20,
+	[FPGA_STATUS_CONTROL_BIT_UNKNOWN2]	= 0x40,
+};
+
+static const uint8_t fpga_status_control_bit_map_new[FPGA_STATUS_CONTROL_BIT_MAX + 1] = {
+	[FPGA_STATUS_CONTROL_BIT_RUNNING]	= 0x20,
+	[FPGA_STATUS_CONTROL_BIT_UPDATE]	= 0x08,
+	[FPGA_STATUS_CONTROL_BIT_UNKNOWN1]	= 0x10,
+	[FPGA_STATUS_CONTROL_BIT_OVERFLOW]	= 0x01,
+	[FPGA_STATUS_CONTROL_BIT_UNKNOWN2]	= 0x04,
+};
+
+static const uint8_t fpga_mode_bit_map_old[FPGA_MODE_BIT_MAX + 1] = {
+	[FPGA_MODE_BIT_CLOCK]		= 0x01,
+	[FPGA_MODE_BIT_UNKNOWN1]	= 0x40,
+	[FPGA_MODE_BIT_UNKNOWN2]	= 0x80,
+};
+
+static const uint8_t fpga_mode_bit_map_new[FPGA_MODE_BIT_MAX + 1] = {
+	[FPGA_MODE_BIT_CLOCK]		= 0x04,
+	[FPGA_MODE_BIT_UNKNOWN1]	= 0x80,
+	[FPGA_MODE_BIT_UNKNOWN2]	= 0x01,
+};
+
+#define FPGA_REG(x) \
+	(devc->fpga_register_map[FPGA_REGISTER_ ## x])
+
+#define FPGA_STATUS_CONTROL(x) \
+	(devc->fpga_status_control_bit_map[FPGA_STATUS_CONTROL_BIT_ ## x])
+
+#define FPGA_MODE(x) \
+	(devc->fpga_mode_bit_map[FPGA_MODE_BIT_ ## x])
+
 static void encrypt(uint8_t *dest, const uint8_t *src, uint8_t cnt)
 {
 	uint8_t state1 = 0x9b, state2 = 0x54;
@@ -107,7 +202,7 @@ static int do_ep1_command(const struct sr_dev_inst *sdi,
 	usb = sdi->conn;
 
 	if (cmd_len < 1 || cmd_len > 64 || reply_len > 64 ||
-	    command == NULL || (reply_len > 0 && reply == NULL))
+	    !command || (reply_len > 0 && !reply))
 		return SR_ERR_ARG;
 
 	encrypt(buf, command, cmd_len);
@@ -120,7 +215,7 @@ static int do_ep1_command(const struct sr_dev_inst *sdi,
 	}
 	if (xfer != cmd_len) {
 		sr_dbg("Failed to send EP1 command 0x%02x: incorrect length "
-		       "%d != %d.", xfer, cmd_len);
+		       "%d != %d.", command[0], xfer, cmd_len);
 		return SR_ERR;
 	}
 
@@ -136,7 +231,7 @@ static int do_ep1_command(const struct sr_dev_inst *sdi,
 	}
 	if (xfer != reply_len) {
 		sr_dbg("Failed to receive reply to EP1 command 0x%02x: "
-		       "incorrect length %d != %d.", xfer, reply_len);
+		       "incorrect length %d != %d.", command[0], xfer, reply_len);
 		return SR_ERR;
 	}
 
@@ -165,7 +260,7 @@ static int upload_led_table(const struct sr_dev_inst *sdi,
 	uint8_t chunk, command[64];
 	int ret;
 
-	if (cnt < 1 || cnt + offset > 64 || table == NULL)
+	if (cnt < 1 || cnt + offset > 64 || !table)
 		return SR_ERR_ARG;
 
 	while (cnt > 0) {
@@ -248,32 +343,76 @@ static uint8_t map_eeprom_data(uint8_t v)
 	return (((v ^ 0x80) + 0x44) ^ 0xd5) + 0x69;
 }
 
+static int setup_register_mapping(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	int ret;
+
+	devc = sdi->priv;
+
+	if (devc->fpga_variant != FPGA_VARIANT_MCUPRO) {
+		uint8_t reg0, reg7;
+
+		/*
+		 * Check for newer bitstream version by polling the
+		 * version register at the old and new location.
+		 */
+
+		if ((ret = read_fpga_register(sdi, 0 /* No mapping */, &reg0)) != SR_OK)
+			return ret;
+
+		if ((ret = read_fpga_register(sdi, 7 /* No mapping */, &reg7)) != SR_OK)
+			return ret;
+
+		if (reg0 == 0 && reg7 > 0x10) {
+			sr_info("Original Saleae Logic16 using new bitstream.");
+			devc->fpga_variant = FPGA_VARIANT_ORIGINAL_NEW_BITSTREAM;
+		} else {
+			sr_info("Original Saleae Logic16 using old bitstream.");
+			devc->fpga_variant = FPGA_VARIANT_ORIGINAL;
+		}
+	}
+
+	if (devc->fpga_variant == FPGA_VARIANT_ORIGINAL_NEW_BITSTREAM) {
+		devc->fpga_register_map = fpga_register_map_new;
+		devc->fpga_status_control_bit_map = fpga_status_control_bit_map_new;
+		devc->fpga_mode_bit_map = fpga_mode_bit_map_new;
+	} else {
+		devc->fpga_register_map = fpga_register_map_old;
+		devc->fpga_status_control_bit_map = fpga_status_control_bit_map_old;
+		devc->fpga_mode_bit_map = fpga_mode_bit_map_old;
+	}
+
+	return SR_OK;
+}
+
 static int prime_fpga(const struct sr_dev_inst *sdi)
 {
+	struct dev_context *devc = sdi->priv;
 	uint8_t eeprom_data[16];
-	uint8_t old_reg_10, version;
+	uint8_t old_mode_reg, version;
 	uint8_t regs[8][2] = {
-		{10, 0x00},
-		{10, 0x40},
-		{12, 0},
-		{10, 0xc0},
-		{10, 0x40},
-		{6, 0},
-		{7, 1},
-		{7, 0}
+		{FPGA_REG(MODE), 0x00},
+		{FPGA_REG(MODE), FPGA_MODE(UNKNOWN1)},
+		{FPGA_REG(PRIMER_DATA2), 0},
+		{FPGA_REG(MODE), FPGA_MODE(UNKNOWN1) | FPGA_MODE(UNKNOWN2)},
+		{FPGA_REG(MODE), FPGA_MODE(UNKNOWN1)},
+		{FPGA_REG(PRIMER_DATA1), 0},
+		{FPGA_REG(PRIMER_CONTROL), 1},
+		{FPGA_REG(PRIMER_CONTROL), 0}
 	};
 	int i, ret;
 
 	if ((ret = read_eeprom(sdi, 16, 16, eeprom_data)) != SR_OK)
 		return ret;
 
-	if ((ret = read_fpga_register(sdi, 10, &old_reg_10)) != SR_OK)
+	if ((ret = read_fpga_register(sdi, FPGA_REG(MODE), &old_mode_reg)) != SR_OK)
 		return ret;
 
-	regs[0][1] = (old_reg_10 &= 0x7f);
-	regs[1][1] |= old_reg_10;
-	regs[3][1] |= old_reg_10;
-	regs[4][1] |= old_reg_10;
+	regs[0][1] = (old_mode_reg &= ~FPGA_MODE(UNKNOWN2));
+	regs[1][1] |= old_mode_reg;
+	regs[3][1] |= old_mode_reg;
+	regs[4][1] |= old_mode_reg;
 
 	for (i = 0; i < 16; i++) {
 		regs[2][1] = eeprom_data[i];
@@ -286,14 +425,14 @@ static int prime_fpga(const struct sr_dev_inst *sdi)
 			return ret;
 	}
 
-	if ((ret = write_fpga_register(sdi, 10, old_reg_10)) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(MODE), old_mode_reg)) != SR_OK)
 		return ret;
 
-	if ((ret = read_fpga_register(sdi, 0, &version)) != SR_OK)
+	if ((ret = read_fpga_register(sdi, FPGA_REG(VERSION), &version)) != SR_OK)
 		return ret;
 
-	if (version != 0x10) {
-		sr_err("Invalid FPGA bitstream version: 0x%02x != 0x10.", version);
+	if (version != 0x10 && version != 0x13 && version != 0x40 && version != 0x41) {
+		sr_err("Unsupported FPGA version: 0x%02x.", version);
 		return SR_ERR;
 	}
 
@@ -308,7 +447,7 @@ static void make_heartbeat(uint8_t *table, int len)
 	len >>= 3;
 	for (i = 0; i < 2; i++)
 		for (j = 0; j < len; j++)
-			*table++ = sin(j * M_PI / len) * 255;
+			*table++ = sin(j * G_PI / len) * 255;
 }
 
 static int configure_led(const struct sr_dev_inst *sdi)
@@ -326,64 +465,74 @@ static int configure_led(const struct sr_dev_inst *sdi)
 static int upload_fpga_bitstream(const struct sr_dev_inst *sdi,
 				 enum voltage_range vrange)
 {
+	uint64_t sum;
+	struct sr_resource bitstream;
 	struct dev_context *devc;
-	int offset, chunksize, ret;
-	const char *filename;
-	uint8_t len, buf[256 * 62], command[64];
-	FILE *fw;
+	struct drv_context *drvc;
+	const char *name;
+	ssize_t chunksize;
+	int ret;
+	uint8_t command[64];
 
 	devc = sdi->priv;
+	drvc = sdi->driver->context;
 
 	if (devc->cur_voltage_range == vrange)
 		return SR_OK;
 
-	switch (vrange) {
-	case VOLTAGE_RANGE_18_33_V:
-		filename = FPGA_FIRMWARE_18;
-		break;
-	case VOLTAGE_RANGE_5_V:
-		filename = FPGA_FIRMWARE_33;
-		break;
-	default:
-		sr_err("Unsupported voltage range.");
-		return SR_ERR;
-	}
-
-	sr_info("Uploading FPGA bitstream at %s.", filename);
-	if ((fw = g_fopen(filename, "rb")) == NULL) {
-		sr_err("Unable to open bitstream file %s for reading: %s.",
-		       filename, strerror(errno));
-		return SR_ERR;
-	}
+	if (devc->fpga_variant != FPGA_VARIANT_MCUPRO) {
+		switch (vrange) {
+		case VOLTAGE_RANGE_18_33_V:
+			name = FPGA_FIRMWARE_18;
+			break;
+		case VOLTAGE_RANGE_5_V:
+			name = FPGA_FIRMWARE_33;
+			break;
+		default:
+			sr_err("Unsupported voltage range.");
+			return SR_ERR;
+		}
 
-	buf[0] = COMMAND_FPGA_UPLOAD_INIT;
-	if ((ret = do_ep1_command(sdi, buf, 1, NULL, 0)) != SR_OK) {
-		fclose(fw);
-		return ret;
-	}
+		sr_info("Uploading FPGA bitstream '%s'.", name);
+		ret = sr_resource_open(drvc->sr_ctx, &bitstream,
+				SR_RESOURCE_FIRMWARE, name);
+		if (ret != SR_OK)
+			return ret;
 
-	while (1) {
-		chunksize = fread(buf, 1, sizeof(buf), fw);
-		if (chunksize == 0)
-			break;
+		command[0] = COMMAND_FPGA_UPLOAD_INIT;
+		if ((ret = do_ep1_command(sdi, command, 1, NULL, 0)) != SR_OK) {
+			sr_resource_close(drvc->sr_ctx, &bitstream);
+			return ret;
+		}
 
-		for (offset = 0; offset < chunksize; offset += 62) {
-			len = (offset + 62 > chunksize ?
-				chunksize - offset : 62);
+		sum = 0;
+		while (1) {
+			chunksize = sr_resource_read(drvc->sr_ctx, &bitstream,
+					&command[2], sizeof(command) - 2);
+			if (chunksize < 0) {
+				sr_resource_close(drvc->sr_ctx, &bitstream);
+				return SR_ERR;
+			}
+			if (chunksize == 0)
+				break;
 			command[0] = COMMAND_FPGA_UPLOAD_SEND_DATA;
-			command[1] = len;
-			memcpy(command + 2, buf + offset, len);
-			ret = do_ep1_command(sdi, command, len + 2, NULL, 0);
+			command[1] = chunksize;
+
+			ret = do_ep1_command(sdi, command, chunksize + 2,
+					NULL, 0);
 			if (ret != SR_OK) {
-				fclose(fw);
+				sr_resource_close(drvc->sr_ctx, &bitstream);
 				return ret;
 			}
+			sum += chunksize;
 		}
-
-		sr_info("Uploaded %d bytes.", chunksize);
+		sr_resource_close(drvc->sr_ctx, &bitstream);
+		sr_info("FPGA bitstream upload (%" PRIu64 " bytes) done.", sum);
 	}
-	fclose(fw);
-	sr_info("FPGA bitstream upload done.");
+
+	/* This needs to be called before accessing any FPGA registers. */
+	if ((ret = setup_register_mapping(sdi)) != SR_OK)
+		return ret;
 
 	if ((ret = prime_fpga(sdi)) != SR_OK)
 		return ret;
@@ -420,7 +569,7 @@ static int abort_acquisition_sync(const struct sr_dev_inst *sdi)
 SR_PRIV int logic16_setup_acquisition(const struct sr_dev_inst *sdi,
 			     uint64_t samplerate, uint16_t channels)
 {
-	uint8_t clock_select, reg1, reg10;
+	uint8_t clock_select, sta_con_reg, mode_reg;
 	uint64_t div;
 	int i, ret, nchan = 0;
 	struct dev_context *devc;
@@ -461,50 +610,52 @@ SR_PRIV int logic16_setup_acquisition(const struct sr_dev_inst *sdi,
 	if (ret != SR_OK)
 		return ret;
 
-	if ((ret = read_fpga_register(sdi, 1, &reg1)) != SR_OK)
+	if ((ret = read_fpga_register(sdi, FPGA_REG(STATUS_CONTROL), &sta_con_reg)) != SR_OK)
 		return ret;
 
-	if (reg1 != 0x08) {
-		sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x08.", reg1);
-		return SR_ERR;
+	/* Ignore FIFO overflow on previous capture */
+	sta_con_reg &= ~FPGA_STATUS_CONTROL(OVERFLOW);
+
+	if (devc->fpga_variant != FPGA_VARIANT_MCUPRO && sta_con_reg != FPGA_STATUS_CONTROL(UNKNOWN1)) {
+		sr_dbg("Invalid state at acquisition setup register 1: 0x%02x != 0x%02x. "
+		       "Proceeding anyway.", sta_con_reg, FPGA_STATUS_CONTROL(UNKNOWN1));
 	}
 
-	if ((ret = write_fpga_register(sdi, 1, 0x40)) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(STATUS_CONTROL), FPGA_STATUS_CONTROL(UNKNOWN2))) != SR_OK)
 		return ret;
 
-	if ((ret = write_fpga_register(sdi, 10, clock_select)) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(MODE), (clock_select? FPGA_MODE(CLOCK) : 0))) != SR_OK)
 		return ret;
 
-	if ((ret = write_fpga_register(sdi, 4, (uint8_t)(div - 1))) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(SAMPLE_RATE_DIVISOR), (uint8_t)(div - 1))) != SR_OK)
 		return ret;
 
-	if ((ret = write_fpga_register(sdi, 2, (uint8_t)(channels & 0xff))) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(CHANNEL_SELECT_LOW), (uint8_t)(channels & 0xff))) != SR_OK)
 		return ret;
 
-	if ((ret = write_fpga_register(sdi, 3, (uint8_t)(channels >> 8))) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(CHANNEL_SELECT_HIGH), (uint8_t)(channels >> 8))) != SR_OK)
 		return ret;
 
-	if ((ret = write_fpga_register(sdi, 1, 0x42)) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(STATUS_CONTROL), FPGA_STATUS_CONTROL(UNKNOWN2) | FPGA_STATUS_CONTROL(UPDATE))) != SR_OK)
 		return ret;
 
-	if ((ret = write_fpga_register(sdi, 1, 0x40)) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(STATUS_CONTROL), FPGA_STATUS_CONTROL(UNKNOWN2))) != SR_OK)
 		return ret;
 
-	if ((ret = read_fpga_register(sdi, 1, &reg1)) != SR_OK)
+	if ((ret = read_fpga_register(sdi, FPGA_REG(STATUS_CONTROL), &sta_con_reg)) != SR_OK)
 		return ret;
 
-	if (reg1 != 0x48) {
-		sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x48.", reg1);
-		return SR_ERR;
+	if (devc->fpga_variant != FPGA_VARIANT_MCUPRO && sta_con_reg != (FPGA_STATUS_CONTROL(UNKNOWN2) | FPGA_STATUS_CONTROL(UNKNOWN1))) {
+		sr_dbg("Invalid state at acquisition setup register 1: 0x%02x != 0x%02x. "
+		       "Proceeding anyway.", sta_con_reg, FPGA_STATUS_CONTROL(UNKNOWN2) | FPGA_STATUS_CONTROL(UNKNOWN1));
 	}
 
-	if ((ret = read_fpga_register(sdi, 10, &reg10)) != SR_OK)
+	if ((ret = read_fpga_register(sdi, FPGA_REG(MODE), &mode_reg)) != SR_OK)
 		return ret;
 
-	if (reg10 != clock_select) {
-		sr_dbg("Invalid state at acquisition setup: 0x%02x != 0x%02x.",
-		       reg10, clock_select);
-		return SR_ERR;
+	if (devc->fpga_variant != FPGA_VARIANT_MCUPRO && mode_reg != (clock_select? FPGA_MODE(CLOCK) : 0)) {
+		sr_dbg("Invalid state at acquisition setup register 10: 0x%02x != 0x%02x. "
+		       "Proceeding anyway.", mode_reg, (clock_select? FPGA_MODE(CLOCK) : 0));
 	}
 
 	return SR_OK;
@@ -516,11 +667,14 @@ SR_PRIV int logic16_start_acquisition(const struct sr_dev_inst *sdi)
 		COMMAND_START_ACQUISITION,
 	};
 	int ret;
+	struct dev_context *devc;
+
+	devc = sdi->priv;
 
 	if ((ret = do_ep1_command(sdi, command, 1, NULL, 0)) != SR_OK)
 		return ret;
 
-	return write_fpga_register(sdi, 1, 0x41);
+	return write_fpga_register(sdi, FPGA_REG(STATUS_CONTROL), FPGA_STATUS_CONTROL(UNKNOWN2) | FPGA_STATUS_CONTROL(RUNNING));
 }
 
 SR_PRIV int logic16_abort_acquisition(const struct sr_dev_inst *sdi)
@@ -529,33 +683,47 @@ SR_PRIV int logic16_abort_acquisition(const struct sr_dev_inst *sdi)
 		COMMAND_ABORT_ACQUISITION_ASYNC,
 	};
 	int ret;
-	uint8_t reg1, reg8, reg9;
+	uint8_t sta_con_reg;
+	struct dev_context *devc;
+
+	devc = sdi->priv;
 
 	if ((ret = do_ep1_command(sdi, command, 1, NULL, 0)) != SR_OK)
 		return ret;
 
-	if ((ret = write_fpga_register(sdi, 1, 0x00)) != SR_OK)
+	if ((ret = write_fpga_register(sdi, FPGA_REG(STATUS_CONTROL), 0x00)) != SR_OK)
 		return ret;
 
-	if ((ret = read_fpga_register(sdi, 1, &reg1)) != SR_OK)
+	if ((ret = read_fpga_register(sdi, FPGA_REG(STATUS_CONTROL), &sta_con_reg)) != SR_OK)
 		return ret;
 
-	if (reg1 != 0x08) {
-		sr_dbg("Invalid state at acquisition stop: 0x%02x != 0x08.", reg1);
+	if (devc->fpga_variant != FPGA_VARIANT_MCUPRO && (sta_con_reg & ~FPGA_STATUS_CONTROL(OVERFLOW)) != FPGA_STATUS_CONTROL(UNKNOWN1)) {
+		sr_dbg("Invalid state at acquisition stop: 0x%02x != 0x%02x.", sta_con_reg & ~0x20, FPGA_STATUS_CONTROL(UNKNOWN1));
 		return SR_ERR;
 	}
 
-	if ((ret = read_fpga_register(sdi, 8, &reg8)) != SR_OK)
-		return ret;
 
-	if ((ret = read_fpga_register(sdi, 9, &reg9)) != SR_OK)
-		return ret;
+	if (devc->fpga_variant == FPGA_VARIANT_ORIGINAL) {
+		uint8_t reg8, reg9;
+
+		if ((ret = read_fpga_register(sdi, 8, &reg8)) != SR_OK)
+			return ret;
+
+		if ((ret = read_fpga_register(sdi, 9, &reg9)) != SR_OK)
+			return ret;
+	}
+
+	if (devc->fpga_variant != FPGA_VARIANT_MCUPRO && sta_con_reg & FPGA_STATUS_CONTROL(OVERFLOW)) {
+		sr_warn("FIFO overflow, capture data may be truncated.");
+		return SR_ERR;
+	}
 
 	return SR_OK;
 }
 
 SR_PRIV int logic16_init_device(const struct sr_dev_inst *sdi)
 {
+	uint8_t version;
 	struct dev_context *devc;
 	int ret;
 
@@ -569,6 +737,17 @@ SR_PRIV int logic16_init_device(const struct sr_dev_inst *sdi)
 	if ((ret = read_eeprom(sdi, 8, 8, devc->eeprom_data)) != SR_OK)
 		return ret;
 
+	/* mcupro Saleae16 has firmware pre-stored in FPGA.
+	   So, we can query it right away. */
+	if (read_fpga_register(sdi, 0 /* No mapping */, &version) == SR_OK &&
+	    (version == 0x40 || version == 0x41)) {
+		sr_info("mcupro Saleae16 detected.");
+		devc->fpga_variant = FPGA_VARIANT_MCUPRO;
+	} else {
+		sr_info("Original Saleae Logic16 detected.");
+		devc->fpga_variant = FPGA_VARIANT_ORIGINAL;
+	}
+
 	ret = upload_fpga_bitstream(sdi, devc->selected_voltage_range);
 	if (ret != SR_OK)
 		return ret;
@@ -576,28 +755,37 @@ SR_PRIV int logic16_init_device(const struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static void finish_acquisition(struct dev_context *devc)
+static void finish_acquisition(struct sr_dev_inst *sdi)
 {
 	struct sr_datafeed_packet packet;
+	struct dev_context *devc;
+
+	devc = sdi->priv;
 
 	/* Terminate session. */
 	packet.type = SR_DF_END;
 	sr_session_send(devc->cb_data, &packet);
 
 	/* Remove fds from polling. */
-	usb_source_remove(devc->ctx);
+	usb_source_remove(sdi->session, devc->ctx);
 
 	devc->num_transfers = 0;
 	g_free(devc->transfers);
 	g_free(devc->convbuffer);
+	if (devc->stl) {
+		soft_trigger_logic_free(devc->stl);
+		devc->stl = NULL;
+	}
 }
 
 static void free_transfer(struct libusb_transfer *transfer)
 {
+	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
 	unsigned int i;
 
-	devc = transfer->user_data;
+	sdi = transfer->user_data;
+	devc = sdi->priv;
 
 	g_free(transfer->buffer);
 	transfer->buffer = NULL;
@@ -612,7 +800,7 @@ static void free_transfer(struct libusb_transfer *transfer)
 
 	devc->submitted_transfers--;
 	if (devc->submitted_transfers == 0)
-		finish_acquisition(devc);
+		finish_acquisition(sdi);
 }
 
 static void resubmit_transfer(struct libusb_transfer *transfer)
@@ -628,9 +816,8 @@ static void resubmit_transfer(struct libusb_transfer *transfer)
 	sr_err("%s: %s", __func__, libusb_error_name(ret));
 }
 
-static size_t convert_sample_data_16(struct dev_context *devc,
-				     uint8_t *dest, size_t destcnt,
-				     const uint8_t *src, size_t srccnt)
+static size_t convert_sample_data(struct dev_context *devc,
+		uint8_t *dest, size_t destcnt, const uint8_t *src, size_t srccnt)
 {
 	uint16_t *channel_data;
 	int i, cur_channel;
@@ -671,85 +858,35 @@ static size_t convert_sample_data_16(struct dev_context *devc,
 	return ret;
 }
 
-static size_t convert_sample_data_8(struct dev_context *devc,
-				    uint8_t *dest, size_t destcnt,
-				    const uint8_t *src, size_t srccnt)
-{
-	uint8_t *channel_data;
-	int i, cur_channel;
-	size_t ret = 0;
-	uint16_t sample;
-	uint8_t channel_mask;
-
-	srccnt /= 2;
-
-	channel_data = (uint8_t *)devc->channel_data;
-	cur_channel = devc->cur_channel;
-
-	while (srccnt--) {
-		sample = src[0] | (src[1] << 8);
-		src += 2;
-
-		channel_mask = devc->channel_masks[cur_channel];
-
-		for (i = 15; i >= 0; --i, sample >>= 1)
-			if (sample & 1)
-				channel_data[i] |= channel_mask;
-
-		if (++cur_channel == devc->num_channels) {
-			cur_channel = 0;
-			if (destcnt < 16) {
-				sr_err("Conversion buffer too small!");
-				break;
-			}
-			memcpy(dest, channel_data, 16);
-			memset(channel_data, 0, 16);
-			dest += 16;
-			ret += 16;
-			destcnt -= 16;
-		}
-	}
-
-	devc->cur_channel = cur_channel;
-
-	return ret;
-}
-
-static size_t convert_sample_data(struct dev_context *devc,
-				  uint8_t *dest, size_t destcnt,
-				  const uint8_t *src, size_t srccnt,
-				  int unitsize)
-{
-	return (unitsize == 2 ?
-		convert_sample_data_16(devc, dest, destcnt, src, srccnt) :
-		convert_sample_data_8(devc, dest, destcnt, src, srccnt));
-}
-
-SR_PRIV void logic16_receive_transfer(struct libusb_transfer *transfer)
+SR_PRIV void LIBUSB_CALL logic16_receive_transfer(struct libusb_transfer *transfer)
 {
 	gboolean packet_has_error = FALSE;
 	struct sr_datafeed_packet packet;
 	struct sr_datafeed_logic logic;
+	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
-	size_t converted_length;
+	size_t new_samples, num_samples;
+	int trigger_offset;
+	int pre_trigger_samples;
 
-	devc = transfer->user_data;
+	sdi = transfer->user_data;
+	devc = sdi->priv;
 
 	/*
 	 * If acquisition has already ended, just free any queued up
 	 * transfer that come in.
 	 */
-	if (devc->num_samples < 0) {
+	if (devc->sent_samples < 0) {
 		free_transfer(transfer);
 		return;
 	}
 
-	sr_info("receive_transfer(): status %d received %d bytes.",
-		transfer->status, transfer->actual_length);
+	sr_info("receive_transfer(): status %s received %d bytes.",
+		libusb_error_name(transfer->status), transfer->actual_length);
 
 	switch (transfer->status) {
 	case LIBUSB_TRANSFER_NO_DEVICE:
-		devc->num_samples = -2;
+		devc->sent_samples = -2;
 		free_transfer(transfer);
 		return;
 	case LIBUSB_TRANSFER_COMPLETED:
@@ -775,7 +912,7 @@ SR_PRIV void logic16_receive_transfer(struct libusb_transfer *transfer)
 			 * The FX2 gave up. End the acquisition, the frontend
 			 * will work out that the samplecount is short.
 			 */
-			devc->num_samples = -2;
+			devc->sent_samples = -2;
 			free_transfer(transfer);
 		} else {
 			resubmit_transfer(transfer);
@@ -785,31 +922,46 @@ SR_PRIV void logic16_receive_transfer(struct libusb_transfer *transfer)
 		devc->empty_transfer_count = 0;
 	}
 
-	converted_length = convert_sample_data(devc, devc->convbuffer,
-				devc->convbuffer_size, transfer->buffer,
-				transfer->actual_length, devc->unitsize);
-
-	if (converted_length > 0) {
-		/* Cap sample count if needed. */
-		if (devc->limit_samples &&
-		    (uint64_t)devc->num_samples + converted_length
-		    > devc->limit_samples) {
-			converted_length =
-				devc->limit_samples - devc->num_samples;
+	new_samples = convert_sample_data(devc, devc->convbuffer,
+			devc->convbuffer_size, transfer->buffer, transfer->actual_length);
+
+	if (new_samples > 0) {
+		if (devc->trigger_fired) {
+			/* Send the incoming transfer to the session bus. */
+			packet.type = SR_DF_LOGIC;
+			packet.payload = &logic;
+			if (devc->limit_samples &&
+					new_samples > devc->limit_samples - devc->sent_samples)
+				new_samples = devc->limit_samples - devc->sent_samples;
+			logic.length = new_samples * 2;
+			logic.unitsize = 2;
+			logic.data = devc->convbuffer;
+			sr_session_send(devc->cb_data, &packet);
+			devc->sent_samples += new_samples;
+		} else {
+			trigger_offset = soft_trigger_logic_check(devc->stl,
+					devc->convbuffer, new_samples * 2, &pre_trigger_samples);
+			if (trigger_offset > -1) {
+				devc->sent_samples += pre_trigger_samples;
+				packet.type = SR_DF_LOGIC;
+				packet.payload = &logic;
+				num_samples = new_samples - trigger_offset;
+				if (devc->limit_samples &&
+						num_samples > devc->limit_samples - devc->sent_samples)
+					num_samples = devc->limit_samples - devc->sent_samples;
+				logic.length = num_samples * 2;
+				logic.unitsize = 2;
+				logic.data = devc->convbuffer + trigger_offset * 2;
+				sr_session_send(devc->cb_data, &packet);
+				devc->sent_samples += num_samples;
+
+				devc->trigger_fired = TRUE;
+			}
 		}
 
-		/* Send the incoming transfer to the session bus. */
-		packet.type = SR_DF_LOGIC;
-		packet.payload = &logic;
-		logic.length = converted_length * devc->unitsize;
-		logic.unitsize = devc->unitsize;
-		logic.data = devc->convbuffer;
-		sr_session_send(devc->cb_data, &packet);
-
-		devc->num_samples += converted_length;
 		if (devc->limit_samples &&
-		    (uint64_t)devc->num_samples >= devc->limit_samples) {
-			devc->num_samples = -2;
+				(uint64_t)devc->sent_samples >= devc->limit_samples) {
+			devc->sent_samples = -2;
 			free_transfer(transfer);
 			return;
 		}
diff --git a/hardware/saleae-logic16/protocol.h b/src/hardware/saleae-logic16/protocol.h
similarity index 79%
rename from hardware/saleae-logic16/protocol.h
rename to src/hardware/saleae-logic16/protocol.h
index 00f9656..9f83416 100644
--- a/hardware/saleae-logic16/protocol.h
+++ b/src/hardware/saleae-logic16/protocol.h
@@ -24,7 +24,7 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "saleae-logic16"
@@ -35,8 +35,17 @@ enum voltage_range {
 	VOLTAGE_RANGE_5_V,	/* 5V logic */
 };
 
+enum fpga_variant {
+	FPGA_VARIANT_ORIGINAL,
+	FPGA_VARIANT_ORIGINAL_NEW_BITSTREAM,
+	FPGA_VARIANT_MCUPRO    /* mcupro clone v4.6 with Actel FPGA */
+};
+
 /** Private, per-device-instance driver context. */
 struct dev_context {
+	/** Distinguishing between original Logic16 and clones */
+	enum fpga_variant fpga_variant;
+
 	/*
 	 * Since we can't keep track of a Logic16 device after upgrading
 	 * the firmware (it renumerates into a different device address
@@ -51,6 +60,9 @@ struct dev_context {
 	/** Maximum number of samples to capture, if nonzero. */
 	uint64_t limit_samples;
 
+	/** Percent of the samples that should be captured before the trigger. */
+	uint64_t capture_ratio;
+
 	/** The currently configured input voltage of the device. */
 	enum voltage_range cur_voltage_range;
 
@@ -63,19 +75,26 @@ struct dev_context {
 	/* EEPROM data from address 8. */
 	uint8_t eeprom_data[8];
 
-	int64_t num_samples;
+	int64_t sent_samples;
 	int submitted_transfers;
 	int empty_transfer_count;
-	int num_channels, cur_channel, unitsize;
+	int num_channels;
+	int cur_channel;
 	uint16_t channel_masks[16];
 	uint16_t channel_data[16];
 	uint8_t *convbuffer;
 	size_t convbuffer_size;
+	struct soft_trigger_logic *stl;
+	gboolean trigger_fired;
 
 	void *cb_data;
 	unsigned int num_transfers;
 	struct libusb_transfer **transfers;
 	struct sr_context *ctx;
+
+	const uint8_t *fpga_register_map;
+	const uint8_t *fpga_status_control_bit_map;
+	const uint8_t *fpga_mode_bit_map;
 };
 
 SR_PRIV int logic16_setup_acquisition(const struct sr_dev_inst *sdi,
@@ -83,6 +102,6 @@ SR_PRIV int logic16_setup_acquisition(const struct sr_dev_inst *sdi,
 SR_PRIV int logic16_start_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV int logic16_abort_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV int logic16_init_device(const struct sr_dev_inst *sdi);
-SR_PRIV void logic16_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL logic16_receive_transfer(struct libusb_transfer *transfer);
 
 #endif
diff --git a/src/hardware/scpi-pps/api.c b/src/hardware/scpi-pps/api.c
new file mode 100644
index 0000000..a29744e
--- /dev/null
+++ b/src/hardware/scpi-pps/api.c
@@ -0,0 +1,663 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <strings.h>
+#include "scpi.h"
+#include "protocol.h"
+
+SR_PRIV struct sr_dev_driver scpi_pps_driver_info;
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+	SR_CONF_POWER_SUPPLY,
+};
+
+static const struct pps_channel_instance pci[] = {
+	{ SR_MQ_VOLTAGE, SCPI_CMD_GET_MEAS_VOLTAGE, "V" },
+	{ SR_MQ_CURRENT, SCPI_CMD_GET_MEAS_CURRENT, "I" },
+	{ SR_MQ_POWER, SCPI_CMD_GET_MEAS_POWER, "P" },
+	{ SR_MQ_FREQUENCY, SCPI_CMD_GET_MEAS_FREQUENCY, "F" },
+};
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
+{
+	struct dev_context *devc;
+	struct sr_dev_inst *sdi;
+	struct sr_scpi_hw_info *hw_info;
+	struct sr_channel_group *cg;
+	struct sr_channel *ch;
+	const struct scpi_pps *device;
+	struct pps_channel *pch;
+	struct channel_spec *channels;
+	struct channel_group_spec *channel_groups, *cgs;
+	struct pps_channel_group *pcg;
+	GRegex *model_re;
+	GMatchInfo *model_mi;
+	GSList *l;
+	uint64_t mask;
+	unsigned int num_channels, num_channel_groups, ch_num, ch_idx, i, j;
+	int ret;
+	const char *vendor;
+	char ch_name[16];
+
+	if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+		sr_info("Couldn't get IDN response.");
+		return NULL;
+	}
+
+	device = NULL;
+	for (i = 0; i < num_pps_profiles; i++) {
+		vendor = sr_vendor_alias(hw_info->manufacturer);
+		if (g_ascii_strcasecmp(vendor, pps_profiles[i].vendor))
+			continue;
+		model_re = g_regex_new(pps_profiles[i].model, 0, 0, NULL);
+		if (g_regex_match(model_re, hw_info->model, 0, &model_mi))
+			device = &pps_profiles[i];
+		g_match_info_unref(model_mi);
+		g_regex_unref(model_re);
+		if (device)
+			break;
+	}
+	if (!device) {
+		sr_scpi_hw_info_free(hw_info);
+		return NULL;
+	}
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->vendor = g_strdup(vendor);
+	sdi->model = g_strdup(hw_info->model);
+	sdi->version = g_strdup(hw_info->firmware_version);
+	sdi->conn = scpi;
+	sdi->driver = &scpi_pps_driver_info;
+	sdi->inst_type = SR_INST_SCPI;
+	sdi->serial_num = g_strdup(hw_info->serial_number);
+
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->device = device;
+	sdi->priv = devc;
+
+	if (device->num_channels) {
+		/* Static channels and groups. */
+		channels = (struct channel_spec *)device->channels;
+		num_channels = device->num_channels;
+		channel_groups = (struct channel_group_spec *)device->channel_groups;
+		num_channel_groups = device->num_channel_groups;
+	} else {
+		/* Channels and groups need to be probed. */
+		ret = device->probe_channels(sdi, hw_info, &channels, &num_channels,
+				&channel_groups, &num_channel_groups);
+		if (ret != SR_OK) {
+			sr_err("Failed to probe for channels.");
+			return NULL;
+		}
+		/*
+		 * Since these were dynamically allocated, we'll need to free them
+		 * later.
+		 */
+		devc->channels = channels;
+		devc->channel_groups = channel_groups;
+	}
+
+	ch_idx = 0;
+	for (ch_num = 0; ch_num < num_channels; ch_num++) {
+		/* Create one channel per measurable output unit. */
+		for (i = 0; i < ARRAY_SIZE(pci); i++) {
+			if (!scpi_cmd_get(devc->device->commands, pci[i].command))
+				continue;
+			g_snprintf(ch_name, 16, "%s%s", pci[i].prefix,
+					channels[ch_num].name);
+			ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE,
+					ch_name);
+			pch = g_malloc0(sizeof(struct pps_channel));
+			pch->hw_output_idx = ch_num;
+			pch->hwname = channels[ch_num].name;
+			pch->mq = pci[i].mq;
+			ch->priv = pch;
+		}
+	}
+
+	for (i = 0; i < num_channel_groups; i++) {
+		cgs = &channel_groups[i];
+		cg = g_malloc0(sizeof(struct sr_channel_group));
+		cg->name = g_strdup(cgs->name);
+		for (j = 0, mask = 1; j < 64; j++, mask <<= 1) {
+			if (cgs->channel_index_mask & mask) {
+				for (l = sdi->channels; l; l = l->next) {
+					ch = l->data;
+					pch = ch->priv;
+					if (pch->hw_output_idx == j)
+						cg->channels = g_slist_append(cg->channels, ch);
+				}
+			}
+		}
+		pcg = g_malloc0(sizeof(struct pps_channel_group));
+		pcg->features = cgs->features;
+		cg->priv = pcg;
+		sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+	}
+
+	sr_scpi_hw_info_free(hw_info);
+	hw_info = NULL;
+
+	scpi_cmd(sdi, devc->device->commands, SCPI_CMD_LOCAL);
+
+	return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	return sr_scpi_scan(di->context, options, probe_device);
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_scpi_dev_inst *scpi;
+	GVariant *beeper;
+
+	if (sdi->status != SR_ST_INACTIVE)
+		return SR_ERR;
+
+	scpi = sdi->conn;
+	if (sr_scpi_open(scpi) < 0)
+		return SR_ERR;
+
+	sdi->status = SR_ST_ACTIVE;
+
+	devc = sdi->priv;
+	scpi_cmd(sdi, devc->device->commands, SCPI_CMD_REMOTE);
+	devc = sdi->priv;
+	devc->beeper_was_set = FALSE;
+	if (scpi_cmd_resp(sdi, devc->device->commands, &beeper,
+			G_VARIANT_TYPE_BOOLEAN, SCPI_CMD_BEEPER) == SR_OK) {
+		if (g_variant_get_boolean(beeper)) {
+			devc->beeper_was_set = TRUE;
+			scpi_cmd(sdi, devc->device->commands, SCPI_CMD_BEEPER_DISABLE);
+		}
+		g_variant_unref(beeper);
+	}
+
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct sr_scpi_dev_inst *scpi;
+	struct dev_context *devc;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+	scpi = sdi->conn;
+	if (scpi) {
+		if (devc->beeper_was_set)
+			scpi_cmd(sdi, devc->device->commands, SCPI_CMD_BEEPER_ENABLE);
+		scpi_cmd(sdi, devc->device->commands, SCPI_CMD_LOCAL);
+		sr_scpi_close(scpi);
+		sdi->status = SR_ST_INACTIVE;
+	}
+
+	return SR_OK;
+}
+
+static void clear_helper(void *priv)
+{
+	struct dev_context *devc;
+
+	devc = priv;
+	g_free(devc->channels);
+	g_free(devc->channel_groups);
+	g_free(devc);
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, clear_helper);
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	const GVariantType *gvtype;
+	unsigned int i;
+	int cmd, ret;
+	const char *s;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	if (cg) {
+		/*
+		 * These options only apply to channel groups with a single
+		 * channel -- they're per-channel settings for the device.
+		 */
+
+		/*
+		 * Config keys are handled below depending on whether a channel
+		 * group was provided by the frontend. However some of these
+		 * take a CG on one PPS but not on others. Check the device's
+		 * profile for that here, and NULL out the channel group as needed.
+		 */
+		for (i = 0; i < devc->device->num_devopts; i++) {
+			if (devc->device->devopts[i] == key) {
+				cg = NULL;
+				break;
+			}
+		}
+	}
+
+	gvtype = NULL;
+	cmd = -1;
+	switch (key) {
+	case SR_CONF_ENABLED:
+		gvtype = G_VARIANT_TYPE_BOOLEAN;
+		cmd = SCPI_CMD_GET_OUTPUT_ENABLED;
+		break;
+	case SR_CONF_VOLTAGE:
+		gvtype = G_VARIANT_TYPE_DOUBLE;
+		cmd = SCPI_CMD_GET_MEAS_VOLTAGE;
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		gvtype = G_VARIANT_TYPE_DOUBLE;
+		cmd = SCPI_CMD_GET_VOLTAGE_TARGET;
+		break;
+	case SR_CONF_OUTPUT_FREQUENCY:
+		gvtype = G_VARIANT_TYPE_DOUBLE;
+		cmd = SCPI_CMD_GET_MEAS_FREQUENCY;
+		break;
+	case SR_CONF_OUTPUT_FREQUENCY_TARGET:
+		gvtype = G_VARIANT_TYPE_DOUBLE;
+		cmd = SCPI_CMD_GET_FREQUENCY_TARGET;
+		break;
+	case SR_CONF_CURRENT:
+		gvtype = G_VARIANT_TYPE_DOUBLE;
+		cmd = SCPI_CMD_GET_MEAS_CURRENT;
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		gvtype = G_VARIANT_TYPE_DOUBLE;
+		cmd = SCPI_CMD_GET_CURRENT_LIMIT;
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+		gvtype = G_VARIANT_TYPE_BOOLEAN;
+		cmd = SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ENABLED;
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE:
+		gvtype = G_VARIANT_TYPE_BOOLEAN;
+		cmd = SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ACTIVE;
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+		gvtype = G_VARIANT_TYPE_DOUBLE;
+		cmd = SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD;
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+		gvtype = G_VARIANT_TYPE_BOOLEAN;
+		cmd = SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ENABLED;
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE:
+		gvtype = G_VARIANT_TYPE_BOOLEAN;
+		cmd = SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ACTIVE;
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+		gvtype = G_VARIANT_TYPE_DOUBLE;
+		cmd = SCPI_CMD_GET_OVER_CURRENT_PROTECTION_THRESHOLD;
+		break;
+	case SR_CONF_OVER_TEMPERATURE_PROTECTION:
+		gvtype = G_VARIANT_TYPE_BOOLEAN;
+		cmd = SCPI_CMD_GET_OVER_TEMPERATURE_PROTECTION;
+		break;
+	case SR_CONF_REGULATION:
+		gvtype = G_VARIANT_TYPE_STRING;
+		cmd = SCPI_CMD_GET_OUTPUT_REGULATION;
+	}
+	if (!gvtype)
+		return SR_ERR_NA;
+
+	if (cg)
+		select_channel(sdi, cg->channels->data);
+	ret = scpi_cmd_resp(sdi, devc->device->commands, data, gvtype, cmd);
+
+	if (cmd == SCPI_CMD_GET_OUTPUT_REGULATION) {
+		/*
+		 * The Rigol DP800 series return CV/CC/UR, Philips PM2800
+		 * return VOLT/CURR. We always return a GVariant string in
+		 * the Rigol notation.
+		 */
+		s = g_variant_get_string(*data, NULL);
+		if (!strcmp(s, "VOLT")) {
+			g_variant_unref(*data);
+			*data = g_variant_new_string("CV");
+		} else if (!strcmp(s, "CURR")) {
+			g_variant_unref(*data);
+			*data = g_variant_new_string("CC");
+		}
+
+		s = g_variant_get_string(*data, NULL);
+		if (strcmp(s, "CV") && strcmp(s, "CC") && strcmp(s, "UR")) {
+			sr_dbg("Unknown response to SCPI_CMD_GET_OUTPUT_REGULATION: %s", s);
+			ret = SR_ERR_DATA;
+		}
+	}
+
+	return ret;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	double d;
+	int ret;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (cg)
+		/* Channel group specified. */
+		select_channel(sdi, cg->channels->data);
+
+	devc = sdi->priv;
+
+	switch (key) {
+	case SR_CONF_ENABLED:
+		if (g_variant_get_boolean(data))
+			ret = scpi_cmd(sdi, devc->device->commands,
+					SCPI_CMD_SET_OUTPUT_ENABLE);
+		else
+			ret = scpi_cmd(sdi, devc->device->commands,
+					SCPI_CMD_SET_OUTPUT_DISABLE);
+		break;
+	case SR_CONF_VOLTAGE_TARGET:
+		d = g_variant_get_double(data);
+		ret = scpi_cmd(sdi, devc->device->commands,
+				SCPI_CMD_SET_VOLTAGE_TARGET, d);
+		break;
+	case SR_CONF_OUTPUT_FREQUENCY_TARGET:
+		d = g_variant_get_double(data);
+		ret = scpi_cmd(sdi, devc->device->commands,
+				SCPI_CMD_SET_FREQUENCY_TARGET, d);
+		break;
+	case SR_CONF_CURRENT_LIMIT:
+		d = g_variant_get_double(data);
+		ret = scpi_cmd(sdi, devc->device->commands,
+				SCPI_CMD_SET_CURRENT_LIMIT, d);
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+		if (g_variant_get_boolean(data))
+			ret = scpi_cmd(sdi, devc->device->commands,
+					SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_ENABLE);
+		else
+			ret = scpi_cmd(sdi, devc->device->commands,
+					SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_DISABLE);
+		break;
+	case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+		d = g_variant_get_double(data);
+		ret = scpi_cmd(sdi, devc->device->commands,
+				SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, d);
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+		if (g_variant_get_boolean(data))
+			ret = scpi_cmd(sdi, devc->device->commands,
+					SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE);
+		else
+			ret = scpi_cmd(sdi, devc->device->commands,
+					SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE);
+		break;
+	case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+		d = g_variant_get_double(data);
+		ret = scpi_cmd(sdi, devc->device->commands,
+				SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD, d);
+		break;
+	case SR_CONF_OVER_TEMPERATURE_PROTECTION:
+		if (g_variant_get_boolean(data))
+			ret = scpi_cmd(sdi, devc->device->commands,
+					SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_ENABLE);
+		else
+			ret = scpi_cmd(sdi, devc->device->commands,
+					SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_DISABLE);
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	struct sr_channel *ch;
+	const struct channel_spec *ch_spec;
+	GVariant *gvar;
+	GVariantBuilder gvb;
+	int ret, i;
+	const char *s[16];
+
+	/* Always available, even without sdi. */
+	if (key == SR_CONF_SCAN_OPTIONS) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		return SR_OK;
+	} else if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	if (!sdi)
+		return SR_ERR_ARG;
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	if (!cg) {
+		/* No channel group: global options. */
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devc->device->devopts, devc->device->num_devopts,
+					sizeof(uint32_t));
+			break;
+		case SR_CONF_CHANNEL_CONFIG:
+			/* Not used. */
+			i = 0;
+			if (devc->device->features & PPS_INDEPENDENT)
+				s[i++] = "Independent";
+			if (devc->device->features & PPS_SERIES)
+				s[i++] = "Series";
+			if (devc->device->features & PPS_PARALLEL)
+				s[i++] = "Parallel";
+			if (i == 0) {
+				/*
+				 * Shouldn't happen: independent-only devices
+				 * shouldn't advertise this option at all.
+				 */
+				return SR_ERR_NA;
+			}
+			*data = g_variant_new_strv(s, i);
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	} else {
+		/* Channel group specified. */
+		/*
+		 * Per-channel-group options depending on a channel are actually
+		 * done with the first channel. Channel groups in PPS can have
+		 * more than one channel, but they will typically be of equal
+		 * specification for use in series or parallel mode.
+		 */
+		ch = cg->channels->data;
+
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devc->device->devopts_cg, devc->device->num_devopts_cg,
+					sizeof(uint32_t));
+			break;
+		case SR_CONF_VOLTAGE_TARGET:
+			ch_spec = &(devc->device->channels[ch->index]);
+			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+			/* Min, max, write resolution. */
+			for (i = 0; i < 3; i++) {
+				gvar = g_variant_new_double(ch_spec->voltage[i]);
+				g_variant_builder_add_value(&gvb, gvar);
+			}
+			*data = g_variant_builder_end(&gvb);
+			break;
+		case SR_CONF_OUTPUT_FREQUENCY_TARGET:
+			ch_spec = &(devc->device->channels[ch->index]);
+			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+			/* Min, max, write resolution. */
+			for (i = 0; i < 3; i++) {
+				gvar = g_variant_new_double(ch_spec->frequency[i]);
+				g_variant_builder_add_value(&gvb, gvar);
+			}
+			*data = g_variant_builder_end(&gvb);
+			break;
+		case SR_CONF_CURRENT_LIMIT:
+			g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+			/* Min, max, step. */
+			for (i = 0; i < 3; i++) {
+				ch_spec = &(devc->device->channels[ch->index]);
+				gvar = g_variant_new_double(ch_spec->current[i]);
+				g_variant_builder_add_value(&gvb, gvar);
+			}
+			*data = g_variant_builder_end(&gvb);
+			break;
+		default:
+			return SR_ERR_NA;
+		}
+	}
+
+	return ret;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc;
+	struct sr_scpi_dev_inst *scpi;
+	struct sr_channel *ch;
+	struct pps_channel *pch;
+	int cmd, ret;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+	scpi = sdi->conn;
+	devc->cb_data = cb_data;
+
+	if ((ret = sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 10,
+			scpi_pps_receive_data, (void *)sdi)) != SR_OK)
+		return ret;
+	std_session_send_df_header(sdi, LOG_PREFIX);
+
+	/* Prime the pipe with the first channel's fetch. */
+	ch = sr_next_enabled_channel(sdi, NULL);
+	pch = ch->priv;
+	if ((ret = select_channel(sdi, ch)) < 0)
+		return ret;
+	if (pch->mq == SR_MQ_VOLTAGE)
+		cmd = SCPI_CMD_GET_MEAS_VOLTAGE;
+	else if (pch->mq == SR_MQ_FREQUENCY)
+		cmd = SCPI_CMD_GET_MEAS_FREQUENCY;
+	else if (pch->mq == SR_MQ_CURRENT)
+		cmd = SCPI_CMD_GET_MEAS_CURRENT;
+	else if (pch->mq == SR_MQ_POWER)
+		cmd = SCPI_CMD_GET_MEAS_POWER;
+	else
+		return SR_ERR;
+	scpi_cmd(sdi, devc->device->commands, cmd, pch->hwname);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_scpi_dev_inst *scpi;
+	float f;
+
+	(void)cb_data;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	scpi = sdi->conn;
+
+	/*
+	 * A requested value is certainly on the way. Retrieve it now,
+	 * to avoid leaving the device in a state where it's not expecting
+	 * commands.
+	 */
+	sr_scpi_get_float(scpi, NULL, &f);
+	sr_scpi_source_remove(sdi->session, scpi);
+
+	packet.type = SR_DF_END;
+	sr_session_send(sdi, &packet);
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver scpi_pps_driver_info = {
+	.name = "scpi-pps",
+	.longname = "SCPI PPS",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/scpi-pps/profiles.c b/src/hardware/scpi-pps/profiles.c
new file mode 100644
index 0000000..fe6ea18
--- /dev/null
+++ b/src/hardware/scpi-pps/profiles.c
@@ -0,0 +1,549 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ * Copyright (C) 2015 Google, Inc.
+ * (Written by Alexandru Gagniuc <mrnuke at google.com> for Google, Inc.)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <strings.h>
+#include "protocol.h"
+
+#define CH_IDX(x) (1 << x)
+#define FREQ_DC_ONLY {0, 0, 0}
+
+static const uint32_t devopts_none[] = { };
+
+/* Agilent/Keysight N5700A series */
+static const uint32_t agilent_n5700a_devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+};
+
+static const uint32_t agilent_n5700a_devopts_cg[] = {
+	SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const struct channel_spec agilent_n5767a_ch[] = {
+	{ "1", { 0, 60, 0.0001 }, { 0, 25, 0.1 }, FREQ_DC_ONLY },
+};
+
+static const struct channel_group_spec agilent_n5767a_cg[] = {
+	{ "1", CH_IDX(0), PPS_OVP | PPS_OCP },
+};
+
+/*
+ * TODO: OVER_CURRENT_PROTECTION_ACTIVE status can be determined by the OC bit
+ * in STAT:QUES:EVEN?, but this is not implemented.
+ */
+static const struct scpi_command agilent_n5700a_cmd[] = {
+	{ SCPI_CMD_REMOTE, "SYST:COMM:RLST REM" },
+	{ SCPI_CMD_LOCAL, "SYST:COMM:RLST LOC" },
+	{ SCPI_CMD_GET_MEAS_VOLTAGE, ":MEAS:VOLT?" },
+	{ SCPI_CMD_GET_MEAS_CURRENT, "MEAS:CURR?" },
+	{ SCPI_CMD_GET_VOLTAGE_TARGET, ":SOUR:VOLT?" },
+	{ SCPI_CMD_SET_VOLTAGE_TARGET, ":SOUR:VOLT %.6f" },
+	{ SCPI_CMD_GET_CURRENT_LIMIT, ":SOUR:CURR?" },
+	{ SCPI_CMD_SET_CURRENT_LIMIT, ":SOUR:CURR %.6f" },
+	{ SCPI_CMD_GET_OUTPUT_ENABLED, ":OUTP:STAT?" },
+	{ SCPI_CMD_SET_OUTPUT_ENABLE, ":OUTP ON" },
+	{ SCPI_CMD_SET_OUTPUT_DISABLE, ":OUTP OFF" },
+	{ SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":VOLT:PROT?" },
+	{ SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":VOLT:PROT %.6f" },
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ENABLED, ":CURR:PROT:STAT?" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE, ":CURR:PROT:STAT ON?"},
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE, ":CURR:PROT:STAT OFF?"},
+	/* Current limit (CC mode) and OCP are set using the same command. */
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_THRESHOLD, ":SOUR:CURR?" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD, ":SOUR:CURR %.6f" },
+	ALL_ZERO
+};
+
+/* Chroma 61600 series AC source */
+static const uint32_t chroma_61604_devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+};
+
+static const uint32_t chroma_61604_devopts_cg[] = {
+	SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET,
+	SR_CONF_OUTPUT_FREQUENCY_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const struct channel_spec chroma_61604_ch[] = {
+	{ "1", { 0, 300, 0.1 }, { 0, 16, 0.1 }, { 1.0, 1000.0, 0.01 } },
+};
+
+static const struct channel_group_spec chroma_61604_cg[] = {
+	{ "1", CH_IDX(0), PPS_OVP | PPS_OCP },
+};
+
+static const struct scpi_command chroma_61604_cmd[] = {
+	{ SCPI_CMD_REMOTE, "SYST:REM" },
+	{ SCPI_CMD_LOCAL, "SYST:LOC" },
+	{ SCPI_CMD_GET_MEAS_VOLTAGE, ":FETC:VOLT:ACDC?" },
+	{ SCPI_CMD_GET_MEAS_FREQUENCY, ":FETC:FREQ?" },
+	{ SCPI_CMD_GET_MEAS_CURRENT, ":FETC:CURR:AC?" },
+	{ SCPI_CMD_GET_MEAS_POWER, ":FETC:POW:AC?" },
+	{ SCPI_CMD_GET_VOLTAGE_TARGET, ":SOUR:VOLT:AC?" },
+	{ SCPI_CMD_SET_VOLTAGE_TARGET, ":SOUR:VOLT:AC %.1f" },
+	{ SCPI_CMD_GET_FREQUENCY_TARGET, ":SOUR:FREQ?" },
+	{ SCPI_CMD_SET_FREQUENCY_TARGET, ":SOUR:FREQ %.2f" },
+	{ SCPI_CMD_GET_OUTPUT_ENABLED, ":OUTP?" },
+	{ SCPI_CMD_SET_OUTPUT_ENABLE, ":OUTP ON" },
+	{ SCPI_CMD_SET_OUTPUT_DISABLE, ":OUTP OFF" },
+	{ SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":SOUR:VOLT:LIM:AC?" },
+	{ SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":SOUR:VOLT:LIM:AC %.1f" },
+	/* This is not a current limit mode. It is overcurrent protection. */
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_THRESHOLD, ":SOUR:CURR:LIM?" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD, ":SOUR:CURR:LIM %.2f" },
+	ALL_ZERO
+};
+
+/* Chroma 62000 series DC source */
+
+static const uint32_t chroma_62000_devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+};
+
+static const uint32_t chroma_62000_devopts_cg[] = {
+	SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const struct channel_group_spec chroma_62000_cg[] = {
+	{ "1", CH_IDX(0), PPS_OVP | PPS_OCP },
+};
+
+static const struct scpi_command chroma_62000_cmd[] = {
+	{ SCPI_CMD_REMOTE, ":CONF:REM ON" },
+	{ SCPI_CMD_LOCAL, ":CONF:REM OFF" },
+	{ SCPI_CMD_BEEPER, ":CONF:BEEP?" },
+	{ SCPI_CMD_BEEPER_ENABLE, ":CONF:BEEP ON" },
+	{ SCPI_CMD_BEEPER_DISABLE, ":CONF:BEEP OFF" },
+	{ SCPI_CMD_GET_MEAS_VOLTAGE, ":MEAS:VOLT?" },
+	{ SCPI_CMD_GET_MEAS_CURRENT, ":MEAS:CURR?" },
+	{ SCPI_CMD_GET_MEAS_POWER, ":MEAS:POW?" },
+	{ SCPI_CMD_GET_VOLTAGE_TARGET, ":SOUR:VOLT?" },
+	{ SCPI_CMD_SET_VOLTAGE_TARGET, ":SOUR:VOLT %.2f" },
+	{ SCPI_CMD_GET_CURRENT_LIMIT, ":SOUR:CURR?" },
+	{ SCPI_CMD_SET_CURRENT_LIMIT, ":SOUR:CURR %.6f" },
+	{ SCPI_CMD_GET_OUTPUT_ENABLED, ":CONF:OUTP?" },
+	{ SCPI_CMD_SET_OUTPUT_ENABLE, ":CONF:OUTP ON" },
+	{ SCPI_CMD_SET_OUTPUT_DISABLE, ":CONF:OUTP OFF" },
+	{ SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":SOUR:VOLT:PROT:HIGH?" },
+	{ SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":SOUR:VOLT:PROT:HIGH %.6f" },
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_THRESHOLD, ":SOUR:CURR:PROT:HIGH?" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD, ":SOUR:CURR:PROT:HIGH %.6f" },
+	ALL_ZERO
+};
+
+static int chroma_62000p_probe_channels(struct sr_dev_inst *sdi,
+		struct sr_scpi_hw_info *hw_info,
+		struct channel_spec **channels, unsigned int *num_channels,
+		struct channel_group_spec **channel_groups,
+		unsigned int *num_channel_groups)
+{
+	unsigned int volts, amps;
+	struct channel_spec *channel;
+
+	(void)sdi;
+
+	sscanf(hw_info->model, "%*[^P]P-%u-%u", &volts, &amps);
+	sr_dbg("Found device rated for %d V and %d A", volts, amps);
+
+	if (volts > 600) {
+		sr_err("Probed max voltage of %u V is out of spec.", volts);
+		return SR_ERR_BUG;
+	}
+
+	if (volts > 120) {
+		sr_err("Probed max current of %u A is out of spec.", amps);
+		return SR_ERR_BUG;
+	}
+
+	channel = g_malloc0(sizeof(struct channel_spec));
+	channel->name = "1";
+	channel->voltage[0] = channel->current[0] = 0.0;
+	channel->voltage[1] = (float)volts;
+	channel->current[1] = (float)amps;
+	channel->voltage[2] = channel->current[2] = 0.01;
+	*channels = channel;
+	*num_channels = 1;
+
+	*channel_groups = g_malloc(sizeof(struct channel_group_spec));
+	**channel_groups = chroma_62000_cg[0];
+	*num_channel_groups = 1;
+
+	return SR_OK;
+}
+
+/* Rigol DP800 series */
+static const uint32_t rigol_dp800_devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t rigol_dp800_devopts_cg[] = {
+	SR_CONF_REGULATION | SR_CONF_GET,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
+	SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const struct channel_spec rigol_dp821a_ch[] = {
+	{ "1", { 0, 60, 0.001 }, { 0, 1, 0.0001 }, FREQ_DC_ONLY },
+	{ "2", { 0, 8, 0.001 }, { 0, 10, 0.001 }, FREQ_DC_ONLY },
+};
+
+static const struct channel_spec rigol_dp831_ch[] = {
+	{ "1", { 0, 8, 0.001 }, { 0, 5, 0.0003 }, FREQ_DC_ONLY },
+	{ "2", { 0, 30, 0.001 }, { 0, 2, 0.0001 }, FREQ_DC_ONLY },
+	{ "3", { 0, -30, 0.001 }, { 0, 2, 0.0001 }, FREQ_DC_ONLY },
+};
+
+static const struct channel_spec rigol_dp832_ch[] = {
+	{ "1", { 0, 30, 0.001 }, { 0, 3, 0.001 }, FREQ_DC_ONLY },
+	{ "2", { 0, 30, 0.001 }, { 0, 3, 0.001 }, FREQ_DC_ONLY },
+	{ "3", { 0, 5, 0.001 }, { 0, 3, 0.001 }, FREQ_DC_ONLY },
+};
+
+static const struct channel_group_spec rigol_dp820_cg[] = {
+	{ "1", CH_IDX(0), PPS_OVP | PPS_OCP },
+	{ "2", CH_IDX(1), PPS_OVP | PPS_OCP },
+};
+
+static const struct channel_group_spec rigol_dp830_cg[] = {
+	{ "1", CH_IDX(0), PPS_OVP | PPS_OCP },
+	{ "2", CH_IDX(1), PPS_OVP | PPS_OCP },
+	{ "3", CH_IDX(2), PPS_OVP | PPS_OCP },
+};
+
+static const struct scpi_command rigol_dp800_cmd[] = {
+	{ SCPI_CMD_REMOTE, "SYST:REMOTE" },
+	{ SCPI_CMD_LOCAL, "SYST:LOCAL" },
+	{ SCPI_CMD_BEEPER, "SYST:BEEP:STAT?" },
+	{ SCPI_CMD_BEEPER_ENABLE, "SYST:BEEP:STAT ON" },
+	{ SCPI_CMD_BEEPER_DISABLE, "SYST:BEEP:STAT OFF" },
+	{ SCPI_CMD_SELECT_CHANNEL, ":INST:NSEL %s" },
+	{ SCPI_CMD_GET_MEAS_VOLTAGE, ":MEAS:VOLT?" },
+	{ SCPI_CMD_GET_MEAS_CURRENT, ":MEAS:CURR?" },
+	{ SCPI_CMD_GET_MEAS_POWER, ":MEAS:POWE?" },
+	{ SCPI_CMD_GET_VOLTAGE_TARGET, ":SOUR:VOLT?" },
+	{ SCPI_CMD_SET_VOLTAGE_TARGET, ":SOUR:VOLT %.6f" },
+	{ SCPI_CMD_GET_CURRENT_LIMIT, ":SOUR:CURR?" },
+	{ SCPI_CMD_SET_CURRENT_LIMIT, ":SOUR:CURR %.6f" },
+	{ SCPI_CMD_GET_OUTPUT_ENABLED, ":OUTP?" },
+	{ SCPI_CMD_SET_OUTPUT_ENABLE, ":OUTP ON" },
+	{ SCPI_CMD_SET_OUTPUT_DISABLE, ":OUTP OFF" },
+	{ SCPI_CMD_GET_OUTPUT_REGULATION, ":OUTP:MODE?" },
+	{ SCPI_CMD_GET_OVER_TEMPERATURE_PROTECTION, ":SYST:OTP?" },
+	{ SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_ENABLE, ":SYST:OTP ON" },
+	{ SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_DISABLE, ":SYST:OTP OFF" },
+	{ SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ENABLED, ":OUTP:OVP?" },
+	{ SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_ENABLE, ":OUTP:OVP ON" },
+	{ SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_DISABLE, ":OUTP:OVP OFF" },
+	{ SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ACTIVE, ":OUTP:OVP:QUES?" },
+	{ SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":OUTP:OVP:VAL?" },
+	{ SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":OUTP:OVP:VAL %.6f" },
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ENABLED, ":OUTP:OCP?" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE, ":OUTP:OCP:STAT ON" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE, ":OUTP:OCP:STAT OFF" },
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ACTIVE, ":OUTP:OCP:QUES?" },
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_THRESHOLD, ":OUTP:OCP:VAL?" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD, ":OUTP:OCP:VAL %.6f" },
+	ALL_ZERO
+};
+
+/* HP 663xx series */
+static const uint32_t hp_6632b_devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const struct channel_spec hp_6632b_ch[] = {
+	{ "1", { 0, 20.475, 0.005 }, { 0, 5.1188, 0.00132 }, FREQ_DC_ONLY },
+};
+
+static const struct channel_group_spec hp_6632b_cg[] = {
+	{ "1", CH_IDX(0), 0 },
+};
+
+static const struct scpi_command hp_6632b_cmd[] = {
+	{ SCPI_CMD_GET_OUTPUT_ENABLED, "OUTP:STAT?" },
+	{ SCPI_CMD_SET_OUTPUT_ENABLE, "OUTP:STAT ON" },
+	{ SCPI_CMD_SET_OUTPUT_DISABLE, "OUTP:STAT OFF" },
+	{ SCPI_CMD_GET_MEAS_VOLTAGE, ":MEAS:VOLT?" },
+	{ SCPI_CMD_GET_MEAS_CURRENT, ":MEAS:CURR?" },
+	{ SCPI_CMD_GET_VOLTAGE_TARGET, ":SOUR:VOLT?" },
+	{ SCPI_CMD_SET_VOLTAGE_TARGET, ":SOUR:VOLT %.6f" },
+	{ SCPI_CMD_GET_CURRENT_LIMIT, ":SOUR:CURR?" },
+	{ SCPI_CMD_SET_CURRENT_LIMIT, ":SOUR:CURR %.6f" },
+	ALL_ZERO
+};
+
+/* Philips/Fluke PM2800 series */
+static const uint32_t philips_pm2800_devopts[] = {
+	SR_CONF_CONTINUOUS | SR_CONF_SET,
+};
+
+static const uint32_t philips_pm2800_devopts_cg[] = {
+	SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_VOLTAGE | SR_CONF_GET,
+	SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_CURRENT | SR_CONF_GET,
+	SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
+	SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
+	SR_CONF_REGULATION | SR_CONF_GET,
+};
+
+enum philips_pm2800_modules {
+	PM2800_MOD_30V_10A = 1,
+	PM2800_MOD_60V_5A,
+	PM2800_MOD_60V_10A,
+	PM2800_MOD_8V_15A,
+	PM2800_MOD_60V_2A,
+	PM2800_MOD_120V_1A,
+};
+
+static const struct philips_pm2800_module_spec {
+	/* Min, max, programming resolution. */
+	float voltage[3];
+	float current[3];
+} philips_pm2800_module_specs[] = {
+	/* Autoranging modules. */
+	[PM2800_MOD_30V_10A] = { { 0, 30, 0.0075 }, { 0, 10, 0.0025 } },
+	[PM2800_MOD_60V_5A] = { { 0, 60, 0.015 }, { 0, 5, 0.00125 } },
+	[PM2800_MOD_60V_10A] = { { 0, 60, 0.015 }, { 0, 10, 0.0025 } },
+	/* Linear modules. */
+	[PM2800_MOD_8V_15A] = { { 0, 8, 0.002 }, { -15, 15, 0.00375 } },
+	[PM2800_MOD_60V_2A] = { { 0, 60, 0.015 }, { -2, 2, 0.0005 } },
+	[PM2800_MOD_120V_1A] = { { 0, 120, 0.030 }, { -1, 1, 0.00025 } },
+};
+
+static const struct philips_pm2800_model {
+	unsigned int chassis;
+	unsigned int num_modules;
+	unsigned int set;
+	unsigned int modules[3];
+} philips_pm2800_matrix[] = {
+	/* Autoranging chassis. */
+	{ 1, 1, 0, { PM2800_MOD_30V_10A, 0, 0 } },
+	{ 1, 1, 1, { PM2800_MOD_60V_5A, 0, 0 } },
+	{ 1, 2, 0, { PM2800_MOD_30V_10A, PM2800_MOD_30V_10A, 0 } },
+	{ 1, 2, 1, { PM2800_MOD_60V_5A, PM2800_MOD_60V_5A, 0 } },
+	{ 1, 2, 2, { PM2800_MOD_30V_10A, PM2800_MOD_60V_5A, 0 } },
+	{ 1, 2, 3, { PM2800_MOD_30V_10A, PM2800_MOD_60V_10A, 0 } },
+	{ 1, 2, 4, { PM2800_MOD_60V_5A, PM2800_MOD_60V_10A, 0 } },
+	{ 1, 3, 0, { PM2800_MOD_30V_10A, PM2800_MOD_30V_10A, PM2800_MOD_30V_10A } },
+	{ 1, 3, 1, { PM2800_MOD_60V_5A, PM2800_MOD_60V_5A, PM2800_MOD_60V_5A } },
+	{ 1, 3, 2, { PM2800_MOD_30V_10A, PM2800_MOD_30V_10A, PM2800_MOD_60V_5A } },
+	{ 1, 3, 3, { PM2800_MOD_30V_10A, PM2800_MOD_60V_5A, PM2800_MOD_60V_5A } },
+	/* Linear chassis. */
+	{ 3, 1, 0, { PM2800_MOD_60V_2A, 0, 0 } },
+	{ 3, 1, 1, { PM2800_MOD_120V_1A, 0, 0 } },
+	{ 3, 1, 2, { PM2800_MOD_8V_15A, 0, 0 } },
+	{ 3, 2, 0, { PM2800_MOD_60V_2A, 0, 0 } },
+	{ 3, 2, 1, { PM2800_MOD_120V_1A, 0, 0 } },
+	{ 3, 2, 2, { PM2800_MOD_60V_2A, PM2800_MOD_120V_1A, 0 } },
+	{ 3, 2, 3, { PM2800_MOD_8V_15A, PM2800_MOD_8V_15A, 0 } },
+};
+
+static const char *philips_pm2800_names[] = { "1", "2", "3" };
+
+static int philips_pm2800_probe_channels(struct sr_dev_inst *sdi,
+		struct sr_scpi_hw_info *hw_info,
+		struct channel_spec **channels, unsigned int *num_channels,
+		struct channel_group_spec **channel_groups, unsigned int *num_channel_groups)
+{
+	const struct philips_pm2800_model *model;
+	const struct philips_pm2800_module_spec *spec;
+	unsigned int chassis, num_modules, set, module, m, i;
+
+	(void)sdi;
+
+	/*
+	 * The model number as reported by *IDN? looks like e.g. PM2813/11,
+	 * Where "PM28" is fixed, followed by the chassis code (1 = autoranging,
+	 * 3 = linear series) and the number of modules: 1-3 for autoranging,
+	 * 1-2 for linear.
+	 * After the slash, the first digit denotes the module set. The
+	 * digit after that denotes front (5) or rear (1) binding posts.
+	 */
+	chassis = hw_info->model[4] - 0x30;
+	num_modules = hw_info->model[5] - 0x30;
+	set = hw_info->model[7] - 0x30;
+	for (m = 0; m < ARRAY_SIZE(philips_pm2800_matrix); m++) {
+		model = &philips_pm2800_matrix[m];
+		if (model->chassis == chassis && model->num_modules == num_modules
+				&& model->set == set)
+			break;
+	}
+	if (m == ARRAY_SIZE(philips_pm2800_matrix)) {
+		sr_dbg("Model %s not found in matrix.", hw_info->model);
+		return SR_ERR;
+	}
+
+	sr_dbg("Found %d output channel%s:", num_modules, num_modules > 1 ? "s" : "");
+	*channels = g_malloc0(sizeof(struct channel_spec) * num_modules);
+	*channel_groups = g_malloc0(sizeof(struct channel_group_spec) * num_modules);
+	for (i = 0; i < num_modules; i++) {
+		module = model->modules[i];
+		spec = &philips_pm2800_module_specs[module];
+		sr_dbg("output %d: %.0f - %.0fV, %.0f - %.0fA", i + 1,
+				spec->voltage[0], spec->voltage[1],
+				spec->current[0], spec->current[1]);
+		(*channels)[i].name = (char *)philips_pm2800_names[i];
+		memcpy(&((*channels)[i].voltage), spec, sizeof(float) * 6);
+		(*channel_groups)[i].name = (char *)philips_pm2800_names[i];
+		(*channel_groups)[i].channel_index_mask = 1 << i;
+		(*channel_groups)[i].features = PPS_OTP | PPS_OVP | PPS_OCP;
+	}
+	*num_channels = *num_channel_groups = num_modules;
+
+	return SR_OK;
+}
+
+static const struct scpi_command philips_pm2800_cmd[] = {
+	{ SCPI_CMD_SELECT_CHANNEL, ":INST:NSEL %s" },
+	{ SCPI_CMD_GET_MEAS_VOLTAGE, ":MEAS:VOLT?" },
+	{ SCPI_CMD_GET_MEAS_CURRENT, ":MEAS:CURR?" },
+	{ SCPI_CMD_GET_VOLTAGE_TARGET, ":SOUR:VOLT?" },
+	{ SCPI_CMD_SET_VOLTAGE_TARGET, ":SOUR:VOLT %.6f" },
+	{ SCPI_CMD_GET_CURRENT_LIMIT, ":SOUR:CURR?" },
+	{ SCPI_CMD_SET_CURRENT_LIMIT, ":SOUR:CURR %.6f" },
+	{ SCPI_CMD_GET_OUTPUT_ENABLED, ":OUTP?" },
+	{ SCPI_CMD_SET_OUTPUT_ENABLE, ":OUTP ON" },
+	{ SCPI_CMD_SET_OUTPUT_DISABLE, ":OUTP OFF" },
+	{ SCPI_CMD_GET_OUTPUT_REGULATION, ":SOUR:FUNC:MODE?" },
+	{ SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ACTIVE, ":SOUR:VOLT:PROT:TRIP?" },
+	{ SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":SOUR:VOLT:PROT:LEV?" },
+	{ SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":SOUR:VOLT:PROT:LEV %.6f" },
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ENABLED, ":SOUR:CURR:PROT:STAT?" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE, ":SOUR:CURR:PROT:STAT ON" },
+	{ SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE, ":SOUR:CURR:PROT:STAT OFF" },
+	{ SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ACTIVE, ":SOUR:CURR:PROT:TRIP?" },
+	ALL_ZERO
+};
+
+SR_PRIV const struct scpi_pps pps_profiles[] = {
+	/* Agilent N5767A */
+	{ "Agilent", "N5767A", 0,
+		ARRAY_AND_SIZE(agilent_n5700a_devopts),
+		ARRAY_AND_SIZE(agilent_n5700a_devopts_cg),
+		ARRAY_AND_SIZE(agilent_n5767a_ch),
+		ARRAY_AND_SIZE(agilent_n5767a_cg),
+		agilent_n5700a_cmd,
+		.probe_channels = NULL,
+	},
+	/* Chroma 61604 */
+	{ "Chroma", "61604", 0,
+		ARRAY_AND_SIZE(chroma_61604_devopts),
+		ARRAY_AND_SIZE(chroma_61604_devopts_cg),
+		ARRAY_AND_SIZE(chroma_61604_ch),
+		ARRAY_AND_SIZE(chroma_61604_cg),
+		chroma_61604_cmd,
+		.probe_channels = NULL,
+	},
+	/* Chroma 62000 series */
+	{ "Chroma", "620[0-9]{2}P-[0-9]{2,3}-[0-9]{1,3}", 0,
+		ARRAY_AND_SIZE(chroma_62000_devopts),
+		ARRAY_AND_SIZE(chroma_62000_devopts_cg),
+		NULL, 0,
+		NULL, 0,
+		chroma_62000_cmd,
+		.probe_channels = chroma_62000p_probe_channels,
+	},
+	/* HP 6632B */
+	{ "HP", "6632B", 0,
+		ARRAY_AND_SIZE(hp_6632b_devopts),
+		ARRAY_AND_SIZE(devopts_none),
+		ARRAY_AND_SIZE(hp_6632b_ch),
+		ARRAY_AND_SIZE(hp_6632b_cg),
+		hp_6632b_cmd,
+		.probe_channels = NULL,
+	},
+
+	/* Rigol DP800 series */
+	{ "Rigol", "^DP821A$", PPS_OTP,
+		ARRAY_AND_SIZE(rigol_dp800_devopts),
+		ARRAY_AND_SIZE(rigol_dp800_devopts_cg),
+		ARRAY_AND_SIZE(rigol_dp821a_ch),
+		ARRAY_AND_SIZE(rigol_dp820_cg),
+		rigol_dp800_cmd,
+		.probe_channels = NULL,
+	},
+	{ "Rigol", "^DP831A$", PPS_OTP,
+		ARRAY_AND_SIZE(rigol_dp800_devopts),
+		ARRAY_AND_SIZE(rigol_dp800_devopts_cg),
+		ARRAY_AND_SIZE(rigol_dp831_ch),
+		ARRAY_AND_SIZE(rigol_dp830_cg),
+		rigol_dp800_cmd,
+		.probe_channels = NULL,
+	},
+	{ "Rigol", "^(DP832|DP832A)$", PPS_OTP,
+		ARRAY_AND_SIZE(rigol_dp800_devopts),
+		ARRAY_AND_SIZE(rigol_dp800_devopts_cg),
+		ARRAY_AND_SIZE(rigol_dp832_ch),
+		ARRAY_AND_SIZE(rigol_dp830_cg),
+		rigol_dp800_cmd,
+		.probe_channels = NULL,
+	},
+
+	/* Philips/Fluke PM2800 series */
+	{ "Philips", "^PM28[13][123]/[01234]{1,2}$", 0,
+		ARRAY_AND_SIZE(philips_pm2800_devopts),
+		ARRAY_AND_SIZE(philips_pm2800_devopts_cg),
+		NULL, 0,
+		NULL, 0,
+		philips_pm2800_cmd,
+		philips_pm2800_probe_channels,
+	},
+};
+
+SR_PRIV unsigned int num_pps_profiles = ARRAY_SIZE(pps_profiles);
diff --git a/src/hardware/scpi-pps/protocol.c b/src/hardware/scpi-pps/protocol.c
new file mode 100644
index 0000000..b6b57fb
--- /dev/null
+++ b/src/hardware/scpi-pps/protocol.c
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include "scpi.h"
+#include "protocol.h"
+
+SR_PRIV int select_channel(const struct sr_dev_inst *sdi, struct sr_channel *ch)
+{
+	struct dev_context *devc;
+	struct pps_channel *cur_pch, *new_pch;
+	int ret;
+
+	if (g_slist_length(sdi->channels) == 1)
+		return SR_OK;
+
+	devc = sdi->priv;
+	if (ch == devc->cur_channel)
+		return SR_OK;
+
+	new_pch = ch->priv;
+	if (devc->cur_channel) {
+		cur_pch = devc->cur_channel->priv;
+		if (cur_pch->hw_output_idx == new_pch->hw_output_idx) {
+			/* Same underlying output channel. */
+			devc->cur_channel = ch;
+			return SR_OK;
+		}
+	}
+
+	if ((ret = scpi_cmd(sdi, devc->device->commands, SCPI_CMD_SELECT_CHANNEL,
+			new_pch->hwname)) >= 0)
+		devc->cur_channel = ch;
+
+	return ret;
+}
+
+SR_PRIV int scpi_pps_receive_data(int fd, int revents, void *cb_data)
+{
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+	const struct sr_dev_inst *sdi;
+	struct sr_channel *next_channel;
+	struct sr_scpi_dev_inst *scpi;
+	struct pps_channel *pch;
+	float f;
+	int cmd;
+
+	(void)fd;
+	(void)revents;
+
+	if (!(sdi = cb_data))
+		return TRUE;
+
+	if (!(devc = sdi->priv))
+		return TRUE;
+
+	scpi = sdi->conn;
+
+	/* Retrieve requested value for this state. */
+	if (sr_scpi_get_float(scpi, NULL, &f) == SR_OK) {
+		pch = devc->cur_channel->priv;
+		packet.type = SR_DF_ANALOG_OLD;
+		packet.payload = &analog;
+		analog.channels = g_slist_append(NULL, devc->cur_channel);
+		analog.num_samples = 1;
+		analog.mq = pch->mq;
+		if (pch->mq == SR_MQ_VOLTAGE)
+			analog.unit = SR_UNIT_VOLT;
+		else if (pch->mq == SR_MQ_CURRENT)
+			analog.unit = SR_UNIT_AMPERE;
+		else if (pch->mq == SR_MQ_POWER)
+			analog.unit = SR_UNIT_WATT;
+		analog.mqflags = SR_MQFLAG_DC;
+		analog.data = &f;
+		sr_session_send(sdi, &packet);
+		g_slist_free(analog.channels);
+	}
+
+	if (g_slist_length(sdi->channels) > 1) {
+		next_channel = sr_next_enabled_channel(sdi, devc->cur_channel);
+		if (select_channel(sdi, next_channel) != SR_OK) {
+			sr_err("Failed to select channel %s", next_channel->name);
+			return FALSE;
+		}
+	}
+
+	pch = devc->cur_channel->priv;
+	if (pch->mq == SR_MQ_VOLTAGE)
+		cmd = SCPI_CMD_GET_MEAS_VOLTAGE;
+	else if (pch->mq == SR_MQ_FREQUENCY)
+		cmd = SCPI_CMD_GET_MEAS_FREQUENCY;
+	else if (pch->mq == SR_MQ_CURRENT)
+		cmd = SCPI_CMD_GET_MEAS_CURRENT;
+	else if (pch->mq == SR_MQ_POWER)
+		cmd = SCPI_CMD_GET_MEAS_POWER;
+	else
+		return SR_ERR;
+	scpi_cmd(sdi, devc->device->commands, cmd);
+
+	return TRUE;
+}
diff --git a/src/hardware/scpi-pps/protocol.h b/src/hardware/scpi-pps/protocol.h
new file mode 100644
index 0000000..6c84c08
--- /dev/null
+++ b/src/hardware/scpi-pps/protocol.h
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at 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_SCPI_PPS_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SCPI_PPS_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+
+#define LOG_PREFIX "scpi-pps"
+
+enum pps_scpi_cmds {
+	SCPI_CMD_REMOTE,
+	SCPI_CMD_LOCAL,
+	SCPI_CMD_BEEPER,
+	SCPI_CMD_BEEPER_ENABLE,
+	SCPI_CMD_BEEPER_DISABLE,
+	SCPI_CMD_SELECT_CHANNEL,
+	SCPI_CMD_GET_MEAS_VOLTAGE,
+	SCPI_CMD_GET_MEAS_CURRENT,
+	SCPI_CMD_GET_MEAS_POWER,
+	SCPI_CMD_GET_MEAS_FREQUENCY,
+	SCPI_CMD_GET_VOLTAGE_TARGET,
+	SCPI_CMD_SET_VOLTAGE_TARGET,
+	SCPI_CMD_GET_FREQUENCY_TARGET,
+	SCPI_CMD_SET_FREQUENCY_TARGET,
+	SCPI_CMD_GET_CURRENT_LIMIT,
+	SCPI_CMD_SET_CURRENT_LIMIT,
+	SCPI_CMD_GET_OUTPUT_ENABLED,
+	SCPI_CMD_SET_OUTPUT_ENABLE,
+	SCPI_CMD_SET_OUTPUT_DISABLE,
+	SCPI_CMD_GET_OUTPUT_REGULATION,
+	SCPI_CMD_GET_OVER_TEMPERATURE_PROTECTION,
+	SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_ENABLE,
+	SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_DISABLE,
+	SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ENABLED,
+	SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_ENABLE,
+	SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_DISABLE,
+	SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_ACTIVE,
+	SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD,
+	SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD,
+	SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ENABLED,
+	SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE,
+	SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE,
+	SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ACTIVE,
+	SCPI_CMD_GET_OVER_CURRENT_PROTECTION_THRESHOLD,
+	SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD,
+};
+
+/*
+ * These are bit values denoting features a device can have either globally,
+ * in scpi_pps.features, or on a per-channel-group basis in
+ * channel_group_spec.features.
+ */
+enum pps_features {
+	PPS_OTP           = (1 << 0),
+	PPS_OVP           = (1 << 1),
+	PPS_OCP           = (1 << 2),
+	PPS_INDEPENDENT   = (1 << 3),
+	PPS_SERIES        = (1 << 4),
+	PPS_PARALLEL      = (1 << 5),
+};
+
+struct scpi_pps {
+	const char *vendor;
+	const char *model;
+	uint64_t features;
+	const uint32_t *devopts;
+	unsigned int num_devopts;
+	const uint32_t *devopts_cg;
+	unsigned int num_devopts_cg;
+	const struct channel_spec *channels;
+	unsigned int num_channels;
+	const struct channel_group_spec *channel_groups;
+	unsigned int num_channel_groups;
+	const struct scpi_command *commands;
+	int (*probe_channels) (struct sr_dev_inst *sdi, struct sr_scpi_hw_info *hwinfo,
+		struct channel_spec **channels, unsigned int *num_channels,
+		struct channel_group_spec **channel_groups, unsigned int *num_channel_groups);
+};
+
+struct channel_spec {
+	const char *name;
+	/* Min, max, programming resolution. */
+	float voltage[3];
+	float current[3];
+	float frequency[3];
+};
+
+struct channel_group_spec {
+	const char *name;
+	uint64_t channel_index_mask;
+	uint64_t features;
+};
+
+struct pps_channel {
+	int mq;
+	unsigned int hw_output_idx;
+	const char *hwname;
+};
+
+struct pps_channel_instance {
+	int mq;
+	int command;
+	const char *prefix;
+};
+
+struct pps_channel_group {
+	uint64_t features;
+};
+
+enum acq_states {
+	STATE_VOLTAGE,
+	STATE_CURRENT,
+	STATE_STOP,
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/* Model-specific information */
+	const struct scpi_pps *device;
+
+	/* Acquisition settings */
+	void *cb_data;
+
+	/* Operational state */
+	gboolean beeper_was_set;
+	struct channel_spec *channels;
+	struct channel_group_spec *channel_groups;
+
+	/* Temporary state across callbacks */
+	struct sr_channel *cur_channel;
+};
+
+SR_PRIV extern unsigned int num_pps_profiles;
+SR_PRIV extern const struct scpi_pps pps_profiles[];
+
+SR_PRIV const char *get_vendor(const char *raw_vendor);
+SR_PRIV int select_channel(const struct sr_dev_inst *sdi, struct sr_channel *ch);
+SR_PRIV int scpi_pps_receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/src/hardware/serial-dmm/api.c b/src/hardware/serial-dmm/api.c
new file mode 100644
index 0000000..a589d01
--- /dev/null
+++ b/src/hardware/serial-dmm/api.c
@@ -0,0 +1,615 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert at biot.com>
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ * Copyright (C) 2012 Uwe Hermann <uwe at hermann-uwe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_MULTIMETER,
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+};
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct dmm_info *dmm;
+	struct sr_config *src;
+	GSList *l, *devices;
+	const char *conn, *serialcomm;
+	struct sr_dev_inst *sdi;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_serial_dev_inst *serial;
+	int dropped, ret;
+	size_t len;
+	uint8_t buf[128];
+
+	dmm = (struct dmm_info *)di;
+
+	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 = dmm->conn;
+
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
+
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+		return NULL;
+
+	sr_info("Probing serial port %s.", conn);
+
+	drvc = di->context;
+	devices = NULL;
+	serial_flush(serial);
+
+	/* Request a packet if the DMM requires this. */
+	if (dmm->packet_request) {
+		if ((ret = dmm->packet_request(serial)) < 0) {
+			sr_err("Failed to request packet: %d.", ret);
+			return FALSE;
+		}
+	}
+
+	/*
+	 * There's no way to get an ID from the multimeter. It just sends data
+	 * periodically (or upon request), so the best we can do is check if
+	 * the packets match the expected format.
+	 */
+
+	/* Let's get a bit of data and see if we can find a packet. */
+	len = sizeof(buf);
+	ret = serial_stream_detect(serial, buf, &len, dmm->packet_size,
+				   dmm->packet_valid, 3000,
+				   dmm->baudrate);
+	if (ret != SR_OK)
+		goto scan_cleanup;
+
+	/*
+	 * If we dropped more than two packets worth of data, something is
+	 * wrong. We shouldn't quit however, since the dropped bytes might be
+	 * just zeroes at the beginning of the stream. Those can occur as a
+	 * combination of the nonstandard cable that ships with some devices
+	 * and the serial port or USB to serial adapter.
+	 */
+	dropped = len - dmm->packet_size;
+	if (dropped > 2 * dmm->packet_size)
+		sr_warn("Had to drop too much data.");
+
+	sr_info("Found device on port %s.", conn);
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(dmm->vendor);
+	sdi->model = g_strdup(dmm->device);
+	devc = g_malloc0(sizeof(struct dev_context));
+	sdi->inst_type = SR_INST_SERIAL;
+	sdi->conn = serial;
+	sdi->priv = devc;
+	sdi->driver = di;
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
+	drvc->instances = g_slist_append(drvc->instances, sdi);
+	devices = g_slist_append(devices, sdi);
+
+scan_cleanup:
+	serial_close(serial);
+
+	return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return dev_clear(di);
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (!(devc = sdi->priv)) {
+		sr_err("sdi->priv was NULL.");
+		return SR_ERR_BUG;
+	}
+
+	switch (key) {
+	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:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	(void)sdi;
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int 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;
+	serial_source_add(sdi->session, serial, G_IO_IN, 50,
+		      receive_data, (void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	return std_serial_dev_acquisition_stop(sdi, cb_data, std_serial_dev_close,
+			sdi->conn, LOG_PREFIX);
+}
+
+#define DMM(ID, CHIPSET, VENDOR, MODEL, CONN, BAUDRATE, PACKETSIZE, TIMEOUT, \
+			DELAY, REQUEST, VALID, PARSE, DETAILS) \
+	&(struct dmm_info) { \
+		{ \
+			.name = ID, \
+			.longname = VENDOR " " MODEL, \
+			.api_version = 1, \
+			.init = init, \
+			.cleanup = cleanup, \
+			.scan = scan, \
+			.dev_list = dev_list, \
+			.dev_clear = dev_clear, \
+			.config_get = NULL, \
+			.config_set = config_set, \
+			.config_list = config_list, \
+			.dev_open = std_serial_dev_open, \
+			.dev_close = std_serial_dev_close, \
+			.dev_acquisition_start = dev_acquisition_start, \
+			.dev_acquisition_stop = dev_acquisition_stop, \
+			.context = NULL, \
+		}, \
+		VENDOR, MODEL, CONN, BAUDRATE, PACKETSIZE, TIMEOUT, DELAY, \
+		REQUEST, VALID, PARSE, DETAILS, sizeof(struct CHIPSET##_info) \
+	}
+
+SR_PRIV const struct dmm_info *serial_dmm_drivers[] = {
+	DMM(
+		"bbcgm-2010", metex14,
+		"BBC Goertz Metrawatt", "M2110", "1200/7n2", 1200,
+		BBCGM_M2110_PACKET_SIZE, 0, 0, NULL,
+		sr_m2110_packet_valid, sr_m2110_parse,
+		NULL
+	),
+	DMM(
+		"digitek-dt4000zc", fs9721,
+		"Digitek", "DT4000ZC", "2400/8n1/dtr=1", 2400,
+		FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_10_temp_c
+	),
+	DMM(
+		"tekpower-tp4000ZC", fs9721,
+		"TekPower", "TP4000ZC", "2400/8n1/dtr=1", 2400,
+		FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_10_temp_c
+	),
+	DMM(
+		"metex-me31", metex14,
+		"Metex", "ME-31", "600/7n2/rts=0/dtr=1", 600,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"peaktech-3410", metex14,
+		"Peaktech", "3410", "600/7n2/rts=0/dtr=1", 600,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"mastech-mas345", metex14,
+		"MASTECH", "MAS345", "600/7n2/rts=0/dtr=1", 600,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"mastech-ms8250b", fs9721,
+		"MASTECH", "MS8250B", "2400/8n1/rts=0/dtr=1",
+		2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		NULL
+	),
+	DMM(
+		"va-va18b", fs9721,
+		"V&A", "VA18B", "2400/8n1", 2400,
+		FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_01_temp_c
+	),
+	DMM(
+		"va-va40b", fs9721,
+		"V&A", "VA40B", "2400/8n1", 2400,
+		FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_max_c_min
+	),
+	DMM(
+		"metex-m3640d", metex14,
+		"Metex", "M-3640D", "1200/7n2/rts=0/dtr=1", 1200,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"metex-m4650cr", metex14,
+		"Metex", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"peaktech-4370", metex14,
+		"PeakTech", "4370", "1200/7n2/rts=0/dtr=1", 1200,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"pce-pce-dm32", fs9721,
+		"PCE", "PCE-DM32", "2400/8n1", 2400,
+		FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_01_10_temp_f_c
+	),
+	DMM(
+		"radioshack-22-168", metex14,
+		"RadioShack", "22-168", "1200/7n2/rts=0/dtr=1", 1200,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"radioshack-22-805", metex14,
+		"RadioShack", "22-805", "600/7n2/rts=0/dtr=1", 600,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"radioshack-22-812", rs9lcd,
+		"RadioShack", "22-812", "4800/8n1/rts=0/dtr=1", 4800,
+		RS9LCD_PACKET_SIZE, 0, 0, NULL,
+		sr_rs9lcd_packet_valid, sr_rs9lcd_parse,
+		NULL
+	),
+	DMM(
+		"tecpel-dmm-8061-ser", fs9721,
+		"Tecpel", "DMM-8061 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_00_temp_c
+	),
+	DMM(
+		"voltcraft-m3650cr", metex14,
+		"Voltcraft", "M-3650CR", "1200/7n2/rts=0/dtr=1", 1200,
+		METEX14_PACKET_SIZE, 150, 20, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"voltcraft-m3650d", metex14,
+		"Voltcraft", "M-3650D", "1200/7n2/rts=0/dtr=1", 1200,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"voltcraft-m4650cr", metex14,
+		"Voltcraft", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
+		METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"voltcraft-me42", metex14,
+		"Voltcraft", "ME-42", "600/7n2/rts=0/dtr=1", 600,
+		METEX14_PACKET_SIZE, 250, 60, sr_metex14_packet_request,
+		sr_metex14_packet_valid, sr_metex14_parse,
+		NULL
+	),
+	DMM(
+		"voltcraft-vc820-ser", fs9721,
+		"Voltcraft", "VC-820 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		NULL
+	),
+	DMM(
+		/*
+		 * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
+		 * the FS9922 protocol. Instead, it only sets the user-defined
+		 * bit "z1" to indicate "diode mode" and "voltage".
+		 */
+		"voltcraft-vc830-ser", fs9922,
+		"Voltcraft", "VC-830 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9922_packet_valid, sr_fs9922_parse,
+		&sr_fs9922_z1_diode
+	),
+	DMM(
+		"voltcraft-vc840-ser", fs9721,
+		"Voltcraft", "VC-840 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_00_temp_c
+	),
+	DMM(
+		"voltcraft-vc870-ser", vc870,
+		"Voltcraft", "VC-870 (UT-D02 cable)", "9600/8n1/rts=0/dtr=1",
+		9600, VC870_PACKET_SIZE, 0, 0, NULL,
+		sr_vc870_packet_valid, sr_vc870_parse, NULL
+	),
+	DMM(
+		"voltcraft-vc920-ser", ut71x,
+		"Voltcraft", "VC-920 (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"voltcraft-vc940-ser", ut71x,
+		"Voltcraft", "VC-940 (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"voltcraft-vc960-ser", ut71x, 
+		"Voltcraft", "VC-960 (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut60a-ser", fs9721,
+		"UNI-T", "UT60A (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut60e-ser", fs9721,
+		"UNI-T", "UT60E (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_00_temp_c
+	),
+	DMM(
+		/* Note: ES51986 baudrate is actually 19230! */
+		"uni-t-ut60g-ser", es519xx,
+		"UNI-T", "UT60G (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
+		19200, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
+		sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut61b-ser", fs9922,
+		"UNI-T", "UT61B (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9922_packet_valid, sr_fs9922_parse, NULL
+	),
+	DMM(
+		"uni-t-ut61c-ser", fs9922,
+		"UNI-T", "UT61C (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9922_packet_valid, sr_fs9922_parse, NULL
+	),
+	DMM(
+		"uni-t-ut61d-ser", fs9922,
+		"UNI-T", "UT61D (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9922_packet_valid, sr_fs9922_parse, NULL
+	),
+	DMM(
+		"uni-t-ut61e-ser", es519xx,
+		/* Note: ES51922 baudrate is actually 19230! */
+		"UNI-T", "UT61E (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
+		19200, ES519XX_14B_PACKET_SIZE, 0, 0, NULL,
+		sr_es519xx_19200_14b_packet_valid, sr_es519xx_19200_14b_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut71a-ser", ut71x,
+		"UNI-T", "UT71A (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut71b-ser", ut71x,
+		"UNI-T", "UT71B (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut71c-ser", ut71x,
+		"UNI-T", "UT71C (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut71d-ser", ut71x,
+		"UNI-T", "UT71D (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut71e-ser", ut71x,
+		"UNI-T", "UT71E (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"iso-tech-idm103n", es519xx,
+		"ISO-TECH", "IDM103N", "2400/7o1/rts=0/dtr=1",
+		2400, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
+		sr_es519xx_2400_11b_packet_valid, sr_es519xx_2400_11b_parse,
+		NULL
+	),
+	DMM(
+		"tenma-72-7730-ser", ut71x,
+		"Tenma", "72-7730 (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"tenma-72-7732-ser", ut71x,
+		"Tenma", "72-7732 (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"tenma-72-9380a-ser", ut71x,
+		"Tenma", "72-9380A (UT-D02 cable)", "2400/7o1/rts=0/dtr=1",
+		2400, UT71X_PACKET_SIZE, 0, 0, NULL,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"tenma-72-7745-ser", fs9721,
+		"Tenma", "72-7745 (UT-D02 cable)", "2400/8n1/rts=0/dtr=1",
+		2400, FS9721_PACKET_SIZE, 0, 0, NULL,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_00_temp_c
+	),
+	DMM(
+		"tenma-72-7750-ser", es519xx,
+		/* Note: ES51986 baudrate is actually 19230! */
+		"Tenma", "72-7750 (UT-D02 cable)", "19200/7o1/rts=0/dtr=1",
+		19200, ES519XX_11B_PACKET_SIZE, 0, 0, NULL,
+		sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
+		NULL
+	),
+	DMM(
+		"brymen-bm25x", bm25x,
+		"Brymen", "BM25x", "9600/8n1/rts=1/dtr=1",
+		9600, BRYMEN_BM25X_PACKET_SIZE, 0, 0, NULL,
+		sr_brymen_bm25x_packet_valid, sr_brymen_bm25x_parse,
+		NULL
+	),
+	DMM(
+		"velleman-dvm4100", dtm0660,
+		"Velleman", "DVM4100", "2400/8n1/rts=0/dtr=1",
+		2400, DTM0660_PACKET_SIZE, 0, 0, NULL,
+		sr_dtm0660_packet_valid, sr_dtm0660_parse, NULL
+	),
+	DMM(
+		"peaktech-3415", dtm0660,
+		"Peaktech", "3415", "2400/8n1/rts=0/dtr=1",
+		2400, DTM0660_PACKET_SIZE, 0, 0, NULL,
+		sr_dtm0660_packet_valid, sr_dtm0660_parse, NULL
+	),
+	NULL
+};
diff --git a/hardware/serial-dmm/protocol.c b/src/hardware/serial-dmm/protocol.c
similarity index 53%
rename from hardware/serial-dmm/protocol.c
rename to src/hardware/serial-dmm/protocol.c
index 36e6944..8e9b397 100644
--- a/hardware/serial-dmm/protocol.c
+++ b/src/hardware/serial-dmm/protocol.c
@@ -18,68 +18,111 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
-#include <errno.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
 static void log_dmm_packet(const uint8_t *buf)
 {
-	sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x"
-	       " %02x %02x %02x %02x %02x %02x %02x",
+	sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x "
+	       "%02x %02x %02x %02x %02x %02x %02x %02x %02x "
+	       "%02x %02x %02x %02x %02x %02x %02x",
 	       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
-	       buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
+	       buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13],
+	       buf[14], buf[15], buf[16], buf[17], buf[18], buf[19], buf[20],
+	       buf[21], buf[22]);
 }
 
 static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi,
-			  int dmm, void *info)
+			  void *info)
 {
+	struct dmm_info *dmm;
 	float floatval;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct dev_context *devc;
 
+	dmm = (struct dmm_info *)sdi->driver;
+
 	log_dmm_packet(buf);
 	devc = sdi->priv;
 
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 
 	analog.channels = sdi->channels;
 	analog.num_samples = 1;
 	analog.mq = -1;
 
-	dmms[dmm].packet_parse(buf, &floatval, &analog, info);
+	dmm->packet_parse(buf, &floatval, &analog, info);
 	analog.data = &floatval;
 
 	/* If this DMM needs additional handling, call the resp. function. */
-	if (dmms[dmm].dmm_details)
-		dmms[dmm].dmm_details(&analog, info);
+	if (dmm->dmm_details)
+		dmm->dmm_details(&analog, info);
 
 	if (analog.mq != -1) {
 		/* Got a measurement. */
-		packet.type = SR_DF_ANALOG;
+		packet.type = SR_DF_ANALOG_OLD;
 		packet.payload = &analog;
 		sr_session_send(devc->cb_data, &packet);
 		devc->num_samples++;
 	}
 }
 
-static void handle_new_data(struct sr_dev_inst *sdi, int dmm, void *info)
+/** Request packet, if required. */
+SR_PRIV int req_packet(struct sr_dev_inst *sdi)
+{
+	struct dmm_info *dmm;
+	struct dev_context *devc;
+	struct sr_serial_dev_inst *serial;
+	int ret;
+
+	dmm = (struct dmm_info *)sdi->driver;
+
+	if (!dmm->packet_request)
+		return SR_OK;
+
+	devc = sdi->priv;
+	serial = sdi->conn;
+
+	if (devc->req_next_at && (devc->req_next_at > g_get_monotonic_time())) {
+		sr_spew("Not requesting new packet yet, %" PRIi64 " ms left.",
+			((devc->req_next_at - g_get_monotonic_time()) / 1000));
+		return SR_OK;
+	}
+
+	ret = dmm->packet_request(serial);
+	if (ret < 0) {
+		sr_err("Failed to request packet: %d.", ret);
+		return ret;
+	}
+
+	if (dmm->req_timeout_ms)
+		devc->req_next_at = g_get_monotonic_time() + (dmm->req_timeout_ms * 1000);
+
+	return SR_OK;
+}
+
+static void handle_new_data(struct sr_dev_inst *sdi, void *info)
 {
+	struct dmm_info *dmm;
 	struct dev_context *devc;
 	int len, i, offset = 0;
 	struct sr_serial_dev_inst *serial;
 
+	dmm = (struct dmm_info *)sdi->driver;
+
 	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);
+	len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
 	if (len == 0)
 		return; /* No new bytes, nothing to do. */
 	if (len < 0) {
@@ -89,10 +132,18 @@ static void handle_new_data(struct sr_dev_inst *sdi, int dmm, void *info)
 	devc->buflen += len;
 
 	/* Now look for packets in that data. */
-	while ((devc->buflen - offset) >= dmms[dmm].packet_size) {
-		if (dmms[dmm].packet_valid(devc->buf + offset)) {
-			handle_packet(devc->buf + offset, sdi, dmm, info);
-			offset += dmms[dmm].packet_size;
+	while ((devc->buflen - offset) >= dmm->packet_size) {
+		if (dmm->packet_valid(devc->buf + offset)) {
+			handle_packet(devc->buf + offset, sdi, info);
+			offset += dmm->packet_size;
+
+			/* Request next packet, if required. */
+			if (!dmm->packet_request)
+				break;
+			if (dmm->req_timeout_ms || dmm->req_delay_ms)
+				devc->req_next_at = g_get_monotonic_time() +
+					dmm->req_delay_ms * 1000;
+			req_packet(sdi);
 		} else {
 			offset++;
 		}
@@ -104,13 +155,13 @@ static void handle_new_data(struct sr_dev_inst *sdi, int dmm, void *info)
 	devc->buflen -= offset;
 }
 
-static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
+int receive_data(int fd, int revents, void *cb_data)
 {
 	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
-	struct sr_serial_dev_inst *serial;
+	struct dmm_info *dmm;
 	int64_t time;
-	int ret;
+	void *info;
 
 	(void)fd;
 
@@ -120,20 +171,17 @@ static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
 	if (!(devc = sdi->priv))
 		return TRUE;
 
-	serial = sdi->conn;
+	dmm = (struct dmm_info *)sdi->driver;
 
 	if (revents == G_IO_IN) {
 		/* Serial data arrived. */
-		handle_new_data(sdi, dmm, info);
+		info = g_malloc(dmm->info_size);
+		handle_new_data(sdi, info);
+		g_free(info);
 	} else {
-		/* Timeout, send another packet request (if DMM needs it). */
-		if (dmms[dmm].packet_request) {
-			ret = dmms[dmm].packet_request(serial);
-			if (ret < 0) {
-				sr_err("Failed to request packet: %d.", ret);
-				return FALSE;
-			}
-		}
+		/* Timeout; send another packet request if DMM needs it. */
+		if (dmm->packet_request && (req_packet(sdi) < 0))
+			return FALSE;
 	}
 
 	if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
@@ -153,43 +201,3 @@ static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
 
 	return TRUE;
 }
-
-#define RECEIVE_DATA(ID_UPPER, DMM_DRIVER) \
-SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
-	struct DMM_DRIVER##_info info; \
-	return receive_data(fd, revents, ID_UPPER, &info, cb_data); }
-
-/* Driver-specific receive_data() wrappers */
-RECEIVE_DATA(BBCGM_M2110, metex14) /* metex14_info used as a dummy. */
-RECEIVE_DATA(DIGITEK_DT4000ZC, fs9721)
-RECEIVE_DATA(TEKPOWER_TP4000ZC, fs9721)
-RECEIVE_DATA(METEX_ME31, metex14)
-RECEIVE_DATA(PEAKTECH_3410, metex14)
-RECEIVE_DATA(MASTECH_MAS345, metex14)
-RECEIVE_DATA(VA_VA18B, fs9721)
-RECEIVE_DATA(VA_VA40B, fs9721)
-RECEIVE_DATA(METEX_M3640D, metex14)
-RECEIVE_DATA(METEX_M4650CR, metex14)
-RECEIVE_DATA(PEAKTECH_4370, metex14)
-RECEIVE_DATA(PCE_PCE_DM32, fs9721)
-RECEIVE_DATA(RADIOSHACK_22_168, metex14)
-RECEIVE_DATA(RADIOSHACK_22_805, metex14)
-RECEIVE_DATA(RADIOSHACK_22_812, rs9lcd)
-RECEIVE_DATA(TECPEL_DMM_8061_SER, fs9721)
-RECEIVE_DATA(VOLTCRAFT_M3650CR, metex14)
-RECEIVE_DATA(VOLTCRAFT_M3650D, metex14)
-RECEIVE_DATA(VOLTCRAFT_M4650CR, metex14)
-RECEIVE_DATA(VOLTCRAFT_ME42, metex14)
-RECEIVE_DATA(VOLTCRAFT_VC820_SER, fs9721)
-RECEIVE_DATA(VOLTCRAFT_VC830_SER, fs9922)
-RECEIVE_DATA(VOLTCRAFT_VC840_SER, fs9721)
-RECEIVE_DATA(UNI_T_UT60A_SER, fs9721)
-RECEIVE_DATA(UNI_T_UT60E_SER, fs9721)
-RECEIVE_DATA(UNI_T_UT60G_SER, es519xx)
-RECEIVE_DATA(UNI_T_UT61B_SER, fs9922)
-RECEIVE_DATA(UNI_T_UT61C_SER, fs9922)
-RECEIVE_DATA(UNI_T_UT61D_SER, fs9922)
-RECEIVE_DATA(UNI_T_UT61E_SER, es519xx)
-RECEIVE_DATA(ISO_TECH_IDM103N, es519xx)
-RECEIVE_DATA(TENMA_72_7745_SER, fs9721)
-RECEIVE_DATA(TENMA_72_7750_SER, es519xx)
diff --git a/src/hardware/serial-dmm/protocol.h b/src/hardware/serial-dmm/protocol.h
new file mode 100644
index 0000000..3c2d88b
--- /dev/null
+++ b/src/hardware/serial-dmm/protocol.h
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me at 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_SERIAL_DMM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SERIAL_DMM_PROTOCOL_H
+
+#define LOG_PREFIX "serial-dmm"
+
+struct dmm_info {
+	/** libsigrok driver info struct. */
+	struct sr_dev_driver di;
+	/** Manufacturer/brand. */
+	const char *vendor;
+	/** Model. */
+	const char *device;
+	/** serialconn string. */
+	const char *conn;
+	/** Baud rate. */
+	uint32_t baudrate;
+	/** Packet size in bytes. */
+	int packet_size;
+	/** Request timeout [ms] before request is considered lost and a new
+	 *  one is sent. Used only if device needs polling. */
+	int64_t req_timeout_ms;
+	/** Delay between reception of packet and next request. Some DMMs
+	 *  need this. Used only if device needs polling. */
+	int64_t req_delay_ms;
+	/** Packet request function. */
+	int (*packet_request)(struct sr_serial_dev_inst *);
+	/** Packet validation function. */
+	gboolean (*packet_valid)(const uint8_t *);
+	/** Packet parsing function. */
+	int (*packet_parse)(const uint8_t *, float *,
+			    struct sr_datafeed_analog_old *, void *);
+	/** */
+	void (*dmm_details)(struct sr_datafeed_analog_old *, void *);
+	/** Size of chipset info struct. */
+	gsize info_size;
+};
+
+#define DMM_BUFSIZE 256
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/** The current sampling limit (in number of samples). */
+	uint64_t limit_samples;
+
+	/** The time limit (in milliseconds). */
+	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;
+
+	/** The starting time of current sampling run. */
+	int64_t starttime;
+
+	uint8_t buf[DMM_BUFSIZE];
+	int bufoffset;
+	int buflen;
+
+	/** The timestamp [µs] to send the next request.
+	 *  Used only if device needs polling. */
+	int64_t req_next_at;
+};
+
+SR_PRIV int req_packet(struct sr_dev_inst *sdi);
+SR_PRIV int receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/src/hardware/sysclk-lwla/api.c b/src/hardware/sysclk-lwla/api.c
new file mode 100644
index 0000000..7e61903
--- /dev/null
+++ b/src/hardware/sysclk-lwla/api.c
@@ -0,0 +1,824 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <libusb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include <libsigrok-internal.h>
+#include "protocol.h"
+
+/* Supported device scan options.
+ */
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+};
+
+/* Driver capabilities.
+ */
+static const uint32_t drvopts[] = {
+	SR_CONF_LOGIC_ANALYZER,
+};
+
+/* Supported trigger match conditions.
+ */
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
+	SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING,
+};
+
+/* Names assigned to available trigger sources.
+ */
+static const char *const trigger_source_names[] = {
+	[TRIGGER_CHANNELS] = "CH",
+	[TRIGGER_EXT_TRG] = "TRG",
+};
+
+/* Names assigned to available edge slope choices.
+ */
+static const char *const signal_edge_names[] = {
+	[EDGE_POSITIVE] = "r",
+	[EDGE_NEGATIVE] = "f",
+};
+
+/* Initialize the SysClk LWLA driver.
+ */
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+/* Create a new sigrok device instance for the indicated LWLA model.
+ */
+static struct sr_dev_inst *dev_inst_new(const struct model_info *model)
+{
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	int i;
+	char name[8];
+
+	/* Initialize private device context. */
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->model = model;
+	devc->active_fpga_config = FPGA_NOCONF;
+	devc->cfg_rle = TRUE;
+	devc->samplerate = model->samplerates[0];
+	devc->channel_mask = (UINT64_C(1) << model->num_channels) - 1;
+
+	/* Create sigrok device instance. */
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(VENDOR_NAME);
+	sdi->model = g_strdup(model->name);
+	sdi->priv = devc;
+
+	/* Generate list of logic channels. */
+	for (i = 0; i < model->num_channels; i++) {
+		/* The LWLA series simply number channels from CH1 to CHxx. */
+		g_snprintf(name, sizeof(name), "CH%d", i + 1);
+		sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
+	}
+
+	return sdi;
+}
+
+/* Create a new device instance for a libusb device if it is a SysClk LWLA
+ * device and also matches the connection specification.
+ */
+static struct sr_dev_inst *dev_inst_new_matching(GSList *conn_matches,
+						 libusb_device *dev)
+{
+	GSList *node;
+	struct sr_usb_dev_inst *usb;
+	const struct model_info *model;
+	struct sr_dev_inst *sdi;
+	struct libusb_device_descriptor des;
+	int bus, address, ret;
+	unsigned int vid, pid;
+
+	bus = libusb_get_bus_number(dev);
+	address = libusb_get_device_address(dev);
+
+	for (node = conn_matches; node != NULL; node = node->next) {
+		usb = node->data;
+		if (usb && usb->bus == bus && usb->address == address)
+			break; /* found */
+	}
+	if (conn_matches && !node)
+		return NULL; /* no match */
+
+	ret = libusb_get_device_descriptor(dev, &des);
+	if (ret != 0) {
+		sr_err("Failed to get USB device descriptor: %s.",
+			libusb_error_name(ret));
+		return NULL;
+	}
+	vid = des.idVendor;
+	pid = des.idProduct;
+
+	/* Create sigrok device instance. */
+	if (vid == USB_VID_SYSCLK && pid == USB_PID_LWLA1016) {
+		model = &lwla1016_info;
+	} else if (vid == USB_VID_SYSCLK && pid == USB_PID_LWLA1034) {
+		model = &lwla1034_info;
+	} else {
+		if (conn_matches)
+			sr_warn("USB device %d.%d (%04x:%04x) is not a"
+				" SysClk LWLA.", bus, address, vid, pid);
+		return NULL;
+	}
+	sdi = dev_inst_new(model);
+
+	sdi->inst_type = SR_INST_USB;
+	sdi->conn = sr_usb_dev_inst_new(bus, address, NULL);
+
+	return sdi;
+}
+
+/* Scan for SysClk LWLA devices and create a device instance for each one.
+ */
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	GSList *conn_devices, *devices, *node;
+	struct drv_context *drvc;
+	struct sr_dev_inst *sdi;
+	struct sr_config *src;
+	const char *conn;
+	libusb_device **devlist;
+	ssize_t num_devs, i;
+
+	drvc = di->context;
+	conn = NULL;
+	conn_devices = NULL;
+	devices = NULL;
+
+	for (node = options; node != NULL; node = node->next) {
+		src = node->data;
+		if (src->key == SR_CONF_CONN) {
+			conn = g_variant_get_string(src->data, NULL);
+			break;
+		}
+	}
+	if (conn) {
+		/* Find devices matching the connection specification. */
+		conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+	}
+
+	/* List all libusb devices. */
+	num_devs = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+	if (num_devs < 0) {
+		sr_err("Failed to list USB devices: %s.",
+			libusb_error_name(num_devs));
+		g_slist_free_full(conn_devices,
+			(GDestroyNotify)&sr_usb_dev_inst_free);
+		return NULL;
+	}
+
+	/* Scan the USB device list for matching LWLA devices. */
+	for (i = 0; i < num_devs; i++) {
+		sdi = dev_inst_new_matching(conn_devices, devlist[i]);
+		if (!sdi)
+			continue; /* no match */
+
+		/* Register device instance with driver. */
+		sdi->driver = di;
+		drvc->instances = g_slist_append(drvc->instances, sdi);
+		devices = g_slist_append(devices, sdi);
+	}
+
+	libusb_free_device_list(devlist, 1);
+	g_slist_free_full(conn_devices, (GDestroyNotify)&sr_usb_dev_inst_free);
+
+	return devices;
+}
+
+/* Return the list of devices found during scan.
+ */
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+/* Destroy the private device context.
+ */
+static void clear_dev_context(void *priv)
+{
+	struct dev_context *devc;
+
+	devc = priv;
+
+	if (devc->acquisition) {
+		sr_err("Cannot clear device context during acquisition!");
+		return; /* Leak and pray. */
+	}
+	sr_dbg("Device context cleared.");
+
+	g_free(devc);
+}
+
+/* Destroy all device instances.
+ */
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, &clear_dev_context);
+}
+
+/* Drain any pending data from the USB transfer buffers on the device.
+ * This may be necessary e.g. after a crash or generally to clean up after
+ * an abnormal condition.
+ */
+static int drain_usb(struct sr_usb_dev_inst *usb, unsigned int endpoint)
+{
+	int drained, xfer_len, ret;
+	unsigned char buf[512];
+	const unsigned int drain_timeout_ms = 10;
+
+	drained = 0;
+	do {
+		xfer_len = 0;
+		ret = libusb_bulk_transfer(usb->devhdl, endpoint,
+					   buf, sizeof(buf), &xfer_len,
+					   drain_timeout_ms);
+		drained += xfer_len;
+	} while (ret == LIBUSB_SUCCESS && xfer_len != 0);
+
+	if (ret != LIBUSB_SUCCESS && ret != LIBUSB_ERROR_TIMEOUT) {
+		sr_err("Failed to drain USB endpoint %u: %s.",
+		       endpoint & (LIBUSB_ENDPOINT_IN - 1),
+		       libusb_error_name(ret));
+		return SR_ERR;
+	}
+	if (drained > 0) {
+		sr_warn("Drained %d bytes from USB endpoint %u.",
+			drained, endpoint & (LIBUSB_ENDPOINT_IN - 1));
+	}
+
+	return SR_OK;
+}
+
+/* Open and initialize device.
+ */
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	int i, ret;
+
+	drvc = sdi->driver->context;
+	devc = sdi->priv;
+	usb = sdi->conn;
+
+	if (!drvc) {
+		sr_err("Driver was not initialized.");
+		return SR_ERR;
+	}
+	if (sdi->status != SR_ST_INACTIVE) {
+		sr_err("Device already open.");
+		return SR_ERR;
+	}
+
+	/* Try the whole shebang three times, fingers crossed. */
+	for (i = 0; i < 3; i++) {
+		ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
+		if (ret != SR_OK)
+			return ret;
+
+		ret = libusb_set_configuration(usb->devhdl, USB_CONFIG);
+		if (ret != LIBUSB_SUCCESS) {
+			sr_err("Failed to set USB configuration: %s.",
+				libusb_error_name(ret));
+			sr_usb_close(usb);
+			return SR_ERR;
+		}
+
+		ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+		if (ret != LIBUSB_SUCCESS) {
+			sr_err("Failed to claim interface: %s.",
+				libusb_error_name(ret));
+			sr_usb_close(usb);
+			return SR_ERR;
+		}
+
+		ret = drain_usb(usb, EP_REPLY);
+		if (ret != SR_OK) {
+			sr_usb_close(usb);
+			return ret;
+		}
+		/* This delay appears to be necessary for reliable operation. */
+		g_usleep(30 * 1000);
+
+		sdi->status = SR_ST_ACTIVE;
+
+		devc->active_fpga_config = FPGA_NOCONF;
+		devc->short_transfer_quirk = FALSE;
+		devc->state = STATE_IDLE;
+
+		ret = (*devc->model->apply_fpga_config)(sdi);
+
+		if (ret == SR_OK)
+			ret = (*devc->model->device_init_check)(sdi);
+		if (ret == SR_OK)
+			break;
+
+		/* Rinse and repeat. */
+		sdi->status = SR_ST_INACTIVE;
+		sr_usb_close(usb);
+	}
+
+	if (ret == SR_OK && devc->short_transfer_quirk)
+		sr_warn("Short transfer quirk detected! "
+			"Memory reads will be slow.");
+	return ret;
+}
+
+/* Shutdown and close device.
+ */
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	int ret;
+
+	drvc = sdi->driver->context;
+	devc = sdi->priv;
+	usb = sdi->conn;
+
+	if (!drvc) {
+		sr_err("Driver was not initialized.");
+		return SR_ERR;
+	}
+	if (sdi->status == SR_ST_INACTIVE) {
+		sr_dbg("Device already closed.");
+		return SR_OK;
+	}
+	if (devc->acquisition) {
+		sr_err("Cannot close device during acquisition!");
+		/* Request stop, leak handle, and prepare for the worst. */
+		devc->cancel_requested = TRUE;
+		return SR_ERR_BUG;
+	}
+
+	sdi->status = SR_ST_INACTIVE;
+
+	/* Download of the shutdown bitstream, if any. */
+	ret = (*devc->model->apply_fpga_config)(sdi);
+	if (ret != SR_OK)
+		sr_warn("Unable to shut down device.");
+
+	libusb_release_interface(usb->devhdl, USB_INTERFACE);
+	sr_usb_close(usb);
+
+	return ret;
+}
+
+/* Check whether the device options contain a specific key.
+ * Also match against get/set/list bits if specified.
+ */
+static int has_devopt(const struct model_info *model, uint32_t key)
+{
+	unsigned int i;
+
+	for (i = 0; i < model->num_devopts; i++) {
+		if ((model->devopts[i] & (SR_CONF_MASK | key)) == key)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+/* Read device configuration setting.
+ */
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		      const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	unsigned int idx;
+
+	(void)cg;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	if (!has_devopt(devc->model, key | SR_CONF_GET))
+		return SR_ERR_NA;
+
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		*data = g_variant_new_uint64(devc->samplerate);
+		break;
+	case SR_CONF_LIMIT_MSEC:
+		*data = g_variant_new_uint64(devc->limit_msec);
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		*data = g_variant_new_uint64(devc->limit_samples);
+		break;
+	case SR_CONF_RLE:
+		*data = g_variant_new_boolean(devc->cfg_rle);
+		break;
+	case SR_CONF_EXTERNAL_CLOCK:
+		*data = g_variant_new_boolean(devc->cfg_clock_source
+						== CLOCK_EXT_CLK);
+		break;
+	case SR_CONF_CLOCK_EDGE:
+		idx = devc->cfg_clock_edge;
+		if (idx >= ARRAY_SIZE(signal_edge_names))
+			return SR_ERR_BUG;
+		*data = g_variant_new_string(signal_edge_names[idx]);
+		break;
+	case SR_CONF_TRIGGER_SOURCE:
+		idx = devc->cfg_trigger_source;
+		if (idx >= ARRAY_SIZE(trigger_source_names))
+			return SR_ERR_BUG;
+		*data = g_variant_new_string(trigger_source_names[idx]);
+		break;
+	case SR_CONF_TRIGGER_SLOPE:
+		idx = devc->cfg_trigger_slope;
+		if (idx >= ARRAY_SIZE(signal_edge_names))
+			return SR_ERR_BUG;
+		*data = g_variant_new_string(signal_edge_names[idx]);
+		break;
+	default:
+		/* Must not happen for a key listed in devopts. */
+		return SR_ERR_BUG;
+	}
+
+	return SR_OK;
+}
+
+/* Helper for mapping a string-typed configuration value to an index
+ * within a table of possible values.
+ */
+static int lookup_index(GVariant *value, const char *const *table, int len)
+{
+	const char *entry;
+	int i;
+
+	entry = g_variant_get_string(value, NULL);
+	if (!entry)
+		return -1;
+
+	/* Linear search is fine for very small tables. */
+	for (i = 0; i < len; i++) {
+		if (strcmp(entry, table[i]) == 0)
+			return i;
+	}
+
+	return -1;
+}
+
+/* Write device configuration setting.
+ */
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		      const struct sr_channel_group *cg)
+{
+	uint64_t value;
+	struct dev_context *devc;
+	int idx;
+
+	(void)cg;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	if (!has_devopt(devc->model, key | SR_CONF_SET))
+		return SR_ERR_NA;
+
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		value = g_variant_get_uint64(data);
+		if (value < devc->model->samplerates[devc->model->num_samplerates - 1]
+				|| value > devc->model->samplerates[0])
+			return SR_ERR_SAMPLERATE;
+		devc->samplerate = value;
+		break;
+	case SR_CONF_LIMIT_MSEC:
+		value = g_variant_get_uint64(data);
+		if (value > MAX_LIMIT_MSEC)
+			return SR_ERR_ARG;
+		devc->limit_msec = value;
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		value = g_variant_get_uint64(data);
+		if (value > MAX_LIMIT_SAMPLES)
+			return SR_ERR_ARG;
+		devc->limit_samples = value;
+		break;
+	case SR_CONF_RLE:
+		devc->cfg_rle = g_variant_get_boolean(data);
+		break;
+	case SR_CONF_EXTERNAL_CLOCK:
+		devc->cfg_clock_source = (g_variant_get_boolean(data))
+			? CLOCK_EXT_CLK : CLOCK_INTERNAL;
+		break;
+	case SR_CONF_CLOCK_EDGE:
+		idx = lookup_index(data, signal_edge_names,
+				   ARRAY_SIZE(signal_edge_names));
+		if (idx < 0)
+			return SR_ERR_ARG;
+		devc->cfg_clock_edge = idx;
+		break;
+	case SR_CONF_TRIGGER_SOURCE:
+		idx = lookup_index(data, trigger_source_names,
+				   ARRAY_SIZE(trigger_source_names));
+		if (idx < 0)
+			return SR_ERR_ARG;
+		devc->cfg_trigger_source = idx;
+		break;
+	case SR_CONF_TRIGGER_SLOPE:
+		idx = lookup_index(data, signal_edge_names,
+				   ARRAY_SIZE(signal_edge_names));
+		if (idx < 0)
+			return SR_ERR_ARG;
+		devc->cfg_trigger_slope = idx;
+		break;
+	default:
+		/* Must not happen for a key listed in devopts. */
+		return SR_ERR_BUG;
+	}
+
+	return SR_OK;
+}
+
+/* Apply channel configuration change.
+ */
+static int config_channel_set(const struct sr_dev_inst *sdi,
+			      struct sr_channel *ch, unsigned int changes)
+{
+	uint64_t channel_bit;
+	struct dev_context *devc;
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	if (ch->index < 0 || ch->index >= devc->model->num_channels) {
+		sr_err("Channel index %d out of range.", ch->index);
+		return SR_ERR_BUG;
+	}
+
+	if ((changes & SR_CHANNEL_SET_ENABLED) != 0) {
+		channel_bit = UINT64_C(1) << ch->index;
+
+		/* Enable or disable logic input for this channel. */
+		if (ch->enabled)
+			devc->channel_mask |= channel_bit;
+		else
+			devc->channel_mask &= ~channel_bit;
+	}
+
+	return SR_OK;
+}
+
+/* Derive trigger masks from the session's trigger configuration.
+ */
+static int prepare_trigger_masks(const struct sr_dev_inst *sdi)
+{
+	uint64_t trigger_mask, trigger_values, trigger_edge_mask;
+	uint64_t level_bit, type_bit;
+	struct dev_context *devc;
+	struct sr_trigger *trigger;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	const GSList *node;
+	int idx;
+	enum sr_trigger_matches trg;
+
+	devc = sdi->priv;
+
+	trigger = sr_session_trigger_get(sdi->session);
+	if (!trigger || !trigger->stages)
+		return SR_OK;
+
+	if (trigger->stages->next) {
+		sr_err("This device only supports 1 trigger stage.");
+		return SR_ERR_ARG;
+	}
+	stage = trigger->stages->data;
+
+	trigger_mask = 0;
+	trigger_values = 0;
+	trigger_edge_mask = 0;
+
+	for (node = stage->matches; node; node = node->next) {
+		match = node->data;
+
+		if (!match->channel->enabled)
+			continue; /* Ignore disabled channel. */
+
+		idx = match->channel->index;
+		trg = match->match;
+
+		if (idx < 0 || idx >= devc->model->num_channels) {
+			sr_err("Channel index %d out of range.", idx);
+			return SR_ERR_BUG; /* Should not happen. */
+		}
+		if (trg != SR_TRIGGER_ZERO
+				&& trg != SR_TRIGGER_ONE
+				&& trg != SR_TRIGGER_RISING
+				&& trg != SR_TRIGGER_FALLING) {
+			sr_err("Unsupported trigger match for CH%d.", idx + 1);
+			return SR_ERR_ARG;
+		}
+		level_bit = (trg == SR_TRIGGER_ONE
+			|| trg == SR_TRIGGER_RISING) ? 1 : 0;
+		type_bit = (trg == SR_TRIGGER_RISING
+			|| trg == SR_TRIGGER_FALLING) ? 1 : 0;
+
+		trigger_mask |= UINT64_C(1) << idx;
+		trigger_values |= level_bit << idx;
+		trigger_edge_mask |= type_bit << idx;
+	}
+	devc->trigger_mask = trigger_mask;
+	devc->trigger_values = trigger_values;
+	devc->trigger_edge_mask = trigger_edge_mask;
+
+	return SR_OK;
+}
+
+/* Apply current device configuration to the hardware.
+ */
+static int config_commit(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	int ret;
+
+	devc = sdi->priv;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (devc->acquisition) {
+		sr_err("Acquisition still in progress?");
+		return SR_ERR;
+	}
+
+	ret = prepare_trigger_masks(sdi);
+	if (ret != SR_OK)
+		return ret;
+
+	ret = (*devc->model->apply_fpga_config)(sdi);
+	if (ret != SR_OK) {
+		sr_err("Failed to apply FPGA configuration.");
+		return ret;
+	}
+
+	return SR_OK;
+}
+
+/* List available choices for a configuration setting.
+ */
+static int config_list(uint32_t key, GVariant **data,
+		       const struct sr_dev_inst *sdi,
+		       const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	GVariant *gvar;
+	GVariantBuilder gvb;
+
+	(void)cg;
+
+	if (key == SR_CONF_SCAN_OPTIONS) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			scanopts, ARRAY_SIZE(scanopts), sizeof(scanopts[0]));
+		return SR_OK;
+	}
+	if (!sdi) {
+		if (key != SR_CONF_DEVICE_OPTIONS)
+			return SR_ERR_ARG;
+
+		/* List driver capabilities. */
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			drvopts, ARRAY_SIZE(drvopts), sizeof(drvopts[0]));
+		return SR_OK;
+	}
+
+	devc = sdi->priv;
+
+	/* List the model's device options. */
+	if (key == SR_CONF_DEVICE_OPTIONS) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			devc->model->devopts, devc->model->num_devopts,
+			sizeof(devc->model->devopts[0]));
+		return SR_OK;
+	}
+
+	if (!has_devopt(devc->model, key | SR_CONF_LIST))
+		return SR_ERR_NA;
+
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		g_variant_builder_init(&gvb, G_VARIANT_TYPE_VARDICT);
+		gvar = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
+			devc->model->samplerates, devc->model->num_samplerates,
+			sizeof(devc->model->samplerates[0]));
+		g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+		*data = g_variant_builder_end(&gvb);
+		break;
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+			trigger_matches, ARRAY_SIZE(trigger_matches),
+			sizeof(trigger_matches[0]));
+		break;
+	case SR_CONF_TRIGGER_SOURCE:
+		*data = g_variant_new_strv(trigger_source_names,
+			ARRAY_SIZE(trigger_source_names));
+		break;
+	case SR_CONF_TRIGGER_SLOPE:
+	case SR_CONF_CLOCK_EDGE:
+		*data = g_variant_new_strv(signal_edge_names,
+			ARRAY_SIZE(signal_edge_names));
+		break;
+	default:
+		/* Must not happen for a key listed in devopts. */
+		return SR_ERR_BUG;
+	}
+
+	return SR_OK;
+}
+
+/* Set up the device hardware to begin capturing samples as soon as the
+ * configured trigger conditions are met, or immediately if no triggers
+ * are configured.
+ */
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	(void)cb_data;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	sr_info("Starting acquisition.");
+
+	return lwla_start_acquisition(sdi);
+}
+
+/* Request that a running capture operation be stopped.
+ */
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc;
+
+	(void)cb_data;
+
+	devc = sdi->priv;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (devc->state != STATE_IDLE && !devc->cancel_requested) {
+		devc->cancel_requested = TRUE;
+		sr_dbg("Stopping acquisition.");
+	}
+
+	return SR_OK;
+}
+
+/* SysClk LWLA driver descriptor.
+ */
+SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info = {
+	.name = "sysclk-lwla",
+	.longname = "SysClk LWLA series",
+	.api_version = 1,
+	.init = init,
+	.cleanup = dev_clear,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_channel_set = config_channel_set,
+	.config_commit = config_commit,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/hardware/sysclk-lwla/lwla.c b/src/hardware/sysclk-lwla/lwla.c
similarity index 58%
rename from hardware/sysclk-lwla/lwla.c
rename to src/hardware/sysclk-lwla/lwla.c
index dcb7af7..6e6413f 100644
--- a/hardware/sysclk-lwla/lwla.c
+++ b/src/hardware/sysclk-lwla/lwla.c
@@ -17,68 +17,57 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
+#include <glib/gstdio.h>
+#include <libsigrok/libsigrok.h>
+#include <libsigrok-internal.h>
 #include "lwla.h"
 #include "protocol.h"
-#include "libsigrok-internal.h"
-#include <errno.h>
-#include <glib/gstdio.h>
 
-#define BITSTREAM_MAX_SIZE	262144	/* bitstream size limit for safety */
-#define BITSTREAM_HEADER_SIZE	4	/* transfer header size in bytes */
+#define BITSTREAM_MAX_SIZE    (256 * 1024) /* Bitstream size limit for safety */
+#define BITSTREAM_HEADER_SIZE 4            /* Transfer header size in bytes */
 
-/* Load a bitstream file into memory.  Returns a newly allocated array
+/* Load a bitstream file into memory. Returns a newly allocated array
  * consisting of a 32-bit length field followed by the bitstream data.
  */
-static unsigned char *load_bitstream_file(const char *filename, int *length_p)
+static unsigned char *load_bitstream(struct sr_context *ctx,
+				     const char *name, int *length_p)
 {
-	GStatBuf statbuf;
-	FILE *file;
+	struct sr_resource rbf;
 	unsigned char *stream;
-	size_t length, count;
+	ssize_t length, count;
 
-	/* Retrieve and validate the file size. */
-	if (g_stat(filename, &statbuf) < 0) {
-		sr_err("Failed to access bitstream file: %s.",
-		       g_strerror(errno));
-		return NULL;
-	}
-	if (!S_ISREG(statbuf.st_mode)) {
-		sr_err("Bitstream is not a regular file.");
+	if (sr_resource_open(ctx, &rbf, SR_RESOURCE_FIRMWARE, name) != SR_OK)
 		return NULL;
-	}
-	if (statbuf.st_size <= 0 || statbuf.st_size > BITSTREAM_MAX_SIZE) {
+
+	if (rbf.size == 0 || rbf.size > BITSTREAM_MAX_SIZE) {
 		sr_err("Refusing to load bitstream of unreasonable size "
-		       "(%" PRIu64 " bytes).", (uint64_t)statbuf.st_size);
+		       "(%" PRIu64 " bytes).", rbf.size);
+		sr_resource_close(ctx, &rbf);
 		return NULL;
 	}
 
 	/* The message length includes the 4-byte header. */
-	length = BITSTREAM_HEADER_SIZE + statbuf.st_size;
+	length = BITSTREAM_HEADER_SIZE + rbf.size;
 	stream = g_try_malloc(length);
 	if (!stream) {
 		sr_err("Failed to allocate bitstream buffer.");
-		return NULL;
-	}
-
-	file = g_fopen(filename, "rb");
-	if (!file) {
-		sr_err("Failed to open bitstream file: %s.", g_strerror(errno));
-		g_free(stream);
+		sr_resource_close(ctx, &rbf);
 		return NULL;
 	}
 
 	/* Write the message length header. */
 	*(uint32_t *)stream = GUINT32_TO_BE(length);
 
-	count = fread(stream + BITSTREAM_HEADER_SIZE,
-		      length - BITSTREAM_HEADER_SIZE, 1, file);
-	if (count != 1) {
-		sr_err("Failed to read bitstream file: %s.", g_strerror(errno));
-		fclose(file);
+	count = sr_resource_read(ctx, &rbf, stream + BITSTREAM_HEADER_SIZE,
+				 length - BITSTREAM_HEADER_SIZE);
+	sr_resource_close(ctx, &rbf);
+
+	if (count != length - BITSTREAM_HEADER_SIZE) {
+		sr_err("Failed to read bitstream '%s'.", name);
 		g_free(stream);
 		return NULL;
 	}
-	fclose(file);
 
 	*length_p = length;
 	return stream;
@@ -87,30 +76,25 @@ static unsigned char *load_bitstream_file(const char *filename, int *length_p)
 /* Load a Raw Binary File (.rbf) from the firmware directory and transfer
  * it to the device.
  */
-SR_PRIV int lwla_send_bitstream(const struct sr_usb_dev_inst *usb,
-				const char *basename)
+SR_PRIV int lwla_send_bitstream(struct sr_context *ctx,
+				const struct sr_usb_dev_inst *usb,
+				const char *name)
 {
-	char *filename;
 	unsigned char *stream;
-	int ret;
-	int length;
-	int xfer_len;
+	int ret, length, xfer_len;
 
-	if (!usb || !basename)
+	if (!ctx || !usb || !name)
 		return SR_ERR_BUG;
 
-	filename = g_build_filename(FIRMWARE_DIR, basename, NULL);
-	sr_info("Downloading FPGA bitstream at '%s'.", filename);
-
-	stream = load_bitstream_file(filename, &length);
-	g_free(filename);
-
+	stream = load_bitstream(ctx, name, &length);
 	if (!stream)
 		return SR_ERR;
 
+	sr_info("Downloading FPGA bitstream '%s'.", name);
+
 	/* Transfer the entire bitstream in one URB. */
-	ret = libusb_bulk_transfer(usb->devhdl, EP_BITSTREAM,
-				   stream, length, &xfer_len, USB_TIMEOUT);
+	ret = libusb_bulk_transfer(usb->devhdl, EP_CONFIG,
+				   stream, length, &xfer_len, USB_TIMEOUT_MS);
 	g_free(stream);
 
 	if (ret != 0) {
@@ -126,7 +110,7 @@ SR_PRIV int lwla_send_bitstream(const struct sr_usb_dev_inst *usb,
 	sr_info("FPGA bitstream download of %d bytes done.", xfer_len);
 
 	/* This delay appears to be necessary for reliable operation. */
-	g_usleep(30000);
+	g_usleep(30 * 1000);
 
 	return SR_OK;
 }
@@ -134,8 +118,7 @@ SR_PRIV int lwla_send_bitstream(const struct sr_usb_dev_inst *usb,
 SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
 			      const uint16_t *command, int cmd_len)
 {
-	int ret;
-	int xfer_len;
+	int ret, xfer_len;
 
 	if (!usb || !command || cmd_len <= 0)
 		return SR_ERR_BUG;
@@ -143,7 +126,7 @@ SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
 	xfer_len = 0;
 	ret = libusb_bulk_transfer(usb->devhdl, EP_COMMAND,
 				   (unsigned char *)command, cmd_len * 2,
-				   &xfer_len, USB_TIMEOUT);
+				   &xfer_len, USB_TIMEOUT_MS);
 	if (ret != 0) {
 		sr_dbg("Failed to send command %d: %s.",
 		       LWLA_TO_UINT16(command[0]), libusb_error_name(ret));
@@ -154,55 +137,54 @@ SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
 		       LWLA_TO_UINT16(command[0]), xfer_len, cmd_len * 2);
 		return SR_ERR;
 	}
+
 	return SR_OK;
 }
 
 SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
-			       uint32_t *reply, int reply_len, int expect_len)
+			       void *reply, int buf_size, int *xfer_len)
 {
 	int ret;
-	int xfer_len;
 
-	if (!usb || !reply || reply_len <= 0)
+	if (!usb || !reply || buf_size <= 0)
 		return SR_ERR_BUG;
 
-	xfer_len = 0;
-	ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY,
-				   (unsigned char *)reply, reply_len * 4,
-				   &xfer_len, USB_TIMEOUT);
+	ret = libusb_bulk_transfer(usb->devhdl, EP_REPLY, reply, buf_size,
+				   xfer_len, USB_TIMEOUT_MS);
 	if (ret != 0) {
 		sr_dbg("Failed to receive reply: %s.", libusb_error_name(ret));
 		return SR_ERR;
 	}
-	if (xfer_len != expect_len * 4) {
-		sr_dbg("Failed to receive reply: incorrect length %d != %d.",
-		       xfer_len, expect_len * 4);
-		return SR_ERR;
-	}
+
 	return SR_OK;
 }
 
 SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
 			  uint16_t reg, uint32_t *value)
 {
-	int ret;
+	int xfer_len, ret;
 	uint16_t command[2];
-	uint32_t reply[128]; /* full EP buffer to avoid overflows */
+	uint32_t reply[128]; /* Full EP buffer to avoid overflows. */
 
 	command[0] = LWLA_WORD(CMD_READ_REG);
 	command[1] = LWLA_WORD(reg);
 
-	ret = lwla_send_command(usb, command, G_N_ELEMENTS(command));
-
+	ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
 	if (ret != SR_OK)
 		return ret;
 
-	ret = lwla_receive_reply(usb, reply, G_N_ELEMENTS(reply), 1);
+	ret = lwla_receive_reply(usb, reply, sizeof(reply), &xfer_len);
+	if (ret != SR_OK)
+		return ret;
 
-	if (ret == SR_OK)
-		*value = LWLA_TO_UINT32(reply[0]);
+	if (xfer_len != 4) {
+		sr_dbg("Invalid register read response of length %d.",
+		       xfer_len);
+		return SR_ERR;
+	}
+	*value = LWLA_TO_UINT32(reply[0]);
 
-	return ret;
+	return SR_OK;
 }
 
 SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
@@ -215,18 +197,17 @@ SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
 	command[2] = LWLA_WORD_0(value);
 	command[3] = LWLA_WORD_1(value);
 
-	return lwla_send_command(usb, command, G_N_ELEMENTS(command));
+	return lwla_send_command(usb, command, ARRAY_SIZE(command));
 }
 
 SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
-			    const struct regval_pair *regvals, int count)
+			    const struct regval *regvals, int count)
 {
-	int i;
-	int ret;
+	int i, ret;
 
 	ret = SR_OK;
 
-	for (i = 0; i < count; ++i) {
+	for (i = 0; i < count; i++) {
 		ret = lwla_write_reg(usb, regvals[i].reg, regvals[i].val);
 
 		if (ret != SR_OK)
diff --git a/src/hardware/sysclk-lwla/lwla.h b/src/hardware/sysclk-lwla/lwla.h
new file mode 100644
index 0000000..35e33f7
--- /dev/null
+++ b/src/hardware/sysclk-lwla/lwla.h
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta at 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_SYSCLK_LWLA_LWLA_H
+#define LIBSIGROK_HARDWARE_SYSCLK_LWLA_LWLA_H
+
+#include <stdint.h>
+#include <libusb.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+
+struct sr_usb_dev_inst;
+
+/* Rotate argument n bits to the left.
+ * This construct is an idiom recognized by GCC as bit rotation.
+ */
+#define LROTATE(a, n) (((a) << (n)) | ((a) >> (CHAR_BIT * sizeof(a) - (n))))
+
+/* Convert 16-bit little endian LWLA protocol word to machine word order. */
+#define LWLA_TO_UINT16(val) GUINT16_FROM_LE(val)
+
+/* Convert 32-bit mixed endian LWLA protocol word to machine word order. */
+#define LWLA_TO_UINT32(val) LROTATE(GUINT32_FROM_LE(val), 16)
+
+/* Convert 16-bit argument to LWLA protocol word. */
+#define LWLA_WORD(val) GUINT16_TO_LE(val)
+
+/* Extract 16-bit units in mixed endian order from 32/64-bit value. */
+#define LWLA_WORD_0(val) GUINT16_TO_LE(((val) >> 16) & 0xFFFF)
+#define LWLA_WORD_1(val) GUINT16_TO_LE((val) & 0xFFFF)
+#define LWLA_WORD_2(val) GUINT16_TO_LE(((val) >> 48) & 0xFFFF)
+#define LWLA_WORD_3(val) GUINT16_TO_LE(((val) >> 32) & 0xFFFF)
+
+/* Maximum number of 16-bit words sent at a time during acquisition.
+ * Used for allocating the libusb transfer buffer. Keep this even so that
+ * subsequent members are always 32-bit aligned.
+ */
+#define MAX_ACQ_SEND_LEN16	64 /* 43 for capture setup plus stuffing */
+
+/* Maximum number of 32-bit words received at a time during acquisition.
+ * This is a multiple of the endpoint buffer size to avoid transfer overflow
+ * conditions.
+ */
+#define MAX_ACQ_RECV_LEN32	(2 * 512 / 4)
+
+/* Maximum length of a register read/write sequence.
+ */
+#define MAX_REG_SEQ_LEN		8
+
+/* Logic datafeed packet size in bytes.
+ * This is a multiple of both 4 and 5 to match any model's unit size
+ * and memory granularity.
+ */
+#define PACKET_SIZE		(5000 * 4 * 5)
+
+/** LWLA protocol command ID codes.
+ */
+enum command_id {
+	CMD_READ_REG	= 1,
+	CMD_WRITE_REG	= 2,
+	CMD_READ_MEM32	= 3,
+	CMD_READ_MEM36	= 6,
+	CMD_WRITE_LREGS	= 7,
+	CMD_READ_LREGS	= 8,
+};
+
+/** LWLA capture state flags.
+ * The bit positions are the same as in the LWLA1016 control register.
+ */
+enum status_flag {
+	STATUS_CAPTURING = 1 << 2,
+	STATUS_TRIGGERED = 1 << 5,
+	STATUS_MEM_AVAIL = 1 << 6,
+};
+
+/** LWLA1034 run-length encoding states.
+ */
+enum rle_state {
+	RLE_STATE_DATA,
+	RLE_STATE_LEN
+};
+
+/** Register address/value pair.
+ */
+struct regval {
+	unsigned int reg;
+	uint32_t val;
+};
+
+/** LWLA sample acquisition and decompression state.
+ */
+struct acquisition_state {
+	uint64_t samples_max;	/* maximum number of samples to process */
+	uint64_t samples_done;	/* number of samples sent to the session bus */
+	uint64_t duration_max;	/* maximum capture duration in milliseconds */
+	uint64_t duration_now;	/* running capture duration since trigger */
+
+	uint64_t sample;	/* last sample read from capture memory */
+	uint64_t run_len;	/* remaining run length of current sample */
+
+	struct libusb_transfer *xfer_in;	/* USB in transfer record */
+	struct libusb_transfer *xfer_out;	/* USB out transfer record */
+
+	unsigned int mem_addr_fill;	/* capture memory fill level */
+	unsigned int mem_addr_done;	/* next address to be processed */
+	unsigned int mem_addr_next;	/* start address for next async read */
+	unsigned int mem_addr_stop;	/* end of memory range to be read */
+	unsigned int in_index;		/* position in read transfer buffer */
+	unsigned int out_index;		/* position in logic packet buffer */
+	enum rle_state rle;		/* RLE decoding state */
+
+	gboolean rle_enabled;	/* capturing in timing-state mode */
+	gboolean clock_boost;	/* switch to faster clock during capture */
+	unsigned int status;	/* last received device status */
+
+	unsigned int reg_seq_pos;	/* index of next register/value pair */
+	unsigned int reg_seq_len;	/* length of register/value sequence */
+
+	struct regval reg_sequence[MAX_REG_SEQ_LEN];	/* register buffer */
+	uint32_t xfer_buf_in[MAX_ACQ_RECV_LEN32];	/* USB in buffer */
+	uint16_t xfer_buf_out[MAX_ACQ_SEND_LEN16];	/* USB out buffer */
+	uint8_t out_packet[PACKET_SIZE];		/* logic payload */
+};
+
+static inline void lwla_queue_regval(struct acquisition_state *acq,
+				     unsigned int reg, uint32_t value)
+{
+	acq->reg_sequence[acq->reg_seq_len].reg = reg;
+	acq->reg_sequence[acq->reg_seq_len].val = value;
+	acq->reg_seq_len++;
+}
+
+SR_PRIV int lwla_send_bitstream(struct sr_context *ctx,
+				const struct sr_usb_dev_inst *usb,
+				const char *name);
+
+SR_PRIV int lwla_send_command(const struct sr_usb_dev_inst *usb,
+			      const uint16_t *command, int cmd_len);
+
+SR_PRIV int lwla_receive_reply(const struct sr_usb_dev_inst *usb,
+			       void *reply, int buf_size, int *xfer_len);
+
+SR_PRIV int lwla_read_reg(const struct sr_usb_dev_inst *usb,
+			  uint16_t reg, uint32_t *value);
+
+SR_PRIV int lwla_write_reg(const struct sr_usb_dev_inst *usb,
+			   uint16_t reg, uint32_t value);
+
+SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
+			    const struct regval *regvals, int count);
+
+#endif
diff --git a/src/hardware/sysclk-lwla/lwla1016.c b/src/hardware/sysclk-lwla/lwla1016.c
new file mode 100644
index 0000000..b280b7a
--- /dev/null
+++ b/src/hardware/sysclk-lwla/lwla1016.c
@@ -0,0 +1,477 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "lwla.h"
+#include "protocol.h"
+
+/* Number of logic channels.
+ */
+#define NUM_CHANNELS	16
+
+/* Unit size for the sigrok logic datafeed.
+ */
+#define UNIT_SIZE	((NUM_CHANNELS + 7) / 8)
+
+/* Size of the acquisition buffer in device memory units.
+ */
+#define MEMORY_DEPTH	(256 * 1024)	/* 256k x 32 bit */
+
+/* Capture memory read start address.
+ */
+#define READ_START_ADDR	2
+
+/* Number of device memory units (32 bit) to read at a time.
+ */
+#define READ_CHUNK_LEN	250
+
+/** LWLA1016 register addresses.
+ */
+enum reg_addr {
+	REG_CHAN_MASK	= 0x1000, /* bit mask of enabled channels */
+
+	REG_DURATION	= 0x1010, /* capture duration in ms */
+
+	REG_MEM_WR_PTR	= 0x1070,
+	REG_MEM_RD_PTR	= 0x1074,
+	REG_MEM_DATA	= 0x1078,
+	REG_MEM_CTRL	= 0x107C,
+
+	REG_CAP_COUNT	= 0x10B0,
+
+	REG_TEST_ID	= 0x10B4, /* read */
+	REG_TRG_SEL	= 0x10B4, /* write */
+
+	REG_CAP_CTRL	= 0x10B8,
+
+	REG_CAP_TOTAL	= 0x10BC, /* read */
+	REG_DIV_COUNT	= 0x10BC, /* write */
+};
+
+/** Flag bits for REG_MEM_CTRL.
+ */
+enum mem_ctrl_flag {
+	MEM_CTRL_RESET	= 1 << 0,
+	MEM_CTRL_WRITE	= 1 << 1,
+};
+
+/** Flag bits for REG_CAP_CTRL.
+ */
+enum cap_ctrl_flag {
+	CAP_CTRL_FIFO32_FULL	= 1 << 0, /* "fifo32_ful" bit */
+	CAP_CTRL_FIFO64_FULL	= 1 << 1, /* "fifo64_ful" bit */
+	CAP_CTRL_TRG_EN		= 1 << 2, /* "trg_en" bit */
+	CAP_CTRL_CLR_TIMEBASE	= 1 << 3, /* "do_clr_timebase" bit */
+	CAP_CTRL_FIFO_EMPTY	= 1 << 4, /* "fifo_empty" bit */
+	CAP_CTRL_SAMPLE_EN	= 1 << 5, /* "sample_en" bit */
+	CAP_CTRL_CNTR_NOT_ENDR	= 1 << 6, /* "cntr_not_endr" bit */
+};
+
+/* Available FPGA configurations.
+ */
+enum fpga_config {
+	FPGA_100 = 0,	/* 100 MS/s, no compression */
+	FPGA_100_TS,	/* 100 MS/s, timing-state mode */
+};
+
+/* FPGA bitstream resource filenames.
+ */
+static const char bitstream_map[][32] = {
+	[FPGA_100]	= "sysclk-lwla1016-100.rbf",
+	[FPGA_100_TS]	= "sysclk-lwla1016-100-ts.rbf",
+};
+
+/* Demangle incoming sample data from the transfer buffer.
+ */
+static void read_response(struct acquisition_state *acq)
+{
+	uint32_t *in_p, *out_p;
+	unsigned int words_left, num_words;
+	unsigned int max_samples, run_samples;
+	unsigned int i;
+
+	words_left = MIN(acq->mem_addr_next, acq->mem_addr_stop)
+			- acq->mem_addr_done;
+	/* Calculate number of samples to write into packet. */
+	max_samples = MIN(acq->samples_max - acq->samples_done,
+			  PACKET_SIZE / UNIT_SIZE - acq->out_index);
+	run_samples = MIN(max_samples, 2 * words_left);
+
+	/* Round up in case the samples limit is an odd number. */
+	num_words = (run_samples + 1) / 2;
+	/*
+	 * Without RLE the output index will always be a multiple of two
+	 * samples (at least before reaching the samples limit), thus 32-bit
+	 * alignment is guaranteed.
+	 */
+	out_p = (uint32_t *)&acq->out_packet[acq->out_index * UNIT_SIZE];
+	in_p  = &acq->xfer_buf_in[acq->in_index];
+	/*
+	 * Transfer two samples at a time, taking care to swap the 16-bit
+	 * halves of each input word but keeping the samples themselves in
+	 * the original Little Endian order.
+	 */
+	for (i = 0; i < num_words; i++)
+		out_p[i] = LROTATE(in_p[i], 16);
+
+	acq->in_index += num_words;
+	acq->mem_addr_done += num_words;
+	acq->out_index += run_samples;
+	acq->samples_done += run_samples;
+}
+
+/* Demangle and decompress incoming sample data from the transfer buffer.
+ */
+static void read_response_rle(struct acquisition_state *acq)
+{
+	uint32_t *in_p;
+	uint16_t *out_p;
+	unsigned int words_left, max_samples, run_samples, wi, ri;
+	uint32_t word;
+	uint16_t sample;
+
+	words_left = MIN(acq->mem_addr_next, acq->mem_addr_stop)
+			- acq->mem_addr_done;
+	in_p = &acq->xfer_buf_in[acq->in_index];
+
+	for (wi = 0;; wi++) {
+		/* Calculate number of samples to write into packet. */
+		max_samples = MIN(acq->samples_max - acq->samples_done,
+				  PACKET_SIZE / UNIT_SIZE - acq->out_index);
+		run_samples = MIN(max_samples, acq->run_len);
+
+		/* Expand run-length samples into session packet. */
+		sample = GUINT16_TO_LE(acq->sample);
+		out_p = &((uint16_t *)acq->out_packet)[acq->out_index];
+
+		for (ri = 0; ri < run_samples; ri++)
+			out_p[ri] = sample;
+
+		acq->run_len -= run_samples;
+		acq->out_index += run_samples;
+		acq->samples_done += run_samples;
+
+		if (run_samples == max_samples)
+			break; /* Packet full or sample limit reached. */
+		if (wi >= words_left)
+			break; /* Done with current transfer. */
+
+		word = GUINT32_FROM_LE(in_p[wi]);
+		acq->sample = word >> 16;
+		acq->run_len = (word & 0xFFFF) + 1;
+	}
+
+	acq->in_index += wi;
+	acq->mem_addr_done += wi;
+}
+
+/* Check whether we can receive responses of more than 64 bytes.
+ * The FX2 firmware of the LWLA1016 has a bug in the reset logic which
+ * sometimes causes the response endpoint to be limited to transfers of
+ * 64 bytes at a time, instead of the expected 2*512 bytes. The problem
+ * can be worked around by never requesting more than 64 bytes.
+ * This quirk manifests itself only under certain conditions, and some
+ * users seem to see it more frequently than others. Detect it here in
+ * order to avoid paying the penalty unnecessarily.
+ */
+static int test_read_memory(const struct sr_dev_inst *sdi,
+			    unsigned int start, unsigned int count)
+{
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	unsigned int i;
+	int xfer_len, ret;
+	uint16_t command[5];
+	unsigned char reply[512];
+
+	devc = sdi->priv;
+	usb  = sdi->conn;
+
+	command[0] = LWLA_WORD(CMD_READ_MEM32);
+	command[1] = LWLA_WORD_0(start);
+	command[2] = LWLA_WORD_1(start);
+	command[3] = LWLA_WORD_0(count);
+	command[4] = LWLA_WORD_1(count);
+
+	ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+	if (ret != SR_OK)
+		return ret;
+
+	ret = lwla_receive_reply(usb, reply, sizeof(reply), &xfer_len);
+	if (ret != SR_OK)
+		return ret;
+
+	devc->short_transfer_quirk = (xfer_len == 64);
+
+	for (i = xfer_len; i < 4 * count && xfer_len == 64; i += xfer_len) {
+		ret = lwla_receive_reply(usb, reply, sizeof(reply), &xfer_len);
+		if (ret != SR_OK)
+			return ret;
+	}
+	if (i != 4 * count) {
+		sr_err("Invalid read response of unexpected length %d.",
+		       xfer_len);
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+/* Select and transfer FPGA bitstream for the current configuration.
+ */
+static int apply_fpga_config(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct drv_context *drvc;
+	int config, ret;
+
+	devc = sdi->priv;
+	drvc = sdi->driver->context;
+
+	if (sdi->status == SR_ST_INACTIVE)
+		return SR_OK; /* The LWLA1016 has no off state. */
+
+	config = (devc->cfg_rle) ? FPGA_100_TS : FPGA_100;
+
+	if (config == devc->active_fpga_config)
+		return SR_OK; /* No change. */
+
+	ret = lwla_send_bitstream(drvc->sr_ctx, sdi->conn,
+				  bitstream_map[config]);
+	devc->active_fpga_config = (ret == SR_OK) ? config : FPGA_NOCONF;
+
+	return ret;
+}
+
+/* Perform initialization self test.
+ */
+static int device_init_check(const struct sr_dev_inst *sdi)
+{
+	static const struct regval mem_reset[] = {
+		{REG_MEM_CTRL, MEM_CTRL_RESET},
+		{REG_MEM_CTRL, 0},
+	};
+	uint32_t value;
+	int ret;
+	const unsigned int test_count = 24;
+
+	lwla_read_reg(sdi->conn, REG_TEST_ID, &value);
+
+	/* Ignore the value returned by the first read. */
+	ret = lwla_read_reg(sdi->conn, REG_TEST_ID, &value);
+	if (ret != SR_OK)
+		return ret;
+
+	if (value != 0x12345678) {
+		sr_err("Received invalid test word 0x%08X.", value);
+		return SR_ERR;
+	}
+
+	ret = lwla_write_regs(sdi->conn, mem_reset, ARRAY_SIZE(mem_reset));
+	if (ret != SR_OK)
+		return ret;
+
+	ret = test_read_memory(sdi, 0, test_count);
+	if (ret != SR_OK)
+		return ret;
+
+	/*
+	 * Issue another read request or the device will stall, for whatever
+	 * reason. This happens both with and without the short transfer quirk.
+	 */
+	return test_read_memory(sdi, test_count, test_count);
+}
+
+static int setup_acquisition(const struct sr_dev_inst *sdi)
+{
+	static const struct regval capture_init[] = {
+		{REG_CAP_CTRL,  0},
+		{REG_DURATION,  0},
+		{REG_MEM_CTRL,  MEM_CTRL_RESET},
+		{REG_MEM_CTRL,  0},
+		{REG_MEM_CTRL,  MEM_CTRL_WRITE},
+		{REG_CAP_CTRL,  CAP_CTRL_FIFO32_FULL | CAP_CTRL_FIFO64_FULL},
+		{REG_CAP_CTRL,  CAP_CTRL_FIFO_EMPTY},
+		{REG_CAP_CTRL,  0},
+		{REG_CAP_COUNT, MEMORY_DEPTH - 5},
+	};
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	uint32_t divider_count, trigger_setup;
+	int ret;
+
+	devc = sdi->priv;
+	usb  = sdi->conn;
+
+	ret = lwla_write_reg(usb, REG_CHAN_MASK, devc->channel_mask);
+	if (ret != SR_OK)
+		return ret;
+
+	if (devc->samplerate > 0 && devc->samplerate < SR_MHZ(100))
+		divider_count = SR_MHZ(100) / devc->samplerate - 1;
+	else
+		divider_count = 0;
+
+	ret = lwla_write_reg(usb, REG_DIV_COUNT, divider_count);
+	if (ret != SR_OK)
+		return ret;
+
+	ret = lwla_write_regs(usb, capture_init, ARRAY_SIZE(capture_init));
+	if (ret != SR_OK)
+		return ret;
+
+	trigger_setup = ((devc->trigger_edge_mask & 0xFFFF) << 16)
+			| (devc->trigger_values & 0xFFFF);
+
+	return lwla_write_reg(usb, REG_TRG_SEL, trigger_setup);
+}
+
+static int prepare_request(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+	unsigned int chunk_len, count;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	acq->xfer_out->length = 0;
+	acq->reg_seq_pos = 0;
+	acq->reg_seq_len = 0;
+
+	switch (devc->state) {
+	case STATE_START_CAPTURE:
+		lwla_queue_regval(acq, REG_CAP_CTRL, CAP_CTRL_TRG_EN
+				| ((devc->trigger_mask & 0xFFFF) << 16));
+		break;
+	case STATE_STOP_CAPTURE:
+		lwla_queue_regval(acq, REG_CAP_CTRL, 0);
+		lwla_queue_regval(acq, REG_DIV_COUNT, 0);
+		break;
+	case STATE_READ_PREPARE:
+		lwla_queue_regval(acq, REG_MEM_CTRL, 0);
+		break;
+	case STATE_READ_FINISH:
+		lwla_queue_regval(acq, REG_MEM_CTRL, MEM_CTRL_RESET);
+		lwla_queue_regval(acq, REG_MEM_CTRL, 0);
+		break;
+	case STATE_STATUS_REQUEST:
+		lwla_queue_regval(acq, REG_CAP_CTRL, 0);
+		lwla_queue_regval(acq, REG_MEM_WR_PTR, 0);
+		lwla_queue_regval(acq, REG_DURATION, 0);
+		break;
+	case STATE_LENGTH_REQUEST:
+		lwla_queue_regval(acq, REG_CAP_COUNT, 0);
+		break;
+	case STATE_READ_REQUEST:
+		/* Limit reads to 16 device words (64 bytes) at a time if the
+		 * device firmware has the short transfer quirk. */
+		chunk_len = (devc->short_transfer_quirk) ? 16 : READ_CHUNK_LEN;
+		count = MIN(chunk_len, acq->mem_addr_stop - acq->mem_addr_next);
+
+		acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_MEM32);
+		acq->xfer_buf_out[1] = LWLA_WORD_0(acq->mem_addr_next);
+		acq->xfer_buf_out[2] = LWLA_WORD_1(acq->mem_addr_next);
+		acq->xfer_buf_out[3] = LWLA_WORD_0(count);
+		acq->xfer_buf_out[4] = LWLA_WORD_1(count);
+		acq->xfer_out->length = 5 * sizeof(acq->xfer_buf_out[0]);
+
+		acq->mem_addr_next += count;
+		break;
+	default:
+		sr_err("BUG: unhandled request state %d.", devc->state);
+		return SR_ERR_BUG;
+	}
+
+	return SR_OK;
+}
+
+static int handle_response(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+	int expect_len;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	switch (devc->state) {
+	case STATE_STATUS_REQUEST:
+		acq->status = acq->reg_sequence[0].val & 0x7F;
+		acq->mem_addr_fill = acq->reg_sequence[1].val;
+		acq->duration_now  = acq->reg_sequence[2].val;
+		break;
+	case STATE_LENGTH_REQUEST:
+		acq->mem_addr_next = READ_START_ADDR;
+		acq->mem_addr_stop = acq->reg_sequence[0].val + READ_START_ADDR - 1;
+		break;
+	case STATE_READ_REQUEST:
+		expect_len = (acq->mem_addr_next - acq->mem_addr_done
+				+ acq->in_index) * sizeof(acq->xfer_buf_in[0]);
+		if (acq->xfer_in->actual_length != expect_len) {
+			sr_err("Received size %d does not match expected size %d.",
+			       acq->xfer_in->actual_length, expect_len);
+			devc->transfer_error = TRUE;
+			return SR_ERR;
+		}
+		if (acq->rle_enabled)
+			read_response_rle(acq);
+		else
+			read_response(acq);
+		break;
+	default:
+		sr_err("BUG: unhandled response state %d.", devc->state);
+		return SR_ERR_BUG;
+	}
+
+	return SR_OK;
+}
+
+/* Model descriptor for the LWLA1016.
+ */
+SR_PRIV const struct model_info lwla1016_info = {
+	.name = "LWLA1016",
+	.num_channels = NUM_CHANNELS,
+
+	.num_devopts = 5,
+	.devopts = {
+		SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+		SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+		SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+		SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+		SR_CONF_RLE | SR_CONF_GET | SR_CONF_SET,
+	},
+	.num_samplerates = 19,
+	.samplerates = {
+		SR_MHZ(100),
+		SR_MHZ(50),  SR_MHZ(20),  SR_MHZ(10),
+		SR_MHZ(5),   SR_MHZ(2),   SR_MHZ(1),
+		SR_KHZ(500), SR_KHZ(200), SR_KHZ(100),
+		SR_KHZ(50),  SR_KHZ(20),  SR_KHZ(10),
+		SR_KHZ(5),   SR_KHZ(2),   SR_KHZ(1),
+		SR_HZ(500),  SR_HZ(200),  SR_HZ(100),
+	},
+
+	.apply_fpga_config = &apply_fpga_config,
+	.device_init_check = &device_init_check,
+	.setup_acquisition = &setup_acquisition,
+
+	.prepare_request = &prepare_request,
+	.handle_response = &handle_response,
+};
diff --git a/src/hardware/sysclk-lwla/lwla1034.c b/src/hardware/sysclk-lwla/lwla1034.c
new file mode 100644
index 0000000..14692c3
--- /dev/null
+++ b/src/hardware/sysclk-lwla/lwla1034.c
@@ -0,0 +1,602 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "lwla.h"
+#include "protocol.h"
+
+/* Number of logic channels.
+ */
+#define NUM_CHANNELS	34
+
+/* Bit mask covering all logic channels.
+ */
+#define ALL_CHANNELS_MASK	((UINT64_C(1) << NUM_CHANNELS) - 1)
+
+/* Unit size for the sigrok logic datafeed.
+ */
+#define UNIT_SIZE	((NUM_CHANNELS + 7) / 8)
+
+/* Size of the acquisition buffer in device memory units.
+ */
+#define MEMORY_DEPTH	(256 * 1024)	/* 256k x 36 bit */
+
+/* Capture memory read start address.
+ */
+#define READ_START_ADDR	4
+
+/* Number of device memory units (36 bit) to read at a time. Slices of 8
+ * consecutive 36-bit words are mapped to 9 32-bit words each, so the chunk
+ * length should be a multiple of 8 to ensure alignment to slice boundaries.
+ *
+ * Experimentation has shown that reading chunks larger than about 1024 bytes
+ * is unreliable. The threshold seems to relate to the buffer size on the FX2
+ * USB chip: The configured endpoint buffer size is 512, and with double or
+ * triple buffering enabled a multiple of 512 bytes can be kept in fly.
+ *
+ * The vendor software limits reads to 120 words (15 slices, 540 bytes) at
+ * a time. So far, it appears safe to increase this to 224 words (28 slices,
+ * 1008 bytes), thus making the most of two 512 byte buffers.
+ */
+#define READ_CHUNK_LEN	(28 * 8)
+
+/* Bit mask for the RLE repeat-count-follows flag.
+ */
+#define RLE_FLAG_LEN_FOLLOWS	(UINT64_C(1) << 35)
+
+/* Start index and count for bulk long register reads.
+ * The first five long registers do not return useful values when read,
+ * so skip over them to reduce the transfer size of status poll responses.
+ */
+#define READ_LREGS_START	LREG_MEM_FILL
+#define READ_LREGS_COUNT	(LREG_STATUS + 1 - READ_LREGS_START)
+
+/** LWLA1034 register addresses.
+ */
+enum reg_addr {
+	REG_MEM_CTRL	= 0x1074, /* capture buffer control */
+	REG_MEM_FILL	= 0x1078, /* capture buffer fill level */
+	REG_MEM_START	= 0x107C, /* capture buffer start address */
+
+	REG_CLK_BOOST	= 0x1094, /* logic clock boost flag */
+
+	REG_LONG_STROBE	= 0x10B0, /* long register read/write strobe */
+	REG_LONG_ADDR	= 0x10B4, /* long register address */
+	REG_LONG_LOW	= 0x10B8, /* long register low word */
+	REG_LONG_HIGH	= 0x10BC, /* long register high word */
+};
+
+/** Flag bits for REG_MEM_CTRL.
+ */
+enum mem_ctrl_flag {
+	MEM_CTRL_WRITE   = 1 << 0, /* "wr1rd0" bit */
+	MEM_CTRL_CLR_IDX = 1 << 1, /* "clr_idx" bit */
+};
+
+/* LWLA1034 long register addresses.
+ */
+enum long_reg_addr {
+	LREG_CHAN_MASK	= 0,	/* channel enable mask */
+	LREG_DIV_COUNT	= 1,	/* clock divider max count */
+	LREG_TRG_VALUE	= 2,	/* trigger level/slope bits */
+	LREG_TRG_TYPE	= 3,	/* trigger type bits (level or edge) */
+	LREG_TRG_ENABLE	= 4,	/* trigger enable mask */
+	LREG_MEM_FILL	= 5,	/* capture memory fill level or limit */
+
+	LREG_DURATION	= 7,	/* elapsed time in ms (0.8 ms at 125 MS/s) */
+	LREG_CHAN_STATE	= 8,	/* current logic levels at the inputs */
+	LREG_STATUS	= 9,	/* capture status flags */
+
+	LREG_CAP_CTRL	= 10,	/* capture control bits */
+	LREG_TEST_ID	= 100,	/* constant test ID */
+};
+
+/** Flag bits for LREG_CAP_CTRL.
+ */
+enum cap_ctrl_flag {
+	CAP_CTRL_TRG_EN       = 1 << 0, /* "trg_en" bit */
+	CAP_CTRL_CLR_TIMEBASE = 1 << 2, /* "do_clr_timebase" bit */
+	CAP_CTRL_FLUSH_FIFO   = 1 << 4, /* "flush_fifo" bit */
+	CAP_CTRL_CLR_FIFOFULL = 1 << 5, /* "clr_fifo32_ful" bit */
+	CAP_CTRL_CLR_COUNTER  = 1 << 6, /* "clr_cntr0" bit */
+};
+
+/* Available FPGA configurations.
+ */
+enum fpga_config {
+	FPGA_OFF = 0,	/* FPGA shutdown config */
+	FPGA_INT,	/* internal clock config */
+	FPGA_EXTPOS,	/* external clock, rising edge config */
+	FPGA_EXTNEG,	/* external clock, falling edge config */
+};
+
+/* FPGA bitstream resource filenames.
+ */
+static const char bitstream_map[][32] = {
+	[FPGA_OFF]	= "sysclk-lwla1034-off.rbf",
+	[FPGA_INT]	= "sysclk-lwla1034-int.rbf",
+	[FPGA_EXTPOS]	= "sysclk-lwla1034-extpos.rbf",
+	[FPGA_EXTNEG]	= "sysclk-lwla1034-extneg.rbf",
+};
+
+/* Read 64-bit long register.
+ */
+static int read_long_reg(const struct sr_usb_dev_inst *usb,
+			 uint32_t addr, uint64_t *value)
+{
+	uint32_t low, high, dummy;
+	int ret;
+
+	ret = lwla_write_reg(usb, REG_LONG_ADDR, addr);
+	if (ret != SR_OK)
+		return ret;
+
+	ret = lwla_read_reg(usb, REG_LONG_STROBE, &dummy);
+	if (ret != SR_OK)
+		return ret;
+
+	ret = lwla_read_reg(usb, REG_LONG_HIGH, &high);
+	if (ret != SR_OK)
+		return ret;
+
+	ret = lwla_read_reg(usb, REG_LONG_LOW, &low);
+	if (ret != SR_OK)
+		return ret;
+
+	*value = ((uint64_t)high << 32) | low;
+
+	return SR_OK;
+}
+
+/* Queue access sequence for a long register write.
+ */
+static void queue_long_regval(struct acquisition_state *acq,
+			      uint32_t addr, uint64_t value)
+{
+	lwla_queue_regval(acq, REG_LONG_ADDR, addr);
+	lwla_queue_regval(acq, REG_LONG_LOW, value & 0xFFFFFFFF);
+	lwla_queue_regval(acq, REG_LONG_HIGH, value >> 32);
+	lwla_queue_regval(acq, REG_LONG_STROBE, 0);
+}
+
+/* Helper to fill in the long register bulk write command.
+ */
+static inline void bulk_long_set(struct acquisition_state *acq,
+				 unsigned int idx, uint64_t value)
+{
+	acq->xfer_buf_out[4 * idx + 3] = LWLA_WORD_0(value);
+	acq->xfer_buf_out[4 * idx + 4] = LWLA_WORD_1(value);
+	acq->xfer_buf_out[4 * idx + 5] = LWLA_WORD_2(value);
+	acq->xfer_buf_out[4 * idx + 6] = LWLA_WORD_3(value);
+}
+
+/* Helper for dissecting the response to a long register bulk read.
+ */
+static inline uint64_t bulk_long_get(const struct acquisition_state *acq,
+				     unsigned int idx)
+{
+	uint64_t low, high;
+
+	low  = LWLA_TO_UINT32(acq->xfer_buf_in[2 * (idx - READ_LREGS_START)]);
+	high = LWLA_TO_UINT32(acq->xfer_buf_in[2 * (idx - READ_LREGS_START) + 1]);
+
+	return (high << 32) | low;
+}
+
+/* Demangle and decompress incoming sample data from the transfer buffer.
+ * The data chunk is taken from the acquisition state, and is expected to
+ * contain a multiple of 8 packed 36-bit words.
+ */
+static void read_response(struct acquisition_state *acq)
+{
+	uint64_t sample, high_nibbles, word;
+	uint32_t *slice;
+	uint8_t *out_p;
+	unsigned int words_left, max_samples, run_samples, wi, ri, si;
+
+	/* Number of 36-bit words remaining in the transfer buffer. */
+	words_left = MIN(acq->mem_addr_next, acq->mem_addr_stop)
+			- acq->mem_addr_done;
+
+	for (wi = 0;; wi++) {
+		/* Calculate number of samples to write into packet. */
+		max_samples = MIN(acq->samples_max - acq->samples_done,
+				  PACKET_SIZE / UNIT_SIZE - acq->out_index);
+		run_samples = MIN(max_samples, acq->run_len);
+
+		/* Expand run-length samples into session packet. */
+		sample = acq->sample;
+		out_p = &acq->out_packet[acq->out_index * UNIT_SIZE];
+
+		for (ri = 0; ri < run_samples; ri++) {
+			out_p[0] =  sample        & 0xFF;
+			out_p[1] = (sample >>  8) & 0xFF;
+			out_p[2] = (sample >> 16) & 0xFF;
+			out_p[3] = (sample >> 24) & 0xFF;
+			out_p[4] = (sample >> 32) & 0xFF;
+			out_p += UNIT_SIZE;
+		}
+		acq->run_len -= run_samples;
+		acq->out_index += run_samples;
+		acq->samples_done += run_samples;
+
+		if (run_samples == max_samples)
+			break; /* Packet full or sample limit reached. */
+		if (wi >= words_left)
+			break; /* Done with current transfer. */
+
+		/* Get the current slice of 8 packed 36-bit words. */
+		slice = &acq->xfer_buf_in[(acq->in_index + wi) / 8 * 9];
+		si = (acq->in_index + wi) % 8; /* Word index within slice. */
+
+		/* Extract the next 36-bit word. */
+		high_nibbles = LWLA_TO_UINT32(slice[8]);
+		word = LWLA_TO_UINT32(slice[si]);
+		word |= (high_nibbles << (4 * si + 4)) & (UINT64_C(0xF) << 32);
+
+		if (acq->rle == RLE_STATE_DATA) {
+			acq->sample = word & ALL_CHANNELS_MASK;
+			acq->run_len = ((word >> NUM_CHANNELS) & 1) + 1;
+			acq->rle = ((word & RLE_FLAG_LEN_FOLLOWS) != 0)
+					? RLE_STATE_LEN : RLE_STATE_DATA;
+		} else {
+			acq->run_len += word << 1;
+			acq->rle = RLE_STATE_DATA;
+		}
+	}
+
+	acq->in_index += wi;
+	acq->mem_addr_done += wi;
+}
+
+/* Check whether we can receive responses of more than 64 bytes.
+ * The FX2 firmware of the LWLA1034 has a bug in the reset logic which
+ * sometimes causes the response endpoint to be limited to transfers of
+ * 64 bytes at a time, instead of the expected 2*512 bytes. The problem
+ * can be worked around by never requesting more than 64 bytes.
+ * This quirk manifests itself only under certain conditions, and some
+ * users seem to see it more frequently than others. Detect it here in
+ * order to avoid paying the penalty unnecessarily.
+ */
+static int detect_short_transfer_quirk(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	int xfer_len, ret;
+	uint16_t command[3];
+	unsigned char buf[512];
+	const int lreg_count = 10;
+
+	devc = sdi->priv;
+	usb  = sdi->conn;
+
+	command[0] = LWLA_WORD(CMD_READ_LREGS);
+	command[1] = LWLA_WORD(0);
+	command[2] = LWLA_WORD(lreg_count);
+
+	ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+	if (ret != SR_OK)
+		return ret;
+
+	ret = lwla_receive_reply(usb, buf, sizeof(buf), &xfer_len);
+	if (ret != SR_OK)
+		return ret;
+
+	devc->short_transfer_quirk = (xfer_len == 64);
+
+	if (xfer_len == 8 * lreg_count)
+		return SR_OK;
+
+	if (xfer_len == 64) {
+		/* Drain the tailing portion of the split transfer. */
+		ret = lwla_receive_reply(usb, buf, sizeof(buf), &xfer_len);
+		if (ret != SR_OK)
+			return ret;
+
+		if (xfer_len == 8 * lreg_count - 64)
+			return SR_OK;
+	}
+	sr_err("Received response of unexpected length %d.", xfer_len);
+
+	return SR_ERR;
+}
+
+/* Select and transfer FPGA bitstream for the current configuration.
+ */
+static int apply_fpga_config(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct drv_context *drvc;
+	int config, ret;
+
+	devc = sdi->priv;
+	drvc = sdi->driver->context;
+
+	if (sdi->status == SR_ST_INACTIVE)
+		config = FPGA_OFF;
+	else if (devc->cfg_clock_source == CLOCK_INTERNAL)
+		config = FPGA_INT;
+	else if (devc->cfg_clock_edge == EDGE_POSITIVE)
+		config = FPGA_EXTPOS;
+	else
+		config = FPGA_EXTNEG;
+
+	if (config == devc->active_fpga_config)
+		return SR_OK; /* No change. */
+
+	ret = lwla_send_bitstream(drvc->sr_ctx, sdi->conn,
+				  bitstream_map[config]);
+	devc->active_fpga_config = (ret == SR_OK) ? config : FPGA_NOCONF;
+
+	return ret;
+}
+
+/* Perform initialization self test.
+ */
+static int device_init_check(const struct sr_dev_inst *sdi)
+{
+	uint64_t value;
+	int ret;
+
+	read_long_reg(sdi->conn, LREG_TEST_ID, &value);
+
+	/* Ignore the value returned by the first read. */
+	ret = read_long_reg(sdi->conn, LREG_TEST_ID, &value);
+	if (ret != SR_OK)
+		return ret;
+
+	if (value != UINT64_C(0x1234567887654321)) {
+		sr_err("Received invalid test word 0x%016" PRIX64 ".", value);
+		return SR_ERR;
+	}
+
+	return detect_short_transfer_quirk(sdi);
+}
+
+/* Set up the device in preparation for an acquisition session.
+ */
+static int setup_acquisition(const struct sr_dev_inst *sdi)
+{
+	static const struct regval capture_init[] = {
+		{REG_MEM_CTRL,    MEM_CTRL_CLR_IDX},
+		{REG_MEM_CTRL,    MEM_CTRL_WRITE},
+		{REG_LONG_ADDR,   LREG_CAP_CTRL},
+		{REG_LONG_LOW,    CAP_CTRL_CLR_TIMEBASE | CAP_CTRL_FLUSH_FIFO |
+				  CAP_CTRL_CLR_FIFOFULL | CAP_CTRL_CLR_COUNTER},
+		{REG_LONG_HIGH,   0},
+		{REG_LONG_STROBE, 0},
+	};
+	uint64_t divider_count, trigger_mask;
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	struct acquisition_state *acq;
+	int ret;
+
+	devc = sdi->priv;
+	usb  = sdi->conn;
+	acq  = devc->acquisition;
+
+	ret = lwla_write_regs(usb, capture_init, ARRAY_SIZE(capture_init));
+	if (ret != SR_OK)
+		return ret;
+
+	ret = lwla_write_reg(usb, REG_CLK_BOOST, acq->clock_boost);
+	if (ret != SR_OK)
+		return ret;
+
+	acq->xfer_buf_out[0] = LWLA_WORD(CMD_WRITE_LREGS);
+	acq->xfer_buf_out[1] = LWLA_WORD(0);
+	acq->xfer_buf_out[2] = LWLA_WORD(LREG_STATUS + 1);
+
+	bulk_long_set(acq, LREG_CHAN_MASK, devc->channel_mask);
+
+	if (devc->samplerate > 0 && devc->samplerate <= SR_MHZ(100)
+			&& !acq->clock_boost)
+		divider_count = SR_MHZ(100) / devc->samplerate - 1;
+	else
+		divider_count = 0;
+
+	bulk_long_set(acq, LREG_DIV_COUNT, divider_count);
+	bulk_long_set(acq, LREG_TRG_VALUE, devc->trigger_values);
+	bulk_long_set(acq, LREG_TRG_TYPE,  devc->trigger_edge_mask);
+
+	trigger_mask = devc->trigger_mask;
+
+	/* Set bits to select external TRG input edge. */
+	if (devc->cfg_trigger_source == TRIGGER_EXT_TRG)
+		switch (devc->cfg_trigger_slope) {
+		case EDGE_POSITIVE:
+			trigger_mask |= UINT64_C(1) << 35;
+			break;
+		case EDGE_NEGATIVE:
+			trigger_mask |= UINT64_C(1) << 34;
+			break;
+		}
+
+	bulk_long_set(acq, LREG_TRG_ENABLE, trigger_mask);
+
+	/* Set the capture memory full threshold. This is slightly less
+	 * than the actual maximum, most likely in order to compensate for
+	 * pipeline latency.
+	 */
+	bulk_long_set(acq, LREG_MEM_FILL, MEMORY_DEPTH - 16);
+
+	/* Fill remaining words with zeroes. */
+	bulk_long_set(acq, 6, 0);
+	bulk_long_set(acq, LREG_DURATION, 0);
+	bulk_long_set(acq, LREG_CHAN_STATE, 0);
+	bulk_long_set(acq, LREG_STATUS, 0);
+
+	return lwla_send_command(sdi->conn, acq->xfer_buf_out,
+				 3 + (LREG_STATUS + 1) * 4);
+}
+
+static int prepare_request(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+	unsigned int chunk_len, remaining, count;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	acq->xfer_out->length = 0;
+	acq->reg_seq_pos = 0;
+	acq->reg_seq_len = 0;
+
+	switch (devc->state) {
+	case STATE_START_CAPTURE:
+		queue_long_regval(acq, LREG_CAP_CTRL, CAP_CTRL_TRG_EN);
+		break;
+	case STATE_STOP_CAPTURE:
+		queue_long_regval(acq, LREG_CAP_CTRL, 0);
+		lwla_queue_regval(acq, REG_CLK_BOOST, 0);
+		break;
+	case STATE_READ_PREPARE:
+		lwla_queue_regval(acq, REG_CLK_BOOST, 1);
+		lwla_queue_regval(acq, REG_MEM_CTRL, MEM_CTRL_CLR_IDX);
+		lwla_queue_regval(acq, REG_MEM_START, READ_START_ADDR);
+		break;
+	case STATE_READ_FINISH:
+		lwla_queue_regval(acq, REG_CLK_BOOST, 0);
+		break;
+	case STATE_STATUS_REQUEST:
+		acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_LREGS);
+		acq->xfer_buf_out[1] = LWLA_WORD(READ_LREGS_START);
+		acq->xfer_buf_out[2] = LWLA_WORD(READ_LREGS_COUNT);
+		acq->xfer_out->length = 3 * sizeof(acq->xfer_buf_out[0]);
+		break;
+	case STATE_LENGTH_REQUEST:
+		lwla_queue_regval(acq, REG_MEM_FILL, 0);
+		break;
+	case STATE_READ_REQUEST:
+		/* Limit reads to 8 device words (36 bytes) at a time if the
+		 * device firmware has the short transfer quirk. */
+		chunk_len = (devc->short_transfer_quirk) ? 8 : READ_CHUNK_LEN;
+		/* Always read a multiple of 8 device words. */
+		remaining = (acq->mem_addr_stop - acq->mem_addr_next + 7) / 8 * 8;
+		count = MIN(chunk_len, remaining);
+
+		acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_MEM36);
+		acq->xfer_buf_out[1] = LWLA_WORD_0(acq->mem_addr_next);
+		acq->xfer_buf_out[2] = LWLA_WORD_1(acq->mem_addr_next);
+		acq->xfer_buf_out[3] = LWLA_WORD_0(count);
+		acq->xfer_buf_out[4] = LWLA_WORD_1(count);
+		acq->xfer_out->length = 5 * sizeof(acq->xfer_buf_out[0]);
+
+		acq->mem_addr_next += count;
+		break;
+	default:
+		sr_err("BUG: unhandled request state %d.", devc->state);
+		return SR_ERR_BUG;
+	}
+
+	return SR_OK;
+}
+
+static int handle_response(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+	int expect_len;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	switch (devc->state) {
+	case STATE_STATUS_REQUEST:
+		if (acq->xfer_in->actual_length != READ_LREGS_COUNT * 8) {
+			sr_err("Received size %d doesn't match expected size %d.",
+			       acq->xfer_in->actual_length, READ_LREGS_COUNT * 8);
+			return SR_ERR;
+		}
+		acq->mem_addr_fill = bulk_long_get(acq, LREG_MEM_FILL) & 0xFFFFFFFF;
+		acq->duration_now  = bulk_long_get(acq, LREG_DURATION);
+		/* Shift left by one so the bit positions match the LWLA1016. */
+		acq->status = (bulk_long_get(acq, LREG_STATUS) & 0x3F) << 1;
+		/*
+		 * It seems that the 125 MS/s mode is implemented simply by
+		 * running the FPGA logic at a 25% higher clock rate. As a
+		 * result, the millisecond counter for the capture duration
+		 * is also off by 25%, and thus needs to be corrected here.
+		 */
+		if (acq->clock_boost)
+			acq->duration_now = acq->duration_now * 4 / 5;
+		break;
+	case STATE_LENGTH_REQUEST:
+		acq->mem_addr_next = READ_START_ADDR;
+		acq->mem_addr_stop = acq->reg_sequence[0].val;
+		break;
+	case STATE_READ_REQUEST:
+		/* Expect a multiple of 8 36-bit words packed into 9 32-bit
+		 * words. */
+		expect_len = (acq->mem_addr_next - acq->mem_addr_done
+			+ acq->in_index + 7) / 8 * 9 * sizeof(acq->xfer_buf_in[0]);
+
+		if (acq->xfer_in->actual_length != expect_len) {
+			sr_err("Received size %d does not match expected size %d.",
+			       acq->xfer_in->actual_length, expect_len);
+			devc->transfer_error = TRUE;
+			return SR_ERR;
+		}
+		read_response(acq);
+		break;
+	default:
+		sr_err("BUG: unhandled response state %d.", devc->state);
+		return SR_ERR_BUG;
+	}
+
+	return SR_OK;
+}
+
+/** Model descriptor for the LWLA1034.
+ */
+SR_PRIV const struct model_info lwla1034_info = {
+	.name = "LWLA1034",
+	.num_channels = NUM_CHANNELS,
+
+	.num_devopts = 8,
+	.devopts = {
+		SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+		SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+		SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+		SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+		SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
+		SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+		SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+		SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	},
+	.num_samplerates = 20,
+	.samplerates = {
+		SR_MHZ(125), SR_MHZ(100),
+		SR_MHZ(50),  SR_MHZ(20),  SR_MHZ(10),
+		SR_MHZ(5),   SR_MHZ(2),   SR_MHZ(1),
+		SR_KHZ(500), SR_KHZ(200), SR_KHZ(100),
+		SR_KHZ(50),  SR_KHZ(20),  SR_KHZ(10),
+		SR_KHZ(5),   SR_KHZ(2),   SR_KHZ(1),
+		SR_HZ(500),  SR_HZ(200),  SR_HZ(100),
+	},
+
+	.apply_fpga_config = &apply_fpga_config,
+	.device_init_check = &device_init_check,
+	.setup_acquisition = &setup_acquisition,
+
+	.prepare_request = &prepare_request,
+	.handle_response = &handle_response,
+};
diff --git a/src/hardware/sysclk-lwla/protocol.c b/src/hardware/sysclk-lwla/protocol.c
new file mode 100644
index 0000000..20cf10f
--- /dev/null
+++ b/src/hardware/sysclk-lwla/protocol.c
@@ -0,0 +1,590 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include "protocol.h"
+#include "lwla.h"
+
+/* Submit an already filled-in USB transfer.
+ */
+static int submit_transfer(struct dev_context *devc,
+			   struct libusb_transfer *xfer)
+{
+	int ret;
+
+	ret = libusb_submit_transfer(xfer);
+
+	if (ret != 0) {
+		sr_err("Submit transfer failed: %s.", libusb_error_name(ret));
+		devc->transfer_error = TRUE;
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+/* Set up transfer for the next register in a write sequence.
+ */
+static void next_reg_write(struct acquisition_state *acq)
+{
+	struct regval *regval;
+
+	regval = &acq->reg_sequence[acq->reg_seq_pos];
+
+	acq->xfer_buf_out[0] = LWLA_WORD(CMD_WRITE_REG);
+	acq->xfer_buf_out[1] = LWLA_WORD(regval->reg);
+	acq->xfer_buf_out[2] = LWLA_WORD_0(regval->val);
+	acq->xfer_buf_out[3] = LWLA_WORD_1(regval->val);
+
+	acq->xfer_out->length = 4 * sizeof(acq->xfer_buf_out[0]);
+}
+
+/* Set up transfer for the next register in a read sequence.
+ */
+static void next_reg_read(struct acquisition_state *acq)
+{
+	unsigned int addr;
+
+	addr = acq->reg_sequence[acq->reg_seq_pos].reg;
+
+	acq->xfer_buf_out[0] = LWLA_WORD(CMD_READ_REG);
+	acq->xfer_buf_out[1] = LWLA_WORD(addr);
+
+	acq->xfer_out->length = 2 * sizeof(acq->xfer_buf_out[0]);
+}
+
+/* Decode the response to a register read request.
+ */
+static int read_reg_response(struct acquisition_state *acq)
+{
+	uint32_t value;
+
+	if (acq->xfer_in->actual_length != 4) {
+		sr_err("Received size %d doesn't match expected size 4.",
+		       acq->xfer_in->actual_length);
+		return SR_ERR;
+	}
+	value = LWLA_TO_UINT32(acq->xfer_buf_in[0]);
+	acq->reg_sequence[acq->reg_seq_pos].val = value;
+
+	return SR_OK;
+}
+
+/* Enter a new state and submit the corresponding request to the device.
+ */
+static int submit_request(const struct sr_dev_inst *sdi,
+			  enum protocol_state state)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+	int ret;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	devc->state = state;
+
+	acq->xfer_out->length = 0;
+	acq->reg_seq_pos = 0;
+	acq->reg_seq_len = 0;
+
+	/* Perform the model-specific action for the new state. */
+	ret = (*devc->model->prepare_request)(sdi);
+
+	if (ret != SR_OK) {
+		devc->transfer_error = TRUE;
+		return ret;
+	}
+
+	if (acq->reg_seq_pos < acq->reg_seq_len) {
+		if ((state & STATE_EXPECT_RESPONSE) != 0)
+			next_reg_read(acq);
+		else
+			next_reg_write(acq);
+	}
+
+	return submit_transfer(devc, acq->xfer_out);
+}
+
+/* Evaluate and act on the response to a capture status request.
+ */
+static void handle_status_response(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+	unsigned int old_status;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+	old_status = acq->status;
+
+	if ((*devc->model->handle_response)(sdi) != SR_OK) {
+		devc->transfer_error = TRUE;
+		return;
+	}
+	devc->state = STATE_STATUS_WAIT;
+
+	sr_spew("Captured %u words, %" PRIu64 " ms, status 0x%02X.",
+		acq->mem_addr_fill, acq->duration_now, acq->status);
+
+	if ((~old_status & acq->status & STATUS_TRIGGERED) != 0)
+		sr_info("Capture triggered.");
+
+	if (acq->duration_now >= acq->duration_max) {
+		sr_dbg("Time limit reached, stopping capture.");
+		submit_request(sdi, STATE_STOP_CAPTURE);
+	} else if ((acq->status & STATUS_TRIGGERED) == 0) {
+		sr_spew("Waiting for trigger.");
+	} else if ((acq->status & STATUS_MEM_AVAIL) == 0) {
+		sr_dbg("Capture memory filled.");
+		submit_request(sdi, STATE_LENGTH_REQUEST);
+	} else if ((acq->status & STATUS_CAPTURING) != 0) {
+		sr_spew("Sampling in progress.");
+	}
+}
+
+/* Evaluate and act on the response to a capture length request.
+ */
+static void handle_length_response(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	if ((*devc->model->handle_response)(sdi) != SR_OK) {
+		devc->transfer_error = TRUE;
+		return;
+	}
+	acq->rle = RLE_STATE_DATA;
+	acq->sample = 0;
+	acq->run_len = 0;
+	acq->samples_done = 0;
+	acq->mem_addr_done = acq->mem_addr_next;
+	acq->out_index = 0;
+
+	if (acq->mem_addr_next >= acq->mem_addr_stop) {
+		submit_request(sdi, STATE_READ_FINISH);
+		return;
+	}
+	sr_dbg("%u words in capture buffer.",
+	       acq->mem_addr_stop - acq->mem_addr_next);
+
+	submit_request(sdi, STATE_READ_PREPARE);
+}
+
+/* Evaluate and act on the response to a capture memory read request.
+ */
+static void handle_read_response(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_logic logic;
+	unsigned int end_addr;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	/* Prepare session packet. */
+	packet.type    = SR_DF_LOGIC;
+	packet.payload = &logic;
+	logic.unitsize = (devc->model->num_channels + 7) / 8;
+	logic.data     = acq->out_packet;
+
+	end_addr = MIN(acq->mem_addr_next, acq->mem_addr_stop);
+	acq->in_index = 0;
+
+	/*
+	 * Repeatedly call the model-specific read response handler until
+	 * all data received in the transfer has been accounted for.
+	 */
+	while (!devc->cancel_requested
+			&& (acq->run_len > 0 || acq->mem_addr_done < end_addr)
+			&& acq->samples_done < acq->samples_max) {
+
+		if ((*devc->model->handle_response)(sdi) != SR_OK) {
+			devc->transfer_error = TRUE;
+			return;
+		}
+		if (acq->out_index * logic.unitsize >= PACKET_SIZE) {
+			/* Send off full logic packet. */
+			logic.length = acq->out_index * logic.unitsize;
+			sr_session_send(sdi, &packet);
+			acq->out_index = 0;
+		}
+	}
+
+	if (!devc->cancel_requested
+			&& acq->samples_done < acq->samples_max
+			&& acq->mem_addr_next < acq->mem_addr_stop) {
+		/* Request the next block. */
+		submit_request(sdi, STATE_READ_REQUEST);
+		return;
+	}
+
+	/* Send partially filled packet as it is the last one. */
+	if (!devc->cancel_requested && acq->out_index > 0) {
+ 		logic.length = acq->out_index * logic.unitsize;
+		sr_session_send(sdi, &packet);
+		acq->out_index = 0;
+	}
+	submit_request(sdi, STATE_READ_FINISH);
+}
+
+/* Destroy and unset the acquisition state record.
+ */
+static void clear_acquisition_state(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	devc->acquisition = NULL;
+
+	if (acq) {
+		libusb_free_transfer(acq->xfer_out);
+		libusb_free_transfer(acq->xfer_in);
+		g_free(acq);
+	}
+}
+
+/* USB I/O source callback.
+ */
+static int transfer_event(int fd, int revents, void *cb_data)
+{
+	const struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct drv_context *drvc;
+	struct timeval tv;
+	struct sr_datafeed_packet packet;
+	int ret;
+
+	(void)fd;
+
+	sdi  = cb_data;
+	devc = sdi->priv;
+	drvc = sdi->driver->context;
+
+	if (!devc || !drvc)
+		return G_SOURCE_REMOVE;
+
+	/* Handle pending USB events without blocking. */
+	tv.tv_sec  = 0;
+	tv.tv_usec = 0;
+	ret = libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx,
+						     &tv, NULL);
+	if (ret != 0) {
+		sr_err("Event handling failed: %s.", libusb_error_name(ret));
+		devc->transfer_error = TRUE;
+	}
+
+	if (!devc->transfer_error && devc->state == STATE_STATUS_WAIT) {
+		if (devc->cancel_requested)
+			submit_request(sdi, STATE_STOP_CAPTURE);
+		else if (revents == 0) /* status poll timeout */
+			submit_request(sdi, STATE_STATUS_REQUEST);
+	}
+
+	/* Stop processing events if an error occurred on a transfer. */
+	if (devc->transfer_error)
+		devc->state = STATE_IDLE;
+
+	if (devc->state != STATE_IDLE)
+		return G_SOURCE_CONTINUE;
+
+	sr_info("Acquisition stopped.");
+
+	/* We are done, clean up and send end packet to session bus. */
+	clear_acquisition_state(sdi);
+
+	packet.type = SR_DF_END;
+	packet.payload = NULL;
+	sr_session_send(sdi, &packet);
+
+	return G_SOURCE_REMOVE;
+}
+
+/* USB output transfer completion callback.
+ */
+static void LIBUSB_CALL transfer_out_completed(struct libusb_transfer *transfer)
+{
+	const struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+
+	sdi  = transfer->user_data;
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+		sr_err("Transfer to device failed (state %d): %s.",
+		       devc->state, libusb_error_name(transfer->status));
+		devc->transfer_error = TRUE;
+		return;
+	}
+
+	/* If this was a read request, wait for the response. */
+	if ((devc->state & STATE_EXPECT_RESPONSE) != 0) {
+		submit_transfer(devc, acq->xfer_in);
+		return;
+	}
+	if (acq->reg_seq_pos < acq->reg_seq_len)
+		acq->reg_seq_pos++; /* register write completed */
+
+	/* Repeat until all queued registers have been written. */
+	if (acq->reg_seq_pos < acq->reg_seq_len && !devc->cancel_requested) {
+		next_reg_write(acq);
+		submit_transfer(devc, acq->xfer_out);
+		return;
+	}
+
+	switch (devc->state) {
+	case STATE_START_CAPTURE:
+		sr_info("Acquisition started.");
+
+		if (!devc->cancel_requested)
+			devc->state = STATE_STATUS_WAIT;
+		else
+			submit_request(sdi, STATE_STOP_CAPTURE);
+		break;
+	case STATE_STOP_CAPTURE:
+		if (!devc->cancel_requested)
+			submit_request(sdi, STATE_LENGTH_REQUEST);
+		else
+			devc->state = STATE_IDLE;
+		break;
+	case STATE_READ_PREPARE:
+		if (acq->mem_addr_next < acq->mem_addr_stop && !devc->cancel_requested)
+			submit_request(sdi, STATE_READ_REQUEST);
+		else
+			submit_request(sdi, STATE_READ_FINISH);
+		break;
+	case STATE_READ_FINISH:
+		devc->state = STATE_IDLE;
+		break;
+	default:
+		sr_err("Unexpected device state %d.", devc->state);
+		devc->transfer_error = TRUE;
+		break;
+	}
+}
+
+/* USB input transfer completion callback.
+ */
+static void LIBUSB_CALL transfer_in_completed(struct libusb_transfer *transfer)
+{
+	const struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct acquisition_state *acq;
+
+	sdi  = transfer->user_data;
+	devc = sdi->priv;
+	acq  = devc->acquisition;
+
+	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+		sr_err("Transfer from device failed (state %d): %s.",
+		       devc->state, libusb_error_name(transfer->status));
+		devc->transfer_error = TRUE;
+		return;
+	}
+	if ((devc->state & STATE_EXPECT_RESPONSE) == 0) {
+		sr_err("Unexpected completion of input transfer (state %d).",
+		       devc->state);
+		devc->transfer_error = TRUE;
+		return;
+	}
+
+	if (acq->reg_seq_pos < acq->reg_seq_len && !devc->cancel_requested) {
+		/* Complete register read sequence. */
+		if (read_reg_response(acq) != SR_OK) {
+			devc->transfer_error = TRUE;
+			return;
+		}
+		/* Repeat until all queued registers have been read. */
+		if (++acq->reg_seq_pos < acq->reg_seq_len) {
+			next_reg_read(acq);
+			submit_transfer(devc, acq->xfer_out);
+			return;
+		}
+	}
+
+	switch (devc->state) {
+	case STATE_STATUS_REQUEST:
+		if (devc->cancel_requested)
+			submit_request(sdi, STATE_STOP_CAPTURE);
+		else
+			handle_status_response(sdi);
+		break;
+	case STATE_LENGTH_REQUEST:
+		if (devc->cancel_requested)
+			submit_request(sdi, STATE_READ_FINISH);
+		else
+			handle_length_response(sdi);
+		break;
+	case STATE_READ_REQUEST:
+		handle_read_response(sdi);
+		break;
+	default:
+		sr_err("Unexpected device state %d.", devc->state);
+		devc->transfer_error = TRUE;
+		break;
+	}
+}
+
+/* Set up the acquisition state record.
+ */
+static int init_acquisition_state(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	struct acquisition_state *acq;
+
+	devc = sdi->priv;
+	usb  = sdi->conn;
+
+	if (devc->acquisition) {
+		sr_err("Acquisition still in progress?");
+		return SR_ERR;
+	}
+	if (devc->cfg_clock_source == CLOCK_INTERNAL && devc->samplerate == 0) {
+		sr_err("Samplerate not set.");
+		return SR_ERR;
+	}
+
+	acq = g_try_malloc0(sizeof(struct acquisition_state));
+	if (!acq)
+		return SR_ERR_MALLOC;
+
+	acq->xfer_in = libusb_alloc_transfer(0);
+	if (!acq->xfer_in) {
+		g_free(acq);
+		return SR_ERR_MALLOC;
+	}
+	acq->xfer_out = libusb_alloc_transfer(0);
+	if (!acq->xfer_out) {
+		libusb_free_transfer(acq->xfer_in);
+		g_free(acq);
+		return SR_ERR_MALLOC;
+	}
+
+	libusb_fill_bulk_transfer(acq->xfer_out, usb->devhdl, EP_COMMAND,
+				  (unsigned char *)acq->xfer_buf_out, 0,
+				  &transfer_out_completed,
+				  (struct sr_dev_inst *)sdi, USB_TIMEOUT_MS);
+
+	libusb_fill_bulk_transfer(acq->xfer_in, usb->devhdl, EP_REPLY,
+				  (unsigned char *)acq->xfer_buf_in,
+				  sizeof(acq->xfer_buf_in),
+				  &transfer_in_completed,
+				  (struct sr_dev_inst *)sdi, USB_TIMEOUT_MS);
+
+	if (devc->limit_msec > 0) {
+		acq->duration_max = devc->limit_msec;
+		sr_info("Acquisition time limit %" PRIu64 " ms.",
+			devc->limit_msec);
+	} else
+		acq->duration_max = MAX_LIMIT_MSEC;
+
+	if (devc->limit_samples > 0) {
+		acq->samples_max = devc->limit_samples;
+		sr_info("Acquisition sample count limit %" PRIu64 ".",
+			devc->limit_samples);
+	} else
+		acq->samples_max = MAX_LIMIT_SAMPLES;
+
+	if (devc->cfg_clock_source == CLOCK_INTERNAL) {
+		sr_info("Internal clock, samplerate %" PRIu64 ".",
+			devc->samplerate);
+		/* Ramp up clock speed to enable samplerates above 100 MS/s. */
+		acq->clock_boost = (devc->samplerate > SR_MHZ(100));
+
+		/* If only one of the limits is set, derive the other one. */
+		if (devc->limit_msec == 0 && devc->limit_samples > 0)
+			acq->duration_max = devc->limit_samples
+					* 1000 / devc->samplerate + 1;
+		else if (devc->limit_samples == 0 && devc->limit_msec > 0)
+			acq->samples_max = devc->limit_msec
+					* devc->samplerate / 1000;
+	} else {
+		acq->clock_boost = TRUE;
+
+		if (devc->cfg_clock_edge == EDGE_POSITIVE)
+			sr_info("External clock, rising edge.");
+		else
+			sr_info("External clock, falling edge.");
+	}
+
+	acq->rle_enabled = devc->cfg_rle;
+	devc->acquisition = acq;
+
+	return SR_OK;
+}
+
+SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi)
+{
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	int ret;
+	const int poll_interval_ms = 100;
+
+	drvc = sdi->driver->context;
+	devc = sdi->priv;
+
+	if (devc->state != STATE_IDLE) {
+		sr_err("Not in idle state, cannot start acquisition.");
+		return SR_ERR;
+	}
+	devc->cancel_requested = FALSE;
+	devc->transfer_error = FALSE;
+
+	ret = init_acquisition_state(sdi);
+	if (ret != SR_OK)
+		return ret;
+
+	ret = (*devc->model->setup_acquisition)(sdi);
+	if (ret != SR_OK) {
+		sr_err("Failed to set up device for acquisition.");
+		clear_acquisition_state(sdi);
+		return ret;
+	}
+	/* Register event source for asynchronous USB I/O. */
+	ret = usb_source_add(sdi->session, drvc->sr_ctx, poll_interval_ms,
+			     &transfer_event, (struct sr_dev_inst *)sdi);
+	if (ret != SR_OK) {
+		clear_acquisition_state(sdi);
+		return ret;
+	}
+	ret = submit_request(sdi, STATE_START_CAPTURE);
+
+	if (ret == SR_OK) {
+		/* Send header packet to the session bus. */
+		ret = std_session_send_df_header(sdi, LOG_PREFIX);
+	}
+	if (ret != SR_OK) {
+		usb_source_remove(sdi->session, drvc->sr_ctx);
+		clear_acquisition_state(sdi);
+	}
+
+	return ret;
+}
diff --git a/src/hardware/sysclk-lwla/protocol.h b/src/hardware/sysclk-lwla/protocol.h
new file mode 100644
index 0000000..ebfc704
--- /dev/null
+++ b/src/hardware/sysclk-lwla/protocol.h
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Daniel Elstner <daniel.kitta at 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_SYSCLK_LWLA_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SYSCLK_LWLA_PROTOCOL_H
+
+#define LOG_PREFIX "sysclk-lwla"
+
+#include <stdint.h>
+#include <libusb.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include <libsigrok-internal.h>
+
+#define VENDOR_NAME	"SysClk"
+
+/* Maximum configurable sample count limit.
+ * Due to compression, there is no meaningful hardware limit the driver
+ * could report. So this value is less than 2^64-1 for no reason other
+ * than to safeguard against integer overflows.
+ */
+#define MAX_LIMIT_SAMPLES	(UINT64_C(1000) * 1000 * 1000 * 1000)
+
+/* Maximum configurable acquisition time limit.
+ * Due to compression, there is no hardware limit that would be meaningful
+ * in practice. However, the LWLA1016 reports the elapsed time as a 32-bit
+ * value, so keep this below 2^32.
+ */
+#define MAX_LIMIT_MSEC		(1000 * 1000 * 1000)
+
+struct acquisition_state;
+
+/* USB vendor and product IDs.
+ */
+enum {
+	USB_VID_SYSCLK   = 0x2961,
+	USB_PID_LWLA1016 = 0x6688,
+	USB_PID_LWLA1034 = 0x6689,
+};
+
+/* USB device characteristics.
+ */
+enum {
+	USB_CONFIG	= 1,
+	USB_INTERFACE	= 0,
+	USB_TIMEOUT_MS	= 1000,
+};
+
+/** USB device end points.
+ */
+enum usb_endpoint {
+	EP_COMMAND = 2,
+	EP_CONFIG  = 4,
+	EP_REPLY   = 6 | LIBUSB_ENDPOINT_IN
+};
+
+/** LWLA1034 clock sources.
+ */
+enum clock_source {
+	CLOCK_INTERNAL = 0,
+	CLOCK_EXT_CLK,
+};
+
+/** LWLA1034 trigger sources.
+ */
+enum trigger_source {
+	TRIGGER_CHANNELS = 0,
+	TRIGGER_EXT_TRG,
+};
+
+/** Edge choices for the LWLA1034 external clock and trigger inputs.
+ */
+enum signal_edge {
+	EDGE_POSITIVE = 0,
+	EDGE_NEGATIVE,
+};
+
+/* Common indicator for no or unknown FPGA config. */
+enum {
+	FPGA_NOCONF = -1,
+};
+
+/** Acquisition protocol states.
+ */
+enum protocol_state {
+	/* idle states */
+	STATE_IDLE = 0,
+	STATE_STATUS_WAIT,
+	/* device command states */
+	STATE_START_CAPTURE,
+	STATE_STOP_CAPTURE,
+	STATE_READ_PREPARE,
+	STATE_READ_FINISH,
+	/* command followed by response */
+	STATE_EXPECT_RESPONSE = 1 << 3,
+	STATE_STATUS_REQUEST = STATE_EXPECT_RESPONSE,
+	STATE_LENGTH_REQUEST,
+	STATE_READ_REQUEST,
+};
+
+/** Private, per-device-instance driver context.
+ */
+struct dev_context {
+	uint64_t samplerate;	/* requested samplerate */
+	uint64_t limit_msec;	/* requested capture duration in ms */
+	uint64_t limit_samples;	/* requested capture length in samples */
+
+	uint64_t channel_mask;		/* bit mask of enabled channels */
+	uint64_t trigger_mask;		/* trigger enable mask */
+	uint64_t trigger_edge_mask;	/* trigger type mask */
+	uint64_t trigger_values;	/* trigger level/slope bits */
+
+	const struct model_info *model;		/* device model descriptor */
+	struct acquisition_state *acquisition;	/* running capture state */
+	int active_fpga_config;			/* FPGA configuration index */
+	gboolean short_transfer_quirk;		/* 64 bytes response limit */
+
+	enum protocol_state state;	/* async protocol state */
+	gboolean cancel_requested;	/* stop after current transfer */
+	gboolean transfer_error;	/* error during device communication */
+
+	gboolean cfg_rle;			/* RLE compression setting */
+	enum clock_source cfg_clock_source;	/* clock source setting */
+	enum signal_edge cfg_clock_edge;	/* ext clock edge setting */
+	enum trigger_source cfg_trigger_source;	/* trigger source setting */
+	enum signal_edge cfg_trigger_slope;	/* ext trigger slope setting */
+};
+
+/** LWLA model descriptor.
+ */
+struct model_info {
+	char name[12];
+	int num_channels;
+
+	unsigned int num_devopts;
+	uint32_t devopts[8];
+
+	unsigned int num_samplerates;
+	uint64_t samplerates[20];
+
+	int (*apply_fpga_config)(const struct sr_dev_inst *sdi);
+	int (*device_init_check)(const struct sr_dev_inst *sdi);
+	int (*setup_acquisition)(const struct sr_dev_inst *sdi);
+
+	int (*prepare_request)(const struct sr_dev_inst *sdi);
+	int (*handle_response)(const struct sr_dev_inst *sdi);
+};
+
+extern SR_PRIV const struct model_info lwla1016_info;
+extern SR_PRIV const struct model_info lwla1034_info;
+
+SR_PRIV int lwla_start_acquisition(const struct sr_dev_inst *sdi);
+
+#endif
diff --git a/hardware/teleinfo/api.c b/src/hardware/teleinfo/api.c
similarity index 57%
rename from hardware/teleinfo/api.c
rename to src/hardware/teleinfo/api.c
index 931e599..dbf7f03 100644
--- a/hardware/teleinfo/api.c
+++ b/src/hardware/teleinfo/api.c
@@ -17,39 +17,38 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_ENERGYMETER,
-	SR_CONF_LIMIT_SAMPLES,
-	SR_CONF_LIMIT_MSEC,
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
 };
 
 SR_PRIV struct sr_dev_driver teleinfo_driver_info;
-static struct sr_dev_driver *di = &teleinfo_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_serial_dev_inst *serial;
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	GSList *devices = NULL, *l;
 	const char *conn = NULL, *serialcomm = NULL;
 	uint8_t buf[292];
@@ -74,14 +73,14 @@ static GSList *scan(GSList *options)
 	if (!serialcomm)
 		serialcomm = "1200/7e1";
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
-	if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK)
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
+
+	if (serial_open(serial, SERIAL_RDONLY) != SR_OK)
 		return NULL;
 
 	sr_info("Probing serial port %s.", conn);
 
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 	serial_flush(serial);
 
@@ -92,71 +91,38 @@ static GSList *scan(GSList *options)
 
 	sr_info("Found device on port %s.", conn);
 
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "EDF", "Teleinfo", NULL)))
-		goto scan_cleanup;
-
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		goto scan_cleanup;
-	}
-
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("EDF");
+	sdi->model = g_strdup("Teleinfo");
+	devc = g_malloc0(sizeof(struct dev_context));
 	devc->optarif = teleinfo_get_optarif(buf);
-
 	sdi->inst_type = SR_INST_SERIAL;
 	sdi->conn = serial;
 	sdi->priv = devc;
 	sdi->driver = di;
 
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P")))
-		goto scan_cleanup;
-	sdi->channels = g_slist_append(sdi->channels, ch);
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P");
 
 	if (devc->optarif == OPTARIF_BASE) {
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "BASE")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "BASE");
 	} else if (devc->optarif == OPTARIF_HC) {
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HP")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HC")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HP");
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HC");
 	} else if (devc->optarif == OPTARIF_EJP) {
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HN")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPM")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HN");
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HPM");
 	} else if (devc->optarif == OPTARIF_BBR) {
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJB")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJW")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HPJR")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJB")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJW")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "HCJR")))
-			goto scan_cleanup;
-		sdi->channels = g_slist_append(sdi->channels, ch);
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HPJB");
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HPJW");
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HPJR");
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HCJB");
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HCJW");
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "HCJR");
 	}
 
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "IINST")))
-		goto scan_cleanup;
-	sdi->channels = g_slist_append(sdi->channels, ch);
-
-	if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "PAPP")))
-		goto scan_cleanup;
-	sdi->channels = g_slist_append(sdi->channels, ch);
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "IINST");
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "PAPP");
 
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
@@ -167,17 +133,17 @@ scan_cleanup:
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -195,11 +161,9 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_samples = g_variant_get_uint64(data);
-		sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples);
 		break;
 	case SR_CONF_LIMIT_MSEC:
 		devc->limit_msec = g_variant_get_uint64(data);
-		sr_dbg("Setting time limit to %" PRIu64 "ms.", devc->limit_msec);
 		break;
 	default:
 		return SR_ERR_NA;
@@ -208,7 +172,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -216,12 +180,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -257,7 +221,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	std_session_send_df_header(cb_data, LOG_PREFIX);
 
 	/* Poll every 50ms, or whenever some data comes in. */
-	serial_source_add(serial, G_IO_IN, 50, teleinfo_receive_data, (void *)sdi);
+	serial_source_add(sdi->session, serial, G_IO_IN, 50,
+			teleinfo_receive_data, (void *)sdi);
 
 	return SR_OK;
 }
@@ -284,5 +249,5 @@ SR_PRIV struct sr_dev_driver teleinfo_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/teleinfo/protocol.c b/src/hardware/teleinfo/protocol.c
similarity index 94%
rename from hardware/teleinfo/protocol.c
rename to src/hardware/teleinfo/protocol.c
index a610173..21b695b 100644
--- a/hardware/teleinfo/protocol.c
+++ b/src/hardware/teleinfo/protocol.c
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
@@ -59,7 +60,7 @@ static void teleinfo_send_value(struct sr_dev_inst *sdi, const char *channel_nam
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct sr_channel *ch;
 
 	devc = sdi->priv;
@@ -68,22 +69,21 @@ static void teleinfo_send_value(struct sr_dev_inst *sdi, const char *channel_nam
 	if (!ch || !ch->enabled)
 		return;
 
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 	analog.channels = g_slist_append(analog.channels, ch);
 	analog.num_samples = 1;
 	analog.mq = mq;
 	analog.unit = unit;
 	analog.data = &value;
 
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->session_cb_data, &packet);
 	g_slist_free(analog.channels);
 }
 
-static void teleinfo_handle_mesurement(struct sr_dev_inst *sdi,
-                                       const char *label, const char *data,
-                                       char *optarif)
+static void teleinfo_handle_measurement(struct sr_dev_inst *sdi,
+		const char *label, const char *data, char *optarif)
 {
 	struct dev_context *devc;
 	int v = atoi(data);
@@ -135,7 +135,7 @@ static gboolean teleinfo_parse_group(struct sr_dev_inst *sdi,
 		return FALSE;
 	if (!teleinfo_control_check(label, data, control))
 		return FALSE;
-	teleinfo_handle_mesurement(sdi, label, data, optarif);
+	teleinfo_handle_measurement(sdi, label, data, optarif);
 	return TRUE;
 }
 
@@ -196,7 +196,7 @@ SR_PRIV int teleinfo_receive_data(int fd, int revents, void *cb_data)
 
 	/* Try to get as much data as the buffer can hold. */
 	len = TELEINFO_BUF_SIZE - devc->buf_len;
-	len = serial_read(serial, devc->buf + devc->buf_len, len);
+	len = serial_read_nonblocking(serial, devc->buf + devc->buf_len, len);
 	if (len < 1) {
 		sr_err("Serial port read error: %d.", len);
 		return FALSE;
diff --git a/hardware/teleinfo/protocol.h b/src/hardware/teleinfo/protocol.h
similarity index 94%
rename from hardware/teleinfo/protocol.h
rename to src/hardware/teleinfo/protocol.h
index f95c50e..84a7546 100644
--- a/hardware/teleinfo/protocol.h
+++ b/src/hardware/teleinfo/protocol.h
@@ -22,7 +22,7 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "teleinfo"
@@ -45,7 +45,7 @@ struct dev_context {
 	void *session_cb_data;    /**< Opaque pointer passed in by the frontend. */
 
 	/* Operational state */
-	enum optarif optarif;     /**< The device mode (which mesures are reported) */
+	enum optarif optarif;     /**< The device mode (which measures are reported) */
 	uint64_t num_samples;     /**< The number of already received samples. */
 	int64_t start_time;       /**< The time at which sampling started. */
 
diff --git a/src/hardware/testo/api.c b/src/hardware/testo/api.c
new file mode 100644
index 0000000..0c37886
--- /dev/null
+++ b/src/hardware/testo/api.c
@@ -0,0 +1,540 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include "protocol.h"
+
+#define SERIALCOMM "115200/8n1"
+
+SR_PRIV struct sr_dev_driver testo_driver_info;
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_MULTIMETER,
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+};
+
+static const uint8_t TESTO_x35_REQUEST[] = { 0x12, 0, 0, 0, 1, 1, 0x55, 0xd1, 0xb7 };
+
+static const struct testo_model models[] = {
+	{ "435", 9, TESTO_x35_REQUEST },
+};
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_config *src;
+	struct sr_dev_inst *sdi;
+	struct sr_usb_dev_inst *usb;
+	struct libusb_device_descriptor des;
+	libusb_device **devlist;
+	struct libusb_device_handle *hdl;
+	GSList *conn_devices, *devices, *l;
+	int ret, i;
+	const char *str;
+	char manufacturer[64], product[64], connection_id[64];
+
+	devices = NULL;
+	drvc = di->context;
+	drvc->instances = NULL;
+
+	conn_devices = NULL;
+	for (l = options; l; l = l->next) {
+		src = l->data;
+		if (src->key != SR_CONF_CONN)
+			continue;
+		str = g_variant_get_string(src->data, NULL);
+		conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, str);
+	}
+
+	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+	for (i = 0; devlist[i]; i++) {
+		if (conn_devices) {
+			usb = NULL;
+			for (l = conn_devices; l; l = l->next) {
+				usb = l->data;
+				if (usb->bus == libusb_get_bus_number(devlist[i])
+					&& usb->address == libusb_get_device_address(devlist[i]))
+					break;
+			}
+			if (!l)
+				/* This device matched none of the ones that
+				 * matched the conn specification. */
+				continue;
+		}
+
+		libusb_get_device_descriptor(devlist[i], &des);
+
+		if ((ret = libusb_open(devlist[i], &hdl)) < 0)
+			continue;
+
+		manufacturer[0] = product[0] = '\0';
+		if (des.iManufacturer && (ret = libusb_get_string_descriptor_ascii(
+				hdl, des.iManufacturer, (unsigned char *) manufacturer,
+				sizeof(manufacturer))) < 0) {
+			sr_warn("Failed to get manufacturer string descriptor: %s.",
+				libusb_error_name(ret));
+		}
+		if (des.iProduct && (ret = libusb_get_string_descriptor_ascii(
+				hdl, des.iProduct, (unsigned char *) product,
+				sizeof(product))) < 0) {
+			sr_warn("Failed to get product string descriptor: %s.",
+				libusb_error_name(ret));
+		}
+		libusb_close(hdl);
+
+		if (strncmp(manufacturer, "testo", 5))
+			continue;
+
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+
+		/* Hardcode the 435 for now.*/
+		if (strcmp(product, "testo 435/635/735"))
+			continue;
+
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup("Testo");
+		sdi->model = g_strdup("435/635/735");
+		sdi->driver = di;
+		sdi->inst_type = SR_INST_USB;
+		sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+				libusb_get_device_address(devlist[i]), NULL);
+		sdi->connection_id = g_strdup(connection_id);
+		devc = g_malloc(sizeof(struct dev_context));
+		devc->model = &models[0];
+		devc->limit_msec = 0;
+		devc->limit_samples = 0;
+		sdi->priv = devc;
+		if (testo_probe_channels(sdi) != SR_OK)
+			continue;
+		drvc->instances = g_slist_append(drvc->instances, sdi);
+		devices = g_slist_append(devices, sdi);
+	}
+	libusb_free_device_list(devlist, 1);
+	g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+
+	return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct sr_dev_driver *di = sdi->driver;
+	struct drv_context *drvc = di->context;
+	struct sr_usb_dev_inst *usb;
+	libusb_device **devlist;
+	int ret, i;
+	char connection_id[64];
+
+	if (!di->context) {
+		sr_err("Driver was not initialized.");
+		return SR_ERR;
+	}
+
+	usb = sdi->conn;
+	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+	for (i = 0; devlist[i]; i++) {
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+		if (strcmp(sdi->connection_id, connection_id))
+			continue;
+		if ((ret = libusb_open(devlist[i], &usb->devhdl))) {
+			sr_err("Failed to open device: %s.", libusb_error_name(ret));
+			return SR_ERR;
+		}
+		break;
+	}
+	libusb_free_device_list(devlist, 1);
+	if (!devlist[i]) {
+		sr_err("Device not found.");
+		return SR_ERR;
+	}
+
+	if (libusb_has_capability(LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) {
+		if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
+			if ((ret = libusb_detach_kernel_driver(usb->devhdl, 0)) < 0) {
+				sr_err("Failed to detach kernel driver: %s.",
+					   libusb_error_name(ret));
+				return SR_ERR;
+			}
+		}
+	}
+
+	if ((ret = libusb_claim_interface(usb->devhdl, 0))) {
+		sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
+		return SR_ERR;
+	}
+	sdi->status = SR_ST_ACTIVE;
+
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	struct sr_dev_driver *di = sdi->driver;
+	struct sr_usb_dev_inst *usb;
+
+	if (!di->context) {
+		sr_err("Driver was not initialized.");
+		return SR_ERR;
+	}
+
+	usb = sdi->conn;
+	if (!usb->devhdl)
+		/*  Nothing to do. */
+		return SR_OK;
+
+	libusb_release_interface(usb->devhdl, 0);
+	libusb_close(usb->devhdl);
+	usb->devhdl = NULL;
+	sdi->status = SR_ST_INACTIVE;
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	int ret;
+	struct drv_context *drvc;
+
+	if (!(drvc = di->context))
+		return SR_OK;
+
+	ret = dev_clear(di);
+	g_free(drvc);
+
+	return ret;
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct sr_usb_dev_inst *usb;
+	char str[128];
+
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_CONN:
+		if (!sdi || !sdi->conn)
+			return SR_ERR_ARG;
+		usb = sdi->conn;
+		snprintf(str, 128, "%d.%d", usb->bus, usb->address);
+		*data = g_variant_new_string(str);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct sr_dev_driver *di = sdi->driver;
+	struct dev_context *devc;
+	gint64 now;
+	int ret;
+
+	(void)cg;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (!di->context) {
+		sr_err("Driver was not initialized.");
+		return SR_ERR;
+	}
+
+	devc = sdi->priv;
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_LIMIT_MSEC:
+		devc->limit_msec = g_variant_get_uint64(data);
+		now = g_get_monotonic_time() / 1000;
+		devc->end_time = now + devc->limit_msec;
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		devc->limit_samples = g_variant_get_uint64(data);
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	(void)sdi;
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static void receive_data(struct sr_dev_inst *sdi, unsigned char *data, int len)
+{
+	struct dev_context *devc;
+	int packet_size;
+	uint16_t crc;
+
+	devc = sdi->priv;
+
+	if (devc->reply_size + len > MAX_REPLY_SIZE) {
+		/* Something went very wrong. */
+		sr_dbg("Receive buffer overrun.");
+		devc->reply_size = 0;
+		return;
+	}
+
+	memcpy(devc->reply + devc->reply_size, data, len);
+	devc->reply_size += len;
+	/* Sixth byte contains the length of the packet. */
+	if (devc->reply_size < 7)
+		return;
+
+	packet_size = 7 + devc->reply[6] * 7 + 2;
+	if (devc->reply_size < packet_size)
+		return;
+
+	if (!testo_check_packet_prefix(devc->reply, devc->reply_size))
+		return;
+
+	crc = crc16_mcrf4xx(0xffff, devc->reply, devc->reply_size - 2);
+	if (crc == RL16(&devc->reply[devc->reply_size - 2])) {
+		testo_receive_packet(sdi);
+		devc->num_samples++;
+	} else {
+		sr_dbg("Packet has invalid CRC.");
+	}
+
+	devc->reply_size = 0;
+	if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
+		dev_acquisition_stop(sdi, devc->cb_data);
+	else
+		testo_request_packet(sdi);
+
+}
+
+SR_PRIV void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
+{
+	struct dev_context *devc;
+	struct sr_dev_inst *sdi;
+	int ret;
+
+	sdi = transfer->user_data;
+	devc = sdi->priv;
+	if (transfer == devc->out_transfer)
+		/* Just the command acknowledgement. */
+		return;
+
+	if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
+		/* USB device was unplugged. */
+		dev_acquisition_stop(sdi, devc->cb_data);
+	} else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+		/* First two bytes in any transfer are FTDI status bytes. */
+		if (transfer->actual_length > 2)
+			receive_data(sdi, transfer->buffer + 2, transfer->actual_length - 2);
+	}
+	/* Anything else is either an error or a timeout, which is fine:
+	 * we were just going to send another transfer request anyway. */
+
+	if (sdi->status == SR_ST_ACTIVE) {
+		if ((ret = libusb_submit_transfer(transfer) != 0)) {
+			sr_err("Unable to resubmit transfer: %s.",
+			       libusb_error_name(ret));
+			g_free(transfer->buffer);
+			libusb_free_transfer(transfer);
+			dev_acquisition_stop(sdi, devc->cb_data);
+		}
+	} else {
+		/* This was the last transfer we're going to receive, so
+		 * clean up now. */
+		g_free(transfer->buffer);
+		libusb_free_transfer(transfer);
+	}
+}
+
+static int handle_events(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_driver *di;
+	struct dev_context *devc;
+	struct drv_context *drvc;
+	struct sr_datafeed_packet packet;
+	struct sr_dev_inst *sdi;
+	struct timeval tv;
+	gint64 now;
+
+	(void)fd;
+	(void)revents;
+
+	sdi = cb_data;
+	devc = sdi->priv;
+	di = sdi->driver;
+	drvc = di->context;
+
+	if (devc->limit_msec) {
+		now = g_get_monotonic_time() / 1000;
+		if (now > devc->end_time)
+			dev_acquisition_stop(sdi, devc->cb_data);
+	}
+
+	if (sdi->status == SR_ST_STOPPING) {
+		usb_source_remove(sdi->session, drvc->sr_ctx);
+
+		dev_close(sdi);
+
+		packet.type = SR_DF_END;
+		sr_session_send(sdi, &packet);
+	}
+
+	memset(&tv, 0, sizeof(struct timeval));
+	libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
+			NULL);
+
+	return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_dev_driver *di = sdi->driver;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	struct libusb_transfer *transfer;
+	int ret;
+	unsigned char *buf;
+
+	drvc = di->context;
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	if (!di->context) {
+		sr_err("Driver was not initialized.");
+		return SR_ERR;
+	}
+
+	devc = sdi->priv;
+	usb = sdi->conn;
+	devc->cb_data = cb_data;
+	devc->end_time = 0;
+	devc->num_samples = 0;
+	devc->reply_size = 0;
+
+	/* Send header packet to the session bus. */
+	std_session_send_df_header(cb_data, LOG_PREFIX);
+
+	usb_source_add(sdi->session, drvc->sr_ctx, 100,
+			handle_events, (void *)sdi);
+
+	if (testo_set_serial_params(usb) != SR_OK)
+		return SR_ERR;
+
+	devc->out_transfer = libusb_alloc_transfer(0);
+	if (testo_request_packet(sdi) != SR_OK)
+		return SR_ERR;
+
+	buf = g_malloc(MAX_REPLY_SIZE);
+	transfer = libusb_alloc_transfer(0);
+	libusb_fill_bulk_transfer(transfer, usb->devhdl, EP_IN, buf,
+			MAX_REPLY_SIZE, receive_transfer, (void *)sdi, 100);
+	if ((ret = libusb_submit_transfer(transfer) != 0)) {
+		sr_err("Unable to submit transfer: %s.", libusb_error_name(ret));
+		libusb_free_transfer(transfer);
+		g_free(buf);
+		return SR_ERR;
+	}
+	devc->reply_size = 0;
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_dev_driver *di = sdi->driver;
+	(void)cb_data;
+
+	if (!di->context) {
+		sr_err("Driver was not initialized.");
+		return SR_ERR;
+	}
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	sdi->status = SR_ST_STOPPING;
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver testo_driver_info = {
+	.name = "testo",
+	.longname = "Testo",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/testo/protocol.c b/src/hardware/testo/protocol.c
new file mode 100644
index 0000000..be36023
--- /dev/null
+++ b/src/hardware/testo/protocol.c
@@ -0,0 +1,291 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include "protocol.h"
+
+SR_PRIV int testo_set_serial_params(struct sr_usb_dev_inst *usb)
+{
+	int ret;
+
+    if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_BAUDRATE,
+			FTDI_BAUDRATE_115200, FTDI_INDEX, NULL, 0, 10)) < 0) {
+			sr_err("Failed to set baudrate: %s", libusb_error_name(ret));
+			return SR_ERR;
+	}
+
+    if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_PARAMS,
+			FTDI_PARAMS_8N1, FTDI_INDEX, NULL, 0, 10)) < 0) {
+			sr_err("Failed to set comm parameters: %s", libusb_error_name(ret));
+			return SR_ERR;
+	}
+
+    if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_FLOWCTRL,
+			FTDI_FLOW_NONE, FTDI_INDEX, NULL, 0, 10)) < 0) {
+			sr_err("Failed to set flow control: %s", libusb_error_name(ret));
+			return SR_ERR;
+	}
+
+    if ((ret = libusb_control_transfer(usb->devhdl, 0x40, FTDI_SET_MODEMCTRL,
+			FTDI_MODEM_ALLHIGH, FTDI_INDEX, NULL, 0, 10)) < 0) {
+			sr_err("Failed to set modem control: %s", libusb_error_name(ret));
+			return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+/* Due to the modular nature of the Testo hardware, you can't assume
+ * which measurements the device will supply. Fetch a single result
+ * set synchronously to see which measurements it has. */
+SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	int unit, packet_len, len, i;
+	unsigned char packet[MAX_REPLY_SIZE], buf[MAX_REPLY_SIZE];
+	const char *probe_name;
+
+	devc = sdi->priv;
+	usb = sdi->conn;
+
+	sr_dbg("Probing for channels.");
+	if (sdi->driver->dev_open(sdi) != SR_OK)
+		return SR_ERR;
+	if (testo_set_serial_params(usb) != SR_OK)
+		return SR_ERR;
+
+	/* Flush anything buffered from a previous run. */
+	do {
+		libusb_bulk_transfer(usb->devhdl, EP_IN, buf, MAX_REPLY_SIZE, &len, 10);
+	} while (len > 2);
+
+	if (libusb_bulk_transfer(usb->devhdl, EP_OUT, (unsigned char *)devc->model->request,
+			devc->model->request_size, &devc->reply_size, 10) < 0)
+		return SR_ERR;
+
+	packet_len = 0;
+	while (TRUE) {
+		if (libusb_bulk_transfer(usb->devhdl, EP_IN, buf, MAX_REPLY_SIZE,
+				&len, 250) < 0)
+			return SR_ERR;
+		if (len == 2)
+			/* FTDI cruft */
+			continue;
+		if (packet_len + len - 2 > MAX_REPLY_SIZE)
+			return SR_ERR;
+
+		memcpy(packet + packet_len, buf + 2, len - 2);
+		packet_len += len - 2;
+		if (packet_len < 5)
+			/* Not even enough to check prefix yet. */
+			continue;
+
+		if (!testo_check_packet_prefix(packet, packet_len)) {
+			/* Tail end of some previous data, drop it. */
+			packet_len = 0;
+			continue;
+		}
+
+		if (packet_len >= 7 + packet[6] * 7 + 2)
+			/* Got a complete packet. */
+			break;
+	}
+	sdi->driver->dev_close(sdi);
+
+	if (packet[6] > MAX_CHANNELS) {
+		sr_err("Device says it has %d channels!", packet[6]);
+		return SR_ERR;
+	}
+
+	for (i = 0; i < packet[6]; i++) {
+		unit = packet[7 + i * 7 + 4];
+		devc->channel_units[i] = unit;
+		switch (unit) {
+		case 1:
+			probe_name = "Temperature";
+			break;
+		case 3:
+			probe_name = "Humidity";
+			break;
+		case 5:
+			probe_name = "Windspeed";
+			break;
+		case 24:
+			probe_name = "Pressure";
+			break;
+		default:
+			sr_dbg("Unsupported measurement unit %d", unit);
+			return SR_ERR;
+		}
+		sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, probe_name);
+	}
+	devc->num_channels = packet[6];
+	sr_dbg("Found %d channel%s.", devc->num_channels,
+			devc->num_channels > 1 ? "s" : "");
+
+	return SR_OK;
+}
+
+SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+	int ret;
+
+	devc = sdi->priv;
+	usb = sdi->conn;
+
+	libusb_fill_bulk_transfer(devc->out_transfer, usb->devhdl, EP_OUT,
+			(unsigned char *)devc->model->request, devc->model->request_size,
+			receive_transfer, (void *)sdi, 100);
+	if ((ret = libusb_submit_transfer(devc->out_transfer) != 0)) {
+		sr_err("Failed to request packet: %s.", libusb_error_name(ret));
+		sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
+				devc->cb_data);
+		return SR_ERR;
+	}
+	sr_dbg("Requested new packet.");
+
+	return SR_OK;
+}
+
+/* Check if the packet is well-formed. This matches packets for the
+ * Testo 175/177/400/650/950/435/635/735/445/645/945/946/545. */
+SR_PRIV gboolean testo_check_packet_prefix(unsigned char *buf, int len)
+{
+	int i;
+	unsigned char check[] = { 0x21, 0, 0, 0, 1 };
+
+	if (len < 5)
+		return FALSE;
+
+	for (i = 0; i < 5; i++) {
+		if (buf[i] != check[i]) {
+			sr_dbg("Packet has invalid prefix.");
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+SR_PRIV uint16_t crc16_mcrf4xx(uint16_t crc, uint8_t *data, size_t len)
+{
+	int i;
+
+	if (!data || !len)
+		return crc;
+
+	while (len--) {
+		crc ^= *data++;
+		for (i = 0; i < 8; i++) {
+			if (crc & 1)
+				crc = (crc >> 1) ^ 0x8408;
+			else
+				crc = (crc >> 1);
+		}
+	}
+
+	return crc;
+}
+
+static float binary32_le_to_float(unsigned char *buf)
+{
+	GFloatIEEE754 f;
+
+	f.v_float = 0;
+	f.mpn.sign = (buf[3] & 0x80) ? 1 : 0;
+	f.mpn.biased_exponent = (buf[3] << 1) | (buf[2] >> 7);
+	f.mpn.mantissa = buf[0] | (buf[1] << 8) | ((buf[2] & 0x7f) << 16);
+
+	return f.v_float;
+}
+
+SR_PRIV void testo_receive_packet(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+	struct sr_channel *ch;
+	GString *dbg;
+	float value;
+	int i;
+	unsigned char *buf;
+
+	devc = sdi->priv;
+	sr_dbg("Got %d-byte packet.", devc->reply_size);
+
+	if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+		dbg = g_string_sized_new(128);
+		g_string_printf(dbg, "Packet:");
+		for (i = 0; i < devc->reply_size; i++)
+			g_string_append_printf(dbg, " %.2x", devc->reply[i]);
+		sr_spew("%s", dbg->str);
+		g_string_free(dbg, TRUE);
+	}
+
+	packet.type = SR_DF_ANALOG_OLD;
+	packet.payload = &analog;
+	analog.num_samples = 1;
+	analog.mqflags = 0;
+	analog.data = &value;
+	/* Decode 7-byte values */
+	for (i = 0; i < devc->reply[6]; i++) {
+		buf = devc->reply + 7 + i * 7;
+		value = binary32_le_to_float(buf);
+		switch (buf[4]) {
+		case 1:
+			analog.mq = SR_MQ_TEMPERATURE;
+			analog.unit = SR_UNIT_CELSIUS;
+			break;
+		case 3:
+			analog.mq = SR_MQ_RELATIVE_HUMIDITY;
+			analog.unit = SR_UNIT_HUMIDITY_293K;
+			break;
+		case 5:
+			analog.mq = SR_MQ_WIND_SPEED;
+			analog.unit = SR_UNIT_METER_SECOND;
+			break;
+		case 24:
+			analog.mq = SR_MQ_PRESSURE;
+			analog.unit = SR_UNIT_HECTOPASCAL;
+			break;
+		default:
+			sr_dbg("Unsupported measurement unit %d.", buf[4]);
+			return;
+		}
+
+		/* Match this measurement with its channel. */
+		for (i = 0; i < devc->num_channels; i++) {
+			if (devc->channel_units[i] == buf[4])
+				break;
+		}
+		if (i == devc->num_channels) {
+			/* Shouldn't happen. */
+			sr_err("Some channel hotswapped in!");
+			return;
+		}
+		ch = g_slist_nth_data(sdi->channels, i);
+		analog.channels = g_slist_append(NULL, ch);
+		sr_session_send(sdi, &packet);
+		g_slist_free(analog.channels);
+	}
+}
diff --git a/src/hardware/testo/protocol.h b/src/hardware/testo/protocol.h
new file mode 100644
index 0000000..06a2964
--- /dev/null
+++ b/src/hardware/testo/protocol.h
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at 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_TESTO_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_TESTO_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "testo"
+
+#define MAX_REPLY_SIZE       128
+#define MAX_CHANNELS         16
+
+/* FTDI commands */
+#define FTDI_SET_MODEMCTRL   0x01
+#define FTDI_SET_FLOWCTRL    0x02
+#define FTDI_SET_BAUDRATE    0x03
+#define FTDI_SET_PARAMS      0x04
+/* FTDI command values */
+#define FTDI_BAUDRATE_115200 0x001a
+#define FTDI_PARAMS_8N1      0x0008
+#define FTDI_FLOW_NONE       0x0008
+#define FTDI_MODEM_ALLHIGH   0x0303
+#define FTDI_INDEX           0x0000
+/* FTDI USB stuff */
+#define EP_IN                1 | LIBUSB_ENDPOINT_IN
+#define EP_OUT               2 | LIBUSB_ENDPOINT_OUT
+
+struct testo_model {
+	const char *name;
+	int request_size;
+	const uint8_t *request;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/* Model-specific information */
+	const struct testo_model *model;
+
+	/* Acquisition settings */
+	uint64_t limit_msec;
+	uint64_t limit_samples;
+	void *cb_data;
+
+	/* Operational state */
+	gint64 end_time;
+	uint64_t num_samples;
+	uint8_t channel_units[MAX_CHANNELS];
+	int num_channels;
+
+	/* Temporary state across callbacks */
+	struct libusb_transfer *out_transfer;
+	uint8_t reply[MAX_REPLY_SIZE];
+	int reply_size;
+};
+
+SR_PRIV int testo_set_serial_params(struct sr_usb_dev_inst *usb);
+SR_PRIV int testo_probe_channels(struct sr_dev_inst *sdi);
+SR_PRIV void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV int testo_request_packet(const struct sr_dev_inst *sdi);
+SR_PRIV gboolean testo_check_packet_prefix(uint8_t *buf, int len);
+SR_PRIV uint16_t crc16_mcrf4xx(uint16_t crc, uint8_t *data, size_t len);
+SR_PRIV void testo_receive_packet(const struct sr_dev_inst *sdi);
+
+#endif
diff --git a/hardware/tondaj-sl-814/api.c b/src/hardware/tondaj-sl-814/api.c
similarity index 71%
rename from hardware/tondaj-sl-814/api.c
rename to src/hardware/tondaj-sl-814/api.c
index d112b19..5e83def 100644
--- a/hardware/tondaj-sl-814/api.c
+++ b/src/hardware/tondaj-sl-814/api.c
@@ -18,45 +18,44 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <fcntl.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
 #define SERIALCOMM "9600/8e1"
 
-static const int32_t hwopts[] = {
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 	SR_CONF_SERIALCOMM,
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_SOUNDLEVELMETER,
-	SR_CONF_LIMIT_SAMPLES,
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
 };
 
 SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info;
-static struct sr_dev_driver *di = &tondaj_sl_814_driver_info;
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
 	struct sr_config *src;
-	struct sr_channel *ch;
 	GSList *devices, *l;
 	const char *conn, *serialcomm;
 	struct sr_serial_dev_inst *serial;
 
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	devices = NULL;
@@ -84,21 +83,15 @@ static GSList *scan(GSList *options)
 	if (!serialcomm)
 		serialcomm = SERIALCOMM;
 
-	if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Tondaj",
-				    "SL-814", NULL))) {
-		sr_err("Failed to create device instance.");
-		return NULL;
-	}
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup("Tondaj");
+	sdi->model = g_strdup("SL-814");
+	devc = g_malloc0(sizeof(struct dev_context));
 
-	if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-		sr_err("Device context malloc failed.");
-		return NULL;
-	}
+	serial = sr_serial_dev_inst_new(conn, serialcomm);
 
-	if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
-		return NULL;
-
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return NULL;
 
 	sdi->inst_type = SR_INST_SERIAL;
@@ -106,29 +99,24 @@ static GSList *scan(GSList *options)
 
 	sdi->priv = devc;
 	sdi->driver = di;
-	ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1");
-	if (!ch) {
-		sr_err("Failed to create channel.");
-		return NULL;
-	}
-	sdi->channels = g_slist_append(sdi->channels, ch);
+	sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
 	drvc->instances = g_slist_append(drvc->instances, sdi);
 	devices = g_slist_append(devices, sdi);
 
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -140,11 +128,9 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
 	devc = sdi->priv;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_samples = g_variant_get_uint64(data);
-		sr_dbg("Setting sample limit to %" PRIu64 ".",
-		       devc->limit_samples);
 		break;
 	default:
 		return SR_ERR_NA;
@@ -153,7 +139,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -161,12 +147,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -192,7 +178,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 
 	/* Poll every 500ms, or whenever some data comes in. */
 	serial = sdi->conn;
-	serial_source_add(serial, G_IO_IN, 500,
+	serial_source_add(sdi->session, serial, G_IO_IN, 500,
 		      tondaj_sl_814_receive_data, (void *)sdi);
 
 	return SR_OK;
@@ -220,5 +206,5 @@ SR_PRIV struct sr_dev_driver tondaj_sl_814_driver_info = {
 	.dev_close = std_serial_dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/tondaj-sl-814/protocol.c b/src/hardware/tondaj-sl-814/protocol.c
similarity index 89%
rename from hardware/tondaj-sl-814/protocol.c
rename to src/hardware/tondaj-sl-814/protocol.c
index feeec69..d09d19b 100644
--- a/hardware/tondaj-sl-814/protocol.c
+++ b/src/hardware/tondaj-sl-814/protocol.c
@@ -18,9 +18,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
@@ -33,7 +34,7 @@ enum {
 };
 
 static void parse_packet(const uint8_t *buf, float *floatval,
-			 struct sr_datafeed_analog *analog)
+			 struct sr_datafeed_analog_old *analog)
 {
 	gboolean is_a, is_fast;
 	uint16_t intval;
@@ -88,12 +89,12 @@ static void parse_packet(const uint8_t *buf, float *floatval,
 static void decode_packet(struct sr_dev_inst *sdi)
 {
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	struct dev_context *devc;
 	float floatval;
 
 	devc = sdi->priv;
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 
 	parse_packet(devc->buf, &floatval, &analog);
 
@@ -101,14 +102,14 @@ static void decode_packet(struct sr_dev_inst *sdi)
 	analog.channels = sdi->channels;
 	analog.num_samples = 1;
 	analog.data = &floatval;
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 
 	devc->num_samples++;
 }
 
-int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data)
+SR_PRIV int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data)
 {
 	struct sr_dev_inst *sdi;
 	struct dev_context *devc;
@@ -133,14 +134,15 @@ int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data)
 		buf[2] = 0x0d;
 		sr_spew("Sending init command: %02x %02x %02x.",
 			buf[0], buf[1], buf[2]);
-		if ((ret = serial_write(serial, buf, 3)) < 0) {
+		if ((ret = serial_write_blocking(serial, buf, 3,
+				serial_timeout(serial, 3))) < 0) {
 			sr_err("Error sending init command: %d.", ret);
 			return FALSE;
 		}
 		devc->state = GET_INIT_REPLY;
 	} else if (devc->state == GET_INIT_REPLY) {
 		/* If we just sent the "init" command, get its reply. */
-		if ((ret = serial_read(serial, buf, 2)) < 0) {
+		if ((ret = serial_read_blocking(serial, buf, 2, 0)) < 0) {
 			sr_err("Error reading init reply: %d.", ret);
 			return FALSE;
 		}
@@ -159,7 +161,8 @@ int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data)
 		buf[2] = 0x0d;
 		sr_spew("Sending data request command: %02x %02x %02x.",
 			buf[0], buf[1], buf[2]);
-		if ((ret = serial_write(serial, buf, 3)) < 0) {
+		if ((ret = serial_write_blocking(serial, buf, 3,
+				serial_timeout(serial, 3))) < 0) {
 			sr_err("Error sending request command: %d.", ret);
 			return FALSE;
 		}
@@ -167,7 +170,7 @@ int tondaj_sl_814_receive_data(int fd, int revents, void *cb_data)
 		devc->state = GET_PACKET;
 	} else if (devc->state == GET_PACKET) {
 		/* Read a packet from the device. */
-		ret = serial_read(serial, devc->buf + devc->buflen,
+		ret = serial_read_nonblocking(serial, devc->buf + devc->buflen,
 				  4 - devc->buflen);
 		if (ret < 0) {
 			sr_err("Error reading packet: %d.", ret);
diff --git a/hardware/tondaj-sl-814/protocol.h b/src/hardware/tondaj-sl-814/protocol.h
similarity index 97%
rename from hardware/tondaj-sl-814/protocol.h
rename to src/hardware/tondaj-sl-814/protocol.h
index 027b66d..77c6ca0 100644
--- a/hardware/tondaj-sl-814/protocol.h
+++ b/src/hardware/tondaj-sl-814/protocol.h
@@ -22,7 +22,7 @@
 #define LIBSIGROK_HARDWARE_TONDAJ_SL_814_PROTOCOL_H
 
 #include <stdint.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "tondaj-sl-814"
diff --git a/src/hardware/uni-t-dmm/api.c b/src/hardware/uni-t-dmm/api.c
new file mode 100644
index 0000000..f74d10d
--- /dev/null
+++ b/src/hardware/uni-t-dmm/api.c
@@ -0,0 +1,425 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012-2013 Uwe Hermann <uwe at 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 <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+#define UNI_T_UT_D04_NEW "1a86.e008"
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_MULTIMETER,
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+};
+
+/*
+ * Note 1: The actual baudrate of the Cyrustek ES519xx chip used in this DMM
+ * is 19230. However, the WCH CH9325 chip (UART to USB/HID) used in (some
+ * versions of) the UNI-T UT-D04 cable doesn't support 19230 baud. It only
+ * supports 19200, and setting an unsupported baudrate will result in the
+ * default of 2400 being used (which will not work with this DMM, of course).
+ */
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, NULL);
+}
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	GSList *usb_devices, *devices, *l;
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct drv_context *drvc;
+	struct dmm_info *dmm;
+	struct sr_usb_dev_inst *usb;
+	struct sr_config *src;
+	const char *conn;
+
+	drvc = di->context;
+	dmm = (struct dmm_info *)di;
+
+	conn = NULL;
+	for (l = options; l; l = l->next) {
+		src = l->data;
+		switch (src->key) {
+		case SR_CONF_CONN:
+			conn = g_variant_get_string(src->data, NULL);
+			break;
+		}
+	}
+	if (!conn)
+		return NULL;
+
+	devices = NULL;
+	if (!(usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
+		g_slist_free_full(usb_devices, g_free);
+		return NULL;
+	}
+
+	for (l = usb_devices; l; l = l->next) {
+		usb = l->data;
+		devc = g_malloc0(sizeof(struct dev_context));
+		devc->first_run = TRUE;
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup(dmm->vendor);
+		sdi->model = g_strdup(dmm->device);
+		sdi->priv = devc;
+		sdi->driver = di;
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
+		sdi->inst_type = SR_INST_USB;
+		sdi->conn = usb;
+		drvc->instances = g_slist_append(drvc->instances, sdi);
+		devices = g_slist_append(devices, sdi);
+	}
+
+	return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	struct sr_dev_driver *di;
+	struct drv_context *drvc;
+	struct sr_usb_dev_inst *usb;
+	int ret;
+
+	di = sdi->driver;
+	drvc = di->context;
+	usb = sdi->conn;
+
+	if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
+		sdi->status = SR_ST_ACTIVE;
+
+	return ret;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	/* TODO */
+
+	sdi->status = SR_ST_INACTIVE;
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	return dev_clear(di);
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	devc = sdi->priv;
+
+	switch (key) {
+	case SR_CONF_LIMIT_MSEC:
+		devc->limit_msec = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		devc->limit_samples = g_variant_get_uint64(data);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	(void)sdi;
+	(void)cg;
+
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+		break;
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+
+	devc->cb_data = cb_data;
+
+	devc->starttime = g_get_monotonic_time();
+
+	/* Send header packet to the session bus. */
+	std_session_send_df_header(sdi, LOG_PREFIX);
+
+	sr_session_source_add(sdi->session, -1, 0, 10 /* poll_timeout */,
+		      uni_t_dmm_receive_data, (void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct sr_datafeed_packet packet;
+
+	(void)cb_data;
+
+	sr_dbg("Stopping acquisition.");
+
+	/* Send end packet to the session bus. */
+	sr_dbg("Sending SR_DF_END.");
+	packet.type = SR_DF_END;
+	sr_session_send(sdi, &packet);
+
+	sr_session_source_remove(sdi->session, -1);
+
+	return SR_OK;
+}
+
+#define DMM(ID, CHIPSET, VENDOR, MODEL, BAUDRATE, PACKETSIZE, \
+			VALID, PARSE, DETAILS) \
+    &(struct dmm_info) { \
+		{ \
+			.name = ID, \
+			.longname = VENDOR " " MODEL, \
+			.api_version = 1, \
+			.init = init, \
+			.cleanup = cleanup, \
+			.scan = scan, \
+			.dev_list = dev_list, \
+			.dev_clear = dev_clear, \
+			.config_get = NULL, \
+			.config_set = config_set, \
+			.config_list = config_list, \
+			.dev_open = dev_open, \
+			.dev_close = dev_close, \
+			.dev_acquisition_start = dev_acquisition_start, \
+			.dev_acquisition_stop = dev_acquisition_stop, \
+			.context = NULL, \
+		}, \
+		VENDOR, MODEL, BAUDRATE, PACKETSIZE, \
+		VALID, PARSE, DETAILS, sizeof(struct CHIPSET##_info) \
+	}
+
+SR_PRIV const struct dmm_info *uni_t_dmm_drivers[] = {
+	DMM(
+		"tecpel-dmm-8061", fs9721,
+		"Tecpel", "DMM-8061", 2400,
+		FS9721_PACKET_SIZE,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_00_temp_c
+	),
+	DMM(
+		"uni-t-ut372", ut372,
+		"UNI-T", "UT372", 2400,
+		UT372_PACKET_SIZE,
+		sr_ut372_packet_valid, sr_ut372_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut60a", fs9721,
+		"UNI-T", "UT60A", 2400,
+		FS9721_PACKET_SIZE,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut60e", fs9721,
+		"UNI-T", "UT60E", 2400,
+		FS9721_PACKET_SIZE,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_00_temp_c
+	),
+	DMM(
+		"uni-t-ut60g", es519xx,
+		/* The baudrate is actually 19230, see "Note 1" below. */
+		"UNI-T", "UT60G", 19200,
+		ES519XX_11B_PACKET_SIZE,
+		sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut61b", fs9922,
+		"UNI-T", "UT61B", 2400,
+		FS9922_PACKET_SIZE,
+		sr_fs9922_packet_valid, sr_fs9922_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut61c", fs9922,
+		"UNI-T", "UT61C", 2400,
+		FS9922_PACKET_SIZE,
+		sr_fs9922_packet_valid, sr_fs9922_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut61d", fs9922,
+		"UNI-T", "UT61D", 2400,
+		FS9922_PACKET_SIZE,
+		sr_fs9922_packet_valid, sr_fs9922_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut61e", es519xx,
+		/* The baudrate is actually 19230, see "Note 1" below. */
+		"UNI-T", "UT61E", 19200,
+		ES519XX_14B_PACKET_SIZE,
+		sr_es519xx_19200_14b_packet_valid, sr_es519xx_19200_14b_parse,
+		NULL
+	),
+	DMM(
+		"uni-t-ut71a", ut71x,
+		"UNI-T", "UT71A", 2400, UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut71b", ut71x,
+		"UNI-T", "UT71B", 2400, UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut71c", ut71x,
+		"UNI-T", "UT71C", 2400, UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut71d", ut71x,
+		"UNI-T", "UT71D", 2400, UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"uni-t-ut71e", ut71x,
+		"UNI-T", "UT71E", 2400, UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"voltcraft-vc820", fs9721,
+		"Voltcraft", "VC-820", 2400,
+		FS9721_PACKET_SIZE,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		NULL
+	),
+	DMM(
+		"voltcraft-vc830", fs9922,
+		/*
+		 * Note: The VC830 doesn't set the 'volt' and 'diode' bits of
+		 * the FS9922 protocol. Instead, it only sets the user-defined
+		 * bit "z1" to indicate "diode mode" and "voltage".
+		 */
+		"Voltcraft", "VC-830", 2400,
+		FS9922_PACKET_SIZE,
+		sr_fs9922_packet_valid, sr_fs9922_parse,
+		&sr_fs9922_z1_diode
+	),
+	DMM(
+		"voltcraft-vc840", fs9721,
+		"Voltcraft", "VC-840", 2400,
+		FS9721_PACKET_SIZE,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_00_temp_c
+	),
+	DMM(
+		"voltcraft-vc870", vc870,
+		"Voltcraft", "VC-870", 9600, VC870_PACKET_SIZE,
+		sr_vc870_packet_valid, sr_vc870_parse, NULL
+	),
+	DMM(
+		"voltcraft-vc920", ut71x,
+		"Voltcraft", "VC-920", 2400, UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"voltcraft-vc940", ut71x,
+		"Voltcraft", "VC-940", 2400, UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"voltcraft-vc960", ut71x,
+		"Voltcraft", "VC-960", 2400, UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"tenma-72-7730", ut71x,
+		"Tenma", "72-7730", 2400,
+		UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"tenma-72-7732", ut71x,
+		"Tenma", "72-7732", 2400,
+		UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"tenma-72-9380a", ut71x,
+		"Tenma", "72-9380A", 2400,
+		UT71X_PACKET_SIZE,
+		sr_ut71x_packet_valid, sr_ut71x_parse, NULL
+	),
+	DMM(
+		"tenma-72-7745", es519xx,
+		"Tenma", "72-7745", 2400,
+		FS9721_PACKET_SIZE,
+		sr_fs9721_packet_valid, sr_fs9721_parse,
+		sr_fs9721_00_temp_c
+	),
+	DMM(
+		"tenma-72-7750", es519xx,
+		/* The baudrate is actually 19230, see "Note 1" below. */
+		"Tenma", "72-7750", 19200,
+		ES519XX_11B_PACKET_SIZE,
+		sr_es519xx_19200_11b_packet_valid, sr_es519xx_19200_11b_parse,
+		NULL
+	),
+	NULL
+};
diff --git a/hardware/uni-t-dmm/protocol.c b/src/hardware/uni-t-dmm/protocol.c
similarity index 80%
rename from hardware/uni-t-dmm/protocol.c
rename to src/hardware/uni-t-dmm/protocol.c
index ca2568e..4bbe20d 100644
--- a/hardware/uni-t-dmm/protocol.c
+++ b/src/hardware/uni-t-dmm/protocol.c
@@ -18,14 +18,13 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
-extern struct dmm_info udmms[];
-
 /*
  * Driver for various UNI-T multimeters (and rebranded ones).
  *
@@ -53,34 +52,40 @@ extern struct dmm_info udmms[];
  *  f1 d1 00 00 00 00 00 00 (1 data byte, 0xd1)
  */
 
-static void decode_packet(struct sr_dev_inst *sdi, int dmm, const uint8_t *buf,
-			  void *info)
+static void decode_packet(struct sr_dev_inst *sdi, const uint8_t *buf)
 {
 	struct dev_context *devc;
+	struct dmm_info *dmm;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	float floatval;
+	void *info;
 	int ret;
 
 	devc = sdi->priv;
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	dmm = (struct dmm_info *)sdi->driver;
+	memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
+	info = g_malloc(dmm->info_size);
 
 	/* Parse the protocol packet. */
-	ret = udmms[dmm].packet_parse(buf, &floatval, &analog, info);
+	ret = dmm->packet_parse(buf, &floatval, &analog, info);
 	if (ret != SR_OK) {
 		sr_dbg("Invalid DMM packet, ignoring.");
+		g_free(info);
 		return;
 	}
 
 	/* If this DMM needs additional handling, call the resp. function. */
-	if (udmms[dmm].dmm_details)
-		udmms[dmm].dmm_details(&analog, info);
+	if (dmm->dmm_details)
+		dmm->dmm_details(&analog, info);
+
+	g_free(info);
 
 	/* Send a sample packet with one analog value. */
 	analog.channels = sdi->channels;
 	analog.num_samples = 1;
 	analog.data = &floatval;
-	packet.type = SR_DF_ANALOG;
+	packet.type = SR_DF_ANALOG_OLD;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
 
@@ -171,20 +176,22 @@ static void log_dmm_packet(const uint8_t *buf)
 	       buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
 }
 
-static int get_and_handle_data(struct sr_dev_inst *sdi, int dmm, void *info)
+static int get_and_handle_data(struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
+	struct dmm_info *dmm;
 	uint8_t buf[CHUNK_SIZE], *pbuf;
 	int i, ret, len, num_databytes_in_chunk;
 	struct sr_usb_dev_inst *usb;
 
 	devc = sdi->priv;
+	dmm = (struct dmm_info *)sdi->driver;
 	usb = sdi->conn;
 	pbuf = devc->protocol_buf;
 
 	/* On the first run, we need to init the HID chip. */
 	if (devc->first_run) {
-		if ((ret = hid_chip_init(sdi, udmms[dmm].baudrate)) != SR_OK) {
+		if ((ret = hid_chip_init(sdi, dmm->baudrate)) != SR_OK) {
 			sr_err("HID chip init failed: %d.", ret);
 			return SR_ERR;
 		}
@@ -226,27 +233,29 @@ static int get_and_handle_data(struct sr_dev_inst *sdi, int dmm, void *info)
 	 * Append the 1-7 data bytes of this chunk to pbuf.
 	 *
 	 * Special case:
-	 * DMMs with Cyrustek ES51922 chip need serial settings of
-	 * 19230/7o1. The WCH CH9325 UART to USB/HID chip used in (some
+	 * DMMs with Cyrustek ES51922 chip and UT71x DMMs need serial settings
+	 * of 7o1. The WCH CH9325 UART to USB/HID chip used in (some
 	 * versions of) the UNI-T UT-D04 cable however, will also send
 	 * the parity bit to the host in the 8-byte data chunks. This bit
 	 * is encoded in bit 7 of each of the 1-7 data bytes and must thus
-	 * be removed in order for the actual ES51922 protocol parser to
-	 * work properly.
+	 * be removed in order for the actual protocol parser to work properly.
 	 */
 	num_databytes_in_chunk = buf[0] & 0x0f;
 	for (i = 0; i < num_databytes_in_chunk; i++, devc->buflen++) {
 		pbuf[devc->buflen] = buf[1 + i];
-		if (udmms[dmm].packet_parse == sr_es519xx_19200_14b_parse)
+		if ((dmm->packet_parse == sr_es519xx_19200_14b_parse) ||
+		    (dmm->packet_parse == sr_ut71x_parse)) {
+			/* Mask off the parity bit. */
 			pbuf[devc->buflen] &= ~(1 << 7);
+		}
 	}
 
 	/* Now look for packets in that data. */
-	while ((devc->buflen - devc->bufoffset) >= udmms[dmm].packet_size) {
-		if (udmms[dmm].packet_valid(pbuf + devc->bufoffset)) {
+	while ((devc->buflen - devc->bufoffset) >= dmm->packet_size) {
+		if (dmm->packet_valid(pbuf + devc->bufoffset)) {
 			log_dmm_packet(pbuf + devc->bufoffset);
-			decode_packet(sdi, dmm, pbuf + devc->bufoffset, info);
-			devc->bufoffset += udmms[dmm].packet_size;
+			decode_packet(sdi, pbuf + devc->bufoffset);
+			devc->bufoffset += dmm->packet_size;
 		} else {
 			devc->bufoffset++;
 		}
@@ -260,7 +269,7 @@ static int get_and_handle_data(struct sr_dev_inst *sdi, int dmm, void *info)
 	return SR_OK;
 }
 
-static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
+SR_PRIV int uni_t_dmm_receive_data(int fd, int revents, void *cb_data)
 {
 	int ret;
 	struct sr_dev_inst *sdi;
@@ -273,7 +282,7 @@ static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
 	sdi = cb_data;
 	devc = sdi->priv;
 
-	if ((ret = get_and_handle_data(sdi, dmm, info)) != SR_OK)
+	if ((ret = get_and_handle_data(sdi)) != SR_OK)
 		return FALSE;
 
 	/* Abort acquisition if we acquired enough samples. */
@@ -293,23 +302,3 @@ static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data)
 
 	return TRUE;
 }
-
-#define RECEIVE_DATA(ID_UPPER, DMM_DRIVER) \
-SR_PRIV int receive_data_##ID_UPPER(int fd, int revents, void *cb_data) { \
-	struct DMM_DRIVER##_info info; \
-	return receive_data(fd, revents, ID_UPPER, &info, cb_data); }
-
-/* Driver-specific receive_data() wrappers */
-RECEIVE_DATA(TECPEL_DMM_8061, fs9721)
-RECEIVE_DATA(UNI_T_UT60A, fs9721)
-RECEIVE_DATA(UNI_T_UT60E, fs9721)
-RECEIVE_DATA(UNI_T_UT60G, es519xx)
-RECEIVE_DATA(UNI_T_UT61B, fs9922)
-RECEIVE_DATA(UNI_T_UT61C, fs9922)
-RECEIVE_DATA(UNI_T_UT61D, fs9922)
-RECEIVE_DATA(UNI_T_UT61E, es519xx)
-RECEIVE_DATA(VOLTCRAFT_VC820, fs9721)
-RECEIVE_DATA(VOLTCRAFT_VC830, fs9922)
-RECEIVE_DATA(VOLTCRAFT_VC840, fs9721)
-RECEIVE_DATA(TENMA_72_7745, es519xx)
-RECEIVE_DATA(TENMA_72_7750, es519xx)
diff --git a/hardware/uni-t-dmm/protocol.h b/src/hardware/uni-t-dmm/protocol.h
similarity index 55%
rename from hardware/uni-t-dmm/protocol.h
rename to src/hardware/uni-t-dmm/protocol.h
index 08fb537..f8fdbb5 100644
--- a/hardware/uni-t-dmm/protocol.h
+++ b/src/hardware/uni-t-dmm/protocol.h
@@ -24,38 +24,22 @@
 #include <stdint.h>
 #include <glib.h>
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "uni-t-dmm"
 
-enum {
-	TECPEL_DMM_8061,
-	UNI_T_UT60A,
-	UNI_T_UT60E,
-	UNI_T_UT60G,
-	UNI_T_UT61B,
-	UNI_T_UT61C,
-	UNI_T_UT61D,
-	UNI_T_UT61E,
-	VOLTCRAFT_VC820,
-	VOLTCRAFT_VC830,
-	VOLTCRAFT_VC840,
-	TENMA_72_7745,
-	TENMA_72_7750,
-};
-
 struct dmm_info {
-	char *vendor;
-	char *device;
+	struct sr_dev_driver di;
+	const char *vendor;
+	const char *device;
 	uint32_t baudrate;
 	int packet_size;
 	gboolean (*packet_valid)(const uint8_t *);
 	int (*packet_parse)(const uint8_t *, float *,
-			    struct sr_datafeed_analog *, void *);
-	void (*dmm_details)(struct sr_datafeed_analog *, void *);
-	struct sr_dev_driver *di;
-	int (*receive_data)(int, int, void *);
+			    struct sr_datafeed_analog_old *, void *);
+	void (*dmm_details)(struct sr_datafeed_analog_old *, void *);
+	gsize info_size;
 };
 
 #define CHUNK_SIZE		8
@@ -85,18 +69,6 @@ struct dev_context {
 	uint8_t buflen;
 };
 
-SR_PRIV int receive_data_TECPEL_DMM_8061(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60A(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60E(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT60G(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61B(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61C(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61D(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_UNI_T_UT61E(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC820(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC830(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_VOLTCRAFT_VC840(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TENMA_72_7745(int fd, int revents, void *cb_data);
-SR_PRIV int receive_data_TENMA_72_7750(int fd, int revents, void *cb_data);
+SR_PRIV int uni_t_dmm_receive_data(int fd, int revents, void *cb_data);
 
 #endif
diff --git a/hardware/uni-t-ut32x/api.c b/src/hardware/uni-t-ut32x/api.c
similarity index 80%
rename from hardware/uni-t-ut32x/api.c
rename to src/hardware/uni-t-ut32x/api.c
index b38d2fb..433729e 100644
--- a/hardware/uni-t-ut32x/api.c
+++ b/src/hardware/uni-t-ut32x/api.c
@@ -17,21 +17,19 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "protocol.h"
-
+#include <config.h>
 #include <string.h>
+#include "protocol.h"
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_THERMOMETER,
-	SR_CONF_LIMIT_SAMPLES,
 	SR_CONF_CONTINUOUS,
-	SR_CONF_DATA_SOURCE,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static char *channels[] = {
-	"T1",
-	"T2",
-	"T1-T2",
+static const char *channel_names[] = {
+	"T1", "T2", "T1-T2",
 };
 
 static const char *data_sources[] = {
@@ -40,26 +38,23 @@ static const char *data_sources[] = {
 };
 
 SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info;
-static struct sr_dev_driver *di = &uni_t_ut32x_driver_info;
 
-
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	struct sr_config *src;
 	GSList *usb_devices, *devices, *l;
-	int i;
+	unsigned int i;
 	const char *conn;
 
-	drvc = di->priv;
+	drvc = di->context;
 	drvc->instances = NULL;
 
 	conn = NULL;
@@ -79,25 +74,17 @@ static GSList *scan(GSList *options)
 		/* We have a list of sr_usb_dev_inst matching the connection
 		 * string. Wrap them in sr_dev_inst and we're done. */
 		for (l = usb_devices; l; l = l->next) {
-			if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, VENDOR,
-					MODEL, NULL)))
-				return NULL;
+			sdi = g_malloc0(sizeof(struct sr_dev_inst));
+			sdi->status = SR_ST_INACTIVE;
+			sdi->vendor = g_strdup(VENDOR);
+			sdi->model = g_strdup(MODEL);
 			sdi->driver = di;
 			sdi->inst_type = SR_INST_USB;
 			sdi->conn = l->data;
-			for (i = 0; i < 3; i++) {
-				if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE,
-						channels[i]))) {
-					sr_dbg("Channel malloc failed.");
-					return NULL;
-				}
-				sdi->channels = g_slist_append(sdi->channels, ch);
-			}
-
-			if (!(devc = g_try_malloc(sizeof(struct dev_context)))) {
-				sr_dbg("Device context malloc failed.");
-				return NULL;
-			}
+			for (i = 0; i < ARRAY_SIZE(channel_names); i++)
+				sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE,
+						channel_names[i]);
+			devc = g_malloc0(sizeof(struct dev_context));
 			sdi->priv = devc;
 			devc->limit_samples = 0;
 			devc->data_source = DEFAULT_DATA_SOURCE;
@@ -111,18 +98,19 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct drv_context *drvc;
 	struct sr_usb_dev_inst *usb;
 	int ret;
 
-	if (!(drvc = di->priv)) {
+	if (!(drvc = di->context)) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -133,7 +121,7 @@ static int dev_open(struct sr_dev_inst *sdi)
 		return SR_ERR;
 
 /*
- * The libusbx 1.0.9 darwin backend is broken: it can report a kernel
+ * The libusb 1.0.9 Darwin backend is broken: it can report a kernel
  * driver being active, but detaching it always returns an error.
  */
 #if !defined(__APPLE__)
@@ -162,9 +150,10 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct sr_usb_dev_inst *usb;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -182,24 +171,22 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	int ret;
 	struct drv_context *drvc;
 
-	if (!(drvc = di->priv))
+	if (!(drvc = di->context))
 		/* Can get called on an unused driver, doesn't matter. */
 		return SR_OK;
 
-
 	ret = std_dev_clear(di, NULL);
 	g_free(drvc);
-	di->priv = NULL;
 
 	return ret;
 }
 
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -224,11 +211,11 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct dev_context *devc;
-	int ret;
 	const char *tmp_str;
 
 	(void)cg;
@@ -236,18 +223,16 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
 
 	devc = sdi->priv;
-	ret = SR_OK;
+
 	switch (key) {
 	case SR_CONF_LIMIT_SAMPLES:
 		devc->limit_samples = g_variant_get_uint64(data);
-		sr_dbg("Setting sample limit to %" PRIu64 ".",
-		       devc->limit_samples);
 		break;
 	case SR_CONF_DATA_SOURCE:
 		tmp_str = g_variant_get_string(data, NULL);
@@ -259,23 +244,22 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
 			return SR_ERR;
 		break;
 	default:
-		ret = SR_ERR_NA;
+		return SR_ERR_NA;
 	}
 
-	return ret;
+	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
-
 	(void)sdi;
 	(void)cg;
 
 	switch (key) {
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DATA_SOURCE:
 		*data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
@@ -290,6 +274,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 				    void *cb_data)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_usb_dev_inst *usb;
@@ -299,7 +284,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	drvc = di->priv;
+	drvc = di->context;
 	devc = sdi->priv;
 	usb = sdi->conn;
 
@@ -345,7 +330,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 		return SR_ERR;
 	}
 
-	usb_source_add(drvc->sr_ctx, 10, uni_t_ut32x_handle_events, (void *)sdi);
+	usb_source_add(sdi->session, drvc->sr_ctx, 10,
+			uni_t_ut32x_handle_events, (void *)sdi);
 
 	return SR_OK;
 }
@@ -380,5 +366,5 @@ SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/uni-t-ut32x/protocol.c b/src/hardware/uni-t-ut32x/protocol.c
similarity index 94%
rename from hardware/uni-t-ut32x/protocol.c
rename to src/hardware/uni-t-ut32x/protocol.c
index 863f9fa..7fd1a02 100644
--- a/hardware/uni-t-ut32x/protocol.c
+++ b/src/hardware/uni-t-ut32x/protocol.c
@@ -17,13 +17,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "protocol.h"
-
+#include <config.h>
 #include <string.h>
 #include <math.h>
+#include "protocol.h"
 
 extern struct sr_dev_driver uni_t_ut32x_driver_info;
-static struct sr_dev_driver *di = &uni_t_ut32x_driver_info;
 
 static float parse_temperature(unsigned char *buf)
 {
@@ -63,7 +62,7 @@ static void process_packet(struct sr_dev_inst *sdi)
 {
 	struct dev_context *devc;
 	struct sr_datafeed_packet packet;
-	struct sr_datafeed_analog analog;
+	struct sr_datafeed_analog_old analog;
 	GString *spew;
 	float temp;
 	int i;
@@ -90,7 +89,7 @@ static void process_packet(struct sr_dev_inst *sdi)
 		is_valid = FALSE;
 
 	if (is_valid) {
-		memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+		memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
 		analog.mq = SR_MQ_TEMPERATURE;
 		analog.mqflags = 0;
 		switch (devc->packet[5] - 0x30) {
@@ -129,7 +128,7 @@ static void process_packet(struct sr_dev_inst *sdi)
 		if (is_valid) {
 			analog.num_samples = 1;
 			analog.data = &temp;
-			packet.type = SR_DF_ANALOG;
+			packet.type = SR_DF_ANALOG_OLD;
 			packet.payload = &analog;
 			sr_session_send(devc->cb_data, &packet);
 			g_slist_free(analog.channels);
@@ -147,7 +146,7 @@ static void process_packet(struct sr_dev_inst *sdi)
 
 }
 
-SR_PRIV void uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer)
+SR_PRIV void LIBUSB_CALL uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer)
 {
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
@@ -194,6 +193,7 @@ SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
+	struct sr_dev_driver *di;
 	struct sr_dev_inst *sdi;
 	struct sr_datafeed_packet packet;
 	struct sr_usb_dev_inst *usb;
@@ -204,11 +204,12 @@ SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data)
 	(void)fd;
 	(void)revents;
 
-	drvc = di->priv;
-
 	if (!(sdi = cb_data))
 		return TRUE;
 
+	di = sdi->driver;
+	drvc = di->context;
+
 	if (!(devc = sdi->priv))
 		return TRUE;
 
@@ -217,7 +218,7 @@ SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data)
 			NULL);
 
 	if (sdi->status == SR_ST_STOPPING) {
-		usb_source_remove(drvc->sr_ctx);
+		usb_source_remove(sdi->session, drvc->sr_ctx);
 		packet.type = SR_DF_END;
 		sr_session_send(cb_data, &packet);
 
@@ -237,4 +238,3 @@ SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data)
 
 	return TRUE;
 }
-
diff --git a/hardware/uni-t-ut32x/protocol.h b/src/hardware/uni-t-ut32x/protocol.h
similarity index 92%
rename from hardware/uni-t-ut32x/protocol.h
rename to src/hardware/uni-t-ut32x/protocol.h
index 8513117..4010e17 100644
--- a/hardware/uni-t-ut32x/protocol.h
+++ b/src/hardware/uni-t-ut32x/protocol.h
@@ -22,7 +22,7 @@
 
 #include <stdint.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "uni-t-ut32x"
@@ -34,7 +34,7 @@
 #define USB_INTERFACE 0
 #define USB_CONFIGURATION 1
 
-#define EP_IN 0x80 | 2
+#define EP_IN (0x80 | 2)
 #define EP_OUT 2
 
 enum {
@@ -66,6 +66,6 @@ struct dev_context {
 };
 
 SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data);
-SR_PRIV void uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer);
 
 #endif
diff --git a/hardware/victor-dmm/api.c b/src/hardware/victor-dmm/api.c
similarity index 74%
rename from hardware/victor-dmm/api.c
rename to src/hardware/victor-dmm/api.c
index 74523ed..8e8f027 100644
--- a/hardware/victor-dmm/api.c
+++ b/src/hardware/victor-dmm/api.c
@@ -17,11 +17,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <glib.h>
 #include <libusb.h>
 #include <stdlib.h>
 #include <string.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
@@ -29,72 +30,67 @@
 #define VICTOR_PID 0xd237
 #define VICTOR_VENDOR "Victor"
 #define VICTOR_INTERFACE 0
-#define VICTOR_ENDPOINT LIBUSB_ENDPOINT_IN | 1
+#define VICTOR_ENDPOINT (LIBUSB_ENDPOINT_IN | 1)
 
 SR_PRIV struct sr_dev_driver victor_dmm_driver_info;
-static struct sr_dev_driver *di = &victor_dmm_driver_info;
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
 
-static const int32_t hwopts[] = {
+static const uint32_t drvopts[] = {
+	SR_CONF_MULTIMETER,
+};
+
+static const uint32_t scanopts[] = {
 	SR_CONF_CONN,
 };
 
-static const int32_t hwcaps[] = {
-	SR_CONF_MULTIMETER,
-	SR_CONF_LIMIT_MSEC,
-	SR_CONF_LIMIT_SAMPLES,
+static const uint32_t devopts[] = {
 	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+	SR_CONF_CONN | SR_CONF_GET,
 };
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	struct libusb_device_descriptor des;
 	libusb_device **devlist;
 	GSList *devices;
-	int ret, devcnt, i;
+	int i;
+	char connection_id[64];
 
 	(void)options;
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	devices = NULL;
 	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
 	for (i = 0; devlist[i]; i++) {
-		if ((ret = libusb_get_device_descriptor(devlist[i], &des)) != 0) {
-			sr_warn("Failed to get device descriptor: %s",
-					libusb_error_name(ret));
-			continue;
-		}
+		libusb_get_device_descriptor(devlist[i], &des);
 
 		if (des.idVendor != VICTOR_VID || des.idProduct != VICTOR_PID)
 			continue;
 
-		devcnt = g_slist_length(drvc->instances);
-		if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE,
-				VICTOR_VENDOR, NULL, NULL)))
-			return NULL;
-		sdi->driver = di;
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
 
-		if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
-			return NULL;
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup(VICTOR_VENDOR);
+		sdi->driver = di;
+		sdi->connection_id = g_strdup(connection_id);
+		devc = g_malloc0(sizeof(struct dev_context));
 		sdi->priv = devc;
 
-		if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "P1")))
-			return NULL;
-		sdi->channels = g_slist_append(NULL, ch);
-
-		if (!(sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
-				libusb_get_device_address(devlist[i]), NULL)))
-			return NULL;
+		sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
+		sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+				libusb_get_device_address(devlist[i]), NULL);
 		sdi->inst_type = SR_INST_USB;
 
 		drvc->instances = g_slist_append(drvc->instances, sdi);
@@ -105,19 +101,21 @@ static GSList *scan(GSList *options)
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
-	struct drv_context *drvc = di->priv;
+	struct sr_dev_driver *di = sdi->driver;
+	struct drv_context *drvc = di->context;
 	struct sr_usb_dev_inst *usb;
 	libusb_device **devlist;
 	int ret, i;
+	char connection_id[64];
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -126,8 +124,8 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
 	for (i = 0; devlist[i]; i++) {
-		if (libusb_get_bus_number(devlist[i]) != usb->bus
-				|| libusb_get_device_address(devlist[i]) != usb->address)
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+		if (strcmp(sdi->connection_id, connection_id))
 			continue;
 		if ((ret = libusb_open(devlist[i], &usb->devhdl))) {
 			sr_err("Failed to open device: %s.", libusb_error_name(ret));
@@ -163,9 +161,10 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct sr_usb_dev_inst *usb;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -184,24 +183,22 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	int ret;
 	struct drv_context *drvc;
 
-	if (!(drvc = di->priv))
+	if (!(drvc = di->context))
 		/* Can get called on an unused driver, doesn't matter. */
 		return SR_OK;
 
-
 	ret = std_dev_clear(di, NULL);
 	g_free(drvc);
-	di->priv = NULL;
 
 	return ret;
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct sr_usb_dev_inst *usb;
@@ -209,7 +206,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 
 	(void)cg;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_CONN:
 		if (!sdi || !sdi->conn)
 			return SR_ERR_ARG;
@@ -224,46 +221,42 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct dev_context *devc;
 	gint64 now;
-	int ret;
 
 	(void)cg;
 
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
 
 	devc = sdi->priv;
-	ret = SR_OK;
-	switch (id) {
+
+	switch (key) {
 	case SR_CONF_LIMIT_MSEC:
 		devc->limit_msec = g_variant_get_uint64(data);
 		now = g_get_monotonic_time() / 1000;
 		devc->end_time = now + devc->limit_msec;
-		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:
-		ret = SR_ERR_NA;
+		return SR_ERR_NA;
 	}
 
-	return ret;
+	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -271,12 +264,16 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_DEVICE_OPTIONS:
-		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-				hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+		if (!sdi)
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+		else
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -285,7 +282,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static void receive_transfer(struct libusb_transfer *transfer)
+static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
 {
 	struct dev_context *devc;
 	struct sr_dev_inst *sdi;
@@ -329,9 +326,10 @@ static void receive_transfer(struct libusb_transfer *transfer)
 static int handle_events(int fd, int revents, void *cb_data)
 {
 	struct dev_context *devc;
-	struct drv_context *drvc = di->priv;
+	struct drv_context *drvc;
 	struct sr_datafeed_packet packet;
 	struct sr_dev_inst *sdi;
+	struct sr_dev_driver *di;
 	struct timeval tv;
 	gint64 now;
 
@@ -340,6 +338,8 @@ static int handle_events(int fd, int revents, void *cb_data)
 
 	sdi = cb_data;
 	devc = sdi->priv;
+	di = sdi->driver;
+	drvc = di->context;
 
 	if (devc->limit_msec) {
 		now = g_get_monotonic_time() / 1000;
@@ -348,7 +348,7 @@ static int handle_events(int fd, int revents, void *cb_data)
 	}
 
 	if (sdi->status == SR_ST_STOPPING) {
-		usb_source_remove(drvc->sr_ctx);
+		usb_source_remove(sdi->session, drvc->sr_ctx);
 
 		dev_close(sdi);
 
@@ -365,8 +365,9 @@ static int handle_events(int fd, int revents, void *cb_data)
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct dev_context *devc;
-	struct drv_context *drvc = di->priv;
+	struct drv_context *drvc = di->context;
 	struct sr_usb_dev_inst *usb;
 	struct libusb_transfer *transfer;
 	int ret;
@@ -375,7 +376,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	if (sdi->status != SR_ST_ACTIVE)
 		return SR_ERR_DEV_CLOSED;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -387,9 +388,10 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	/* Send header packet to the session bus. */
 	std_session_send_df_header(cb_data, LOG_PREFIX);
 
-	usb_source_add(drvc->sr_ctx, 100, handle_events, (void *)sdi);
+	usb_source_add(sdi->session, drvc->sr_ctx, 100,
+			handle_events, (void *)sdi);
 
-	buf = g_try_malloc(DMM_DATA_SIZE);
+	buf = g_malloc(DMM_DATA_SIZE);
 	transfer = libusb_alloc_transfer(0);
 	/* Each transfer request gets 100ms to arrive before it's restarted.
 	 * The device only sends 1 transfer/second no matter how many
@@ -410,9 +412,10 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	(void)cb_data;
 
-	if (!di->priv) {
+	if (!di->context) {
 		sr_err("Driver was not initialized.");
 		return SR_ERR;
 	}
@@ -443,5 +446,5 @@ SR_PRIV struct sr_dev_driver victor_dmm_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/victor-dmm/protocol.c b/src/hardware/victor-dmm/protocol.c
similarity index 81%
rename from hardware/victor-dmm/protocol.c
rename to src/hardware/victor-dmm/protocol.c
index 6af5141..b0eb384 100644
--- a/hardware/victor-dmm/protocol.c
+++ b/src/hardware/victor-dmm/protocol.c
@@ -17,10 +17,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <glib.h>
 #include <string.h>
 #include <math.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
@@ -44,6 +45,9 @@ static void decode_buf(struct sr_dev_inst *sdi, unsigned char *data)
 {
 	struct sr_datafeed_packet packet;
 	struct sr_datafeed_analog analog;
+	struct sr_analog_encoding encoding;
+	struct sr_analog_meaning meaning;
+	struct sr_analog_spec spec;
 	struct dev_context *devc;
 	long factor, ivalue;
 	uint8_t digits[4];
@@ -164,52 +168,53 @@ static void decode_buf(struct sr_dev_inst *sdi, unsigned char *data)
 	if (minus)
 		fvalue = -fvalue;
 
-	memset(&analog, 0, sizeof(struct sr_datafeed_analog));
+	sr_analog_init(&analog, &encoding, &meaning, &spec, 4);
 
 	/* Measurement mode */
-	analog.mq = -1;
+	meaning.channels = sdi->channels;
+	meaning.mq = 0;
 	switch (data[3]) {
 	case 0x00:
 		if (is_duty) {
-			analog.mq = SR_MQ_DUTY_CYCLE;
-			analog.unit = SR_UNIT_PERCENTAGE;
+			meaning.mq = SR_MQ_DUTY_CYCLE;
+			meaning.unit = SR_UNIT_PERCENTAGE;
 		} else
 			sr_dbg("Unknown measurement mode: %.2x.", data[3]);
 		break;
 	case 0x01:
 		if (is_diode) {
-			analog.mq = SR_MQ_VOLTAGE;
-			analog.unit = SR_UNIT_VOLT;
-			analog.mqflags |= SR_MQFLAG_DIODE;
+			meaning.mq = SR_MQ_VOLTAGE;
+			meaning.unit = SR_UNIT_VOLT;
+			meaning.mqflags |= SR_MQFLAG_DIODE;
 			if (ivalue < 0)
 				fvalue = NAN;
 		} else {
 			if (ivalue < 0)
 				break;
-			analog.mq = SR_MQ_VOLTAGE;
-			analog.unit = SR_UNIT_VOLT;
+			meaning.mq = SR_MQ_VOLTAGE;
+			meaning.unit = SR_UNIT_VOLT;
 			if (is_ac)
-				analog.mqflags |= SR_MQFLAG_AC;
+				meaning.mqflags |= SR_MQFLAG_AC;
 			if (is_dc)
-				analog.mqflags |= SR_MQFLAG_DC;
+				meaning.mqflags |= SR_MQFLAG_DC;
 		}
 		break;
 	case 0x02:
-		analog.mq = SR_MQ_CURRENT;
-		analog.unit = SR_UNIT_AMPERE;
+		meaning.mq = SR_MQ_CURRENT;
+		meaning.unit = SR_UNIT_AMPERE;
 		if (is_ac)
-			analog.mqflags |= SR_MQFLAG_AC;
+			meaning.mqflags |= SR_MQFLAG_AC;
 		if (is_dc)
-			analog.mqflags |= SR_MQFLAG_DC;
+			meaning.mqflags |= SR_MQFLAG_DC;
 		break;
 	case 0x04:
 		if (is_continuity) {
-			analog.mq = SR_MQ_CONTINUITY;
-			analog.unit = SR_UNIT_BOOLEAN;
+			meaning.mq = SR_MQ_CONTINUITY;
+			meaning.unit = SR_UNIT_BOOLEAN;
 			fvalue = ivalue < 0 ? 0.0 : 1.0;
 		} else {
-			analog.mq = SR_MQ_RESISTANCE;
-			analog.unit = SR_UNIT_OHM;
+			meaning.mq = SR_MQ_RESISTANCE;
+			meaning.unit = SR_UNIT_OHM;
 			if (ivalue < 0)
 				fvalue = INFINITY;
 		}
@@ -219,42 +224,42 @@ static void decode_buf(struct sr_dev_inst *sdi, unsigned char *data)
 		sr_dbg("Unknown measurement mode: 0x%.2x.", data[3]);
 		break;
 	case 0x10:
-		analog.mq = SR_MQ_FREQUENCY;
-		analog.unit = SR_UNIT_HERTZ;
+		meaning.mq = SR_MQ_FREQUENCY;
+		meaning.unit = SR_UNIT_HERTZ;
 		break;
 	case 0x20:
-		analog.mq = SR_MQ_CAPACITANCE;
-		analog.unit = SR_UNIT_FARAD;
+		meaning.mq = SR_MQ_CAPACITANCE;
+		meaning.unit = SR_UNIT_FARAD;
 		break;
 	case 0x40:
-		analog.mq = SR_MQ_TEMPERATURE;
-		analog.unit = SR_UNIT_CELSIUS;
+		meaning.mq = SR_MQ_TEMPERATURE;
+		meaning.unit = SR_UNIT_CELSIUS;
 		break;
 	case 0x80:
-		analog.mq = SR_MQ_TEMPERATURE;
-		analog.unit = SR_UNIT_FAHRENHEIT;
+		meaning.mq = SR_MQ_TEMPERATURE;
+		meaning.unit = SR_UNIT_FAHRENHEIT;
 		break;
 	default:
 		sr_dbg("Unknown/invalid measurement mode: 0x%.2x.", data[3]);
 		break;
 	}
-	if (analog.mq == -1)
+	if (meaning.mq == 0)
 		return;
 
 	if (is_auto)
-		analog.mqflags |= SR_MQFLAG_AUTORANGE;
+		meaning.mqflags |= SR_MQFLAG_AUTORANGE;
 	if (is_hold)
-		analog.mqflags |= SR_MQFLAG_HOLD;
+		meaning.mqflags |= SR_MQFLAG_HOLD;
 	if (is_max)
-		analog.mqflags |= SR_MQFLAG_MAX;
+		meaning.mqflags |= SR_MQFLAG_MAX;
 	if (is_min)
-		analog.mqflags |= SR_MQFLAG_MIN;
+		meaning.mqflags |= SR_MQFLAG_MIN;
 	if (is_relative)
-		analog.mqflags |= SR_MQFLAG_RELATIVE;
+		meaning.mqflags |= SR_MQFLAG_RELATIVE;
 
-	analog.channels = sdi->channels;
-	analog.num_samples = 1;
 	analog.data = &fvalue;
+	analog.num_samples = 1;
+
 	packet.type = SR_DF_ANALOG;
 	packet.payload = &analog;
 	sr_session_send(devc->cb_data, &packet);
diff --git a/hardware/victor-dmm/protocol.h b/src/hardware/victor-dmm/protocol.h
similarity index 97%
rename from hardware/victor-dmm/protocol.h
rename to src/hardware/victor-dmm/protocol.h
index 8f12680..38fd524 100644
--- a/hardware/victor-dmm/protocol.h
+++ b/src/hardware/victor-dmm/protocol.h
@@ -21,7 +21,7 @@
 #define LIBSIGROK_HARDWARE_VICTOR_DMM_PROTOCOL_H
 
 #include <stdint.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "victor-dmm"
diff --git a/src/hardware/yokogawa-dlm/api.c b/src/hardware/yokogawa-dlm/api.c
new file mode 100644
index 0000000..21fa165
--- /dev/null
+++ b/src/hardware/yokogawa-dlm/api.c
@@ -0,0 +1,719 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 abraxa (Soeren Apel) <soeren at apelpie.net>
+ * Based on the Hameg HMO driver by poljar (Damir Jelić) <poljarinho at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include "scpi.h"
+#include "protocol.h"
+
+SR_PRIV struct sr_dev_driver yokogawa_dlm_driver_info;
+
+static const char *MANUFACTURER_ID = "YOKOGAWA";
+static const char *MANUFACTURER_NAME = "Yokogawa";
+
+static const uint32_t dlm_scanopts[] = {
+	SR_CONF_CONN,
+};
+
+static const uint32_t dlm_drvopts[] = {
+	SR_CONF_LOGIC_ANALYZER,
+	SR_CONF_OSCILLOSCOPE,
+};
+
+static const uint32_t dlm_devopts[] = {
+	SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET,
+	SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_NUM_HDIV | SR_CONF_GET,
+	SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint32_t dlm_analog_devopts[] = {
+	SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_NUM_VDIV | SR_CONF_GET,
+};
+
+static const uint32_t dlm_digital_devopts[] = {
+};
+
+enum {
+	CG_INVALID = -1,
+	CG_NONE,
+	CG_ANALOG,
+	CG_DIGITAL,
+};
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+	return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct sr_dev_inst *probe_usbtmc_device(struct sr_scpi_dev_inst *scpi)
+{
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	struct sr_scpi_hw_info *hw_info;
+	char *model_name;
+	int model_index;
+
+	sdi = NULL;
+	devc = NULL;
+	hw_info = NULL;
+
+	if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+		sr_info("Couldn't get IDN response.");
+		goto fail;
+	}
+
+	if (strcmp(hw_info->manufacturer, MANUFACTURER_ID) != 0)
+		goto fail;
+
+	if (dlm_model_get(hw_info->model, &model_name, &model_index) != SR_OK)
+		goto fail;
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->vendor = g_strdup(MANUFACTURER_NAME);
+	sdi->model = g_strdup(model_name);
+	sdi->version = g_strdup(hw_info->firmware_version);
+
+	sdi->serial_num = g_strdup(hw_info->serial_number);
+
+	sr_scpi_hw_info_free(hw_info);
+	hw_info = NULL;
+
+	devc = g_malloc0(sizeof(struct dev_context));
+
+	sdi->driver = &yokogawa_dlm_driver_info;
+	sdi->priv = devc;
+	sdi->inst_type = SR_INST_SCPI;
+	sdi->conn = scpi;
+
+	if (dlm_device_init(sdi, model_index) != SR_OK)
+		goto fail;
+
+	return sdi;
+
+fail:
+	if (hw_info)
+		sr_scpi_hw_info_free(hw_info);
+	if (sdi)
+		sr_dev_inst_free(sdi);
+	g_free(devc);
+
+	return NULL;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	return sr_scpi_scan(di->context, options, probe_usbtmc_device);
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+	return ((struct drv_context *)(di->context))->instances;
+}
+
+static void clear_helper(void *priv)
+{
+	struct dev_context *devc;
+
+	devc = priv;
+
+	dlm_scope_state_destroy(devc->model_state);
+
+	g_free(devc->analog_groups);
+	g_free(devc->digital_groups);
+	g_free(devc);
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+	return std_dev_clear(di, clear_helper);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
+		return SR_ERR;
+
+	if (dlm_scope_state_query(sdi) != SR_OK)
+		return SR_ERR;
+
+	sdi->status = SR_ST_ACTIVE;
+
+	return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	if (sdi->status == SR_ST_INACTIVE)
+		return SR_OK;
+
+	sr_scpi_close(sdi->conn);
+
+	sdi->status = SR_ST_INACTIVE;
+
+	return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+	dev_clear(di);
+
+	return SR_OK;
+}
+
+/**
+ * Check which category a given channel group belongs to.
+ *
+ * @param devc Our internal device context.
+ * @param cg The channel group to check.
+ *
+ * @retval CG_NONE cg is NULL
+ * @retval CG_ANALOG cg is an analog group
+ * @retval CG_DIGITAL cg is a digital group
+ * @retval CG_INVALID cg is something else
+ */
+static int check_channel_group(struct dev_context *devc,
+			const struct sr_channel_group *cg)
+{
+	unsigned int i;
+	const struct scope_config *model;
+
+	model = devc->model_config;
+
+	if (!cg)
+		return CG_NONE;
+
+	for (i = 0; i < model->analog_channels; i++)
+		if (cg == devc->analog_groups[i])
+			return CG_ANALOG;
+
+	for (i = 0; i < model->pods; i++)
+		if (cg == devc->digital_groups[i])
+			return CG_DIGITAL;
+
+	sr_err("Invalid channel group specified.");
+	return CG_INVALID;
+}
+
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	int ret, cg_type;
+	unsigned int i;
+	struct dev_context *devc;
+	const struct scope_config *model;
+	struct scope_state *state;
+
+	if (!sdi || !(devc = sdi->priv))
+		return SR_ERR_ARG;
+
+	if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
+		return SR_ERR;
+
+	model = devc->model_config;
+	state = devc->model_state;
+
+	switch (key) {
+	case SR_CONF_NUM_HDIV:
+		*data = g_variant_new_int32(model->num_xdivs);
+		ret = SR_OK;
+		break;
+	case SR_CONF_TIMEBASE:
+		*data = g_variant_new("(tt)",
+				dlm_timebases[state->timebase][0],
+				dlm_timebases[state->timebase][1]);
+		ret = SR_OK;
+		break;
+	case SR_CONF_NUM_VDIV:
+		if (cg_type == CG_NONE) {
+			sr_err("No channel group specified.");
+			return SR_ERR_CHANNEL_GROUP;
+		} else if (cg_type == CG_ANALOG) {
+				*data = g_variant_new_int32(model->num_ydivs);
+				ret = SR_OK;
+				break;
+		} else {
+			ret = SR_ERR_NA;
+		}
+		break;
+	case SR_CONF_VDIV:
+		ret = SR_ERR_NA;
+		if (cg_type == CG_NONE) {
+			sr_err("No channel group specified.");
+			return SR_ERR_CHANNEL_GROUP;
+		} else if (cg_type != CG_ANALOG)
+			break;
+
+		for (i = 0; i < model->analog_channels; i++) {
+			if (cg != devc->analog_groups[i])
+				continue;
+			*data = g_variant_new("(tt)",
+					dlm_vdivs[state->analog_states[i].vdiv][0],
+					dlm_vdivs[state->analog_states[i].vdiv][1]);
+			ret = SR_OK;
+			break;
+		}
+		break;
+	case SR_CONF_TRIGGER_SOURCE:
+		*data = g_variant_new_string((*model->trigger_sources)[state->trigger_source]);
+		ret = SR_OK;
+		break;
+	case SR_CONF_TRIGGER_SLOPE:
+		*data = g_variant_new_string(dlm_trigger_slopes[state->trigger_slope]);
+		ret = SR_OK;
+		break;
+	case SR_CONF_HORIZ_TRIGGERPOS:
+		*data = g_variant_new_double(state->horiz_triggerpos);
+		ret = SR_OK;
+		break;
+	case SR_CONF_COUPLING:
+		ret = SR_ERR_NA;
+		if (cg_type == CG_NONE) {
+			sr_err("No channel group specified.");
+			return SR_ERR_CHANNEL_GROUP;
+		} else if (cg_type != CG_ANALOG)
+			break;
+
+		for (i = 0; i < model->analog_channels; i++) {
+			if (cg != devc->analog_groups[i])
+				continue;
+			*data = g_variant_new_string((*model->coupling_options)[state->analog_states[i].coupling]);
+			ret = SR_OK;
+			break;
+		}
+		break;
+	case SR_CONF_SAMPLERATE:
+		*data = g_variant_new_uint64(state->sample_rate);
+		ret = SR_OK;
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static GVariant *build_tuples(const uint64_t (*array)[][2], unsigned int n)
+{
+	unsigned int i;
+	GVariant *rational[2];
+	GVariantBuilder gvb;
+
+	g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+	for (i = 0; i < n; i++) {
+		rational[0] = g_variant_new_uint64((*array)[i][0]);
+		rational[1] = g_variant_new_uint64((*array)[i][1]);
+
+		/* FIXME: Valgrind reports a memory leak here. */
+		g_variant_builder_add_value(&gvb, g_variant_new_tuple(rational, 2));
+	}
+
+	return g_variant_builder_end(&gvb);
+}
+
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	int ret, cg_type;
+	unsigned int i, j;
+	char float_str[30];
+	struct dev_context *devc;
+	const struct scope_config *model;
+	struct scope_state *state;
+	const char *tmp;
+	uint64_t p, q;
+	double tmp_d;
+	gboolean update_sample_rate;
+
+	if (!sdi || !(devc = sdi->priv))
+		return SR_ERR_ARG;
+
+	if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
+		return SR_ERR;
+
+	model = devc->model_config;
+	state = devc->model_state;
+	update_sample_rate = FALSE;
+
+	ret = SR_ERR_NA;
+
+	switch (key) {
+	case SR_CONF_LIMIT_FRAMES:
+		devc->frame_limit = g_variant_get_uint64(data);
+		ret = SR_OK;
+		break;
+	case SR_CONF_TRIGGER_SOURCE:
+		tmp = g_variant_get_string(data, NULL);
+		for (i = 0; (*model->trigger_sources)[i]; i++) {
+			if (g_strcmp0(tmp, (*model->trigger_sources)[i]) != 0)
+				continue;
+			state->trigger_source = i;
+			/* TODO: A and B trigger support possible? */
+			ret = dlm_trigger_source_set(sdi->conn, (*model->trigger_sources)[i]);
+			break;
+		}
+		break;
+	case SR_CONF_VDIV:
+		if (cg_type == CG_NONE) {
+			sr_err("No channel group specified.");
+			return SR_ERR_CHANNEL_GROUP;
+		}
+
+		g_variant_get(data, "(tt)", &p, &q);
+
+		for (i = 0; i < ARRAY_SIZE(dlm_vdivs); i++) {
+			if (p != dlm_vdivs[i][0] ||
+					q != dlm_vdivs[i][1])
+				continue;
+			for (j = 1; j <= model->analog_channels; j++) {
+				if (cg != devc->analog_groups[j - 1])
+					continue;
+				state->analog_states[j - 1].vdiv = i;
+				g_ascii_formatd(float_str, sizeof(float_str),
+						"%E", (float) p / q);
+				if (dlm_analog_chan_vdiv_set(sdi->conn, j, float_str) != SR_OK ||
+						sr_scpi_get_opc(sdi->conn) != SR_OK)
+					return SR_ERR;
+
+				break;
+			}
+
+			ret = SR_OK;
+			break;
+		}
+		break;
+	case SR_CONF_TIMEBASE:
+		g_variant_get(data, "(tt)", &p, &q);
+
+		for (i = 0; i < ARRAY_SIZE(dlm_timebases); i++) {
+			if (p != dlm_timebases[i][0] ||
+					q != dlm_timebases[i][1])
+				continue;
+			state->timebase = i;
+			g_ascii_formatd(float_str, sizeof(float_str),
+					"%E", (float) p / q);
+			ret = dlm_timebase_set(sdi->conn, float_str);
+			update_sample_rate = TRUE;
+			break;
+		}
+		break;
+	case SR_CONF_HORIZ_TRIGGERPOS:
+		tmp_d = g_variant_get_double(data);
+
+		/* TODO: Check if the calculation makes sense for the DLM. */
+		if (tmp_d < 0.0 || tmp_d > 1.0)
+			return SR_ERR;
+
+		state->horiz_triggerpos = tmp_d;
+		tmp_d = -(tmp_d - 0.5) *
+				((double) dlm_timebases[state->timebase][0] /
+				dlm_timebases[state->timebase][1])
+				* model->num_xdivs;
+
+		g_ascii_formatd(float_str, sizeof(float_str), "%E", tmp_d);
+		ret = dlm_horiz_trigger_pos_set(sdi->conn, float_str);
+		break;
+	case SR_CONF_TRIGGER_SLOPE:
+		tmp = g_variant_get_string(data, NULL);
+
+		if (!tmp || !(tmp[0] == 'f' || tmp[0] == 'r'))
+			return SR_ERR_ARG;
+
+		/* Note: See dlm_trigger_slopes[] in protocol.c. */
+		state->trigger_slope = (tmp[0] == 'r') ?
+				SLOPE_POSITIVE : SLOPE_NEGATIVE;
+
+		ret = dlm_trigger_slope_set(sdi->conn, state->trigger_slope);
+		break;
+	case SR_CONF_COUPLING:
+		if (cg_type == CG_NONE) {
+			sr_err("No channel group specified.");
+			return SR_ERR_CHANNEL_GROUP;
+		}
+
+		tmp = g_variant_get_string(data, NULL);
+
+		for (i = 0; (*model->coupling_options)[i]; i++) {
+			if (strcmp(tmp, (*model->coupling_options)[i]) != 0)
+				continue;
+			for (j = 1; j <= model->analog_channels; j++) {
+				if (cg != devc->analog_groups[j - 1])
+					continue;
+				state->analog_states[j-1].coupling = i;
+
+				if (dlm_analog_chan_coupl_set(sdi->conn, j, tmp) != SR_OK ||
+						sr_scpi_get_opc(sdi->conn) != SR_OK)
+					return SR_ERR;
+				break;
+			}
+
+			ret = SR_OK;
+			break;
+		}
+		break;
+	default:
+		ret = SR_ERR_NA;
+		break;
+	}
+
+	if (ret == SR_OK)
+		ret = sr_scpi_get_opc(sdi->conn);
+
+	if (ret == SR_OK && update_sample_rate)
+		ret = dlm_sample_rate_query(sdi);
+
+	return ret;
+}
+
+static int config_channel_set(const struct sr_dev_inst *sdi,
+			struct sr_channel *ch, unsigned int changes)
+{
+	/* Currently we only handle SR_CHANNEL_SET_ENABLED. */
+	if (changes != SR_CHANNEL_SET_ENABLED)
+		return SR_ERR_NA;
+
+	return dlm_channel_state_set(sdi, ch->index, ch->enabled);
+}
+
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg)
+{
+	int cg_type = CG_NONE;
+	struct dev_context *devc = NULL;
+	const struct scope_config *model = NULL;
+
+	/* SR_CONF_SCAN_OPTIONS is always valid, regardless of sdi or probe group. */
+	if (key == SR_CONF_SCAN_OPTIONS) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				dlm_scanopts, ARRAY_SIZE(dlm_scanopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	/* If sdi is NULL, nothing except SR_CONF_DEVICE_OPTIONS can be provided. */
+	if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				dlm_drvopts, ARRAY_SIZE(dlm_drvopts), sizeof(uint32_t));
+		return SR_OK;
+	}
+
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+	model = devc->model_config;
+
+	/*
+	 * If cg is NULL, only the SR_CONF_DEVICE_OPTIONS that are not
+	 * specific to a probe group must be returned.
+	 */
+	if (!cg) {
+		switch (key) {
+		case SR_CONF_DEVICE_OPTIONS:
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+					dlm_devopts, ARRAY_SIZE(dlm_devopts), sizeof(uint32_t));
+			return SR_OK;
+		case SR_CONF_TIMEBASE:
+			*data = build_tuples(&dlm_timebases, ARRAY_SIZE(dlm_timebases));
+			return SR_OK;
+		case SR_CONF_TRIGGER_SOURCE:
+			if (!model)
+				return SR_ERR_ARG;
+			*data = g_variant_new_strv(*model->trigger_sources,
+					g_strv_length((char **)*model->trigger_sources));
+			return SR_OK;
+		case SR_CONF_TRIGGER_SLOPE:
+			*data = g_variant_new_strv(dlm_trigger_slopes,
+					g_strv_length((char **)dlm_trigger_slopes));
+			return SR_OK;
+		case SR_CONF_NUM_HDIV:
+			*data = g_variant_new_uint32(model->num_xdivs);
+			return SR_OK;
+		default:
+			return SR_ERR_NA;
+		}
+	}
+
+	if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
+		return SR_ERR;
+
+	switch (key) {
+	case SR_CONF_DEVICE_OPTIONS:
+		if (cg_type == CG_ANALOG) {
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				dlm_analog_devopts, ARRAY_SIZE(dlm_analog_devopts), sizeof(uint32_t));
+		} else if (cg_type == CG_DIGITAL) {
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				dlm_digital_devopts, ARRAY_SIZE(dlm_digital_devopts), sizeof(uint32_t));
+		} else {
+			*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				NULL, 0, sizeof(uint32_t));
+		}
+		break;
+	case SR_CONF_COUPLING:
+		if (cg_type == CG_NONE)
+			return SR_ERR_CHANNEL_GROUP;
+		*data = g_variant_new_strv(*model->coupling_options,
+				g_strv_length((char **)*model->coupling_options));
+		break;
+	case SR_CONF_VDIV:
+		if (cg_type == CG_NONE)
+			return SR_ERR_CHANNEL_GROUP;
+		*data = build_tuples(&dlm_vdivs, ARRAY_SIZE(dlm_vdivs));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int dlm_check_channels(GSList *channels)
+{
+	GSList *l;
+	struct sr_channel *ch;
+	gboolean enabled_pod1, enabled_chan4;
+
+	enabled_pod1 = enabled_chan4 = FALSE;
+
+	/* Note: On the DLM2000, CH4 and Logic are shared. */
+	/* TODO Handle non-DLM2000 models. */
+	for (l = channels; l; l = l->next) {
+		ch = l->data;
+		switch (ch->type) {
+		case SR_CHANNEL_ANALOG:
+			if (ch->index == 3)
+				enabled_chan4 = TRUE;
+			break;
+		case SR_CHANNEL_LOGIC:
+			enabled_pod1 = TRUE;
+			break;
+		default:
+			return SR_ERR;
+		}
+	}
+
+	if (enabled_pod1 && enabled_chan4)
+		return SR_ERR;
+
+	return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+	GSList *l;
+	gboolean digital_added;
+	struct sr_channel *ch;
+	struct dev_context *devc;
+	struct sr_scpi_dev_inst *scpi;
+
+	(void)cb_data;
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	scpi = sdi->conn;
+	devc = sdi->priv;
+	digital_added = FALSE;
+
+	g_slist_free(devc->enabled_channels);
+	devc->enabled_channels = NULL;
+
+	for (l = sdi->channels; l; l = l->next) {
+		ch = l->data;
+		if (!ch->enabled)
+			continue;
+		/* Only add a single digital channel. */
+		if (ch->type != SR_CHANNEL_LOGIC || !digital_added) {
+			devc->enabled_channels = g_slist_append(
+				devc->enabled_channels, ch);
+			if (ch->type == SR_CHANNEL_LOGIC)
+				digital_added = TRUE;
+		}
+	}
+
+	if (!devc->enabled_channels)
+		return SR_ERR;
+
+	if (dlm_check_channels(devc->enabled_channels) != SR_OK) {
+		sr_err("Invalid channel configuration specified!");
+		return SR_ERR_NA;
+	}
+
+	/* Request data for the first enabled channel. */
+	devc->current_channel = devc->enabled_channels;
+	dlm_channel_data_request(sdi);
+
+	/* Call our callback when data comes in or after 5ms. */
+	sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 5,
+			dlm_data_receive, (void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct dev_context *devc;
+	struct sr_datafeed_packet packet;
+
+	(void)cb_data;
+
+	packet.type = SR_DF_END;
+	packet.payload = NULL;
+	sr_session_send(sdi, &packet);
+
+	if (sdi->status != SR_ST_ACTIVE)
+		return SR_ERR_DEV_CLOSED;
+
+	devc = sdi->priv;
+
+	devc->num_frames = 0;
+	g_slist_free(devc->enabled_channels);
+	devc->enabled_channels = NULL;
+
+	sr_scpi_source_remove(sdi->session, sdi->conn);
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver yokogawa_dlm_driver_info = {
+	.name = "yokogawa-dlm",
+	.longname = "Yokogawa DL/DLM",
+	.api_version = 1,
+	.init = init,
+	.cleanup = cleanup,
+	.scan = scan,
+	.dev_list = dev_list,
+	.dev_clear = dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_channel_set = config_channel_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = dev_acquisition_start,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
+};
diff --git a/src/hardware/yokogawa-dlm/protocol.c b/src/hardware/yokogawa-dlm/protocol.c
new file mode 100644
index 0000000..e63e635
--- /dev/null
+++ b/src/hardware/yokogawa-dlm/protocol.c
@@ -0,0 +1,1188 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 abraxa (Soeren Apel) <soeren at apelpie.net>
+ * Based on the Hameg HMO driver by poljar (Damir Jelić) <poljarinho at 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/>.
+ */
+
+/**
+ * @file
+ *
+ * <em>Yokogawa DL/DLM series</em> oscilloscope driver
+ * @internal
+ */
+
+#include <config.h>
+#include "scpi.h"
+#include "protocol.h"
+
+static const char *dlm_coupling_options[] = {
+	"AC",
+	"DC",
+	"DC50",
+	"GND",
+	NULL,
+};
+
+static const char *dlm_2ch_trigger_sources[] = {
+	"1",
+	"2",
+	"LINE",
+	"EXT",
+	NULL,
+};
+
+/* TODO: Is BITx handled correctly or is Dx required? */
+static const char *dlm_4ch_trigger_sources[] = {
+	"1",
+	"2",
+	"3",
+	"4",
+	"LINE",
+	"EXT",
+	"BIT1",
+	"BIT2",
+	"BIT3",
+	"BIT4",
+	"BIT5",
+	"BIT6",
+	"BIT7",
+	"BIT8",
+	NULL,
+};
+
+/* Note: Values must correlate to the trigger_slopes values. */
+const char *dlm_trigger_slopes[3] = {
+	"r",
+	"f",
+	NULL,
+};
+
+const uint64_t dlm_timebases[36][2] = {
+	/* nanoseconds */
+	{ 1, 1000000000 },
+	{ 2, 1000000000 },
+	{ 5, 1000000000 },
+	{ 10, 1000000000 },
+	{ 20, 1000000000 },
+	{ 50, 1000000000 },
+	{ 100, 1000000000 },
+	{ 200, 1000000000 },
+	{ 500, 1000000000 },
+	/* microseconds */
+	{ 1, 1000000 },
+	{ 2, 1000000 },
+	{ 5, 1000000 },
+	{ 10, 1000000 },
+	{ 20, 1000000 },
+	{ 50, 1000000 },
+	{ 100, 1000000 },
+	{ 200, 1000000 },
+	{ 500, 1000000 },
+	/* milliseconds */
+	{ 1, 1000 },
+	{ 2, 1000 },
+	{ 5, 1000 },
+	{ 10, 1000 },
+	{ 20, 1000 },
+	{ 50, 1000 },
+	{ 100, 1000 },
+	{ 200, 1000 },
+	{ 500, 1000 },
+	/* seconds */
+	{ 1, 1 },
+	{ 2, 1 },
+	{ 5, 1 },
+	{ 10, 1 },
+	{ 20, 1 },
+	{ 50, 1 },
+	{ 100, 1 },
+	{ 200, 1 },
+	{ 500, 1 },
+};
+
+const uint64_t dlm_vdivs[17][2] = {
+	/* millivolts */
+	{ 2, 1000 },
+	{ 5, 1000 },
+	{ 10, 1000 },
+	{ 20, 1000 },
+	{ 50, 1000 },
+	{ 100, 1000 },
+	{ 200, 1000 },
+	{ 500, 1000 },
+	/* volts */
+	{ 1, 1 },
+	{ 2, 1 },
+	{ 5, 1 },
+	{ 10, 1 },
+	{ 20, 1 },
+	{ 50, 1 },
+	{ 100, 1 },
+	{ 200, 1 },
+	{ 500, 1 },
+};
+
+static const char *scope_analog_channel_names[] = {
+	"1",
+	"2",
+	"3",
+	"4",
+};
+
+static const char *scope_digital_channel_names_8[] = {
+	"D0",
+	"D1",
+	"D2",
+	"D3",
+	"D4",
+	"D5",
+	"D6",
+	"D7",
+};
+
+static const char *scope_digital_channel_names_32[] = {
+	"A0",
+	"A1",
+	"A2",
+	"A3",
+	"A4",
+	"A5",
+	"A6",
+	"A7",
+	"B0",
+	"B1",
+	"B2",
+	"B3",
+	"B4",
+	"B5",
+	"B6",
+	"B7",
+	"C0",
+	"C1",
+	"C2",
+	"C3",
+	"C4",
+	"C5",
+	"C6",
+	"C7",
+	"D0",
+	"D1",
+	"D2",
+	"D3",
+	"D4",
+	"D5",
+	"D6",
+	"D7",
+};
+
+static const struct scope_config scope_models[] = {
+	{
+		.model_id   = {"710105",  "710115",  "710125",  NULL},
+		.model_name = {"DLM2022", "DLM2032", "DLM2052", NULL},
+		.analog_channels = 2,
+		.digital_channels = 0,
+		.pods = 0,
+
+		.analog_names = &scope_analog_channel_names,
+		.digital_names = &scope_digital_channel_names_8,
+
+		.coupling_options = &dlm_coupling_options,
+		.trigger_sources = &dlm_2ch_trigger_sources,
+
+		.num_xdivs = 10,
+		.num_ydivs = 8,
+	},
+	{
+		.model_id    = {"710110",  "710120",  "710130",  NULL},
+		.model_name  = {"DLM2024", "DLM2034", "DLM2054", NULL},
+		.analog_channels = 4,
+		.digital_channels = 8,
+		.pods = 1,
+
+		.analog_names = &scope_analog_channel_names,
+		.digital_names = &scope_digital_channel_names_8,
+
+		.coupling_options = &dlm_coupling_options,
+		.trigger_sources = &dlm_4ch_trigger_sources,
+
+		.num_xdivs = 10,
+		.num_ydivs = 8,
+	},
+	{
+		.model_id   = {"701307", "701308",  "701310", "701311",
+				"701312", "701313",  NULL},
+		.model_name = {"DL9040", "DL9040L", "DL9140", "DL9140L",
+				"DL9240", "DL9240L", NULL},
+		.analog_channels = 4,
+		.digital_channels = 0,
+		.pods = 0,
+
+		.analog_names = &scope_analog_channel_names,
+		.digital_names = NULL,
+
+		.coupling_options = &dlm_coupling_options,
+		.trigger_sources = &dlm_4ch_trigger_sources,
+
+		.num_xdivs = 10,
+		.num_ydivs = 8,
+	},
+	{
+		.model_id   = {"701320",  "701321",  NULL},
+		.model_name = {"DL9505L", "DL9510L", NULL},
+		.analog_channels = 4,
+		.digital_channels = 16,
+		.pods = 4,
+
+		.analog_names = &scope_analog_channel_names,
+		.digital_names = &scope_digital_channel_names_32,
+
+		.coupling_options = &dlm_coupling_options,
+		.trigger_sources = &dlm_4ch_trigger_sources,
+
+		.num_xdivs = 10,
+		.num_ydivs = 8,
+	},
+	{
+		.model_id   = {"701330",  "701331",  NULL},
+		.model_name = {"DL9705L", "DL9710L", NULL},
+		.analog_channels = 4,
+		.digital_channels = 32,
+		.pods = 4,
+
+		.analog_names = &scope_analog_channel_names,
+		.digital_names = &scope_digital_channel_names_32,
+
+		.coupling_options = &dlm_coupling_options,
+		.trigger_sources = &dlm_4ch_trigger_sources,
+
+		.num_xdivs = 10,
+		.num_ydivs = 8,
+	},
+};
+
+/**
+ * Prints out the state of the device as we currently know it.
+ *
+ * @param config This is the scope configuration.
+ * @param state The current scope state to print.
+ */
+static void scope_state_dump(const struct scope_config *config,
+		struct scope_state *state)
+{
+	unsigned int i;
+	char *tmp;
+
+	for (i = 0; i < config->analog_channels; i++) {
+		tmp = sr_voltage_string(dlm_vdivs[state->analog_states[i].vdiv][0],
+				dlm_vdivs[state->analog_states[i].vdiv][1]);
+		sr_info("State of analog channel %d -> %s : %s (coupling) %s (vdiv) %2.2e (offset)",
+				i + 1, state->analog_states[i].state ? "On" : "Off",
+				(*config->coupling_options)[state->analog_states[i].coupling],
+				tmp, state->analog_states[i].vertical_offset);
+	}
+
+	for (i = 0; i < config->digital_channels; i++) {
+		sr_info("State of digital channel %d -> %s", i,
+				state->digital_states[i] ? "On" : "Off");
+	}
+
+	for (i = 0; i < config->pods; i++) {
+		sr_info("State of digital POD %d -> %s", i,
+				state->pod_states[i] ? "On" : "Off");
+	}
+
+	tmp = sr_period_string(dlm_timebases[state->timebase][0] *
+			dlm_timebases[state->timebase][1]);
+	sr_info("Current timebase: %s", tmp);
+	g_free(tmp);
+
+	tmp = sr_samplerate_string(state->sample_rate);
+	sr_info("Current samplerate: %s", tmp);
+	g_free(tmp);
+
+	sr_info("Current samples per acquisition (i.e. frame): %d",
+			state->samples_per_frame);
+
+	sr_info("Current trigger: %s (source), %s (slope) %.2f (offset)",
+			(*config->trigger_sources)[state->trigger_source],
+			dlm_trigger_slopes[state->trigger_slope],
+			state->horiz_triggerpos);
+}
+
+/**
+ * Searches through an array of strings and returns the index to the
+ * array where a given string is located.
+ *
+ * @param value The string to search for.
+ * @param array The array of strings.
+ * @param result The index at which value is located in array. -1 on error.
+ *
+ * @return SR_ERR when value couldn't be found, SR_OK otherwise.
+ */
+static int array_option_get(char *value, const char *(*array)[],
+		int *result)
+{
+	unsigned int i;
+
+	*result = -1;
+
+	for (i = 0; (*array)[i]; i++)
+		if (!g_strcmp0(value, (*array)[i])) {
+			*result = i;
+			break;
+		}
+
+	if (*result == -1)
+		return SR_ERR;
+
+	return SR_OK;
+}
+
+/**
+ * This function takes a value of the form "2.000E-03", converts it to a
+ * significand / factor pair and returns the index of an array where
+ * a matching pair was found.
+ *
+ * It's a bit convoluted because of floating-point issues. The value "10.00E-09"
+ * is parsed by g_ascii_strtod() as 0.000000009999999939, for example.
+ * Therefore it's easier to break the number up into two strings and handle
+ * them separately.
+ *
+ * @param value The string to be parsed.
+ * @param array The array of s/f pairs.
+ * @param array_len The number of pairs in the array.
+ * @param result The index at which a matching pair was found.
+ *
+ * @return SR_ERR on any parsing error, SR_OK otherwise.
+ */
+static int array_float_get(gchar *value, const uint64_t array[][2],
+		int array_len, int *result)
+{
+	int i, e;
+	size_t pos;
+	uint64_t f;
+	float s;
+	unsigned int s_int;
+	gchar ss[10], es[10];
+
+	memset(ss, 0, sizeof(ss));
+	memset(es, 0, sizeof(es));
+
+	/* Get index of the separating 'E' character and break up the string. */
+	pos = strcspn(value, "E");
+
+	strncpy(ss, value, pos);
+	strncpy(es, &(value[pos+1]), 3);
+
+	if (sr_atof_ascii(ss, &s) != SR_OK)
+		return SR_ERR;
+	if (sr_atoi(es, &e) != SR_OK)
+		return SR_ERR;
+
+	/* Transform e.g. 10^-03 to 1000 as the array stores the inverse. */
+	f = pow(10, abs(e));
+
+	/*
+	 * Adjust the significand/factor pair to make sure
+	 * that f is a multiple of 1000.
+	 */
+	while ((int)fmod(log10(f), 3) > 0) {
+		s *= 10;
+
+		if (e < 0)
+			f *= 10;
+		else
+			f /= 10;
+	}
+
+	/* Truncate s to circumvent rounding errors. */
+	s_int = (unsigned int)s;
+
+	for (i = 0; i < array_len; i++) {
+		if ((s_int == array[i][0]) && (f == array[i][1])) {
+			*result = i;
+			return SR_OK;
+		}
+	}
+
+	return SR_ERR;
+}
+
+/**
+ * Obtains information about all analog channels from the oscilloscope.
+ * The internal state information is updated accordingly.
+ *
+ * @param sdi The device instance.
+ * @param config The device's device configuration.
+ * @param state The device's state information.
+ *
+ * @return SR_ERR on error, SR_OK otherwise.
+ */
+static int analog_channel_state_get(const struct sr_dev_inst *sdi,
+		const struct scope_config *config,
+		struct scope_state *state)
+{
+	struct sr_scpi_dev_inst *scpi;
+	int i, j;
+	GSList *l;
+	struct sr_channel *ch;
+	gchar *response;
+
+	scpi = sdi->conn;
+
+	for (i = 0; i < config->analog_channels; i++) {
+
+		if (dlm_analog_chan_state_get(scpi, i + 1,
+				&state->analog_states[i].state) != SR_OK)
+			return SR_ERR;
+
+		for (l = sdi->channels; l; l = l->next) {
+			ch = l->data;
+			if (ch->index == i) {
+				ch->enabled = state->analog_states[i].state;
+				break;
+			}
+		}
+
+		if (dlm_analog_chan_vdiv_get(scpi, i + 1, &response) != SR_OK)
+			return SR_ERR;
+
+		if (array_float_get(response, dlm_vdivs, ARRAY_SIZE(dlm_vdivs),
+				&j) != SR_OK) {
+			g_free(response);
+			return SR_ERR;
+		}
+
+		g_free(response);
+		state->analog_states[i].vdiv = j;
+
+		if (dlm_analog_chan_voffs_get(scpi, i + 1,
+				&state->analog_states[i].vertical_offset) != SR_OK)
+			return SR_ERR;
+
+		if (dlm_analog_chan_wrange_get(scpi, i + 1,
+				&state->analog_states[i].waveform_range) != SR_OK)
+			return SR_ERR;
+
+		if (dlm_analog_chan_woffs_get(scpi, i + 1,
+				&state->analog_states[i].waveform_offset) != SR_OK)
+			return SR_ERR;
+
+		if (dlm_analog_chan_coupl_get(scpi, i + 1, &response) != SR_OK) {
+			g_free(response);
+			return SR_ERR;
+		}
+
+		if (array_option_get(response, config->coupling_options,
+				&state->analog_states[i].coupling) != SR_OK) {
+			g_free(response);
+			return SR_ERR;
+		}
+		g_free(response);
+	}
+
+	return SR_OK;
+}
+
+/**
+ * Obtains information about all digital channels from the oscilloscope.
+ * The internal state information is updated accordingly.
+ *
+ * @param sdi The device instance.
+ * @param config The device's device configuration.
+ * @param state The device's state information.
+ *
+ * @return SR_ERR on error, SR_OK otherwise.
+ */
+static int digital_channel_state_get(const struct sr_dev_inst *sdi,
+		const struct scope_config *config,
+		struct scope_state *state)
+{
+	struct sr_scpi_dev_inst *scpi;
+	int i;
+	GSList *l;
+	struct sr_channel *ch;
+
+	scpi = sdi->conn;
+
+	if (!config->digital_channels) {
+		sr_warn("Tried obtaining digital channel states on a " \
+				"model without digital inputs.");
+		return SR_OK;
+	}
+
+	for (i = 0; i < config->digital_channels; i++) {
+		if (dlm_digital_chan_state_get(scpi, i + 1,
+				&state->digital_states[i]) != SR_OK) {
+			return SR_ERR;
+		}
+
+		for (l = sdi->channels; l; l = l->next) {
+			ch = l->data;
+			if (ch->index == i + DLM_DIG_CHAN_INDEX_OFFS) {
+				ch->enabled = state->digital_states[i];
+				break;
+			}
+		}
+	}
+
+	if (!config->pods) {
+		sr_warn("Tried obtaining pod states on a model without pods.");
+		return SR_OK;
+	}
+
+	for (i = 0; i < config->pods; i++) {
+		if (dlm_digital_pod_state_get(scpi, i + 'A',
+				&state->pod_states[i]) != SR_OK)
+			return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV int dlm_channel_state_set(const struct sr_dev_inst *sdi,
+		const int ch_index, gboolean ch_state)
+{
+	GSList *l;
+	struct sr_channel *ch;
+	struct dev_context *devc = NULL;
+	struct scope_state *state;
+	const struct scope_config *model = NULL;
+	struct sr_scpi_dev_inst *scpi;
+	gboolean chan_found;
+	gboolean *pod_enabled;
+	int i, result;
+
+	result = SR_OK;
+
+	scpi = sdi->conn;
+	devc = sdi->priv;
+	state = devc->model_state;
+	model = devc->model_config;
+	chan_found = FALSE;
+
+	pod_enabled = g_malloc0(sizeof(gboolean) * model->pods);
+
+	for (l = sdi->channels; l; l = l->next) {
+		ch = l->data;
+
+		switch (ch->type) {
+		case SR_CHANNEL_ANALOG:
+			if (ch->index == ch_index) {
+				if (dlm_analog_chan_state_set(scpi, ch->index + 1, ch_state) != SR_OK) {
+					result = SR_ERR;
+					break;
+				}
+
+				ch->enabled = ch_state;
+				state->analog_states[ch->index].state = ch_state;
+				chan_found = TRUE;
+				break;
+			}
+			break;
+		case SR_CHANNEL_LOGIC:
+			i = ch->index - DLM_DIG_CHAN_INDEX_OFFS;
+
+			if (ch->index == ch_index) {
+				if (dlm_digital_chan_state_set(scpi, i + 1, ch_state) != SR_OK) {
+					result = SR_ERR;
+					break;
+				}
+
+				ch->enabled = ch_state;
+				state->digital_states[i] = ch_state;
+				chan_found = TRUE;
+
+				/* The corresponding pod has to be enabled also. */
+				pod_enabled[i / 8] |= ch->enabled;
+			} else {
+				/* Also check all other channels. Maybe we can disable a pod. */
+				pod_enabled[i / 8] |= ch->enabled;
+			}
+			break;
+		default:
+			result = SR_ERR_NA;
+		}
+	}
+
+	for (i = 0; i < model->pods; i++) {
+		if (state->pod_states[i] == pod_enabled[i])
+			continue;
+
+		if (dlm_digital_pod_state_set(scpi, i + 1, pod_enabled[i]) != SR_OK) {
+			result = SR_ERR;
+			break;
+		}
+
+		state->pod_states[i] = pod_enabled[i];
+	}
+
+	g_free(pod_enabled);
+
+	if ((result == SR_OK) && !chan_found)
+		result = SR_ERR_BUG;
+
+	return result;
+}
+
+/**
+ * Obtains information about the sample rate from the oscilloscope.
+ * The internal state information is updated accordingly.
+ *
+ * @param sdi The device instance.
+ *
+ * @return SR_ERR on error, SR_OK otherwise.
+ */
+SR_PRIV int dlm_sample_rate_query(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct scope_state *state;
+	float tmp_float;
+
+	devc = sdi->priv;
+	state = devc->model_state;
+
+	/*
+	 * No need to find an active channel to query the sample rate:
+	 * querying any channel will do, so we use channel 1 all the time.
+	 */
+	if (dlm_analog_chan_srate_get(sdi->conn, 1, &tmp_float) != SR_OK)
+		return SR_ERR;
+
+	state->sample_rate = tmp_float;
+
+	return SR_OK;
+}
+
+/**
+ * Obtains information about the current device state from the oscilloscope,
+ * including all analog and digital channel configurations.
+ * The internal state information is updated accordingly.
+ *
+ * @param sdi The device instance.
+ *
+ * @return SR_ERR on error, SR_OK otherwise.
+ */
+SR_PRIV int dlm_scope_state_query(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct scope_state *state;
+	const struct scope_config *config;
+	float tmp_float;
+	gchar *response;
+	int i;
+
+	devc = sdi->priv;
+	config = devc->model_config;
+	state = devc->model_state;
+
+	if (analog_channel_state_get(sdi, config, state) != SR_OK)
+		return SR_ERR;
+
+	if (digital_channel_state_get(sdi, config, state) != SR_OK)
+		return SR_ERR;
+
+	if (dlm_timebase_get(sdi->conn, &response) != SR_OK)
+		return SR_ERR;
+
+	if (array_float_get(response, dlm_timebases,
+			ARRAY_SIZE(dlm_timebases), &i) != SR_OK) {
+		g_free(response);
+		return SR_ERR;
+	}
+
+	g_free(response);
+	state->timebase = i;
+
+	if (dlm_horiz_trigger_pos_get(sdi->conn, &tmp_float) != SR_OK)
+		return SR_ERR;
+
+	/* TODO: Check if the calculation makes sense for the DLM. */
+	state->horiz_triggerpos = tmp_float /
+			(((double)dlm_timebases[state->timebase][0] /
+			dlm_timebases[state->timebase][1]) * config->num_xdivs);
+	state->horiz_triggerpos -= 0.5;
+	state->horiz_triggerpos *= -1;
+
+	if (dlm_trigger_source_get(sdi->conn, &response) != SR_OK) {
+		g_free(response);
+		return SR_ERR;
+	}
+
+	if (array_option_get(response, config->trigger_sources,
+			&state->trigger_source) != SR_OK) {
+		g_free(response);
+		return SR_ERR;
+	}
+
+	g_free(response);
+
+	if (dlm_trigger_slope_get(sdi->conn, &i) != SR_OK)
+		return SR_ERR;
+
+	state->trigger_slope = i;
+
+	if (dlm_acq_length_get(sdi->conn, &state->samples_per_frame) != SR_OK) {
+		sr_err("Failed to query acquisition length.");
+		return SR_ERR;
+	}
+
+	dlm_sample_rate_query(sdi);
+
+	scope_state_dump(config, state);
+
+	return SR_OK;
+}
+
+/**
+ * Creates a new device state structure.
+ *
+ * @param config The device configuration to use.
+ *
+ * @return The newly allocated scope_state struct.
+ */
+static struct scope_state *dlm_scope_state_new(const struct scope_config *config)
+{
+	struct scope_state *state;
+
+	state = g_malloc0(sizeof(struct scope_state));
+
+	state->analog_states = g_malloc0(config->analog_channels *
+			sizeof(struct analog_channel_state));
+
+	state->digital_states = g_malloc0(config->digital_channels *
+			sizeof(gboolean));
+
+	state->pod_states = g_malloc0(config->pods * sizeof(gboolean));
+
+	return state;
+}
+
+/**
+ * Frees the memory that was allocated by a call to dlm_scope_state_new().
+ *
+ * @param state The device state structure whose memory is to be freed.
+ */
+SR_PRIV void dlm_scope_state_destroy(struct scope_state *state)
+{
+	g_free(state->analog_states);
+	g_free(state->digital_states);
+	g_free(state->pod_states);
+	g_free(state);
+}
+
+SR_PRIV int dlm_model_get(char *model_id, char **model_name, int *model_index)
+{
+	unsigned int i, j;
+
+	*model_index = -1;
+	*model_name = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(scope_models); i++) {
+		for (j = 0; scope_models[i].model_id[j]; j++) {
+			if (!strcmp(model_id, scope_models[i].model_id[j])) {
+				*model_index = i;
+				*model_name = (char *)scope_models[i].model_name[j];
+				break;
+			}
+		}
+		if (*model_index != -1)
+			break;
+	}
+
+	if (*model_index == -1) {
+		sr_err("Found unsupported DLM device with model identifier %s.",
+				model_id);
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+/**
+ * Attempts to initialize a DL/DLM device and prepares internal structures
+ * if a suitable device was found.
+ *
+ * @param sdi The device instance.
+ */
+SR_PRIV int dlm_device_init(struct sr_dev_inst *sdi, int model_index)
+{
+	char tmp[25];
+	int i;
+	struct sr_channel *ch;
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+
+	devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) *
+			scope_models[model_index].analog_channels);
+
+	devc->digital_groups = g_malloc0(sizeof(struct sr_channel_group*) *
+			scope_models[model_index].pods);
+
+	/* Add analog channels, each in its own group. */
+	for (i = 0; i < scope_models[model_index].analog_channels; i++) {
+		ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE,
+				(*scope_models[model_index].analog_names)[i]);
+
+		devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
+
+		devc->analog_groups[i]->name = g_strdup(
+				(char *)(*scope_models[model_index].analog_names)[i]);
+		devc->analog_groups[i]->channels = g_slist_append(NULL, ch);
+
+		sdi->channel_groups = g_slist_append(sdi->channel_groups,
+				devc->analog_groups[i]);
+	}
+
+	/* Add digital channel groups. */
+	for (i = 0; i < scope_models[model_index].pods; i++) {
+		g_snprintf(tmp, sizeof(tmp), "POD%d", i);
+
+		devc->digital_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
+		if (!devc->digital_groups[i])
+			return SR_ERR_MALLOC;
+
+		devc->digital_groups[i]->name = g_strdup(tmp);
+		sdi->channel_groups = g_slist_append(sdi->channel_groups,
+				devc->digital_groups[i]);
+	}
+
+	/* Add digital channels. */
+	for (i = 0; i < scope_models[model_index].digital_channels; i++) {
+		ch = sr_channel_new(sdi, DLM_DIG_CHAN_INDEX_OFFS + i,
+				SR_CHANNEL_LOGIC, TRUE,
+				(*scope_models[model_index].digital_names)[i]);
+
+		devc->digital_groups[i / 8]->channels = g_slist_append(
+				devc->digital_groups[i / 8]->channels, ch);
+	}
+	devc->model_config = &scope_models[model_index];
+	devc->frame_limit = 0;
+
+	if (!(devc->model_state = dlm_scope_state_new(devc->model_config)))
+		return SR_ERR_MALLOC;
+
+	/* Disable non-standard response behavior. */
+	if (dlm_response_headers_set(sdi->conn, FALSE) != SR_OK)
+		return SR_ERR;
+
+	return SR_OK;
+}
+
+SR_PRIV int dlm_channel_data_request(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_channel *ch;
+	int result;
+
+	devc = sdi->priv;
+	ch = devc->current_channel->data;
+
+	switch (ch->type) {
+	case SR_CHANNEL_ANALOG:
+		result = dlm_analog_data_get(sdi->conn, ch->index + 1);
+		break;
+	case SR_CHANNEL_LOGIC:
+		result = dlm_digital_data_get(sdi->conn);
+		break;
+	default:
+		sr_err("Invalid channel type encountered (%d).",
+				ch->type);
+		result = SR_ERR;
+	}
+
+	if (result == SR_OK)
+		devc->data_pending = TRUE;
+	else
+		devc->data_pending = FALSE;
+
+	return result;
+}
+
+/**
+ * Reads and removes the block data header from a given data input.
+ * Format is #ndddd... with n being the number of decimal digits d.
+ * The string dddd... contains the decimal-encoded length of the data.
+ * Example: #9000000013 would yield a length of 13 bytes.
+ *
+ * @param data The input data.
+ * @param len The determined input data length.
+ */
+static int dlm_block_data_header_process(GArray *data, int *len)
+{
+	int i, n;
+	gchar s[20];
+
+	if (g_array_index(data, gchar, 0) != '#')
+		return SR_ERR;
+
+	n = (uint8_t)(g_array_index(data, gchar, 1) - '0');
+
+	for (i = 0; i < n; i++)
+		s[i] = g_array_index(data, gchar, 2 + i);
+	s[i] = 0;
+
+	if (sr_atoi(s, len) != SR_OK)
+		return SR_ERR;
+
+	g_array_remove_range(data, 0, 2 + n);
+
+	return SR_OK;
+}
+
+/**
+ * Turns raw sample data into voltages and sends them off to the session bus.
+ *
+ * @param data The raw sample data.
+ * @ch_state Pointer to the state of the channel whose data we're processing.
+ * @sdi The device instance.
+ *
+ * @return SR_ERR when data is trucated, SR_OK otherwise.
+ */
+static int dlm_analog_samples_send(GArray *data,
+		struct analog_channel_state *ch_state,
+		struct sr_dev_inst *sdi)
+{
+	uint32_t i, samples;
+	float voltage, range, offset;
+	GArray *float_data;
+	struct dev_context *devc;
+	struct scope_state *model_state;
+	struct sr_channel *ch;
+	struct sr_datafeed_analog_old analog;
+	struct sr_datafeed_packet packet;
+
+	devc = sdi->priv;
+	model_state = devc->model_state;
+	samples = model_state->samples_per_frame;
+	ch = devc->current_channel->data;
+
+	if (data->len < samples * sizeof(uint8_t)) {
+		sr_err("Truncated waveform data packet received.");
+		return SR_ERR;
+	}
+
+	range = ch_state->waveform_range;
+	offset = ch_state->waveform_offset;
+
+	/*
+	 * Convert byte sample to voltage according to
+	 * page 269 of the Communication Interface User's Manual.
+	 */
+	float_data = g_array_new(FALSE, FALSE, sizeof(float));
+	for (i = 0; i < samples; i++) {
+		voltage = (float)g_array_index(data, int8_t, i);
+		voltage = (range * voltage /
+				DLM_DIVISION_FOR_BYTE_FORMAT) + offset;
+		g_array_append_val(float_data, voltage);
+	}
+
+	analog.channels = g_slist_append(NULL, ch);
+	analog.num_samples = float_data->len;
+	analog.data = (float*)float_data->data;
+	analog.mq = SR_MQ_VOLTAGE;
+	analog.unit = SR_UNIT_VOLT;
+	analog.mqflags = 0;
+	packet.type = SR_DF_ANALOG_OLD;
+	packet.payload = &analog;
+	sr_session_send(sdi, &packet);
+	g_slist_free(analog.channels);
+
+	g_array_free(float_data, TRUE);
+	g_array_remove_range(data, 0, samples * sizeof(uint8_t));
+
+	return SR_OK;
+}
+
+/**
+ * Sends logic sample data off to the session bus.
+ *
+ * @param data The raw sample data.
+ * @ch_state Pointer to the state of the channel whose data we're processing.
+ * @sdi The device instance.
+ *
+ * @return SR_ERR when data is trucated, SR_OK otherwise.
+ */
+static int dlm_digital_samples_send(GArray *data,
+		struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct scope_state *model_state;
+	uint32_t samples;
+	struct sr_datafeed_logic logic;
+	struct sr_datafeed_packet packet;
+
+	devc = sdi->priv;
+	model_state = devc->model_state;
+	samples = model_state->samples_per_frame;
+
+	if (data->len < samples * sizeof(uint8_t)) {
+		sr_err("Truncated waveform data packet received.");
+		return SR_ERR;
+	}
+
+	logic.length = samples;
+	logic.unitsize = 1;
+	logic.data = data->data;
+	packet.type = SR_DF_LOGIC;
+	packet.payload = &logic;
+	sr_session_send(sdi, &packet);
+
+	g_array_remove_range(data, 0, samples * sizeof(uint8_t));
+
+	return SR_OK;
+}
+
+/**
+ * Attempts to query sample data from the oscilloscope in order to send it
+ * to the session bus for further processing.
+ *
+ * @param fd The file descriptor used as the event source.
+ * @param revents The received events.
+ * @param cb_data Callback data, in this case our device instance.
+ *
+ * @return TRUE in case of success or a recoverable error,
+ *         FALSE when a fatal error was encountered.
+ */
+SR_PRIV int dlm_data_receive(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct scope_state *model_state;
+	struct dev_context *devc;
+	struct sr_channel *ch;
+	struct sr_datafeed_packet packet;
+	int chunk_len, num_bytes;
+	static GArray *data = NULL;
+
+	(void)fd;
+	(void)revents;
+
+	if (!(sdi = cb_data))
+		return FALSE;
+
+	if (!(devc = sdi->priv))
+		return FALSE;
+
+	if (!(model_state = (struct scope_state*)devc->model_state))
+		return FALSE;
+
+	/* Are we waiting for a response from the device? */
+	if (!devc->data_pending)
+		return TRUE;
+
+	/* Check if a new query response is coming our way. */
+	if (!data) {
+		if (sr_scpi_read_begin(sdi->conn) == SR_OK)
+			/* The 16 here accounts for the header and EOL. */
+			data = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t),
+					16 + model_state->samples_per_frame);
+		else
+			return TRUE;
+	}
+
+	/* Store incoming data. */
+	chunk_len = sr_scpi_read_data(sdi->conn, devc->receive_buffer,
+			RECEIVE_BUFFER_SIZE);
+	if (chunk_len < 0) {
+		sr_err("Error while reading data: %d", chunk_len);
+		goto fail;
+	}
+	g_array_append_vals(data, devc->receive_buffer, chunk_len);
+
+	/* Read the entire query response before processing. */
+	if (!sr_scpi_read_complete(sdi->conn))
+		return TRUE;
+
+	/* We finished reading and are no longer waiting for data. */
+	devc->data_pending = FALSE;
+
+	/* Signal the beginning of a new frame if this is the first channel. */
+	if (devc->current_channel == devc->enabled_channels) {
+		packet.type = SR_DF_FRAME_BEGIN;
+		sr_session_send(sdi, &packet);
+	}
+
+	if (dlm_block_data_header_process(data, &num_bytes) != SR_OK) {
+		sr_err("Encountered malformed block data header.");
+		goto fail;
+	}
+
+	if (num_bytes == 0) {
+		sr_warn("Zero-length waveform data packet received. " \
+				"Live mode not supported yet, stopping " \
+				"acquisition and retrying.");
+		/* Don't care about return value here. */
+		dlm_acquisition_stop(sdi->conn);
+		g_array_free(data, TRUE);
+		dlm_channel_data_request(sdi);
+		return TRUE;
+	}
+
+	ch = devc->current_channel->data;
+	switch (ch->type) {
+	case SR_CHANNEL_ANALOG:
+		if (dlm_analog_samples_send(data,
+				&model_state->analog_states[ch->index],
+				sdi) != SR_OK)
+			goto fail;
+		break;
+	case SR_CHANNEL_LOGIC:
+		if (dlm_digital_samples_send(data, sdi) != SR_OK)
+			goto fail;
+		break;
+	default:
+		sr_err("Invalid channel type encountered.");
+		break;
+	}
+
+	g_array_free(data, TRUE);
+	data = NULL;
+
+	/*
+	 * Signal the end of this frame if this was the last enabled channel
+	 * and set the next enabled channel. Then, request its data.
+	 */
+	if (!devc->current_channel->next) {
+		packet.type = SR_DF_FRAME_END;
+		sr_session_send(sdi, &packet);
+		devc->current_channel = devc->enabled_channels;
+
+		/*
+		 * As of now we only support importing the current acquisition
+		 * data so we're going to stop at this point.
+		 */
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+		return TRUE;
+	} else
+		devc->current_channel = devc->current_channel->next;
+
+	if (dlm_channel_data_request(sdi) != SR_OK) {
+		sr_err("Failed to request acquisition data.");
+		goto fail;
+	}
+
+	return TRUE;
+
+fail:
+	if (data) {
+		g_array_free(data, TRUE);
+		data = NULL;
+	}
+
+	return FALSE;
+}
diff --git a/src/hardware/yokogawa-dlm/protocol.h b/src/hardware/yokogawa-dlm/protocol.h
new file mode 100644
index 0000000..80def3f
--- /dev/null
+++ b/src/hardware/yokogawa-dlm/protocol.h
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 abraxa (Soeren Apel) <soeren at apelpie.net>
+ * Based on the Hameg HMO driver by poljar (Damir Jelić) <poljarinho at 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_YOKOGAWA_DLM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_YOKOGAWA_DLM_PROTOCOL_H
+
+#include <glib.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol_wrappers.h"
+
+#define LOG_PREFIX "yokogawa-dlm"
+#define MAX_INSTRUMENT_VERSIONS 8
+
+#define RECEIVE_BUFFER_SIZE 4096
+
+/* See Communication Interface User's Manual on p. 268 (:WAVeform:ALL:SEND?). */
+#define DLM_MAX_FRAME_LENGTH 12500
+/* See Communication Interface User's Manual on p. 269 (:WAVeform:SEND?). */
+#define DLM_DIVISION_FOR_WORD_FORMAT 3200
+#define DLM_DIVISION_FOR_BYTE_FORMAT 12.5
+
+#define DLM_DIG_CHAN_INDEX_OFFS 32
+
+enum trigger_slopes {
+	SLOPE_POSITIVE,
+	SLOPE_NEGATIVE
+};
+
+extern const char *dlm_trigger_slopes[3];
+extern const uint64_t dlm_timebases[36][2];
+extern const uint64_t dlm_vdivs[17][2];
+
+struct scope_config {
+	const char *model_id[MAX_INSTRUMENT_VERSIONS];
+	const char *model_name[MAX_INSTRUMENT_VERSIONS];
+	const uint8_t analog_channels;
+	const uint8_t digital_channels;
+	const uint8_t pods;
+
+	const char *(*analog_names)[];
+	const char *(*digital_names)[];
+
+	const char *(*coupling_options)[];
+	const uint8_t num_coupling_options;
+
+	const char *(*trigger_sources)[];
+	const uint8_t num_trigger_sources;
+
+	const uint8_t num_xdivs;
+	const uint8_t num_ydivs;
+};
+
+struct analog_channel_state {
+	int coupling;
+
+	int vdiv;
+	float vertical_offset, waveform_range, waveform_offset;
+
+	gboolean state;
+};
+
+struct scope_state {
+	struct analog_channel_state *analog_states;
+	gboolean *digital_states;
+	gboolean *pod_states;
+
+	int timebase;
+	float horiz_triggerpos;
+
+	int trigger_source;
+	int trigger_slope;
+	uint64_t sample_rate;
+	uint32_t samples_per_frame;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	const void *model_config;
+	void *model_state;
+
+	struct sr_channel_group **analog_groups;
+	struct sr_channel_group **digital_groups;
+
+	GSList *enabled_channels;
+	GSList *current_channel;
+	uint64_t num_frames;
+
+	uint64_t frame_limit;
+
+	char receive_buffer[RECEIVE_BUFFER_SIZE];
+	gboolean data_pending;
+};
+
+SR_PRIV int dlm_channel_state_set(const struct sr_dev_inst *sdi,
+		const int ch_index, gboolean state);
+SR_PRIV int dlm_data_request(const struct sr_dev_inst *sdi);
+SR_PRIV int dlm_model_get(char *model_id, char **model_name, int *model_index);
+SR_PRIV int dlm_device_init(struct sr_dev_inst *sdi, int model_index);
+SR_PRIV int dlm_data_receive(int fd, int revents, void *cb_data);
+SR_PRIV void dlm_scope_state_destroy(struct scope_state *state);
+SR_PRIV int dlm_scope_state_query(struct sr_dev_inst *sdi);
+SR_PRIV int dlm_sample_rate_query(const struct sr_dev_inst *sdi);
+SR_PRIV int dlm_channel_data_request(const struct sr_dev_inst *sdi);
+
+#endif
diff --git a/src/hardware/yokogawa-dlm/protocol_wrappers.c b/src/hardware/yokogawa-dlm/protocol_wrappers.c
new file mode 100644
index 0000000..e2a05be
--- /dev/null
+++ b/src/hardware/yokogawa-dlm/protocol_wrappers.c
@@ -0,0 +1,359 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 abraxa (Soeren Apel) <soeren at apelpie.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol_wrappers.h"
+
+#define MAX_COMMAND_SIZE 64
+
+/*
+ * DLM2000 comm spec:
+ * https://www.yokogawa.com/pdf/provide/E/GW/IM/0000022842/0/IM710105-17E.pdf
+ */
+
+int dlm_timebase_get(struct sr_scpi_dev_inst *scpi,
+		gchar **response)
+{
+	return sr_scpi_get_string(scpi, ":TIMEBASE:TDIV?", response);
+}
+
+int dlm_timebase_set(struct sr_scpi_dev_inst *scpi,
+		const gchar *value)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":TIMEBASE:TDIV %s", value);
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_horiz_trigger_pos_get(struct sr_scpi_dev_inst *scpi,
+		float *response)
+{
+	return sr_scpi_get_float(scpi, ":TRIGGER:DELAY:TIME?", response);
+}
+
+int dlm_horiz_trigger_pos_set(struct sr_scpi_dev_inst *scpi,
+		const gchar *value)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":TRIGGER:DELAY:TIME %s", value);
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_trigger_source_get(struct sr_scpi_dev_inst *scpi,
+		gchar **response)
+{
+	return sr_scpi_get_string(scpi, ":TRIGGER:ATRIGGER:SIMPLE:SOURCE?", response);
+}
+
+int dlm_trigger_source_set(struct sr_scpi_dev_inst *scpi,
+		const gchar *value)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":TRIGGER:ATRIGGER:SIMPLE:SOURCE %s", value);
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_trigger_slope_get(struct sr_scpi_dev_inst *scpi,
+		int *response)
+{
+	gchar *resp;
+	int result;
+
+	result = SR_ERR;
+
+	if (sr_scpi_get_string(scpi, ":TRIGGER:ATRIGGER:SIMPLE:SLOPE?", &resp) != SR_OK) {
+		g_free(resp);
+		return SR_ERR;
+	}
+
+	if (strcmp("RISE", resp) == 0) {
+		*response = SLOPE_POSITIVE;
+		result = SR_OK;
+	}
+
+	if (strcmp("FALL", resp) == 0) {
+		*response = SLOPE_NEGATIVE;
+		result = SR_OK;
+	}
+
+	g_free(resp);
+
+	return result;
+}
+
+int dlm_trigger_slope_set(struct sr_scpi_dev_inst *scpi,
+		const int value)
+{
+	if (value == SLOPE_POSITIVE)
+		return sr_scpi_send(scpi, ":TRIGGER:ATRIGGER:SIMPLE:SLOPE RISE");
+
+	if (value == SLOPE_NEGATIVE)
+		return sr_scpi_send(scpi, ":TRIGGER:ATRIGGER:SIMPLE:SLOPE FALL");
+
+	return SR_ERR_ARG;
+}
+
+int dlm_analog_chan_state_get(struct sr_scpi_dev_inst *scpi, int channel,
+		gboolean *response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":CHANNEL%d:DISPLAY?", channel);
+	return sr_scpi_get_bool(scpi, cmd, response);
+}
+
+int dlm_analog_chan_state_set(struct sr_scpi_dev_inst *scpi, int channel,
+		const gboolean value)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+
+	if (value)
+		g_snprintf(cmd, sizeof(cmd), ":CHANNEL%d:DISPLAY ON", channel);
+	else
+		g_snprintf(cmd, sizeof(cmd), ":CHANNEL%d:DISPLAY OFF", channel);
+
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_analog_chan_vdiv_get(struct sr_scpi_dev_inst *scpi, int channel,
+		gchar **response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":CHANNEL%d:VDIV?", channel);
+	return sr_scpi_get_string(scpi, cmd, response);
+}
+
+int dlm_analog_chan_vdiv_set(struct sr_scpi_dev_inst *scpi, int channel,
+		const gchar *value)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":CHANNEL%d:VDIV %s", channel, value);
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_analog_chan_voffs_get(struct sr_scpi_dev_inst *scpi, int channel,
+		float *response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":CHANNEL%d:POSITION?", channel);
+	return sr_scpi_get_float(scpi, cmd, response);
+}
+
+int dlm_analog_chan_srate_get(struct sr_scpi_dev_inst *scpi, int channel,
+		float *response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":WAVEFORM:TRACE %d", channel);
+
+	if (sr_scpi_send(scpi, cmd) != SR_OK)
+		return SR_ERR;
+
+	g_snprintf(cmd, sizeof(cmd), ":WAVEFORM:RECORD 0");
+	if (sr_scpi_send(scpi, cmd) != SR_OK)
+		return SR_ERR;
+
+	return sr_scpi_get_float(scpi, ":WAVEFORM:SRATE?", response);
+}
+
+int dlm_analog_chan_coupl_get(struct sr_scpi_dev_inst *scpi, int channel,
+		gchar **response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":CHANNEL%d:COUPLING?", channel);
+	return sr_scpi_get_string(scpi, cmd, response);
+}
+
+int dlm_analog_chan_coupl_set(struct sr_scpi_dev_inst *scpi, int channel,
+		const gchar *value)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":CHANNEL%d:COUPLING %s", channel, value);
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_analog_chan_wrange_get(struct sr_scpi_dev_inst *scpi, int channel,
+		float *response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	int result;
+
+	g_snprintf(cmd, sizeof(cmd), ":WAVEFORM:TRACE %d", channel);
+	result = sr_scpi_send(scpi, cmd);
+	result &= sr_scpi_get_float(scpi, ":WAVEFORM:RANGE?", response);
+	return result;
+}
+
+int dlm_analog_chan_woffs_get(struct sr_scpi_dev_inst *scpi, int channel,
+		float *response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	int result;
+
+	g_snprintf(cmd, sizeof(cmd), ":WAVEFORM:TRACE %d", channel);
+	result = sr_scpi_send(scpi, cmd);
+	result &= sr_scpi_get_float(scpi, ":WAVEFORM:OFFSET?", response);
+	return result;
+}
+
+int dlm_digital_chan_state_get(struct sr_scpi_dev_inst *scpi, int channel,
+		gboolean *response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	g_snprintf(cmd, sizeof(cmd), ":LOGIC:PODA:BIT%d:DISPLAY?", channel);
+	return sr_scpi_get_bool(scpi, cmd, response);
+}
+
+int dlm_digital_chan_state_set(struct sr_scpi_dev_inst *scpi, int channel,
+		const gboolean value)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+
+	if (value)
+		g_snprintf(cmd, sizeof(cmd), ":LOGIC:PODA:BIT%d:DISPLAY ON", channel);
+	else
+		g_snprintf(cmd, sizeof(cmd), ":LOGIC:PODA:BIT%d:DISPLAY OFF", channel);
+
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_digital_pod_state_get(struct sr_scpi_dev_inst *scpi, int pod,
+		gboolean *response)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+
+	/* TODO: pod currently ignored as DLM2000 only has pod A. */
+	(void)pod;
+
+	g_snprintf(cmd, sizeof(cmd), ":LOGIC:MODE?");
+	return sr_scpi_get_bool(scpi, cmd, response);
+}
+
+int dlm_digital_pod_state_set(struct sr_scpi_dev_inst *scpi, int pod,
+		const gboolean value)
+{
+	/* TODO: pod currently ignored as DLM2000 only has pod A. */
+	(void)pod;
+
+	if (value)
+		return sr_scpi_send(scpi, ":LOGIC:MODE ON");
+	else
+		return sr_scpi_send(scpi, ":LOGIC:MODE OFF");
+}
+
+int dlm_response_headers_set(struct sr_scpi_dev_inst *scpi,
+		const gboolean value)
+{
+	if (value)
+		return sr_scpi_send(scpi, ":COMMUNICATE:HEADER ON");
+	else
+		return sr_scpi_send(scpi, ":COMMUNICATE:HEADER OFF");
+}
+
+int dlm_acquisition_stop(struct sr_scpi_dev_inst *scpi)
+{
+	return sr_scpi_send(scpi, ":STOP");
+}
+
+int dlm_acq_length_get(struct sr_scpi_dev_inst *scpi,
+		uint32_t *response)
+{
+	int ret;
+	char *s;
+	long tmp;
+
+	if (sr_scpi_get_string(scpi, ":WAVEFORM:LENGTH?", &s) != SR_OK)
+		if (!s)
+			return SR_ERR;
+
+	if (sr_atol(s, &tmp) == SR_OK)
+		ret = SR_OK;
+	else
+		ret = SR_ERR;
+
+	g_free(s);
+	*response = tmp;
+
+	return ret;
+}
+
+int dlm_chunks_per_acq_get(struct sr_scpi_dev_inst *scpi, int *response)
+{
+	int result, acq_len;
+
+	/*
+	 * Data retrieval queries such as :WAVEFORM:SEND? will only return
+	 * up to 12500 samples at a time. If the oscilloscope operates in a
+	 * mode where more than 12500 samples fit on screen (i.e. in one
+	 * acquisition), data needs to be retrieved multiple times.
+	 */
+
+	result = sr_scpi_get_int(scpi, ":WAVEFORM:LENGTH?", &acq_len);
+	*response = MAX(acq_len / DLM_MAX_FRAME_LENGTH, 1);
+
+	return result;
+}
+
+int dlm_start_frame_set(struct sr_scpi_dev_inst *scpi, int value)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+
+	g_snprintf(cmd, sizeof(cmd), ":WAVEFORM:START %d",
+			value * DLM_MAX_FRAME_LENGTH);
+
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_data_get(struct sr_scpi_dev_inst *scpi, int acquisition_num)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+
+	g_snprintf(cmd, sizeof(cmd), ":WAVEFORM:ALL:SEND? %d", acquisition_num);
+	return sr_scpi_send(scpi, cmd);
+}
+
+int dlm_analog_data_get(struct sr_scpi_dev_inst *scpi, int channel)
+{
+	gchar cmd[MAX_COMMAND_SIZE];
+	int result;
+
+	result = sr_scpi_send(scpi, ":WAVEFORM:FORMAT BYTE");
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:RECORD 0");
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:START 0");
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:END 124999999");
+
+	g_snprintf(cmd, sizeof(cmd), ":WAVEFORM:TRACE %d", channel);
+	if (result == SR_OK) result = sr_scpi_send(scpi, cmd);
+
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:SEND? 1");
+
+	return result;
+}
+
+int dlm_digital_data_get(struct sr_scpi_dev_inst *scpi)
+{
+	int result;
+
+	result = sr_scpi_send(scpi, ":WAVEFORM:FORMAT BYTE");
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:RECORD 0");
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:START 0");
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:END 124999999");
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:TRACE LOGIC");
+	if (result == SR_OK) result = sr_scpi_send(scpi, ":WAVEFORM:SEND? 1");
+
+	return result;
+}
diff --git a/src/hardware/yokogawa-dlm/protocol_wrappers.h b/src/hardware/yokogawa-dlm/protocol_wrappers.h
new file mode 100644
index 0000000..445ce97
--- /dev/null
+++ b/src/hardware/yokogawa-dlm/protocol_wrappers.h
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 abraxa (Soeren Apel) <soeren at apelpie.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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_YOKOGAWA_DLM_PROTOCOL_WRAPPERS_H
+#define LIBSIGROK_HARDWARE_YOKOGAWA_DLM_PROTOCOL_WRAPPERS_H
+
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+#include "protocol.h"
+
+extern int dlm_timebase_get(struct sr_scpi_dev_inst *scpi,
+		gchar **response);
+extern int dlm_timebase_set(struct sr_scpi_dev_inst *scpi,
+		const gchar *value);
+extern int dlm_horiz_trigger_pos_get(struct sr_scpi_dev_inst *scpi,
+		float *response);
+extern int dlm_horiz_trigger_pos_set(struct sr_scpi_dev_inst *scpi,
+		const gchar *value);
+extern int dlm_trigger_source_get(struct sr_scpi_dev_inst *scpi,
+		gchar **response);
+extern int dlm_trigger_source_set(struct sr_scpi_dev_inst *scpi,
+		const gchar *value);
+extern int dlm_trigger_slope_get(struct sr_scpi_dev_inst *scpi,
+		int *value);
+extern int dlm_trigger_slope_set(struct sr_scpi_dev_inst *scpi,
+		const int value);
+
+extern int dlm_analog_chan_state_get(struct sr_scpi_dev_inst *scpi, int channel,
+		gboolean *response);
+extern int dlm_analog_chan_state_set(struct sr_scpi_dev_inst *scpi, int channel,
+		const gboolean value);
+extern int dlm_analog_chan_vdiv_get(struct sr_scpi_dev_inst *scpi, int channel,
+		gchar **response);
+extern int dlm_analog_chan_vdiv_set(struct sr_scpi_dev_inst *scpi, int channel,
+		const gchar *value);
+extern int dlm_analog_chan_voffs_get(struct sr_scpi_dev_inst *scpi, int channel,
+		float *response);
+extern int dlm_analog_chan_srate_get(struct sr_scpi_dev_inst *scpi, int channel,
+		float *response);
+extern int dlm_analog_chan_coupl_get(struct sr_scpi_dev_inst *scpi, int channel,
+		gchar **response);
+extern int dlm_analog_chan_coupl_set(struct sr_scpi_dev_inst *scpi, int channel,
+		const gchar *value);
+extern int dlm_analog_chan_wrange_get(struct sr_scpi_dev_inst *scpi, int channel,
+		float *response);
+extern int dlm_analog_chan_woffs_get(struct sr_scpi_dev_inst *scpi, int channel,
+		float *response);
+
+extern int dlm_digital_chan_state_get(struct sr_scpi_dev_inst *scpi, int channel,
+		gboolean *response);
+extern int dlm_digital_chan_state_set(struct sr_scpi_dev_inst *scpi, int channel,
+		const gboolean value);
+extern int dlm_digital_pod_state_get(struct sr_scpi_dev_inst *scpi, int pod,
+		gboolean *response);
+extern int dlm_digital_pod_state_set(struct sr_scpi_dev_inst *scpi, int pod,
+		const gboolean value);
+
+extern int dlm_response_headers_set(struct sr_scpi_dev_inst *scpi,
+		const gboolean value);
+extern int dlm_acquisition_stop(struct sr_scpi_dev_inst *scpi);
+
+extern int dlm_acq_length_get(struct sr_scpi_dev_inst *scpi,
+		uint32_t *response);
+extern int dlm_chunks_per_acq_get(struct sr_scpi_dev_inst *scpi,
+		int *response);
+extern int dlm_start_frame_set(struct sr_scpi_dev_inst *scpi, int value);
+extern int dlm_data_get(struct sr_scpi_dev_inst *scpi, int acquisition_num);
+extern int dlm_analog_data_get(struct sr_scpi_dev_inst *scpi, int channel);
+extern int dlm_digital_data_get(struct sr_scpi_dev_inst *scpi);
+
+#endif
diff --git a/hardware/zeroplus-logic-cube/analyzer.c b/src/hardware/zeroplus-logic-cube/analyzer.c
similarity index 93%
rename from hardware/zeroplus-logic-cube/analyzer.c
rename to src/hardware/zeroplus-logic-cube/analyzer.c
index 47dcfc8..aa5643a 100644
--- a/hardware/zeroplus-logic-cube/analyzer.c
+++ b/src/hardware/zeroplus-logic-cube/analyzer.c
@@ -29,8 +29,9 @@
  *  THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <config.h>
 #include <assert.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "analyzer.h"
 #include "gl_usb.h"
@@ -309,7 +310,7 @@ static int __analyzer_set_freq(libusb_device_handle *devh, int freq, int scale)
 		{   1, FREQ_SCALE_KHZ, 64,  5,  5 },
 		{ 500, FREQ_SCALE_HZ,  64, 10,  5 },
 		{ 100, FREQ_SCALE_HZ,  68,  5,  8 },
-		{   0, 0,              0,   0,  0 }
+		ALL_ZERO
 	};
 
 	int i;
@@ -488,29 +489,44 @@ SR_PRIV void analyzer_configure(libusb_device_handle *devh)
 	__analyzer_set_compression(devh, g_compression);
 }
 
-SR_PRIV void analyzer_add_trigger(int channel, int type)
-{
-	switch (type) {
-	case TRIGGER_HIGH:
-		g_trigger_status[channel / 4] |= 1 << (channel % 4 * 2);
-		break;
-	case TRIGGER_LOW:
-		g_trigger_status[channel / 4] |= 2 << (channel % 4 * 2);
-		break;
-#if 0
-	case TRIGGER_POSEDGE:
-		g_trigger_status[8] = 0x40 | channel;
-		break;
-	case TRIGGER_NEGEDGE:
-		g_trigger_status[8] = 0x80 | channel;
-		break;
-	case TRIGGER_ANYEDGE:
-		g_trigger_status[8] = 0xc0 | channel;
-		break;
-#endif
-	default:
-		break;
+SR_PRIV int analyzer_add_triggers(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	struct sr_trigger *trigger;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	GSList *l, *m;
+	int channel;
+
+	devc = sdi->priv;
+
+	if (!(trigger = sr_session_trigger_get(sdi->session)))
+		return SR_OK;
+
+	for (l = trigger->stages; l; l = l->next) {
+		stage = l->data;
+		for (m = stage->matches; m; m = m->next) {
+			match = m->data;
+			devc->trigger = 1;
+			if (!match->channel->enabled)
+				/* Ignore disabled channels with a trigger. */
+				continue;
+			channel = match->channel->index;
+			switch (match->match) {
+			case SR_TRIGGER_ZERO:
+				g_trigger_status[channel / 4] |= 2 << (channel % 4 * 2);
+				break;
+			case SR_TRIGGER_ONE:
+				g_trigger_status[channel / 4] |= 1 << (channel % 4 * 2);
+				break;
+			default:
+				sr_err("Unsupported match %d", match->match);
+				return SR_ERR;
+			}
+		}
 	}
+
+	return SR_OK;
 }
 
 SR_PRIV void analyzer_add_filter(int channel, int type)
diff --git a/hardware/zeroplus-logic-cube/analyzer.h b/src/hardware/zeroplus-logic-cube/analyzer.h
similarity index 96%
rename from hardware/zeroplus-logic-cube/analyzer.h
rename to src/hardware/zeroplus-logic-cube/analyzer.h
index f0bb799..41a1eed 100644
--- a/hardware/zeroplus-logic-cube/analyzer.h
+++ b/src/hardware/zeroplus-logic-cube/analyzer.h
@@ -33,7 +33,7 @@
 #define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_ANALYZER_H
 
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 
 #define STATUS_FLAG_NONE	0x00
 #define STATUS_FLAG_RESET	0x01
@@ -74,14 +74,6 @@
 #define COMPRESSION_ENABLE	0x8001
 #define COMPRESSION_DOUBLE	0x8002
 
-enum {
-	TRIGGER_HIGH = 0,
-	TRIGGER_LOW,
-	TRIGGER_POSEDGE,
-	TRIGGER_NEGEDGE,
-	TRIGGER_ANYEDGE,
-};
-
 SR_PRIV void analyzer_set_freq(int freq, int scale);
 SR_PRIV void analyzer_set_ramsize_trigger_address(unsigned int address);
 SR_PRIV void analyzer_set_triggerbar_address(unsigned int address);
@@ -89,7 +81,7 @@ SR_PRIV unsigned int  analyzer_get_ramsize_trigger_address(void );
 SR_PRIV unsigned int analyzer_get_triggerbar_address(void);
 SR_PRIV void analyzer_set_compression(unsigned int type);
 SR_PRIV void analyzer_set_memory_size(unsigned int size);
-SR_PRIV void analyzer_add_trigger(int channel, int type);
+SR_PRIV int analyzer_add_triggers(const struct sr_dev_inst *sdi);
 SR_PRIV void analyzer_set_trigger_count(int count);
 SR_PRIV void analyzer_add_filter(int channel, int type);
 SR_PRIV void analyzer_set_voltage_threshold(int thresh);
diff --git a/hardware/zeroplus-logic-cube/api.c b/src/hardware/zeroplus-logic-cube/api.c
similarity index 77%
rename from hardware/zeroplus-logic-cube/api.c
rename to src/hardware/zeroplus-logic-cube/api.c
index 34213e7..657e5eb 100644
--- a/hardware/zeroplus-logic-cube/api.c
+++ b/src/hardware/zeroplus-logic-cube/api.c
@@ -17,13 +17,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include "protocol.h"
 
 #define VENDOR_NAME			"ZEROPLUS"
 #define USB_INTERFACE			0
 #define USB_CONFIGURATION		1
 #define NUM_TRIGGER_STAGES		4
-#define TRIGGER_TYPE 			"01"
 #define PACKET_SIZE			2048	/* ?? */
 
 //#define ZP_EXPERIMENTAL
@@ -31,7 +31,7 @@
 struct zp_model {
 	uint16_t vid;
 	uint16_t pid;
-	char *model_name;
+	const char *model_name;
 	unsigned int channels;
 	unsigned int sample_depth;	/* In Ksamples/channel */
 	unsigned int max_sampling_freq;
@@ -50,16 +50,22 @@ static const struct zp_model zeroplus_models[] = {
 	{0x0c12, 0x700d, "LAP-C(322000)", 32, 2048, 200},
 	{0x0c12, 0x700e, "LAP-C(16032)",  16, 32,   100},
 	{0x0c12, 0x7016, "LAP-C(162000)", 16, 2048, 200},
-	{ 0, 0, 0, 0, 0, 0 }
+	{0x0c12, 0x7100, "AKIP-9101", 16, 256, 200},
+	ALL_ZERO
 };
 
-static const int32_t hwcaps[] = {
+static const uint32_t devopts[] = {
 	SR_CONF_LOGIC_ANALYZER,
-	SR_CONF_SAMPLERATE,
-	SR_CONF_TRIGGER_TYPE,
-	SR_CONF_CAPTURE_RATIO,
-	SR_CONF_VOLTAGE_THRESHOLD,
-	SR_CONF_LIMIT_SAMPLES,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_ZERO,
+	SR_TRIGGER_ONE,
 };
 
 /*
@@ -71,11 +77,9 @@ static const char *channel_names[] = {
 	"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7",
 	"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7",
 	"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
-	NULL,
 };
 
 SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info;
-static struct sr_dev_driver *di = &zeroplus_logic_cube_driver_info;
 
 /*
  * The hardware supports more samplerates than these, but these are the
@@ -124,95 +128,6 @@ const uint64_t samplerates_200[] = {
 
 static int dev_close(struct sr_dev_inst *sdi);
 
-#if 0
-static int configure_channels(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	const struct sr_channel *ch;
-	const GSList *l;
-	int channel_bit, stage, i;
-	char *tc;
-
-	/* Note: sdi and sdi->priv are non-NULL, the caller checked this. */
-	devc = sdi->priv;
-
-	devc->channel_mask = 0;
-	for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
-		devc->trigger_mask[i] = 0;
-		devc->trigger_value[i] = 0;
-	}
-
-	stage = -1;
-	for (l = sdi->channels; l; l = l->next) {
-		ch = (struct sr_channel *)l->data;
-		if (ch->enabled == FALSE)
-			continue;
-		channel_bit = 1 << (ch->index);
-		devc->channel_mask |= channel_bit;
-
-		if (ch->trigger) {
-			stage = 0;
-			for (tc = ch->trigger; *tc; tc++) {
-				devc->trigger_mask[stage] |= channel_bit;
-				if (*tc == '1')
-					devc->trigger_value[stage] |= channel_bit;
-				stage++;
-				if (stage > NUM_TRIGGER_STAGES)
-					return SR_ERR;
-			}
-		}
-	}
-
-	return SR_OK;
-}
-#endif
-
-static int configure_channels(const struct sr_dev_inst *sdi)
-{
-	struct dev_context *devc;
-	const GSList *l;
-	const struct sr_channel *ch;
-	char *tc;
-	int type;
-
-	/* Note: sdi and sdi->priv are non-NULL, the caller checked this. */
-	devc = sdi->priv;
-
-	for (l = sdi->channels; l; l = l->next) {
-		ch = (struct sr_channel *)l->data;
-		if (ch->enabled == FALSE)
-			continue;
-
-		if ((tc = ch->trigger)) {
-			switch (*tc) {
-			case '1':
-				type = TRIGGER_HIGH;
-				break;
-			case '0':
-				type = TRIGGER_LOW;
-				break;
-#if 0
-			case 'r':
-				type = TRIGGER_POSEDGE;
-				break;
-			case 'f':
-				type = TRIGGER_NEGEDGE;
-				break;
-			case 'c':
-				type = TRIGGER_ANYEDGE;
-				break;
-#endif
-			default:
-				return SR_ERR;
-			}
-			analyzer_add_trigger(ch->index, type);
-			devc->trigger = 1;
-		}
-	}
-
-	return SR_OK;
-}
-
 SR_PRIV int zp_set_samplerate(struct dev_context *devc, uint64_t samplerate)
 {
 	int i;
@@ -240,41 +155,53 @@ SR_PRIV int zp_set_samplerate(struct dev_context *devc, uint64_t samplerate)
 	return SR_OK;
 }
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
 	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
 	struct sr_dev_inst *sdi;
-	struct sr_channel *ch;
 	struct drv_context *drvc;
 	struct dev_context *devc;
 	const struct zp_model *prof;
 	struct libusb_device_descriptor des;
+	struct libusb_device_handle *hdl;
 	libusb_device **devlist;
 	GSList *devices;
-	int ret, devcnt, i, j;
+	int ret, i, j;
+	char serial_num[64], connection_id[64];
 
 	(void)options;
 
-	drvc = di->priv;
+	drvc = di->context;
 
 	devices = NULL;
 
 	/* Find all ZEROPLUS analyzers and add them to device list. */
-	devcnt = 0;
 	libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); /* TODO: Errors. */
 
 	for (i = 0; devlist[i]; i++) {
-		ret = libusb_get_device_descriptor(devlist[i], &des);
-		if (ret != 0) {
-			sr_err("Failed to get device descriptor: %s.",
-			       libusb_error_name(ret));
+		libusb_get_device_descriptor(devlist[i], &des);
+
+		if ((ret = libusb_open(devlist[i], &hdl)) < 0)
+			continue;
+
+		if (des.iSerialNumber == 0) {
+			serial_num[0] = '\0';
+		} else if ((ret = libusb_get_string_descriptor_ascii(hdl,
+				des.iSerialNumber, (unsigned char *) serial_num,
+				sizeof(serial_num))) < 0) {
+			sr_warn("Failed to get serial number string descriptor: %s.",
+				libusb_error_name(ret));
 			continue;
 		}
 
+		libusb_close(hdl);
+
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+
 		prof = NULL;
 		for (j = 0; j < zeroplus_models[j].vid; j++) {
 			if (des.idVendor == zeroplus_models[j].vid &&
@@ -288,19 +215,16 @@ static GSList *scan(GSList *options)
 		sr_info("Found ZEROPLUS %s.", prof->model_name);
 
 		/* Register the device with libsigrok. */
-		if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE,
-				VENDOR_NAME, prof->model_name, NULL))) {
-			sr_err("%s: sr_dev_inst_new failed", __func__);
-			return NULL;
-		}
+		sdi = g_malloc0(sizeof(struct sr_dev_inst));
+		sdi->status = SR_ST_INACTIVE;
+		sdi->vendor = g_strdup(VENDOR_NAME);
+		sdi->model = g_strdup(prof->model_name);
 		sdi->driver = di;
+		sdi->serial_num = g_strdup(serial_num);
+		sdi->connection_id = g_strdup(connection_id);
 
 		/* Allocate memory for our private driver context. */
-		if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
-			sr_err("Device context malloc failed.");
-			return NULL;
-		}
-
+		devc = g_malloc0(sizeof(struct dev_context));
 		sdi->priv = devc;
 		devc->prof = prof;
 		devc->num_channels = prof->channels;
@@ -316,12 +240,9 @@ static GSList *scan(GSList *options)
 		// memset(devc->trigger_buffer, 0, NUM_TRIGGER_STAGES);
 
 		/* Fill in channellist according to this device's profile. */
-		for (j = 0; j < devc->num_channels; j++) {
-			if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
-					channel_names[j])))
-				return NULL;
-			sdi->channels = g_slist_append(sdi->channels, ch);
-		}
+		for (j = 0; j < devc->num_channels; j++)
+			sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE,
+					channel_names[j]);
 
 		devices = g_slist_append(devices, sdi);
 		drvc->instances = g_slist_append(drvc->instances, sdi);
@@ -329,29 +250,28 @@ static GSList *scan(GSList *options)
 		sdi->conn = sr_usb_dev_inst_new(
 			libusb_get_bus_number(devlist[i]),
 			libusb_get_device_address(devlist[i]), NULL);
-		devcnt++;
-
 	}
 	libusb_free_device_list(devlist, 1);
 
 	return devices;
 }
 
-static GSList *dev_list(void)
+static GSList *dev_list(const struct sr_dev_driver *di)
 {
-	return ((struct drv_context *)(di->priv))->instances;
+	return ((struct drv_context *)(di->context))->instances;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di = sdi->driver;
 	struct dev_context *devc;
 	struct drv_context *drvc;
 	struct sr_usb_dev_inst *usb;
 	libusb_device **devlist, *dev;
-	struct libusb_device_descriptor des;
 	int device_count, ret, i;
+	char connection_id[64];
 
-	drvc = di->priv;
+	drvc = di->context;
 	usb = sdi->conn;
 
 	if (!(devc = sdi->priv)) {
@@ -368,27 +288,22 @@ static int dev_open(struct sr_dev_inst *sdi)
 
 	dev = NULL;
 	for (i = 0; i < device_count; i++) {
-		if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
-			sr_err("Failed to get device descriptor: %s.",
-			       libusb_error_name(ret));
-			continue;
-		}
-		if (libusb_get_bus_number(devlist[i]) == usb->bus
-		    && libusb_get_device_address(devlist[i]) == usb->address) {
+		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+		if (!strcmp(sdi->connection_id, connection_id)) {
 			dev = devlist[i];
 			break;
 		}
 	}
 	if (!dev) {
-		sr_err("Device on bus %d address %d disappeared!",
-		       usb->bus, usb->address);
+		sr_err("Device on %d.%d (logical) / %s (physical) disappeared!",
+		       usb->bus, usb->address, sdi->connection_id);
 		return SR_ERR;
 	}
 
 	if (!(ret = libusb_open(dev, &(usb->devhdl)))) {
 		sdi->status = SR_ST_ACTIVE;
-		sr_info("Opened device %d on %d.%d interface %d.",
-			sdi->index, usb->bus, usb->address, USB_INTERFACE);
+		sr_info("Opened device on %d.%d (logical) / %s (physical) interface %d.",
+			usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
 	} else {
 		sr_err("Failed to open device: %s.", libusb_error_name(ret));
 		return SR_ERR;
@@ -451,8 +366,8 @@ static int dev_close(struct sr_dev_inst *sdi)
 	if (!usb->devhdl)
 		return SR_ERR;
 
-	sr_info("Closing device %d on %d.%d interface %d.", sdi->index,
-		usb->bus, usb->address, USB_INTERFACE);
+	sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
+		usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
 	libusb_release_interface(usb->devhdl, USB_INTERFACE);
 	libusb_reset_device(usb->devhdl);
 	libusb_close(usb->devhdl);
@@ -462,44 +377,35 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int cleanup(void)
+static int cleanup(const struct sr_dev_driver *di)
 {
 	return std_dev_clear(di, NULL);
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
+	GVariant *range[2];
 
 	(void)cg;
 
-	switch (id) {
+	if (!sdi)
+		return SR_ERR_ARG;
+
+	devc = sdi->priv;
+
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
-		if (sdi) {
-			devc = sdi->priv;
-			*data = g_variant_new_uint64(devc->cur_samplerate);
-			sr_spew("Returning samplerate: %" PRIu64 "Hz.",
-				devc->cur_samplerate);
-		} else
-			return SR_ERR_ARG;
+		*data = g_variant_new_uint64(devc->cur_samplerate);
 		break;
 	case SR_CONF_CAPTURE_RATIO:
-		if (sdi) {
-			devc = sdi->priv;
-			*data = g_variant_new_uint64(devc->capture_ratio);
-		} else
-			return SR_ERR_ARG;
+		*data = g_variant_new_uint64(devc->capture_ratio);
 		break;
 	case SR_CONF_VOLTAGE_THRESHOLD:
-		if (sdi) {
-			GVariant *range[2];
-			devc = sdi->priv;
-			range[0] = g_variant_new_double(devc->cur_threshold);
-			range[1] = g_variant_new_double(devc->cur_threshold);
-			*data = g_variant_new_tuple(range, 2);
-		} else
-			return SR_ERR_ARG;
+		range[0] = g_variant_new_double(devc->cur_threshold);
+		range[1] = g_variant_new_double(devc->cur_threshold);
+		*data = g_variant_new_tuple(range, 2);
 		break;
 	default:
 		return SR_ERR_NA;
@@ -508,7 +414,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -524,7 +430,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		return SR_ERR_ARG;
 	}
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		return zp_set_samplerate(devc, g_variant_get_uint64(data));
 	case SR_CONF_LIMIT_SAMPLES:
@@ -541,7 +447,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct dev_context *devc;
@@ -554,8 +460,8 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	case SR_CONF_SAMPLERATE:
 		devc = sdi->priv;
@@ -576,8 +482,10 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 		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);
+	case SR_CONF_TRIGGER_MATCH:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+				trigger_matches, ARRAY_SIZE(trigger_matches),
+				sizeof(int32_t));
 		break;
 	case SR_CONF_VOLTAGE_THRESHOLD:
 		g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
@@ -635,8 +543,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 		return SR_ERR_ARG;
 	}
 
-	if (configure_channels(sdi) != SR_OK) {
-		sr_err("Failed to configure channels.");
+	if (analyzer_add_triggers(sdi) != SR_OK) {
+		sr_err("Failed to configure triggers.");
 		return SR_ERR;
 	}
 
@@ -680,10 +588,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
 		return SR_OK;
 	}
 
-	if (!(buf = g_try_malloc(PACKET_SIZE))) {
-		sr_err("Packet buffer malloc failed.");
-		return SR_ERR_MALLOC;
-	}
+	buf = g_malloc(PACKET_SIZE);
 
 	/* Check if the trigger is in the samples we are throwing away */
 	trigger_now = now_address == trigger_address ||
@@ -831,5 +736,5 @@ SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info = {
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
 	.dev_acquisition_stop = dev_acquisition_stop,
-	.priv = NULL,
+	.context = NULL,
 };
diff --git a/hardware/zeroplus-logic-cube/gl_usb.c b/src/hardware/zeroplus-logic-cube/gl_usb.c
similarity index 94%
rename from hardware/zeroplus-logic-cube/gl_usb.c
rename to src/hardware/zeroplus-logic-cube/gl_usb.c
index 2099328..0d662f6 100644
--- a/hardware/zeroplus-logic-cube/gl_usb.c
+++ b/src/hardware/zeroplus-logic-cube/gl_usb.c
@@ -29,9 +29,10 @@
  *  THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <config.h>
 #include <stdio.h>
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "gl_usb.h"
 #include "protocol.h"
@@ -42,7 +43,7 @@
 			 LIBUSB_RECIPIENT_INTERFACE)
 #define EP1_BULK_IN	(LIBUSB_ENDPOINT_IN | 1)
 
-#define TIMEOUT		5000	/* Timeout in ms */
+#define TIMEOUT_MS	(5 * 1000)
 
 enum {
 	REQ_READBULK = 0x82,
@@ -57,7 +58,7 @@ static int gl_write_address(libusb_device_handle *devh, unsigned int address)
 	int ret;
 
 	ret = libusb_control_transfer(devh, CTRL_OUT, 0xc, REQ_WRITEADDR,
-					 0, packet, 1, TIMEOUT);
+					 0, packet, 1, TIMEOUT_MS);
 	if (ret != 1)
 		sr_err("%s: %s.", __func__, libusb_error_name(ret));
 	return ret;
@@ -69,7 +70,7 @@ static int gl_write_data(libusb_device_handle *devh, unsigned int val)
 	int ret;
 
 	ret = libusb_control_transfer(devh, CTRL_OUT, 0xc, REQ_WRITEDATA,
-				      0, packet, 1, TIMEOUT);
+				      0, packet, 1, TIMEOUT_MS);
 	if (ret != 1)
 		sr_err("%s: %s.", __func__, libusb_error_name(ret));
 	return ret;
@@ -81,7 +82,7 @@ static int gl_read_data(libusb_device_handle *devh)
 	int ret;
 
 	ret = libusb_control_transfer(devh, CTRL_IN, 0xc, REQ_READDATA,
-				      0, packet, 1, TIMEOUT);
+				      0, packet, 1, TIMEOUT_MS);
 	if (ret != 1)
 		sr_err("%s: %s, val=%hhx.", __func__,
 		       libusb_error_name(ret), packet[0]);
@@ -97,13 +98,13 @@ SR_PRIV int gl_read_bulk(libusb_device_handle *devh, void *buffer,
 	int ret, transferred = 0;
 
 	ret = libusb_control_transfer(devh, CTRL_OUT, 0x4, REQ_READBULK,
-				      0, packet, 8, TIMEOUT);
+				      0, packet, 8, TIMEOUT_MS);
 	if (ret != 8)
 		sr_err("%s: libusb_control_transfer: %s.", __func__,
 		       libusb_error_name(ret));
 
 	ret = libusb_bulk_transfer(devh, EP1_BULK_IN, buffer, size,
-				   &transferred, TIMEOUT);
+				   &transferred, TIMEOUT_MS);
 	if (ret < 0)
 		sr_err("%s: libusb_bulk_transfer: %s.", __func__,
 		       libusb_error_name(ret));
diff --git a/hardware/zeroplus-logic-cube/gl_usb.h b/src/hardware/zeroplus-logic-cube/gl_usb.h
similarity index 98%
rename from hardware/zeroplus-logic-cube/gl_usb.h
rename to src/hardware/zeroplus-logic-cube/gl_usb.h
index c3a33fb..9eef7c9 100644
--- a/hardware/zeroplus-logic-cube/gl_usb.h
+++ b/src/hardware/zeroplus-logic-cube/gl_usb.h
@@ -33,7 +33,7 @@
 #define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_GL_USB_H
 
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 
 SR_PRIV int gl_read_bulk(libusb_device_handle *devh, void *buffer,
 			 unsigned int size);
diff --git a/hardware/zeroplus-logic-cube/protocol.c b/src/hardware/zeroplus-logic-cube/protocol.c
similarity index 96%
rename from hardware/zeroplus-logic-cube/protocol.c
rename to src/hardware/zeroplus-logic-cube/protocol.c
index ce20ccf..6e0e961 100644
--- a/hardware/zeroplus-logic-cube/protocol.c
+++ b/src/hardware/zeroplus-logic-cube/protocol.c
@@ -17,13 +17,14 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <math.h>
 #include "protocol.h"
 
 SR_PRIV unsigned int get_memory_size(int type)
 {
 	if (type == MEMORY_SIZE_8K)
-		return 8 * 1024;
+		return (8 * 1024);
 	else if (type <= MEMORY_SIZE_8M)
 		return (32 * 1024) << type;
 	else
@@ -63,9 +64,9 @@ SR_PRIV int set_limit_samples(struct dev_context *devc, uint64_t samples)
 
 	devc->limit_samples = samples;
 
-	if (samples <= 2 * 1024)
+	if (samples <= (2 * 1024))
 		devc->memory_size = MEMORY_SIZE_8K;
-	else if (samples <= 16 * 1024)
+	else if (samples <= (16 * 1024))
 		devc->memory_size = MEMORY_SIZE_64K;
 	else
 		devc->memory_size = 19 - clz(samples - 1);
diff --git a/hardware/zeroplus-logic-cube/protocol.h b/src/hardware/zeroplus-logic-cube/protocol.h
similarity index 97%
rename from hardware/zeroplus-logic-cube/protocol.h
rename to src/hardware/zeroplus-logic-cube/protocol.h
index 4b55b33..4d128be 100644
--- a/hardware/zeroplus-logic-cube/protocol.h
+++ b/src/hardware/zeroplus-logic-cube/protocol.h
@@ -22,9 +22,10 @@
 #define LIBSIGROK_HARDWARE_ZEROPLUS_LOGIC_CUBE_PROTOCOL_H
 
 #include <stdint.h>
+#include <string.h>
 #include <glib.h>
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 #include "analyzer.h"
 
diff --git a/src/hwdriver.c b/src/hwdriver.c
new file mode 100644
index 0000000..5790e32
--- /dev/null
+++ b/src/hwdriver.c
@@ -0,0 +1,902 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "hwdriver"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Hardware driver handling in libsigrok.
+ */
+
+/**
+ * @defgroup grp_driver Hardware drivers
+ *
+ * Hardware driver handling in libsigrok.
+ *
+ * @{
+ */
+
+/* Please use the same order/grouping as in enum sr_configkey (libsigrok.h). */
+static struct sr_key_info sr_key_info_config[] = {
+	/* Device classes */
+	{SR_CONF_LOGIC_ANALYZER, SR_T_STRING, NULL, "Logic analyzer", NULL},
+	{SR_CONF_OSCILLOSCOPE, SR_T_STRING, NULL, "Oscilloscope", NULL},
+	{SR_CONF_MULTIMETER, SR_T_STRING, NULL, "Multimeter", NULL},
+	{SR_CONF_DEMO_DEV, SR_T_STRING, NULL, "Demo device", NULL},
+	{SR_CONF_SOUNDLEVELMETER, SR_T_STRING, NULL, "Sound level meter", NULL},
+	{SR_CONF_THERMOMETER, SR_T_STRING, NULL, "Thermometer", NULL},
+	{SR_CONF_HYGROMETER, SR_T_STRING, NULL, "Hygrometer", NULL},
+	{SR_CONF_ENERGYMETER, SR_T_STRING, NULL, "Energy meter", NULL},
+	{SR_CONF_DEMODULATOR, SR_T_STRING, NULL, "Demodulator", NULL},
+	{SR_CONF_POWER_SUPPLY, SR_T_STRING, NULL, "Power supply", NULL},
+	{SR_CONF_LCRMETER, SR_T_STRING, NULL, "LCR meter", NULL},
+	{SR_CONF_ELECTRONIC_LOAD, SR_T_STRING, NULL, "Electronic load", NULL},
+	{SR_CONF_SCALE, SR_T_STRING, NULL, "Scale", NULL},
+
+	/* Driver scan options */
+	{SR_CONF_CONN, SR_T_STRING, "conn",
+		"Connection", NULL},
+	{SR_CONF_SERIALCOMM, SR_T_STRING, "serialcomm",
+		"Serial communication", NULL},
+	{SR_CONF_MODBUSADDR, SR_T_UINT64, "modbusaddr",
+		"Modbus slave address", NULL},
+
+	/* Device (or channel group) configuration */
+	{SR_CONF_SAMPLERATE, SR_T_UINT64, "samplerate",
+		"Sample rate", NULL},
+	{SR_CONF_CAPTURE_RATIO, SR_T_UINT64, "captureratio",
+		"Pre-trigger capture ratio", NULL},
+	{SR_CONF_PATTERN_MODE, SR_T_STRING, "pattern",
+		"Pattern", NULL},
+	{SR_CONF_RLE, SR_T_BOOL, "rle",
+		"Run length encoding", NULL},
+	{SR_CONF_TRIGGER_SLOPE, SR_T_STRING, "triggerslope",
+		"Trigger slope", NULL},
+	{SR_CONF_AVERAGING, SR_T_BOOL, "averaging",
+		"Averaging", NULL},
+	{SR_CONF_AVG_SAMPLES, SR_T_UINT64, "avg_samples",
+		"Number of samples to average over", NULL},
+	{SR_CONF_TRIGGER_SOURCE, SR_T_STRING, "triggersource",
+		"Trigger source", NULL},
+	{SR_CONF_HORIZ_TRIGGERPOS, SR_T_FLOAT, "horiz_triggerpos",
+		"Horizontal trigger position", NULL},
+	{SR_CONF_BUFFERSIZE, SR_T_UINT64, "buffersize",
+		"Buffer size", NULL},
+	{SR_CONF_TIMEBASE, SR_T_RATIONAL_PERIOD, "timebase",
+		"Time base", NULL},
+	{SR_CONF_FILTER, SR_T_BOOL, "filter",
+		"Filter", NULL},
+	{SR_CONF_VDIV, SR_T_RATIONAL_VOLT, "vdiv",
+		"Volts/div", NULL},
+	{SR_CONF_COUPLING, SR_T_STRING, "coupling",
+		"Coupling", NULL},
+	{SR_CONF_TRIGGER_MATCH, SR_T_INT32, "triggermatch",
+		"Trigger matches", NULL},
+	{SR_CONF_SAMPLE_INTERVAL, SR_T_UINT64, "sample_interval",
+		"Sample interval", NULL},
+	{SR_CONF_NUM_HDIV, SR_T_INT32, "num_hdiv",
+		"Number of horizontal divisions", NULL},
+	{SR_CONF_NUM_VDIV, SR_T_INT32, "num_vdiv",
+		"Number of vertical divisions", NULL},
+	{SR_CONF_SPL_WEIGHT_FREQ, SR_T_STRING, "spl_weight_freq",
+		"Sound pressure level frequency weighting", NULL},
+	{SR_CONF_SPL_WEIGHT_TIME, SR_T_STRING, "spl_weight_time",
+		"Sound pressure level time weighting", NULL},
+	{SR_CONF_SPL_MEASUREMENT_RANGE, SR_T_UINT64_RANGE, "spl_meas_range",
+		"Sound pressure level measurement range", NULL},
+	{SR_CONF_HOLD_MAX, SR_T_BOOL, "hold_max",
+		"Hold max", NULL},
+	{SR_CONF_HOLD_MIN, SR_T_BOOL, "hold_min",
+		"Hold min", NULL},
+	{SR_CONF_VOLTAGE_THRESHOLD, SR_T_DOUBLE_RANGE, "voltage_threshold",
+		"Voltage threshold", NULL },
+	{SR_CONF_EXTERNAL_CLOCK, SR_T_BOOL, "external_clock",
+		"External clock mode", NULL},
+	{SR_CONF_SWAP, SR_T_BOOL, "swap",
+		"Swap channel order", NULL},
+	{SR_CONF_CENTER_FREQUENCY, SR_T_UINT64, "center_frequency",
+		"Center frequency", NULL},
+	{SR_CONF_NUM_LOGIC_CHANNELS, SR_T_INT32, "logic_channels",
+		"Number of logic channels", NULL},
+	{SR_CONF_NUM_ANALOG_CHANNELS, SR_T_INT32, "analog_channels",
+		"Number of analog channels", NULL},
+	{SR_CONF_VOLTAGE, SR_T_FLOAT, "voltage",
+		"Current voltage", NULL},
+	{SR_CONF_VOLTAGE_TARGET, SR_T_FLOAT, "voltage_target",
+		"Voltage target", NULL},
+	{SR_CONF_CURRENT, SR_T_FLOAT, "current",
+		"Current current", NULL},
+	{SR_CONF_CURRENT_LIMIT, SR_T_FLOAT, "current_limit",
+		"Current limit", NULL},
+	{SR_CONF_ENABLED, SR_T_BOOL, "enabled",
+		"Channel enabled", NULL},
+	{SR_CONF_CHANNEL_CONFIG, SR_T_STRING, "channel_config",
+		"Channel modes", NULL},
+	{SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED, SR_T_BOOL, "ovp_enabled",
+		"Over-voltage protection enabled", NULL},
+	{SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE, SR_T_BOOL, "ovp_active",
+		"Over-voltage protection active", NULL},
+	{SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD, SR_T_FLOAT, "ovp_threshold",
+		"Over-voltage protection threshold", NULL},
+	{SR_CONF_OVER_CURRENT_PROTECTION_ENABLED, SR_T_BOOL, "ocp_enabled",
+		"Over-current protection enabled", NULL},
+	{SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE, SR_T_BOOL, "ocp_active",
+		"Over-current protection active", NULL},
+	{SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD, SR_T_FLOAT, "ocp_threshold",
+		"Over-current protection threshold", NULL},
+	{SR_CONF_CLOCK_EDGE, SR_T_STRING, "clock_edge",
+		"Clock edge", NULL},
+	{SR_CONF_AMPLITUDE, SR_T_FLOAT, "amplitude",
+		"Amplitude", NULL},
+	{SR_CONF_REGULATION, SR_T_STRING, "regulation",
+		"Channel regulation", NULL},
+	{SR_CONF_OVER_TEMPERATURE_PROTECTION, SR_T_BOOL, "otp",
+		"Over-temperature protection", NULL},
+	{SR_CONF_OUTPUT_FREQUENCY, SR_T_FLOAT, "output_frequency",
+		"Output frequency", NULL},
+	{SR_CONF_OUTPUT_FREQUENCY_TARGET, SR_T_FLOAT, "output_frequency_target",
+		"Output frequency target", NULL},
+	{SR_CONF_MEASURED_QUANTITY, SR_T_MQ, "measured_quantity",
+		"Measured quantity", NULL},
+	{SR_CONF_EQUIV_CIRCUIT_MODEL, SR_T_STRING, "equiv_circuit_model",
+		"Equivalent circuit model", NULL},
+	{SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE, SR_T_BOOL, "otp_active",
+		"Over-temperature protection active", NULL},
+
+	/* Special stuff */
+	{SR_CONF_SESSIONFILE, SR_T_STRING, "sessionfile",
+		"Session file", NULL},
+	{SR_CONF_CAPTUREFILE, SR_T_STRING, "capturefile",
+		"Capture file", NULL},
+	{SR_CONF_CAPTURE_UNITSIZE, SR_T_UINT64, "capture_unitsize",
+		"Capture unitsize", NULL},
+	{SR_CONF_POWER_OFF, SR_T_BOOL, "power_off",
+		"Power off", NULL},
+	{SR_CONF_DATA_SOURCE, SR_T_STRING, "data_source",
+		"Data source", NULL},
+	{SR_CONF_PROBE_FACTOR, SR_T_UINT64, "probe_factor",
+		"Probe factor", NULL},
+
+	/* Acquisition modes, sample limiting */
+	{SR_CONF_LIMIT_MSEC, SR_T_UINT64, "limit_time",
+		"Time limit", NULL},
+	{SR_CONF_LIMIT_SAMPLES, SR_T_UINT64, "limit_samples",
+		"Sample limit", NULL},
+	{SR_CONF_LIMIT_FRAMES, SR_T_UINT64, "limit_frames",
+		"Frame limit", NULL},
+	{SR_CONF_CONTINUOUS, SR_T_UINT64, "continuous",
+		"Continuous sampling", NULL},
+	{SR_CONF_DATALOG, SR_T_BOOL, "datalog",
+		"Datalog", NULL},
+	{SR_CONF_DEVICE_MODE, SR_T_STRING, "device_mode",
+		"Device mode", NULL},
+	{SR_CONF_TEST_MODE, SR_T_STRING, "test_mode",
+		"Test mode", NULL},
+
+	ALL_ZERO
+};
+
+/* Please use the same order as in enum sr_mq (libsigrok.h). */
+static struct sr_key_info sr_key_info_mq[] = {
+	{SR_MQ_VOLTAGE, 0, "voltage", "Voltage", NULL},
+	{SR_MQ_CURRENT, 0, "current", "Current", NULL},
+	{SR_MQ_RESISTANCE, 0, "resistance", "Resistance", NULL},
+	{SR_MQ_CAPACITANCE, 0, "capacitance", "Capacitance", NULL},
+	{SR_MQ_TEMPERATURE, 0, "temperature", "Temperature", NULL},
+	{SR_MQ_FREQUENCY, 0, "frequency", "Frequency", NULL},
+	{SR_MQ_DUTY_CYCLE, 0, "duty_cycle", "Duty cycle", NULL},
+	{SR_MQ_CONTINUITY, 0, "continuity", "Continuity", NULL},
+	{SR_MQ_PULSE_WIDTH, 0, "pulse_width", "Pulse width", NULL},
+	{SR_MQ_CONDUCTANCE, 0, "conductance", "Conductance", NULL},
+	{SR_MQ_POWER, 0, "power", "Power", NULL},
+	{SR_MQ_GAIN, 0, "gain", "Gain", NULL},
+	{SR_MQ_SOUND_PRESSURE_LEVEL, 0, "spl", "Sound pressure level", NULL},
+	{SR_MQ_CARBON_MONOXIDE, 0, "co", "Carbon monoxide", NULL},
+	{SR_MQ_RELATIVE_HUMIDITY, 0, "rh", "Relative humidity", NULL},
+	{SR_MQ_TIME, 0, "time", "Time", NULL},
+	{SR_MQ_WIND_SPEED, 0, "wind_speed", "Wind speed", NULL},
+	{SR_MQ_PRESSURE, 0, "pressure", "Pressure", NULL},
+	{SR_MQ_PARALLEL_INDUCTANCE, 0, "parallel_inductance", "Parallel inductance", NULL},
+	{SR_MQ_PARALLEL_CAPACITANCE, 0, "parallel_capacitance", "Parallel capacitance", NULL},
+	{SR_MQ_PARALLEL_RESISTANCE, 0, "parallel_resistance", "Parallel resistance", NULL},
+	{SR_MQ_SERIES_INDUCTANCE, 0, "series_inductance", "Series inductance", NULL},
+	{SR_MQ_SERIES_CAPACITANCE, 0, "series_capacitance", "Series capacitance", NULL},
+	{SR_MQ_SERIES_RESISTANCE, 0, "series_resistance", "Series resistance", NULL},
+	{SR_MQ_DISSIPATION_FACTOR, 0, "dissipation_factor", "Dissipation factor", NULL},
+	{SR_MQ_QUALITY_FACTOR, 0, "quality_factor", "Quality factor", NULL},
+	{SR_MQ_PHASE_ANGLE, 0, "phase_angle", "Phase angle", NULL},
+	{SR_MQ_DIFFERENCE, 0, "difference", "Difference", NULL},
+	{SR_MQ_COUNT, 0, "count", "Count", NULL},
+	{SR_MQ_POWER_FACTOR, 0, "power_factor", "Power factor", NULL},
+	{SR_MQ_APPARENT_POWER, 0, "apparent_power", "Apparent power", NULL},
+	{SR_MQ_MASS, 0, "mass", "Mass", NULL},
+	ALL_ZERO
+};
+
+/* Please use the same order as in enum sr_mqflag (libsigrok.h). */
+static struct sr_key_info sr_key_info_mqflag[] = {
+	{SR_MQFLAG_AC, 0, "ac", "AC", NULL},
+	{SR_MQFLAG_DC, 0, "dc", "DC", NULL},
+	{SR_MQFLAG_RMS, 0, "rms", "RMS", NULL},
+	{SR_MQFLAG_DIODE, 0, "diode", "Diode", NULL},
+	{SR_MQFLAG_HOLD, 0, "hold", "Hold", NULL},
+	{SR_MQFLAG_MAX, 0, "max", "Max", NULL},
+	{SR_MQFLAG_MIN, 0, "min", "Min", NULL},
+	{SR_MQFLAG_AUTORANGE, 0, "auto_range", "Auto range", NULL},
+	{SR_MQFLAG_RELATIVE, 0, "relative", "Relative", NULL},
+	{SR_MQFLAG_SPL_FREQ_WEIGHT_A, 0, "spl_freq_weight_a",
+		"Frequency weighted (A)", NULL},
+	{SR_MQFLAG_SPL_FREQ_WEIGHT_C, 0, "spl_freq_weight_c",
+		"Frequency weighted (C)", NULL},
+	{SR_MQFLAG_SPL_FREQ_WEIGHT_Z, 0, "spl_freq_weight_z",
+		"Frequency weighted (Z)", NULL},
+	{SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT, 0, "spl_freq_weight_flat",
+		"Frequency weighted (flat)", NULL},
+	{SR_MQFLAG_SPL_TIME_WEIGHT_S, 0, "spl_time_weight_s",
+		"Time weighted (S)", NULL},
+	{SR_MQFLAG_SPL_TIME_WEIGHT_F, 0, "spl_time_weight_f",
+		"Time weighted (F)", NULL},
+	{SR_MQFLAG_SPL_LAT, 0, "spl_time_average", "Time-averaged (LEQ)", NULL},
+	{SR_MQFLAG_SPL_PCT_OVER_ALARM, 0, "spl_pct_over_alarm",
+		"Percentage over alarm", NULL},
+	{SR_MQFLAG_DURATION, 0, "duration", "Duration", NULL},
+	{SR_MQFLAG_AVG, 0, "average", "Average", NULL},
+	{SR_MQFLAG_REFERENCE, 0, "reference", "Reference", NULL},
+	{SR_MQFLAG_UNSTABLE, 0, "unstable", "Unstable", NULL},
+	ALL_ZERO
+};
+
+/* This must handle all the keys from enum sr_datatype (libsigrok.h). */
+SR_PRIV const GVariantType *sr_variant_type_get(int datatype)
+{
+	switch (datatype) {
+	case SR_T_INT32:
+		return G_VARIANT_TYPE_INT32;
+	case SR_T_UINT64:
+		return G_VARIANT_TYPE_UINT64;
+	case SR_T_STRING:
+		return G_VARIANT_TYPE_STRING;
+	case SR_T_BOOL:
+		return G_VARIANT_TYPE_BOOLEAN;
+	case SR_T_FLOAT:
+		return G_VARIANT_TYPE_DOUBLE;
+	case SR_T_RATIONAL_PERIOD:
+	case SR_T_RATIONAL_VOLT:
+	case SR_T_UINT64_RANGE:
+	case SR_T_DOUBLE_RANGE:
+		return G_VARIANT_TYPE_TUPLE;
+	case SR_T_KEYVALUE:
+		return G_VARIANT_TYPE_DICTIONARY;
+	case SR_T_MQ:
+		return G_VARIANT_TYPE_TUPLE;
+	default:
+		return NULL;
+	}
+}
+
+SR_PRIV int sr_variant_type_check(uint32_t key, GVariant *value)
+{
+	const struct sr_key_info *info;
+	const GVariantType *type, *expected;
+	char *expected_string, *type_string;
+
+	info = sr_key_info_get(SR_KEY_CONFIG, key);
+	if (!info)
+		return SR_OK;
+
+	expected = sr_variant_type_get(info->datatype);
+	type = g_variant_get_type(value);
+	if (!g_variant_type_equal(type, expected)
+			&& !g_variant_type_is_subtype_of(type, expected)) {
+		expected_string = g_variant_type_dup_string(expected);
+		type_string = g_variant_type_dup_string(type);
+		sr_err("Wrong variant type for key '%s': expected '%s', got '%s'",
+			info->name, expected_string, type_string);
+		g_free(expected_string);
+		g_free(type_string);
+		return SR_ERR_ARG;
+	}
+
+	return SR_OK;
+}
+
+/**
+ * Return the list of supported hardware drivers.
+ *
+ * @param[in] ctx Pointer to a libsigrok context struct. Must not be NULL.
+ *
+ * @retval NULL The ctx argument was NULL, or there are no supported drivers.
+ * @retval Other Pointer to the NULL-terminated list of hardware drivers.
+ *               The user should NOT g_free() this list, sr_exit() will do that.
+ *
+ * @since 0.4.0
+ */
+SR_API struct sr_dev_driver **sr_driver_list(const struct sr_context *ctx)
+{
+	if (!ctx)
+		return NULL;
+
+	return ctx->driver_list;
+}
+
+/**
+ * Initialize a hardware driver.
+ *
+ * This usually involves memory allocations and variable initializations
+ * within the driver, but _not_ scanning for attached devices.
+ * The API call sr_driver_scan() is used for that.
+ *
+ * @param ctx A libsigrok context object allocated by a previous call to
+ *            sr_init(). Must not be NULL.
+ * @param driver The driver to initialize. This must be a pointer to one of
+ *               the entries returned by sr_driver_list(). Must not be NULL.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid parameter(s).
+ * @retval SR_ERR_BUG Internal errors.
+ * @retval other Another negative error code upon other errors.
+ *
+ * @since 0.2.0
+ */
+SR_API int sr_driver_init(struct sr_context *ctx, struct sr_dev_driver *driver)
+{
+	int ret;
+
+	if (!ctx) {
+		sr_err("Invalid libsigrok context, can't initialize.");
+		return SR_ERR_ARG;
+	}
+
+	if (!driver) {
+		sr_err("Invalid driver, can't initialize.");
+		return SR_ERR_ARG;
+	}
+
+	sr_spew("Initializing driver '%s'.", driver->name);
+	if ((ret = driver->init(driver, ctx)) < 0)
+		sr_err("Failed to initialize the driver: %d.", ret);
+
+	return ret;
+}
+
+/**
+ * Enumerate scan options supported by this driver.
+ *
+ * Before calling sr_driver_scan_options_list(), the user must have previously
+ * initialized the driver by calling sr_driver_init().
+ *
+ * @param driver The driver to enumerate options for. This must be a pointer
+ *               to one of the entries returned by sr_driver_list(). Must not
+ *               be NULL.
+ *
+ * @return A GArray * of uint32_t entries, or NULL on invalid arguments. Each
+ *         entry is a configuration key that is supported as a scan option.
+ *         The array must be freed by the caller using g_array_free().
+ *
+ * @since 0.4.0
+ */
+SR_API GArray *sr_driver_scan_options_list(const struct sr_dev_driver *driver)
+{
+	GVariant *gvar;
+	const uint32_t *opts;
+	gsize num_opts;
+	GArray *result;
+
+	if (sr_config_list(driver, NULL, NULL, SR_CONF_SCAN_OPTIONS, &gvar) != SR_OK)
+		return NULL;
+
+	opts = g_variant_get_fixed_array(gvar, &num_opts, sizeof(uint32_t));
+
+	result = g_array_sized_new(FALSE, FALSE, sizeof(uint32_t), num_opts);
+
+	g_array_insert_vals(result, 0, opts, num_opts);
+
+	g_variant_unref(gvar);
+
+	return result;
+}
+
+static int check_options(struct sr_dev_driver *driver, GSList *options,
+		uint32_t optlist_key, struct sr_dev_inst *sdi,
+		struct sr_channel_group *cg)
+{
+	struct sr_config *src;
+	const struct sr_key_info *srci;
+	GVariant *gvar_opts;
+	GSList *l;
+	const uint32_t *opts;
+	gsize num_opts, i;
+	int ret;
+
+	if (sr_config_list(driver, sdi, cg, optlist_key, &gvar_opts) != SR_OK) {
+		/* Driver publishes no options for this optlist. */
+		return SR_ERR;
+	}
+
+	ret = SR_OK;
+	opts = g_variant_get_fixed_array(gvar_opts, &num_opts, sizeof(uint32_t));
+	for (l = options; l; l = l->next) {
+		src = l->data;
+		for (i = 0; i < num_opts; i++) {
+			if (opts[i] == src->key)
+				break;
+		}
+		if (i == num_opts) {
+			if (!(srci = sr_key_info_get(SR_KEY_CONFIG, src->key)))
+				/* Shouldn't happen. */
+				sr_err("Invalid option %d.", src->key);
+			else
+				sr_err("Invalid option '%s'.", srci->id);
+			ret = SR_ERR_ARG;
+			break;
+		}
+		if (sr_variant_type_check(src->key, src->data) != SR_OK) {
+			ret = SR_ERR_ARG;
+			break;
+		}
+	}
+	g_variant_unref(gvar_opts);
+
+	return ret;
+}
+
+/**
+ * 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 driver The driver that should scan. This must be a pointer to one of
+ *               the entries returned by sr_driver_list(). Must not be NULL.
+ * @param options A 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
+ */
+SR_API GSList *sr_driver_scan(struct sr_dev_driver *driver, GSList *options)
+{
+	GSList *l;
+
+	if (!driver) {
+		sr_err("Invalid driver, can't scan for devices.");
+		return NULL;
+	}
+
+	if (!driver->context) {
+		sr_err("Driver not initialized, can't scan for devices.");
+		return NULL;
+	}
+
+	if (options) {
+		if (check_options(driver, options, SR_CONF_SCAN_OPTIONS, NULL, NULL) != SR_OK)
+			return NULL;
+	}
+
+	l = driver->scan(driver, options);
+
+	sr_spew("Scan of '%s' found %d devices.", driver->name,
+		g_slist_length(l));
+
+	return l;
+}
+
+/**
+ * Call driver cleanup function for all drivers.
+ *
+ * @param[in] ctx Pointer to a libsigrok context struct. Must not be NULL.
+ *
+ * @private
+ */
+SR_PRIV void sr_hw_cleanup_all(const struct sr_context *ctx)
+{
+	int i;
+	struct sr_dev_driver **drivers;
+
+	if (!ctx)
+		return;
+
+	drivers = sr_driver_list(ctx);
+	for (i = 0; drivers[i]; i++) {
+		if (drivers[i]->cleanup)
+			drivers[i]->cleanup(drivers[i]);
+		drivers[i]->context = NULL;
+	}
+}
+
+/** Allocate struct sr_config.
+ *  A floating reference can be passed in for data.
+ *  @private
+ */
+SR_PRIV struct sr_config *sr_config_new(uint32_t key, GVariant *data)
+{
+	struct sr_config *src;
+
+	src = g_malloc0(sizeof(struct sr_config));
+	src->key = key;
+	src->data = g_variant_ref_sink(data);
+
+	return src;
+}
+
+/** Free struct sr_config.
+ *  @private
+ */
+SR_PRIV void sr_config_free(struct sr_config *src)
+{
+
+	if (!src || !src->data) {
+		sr_err("%s: invalid data!", __func__);
+		return;
+	}
+
+	g_variant_unref(src->data);
+	g_free(src);
+
+}
+
+static void log_key(const struct sr_dev_inst *sdi,
+	const struct sr_channel_group *cg, uint32_t key, int op, GVariant *data)
+{
+	const char *opstr;
+	const struct sr_key_info *srci;
+
+	/* Don't log SR_CONF_DEVICE_OPTIONS, it's verbose and not too useful. */
+	if (key == SR_CONF_DEVICE_OPTIONS)
+		return;
+
+	opstr = op == SR_CONF_GET ? "get" : op == SR_CONF_SET ? "set" : "list";
+	srci = sr_key_info_get(SR_KEY_CONFIG, key);
+
+	sr_spew("sr_config_%s(): key %d (%s) sdi %p cg %s -> %s", opstr, key,
+		srci ? srci->id : "NULL", sdi, cg ? cg->name : "NULL",
+		data ? g_variant_print(data, TRUE) : "NULL");
+}
+
+static int check_key(const struct sr_dev_driver *driver,
+		const struct sr_dev_inst *sdi, const struct sr_channel_group *cg,
+		uint32_t key, int op, GVariant *data)
+{
+	const struct sr_key_info *srci;
+	gsize num_opts, i;
+	GVariant *gvar_opts;
+	const uint32_t *opts;
+	uint32_t pub_opt;
+	const char *suffix;
+	const char *opstr;
+
+	if (sdi && cg)
+		suffix = " for this device and channel group";
+	else if (sdi)
+		suffix = " for this device";
+	else
+		suffix = "";
+
+	if (!(srci = sr_key_info_get(SR_KEY_CONFIG, key))) {
+		sr_err("Invalid key %d.", key);
+		return SR_ERR_ARG;
+	}
+	opstr = op == SR_CONF_GET ? "get" : op == SR_CONF_SET ? "set" : "list";
+
+	switch (key) {
+	case SR_CONF_LIMIT_MSEC:
+	case SR_CONF_LIMIT_SAMPLES:
+	case SR_CONF_SAMPLERATE:
+		/* Setting any of these to 0 is not useful. */
+		if (op != SR_CONF_SET || !data)
+			break;
+		if (g_variant_get_uint64(data) == 0) {
+			sr_err("Cannot set '%s' to 0.", srci->id);
+			return SR_ERR_ARG;
+		}
+		break;
+	}
+
+	if (sr_config_list(driver, sdi, cg, SR_CONF_DEVICE_OPTIONS, &gvar_opts) != SR_OK) {
+		/* Driver publishes no options. */
+		sr_err("No options available%s.", suffix);
+		return SR_ERR_ARG;
+	}
+	opts = g_variant_get_fixed_array(gvar_opts, &num_opts, sizeof(uint32_t));
+	pub_opt = 0;
+	for (i = 0; i < num_opts; i++) {
+		if ((opts[i] & SR_CONF_MASK) == key) {
+			pub_opt = opts[i];
+			break;
+		}
+	}
+	g_variant_unref(gvar_opts);
+	if (!pub_opt) {
+		sr_err("Option '%s' not available%s.", srci->id, suffix);
+		return SR_ERR_ARG;
+	}
+
+	if (!(pub_opt & op)) {
+		sr_err("Option '%s' not available to %s%s.", srci->id, opstr, suffix);
+		return SR_ERR_ARG;
+	}
+
+	return SR_OK;
+}
+
+/**
+ * Query value of a configuration key at the given driver or device instance.
+ *
+ * @param[in] driver The sr_dev_driver struct to query.
+ * @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.
+ * @param[in] cg The channel group on the device for which to list the
+ *                    values, or NULL.
+ * @param[in] key The configuration key (SR_CONF_*).
+ * @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
+ */
+SR_API int sr_config_get(const struct sr_dev_driver *driver,
+		const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg,
+		uint32_t key, GVariant **data)
+{
+	int ret;
+
+	if (!driver || !data)
+		return SR_ERR;
+
+	if (!driver->config_get)
+		return SR_ERR_ARG;
+
+	if (check_key(driver, sdi, cg, key, SR_CONF_GET, NULL) != SR_OK)
+		return SR_ERR_ARG;
+
+	if ((ret = driver->config_get(key, data, sdi, cg)) == SR_OK) {
+		log_key(sdi, cg, key, SR_CONF_GET, *data);
+		/* Got a floating reference from the driver. Sink it here,
+		 * caller will need to unref when done with it. */
+		g_variant_ref_sink(*data);
+	}
+
+	return ret;
+}
+
+/**
+ * Set value of a configuration key in a device instance.
+ *
+ * @param[in] sdi The device instance.
+ * @param[in] cg The channel group on the device for which to list the
+ *                    values, or NULL.
+ * @param[in] key The configuration key (SR_CONF_*).
+ * @param data The new value for the key, as a GVariant with GVariantType
+ *        appropriate to that key. A floating reference can be passed
+ *        in; its refcount will be sunk and unreferenced after use.
+ *
+ * @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
+ */
+SR_API int sr_config_set(const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg,
+		uint32_t key, GVariant *data)
+{
+	int ret;
+
+	g_variant_ref_sink(data);
+
+	if (!sdi || !sdi->driver || !data)
+		ret = SR_ERR;
+	else if (!sdi->driver->config_set)
+		ret = SR_ERR_ARG;
+	else if (check_key(sdi->driver, sdi, cg, key, SR_CONF_SET, data) != SR_OK)
+		return SR_ERR_ARG;
+	else if ((ret = sr_variant_type_check(key, data)) == SR_OK) {
+		log_key(sdi, cg, key, SR_CONF_SET, data);
+		ret = sdi->driver->config_set(key, data, sdi, cg);
+	}
+
+	g_variant_unref(data);
+
+	return ret;
+}
+
+/**
+ * Apply configuration settings to the device hardware.
+ *
+ * @param sdi The device instance.
+ *
+ * @return SR_OK upon success or SR_ERR in case of error.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_config_commit(const struct sr_dev_inst *sdi)
+{
+	int ret;
+
+	if (!sdi || !sdi->driver)
+		ret = SR_ERR;
+	else if (!sdi->driver->config_commit)
+		ret = SR_OK;
+	else
+		ret = sdi->driver->config_commit(sdi);
+
+	return ret;
+}
+
+/**
+ * List all possible values for a configuration key.
+ *
+ * @param[in] driver The sr_dev_driver struct to query.
+ * @param[in] sdi (optional) If the key is specific to a device, this must
+ *            contain a pointer to the struct sr_dev_inst to be checked.
+ * @param[in] cg The channel group on the device for which to list the
+ *                    values, or NULL.
+ * @param[in] key The configuration key (SR_CONF_*).
+ * @param[in,out] data A pointer to a GVariant where the list will be stored.
+ *             The caller is given ownership of the GVariant and must thus
+ *             unref the GVariant after use. However if this function
+ *             returns an error code, the field should be considered
+ *             unused, and should not be unreferenced.
+ *
+ * @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
+ */
+SR_API int sr_config_list(const struct sr_dev_driver *driver,
+		const struct sr_dev_inst *sdi,
+		const struct sr_channel_group *cg,
+		uint32_t key, GVariant **data)
+{
+	int ret;
+
+	if (!driver || !data)
+		return SR_ERR;
+	else if (!driver->config_list)
+		return SR_ERR_ARG;
+	else if (key != SR_CONF_SCAN_OPTIONS && key != SR_CONF_DEVICE_OPTIONS) {
+		if (check_key(driver, sdi, cg, key, SR_CONF_LIST, NULL) != SR_OK)
+			return SR_ERR_ARG;
+	}
+	if ((ret = driver->config_list(key, data, sdi, cg)) == SR_OK) {
+		log_key(sdi, cg, key, SR_CONF_LIST, *data);
+		g_variant_ref_sink(*data);
+	}
+
+	return ret;
+}
+
+static struct sr_key_info *get_keytable(int keytype)
+{
+	struct sr_key_info *table;
+
+	switch (keytype) {
+	case SR_KEY_CONFIG:
+		table = sr_key_info_config;
+		break;
+	case SR_KEY_MQ:
+		table = sr_key_info_mq;
+		break;
+	case SR_KEY_MQFLAGS:
+		table = sr_key_info_mqflag;
+		break;
+	default:
+		sr_err("Invalid keytype %d", keytype);
+		return NULL;
+	}
+
+	return table;
+}
+
+/**
+ * Get information about a key, by key.
+ *
+ * @param[in] keytype The namespace the key is in.
+ * @param[in] key The key to find.
+ *
+ * @return A pointer to a struct sr_key_info, or NULL if the key
+ *         was not found.
+ *
+ * @since 0.3.0
+ */
+SR_API const struct sr_key_info *sr_key_info_get(int keytype, uint32_t key)
+{
+	struct sr_key_info *table;
+	int i;
+
+	if (!(table = get_keytable(keytype)))
+		return NULL;
+
+	for (i = 0; table[i].key; i++) {
+		if (table[i].key == key)
+			return &table[i];
+	}
+
+	return NULL;
+}
+
+/**
+ * Get information about a key, by name.
+ *
+ * @param[in] keytype The namespace the key is in.
+ * @param[in] keyid The key id string.
+ *
+ * @return A pointer to a struct sr_key_info, or NULL if the key
+ *         was not found.
+ *
+ * @since 0.2.0
+ */
+SR_API const struct sr_key_info *sr_key_info_name_get(int keytype, const char *keyid)
+{
+	struct sr_key_info *table;
+	int i;
+
+	if (!(table = get_keytable(keytype)))
+		return NULL;
+
+	for (i = 0; table[i].key; i++) {
+		if (!table[i].id)
+			continue;
+		if (!strcmp(table[i].id, keyid))
+			return &table[i];
+	}
+
+	return NULL;
+}
+
+/** @} */
diff --git a/src/input/binary.c b/src/input/binary.c
new file mode 100644
index 0000000..f58af58
--- /dev/null
+++ b/src/input/binary.c
@@ -0,0 +1,173 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/binary"
+
+#define MAX_CHUNK_SIZE        4096
+#define DEFAULT_NUM_CHANNELS  8
+#define DEFAULT_SAMPLERATE    0
+
+struct context {
+	gboolean started;
+	uint64_t samplerate;
+};
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+	struct context *inc;
+	int num_channels, i;
+	char name[16];
+
+	num_channels = g_variant_get_int32(g_hash_table_lookup(options, "numchannels"));
+	if (num_channels < 1) {
+		sr_err("Invalid value for numchannels: must be at least 1.");
+		return SR_ERR_ARG;
+	}
+
+	in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	in->priv = inc = g_malloc0(sizeof(struct context));
+
+	inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
+
+	for (i = 0; i < num_channels; i++) {
+		snprintf(name, 16, "%d", i);
+		sr_channel_new(in->sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
+	}
+
+	return SR_OK;
+}
+
+static int process_buffer(struct sr_input *in)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_meta meta;
+	struct sr_datafeed_logic logic;
+	struct sr_config *src;
+	struct context *inc;
+	gsize chunk_size, i;
+	int chunk;
+
+	inc = in->priv;
+	if (!inc->started) {
+		std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+		if (inc->samplerate) {
+			packet.type = SR_DF_META;
+			packet.payload = &meta;
+			src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
+			meta.config = g_slist_append(NULL, src);
+			sr_session_send(in->sdi, &packet);
+			g_slist_free(meta.config);
+			sr_config_free(src);
+		}
+
+		inc->started = TRUE;
+	}
+
+	packet.type = SR_DF_LOGIC;
+	packet.payload = &logic;
+	logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+
+	/* Cut off at multiple of unitsize. */
+	chunk_size = in->buf->len / logic.unitsize * logic.unitsize;
+
+	for (i = 0; i < chunk_size; i += chunk) {
+		logic.data = in->buf->str + i;
+		chunk = MIN(MAX_CHUNK_SIZE, chunk_size - i);
+		logic.length = chunk;
+		sr_session_send(in->sdi, &packet);
+	}
+	g_string_erase(in->buf, 0, chunk_size);
+
+	return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+	int ret;
+
+	g_string_append_len(in->buf, buf->str, buf->len);
+
+	if (!in->sdi_ready) {
+		/* sdi is ready, notify frontend. */
+		in->sdi_ready = TRUE;
+		return SR_OK;
+	}
+
+	ret = process_buffer(in);
+
+	return ret;
+}
+
+static int end(struct sr_input *in)
+{
+	struct context *inc;
+	struct sr_datafeed_packet packet;
+	int ret;
+
+	if (in->sdi_ready)
+		ret = process_buffer(in);
+	else
+		ret = SR_OK;
+
+	inc = in->priv;
+	if (inc->started) {
+		packet.type = SR_DF_END;
+		sr_session_send(in->sdi, &packet);
+	}
+
+	return ret;
+}
+
+static struct sr_option options[] = {
+	{ "numchannels", "Number of channels", "Number of channels", NULL, NULL },
+	{ "samplerate", "Sample rate", "Sample rate", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_ref_sink(g_variant_new_int32(DEFAULT_NUM_CHANNELS));
+		options[1].def = g_variant_ref_sink(g_variant_new_uint64(DEFAULT_SAMPLERATE));
+	}
+
+	return options;
+}
+
+SR_PRIV struct sr_input_module input_binary = {
+	.id = "binary",
+	.name = "Binary",
+	.desc = "Raw binary",
+	.exts = NULL,
+	.options = get_options,
+	.init = init,
+	.receive = receive,
+	.end = end,
+};
diff --git a/src/input/chronovu_la8.c b/src/input/chronovu_la8.c
new file mode 100644
index 0000000..f42886e
--- /dev/null
+++ b/src/input/chronovu_la8.c
@@ -0,0 +1,187 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Uwe Hermann <uwe at 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 <config.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/chronovu-la8"
+
+#define DEFAULT_NUM_CHANNELS    8
+#define DEFAULT_SAMPLERATE      SR_MHZ(100)
+#define MAX_CHUNK_SIZE          (4 * 1024)
+#define CHRONOVU_LA8_FILESIZE   ((8 * 1024 * 1024) + 5)
+
+struct context {
+	gboolean started;
+	uint64_t samplerate;
+};
+
+static int format_match(GHashTable *metadata)
+{
+	int size;
+
+	size = GPOINTER_TO_INT(g_hash_table_lookup(metadata,
+			GINT_TO_POINTER(SR_INPUT_META_FILESIZE)));
+	if (size == CHRONOVU_LA8_FILESIZE)
+		return SR_OK;
+
+	return SR_ERR;
+}
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+	struct context *inc;
+	int num_channels, i;
+	char name[16];
+
+	num_channels = g_variant_get_int32(g_hash_table_lookup(options, "numchannels"));
+	if (num_channels < 1) {
+		sr_err("Invalid value for numchannels: must be at least 1.");
+		return SR_ERR_ARG;
+	}
+
+	in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	in->priv = inc = g_malloc0(sizeof(struct context));
+
+	inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
+
+	for (i = 0; i < num_channels; i++) {
+		snprintf(name, 16, "%d", i);
+		sr_channel_new(in->sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
+	}
+
+	return SR_OK;
+}
+
+static int process_buffer(struct sr_input *in)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_meta meta;
+	struct sr_datafeed_logic logic;
+	struct sr_config *src;
+	struct context *inc;
+	gsize chunk_size, i;
+	int chunk;
+
+	inc = in->priv;
+	if (!inc->started) {
+		std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+		if (inc->samplerate) {
+			packet.type = SR_DF_META;
+			packet.payload = &meta;
+			src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
+			meta.config = g_slist_append(NULL, src);
+			sr_session_send(in->sdi, &packet);
+			g_slist_free(meta.config);
+			sr_config_free(src);
+		}
+
+		inc->started = TRUE;
+	}
+
+	packet.type = SR_DF_LOGIC;
+	packet.payload = &logic;
+	logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+
+	/* Cut off at multiple of unitsize. */
+	chunk_size = in->buf->len / logic.unitsize * logic.unitsize;
+
+	for (i = 0; i < chunk_size; i += chunk) {
+		logic.data = in->buf->str + i;
+		chunk = MIN(MAX_CHUNK_SIZE, chunk_size - i);
+		logic.length = chunk;
+		sr_session_send(in->sdi, &packet);
+	}
+	g_string_erase(in->buf, 0, chunk_size);
+
+	return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+	int ret;
+
+	g_string_append_len(in->buf, buf->str, buf->len);
+
+	if (!in->sdi_ready) {
+		/* sdi is ready, notify frontend. */
+		in->sdi_ready = TRUE;
+		return SR_OK;
+	}
+
+	ret = process_buffer(in);
+
+	return ret;
+}
+
+static int end(struct sr_input *in)
+{
+	struct context *inc;
+	struct sr_datafeed_packet packet;
+	int ret;
+
+	if (in->sdi_ready)
+		ret = process_buffer(in);
+	else
+		ret = SR_OK;
+
+	inc = in->priv;
+	if (inc->started) {
+		packet.type = SR_DF_END;
+		sr_session_send(in->sdi, &packet);
+	}
+
+	return ret;
+}
+
+static struct sr_option options[] = {
+	{ "numchannels", "Number of channels", "Number of channels", NULL, NULL },
+	{ "samplerate", "Sample rate", "Sample rate", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_ref_sink(g_variant_new_int32(DEFAULT_NUM_CHANNELS));
+		options[1].def = g_variant_ref_sink(g_variant_new_uint64(DEFAULT_SAMPLERATE));
+	}
+
+	return options;
+}
+
+SR_PRIV struct sr_input_module input_chronovu_la8 = {
+	.id = "chronovu-la8",
+	.name = "Chronovu-LA8",
+	.desc = "ChronoVu LA8",
+	.exts = (const char*[]){"kdt", NULL},
+	.metadata = { SR_INPUT_META_FILESIZE | SR_INPUT_META_REQUIRED },
+	.options = get_options,
+	.format_match = format_match,
+	.init = init,
+	.receive = receive,
+	.end = end,
+};
diff --git a/src/input/csv.c b/src/input/csv.c
new file mode 100644
index 0000000..ed2b494
--- /dev/null
+++ b/src/input/csv.c
@@ -0,0 +1,815 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Marc Schink <sigrok-dev at marcschink.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/csv"
+
+/*
+ * The CSV input module has the following options:
+ *
+ * single-column: Specifies the column number which stores the sample data for
+ *                single column mode and enables single column mode. Multi
+ *                column mode is used if this parameter is omitted.
+ *
+ * numchannels:   Specifies the number of channels to use. In multi column mode
+ *                the number of channels are the number of columns and in single
+ *                column mode the number of bits (LSB first) beginning at
+ *                'first-channel'.
+ *
+ * delimiter:     Specifies the delimiter for columns. Must be at least one
+ *                character. Comma is used as default delimiter.
+ *
+ * format:        Specifies the format of the sample data in single column mode.
+ *                Available formats are: 'bin', 'hex' and 'oct'. The binary
+ *                format is used by default. This option has no effect in multi
+ *                column mode.
+ *
+ * comment:       Specifies the prefix character(s) for comments. No prefix
+ *                characters are used by default which disables removing of
+ *                comments.
+ *
+ * samplerate:    Samplerate which the sample data was captured with. Default
+ *                value is 0.
+ *
+ * first-channel: Column number of the first channel in multi column mode and
+ *                position of the bit for the first channel in single column mode.
+ *                Default value is 0.
+ *
+ * header:        Determines if the first line should be treated as header
+ *                and used for channel names in multi column mode. Empty header
+ *                names will be replaced by the channel number. If enabled in
+ *                single column mode the first line will be skipped. Usage of
+ *                header is disabled by default.
+ *
+ * startline:     Line number to start processing sample data. Must be greater
+ *                than 0. The default line number to start processing is 1.
+ */
+
+/* Single column formats. */
+enum {
+	FORMAT_BIN,
+	FORMAT_HEX,
+	FORMAT_OCT
+};
+
+struct context {
+	gboolean started;
+
+	/* Current selected samplerate. */
+	uint64_t samplerate;
+
+	/* Number of channels. */
+	unsigned int num_channels;
+
+	/* Column delimiter character(s). */
+	GString *delimiter;
+
+	/* Comment prefix character(s). */
+	GString *comment;
+
+	/* Termination  character(s) used in current stream. */
+	char *termination;
+
+	/* Determines if sample data is stored in multiple columns. */
+	gboolean multi_column_mode;
+
+	/* Column number of the sample data in single column mode. */
+	unsigned int single_column;
+
+	/*
+	 * Number of the first column to parse. Equivalent to the number of the
+	 * first channel in multi column mode and the single column number in
+	 * single column mode.
+	 */
+	unsigned int first_column;
+
+	/*
+	 * Column number of the first channel in multi column mode and position of
+	 * the bit for the first channel in single column mode.
+	 */
+	unsigned int first_channel;
+
+	/* Line number to start processing. */
+	size_t start_line;
+
+	/*
+	 * Determines if the first line should be treated as header and used for
+	 * channel names in multi column mode.
+	 */
+	gboolean header;
+
+	/* Format sample data is stored in single column mode. */
+	int format;
+
+	/* Size of the sample buffer. */
+	size_t sample_buffer_size;
+
+	/* Buffer to store sample data. */
+	uint8_t *sample_buffer;
+
+	/* Current line number. */
+	size_t line_number;
+};
+
+static void strip_comment(char *buf, const GString *prefix)
+{
+	char *ptr;
+
+	if (!prefix->len)
+		return;
+
+	if ((ptr = strstr(buf, prefix->str)))
+		*ptr = '\0';
+}
+
+static int parse_binstr(const char *str, struct context *inc)
+{
+	gsize i, j, length;
+
+	length = strlen(str);
+
+	if (!length) {
+		sr_err("Column %u in line %zu is empty.", inc->single_column,
+			inc->line_number);
+		return SR_ERR;
+	}
+
+	/* Clear buffer in order to set bits only. */
+	memset(inc->sample_buffer, 0, (inc->num_channels + 7) >> 3);
+
+	i = inc->first_channel;
+
+	for (j = 0; i < length && j < inc->num_channels; i++, j++) {
+		if (str[length - i - 1] == '1') {
+			inc->sample_buffer[j / 8] |= (1 << (j % 8));
+		} else if (str[length - i - 1] != '0') {
+			sr_err("Invalid value '%s' in column %u in line %zu.",
+				str, inc->single_column, inc->line_number);
+			return SR_ERR;
+		}
+	}
+
+	return SR_OK;
+}
+
+static int parse_hexstr(const char *str, struct context *inc)
+{
+	gsize i, j, k, length;
+	uint8_t value;
+	char c;
+
+	length = strlen(str);
+
+	if (!length) {
+		sr_err("Column %u in line %zu is empty.", inc->single_column,
+			inc->line_number);
+		return SR_ERR;
+	}
+
+	/* Clear buffer in order to set bits only. */
+	memset(inc->sample_buffer, 0, (inc->num_channels + 7) >> 3);
+
+	/* Calculate the position of the first hexadecimal digit. */
+	i = inc->first_channel / 4;
+
+	for (j = 0; i < length && j < inc->num_channels; i++) {
+		c = str[length - i - 1];
+
+		if (!g_ascii_isxdigit(c)) {
+			sr_err("Invalid value '%s' in column %u in line %zu.",
+				str, inc->single_column, inc->line_number);
+			return SR_ERR;
+		}
+
+		value = g_ascii_xdigit_value(c);
+
+		k = (inc->first_channel + j) % 4;
+
+		for (; j < inc->num_channels && k < 4; k++) {
+			if (value & (1 << k))
+				inc->sample_buffer[j / 8] |= (1 << (j % 8));
+
+			j++;
+		}
+	}
+
+	return SR_OK;
+}
+
+static int parse_octstr(const char *str, struct context *inc)
+{
+	gsize i, j, k, length;
+	uint8_t value;
+	char c;
+
+	length = strlen(str);
+
+	if (!length) {
+		sr_err("Column %u in line %zu is empty.", inc->single_column,
+			inc->line_number);
+		return SR_ERR;
+	}
+
+	/* Clear buffer in order to set bits only. */
+	memset(inc->sample_buffer, 0, (inc->num_channels + 7) >> 3);
+
+	/* Calculate the position of the first octal digit. */
+	i = inc->first_channel / 3;
+
+	for (j = 0; i < length && j < inc->num_channels; i++) {
+		c = str[length - i - 1];
+
+		if (c < '0' || c > '7') {
+			sr_err("Invalid value '%s' in column %u in line %zu.",
+				str, inc->single_column, inc->line_number);
+			return SR_ERR;
+		}
+
+		value = g_ascii_xdigit_value(c);
+
+		k = (inc->first_channel + j) % 3;
+
+		for (; j < inc->num_channels && k < 3; k++) {
+			if (value & (1 << k))
+				inc->sample_buffer[j / 8] |= (1 << (j % 8));
+
+			j++;
+		}
+	}
+
+	return SR_OK;
+}
+
+static char **parse_line(char *buf, struct context *inc, int max_columns)
+{
+	const char *str, *remainder;
+	GSList *list, *l;
+	char **columns;
+	char *column;
+	gsize n, k;
+
+	n = 0;
+	k = 0;
+	list = NULL;
+
+	remainder = buf;
+	str = strstr(remainder, inc->delimiter->str);
+
+	while (str && max_columns) {
+		if (n >= inc->first_column) {
+			column = g_strndup(remainder, str - remainder);
+			list = g_slist_prepend(list, g_strstrip(column));
+
+			max_columns--;
+			k++;
+		}
+
+		remainder = str + inc->delimiter->len;
+		str = strstr(remainder, inc->delimiter->str);
+		n++;
+	}
+
+	if (buf[0] && max_columns && n >= inc->first_column) {
+		column = g_strdup(remainder);
+		list = g_slist_prepend(list, g_strstrip(column));
+		k++;
+	}
+
+	if (!(columns = g_try_new(char *, k + 1)))
+		return NULL;
+
+	columns[k--] = NULL;
+
+	for (l = list; l; l = l->next)
+		columns[k--] = l->data;
+
+	g_slist_free(list);
+
+	return columns;
+}
+
+static int parse_multi_columns(char **columns, struct context *inc)
+{
+	gsize i;
+
+	/* Clear buffer in order to set bits only. */
+	memset(inc->sample_buffer, 0, (inc->num_channels + 7) >> 3);
+
+	for (i = 0; i < inc->num_channels; i++) {
+		if (columns[i][0] == '1') {
+			inc->sample_buffer[i / 8] |= (1 << (i % 8));
+		} else if (!strlen(columns[i])) {
+			sr_err("Column %zu in line %zu is empty.",
+				inc->first_channel + i, inc->line_number);
+			return SR_ERR;
+		} else if (columns[i][0] != '0') {
+			sr_err("Invalid value '%s' in column %zu in line %zu.",
+				columns[i], inc->first_channel + i,
+				inc->line_number);
+			return SR_ERR;
+		}
+	}
+
+	return SR_OK;
+}
+
+static int parse_single_column(const char *column, struct context *inc)
+{
+	int res;
+
+	res = SR_ERR;
+
+	switch (inc->format) {
+	case FORMAT_BIN:
+		res = parse_binstr(column, inc);
+		break;
+	case FORMAT_HEX:
+		res = parse_hexstr(column, inc);
+		break;
+	case FORMAT_OCT:
+		res = parse_octstr(column, inc);
+		break;
+	}
+
+	return res;
+}
+
+static int send_samples(const struct sr_dev_inst *sdi, uint8_t *buffer,
+		gsize buffer_size, gsize count)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_logic logic;
+	int res;
+	gsize i;
+
+	packet.type = SR_DF_LOGIC;
+	packet.payload = &logic;
+	logic.unitsize = buffer_size;
+	logic.length = buffer_size;
+	logic.data = buffer;
+
+	for (i = 0; i < count; i++) {
+		if ((res = sr_session_send(sdi, &packet)) != SR_OK)
+			return res;
+	}
+
+	return SR_OK;
+}
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+	struct context *inc;
+	const char *s;
+
+	in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	in->priv = inc = g_malloc0(sizeof(struct context));
+
+	inc->single_column = g_variant_get_int32(g_hash_table_lookup(options, "single-column"));
+	inc->multi_column_mode = inc->single_column == 0;
+
+	inc->num_channels = g_variant_get_int32(g_hash_table_lookup(options, "numchannels"));
+
+	inc->delimiter = g_string_new(g_variant_get_string(
+			g_hash_table_lookup(options, "delimiter"), NULL));
+	if (inc->delimiter->len == 0) {
+		sr_err("Delimiter must be at least one character.");
+		return SR_ERR_ARG;
+	}
+
+	s = g_variant_get_string(g_hash_table_lookup(options, "format"), NULL);
+	if (!g_ascii_strncasecmp(s, "bin", 3)) {
+		inc->format = FORMAT_BIN;
+	} else if (!g_ascii_strncasecmp(s, "hex", 3)) {
+		inc->format = FORMAT_HEX;
+	} else if (!g_ascii_strncasecmp(s, "oct", 3)) {
+		inc->format = FORMAT_OCT;
+	} else {
+		sr_err("Invalid format: '%s'", s);
+		return SR_ERR_ARG;
+	}
+
+	inc->comment = g_string_new(g_variant_get_string(
+			g_hash_table_lookup(options, "comment"), NULL));
+	if (g_string_equal(inc->comment, inc->delimiter)) {
+		/* That's never going to work. Likely the result of the user
+		 * setting the delimiter to ; -- the default comment. Clearing
+		 * the comment setting will work in that case. */
+		g_string_truncate(inc->comment, 0);
+	}
+
+	inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
+
+	inc->first_channel = g_variant_get_int32(g_hash_table_lookup(options, "first-channel"));
+
+	inc->header = g_variant_get_boolean(g_hash_table_lookup(options, "header"));
+
+	inc->start_line = g_variant_get_int32(g_hash_table_lookup(options, "startline"));
+	if (inc->start_line < 1) {
+		sr_err("Invalid start line %zu.", inc->start_line);
+		return SR_ERR_ARG;
+	}
+
+	if (inc->multi_column_mode)
+		inc->first_column = inc->first_channel;
+	else
+		inc->first_column = inc->single_column;
+
+	if (!inc->multi_column_mode && !inc->num_channels) {
+		sr_err("Number of channels needs to be specified in single column mode.");
+		return SR_ERR_ARG;
+	}
+
+	return SR_OK;
+}
+
+static const char *get_line_termination(GString *buf)
+{
+	const char *term;
+
+	term = NULL;
+	if (g_strstr_len(buf->str, buf->len, "\r\n"))
+		term = "\r\n";
+	else if (memchr(buf->str, '\n', buf->len))
+		term = "\n";
+	else if (memchr(buf->str, '\r', buf->len))
+		term = "\r";
+
+	return term;
+}
+
+static int initial_parse(const struct sr_input *in, GString *buf)
+{
+	struct context *inc;
+	GString *channel_name;
+	unsigned int num_columns, i;
+	size_t line_number, l;
+	int ret;
+	char **lines, **columns;
+
+	ret = SR_OK;
+	inc = in->priv;
+	columns = NULL;
+
+	line_number = 0;
+	lines = g_strsplit_set(buf->str, "\r\n", 0);
+	for (l = 0; lines[l]; l++) {
+		line_number++;
+		if (inc->start_line > line_number) {
+			sr_spew("Line %zu skipped.", line_number);
+			continue;
+		}
+		if (lines[l][0] == '\0') {
+			sr_spew("Blank line %zu skipped.", line_number);
+			continue;
+		}
+		strip_comment(lines[l], inc->comment);
+		if (lines[l][0] == '\0') {
+			sr_spew("Comment-only line %zu skipped.", line_number);
+			continue;
+		}
+
+		/* Reached first proper line. */
+		break;
+	}
+	if (!lines[l]) {
+		/* Not enough data for a proper line yet. */
+		ret = SR_ERR_NA;
+		goto out;
+	}
+
+	/*
+	 * In order to determine the number of columns parse the current line
+	 * without limiting the number of columns.
+	 */
+	if (!(columns = parse_line(lines[l], inc, -1))) {
+		sr_err("Error while parsing line %zu.", line_number);
+		ret = SR_ERR;
+		goto out;
+	}
+	num_columns = g_strv_length(columns);
+
+	/* Ensure that the first column is not out of bounds. */
+	if (!num_columns) {
+		sr_err("Column %u in line %zu is out of bounds.",
+			inc->first_column, line_number);
+		ret = SR_ERR;
+		goto out;
+	}
+
+	if (inc->multi_column_mode) {
+		/*
+		 * Detect the number of channels in multi column mode
+		 * automatically if not specified.
+		 */
+		if (!inc->num_channels) {
+			inc->num_channels = num_columns;
+			sr_dbg("Number of auto-detected channels: %u.",
+				inc->num_channels);
+		}
+
+		/*
+		 * Ensure that the number of channels does not exceed the number
+		 * of columns in multi column mode.
+		 */
+		if (num_columns < inc->num_channels) {
+			sr_err("Not enough columns for desired number of channels in line %zu.",
+				line_number);
+			ret = SR_ERR;
+			goto out;
+		}
+	}
+
+	channel_name = g_string_sized_new(64);
+	for (i = 0; i < inc->num_channels; i++) {
+		if (inc->header && inc->multi_column_mode && columns[i][0] != '\0')
+			g_string_assign(channel_name, columns[i]);
+		else
+			g_string_printf(channel_name, "%u", i);
+		sr_channel_new(in->sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name->str);
+	}
+	g_string_free(channel_name, TRUE);
+
+	/*
+	 * Calculate the minimum buffer size to store the sample data of the
+	 * channels.
+	 */
+	inc->sample_buffer_size = (inc->num_channels + 7) >> 3;
+	inc->sample_buffer = g_malloc(inc->sample_buffer_size);
+
+out:
+	if (columns)
+		g_strfreev(columns);
+	g_strfreev(lines);
+
+	return ret;
+}
+
+static int initial_receive(const struct sr_input *in)
+{
+	struct context *inc;
+	GString *new_buf;
+	int len, ret;
+	char *p;
+	const char *termination;
+
+	inc = in->priv;
+
+	if (!(termination = get_line_termination(in->buf)))
+		/* Don't have a full line yet. */
+		return SR_ERR_NA;
+
+	if (!(p = g_strrstr_len(in->buf->str, in->buf->len, termination)))
+		/* Don't have a full line yet. */
+		return SR_ERR_NA;
+	len = p - in->buf->str - 1;
+	new_buf = g_string_new_len(in->buf->str, len);
+	g_string_append_c(new_buf, '\0');
+
+	inc->termination = g_strdup(termination);
+
+	if (in->buf->str[0] != '\0')
+		ret = initial_parse(in, new_buf);
+	else
+		ret = SR_OK;
+
+	g_string_free(new_buf, TRUE);
+
+	return ret;
+}
+
+static int process_buffer(struct sr_input *in)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_meta meta;
+	struct sr_config *src;
+	struct context *inc;
+	gsize num_columns;
+	uint64_t samplerate;
+	int max_columns, ret, l;
+	char *p, **lines, **columns;
+
+	inc = in->priv;
+	if (!inc->started) {
+		std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+		if (inc->samplerate) {
+			packet.type = SR_DF_META;
+			packet.payload = &meta;
+			samplerate = inc->samplerate;
+			src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
+			meta.config = g_slist_append(NULL, src);
+			sr_session_send(in->sdi, &packet);
+			g_slist_free(meta.config);
+			sr_config_free(src);
+		}
+
+		inc->started = TRUE;
+	}
+
+	if (!(p = g_strrstr_len(in->buf->str, in->buf->len, inc->termination)))
+		/* Don't have a full line. */
+		return SR_ERR;
+
+	*p = '\0';
+	g_strstrip(in->buf->str);
+
+	/* Limit the number of columns to parse. */
+	if (inc->multi_column_mode)
+		max_columns = inc->num_channels;
+	else
+		max_columns = 1;
+
+	ret = SR_OK;
+	lines = g_strsplit_set(in->buf->str, "\r\n", 0);
+	for (l = 0; lines[l]; l++) {
+		inc->line_number++;
+		if (lines[l][0] == '\0') {
+			sr_spew("Blank line %zu skipped.", inc->line_number);
+			continue;
+		}
+
+		/* Remove trailing comment. */
+		strip_comment(lines[l], inc->comment);
+		if (lines[l][0] == '\0') {
+			sr_spew("Comment-only line %zu skipped.", inc->line_number);
+			continue;
+		}
+
+		/* Skip the header line, its content was used as the channel names. */
+		if (inc->header) {
+			sr_spew("Header line %zu skipped.", inc->line_number);
+			inc->header = FALSE;
+			continue;
+		}
+
+		if (!(columns = parse_line(lines[l], inc, max_columns))) {
+			sr_err("Error while parsing line %zu.", inc->line_number);
+			return SR_ERR;
+		}
+		num_columns = g_strv_length(columns);
+		if (!num_columns) {
+			sr_err("Column %u in line %zu is out of bounds.",
+				inc->first_column, inc->line_number);
+			g_strfreev(columns);
+			return SR_ERR;
+		}
+		/*
+		 * Ensure that the number of channels does not exceed the number
+		 * of columns in multi column mode.
+		 */
+		if (inc->multi_column_mode && num_columns < inc->num_channels) {
+			sr_err("Not enough columns for desired number of channels in line %zu.",
+				inc->line_number);
+			g_strfreev(columns);
+			return SR_ERR;
+		}
+
+		if (inc->multi_column_mode)
+			ret = parse_multi_columns(columns, inc);
+		else
+			ret = parse_single_column(columns[0], inc);
+		if (ret != SR_OK) {
+			g_strfreev(columns);
+			return SR_ERR;
+		}
+
+		/* Send sample data to the session bus. */
+		ret = send_samples(in->sdi, inc->sample_buffer,
+			inc->sample_buffer_size, 1);
+		if (ret != SR_OK) {
+			sr_err("Sending samples failed.");
+			return SR_ERR;
+		}
+		g_strfreev(columns);
+	}
+	g_strfreev(lines);
+	g_string_erase(in->buf, 0, p - in->buf->str + 1);
+
+	return ret;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+	struct context *inc;
+	int ret;
+
+	g_string_append_len(in->buf, buf->str, buf->len);
+
+	inc = in->priv;
+	if (!inc->termination) {
+		if ((ret = initial_receive(in)) == SR_ERR_NA)
+			/* Not enough data yet. */
+			return SR_OK;
+		else if (ret != SR_OK)
+			return SR_ERR;
+
+		/* sdi is ready, notify frontend. */
+		in->sdi_ready = TRUE;
+		return SR_OK;
+	}
+
+	ret = process_buffer(in);
+
+	return ret;
+}
+
+static int end(struct sr_input *in)
+{
+	struct context *inc;
+	struct sr_datafeed_packet packet;
+	int ret;
+
+	if (in->sdi_ready)
+		ret = process_buffer(in);
+	else
+		ret = SR_OK;
+
+	inc = in->priv;
+	if (inc->started) {
+		/* End of stream. */
+		packet.type = SR_DF_END;
+		sr_session_send(in->sdi, &packet);
+	}
+
+	return ret;
+}
+
+static void cleanup(struct sr_input *in)
+{
+	struct context *inc;
+
+	inc = in->priv;
+
+	if (inc->delimiter)
+		g_string_free(inc->delimiter, TRUE);
+
+	if (inc->comment)
+		g_string_free(inc->comment, TRUE);
+
+	g_free(inc->termination);
+	g_free(inc->sample_buffer);
+}
+
+static struct sr_option options[] = {
+	{ "single-column", "Single column", "Enable/specify single column", NULL, NULL },
+	{ "numchannels", "Max channels", "Number of channels", NULL, NULL },
+	{ "delimiter", "Delimiter", "Column delimiter", NULL, NULL },
+	{ "format", "Format", "Numeric format", NULL, NULL },
+	{ "comment", "Comment", "Comment prefix character", NULL, NULL },
+	{ "samplerate", "Samplerate", "Samplerate used during capture", NULL, NULL },
+	{ "first-channel", "First channel", "Column number of first channel", NULL, NULL },
+	{ "header", "Header", "Treat first line as header with channel names", NULL, NULL },
+	{ "startline", "Start line", "Line number at which to start processing samples", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_ref_sink(g_variant_new_int32(0));
+		options[1].def = g_variant_ref_sink(g_variant_new_int32(0));
+		options[2].def = g_variant_ref_sink(g_variant_new_string(","));
+		options[3].def = g_variant_ref_sink(g_variant_new_string("bin"));
+		options[4].def = g_variant_ref_sink(g_variant_new_string(";"));
+		options[5].def = g_variant_ref_sink(g_variant_new_uint64(0));
+		options[6].def = g_variant_ref_sink(g_variant_new_int32(0));
+		options[7].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[8].def = g_variant_ref_sink(g_variant_new_int32(1));
+	}
+
+	return options;
+}
+
+SR_PRIV struct sr_input_module input_csv = {
+	.id = "csv",
+	.name = "CSV",
+	.desc = "Comma-separated values",
+	.exts = (const char*[]){"csv", NULL},
+	.options = get_options,
+	.init = init,
+	.receive = receive,
+	.end = end,
+	.cleanup = cleanup,
+};
diff --git a/src/input/input.c b/src/input/input.c
new file mode 100644
index 0000000..f2cc976
--- /dev/null
+++ b/src/input/input.c
@@ -0,0 +1,586 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "input"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Input module handling.
+ */
+
+/**
+ * @defgroup grp_input Input modules
+ *
+ * Input file/data module handling.
+ *
+ * libsigrok can process acquisition data in several different ways.
+ * Aside from acquiring data from a hardware device, it can also take it
+ * from a file in various formats (binary, CSV, VCD, and so on).
+ *
+ * Like all libsigrok data handling, processing is done in a streaming
+ * manner: input should be supplied a chunk at a time. This way anything
+ * that processes data can do so in real time, without the user having
+ * to wait for the whole thing to be finished.
+ *
+ * Every input module is "pluggable", meaning it's handled as being separate
+ * from the main libsigrok, but linked in to it statically. To keep things
+ * modular and separate like this, functions within an input module should be
+ * declared static, with only the respective 'struct sr_input_module' being
+ * exported for use into the wider libsigrok namespace.
+ *
+ * @{
+ */
+
+/** @cond PRIVATE */
+extern SR_PRIV struct sr_input_module input_chronovu_la8;
+extern SR_PRIV struct sr_input_module input_csv;
+extern SR_PRIV struct sr_input_module input_binary;
+extern SR_PRIV struct sr_input_module input_trace32_ad;
+extern SR_PRIV struct sr_input_module input_vcd;
+extern SR_PRIV struct sr_input_module input_wav;
+extern SR_PRIV struct sr_input_module input_raw_analog;
+/* @endcond */
+
+static const struct sr_input_module *input_module_list[] = {
+	&input_binary,
+	&input_chronovu_la8,
+	&input_csv,
+	&input_trace32_ad,
+	&input_vcd,
+	&input_wav,
+	&input_raw_analog,
+	NULL,
+};
+
+/**
+ * Returns a NULL-terminated list of all available input modules.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_input_module **sr_input_list(void)
+{
+	return input_module_list;
+}
+
+/**
+ * Returns the specified input module's ID.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_input_id_get(const struct sr_input_module *imod)
+{
+	if (!imod) {
+		sr_err("Invalid input module NULL!");
+		return NULL;
+	}
+
+	return imod->id;
+}
+
+/**
+ * Returns the specified input module's name.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_input_name_get(const struct sr_input_module *imod)
+{
+	if (!imod) {
+		sr_err("Invalid input module NULL!");
+		return NULL;
+	}
+
+	return imod->name;
+}
+
+/**
+ * Returns the specified input module's description.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_input_description_get(const struct sr_input_module *imod)
+{
+	if (!imod) {
+		sr_err("Invalid input module NULL!");
+		return NULL;
+	}
+
+	return imod->desc;
+}
+
+/**
+ * Returns the specified input module's file extensions typical for the file
+ * format, as a NULL terminated array, or returns a NULL pointer if there is
+ * no preferred extension.
+ * @note these are a suggestions only.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *const *sr_input_extensions_get(
+		const struct sr_input_module *imod)
+{
+	if (!imod) {
+		sr_err("Invalid input module NULL!");
+		return NULL;
+	}
+
+	return imod->exts;
+}
+
+/**
+ * Return the input module with the specified ID, or NULL if no module
+ * with that id is found.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_input_module *sr_input_find(char *id)
+{
+	int i;
+
+	for (i = 0; input_module_list[i]; i++) {
+		if (!strcmp(input_module_list[i]->id, id))
+			return input_module_list[i];
+	}
+
+	return NULL;
+}
+
+/**
+ * Returns a NULL-terminated array of struct sr_option, or NULL if the
+ * module takes no options.
+ *
+ * Each call to this function must be followed by a call to
+ * sr_input_options_free().
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_option **sr_input_options_get(const struct sr_input_module *imod)
+{
+	const struct sr_option *mod_opts, **opts;
+	int size, i;
+
+	if (!imod || !imod->options)
+		return NULL;
+
+	mod_opts = imod->options();
+
+	for (size = 0; mod_opts[size].id; size++)
+		;
+	opts = g_malloc((size + 1) * sizeof(struct sr_option *));
+
+	for (i = 0; i < size; i++)
+		opts[i] = &mod_opts[i];
+	opts[i] = NULL;
+
+	return opts;
+}
+
+/**
+ * After a call to sr_input_options_get(), this function cleans up all
+ * resources returned by that call.
+ *
+ * @since 0.4.0
+ */
+SR_API void sr_input_options_free(const struct sr_option **options)
+{
+	int i;
+
+	if (!options)
+		return;
+
+	for (i = 0; options[i]; i++) {
+		if (options[i]->def) {
+			g_variant_unref(options[i]->def);
+			((struct sr_option *)options[i])->def = NULL;
+		}
+
+		if (options[i]->values) {
+			g_slist_free_full(options[i]->values, (GDestroyNotify)g_variant_unref);
+			((struct sr_option *)options[i])->values = NULL;
+		}
+	}
+	g_free(options);
+}
+
+/**
+ * Create a new input instance using the specified input module.
+ *
+ * This function is used when a client wants to use a specific input
+ * module to parse a stream. No effort is made to identify the format.
+ *
+ * @param imod The input module to use. Must not be NULL.
+ * @param options GHashTable consisting of keys corresponding with
+ * the module options @c id field. The values should be GVariant
+ * pointers with sunk references, of the same GVariantType as the option's
+ * default value.
+ *
+ * @since 0.4.0
+ */
+SR_API struct sr_input *sr_input_new(const struct sr_input_module *imod,
+		GHashTable *options)
+{
+	struct sr_input *in;
+	const struct sr_option *mod_opts;
+	const GVariantType *gvt;
+	GHashTable *new_opts;
+	GHashTableIter iter;
+	gpointer key, value;
+	int i;
+
+	in = g_malloc0(sizeof(struct sr_input));
+	in->module = imod;
+
+	new_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+			(GDestroyNotify)g_variant_unref);
+	if (imod->options) {
+		mod_opts = imod->options();
+		for (i = 0; mod_opts[i].id; i++) {
+			if (options && g_hash_table_lookup_extended(options,
+					mod_opts[i].id, &key, &value)) {
+				/* Option not given: insert the default value. */
+				gvt = g_variant_get_type(mod_opts[i].def);
+				if (!g_variant_is_of_type(value, gvt)) {
+					sr_err("Invalid type for '%s' option.",
+						(char *)key);
+					g_free(in);
+					return NULL;
+				}
+				g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
+						g_variant_ref(value));
+			} else {
+				/* Pass option along. */
+				g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
+						g_variant_ref(mod_opts[i].def));
+			}
+		}
+
+		/* Make sure no invalid options were given. */
+		if (options) {
+			g_hash_table_iter_init(&iter, options);
+			while (g_hash_table_iter_next(&iter, &key, &value)) {
+				if (!g_hash_table_lookup(new_opts, key)) {
+					sr_err("Input module '%s' has no option '%s'",
+						imod->id, (char *)key);
+					g_hash_table_destroy(new_opts);
+					g_free(in);
+					return NULL;
+				}
+			}
+		}
+	}
+
+	if (in->module->init && in->module->init(in, new_opts) != SR_OK) {
+		g_free(in);
+		in = NULL;
+	} else {
+		in->buf = g_string_sized_new(128);
+	}
+
+	if (new_opts)
+		g_hash_table_destroy(new_opts);
+
+	return in;
+}
+
+/* Returns TRUE if all required meta items are available. */
+static gboolean check_required_metadata(const uint8_t *metadata, uint8_t *avail)
+{
+	int m, a;
+	uint8_t reqd;
+
+	for (m = 0; metadata[m]; m++) {
+		if (!(metadata[m] & SR_INPUT_META_REQUIRED))
+			continue;
+		reqd = metadata[m] & ~SR_INPUT_META_REQUIRED;
+		for (a = 0; avail[a]; a++) {
+			if (avail[a] == reqd)
+				break;
+		}
+		if (!avail[a])
+			/* Found a required meta item that isn't available. */
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+/**
+ * Try to find an input module that can parse the given buffer.
+ *
+ * The buffer must contain enough of the beginning of the file for
+ * the input modules to find a match. This is format-dependent, but
+ * 128 bytes is normally enough.
+ *
+ * If an input module is found, an instance is created into *in.
+ * Otherwise, *in contains NULL.
+ *
+ * If an instance is created, it has the given buffer used for scanning
+ * already submitted to it, to be processed before more data is sent.
+ * This allows a frontend to submit an initial chunk of a non-seekable
+ * stream, such as stdin, without having to keep it around and submit
+ * it again later.
+ *
+ */
+SR_API int sr_input_scan_buffer(GString *buf, const struct sr_input **in)
+{
+	const struct sr_input_module *imod;
+	GHashTable *meta;
+	unsigned int m, i;
+	int ret;
+	uint8_t mitem, avail_metadata[8];
+
+	/* No more metadata to be had from a buffer. */
+	avail_metadata[0] = SR_INPUT_META_HEADER;
+	avail_metadata[1] = 0;
+
+	*in = NULL;
+	ret = SR_ERR;
+	for (i = 0; input_module_list[i]; i++) {
+		imod = input_module_list[i];
+		if (!imod->metadata[0]) {
+			/* Module has no metadata for matching so will take
+			 * any input. No point in letting it try to match. */
+			continue;
+		}
+		if (!check_required_metadata(imod->metadata, avail_metadata))
+			/* Cannot satisfy this module's requirements. */
+			continue;
+
+		meta = g_hash_table_new(NULL, NULL);
+		for (m = 0; m < sizeof(imod->metadata); m++) {
+			mitem = imod->metadata[m] & ~SR_INPUT_META_REQUIRED;
+			if (mitem == SR_INPUT_META_HEADER)
+				g_hash_table_insert(meta, GINT_TO_POINTER(mitem), buf);
+		}
+		if (g_hash_table_size(meta) == 0) {
+			/* No metadata for this module, so nothing to match. */
+			g_hash_table_destroy(meta);
+			continue;
+		}
+		sr_spew("Trying module %s.", imod->id);
+		ret = imod->format_match(meta);
+		g_hash_table_destroy(meta);
+		if (ret == SR_ERR_DATA) {
+			/* Module recognized this buffer, but cannot handle it. */
+			break;
+		} else if (ret == SR_ERR) {
+			/* Module didn't recognize this buffer. */
+			continue;
+		} else if (ret != SR_OK) {
+			/* Can be SR_ERR_NA. */
+			return ret;
+		}
+
+		/* Found a matching module. */
+		sr_spew("Module %s matched.", imod->id);
+		*in = sr_input_new(imod, NULL);
+		g_string_insert_len((*in)->buf, 0, buf->str, buf->len);
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * Try to find an input module that can parse the given file.
+ *
+ * If an input module is found, an instance is created into *in.
+ * Otherwise, *in contains NULL.
+ *
+ */
+SR_API int sr_input_scan_file(const char *filename, const struct sr_input **in)
+{
+	int64_t filesize;
+	FILE *stream;
+	const struct sr_input_module *imod;
+	GHashTable *meta;
+	GString *header;
+	size_t count;
+	unsigned int midx, i;
+	int ret;
+	uint8_t avail_metadata[8];
+
+	*in = NULL;
+
+	if (!filename || !filename[0]) {
+		sr_err("Invalid filename.");
+		return SR_ERR_ARG;
+	}
+	stream = g_fopen(filename, "rb");
+	if (!stream) {
+		sr_err("Failed to open %s: %s", filename, g_strerror(errno));
+		return SR_ERR;
+	}
+	filesize = sr_file_get_size(stream);
+	if (filesize < 0) {
+		sr_err("Failed to get size of %s: %s",
+			filename, g_strerror(errno));
+		fclose(stream);
+		return SR_ERR;
+	}
+	/* This actually allocates 256 bytes to allow for NUL termination. */
+	header = g_string_sized_new(255);
+	count = fread(header->str, 1, header->allocated_len - 1, stream);
+
+	if (count != header->allocated_len - 1 && ferror(stream)) {
+		sr_err("Failed to read %s: %s", filename, g_strerror(errno));
+		fclose(stream);
+		g_string_free(header, TRUE);
+		return SR_ERR;
+	}
+	fclose(stream);
+	g_string_set_size(header, count);
+
+	meta = g_hash_table_new(NULL, NULL);
+	g_hash_table_insert(meta, GINT_TO_POINTER(SR_INPUT_META_FILENAME),
+			(char *)filename);
+	g_hash_table_insert(meta, GINT_TO_POINTER(SR_INPUT_META_FILESIZE),
+			GSIZE_TO_POINTER(MIN(filesize, G_MAXSSIZE)));
+	g_hash_table_insert(meta, GINT_TO_POINTER(SR_INPUT_META_HEADER),
+			header);
+	midx = 0;
+	avail_metadata[midx++] = SR_INPUT_META_FILENAME;
+	avail_metadata[midx++] = SR_INPUT_META_FILESIZE;
+	avail_metadata[midx++] = SR_INPUT_META_HEADER;
+	avail_metadata[midx] = 0;
+	/* TODO: MIME type */
+
+	ret = SR_ERR;
+
+	for (i = 0; input_module_list[i]; i++) {
+		imod = input_module_list[i];
+		if (!imod->metadata[0]) {
+			/* Module has no metadata for matching so will take
+			 * any input. No point in letting it try to match. */
+			continue;
+		}
+		if (!check_required_metadata(imod->metadata, avail_metadata))
+			/* Cannot satisfy this module's requirements. */
+			continue;
+
+		sr_dbg("Trying module %s.", imod->id);
+
+		ret = imod->format_match(meta);
+		if (ret == SR_ERR) {
+			/* Module didn't recognize this buffer. */
+			continue;
+		} else if (ret != SR_OK) {
+			/* Module recognized this buffer, but cannot handle it. */
+			break;
+		}
+		/* Found a matching module. */
+		sr_dbg("Module %s matched.", imod->id);
+
+		*in = sr_input_new(imod, NULL);
+		break;
+	}
+	g_hash_table_destroy(meta);
+	g_string_free(header, TRUE);
+
+	return ret;
+}
+
+/**
+ * Return the input instance's (virtual) device instance. This can be
+ * used to find out the number of channels and other information.
+ *
+ * If the device instance has not yet been fully populated by the input
+ * module, NULL is returned. This indicates the module needs more data
+ * to identify the number of channels and so on.
+ *
+ * @since 0.4.0
+ */
+SR_API struct sr_dev_inst *sr_input_dev_inst_get(const struct sr_input *in)
+{
+	if (in->sdi_ready)
+		return in->sdi;
+	else
+		return NULL;
+}
+
+/**
+ * Send data to the specified input instance.
+ *
+ * When an input module instance is created with sr_input_new(), this
+ * function is used to feed data to the instance.
+ *
+ * As enough data gets fed into this function to completely populate
+ * the device instance associated with this input instance, this is
+ * guaranteed to return the moment it's ready. This gives the caller
+ * the chance to examine the device instance, attach session callbacks
+ * and so on.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_input_send(const struct sr_input *in, GString *buf)
+{
+	sr_spew("Sending %" G_GSIZE_FORMAT " bytes to %s module.",
+		buf->len, in->module->id);
+	return in->module->receive((struct sr_input *)in, buf);
+}
+
+/**
+ * Signal the input module no more data will come.
+ *
+ * This will cause the module to process any data it may have buffered.
+ * The SR_DF_END packet will also typically be sent at this time.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_input_end(const struct sr_input *in)
+{
+	sr_spew("Calling end() on %s module.", in->module->id);
+	return in->module->end((struct sr_input *)in);
+}
+
+/**
+ * Free the specified input instance and all associated resources.
+ *
+ * @since 0.4.0
+ */
+SR_API void sr_input_free(const struct sr_input *in)
+{
+	if (!in)
+		return;
+
+	if (in->module->cleanup)
+		in->module->cleanup((struct sr_input *)in);
+	if (in->sdi)
+		sr_dev_inst_free(in->sdi);
+	if (in->buf->len > 64) {
+		/* That seems more than just some sub-unitsize leftover... */
+		sr_warn("Found %" G_GSIZE_FORMAT
+			" unprocessed bytes at free time.", in->buf->len);
+	}
+	g_string_free(in->buf, TRUE);
+	g_free(in->priv);
+	g_free((gpointer)in);
+}
+
+/** @} */
diff --git a/src/input/raw_analog.c b/src/input/raw_analog.c
new file mode 100644
index 0000000..a72cbd0
--- /dev/null
+++ b/src/input/raw_analog.c
@@ -0,0 +1,283 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ * Copyright (C) 2015 Stefan Brüns <stefan.bruens at rwth-aachen.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/raw_analog"
+
+/* How many bytes at a time to process and send to the session bus. */
+#define CHUNK_SIZE 4096
+#define DEFAULT_NUM_CHANNELS  1
+#define DEFAULT_SAMPLERATE    0
+
+struct context {
+	gboolean started;
+	int fmt_index;
+	uint64_t samplerate;
+	int samplesize;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog analog;
+	struct sr_analog_encoding encoding;
+	struct sr_analog_meaning meaning;
+	struct sr_analog_spec spec;
+};
+
+struct sample_format {
+	const char const *fmt_name;
+	struct sr_analog_encoding encoding;
+};
+
+static const struct sample_format const sample_formats[] =
+{
+	{ "S8",         { 1, TRUE,  FALSE, FALSE, 0, TRUE, { 1,                     128}, { 0, 1}}},
+	{ "U8",         { 1, FALSE, FALSE, FALSE, 0, TRUE, { 1,                     255}, {-1, 2}}},
+	{ "S16_LE",     { 2, TRUE,  FALSE, FALSE, 0, TRUE, { 1,           INT16_MAX + 1}, { 0, 1}}},
+	{ "U16_LE",     { 2, FALSE, FALSE, FALSE, 0, TRUE, { 1,              UINT16_MAX}, {-1, 2}}},
+	{ "S16_BE",     { 2, TRUE,  FALSE, TRUE,  0, TRUE, { 1,           INT16_MAX + 1}, { 0, 1}}},
+	{ "U16_BE",     { 2, FALSE, FALSE, TRUE,  0, TRUE, { 1,              UINT16_MAX}, {-1, 2}}},
+	{ "S32_LE",     { 4, TRUE,  FALSE, FALSE, 0, TRUE, { 1, (uint64_t)INT32_MAX + 1}, { 0, 1}}},
+	{ "U32_LE",     { 4, FALSE, FALSE, FALSE, 0, TRUE, { 1,              UINT32_MAX}, {-1, 2}}},
+	{ "S32_BE",     { 4, TRUE,  FALSE, TRUE,  0, TRUE, { 1, (uint64_t)INT32_MAX + 1}, { 0, 1}}},
+	{ "U32_BE",     { 4, FALSE, FALSE, TRUE,  0, TRUE, { 1,              UINT32_MAX}, {-1, 2}}},
+	{ "FLOAT_LE",   { 4, TRUE,  TRUE,  FALSE, 0, TRUE, { 1,                       1}, { 0, 1}}},
+	{ "FLOAT_BE",   { 4, TRUE,  TRUE,  TRUE,  0, TRUE, { 1,                       1}, { 0, 1}}},
+	{ "FLOAT64_LE", { 8, TRUE,  TRUE,  FALSE, 0, TRUE, { 1,                       1}, { 0, 1}}},
+	{ "FLOAT64_BE", { 8, TRUE,  TRUE,  TRUE,  0, TRUE, { 1,                       1}, { 0, 1}}},
+};
+
+static int parse_format_string(const char *format)
+{
+	for (unsigned int i = 0; i < ARRAY_SIZE(sample_formats); i++) {
+		if (!strcmp(format, sample_formats[i].fmt_name))
+			return i;
+	}
+
+	return -1;
+}
+
+static void init_context(struct context *inc, const struct sample_format *fmt, GSList *channels)
+{
+	inc->packet.type = SR_DF_ANALOG;
+	inc->packet.payload = &inc->analog;
+
+	inc->analog.data = NULL;
+	inc->analog.num_samples = 0;
+	inc->analog.encoding = &inc->encoding;
+	inc->analog.meaning = &inc->meaning;
+	inc->analog.spec = &inc->spec;
+
+	memcpy(&inc->encoding, &fmt->encoding, sizeof(inc->encoding));
+
+	inc->meaning.mq = 0;
+	inc->meaning.unit = 0;
+	inc->meaning.mqflags = 0;
+	inc->meaning.channels = channels;
+
+	inc->spec.spec_digits = 0;
+}
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+	struct context *inc;
+	int num_channels;
+	char channelname[8];
+	const char *format;
+	int fmt_index;
+
+	num_channels = g_variant_get_int32(g_hash_table_lookup(options, "numchannels"));
+	if (num_channels < 1) {
+		sr_err("Invalid value for numchannels: must be at least 1.");
+		return SR_ERR_ARG;
+	}
+
+	format = g_variant_get_string(g_hash_table_lookup(options, "format"), NULL);
+	if ((fmt_index = parse_format_string(format)) == -1) {
+		GString *formats = g_string_sized_new(200);
+		for (unsigned int i = 0; i < ARRAY_SIZE(sample_formats); i++)
+			g_string_append_printf(formats, "%s ", sample_formats[i].fmt_name);
+		sr_err("Invalid format '%s': must be one of: %s.",
+		       format, formats->str);
+		g_string_free(formats, TRUE);
+		return SR_ERR_ARG;
+	}
+
+	in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	in->priv = inc = g_malloc0(sizeof(struct context));
+
+	for (int i = 0; i < num_channels; i++) {
+		snprintf(channelname, 8, "CH%d", i + 1);
+		sr_channel_new(in->sdi, i, SR_CHANNEL_ANALOG, TRUE, channelname);
+	}
+
+	inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
+	inc->samplesize = sample_formats[fmt_index].encoding.unitsize * num_channels;
+	init_context(inc, &sample_formats[fmt_index], in->sdi->channels);
+
+	return SR_OK;
+}
+
+static int process_buffer(struct sr_input *in)
+{
+	struct context *inc;
+	struct sr_datafeed_meta meta;
+	struct sr_datafeed_packet packet;
+	struct sr_config *src;
+	unsigned int offset, chunk_size;
+
+	inc = in->priv;
+	if (!inc->started) {
+		std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+		if (inc->samplerate) {
+			packet.type = SR_DF_META;
+			packet.payload = &meta;
+			src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
+			meta.config = g_slist_append(NULL, src);
+			sr_session_send(in->sdi, &packet);
+			g_slist_free(meta.config);
+			sr_config_free(src);
+		}
+
+		inc->started = TRUE;
+	}
+
+	/* Round down to the last channels * unitsize boundary. */
+	inc->analog.num_samples = CHUNK_SIZE / inc->samplesize;
+	chunk_size = inc->analog.num_samples * inc->samplesize;
+	offset = 0;
+
+	while ((offset + chunk_size) < in->buf->len) {
+		inc->analog.data = in->buf->str + offset;
+		sr_session_send(in->sdi, &inc->packet);
+		offset += chunk_size;
+	}
+
+	inc->analog.num_samples = (in->buf->len - offset) / inc->samplesize;
+	chunk_size = inc->analog.num_samples * inc->samplesize;
+	if (chunk_size > 0) {
+		inc->analog.data = in->buf->str + offset;
+		sr_session_send(in->sdi, &inc->packet);
+		offset += chunk_size;
+	}
+
+	if ((unsigned int)offset < in->buf->len) {
+		/*
+		 * The incoming buffer wasn't processed completely. Stash
+		 * the leftover data for next time.
+		 */
+		g_string_erase(in->buf, 0, offset);
+	} else {
+		g_string_truncate(in->buf, 0);
+	}
+
+	return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+	int ret;
+
+	g_string_append_len(in->buf, buf->str, buf->len);
+
+	if (!in->sdi_ready) {
+		/* sdi is ready, notify frontend. */
+		in->sdi_ready = TRUE;
+		return SR_OK;
+	}
+
+	ret = process_buffer(in);
+
+	return ret;
+}
+
+static int end(struct sr_input *in)
+{
+	struct sr_datafeed_packet packet;
+	struct context *inc;
+	int ret;
+
+	if (in->sdi_ready)
+		ret = process_buffer(in);
+	else
+		ret = SR_OK;
+
+	inc = in->priv;
+	if (inc->started) {
+		packet.type = SR_DF_END;
+		sr_session_send(in->sdi, &packet);
+	}
+
+	return ret;
+}
+
+static struct sr_option options[] = {
+	{ "numchannels", "Number of channels", "Number of channels", NULL, NULL },
+	{ "samplerate", "Sample rate", "Sample rate", NULL, NULL },
+	{ "format", "Format", "Numeric format", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_ref_sink(g_variant_new_int32(DEFAULT_NUM_CHANNELS));
+		options[1].def = g_variant_ref_sink(g_variant_new_uint64(DEFAULT_SAMPLERATE));
+		options[2].def = g_variant_ref_sink(g_variant_new_string(sample_formats[0].fmt_name));
+		for (unsigned int i = 0; i < ARRAY_SIZE(sample_formats); i++) {
+			options[2].values = g_slist_append(options[2].values,
+				g_variant_ref_sink(g_variant_new_string(sample_formats[i].fmt_name)));
+		}
+	}
+
+	return options;
+}
+
+static void cleanup(struct sr_input *in)
+{
+	struct context *inc;
+
+	inc = in->priv;
+	g_variant_unref(options[0].def);
+	g_variant_unref(options[1].def);
+	g_variant_unref(options[2].def);
+	g_slist_free_full(options[2].values, (GDestroyNotify)g_variant_unref);
+	g_free(inc);
+	in->priv = NULL;
+}
+
+SR_PRIV struct sr_input_module input_raw_analog = {
+	.id = "raw_analog",
+	.name = "RAW analog",
+	.desc = "analog signals without header",
+	.exts = (const char*[]){"raw", "bin", NULL},
+	.options = get_options,
+	.init = init,
+	.receive = receive,
+	.end = end,
+	.cleanup = cleanup,
+};
diff --git a/src/input/trace32_ad.c b/src/input/trace32_ad.c
new file mode 100644
index 0000000..81df295
--- /dev/null
+++ b/src/input/trace32_ad.c
@@ -0,0 +1,803 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Soeren Apel <soeren at apelpie.net>
+ * Copyright (C) 2015 Bert Vermeulen <bert at 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/>.
+ */
+
+/*
+ * Usage notes:
+ * This input module reads .ad files created using the
+ * following practice commands:
+ *
+ * I.SAVE <file> /NoCompress
+ * IPROBE.SAVE <file> /NoCompress
+ *
+ * It currently cannot make use of files that have been
+ * saved using /QuickCompress, /Compress or /ZIP.
+ * As a workaround you may load the file in PowerView
+ * using I.LOAD / IPROBE.LOAD and re-save using /NoCompress.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/trace32_ad"
+
+#define MAX_CHUNK_SIZE    4096
+#define OUTBUF_FLUSH_SIZE 10240
+#define MAX_POD_COUNT     12
+#define HEADER_SIZE       80
+
+#define TIMESTAMP_RESOLUTION  ((double)0.000000000078125) /* 0.078125 ns */
+
+/*
+ * The resolution equals a sampling freq of 12.8 GHz. That's a bit high
+ * for inter-record sample generation, so we scale it down to 200 MHz
+ * for now. That way, the scaling factor becomes 32.
+ */
+#define SAMPLING_FREQ 200000000
+#define TIMESTAMP_SCALE ((1 / TIMESTAMP_RESOLUTION) / SAMPLING_FREQ)
+
+enum {
+	AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
+	AD_FORMAT_TXTHDR      /* Textual header, binary data */
+};
+
+enum {
+	AD_DEVICE_PI = 1, /* Data recorded by LA-7940 PowerIntegrator or */
+                          /* LA-394x PowerIntegrator II. */
+	AD_DEVICE_IPROBE  /* Data recorded by LA-769x PowerTrace II IProbe. */
+	/* Missing file format info for LA-793x ICD PowerProbe */
+	/* Missing file format info for LA-4530 uTrace analog probe */
+};
+
+enum {
+	AD_MODE_250MHZ = 0,
+	AD_MODE_500MHZ = 1
+};
+
+enum {
+	AD_COMPR_NONE  = 0, /* File created with /NOCOMPRESS */
+	AD_COMPR_QCOMP = 6  /* File created with /COMPRESS or /QUICKCOMPRESS */
+};
+
+struct context {
+	gboolean meta_sent;
+	gboolean header_read, records_read, trigger_sent;
+	char format, device, record_mode, compression;
+	char pod_status[MAX_POD_COUNT];
+	struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
+	uint64_t trigger_timestamp;
+	uint32_t record_size, record_count, cur_record;
+	int32_t last_record;
+	GString *out_buf;
+};
+
+static int process_header(GString *buf, struct context *inc);
+static void create_channels(struct sr_input *in);
+
+static char get_pod_name_from_id(int id)
+{
+	switch (id) {
+	case 0:  return 'A';
+	case 1:  return 'B';
+	case 2:  return 'C';
+	case 3:  return 'D';
+	case 4:  return 'E';
+	case 5:  return 'F';
+	case 6:  return 'J';
+	case 7:  return 'K';
+	case 8:  return 'L';
+	case 9:  return 'M';
+	case 10: return 'N';
+	case 11: return 'O';
+	default:
+		sr_err("get_pod_name_from_id() called with invalid ID %d!", id);
+	}
+	return 'X';
+}
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+	struct context *inc;
+	int pod;
+	char id[17];
+
+	in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	in->priv = g_malloc0(sizeof(struct context));
+
+	inc = in->priv;
+
+	/* Enable the pods the user chose to see. */
+	for (pod = 0; pod < MAX_POD_COUNT; pod++) {
+		g_snprintf(id, sizeof(id), "pod%c", get_pod_name_from_id(pod));
+		if (g_variant_get_boolean(g_hash_table_lookup(options, id)))
+			inc->pod_status[pod] = 1;
+	}
+
+	create_channels(in);
+	if (g_slist_length(in->sdi->channels) == 0) {
+		sr_err("No pods were selected and thus no channels created, aborting.");
+		g_free(in->priv);
+		g_free(in->sdi);
+		return SR_ERR;
+	}
+
+	inc->out_buf = g_string_sized_new(OUTBUF_FLUSH_SIZE);
+
+	return SR_OK;
+}
+
+static int format_match(GHashTable *metadata)
+{
+	GString *buf;
+
+	buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+
+	return process_header(buf, NULL);
+}
+
+static int process_header(GString *buf, struct context *inc)
+{
+	char *format_name, *format_name_sig;
+	int i, record_size, device_id;
+
+	/*
+	 * 00-31 (0x00-1F) file format name
+	 * 32-39 (0x20-27) trigger timestamp       u64
+	 * 40-47 (0x28-2F) unused
+	 * 48    (0x30)    compression
+	 * 49-53 (0x31-35) ??
+	 *       50 (0x32) 0x00 (PI), 0x01 (iprobe)
+	 * 54    (0x36)    0x08 (PI 250/500), 0x0A (iprobe 250)
+	 * 55    (0x37)    0x00 (250), 0x01 (500)
+	 * 56    (0x38)    record size             u8
+	 * 57-59 (0x39-3B) const 0x00
+	 * 60-63 (0x3C-3F) number of records       u32
+	 * 64-67 (0x40-43) id of last record       s32
+	 * 68-77 (0x44-4D) ??
+	 *       71 (0x47) const 0x80=128
+	 *       72 (0x48) const 0x01
+	 * 78-79 (0x4E-4F) ??
+	 */
+
+	/* Note: inc is off-limits until we check whether it's a valid pointer. */
+
+	format_name = g_strndup(buf->str, 32);
+
+	/* File format name ends on 0x20/0x1A, let's remove both. */
+	for (i = 1; i < 31; i++) {
+		if (format_name[i] == 0x1A) {
+			format_name[i - 1] = 0;
+			format_name[i] = 0;
+		}
+	}
+	g_strchomp(format_name); /* This is for additional padding spaces. */
+
+	format_name_sig = g_strndup(format_name, 5);
+
+	/* Desired file formats either start with digit+space or "trace32". */
+	if (g_strcmp0(format_name_sig, "trace32")) {
+		if (inc)
+			inc->format = AD_FORMAT_BINHDR;
+	} else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == 0x20)) {
+		if (inc)
+			inc->format = AD_FORMAT_TXTHDR;
+		g_free(format_name_sig);
+		g_free(format_name);
+		sr_err("This format isn't implemented yet, aborting.");
+		return SR_ERR;
+	} else {
+		g_free(format_name_sig);
+		g_free(format_name);
+		sr_err("Don't know this file format, aborting.");
+		return SR_ERR;
+	}
+
+	sr_dbg("File says it's \"%s\"", format_name);
+
+	record_size = R8(buf->str + 56);
+	device_id = 0;
+
+	if (g_strcmp0(format_name, "trace32 power integrator data") == 0) {
+		if (record_size == 28 || record_size == 45)
+			device_id = AD_DEVICE_PI;
+	} else if (g_strcmp0(format_name, "trace32 iprobe data") == 0) {
+		if (record_size == 11)
+			device_id = AD_DEVICE_IPROBE;
+	}
+
+	if (!device_id) {
+		g_free(format_name_sig);
+		g_free(format_name);
+		sr_err("Don't know how to handle this file with record size %d.",
+			record_size);
+		return SR_ERR;
+	}
+
+	g_free(format_name_sig);
+	g_free(format_name);
+
+	/* Stop processing the header if we just want to identify the file. */
+	if (!inc)
+		return SR_OK;
+
+	inc->device       = device_id;
+	inc->trigger_timestamp = RL64(buf->str + 32);
+	inc->compression  = R8(buf->str + 48);        /* Maps to the enum. */
+	inc->record_mode  = R8(buf->str + 55);        /* Maps to the enum. */
+	inc->record_size  = record_size;
+	inc->record_count = RL32(buf->str + 60);
+	inc->last_record  = RL32S(buf->str + 64);
+
+	sr_dbg("Trigger occured at %lf s.",
+		inc->trigger_timestamp * TIMESTAMP_RESOLUTION);
+	sr_dbg("File contains %d records: first one is %d, last one is %d.",
+		inc->record_count, (inc->last_record - inc->record_count + 1),
+		inc->last_record);
+
+	/* Check if we can work with this compression. */
+	if (inc->compression != AD_COMPR_NONE) {
+		sr_err("File uses unsupported compression (0x%02X), can't continue.",
+			inc->compression);
+		return SR_ERR;
+	}
+
+	inc->header_read = TRUE;
+
+	return SR_OK;
+}
+
+static void create_channels(struct sr_input *in)
+{
+	struct context *inc;
+	int pod, channel, chan_id;
+	char name[8];
+
+	inc = in->priv;
+	chan_id = 0;
+
+	for (pod = 0; pod < MAX_POD_COUNT; pod++) {
+		if (!inc->pod_status[pod])
+			continue;
+
+		for (channel = 0; channel < 16; channel++) {
+			snprintf(name, 8, "%c%d", get_pod_name_from_id(pod), channel);
+			inc->channels[pod][channel] =
+				sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
+			chan_id++;
+		}
+
+		snprintf(name, 8, "CLK%c", get_pod_name_from_id(pod));
+		inc->channels[pod][16] =
+			sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
+		chan_id++;
+	}
+}
+
+static void send_metadata(struct sr_input *in)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_meta meta;
+	struct sr_config *src;
+	struct context *inc;
+
+	packet.type = SR_DF_META;
+	packet.payload = &meta;
+	src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(SAMPLING_FREQ));
+	meta.config = g_slist_append(NULL, src);
+	sr_session_send(in->sdi, &packet);
+	g_slist_free(meta.config);
+	sr_config_free(src);
+
+	inc = in->priv;
+	inc->meta_sent = TRUE;
+}
+
+static void flush_output_buffer(struct sr_input *in)
+{
+	struct context *inc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_logic logic;
+
+	inc = in->priv;
+
+	if (inc->out_buf->len) {
+		packet.type = SR_DF_LOGIC;
+		packet.payload = &logic;
+		logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+		logic.data = inc->out_buf->str;
+		logic.length = inc->out_buf->len;
+		sr_session_send(in->sdi, &packet);
+
+		g_string_truncate(inc->out_buf, 0);
+	}
+}
+
+static void process_record_pi(struct sr_input *in, gsize start)
+{
+	struct sr_datafeed_packet packet;
+	struct context *inc;
+	uint64_t timestamp, next_timestamp;
+	uint32_t pod_data;
+	char single_payload[12 * 3];
+	GString *buf;
+	int i, pod_count, clk_offset, packet_count, pod;
+	int payload_bit, payload_len, value;
+
+	inc = in->priv;
+	buf = in->buf;
+
+	/*
+	 * 00-07 timestamp
+	 * 08-09 A15..0
+	 * 10-11 B15..0
+	 * 12-13 C15..0
+	 * 14-15 D15..0
+	 * 16-17 E15..0
+	 * 18-19 F15..0
+	 * 20-23 ??
+	 * 24-25 J15..0                         Not present in 500MHz mode
+	 * 26-27 K15..0                         Not present in 500MHz mode
+	 * 28-29 L15..0                         Not present in 500MHz mode
+	 * 30-31 M15..0                         Not present in 500MHz mode
+	 * 32-33 N15..0                         Not present in 500MHz mode
+	 * 34-35 O15..0                         Not present in 500MHz mode
+	 * 36-39 ??                             Not present in 500MHz mode
+	 * 40/24 CLKF..A (32=CLKF, .., 1=CLKA)
+	 * 41    CLKO..J (32=CLKO, .., 1=CLKJ)  Not present in 500MHz mode
+	 * 42/25    ??
+	 * 43/26    ??
+	 * 44/27    ??
+	 */
+
+	timestamp = RL64(buf->str + start);
+
+	if (inc->record_mode == AD_MODE_500MHZ) {
+		pod_count = 6;
+		clk_offset = 24;
+	} else {
+		pod_count = 12;
+		clk_offset = 40;
+	}
+
+	payload_bit = 0;
+	payload_len = 0;
+	single_payload[0] = 0;
+
+	for (pod = 0; pod < pod_count; pod++) {
+		if (!inc->pod_status[pod])
+			continue;
+
+		switch (pod) {
+		case 0: /* A */
+			pod_data = RL16(buf->str + start + 8);
+			pod_data |= (RL16(buf->str + start + clk_offset) & 1) << 16;
+			break;
+		case 1: /* B */
+			pod_data = RL16(buf->str + start + 10);
+			pod_data |= (RL16(buf->str + start + clk_offset) & 2) << 15;
+			break;
+		case 2: /* C */
+			pod_data = RL16(buf->str + start + 12);
+			pod_data |= (RL16(buf->str + start + clk_offset) & 4) << 14;
+			break;
+		case 3: /* D */
+			pod_data = RL16(buf->str + start + 14);
+			pod_data |= (RL16(buf->str + start + clk_offset) & 8) << 13;
+			break;
+		case 4: /* E */
+			pod_data = RL16(buf->str + start + 16);
+			pod_data |= (RL16(buf->str + start + clk_offset) & 16) << 12;
+			break;
+		case 5: /* F */
+			pod_data = RL16(buf->str + start + 18);
+			pod_data |= (RL16(buf->str + start + clk_offset) & 32) << 11;
+			break;
+		case 6: /* J */
+			pod_data = RL16(buf->str + start + 24);
+			pod_data |= (RL16(buf->str + start + 41) & 1) << 16;
+			break;
+		case 7: /* K */
+			pod_data = RL16(buf->str + start + 26);
+			pod_data |= (RL16(buf->str + start + 41) & 2) << 15;
+			break;
+		case 8: /* L */
+			pod_data = RL16(buf->str + start + 28);
+			pod_data |= (RL16(buf->str + start + 41) & 4) << 14;
+			break;
+		case 9: /* M */
+			pod_data = RL16(buf->str + start + 30);
+			pod_data |= (RL16(buf->str + start + 41) & 8) << 13;
+			break;
+		case 10: /* N */
+			pod_data = RL16(buf->str + start + 32);
+			pod_data |= (RL16(buf->str + start + 41) & 16) << 12;
+			break;
+		case 11: /* O */
+			pod_data = RL16(buf->str + start + 34);
+			pod_data |= (RL16(buf->str + start + 41) & 32) << 11;
+			break;
+		default:
+			sr_err("Don't know how to obtain data for pod %d.", pod);
+		}
+
+		for (i = 0; i < 17; i++) {
+			value = (pod_data >> i) & 1;
+			single_payload[payload_len] |= value << payload_bit;
+
+			payload_bit++;
+			if (payload_bit > 7) {
+				payload_bit = 0;
+				payload_len++;
+				single_payload[payload_len] = 0;
+			}
+		}
+	}
+
+	/* Make sure that payload_len accounts for any incomplete bytes used. */
+	if (payload_bit)
+		payload_len++;
+
+	i = (g_slist_length(in->sdi->channels) + 7) / 8;
+	if (payload_len != i) {
+		sr_err("Payload unit size is %d but should be %d!", payload_len, i);
+		return;
+	}
+
+	if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
+		sr_dbg("Trigger @%lf s, record #%d.",
+			timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
+
+		packet.type = SR_DF_TRIGGER;
+		packet.payload = NULL;
+		sr_session_send(in->sdi, &packet);
+		inc->trigger_sent = TRUE;
+	}
+
+	/* Is this the last record in the file? */
+	if (inc->cur_record == inc->record_count - 1) {
+		/* It is, so send the last sample data only once. */
+		g_string_append_len(inc->out_buf, single_payload, payload_len);
+	} else {
+		/* It's not, so fill the time gap by sending lots of data. */
+		next_timestamp = RL64(buf->str + start + inc->record_size);
+		packet_count = (int)(next_timestamp - timestamp) / TIMESTAMP_SCALE;
+
+		/* Make sure we send at least one data set. */
+		if (packet_count == 0)
+			packet_count = 1;
+
+		for (i = 0; i < packet_count; i++)
+			g_string_append_len(inc->out_buf, single_payload, payload_len);
+	}
+
+	if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+		flush_output_buffer(in);
+}
+
+static void process_record_iprobe(struct sr_input *in, gsize start)
+{
+	struct sr_datafeed_packet packet;
+	struct context *inc;
+	uint64_t timestamp, next_timestamp;
+	char single_payload[3];
+	int i, payload_len, packet_count;
+
+	inc = in->priv;
+
+	/*
+	 * 00-07 timestamp
+	 * 08-09 IP15..0
+	 * 10    CLK
+	 */
+
+	timestamp = RL64(in->buf->str + start);
+	single_payload[0] = R8(in->buf->str + start + 8);
+	single_payload[1] = R8(in->buf->str + start + 9);
+	single_payload[2] = R8(in->buf->str + start + 10) & 1;
+	payload_len = 3;
+
+	if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
+		sr_dbg("Trigger @%lf s, record #%d.",
+			timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
+
+		packet.type = SR_DF_TRIGGER;
+		packet.payload = NULL;
+		sr_session_send(in->sdi, &packet);
+		inc->trigger_sent = TRUE;
+	}
+
+	/* Is this the last record in the file? */
+	if (inc->cur_record == inc->record_count - 1) {
+		/* It is, so send the last sample data only once. */
+		g_string_append_len(inc->out_buf, single_payload, payload_len);
+	} else {
+		/* It's not, so fill the time gap by sending lots of data. */
+		next_timestamp = RL64(in->buf->str + start + inc->record_size);
+		packet_count = (int)(next_timestamp - timestamp) / TIMESTAMP_SCALE;
+
+		/* Make sure we send at least one data set. */
+		if (packet_count == 0)
+			packet_count = 1;
+
+		for (i = 0; i < packet_count; i++)
+			g_string_append_len(inc->out_buf, single_payload, payload_len);
+	}
+
+	if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+		flush_output_buffer(in);
+}
+
+static void process_practice_token(struct sr_input *in, char *cmd_token)
+{
+	struct context *inc;
+	char **tokens;
+	char chan_suffix[2], chan_name[33];
+	char *s1, *s2;
+	int pod, ch;
+	struct sr_channel *channel;
+
+	inc = in->priv;
+
+	/*
+	 * Commands of interest (I may also be IPROBE):
+	 *
+	 * I.TWIDTH
+	 * I.TPREDELAY
+	 * I.TDELAY
+	 * I.TYSNC.SELECT I.A0 HIGH
+	 * NAME.SET <port.chan> <name> <+/-> ...
+	 */
+
+	if (!cmd_token)
+		return;
+
+	if (cmd_token[0] == 0)
+		return;
+
+	tokens = g_strsplit(cmd_token, " ", 0);
+
+	if (!tokens)
+		return;
+
+	if (g_strcmp0(tokens[0], "NAME.SET") == 0) {
+		/* Let the user know when the channel has been inverted. */
+		/* This *should* be token #3 but there's an additonal space, making it #4. */
+		chan_suffix[0] = 0;
+		chan_suffix[1] = 0;
+		if (tokens[4]) {
+			if (tokens[4][0] == '-')
+				chan_suffix[0] = '-'; /* This is the way PowerView shows it. */
+		}
+
+		/*
+		 * Command is using structure "NAME.SET I.A00 I.XYZ" or
+		 * "NAME.SET IP.00 IP.XYZ", depending on the device used.
+		 * Let's get strings with the I./IP. from both tokens removed.
+		 */
+		s1 = g_strstr_len(tokens[1], -1, ".") + 1;
+		s2 = g_strstr_len(tokens[2], -1, ".") + 1;
+
+		if (g_strcmp0(s1, "CLK") == 0) {
+			/* CLK for iprobe */
+			pod = 0;
+			ch = 16;
+		} else if ((strlen(s1) == 4) && g_ascii_isupper(s1[3])) {
+			/* CLKA/B/J/K for PowerIntegrator */
+			pod = s1[3] - (char)'A';
+			ch = 16;
+		} else if (g_ascii_isupper(s1[0])) {
+			/* A00 for PowerIntegrator */
+			pod = s1[0] - (char)'A';
+			ch = atoi(s1 + 1);
+		} else {
+			/* 00 for iprobe */
+			pod = 0;
+			ch = atoi(s1);
+		}
+
+		channel = inc->channels[pod][ch];
+		g_snprintf(chan_name, sizeof(chan_name), "%s%s", s2, chan_suffix);
+
+		sr_dbg("Changing channel name for %s to %s.", s1, chan_name);
+		sr_dev_channel_name_set(channel, chan_name);
+	}
+
+	g_strfreev(tokens);
+}
+
+static void process_practice(struct sr_input *in)
+{
+	char delimiter[3];
+	char **tokens, *token;
+	int i;
+
+	/* Gather all input data until we see the end marker. */
+	if (in->buf->str[in->buf->len - 1] != 0x29)
+		return;
+
+	delimiter[0] = 0x0A;
+	delimiter[1] = ' ';
+	delimiter[2] = 0;
+
+	tokens = g_strsplit(in->buf->str, delimiter, 0);
+
+	/* Special case: first token contains the start marker, too. Skip it. */
+	token = tokens[0];
+	for (i = 0; token[i]; i++) {
+		if (token[i] == ' ')
+			process_practice_token(in, token + i + 1);
+	}
+
+	for (i = 1; tokens[i]; i++)
+		process_practice_token(in, tokens[i]);
+
+	g_strfreev(tokens);
+
+	g_string_erase(in->buf, 0, in->buf->len);
+}
+
+static int process_buffer(struct sr_input *in)
+{
+	struct context *inc;
+	int i, chunk_size, res;
+
+	inc = in->priv;
+
+	if (!inc->header_read) {
+		res = process_header(in->buf, inc);
+		g_string_erase(in->buf, 0, HEADER_SIZE);
+		if (res != SR_OK)
+			return res;
+	}
+
+	if (!inc->meta_sent) {
+		std_session_send_df_header(in->sdi, LOG_PREFIX);
+		send_metadata(in);
+	}
+
+	if (!inc->records_read) {
+		/* Cut off at a multiple of the record size. */
+		chunk_size = ((in->buf->len) / inc->record_size) * inc->record_size;
+
+		/* There needs to be at least one more record process_record() can peek into. */
+		chunk_size -= inc->record_size;
+
+		for (i = 0; (i < chunk_size) && (!inc->records_read); i += inc->record_size) {
+			switch (inc->device) {
+			case AD_DEVICE_PI:
+				process_record_pi(in, i);
+				break;
+			case AD_DEVICE_IPROBE:
+				process_record_iprobe(in, i);
+				break;
+			default:
+				sr_err("Trying to process records for unknown device!");
+				return SR_ERR;
+			}
+
+			inc->cur_record++;
+			if (inc->cur_record == inc->record_count)
+				inc->records_read = TRUE;
+		}
+
+		g_string_erase(in->buf, 0, i);
+	}
+
+	if (inc->records_read) {
+		/* Read practice commands that configure the setup. */
+		process_practice(in);
+	}
+
+	return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+	g_string_append_len(in->buf, buf->str, buf->len);
+
+	if (!in->sdi_ready) {
+		/* sdi is ready, notify frontend. */
+		in->sdi_ready = TRUE;
+		return SR_OK;
+	}
+
+	return process_buffer(in);
+}
+
+static int end(struct sr_input *in)
+{
+	struct context *inc;
+	struct sr_datafeed_packet packet;
+	int ret;
+
+	inc = in->priv;
+
+	if (in->sdi_ready)
+		ret = process_buffer(in);
+	else
+		ret = SR_OK;
+
+	flush_output_buffer(in);
+
+	if (inc->meta_sent) {
+		packet.type = SR_DF_END;
+		sr_session_send(in->sdi, &packet);
+	}
+
+	return ret;
+}
+
+static struct sr_option options[] = {
+	{ "podA", "Import pod A / iprobe",
+		"Create channels and data for pod A / iprobe", NULL, NULL },
+
+	{ "podB", "Import pod B", "Create channels and data for pod B", NULL, NULL },
+	{ "podC", "Import pod C", "Create channels and data for pod C", NULL, NULL },
+	{ "podD", "Import pod D", "Create channels and data for pod D", NULL, NULL },
+	{ "podE", "Import pod E", "Create channels and data for pod E", NULL, NULL },
+	{ "podF", "Import pod F", "Create channels and data for pod F", NULL, NULL },
+	{ "podJ", "Import pod J", "Create channels and data for pod J", NULL, NULL },
+	{ "podK", "Import pod K", "Create channels and data for pod K", NULL, NULL },
+	{ "podL", "Import pod L", "Create channels and data for pod L", NULL, NULL },
+	{ "podM", "Import pod M", "Create channels and data for pod M", NULL, NULL },
+	{ "podN", "Import pod N", "Create channels and data for pod N", NULL, NULL },
+	{ "podO", "Import pod O", "Create channels and data for pod O", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
+		options[1].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[2].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[3].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[4].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[5].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[6].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[7].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[8].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+		options[11].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+	}
+
+	return options;
+}
+
+SR_PRIV struct sr_input_module input_trace32_ad = {
+	.id = "trace32_ad",
+	.name = "Trace32_ad",
+	.desc = "Lauterbach Trace32 logic analyzer data",
+	.exts = (const char*[]){"ad", NULL},
+	.options = get_options,
+	.metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
+	.format_match = format_match,
+	.init = init,
+	.receive = receive,
+	.end = end,
+};
diff --git a/src/input/vcd.c b/src/input/vcd.c
new file mode 100644
index 0000000..66f126b
--- /dev/null
+++ b/src/input/vcd.c
@@ -0,0 +1,647 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Petteri Aimonen <jpa at sr.mail.kapsi.fi>
+ * Copyright (C) 2014 Bert Vermeulen <bert at 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/>.
+ */
+
+/* The VCD input module has the following options:
+ *
+ * numchannels: Maximum number of channels to use. The channels are
+ *              detected in the same order as they are listed
+ *              in the $var sections of the VCD file.
+ *
+ * skip:        Allows skipping until given timestamp in the file.
+ *              This can speed up analyzing of long captures.
+ *            
+ *              Value < 0: Skip until first timestamp listed in
+ *              the file. (default)
+ *
+ *              Value = 0: Do not skip, instead generate samples
+ *              beginning from timestamp 0.
+ *
+ *              Value > 0: Start at the given timestamp.
+ *
+ * downsample:  Divide the samplerate by the given factor.
+ *              This can speed up analyzing of long captures.
+ *
+ * compress:    Compress idle periods longer than this value.
+ *              This can speed up analyzing of long captures.
+ *              Default 0 = don't compress.
+ *
+ * Based on Verilog standard IEEE Std 1364-2001 Version C
+ *
+ * Supported features:
+ * - $var with 'wire' and 'reg' types of scalar variables
+ * - $timescale definition for samplerate
+ * - multiple character variable identifiers
+ *
+ * Most important unsupported features:
+ * - vector variables (bit vectors etc.)
+ * - analog, integer and real number variables
+ * - $dumpvars initial value declaration
+ * - $scope namespaces
+ * - more than 64 channels
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/vcd"
+
+#define CHUNKSIZE (1024 * 1024)
+
+struct context {
+	gboolean started;
+	gboolean got_header;
+	uint64_t samplerate;
+	unsigned int maxchannels;
+	unsigned int channelcount;
+	int downsample;
+	unsigned compress;
+	int64_t skip;
+	gboolean skip_until_end;
+	GSList *channels;
+	size_t bytes_per_sample;
+	size_t samples_in_buffer;
+	uint8_t *buffer;
+	uint8_t *current_levels;
+};
+
+struct vcd_channel {
+	gchar *name;
+	gchar *identifier;
+};
+
+/*
+ * Reads a single VCD section from input file and parses it to name/contents.
+ * e.g. $timescale 1ps $end  => "timescale" "1ps"
+ */
+static gboolean parse_section(GString *buf, gchar **name, gchar **contents)
+{
+	GString *sname, *scontent;
+	gboolean status;
+	unsigned int pos;
+
+	*name = *contents = NULL;
+	status = FALSE;
+	pos = 0;
+
+	/* Skip any initial white-space. */
+	while (pos < buf->len && g_ascii_isspace(buf->str[pos]))
+		pos++;
+
+	/* Section tag should start with $. */
+	if (buf->str[pos++] != '$')
+		return FALSE;
+
+	sname = g_string_sized_new(32);
+	scontent = g_string_sized_new(128);
+
+	/* Read the section tag. */
+	while (pos < buf->len && !g_ascii_isspace(buf->str[pos]))
+		g_string_append_c(sname, buf->str[pos++]);
+
+	/* Skip whitespace before content. */
+	while (pos < buf->len && g_ascii_isspace(buf->str[pos]))
+		pos++;
+
+	/* Read the content. */
+	while (pos < buf->len - 4 && strncmp(buf->str + pos, "$end", 4))
+		g_string_append_c(scontent, buf->str[pos++]);
+
+	if (sname->len && pos < buf->len - 4 && !strncmp(buf->str + pos, "$end", 4)) {
+		status = TRUE;
+		pos += 4;
+		while (pos < buf->len && g_ascii_isspace(buf->str[pos]))
+			pos++;
+		g_string_erase(buf, 0, pos);
+	}
+
+	*name = g_string_free(sname, !status);
+	*contents = g_string_free(scontent, !status);
+	if (*contents)
+		g_strchomp(*contents);
+
+	return status;
+}
+
+static void free_channel(void *data)
+{
+	struct vcd_channel *vcd_ch = data;
+	g_free(vcd_ch->name);
+	g_free(vcd_ch->identifier);
+	g_free(vcd_ch);
+}
+
+/* Remove empty parts from an array returned by g_strsplit. */
+static void remove_empty_parts(gchar **parts)
+{
+	gchar **src = parts;
+	gchar **dest = parts;
+	while (*src != NULL) {
+		if (**src != '\0')
+			*dest++ = *src;
+		src++;
+	}
+
+	*dest = NULL;
+}
+
+/*
+ * Parse VCD header to get values for context structure.
+ * The context structure should be zeroed before calling this.
+ */
+static gboolean parse_header(const struct sr_input *in, GString *buf)
+{
+	struct vcd_channel *vcd_ch;
+	uint64_t p, q;
+	struct context *inc;
+	gboolean status;
+	gchar *name, *contents, **parts;
+
+	inc = in->priv;
+	name = contents = NULL;
+	status = FALSE;
+	while (parse_section(buf, &name, &contents)) {
+		sr_dbg("Section '%s', contents '%s'.", name, contents);
+
+		if (g_strcmp0(name, "enddefinitions") == 0) {
+			status = TRUE;
+			break;
+		} else if (g_strcmp0(name, "timescale") == 0) {
+			/*
+			 * The standard allows for values 1, 10 or 100
+			 * and units s, ms, us, ns, ps and fs.
+			 */
+			if (sr_parse_period(contents, &p, &q) == SR_OK) {
+				inc->samplerate = q / p;
+				if (q % p != 0) {
+					/* Does not happen unless time value is non-standard */
+					sr_warn("Inexact rounding of samplerate, %" PRIu64 " / %" PRIu64 " to %" PRIu64 " Hz.",
+						q, p, inc->samplerate);
+				}
+
+				sr_dbg("Samplerate: %" PRIu64, inc->samplerate);
+			} else {
+				sr_err("Parsing timescale failed.");
+			}
+		} else if (g_strcmp0(name, "var") == 0) {
+			/* Format: $var type size identifier reference [opt. index] $end */
+			unsigned int length;
+
+			parts = g_strsplit_set(contents, " \r\n\t", 0);
+			remove_empty_parts(parts);
+			length = g_strv_length(parts);
+
+			if (length != 4 && length != 5)
+				sr_warn("$var section should have 4 or 5 items");
+			else if (g_strcmp0(parts[0], "reg") != 0 && g_strcmp0(parts[0], "wire") != 0)
+				sr_info("Unsupported signal type: '%s'", parts[0]);
+			else if (strtol(parts[1], NULL, 10) != 1)
+				sr_info("Unsupported signal size: '%s'", parts[1]);
+			else if (inc->maxchannels && inc->channelcount >= inc->maxchannels)
+				sr_warn("Skipping '%s%s' because only %d channels requested.",
+					parts[3], parts[4] ? : "", inc->maxchannels);
+			else {
+				vcd_ch = g_malloc(sizeof(struct vcd_channel));
+				vcd_ch->identifier = g_strdup(parts[2]);
+				if (length == 4)
+					vcd_ch->name = g_strdup(parts[3]);
+				else
+					vcd_ch->name = g_strconcat(parts[3], parts[4], NULL);
+
+				sr_info("Channel %d is '%s' identified by '%s'.",
+						inc->channelcount, vcd_ch->name, vcd_ch->identifier);
+
+				sr_channel_new(in->sdi, inc->channelcount++, SR_CHANNEL_LOGIC, TRUE, vcd_ch->name);
+				inc->channels = g_slist_append(inc->channels, vcd_ch);
+			}
+
+			g_strfreev(parts);
+		}
+
+		g_free(name);
+		name = NULL;
+		g_free(contents);
+		contents = NULL;
+	}
+	g_free(name);
+	g_free(contents);
+
+	/*
+	 * Compute how many bytes each sample will have and initialize the
+	 * current levels. The current levels will be updated whenever VCD
+	 * has changes.
+	 */
+	inc->bytes_per_sample = (inc->channelcount + 7) / 8;
+	inc->current_levels = g_malloc0(inc->bytes_per_sample);
+
+	inc->got_header = status;
+
+	return status;
+}
+
+static int format_match(GHashTable *metadata)
+{
+	GString *buf, *tmpbuf;
+	gboolean status;
+	gchar *name, *contents;
+
+	buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+	tmpbuf = g_string_new_len(buf->str, buf->len);
+
+	/*
+	 * If we can parse the first section correctly,
+	 * then it is assumed to be a VCD file.
+	 */
+	status = parse_section(tmpbuf, &name, &contents);
+	g_string_free(tmpbuf, TRUE);
+	g_free(name);
+	g_free(contents);
+
+	return status ? SR_OK : SR_ERR;
+}
+
+/* Send all accumulated bytes from inc->buffer. */
+static void send_buffer(const struct sr_input *in)
+{
+	struct context *inc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_logic logic;
+
+	inc = in->priv;
+
+	if (inc->samples_in_buffer == 0)
+		return;
+
+	packet.type = SR_DF_LOGIC;
+	packet.payload = &logic;
+	logic.unitsize = inc->bytes_per_sample;
+	logic.data = inc->buffer;
+	logic.length = inc->bytes_per_sample * inc->samples_in_buffer;
+	sr_session_send(in->sdi, &packet);
+	inc->samples_in_buffer = 0;
+}
+
+/*
+ * Add N copies of the current sample to buffer.
+ * When the buffer fills up, automatically send it.
+ */
+static void add_samples(const struct sr_input *in, size_t count)
+{
+	struct context *inc;
+	size_t samples_per_chunk;
+	size_t space_left, i;
+	uint8_t *p;
+
+	inc = in->priv;
+	samples_per_chunk = CHUNKSIZE / inc->bytes_per_sample;
+
+	while (count) {
+		space_left = samples_per_chunk - inc->samples_in_buffer;
+
+		if (space_left > count)
+			space_left = count;
+
+		p = inc->buffer + inc->samples_in_buffer * inc->bytes_per_sample;
+		for (i = 0; i < space_left; i++) {
+			memcpy(p, inc->current_levels, inc->bytes_per_sample);
+			p += inc->bytes_per_sample;
+			inc->samples_in_buffer++;
+			count--;
+		}
+
+		if (inc->samples_in_buffer == samples_per_chunk)
+			send_buffer(in);
+	}
+}
+
+/* Set the channel level depending on the identifier and parsed value. */
+static void process_bit(struct context *inc, char *identifier, unsigned int bit)
+{
+	GSList *l;
+	struct vcd_channel *vcd_ch;
+	unsigned int j;
+
+	for (j = 0, l = inc->channels; j < inc->channelcount && l; j++, l = l->next) {
+		vcd_ch = l->data;
+		if (g_strcmp0(identifier, vcd_ch->identifier) == 0) {
+			/* Found our channel. */
+			size_t byte_idx = (j / 8);
+			size_t bit_idx = j - 8 * byte_idx;
+			if (bit)
+				inc->current_levels[byte_idx] |= (uint8_t)1 << bit_idx;
+			else
+				inc->current_levels[byte_idx] &= ~((uint8_t)1 << bit_idx);
+			break;
+		}
+	}
+	if (j == inc->channelcount)
+		sr_dbg("Did not find channel for identifier '%s'.", identifier);
+}
+
+/* Parse a set of lines from the data section. */
+static void parse_contents(const struct sr_input *in, char *data)
+{
+	struct context *inc;
+	uint64_t timestamp, prev_timestamp;
+	unsigned int bit, i;
+	char **tokens;
+
+	inc = in->priv;
+	prev_timestamp = 0;
+
+	/* Read one space-delimited token at a time. */
+	tokens = g_strsplit_set(data, " \t\r\n", 0);
+	remove_empty_parts(tokens);
+	for (i = 0; tokens[i]; i++) {
+		if (inc->skip_until_end) {
+			if (!strcmp(tokens[i], "$end")) {
+				/* Done with unhandled/unknown section. */
+				inc->skip_until_end = FALSE;
+				break;
+			}
+		}
+		if (tokens[i][0] == '#' && g_ascii_isdigit(tokens[i][1])) {
+			/* Numeric value beginning with # is a new timestamp value */
+			timestamp = strtoull(tokens[i] + 1, NULL, 10);
+
+			if (inc->downsample > 1)
+				timestamp /= inc->downsample;
+
+			/*
+			 * Skip < 0 => skip until first timestamp.
+			 * Skip = 0 => don't skip
+			 * Skip > 0 => skip until timestamp >= skip.
+			 */
+			if (inc->skip < 0) {
+				inc->skip = timestamp;
+				prev_timestamp = timestamp;
+			} else if (inc->skip > 0 && timestamp < (uint64_t)inc->skip) {
+				prev_timestamp = inc->skip;
+			} else if (timestamp == prev_timestamp) {
+				/* Ignore repeated timestamps (e.g. sigrok outputs these) */
+			} else {
+				if (inc->compress != 0 && timestamp - prev_timestamp > inc->compress) {
+					/* Compress long idle periods */
+					prev_timestamp = timestamp - inc->compress;
+				}
+
+				sr_dbg("New timestamp: %" PRIu64, timestamp);
+
+				/* Generate samples from prev_timestamp up to timestamp - 1. */
+				add_samples(in, timestamp - prev_timestamp);
+				prev_timestamp = timestamp;
+			}
+		} else if (tokens[i][0] == '$' && tokens[i][1] != '\0') {
+			/*
+			 * This is probably a $dumpvars, $comment or similar.
+			 * $dump* contain useful data.
+			 */
+			if (g_strcmp0(tokens[i], "$dumpvars") == 0
+					|| g_strcmp0(tokens[i], "$dumpon") == 0
+					|| g_strcmp0(tokens[i], "$dumpoff") == 0
+					|| g_strcmp0(tokens[i], "$end") == 0) {
+				/* Ignore, parse contents as normally. */
+			} else {
+				/* Ignore this and future lines until $end. */
+				inc->skip_until_end = TRUE;
+				break;
+			}
+		} else if (strchr("rR", tokens[i][0]) != NULL) {
+			sr_dbg("Real type vector values not supported yet!");
+			if (!tokens[++i])
+				/* No tokens left, bail out */
+				break;
+			else
+				/* Process next token */
+				continue;
+		} else if (strchr("bB", tokens[i][0]) != NULL) {
+			bit = (tokens[i][1] == '1');
+
+			/*
+			 * Bail out if a) char after 'b' is NUL, or b) there is
+			 * a second character after 'b', or c) there is no
+			 * identifier.
+			 */
+			if (!tokens[i][1] || tokens[i][2] || !tokens[++i]) {
+				sr_dbg("Unexpected vector format!");
+				break;
+			}
+
+			process_bit(inc, tokens[i], bit);
+		} else if (strchr("01xXzZ", tokens[i][0]) != NULL) {
+			char *identifier;
+
+			/* A new 1-bit sample value */
+			bit = (tokens[i][0] == '1');
+
+			/*
+			 * The identifier is either the next character, or, if
+			 * there was whitespace after the bit, the next token.
+			 */
+			if (tokens[i][1] == '\0') {
+				if (!tokens[++i]) {
+					sr_dbg("Identifier missing!");
+					break;
+				}
+				identifier = tokens[i];
+			} else {
+				identifier = tokens[i] + 1;
+			}
+			process_bit(inc, identifier, bit);
+		} else {
+			sr_warn("Skipping unknown token '%s'.", tokens[i]);
+		}
+	}
+	g_strfreev(tokens);
+}
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+	struct context *inc;
+
+	inc = in->priv = g_malloc0(sizeof(struct context));
+
+	inc->maxchannels = g_variant_get_int32(g_hash_table_lookup(options, "numchannels"));
+	inc->downsample = g_variant_get_int32(g_hash_table_lookup(options, "downsample"));
+	if (inc->downsample < 1)
+		inc->downsample = 1;
+
+	inc->compress = g_variant_get_int32(g_hash_table_lookup(options, "compress"));
+	inc->skip = g_variant_get_int32(g_hash_table_lookup(options, "skip"));
+	inc->skip /= inc->downsample;
+
+	in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	in->priv = inc;
+
+	inc->buffer = g_malloc(CHUNKSIZE);
+
+	return SR_OK;
+}
+
+static gboolean have_header(GString *buf)
+{
+	unsigned int pos;
+	char *p;
+
+	if (!(p = g_strstr_len(buf->str, buf->len, "$enddefinitions")))
+		return FALSE;
+	pos = p - buf->str + 15;
+	while (pos < buf->len - 4 && g_ascii_isspace(buf->str[pos]))
+		pos++;
+	if (!strncmp(buf->str + pos, "$end", 4))
+		return TRUE;
+
+	return FALSE;
+}
+
+static int process_buffer(struct sr_input *in)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_meta meta;
+	struct sr_config *src;
+	struct context *inc;
+	uint64_t samplerate;
+	char *p;
+
+	inc = in->priv;
+	if (!inc->started) {
+		std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+		packet.type = SR_DF_META;
+		packet.payload = &meta;
+		samplerate = inc->samplerate / inc->downsample;
+		src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
+		meta.config = g_slist_append(NULL, src);
+		sr_session_send(in->sdi, &packet);
+		g_slist_free(meta.config);
+		sr_config_free(src);
+
+		inc->started = TRUE;
+	}
+
+	while ((p = g_strrstr_len(in->buf->str, in->buf->len, "\n"))) {
+		*p = '\0';
+		g_strstrip(in->buf->str);
+		if (in->buf->str[0] != '\0')
+			parse_contents(in, in->buf->str);
+		g_string_erase(in->buf, 0, p - in->buf->str + 1);
+	}
+
+	return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+	struct context *inc;
+	int ret;
+
+	g_string_append_len(in->buf, buf->str, buf->len);
+
+	inc = in->priv;
+	if (!inc->got_header) {
+		if (!have_header(in->buf))
+			return SR_OK;
+		if (!parse_header(in, in->buf))
+			/* There was a header in there, but it was malformed. */
+			return SR_ERR;
+
+		in->sdi_ready = TRUE;
+		/* sdi is ready, notify frontend. */
+		return SR_OK;
+	}
+
+	ret = process_buffer(in);
+
+	return ret;
+}
+
+static int end(struct sr_input *in)
+{
+	struct sr_datafeed_packet packet;
+	struct context *inc;
+	int ret;
+
+	inc = in->priv;
+
+	if (in->sdi_ready)
+		ret = process_buffer(in);
+	else
+		ret = SR_OK;
+
+	/* Send any samples that haven't been sent yet. */
+	send_buffer(in);
+
+	if (inc->started) {
+		packet.type = SR_DF_END;
+		sr_session_send(in->sdi, &packet);
+	}
+
+	return ret;
+}
+
+static void cleanup(struct sr_input *in)
+{
+	struct context *inc;
+
+	inc = in->priv;
+	g_slist_free_full(inc->channels, free_channel);
+	g_free(inc->buffer);
+	inc->buffer = NULL;
+	g_free(inc->current_levels);
+	inc->current_levels = NULL;
+}
+
+static struct sr_option options[] = {
+	{ "numchannels", "Number of channels", "Number of channels", NULL, NULL },
+	{ "skip", "Skip", "Skip until timestamp", NULL, NULL },
+	{ "downsample", "Downsample", "Divide samplerate by factor", NULL, NULL },
+	{ "compress", "Compress", "Compress idle periods longer than this value", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_ref_sink(g_variant_new_int32(0));
+		options[1].def = g_variant_ref_sink(g_variant_new_int32(-1));
+		options[2].def = g_variant_ref_sink(g_variant_new_int32(1));
+		options[3].def = g_variant_ref_sink(g_variant_new_int32(0));
+	}
+
+	return options;
+}
+
+SR_PRIV struct sr_input_module input_vcd = {
+	.id = "vcd",
+	.name = "VCD",
+	.desc = "Value Change Dump",
+	.exts = (const char*[]){"vcd", NULL},
+	.metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
+	.options = get_options,
+	.format_match = format_match,
+	.init = init,
+	.receive = receive,
+	.end = end,
+	.cleanup = cleanup,
+};
diff --git a/src/input/wav.c b/src/input/wav.c
new file mode 100644
index 0000000..f757433
--- /dev/null
+++ b/src/input/wav.c
@@ -0,0 +1,372 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/wav"
+
+/* How many bytes at a time to process and send to the session bus. */
+#define CHUNK_SIZE 4096
+
+/* Minimum size of header + 1 8-bit mono PCM sample. */
+#define MIN_DATA_CHUNK_OFFSET    45
+
+/* Expect to find the "data" chunk within this offset from the start. */
+#define MAX_DATA_CHUNK_OFFSET    1024
+
+#define WAVE_FORMAT_PCM_         0x0001
+#define WAVE_FORMAT_IEEE_FLOAT_  0x0003
+#define WAVE_FORMAT_EXTENSIBLE_  0xfffe
+
+struct context {
+	gboolean started;
+	int fmt_code;
+	uint64_t samplerate;
+	int samplesize;
+	int num_channels;
+	int unitsize;
+	gboolean found_data;
+};
+
+static int parse_wav_header(GString *buf, struct context *inc)
+{
+	uint64_t samplerate;
+	unsigned int fmt_code, samplesize, num_channels, unitsize;
+
+	if (buf->len < MIN_DATA_CHUNK_OFFSET)
+		return SR_ERR_NA;
+
+	fmt_code = RL16(buf->str + 20);
+	samplerate = RL32(buf->str + 24);
+
+	samplesize = RL16(buf->str + 32);
+	num_channels = RL16(buf->str + 22);
+	if (num_channels == 0)
+		return SR_ERR;
+	unitsize = samplesize / num_channels;
+	if (unitsize != 1 && unitsize != 2 && unitsize != 4) {
+		sr_err("Only 8, 16 or 32 bits per sample supported.");
+		return SR_ERR_DATA;
+	}
+
+	if (fmt_code == WAVE_FORMAT_PCM_) {
+	} else if (fmt_code == WAVE_FORMAT_IEEE_FLOAT_) {
+		if (unitsize != 4) {
+			sr_err("only 32-bit floats supported.");
+			return SR_ERR_DATA;
+		}
+	} else if (fmt_code == WAVE_FORMAT_EXTENSIBLE_) {
+		if (buf->len < 70)
+			/* Not enough for extensible header and next chunk. */
+			return SR_ERR_NA;
+
+		if (RL16(buf->str + 16) != 40) {
+			sr_err("WAV extensible format chunk must be 40 bytes.");
+			return SR_ERR;
+		}
+		if (RL16(buf->str + 36) != 22) {
+			sr_err("WAV extension must be 22 bytes.");
+			return SR_ERR;
+		}
+		if (RL16(buf->str + 34) != RL16(buf->str + 38)) {
+			sr_err("Reduced valid bits per sample not supported.");
+			return SR_ERR_DATA;
+		}
+		/* Real format code is the first two bytes of the GUID. */
+		fmt_code = RL16(buf->str + 44);
+		if (fmt_code != WAVE_FORMAT_PCM_ && fmt_code != WAVE_FORMAT_IEEE_FLOAT_) {
+			sr_err("Only PCM and floating point samples are supported.");
+			return SR_ERR_DATA;
+		}
+		if (fmt_code == WAVE_FORMAT_IEEE_FLOAT_ && unitsize != 4) {
+			sr_err("only 32-bit floats supported.");
+			return SR_ERR_DATA;
+		}
+	} else {
+		sr_err("Only PCM and floating point samples are supported.");
+		return SR_ERR_DATA;
+	}
+
+	if (inc) {
+		inc->fmt_code = fmt_code;
+		inc->samplerate = samplerate;
+		inc->samplesize = samplesize;
+		inc->num_channels = num_channels;
+		inc->unitsize = unitsize;
+		inc->found_data = FALSE;
+	}
+
+	return SR_OK;
+}
+
+static int format_match(GHashTable *metadata)
+{
+	GString *buf;
+	int ret;
+
+	buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+	if (strncmp(buf->str, "RIFF", 4))
+		return SR_ERR;
+	if (strncmp(buf->str + 8, "WAVE", 4))
+		return SR_ERR;
+	if (strncmp(buf->str + 12, "fmt ", 4))
+		return SR_ERR;
+	/*
+	 * Only gets called when we already know this is a WAV file, so
+	 * this parser can log error messages.
+	 */
+	if ((ret = parse_wav_header(buf, NULL)) != SR_OK)
+		return ret;
+
+	return SR_OK;
+}
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+	(void)options;
+
+	in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	in->priv = g_malloc0(sizeof(struct context));
+
+	return SR_OK;
+}
+
+static int find_data_chunk(GString *buf, int initial_offset)
+{
+	unsigned int offset, i;
+
+	offset = initial_offset;
+	while (offset < MIN(MAX_DATA_CHUNK_OFFSET, buf->len)) {
+		if (!memcmp(buf->str + offset, "data", 4))
+			/* Skip into the samples. */
+			return offset + 8;
+		for (i = 0; i < 4; i++) {
+			if (!isalnum(buf->str[offset + i])
+					&& !isblank(buf->str[offset + i]))
+				/* Doesn't look like a chunk ID. */
+				return -1;
+		}
+		/* Skip past this chunk. */
+		offset += 8 + RL32(buf->str + offset + 4);
+	}
+
+	if (offset > MAX_DATA_CHUNK_OFFSET)
+		return -1;
+
+	return offset;
+}
+
+static void send_chunk(const struct sr_input *in, int offset, int num_samples)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+	struct context *inc;
+	float fdata[CHUNK_SIZE];
+	int total_samples, samplenum;
+	char *s, *d;
+
+	inc = in->priv;
+
+	s = in->buf->str + offset;
+	d = (char *)fdata;
+	memset(fdata, 0, CHUNK_SIZE);
+	total_samples = num_samples * inc->num_channels;
+	for (samplenum = 0; samplenum < total_samples; samplenum++) {
+		if (inc->fmt_code == WAVE_FORMAT_PCM_) {
+			switch (inc->unitsize) {
+			case 1:
+				/* 8-bit PCM samples are unsigned. */
+				fdata[samplenum] = *(uint8_t*)(s) / (float)255;
+				break;
+			case 2:
+				fdata[samplenum] = RL16S(s) / (float)INT16_MAX;
+				break;
+			case 4:
+				fdata[samplenum] = RL32S(s) / (float)INT32_MAX;
+				break;
+			}
+		} else {
+			/* BINARY32 float */
+#ifdef WORDS_BIGENDIAN
+			int i;
+			for (i = 0; i < inc->unitsize; i++)
+				d[i] = s[inc->unitsize - 1 - i];
+#else
+			memcpy(d, s, inc->unitsize);
+#endif
+		}
+		s += inc->unitsize;
+		d += inc->unitsize;
+	}
+	packet.type = SR_DF_ANALOG_OLD;
+	packet.payload = &analog;
+	analog.channels = in->sdi->channels;
+	analog.num_samples = num_samples;
+	analog.mq = 0;
+	analog.mqflags = 0;
+	analog.unit = 0;
+	analog.data = fdata;
+	sr_session_send(in->sdi, &packet);
+}
+
+static int process_buffer(struct sr_input *in)
+{
+	struct context *inc;
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_meta meta;
+	struct sr_config *src;
+	int offset, chunk_samples, total_samples, processed, max_chunk_samples;
+	int num_samples, i;
+
+	inc = in->priv;
+	if (!inc->started) {
+		std_session_send_df_header(in->sdi, LOG_PREFIX);
+
+		packet.type = SR_DF_META;
+		packet.payload = &meta;
+		src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
+		meta.config = g_slist_append(NULL, src);
+		sr_session_send(in->sdi, &packet);
+		g_slist_free(meta.config);
+		sr_config_free(src);
+
+		inc->started = TRUE;
+	}
+
+	if (!inc->found_data) {
+		/* Skip past size of 'fmt ' chunk. */
+		i = 20 + RL32(in->buf->str + 16);
+		offset = find_data_chunk(in->buf, i);
+		if (offset < 0) {
+			if (in->buf->len > MAX_DATA_CHUNK_OFFSET) {
+				sr_err("Couldn't find data chunk.");
+				return SR_ERR;
+			}
+		}
+		inc->found_data = TRUE;
+	} else
+		offset = 0;
+
+	/* Round off up to the last channels * unitsize boundary. */
+	chunk_samples = (in->buf->len - offset) / inc->samplesize;
+	max_chunk_samples = CHUNK_SIZE / inc->samplesize;
+	processed = 0;
+	total_samples = chunk_samples;
+	while (processed < total_samples) {
+		if (chunk_samples > max_chunk_samples)
+			num_samples = max_chunk_samples;
+		else
+			num_samples = chunk_samples;
+		send_chunk(in, offset, num_samples);
+		offset += num_samples * inc->samplesize;
+		chunk_samples -= num_samples;
+		processed += num_samples;
+	}
+
+	if ((unsigned int)offset < in->buf->len) {
+		/*
+		 * The incoming buffer wasn't processed completely. Stash
+		 * the leftover data for next time.
+		 */
+		g_string_erase(in->buf, 0, offset);
+	} else
+		g_string_truncate(in->buf, 0);
+
+	return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+	struct context *inc;
+	int ret;
+	char channelname[8];
+
+	g_string_append_len(in->buf, buf->str, buf->len);
+
+	if (in->buf->len < MIN_DATA_CHUNK_OFFSET) {
+		/*
+		 * Don't even try until there's enough room
+		 * for the data segment to start.
+		 */
+		return SR_OK;
+	}
+
+	inc = in->priv;
+	if (!in->sdi_ready) {
+		if ((ret = parse_wav_header(in->buf, inc)) == SR_ERR_NA)
+			/* Not enough data yet. */
+			return SR_OK;
+		else if (ret != SR_OK)
+			return ret;
+
+		for (int i = 0; i < inc->num_channels; i++) {
+			snprintf(channelname, 8, "CH%d", i + 1);
+			sr_channel_new(in->sdi, i, SR_CHANNEL_ANALOG, TRUE, channelname);
+		}
+
+		/* sdi is ready, notify frontend. */
+		in->sdi_ready = TRUE;
+		return SR_OK;
+	}
+
+	ret = process_buffer(in);
+
+	return ret;
+}
+
+static int end(struct sr_input *in)
+{
+	struct sr_datafeed_packet packet;
+	struct context *inc;
+	int ret;
+
+	if (in->sdi_ready)
+		ret = process_buffer(in);
+	else
+		ret = SR_OK;
+
+	inc = in->priv;
+	if (inc->started) {
+		packet.type = SR_DF_END;
+		sr_session_send(in->sdi, &packet);
+	}
+
+	return ret;
+}
+
+SR_PRIV struct sr_input_module input_wav = {
+	.id = "wav",
+	.name = "WAV",
+	.desc = "WAV file",
+	.exts = (const char*[]){"wav", NULL},
+	.metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
+	.format_match = format_match,
+	.init = init,
+	.receive = receive,
+	.end = end,
+};
diff --git a/src/lcr/es51919.c b/src/lcr/es51919.c
new file mode 100644
index 0000000..8965653
--- /dev/null
+++ b/src/lcr/es51919.c
@@ -0,0 +1,946 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Janne Huttunen <jahuttun at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "es51919"
+
+struct dev_buffer {
+	/** Total size of the buffer. */
+	size_t size;
+	/** Amount of data currently in the buffer. */
+	size_t len;
+	/** Offset where the data starts in the buffer. */
+	size_t offset;
+	/** Space for the data. */
+	uint8_t data[];
+};
+
+static struct dev_buffer *dev_buffer_new(size_t size)
+{
+	struct dev_buffer *dbuf;
+
+	dbuf = g_malloc0(sizeof(struct dev_buffer) + size);
+	dbuf->size = size;
+	dbuf->len = 0;
+	dbuf->offset = 0;
+
+	return dbuf;
+}
+
+static void dev_buffer_destroy(struct dev_buffer *dbuf)
+{
+	g_free(dbuf);
+}
+
+static int dev_buffer_fill_serial(struct dev_buffer *dbuf,
+				  struct sr_dev_inst *sdi)
+{
+	struct sr_serial_dev_inst *serial;
+	int len;
+
+	serial = sdi->conn;
+
+	/* If we already have data, move it to the beginning of the buffer. */
+	if (dbuf->len > 0 && dbuf->offset > 0)
+		memmove(dbuf->data, dbuf->data + dbuf->offset, dbuf->len);
+
+	dbuf->offset = 0;
+
+	len = dbuf->size - dbuf->len;
+	len = serial_read_nonblocking(serial, dbuf->data + dbuf->len, len);
+	if (len < 0) {
+		sr_err("Serial port read error: %d.", len);
+		return len;
+	}
+
+	dbuf->len += len;
+
+	return SR_OK;
+}
+
+static uint8_t *dev_buffer_packet_find(struct dev_buffer *dbuf,
+				gboolean (*packet_valid)(const uint8_t *),
+				size_t packet_size)
+{
+	size_t offset;
+
+	while (dbuf->len >= packet_size) {
+		if (packet_valid(dbuf->data + dbuf->offset)) {
+			offset = dbuf->offset;
+			dbuf->offset += packet_size;
+			dbuf->len -= packet_size;
+			return dbuf->data + offset;
+		}
+		dbuf->offset++;
+		dbuf->len--;
+	}
+
+	return NULL;
+}
+
+struct dev_limit_counter {
+	/** The current number of received samples/frames/etc. */
+	uint64_t count;
+	/** The limit (in number of samples/frames/etc.). */
+	uint64_t limit;
+};
+
+static void dev_limit_counter_start(struct dev_limit_counter *cnt)
+{
+	cnt->count = 0;
+}
+
+static void dev_limit_counter_inc(struct dev_limit_counter *cnt)
+{
+	cnt->count++;
+}
+
+static void dev_limit_counter_limit_set(struct dev_limit_counter *cnt,
+					uint64_t limit)
+{
+	cnt->limit = limit;
+}
+
+static gboolean dev_limit_counter_limit_reached(struct dev_limit_counter *cnt)
+{
+	if (cnt->limit && cnt->count >= cnt->limit) {
+		sr_info("Requested counter limit reached.");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+struct dev_time_counter {
+	/** The starting time of current sampling run. */
+	int64_t starttime;
+	/** The time limit (in milliseconds). */
+	uint64_t limit;
+};
+
+static void dev_time_counter_start(struct dev_time_counter *cnt)
+{
+	cnt->starttime = g_get_monotonic_time();
+}
+
+static void dev_time_limit_set(struct dev_time_counter *cnt, uint64_t limit)
+{
+	cnt->limit = limit;
+}
+
+static gboolean dev_time_limit_reached(struct dev_time_counter *cnt)
+{
+	int64_t time;
+
+	if (cnt->limit) {
+		time = (g_get_monotonic_time() - cnt->starttime) / 1000;
+		if (time > (int64_t)cnt->limit) {
+			sr_info("Requested time limit reached.");
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void serial_conf_get(GSList *options, const char *def_serialcomm,
+			    const char **conn, const char **serialcomm)
+{
+	struct sr_config *src;
+	GSList *l;
+
+	*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 (*serialcomm == NULL)
+		*serialcomm = def_serialcomm;
+}
+
+static struct sr_serial_dev_inst *serial_dev_new(GSList *options,
+						 const char *def_serialcomm)
+
+{
+	const char *conn, *serialcomm;
+
+	serial_conf_get(options, def_serialcomm, &conn, &serialcomm);
+
+	if (!conn)
+		return NULL;
+
+	return sr_serial_dev_inst_new(conn, serialcomm);
+}
+
+static int serial_stream_check_buf(struct sr_serial_dev_inst *serial,
+				   uint8_t *buf, size_t buflen,
+				   size_t packet_size,
+				   packet_valid_callback is_valid,
+				   uint64_t timeout_ms, int baudrate)
+{
+	size_t len, dropped;
+	int ret;
+
+	if ((ret = serial_open(serial, SERIAL_RDWR)) != SR_OK)
+		return ret;
+
+	serial_flush(serial);
+
+	len = buflen;
+	ret = serial_stream_detect(serial, buf, &len, packet_size,
+				   is_valid, timeout_ms, baudrate);
+
+	serial_close(serial);
+
+	if (ret != SR_OK)
+		return ret;
+
+	/*
+	 * If we dropped more than two packets worth of data, something is
+	 * wrong. We shouldn't quit however, since the dropped bytes might be
+	 * just zeroes at the beginning of the stream. Those can occur as a
+	 * combination of the nonstandard cable that ships with some devices
+	 * and the serial port or USB to serial adapter.
+	 */
+	dropped = len - packet_size;
+	if (dropped > 2 * packet_size)
+		sr_warn("Had to drop too much data.");
+
+	return SR_OK;
+}
+
+static int serial_stream_check(struct sr_serial_dev_inst *serial,
+			       size_t packet_size,
+			       packet_valid_callback is_valid,
+			       uint64_t timeout_ms, int baudrate)
+{
+	uint8_t buf[128];
+
+	return serial_stream_check_buf(serial, buf, sizeof(buf), packet_size,
+				       is_valid, timeout_ms, baudrate);
+}
+
+struct std_opt_desc {
+	const uint32_t *scanopts;
+	const int num_scanopts;
+	const uint32_t *devopts;
+	const int num_devopts;
+};
+
+static int std_config_list(uint32_t key, GVariant **data,
+			   const struct std_opt_desc *d)
+{
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			d->scanopts, d->num_scanopts, sizeof(uint32_t));
+		break;
+	case SR_CONF_DEVICE_OPTIONS:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+			d->devopts, d->num_devopts, sizeof(uint32_t));
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static int send_config_update(struct sr_dev_inst *sdi, struct sr_config *cfg)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_meta meta;
+
+	memset(&meta, 0, sizeof(meta));
+
+	packet.type = SR_DF_META;
+	packet.payload = &meta;
+
+	meta.config = g_slist_append(meta.config, cfg);
+
+	return sr_session_send(sdi, &packet);
+}
+
+static int send_config_update_key(struct sr_dev_inst *sdi, uint32_t key,
+				  GVariant *var)
+{
+	struct sr_config *cfg;
+	int ret;
+
+	cfg = sr_config_new(key, var);
+	if (!cfg)
+		return SR_ERR;
+
+	ret = send_config_update(sdi, cfg);
+	sr_config_free(cfg);
+
+	return ret;
+}
+
+/*
+ * Cyrustek ES51919 LCR chipset host protocol.
+ *
+ * Public official documentation does not contain the protocol
+ * description, so this is all based on reverse engineering.
+ *
+ * Packet structure (17 bytes):
+ *
+ * 0x00: header1 ?? (0x00)
+ * 0x01: header2 ?? (0x0d)
+ *
+ * 0x02: flags
+ *         bit 0 = hold enabled
+ *         bit 1 = reference shown (in delta mode)
+ *         bit 2 = delta mode
+ *         bit 3 = calibration mode
+ *         bit 4 = sorting mode
+ *         bit 5 = LCR mode
+ *         bit 6 = auto mode
+ *         bit 7 = parallel measurement (vs. serial)
+ *
+ * 0x03: config
+ *         bit 0-4 = ??? (0x10)
+ *         bit 5-7 = test frequency
+ *                     0 = 100 Hz
+ *                     1 = 120 Hz
+ *                     2 = 1 kHz
+ *                     3 = 10 kHz
+ *                     4 = 100 kHz
+ *                     5 = 0 Hz (DC)
+ *
+ * 0x04: tolerance (sorting mode)
+ *         0 = not set
+ *         3 = +-0.25%
+ *         4 = +-0.5%
+ *         5 = +-1%
+ *         6 = +-2%
+ *         7 = +-5%
+ *         8 = +-10%
+ *         9 = +-20%
+ *        10 = -20+80%
+ *
+ * 0x05-0x09: primary measurement
+ *   0x05: measured quantity
+ *           1 = inductance
+ *           2 = capacitance
+ *           3 = resistance
+ *           4 = DC resistance
+ *   0x06: measurement MSB  (0x4e20 = 20000 = outside limits)
+ *   0x07: measurement LSB
+ *   0x08: measurement info
+ *           bit 0-2 = decimal point multiplier (10^-val)
+ *           bit 3-7 = unit
+ *                       0 = no unit
+ *                       1 = Ohm
+ *                       2 = kOhm
+ *                       3 = MOhm
+ *                       5 = uH
+ *                       6 = mH
+ *                       7 = H
+ *                       8 = kH
+ *                       9 = pF
+ *                       10 = nF
+ *                       11 = uF
+ *                       12 = mF
+ *                       13 = %
+ *                       14 = degree
+ *   0x09: measurement status
+ *           bit 0-3 = status
+ *                       0 = normal (measurement shown)
+ *                       1 = blank (nothing shown)
+ *                       2 = lines ("----")
+ *                       3 = outside limits ("OL")
+ *                       7 = pass ("PASS")
+ *                       8 = fail ("FAIL")
+ *                       9 = open ("OPEn")
+ *                      10 = shorted ("Srt")
+ *           bit 4-6 = ??? (maybe part of same field with 0-3)
+ *           bit 7   = ??? (some independent flag)
+ *
+ * 0x0a-0x0e: secondary measurement
+ *   0x0a: measured quantity
+ *           0 = none
+ *           1 = dissipation factor
+ *           2 = quality factor
+ *           3 = parallel AC resistance / ESR
+ *           4 = phase angle
+ *   0x0b-0x0e: like primary measurement
+ *
+ * 0x0f: footer1 (0x0d) ?
+ * 0x10: footer2 (0x0a) ?
+ */
+
+#define PACKET_SIZE 17
+
+static const double frequencies[] = {
+	100, 120, 1000, 10000, 100000, 0,
+};
+
+enum { MODEL_NONE, MODEL_PAR, MODEL_SER, MODEL_AUTO, };
+
+static const char *const models[] = {
+	"NONE", "PARALLEL", "SERIES", "AUTO",
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+	/** Opaque pointer passed in by the frontend. */
+	void *cb_data;
+
+	/** The number of frames. */
+	struct dev_limit_counter frame_count;
+
+	/** The time limit counter. */
+	struct dev_time_counter time_count;
+
+	/** Data buffer. */
+	struct dev_buffer *buf;
+
+	/** The frequency of the test signal (index to frequencies[]). */
+	unsigned int freq;
+
+	/** Equivalent circuit model (index to models[]). */
+	unsigned int model;
+};
+
+static const uint8_t *pkt_to_buf(const uint8_t *pkt, int is_secondary)
+{
+	return is_secondary ? pkt + 10 : pkt + 5;
+}
+
+static int parse_mq(const uint8_t *pkt, int is_secondary, int is_parallel)
+{
+	const uint8_t *buf;
+
+	buf = pkt_to_buf(pkt, is_secondary);
+
+	switch (is_secondary << 8 | buf[0]) {
+	case 0x001:
+		return is_parallel ?
+			SR_MQ_PARALLEL_INDUCTANCE : SR_MQ_SERIES_INDUCTANCE;
+	case 0x002:
+		return is_parallel ?
+			SR_MQ_PARALLEL_CAPACITANCE : SR_MQ_SERIES_CAPACITANCE;
+	case 0x003:
+	case 0x103:
+		return is_parallel ?
+			SR_MQ_PARALLEL_RESISTANCE : SR_MQ_SERIES_RESISTANCE;
+	case 0x004:
+		return SR_MQ_RESISTANCE;
+	case 0x100:
+		return SR_MQ_DIFFERENCE;
+	case 0x101:
+		return SR_MQ_DISSIPATION_FACTOR;
+	case 0x102:
+		return SR_MQ_QUALITY_FACTOR;
+	case 0x104:
+		return SR_MQ_PHASE_ANGLE;
+	}
+
+	sr_err("Unknown quantity 0x%03x.", is_secondary << 8 | buf[0]);
+
+	return -1;
+}
+
+static float parse_value(const uint8_t *buf)
+{
+	static const float decimals[] = {
+		1, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7
+	};
+	int16_t val;
+
+	val = (buf[1] << 8) | buf[2];
+	return (float)val * decimals[buf[3] & 7];
+}
+
+static void parse_measurement(const uint8_t *pkt, float *floatval,
+			      struct sr_datafeed_analog_old *analog,
+			      int is_secondary)
+{
+	static const struct {
+		int unit;
+		float mult;
+	} units[] = {
+		{ SR_UNIT_UNITLESS, 1 },	/* no unit */
+		{ SR_UNIT_OHM, 1 },		/* Ohm     */
+		{ SR_UNIT_OHM, 1e3 },		/* kOhm    */
+		{ SR_UNIT_OHM, 1e6 },		/* MOhm    */
+		{ -1, 0 },			/* ???     */
+		{ SR_UNIT_HENRY, 1e-6 },	/* uH      */
+		{ SR_UNIT_HENRY, 1e-3 },	/* mH      */
+		{ SR_UNIT_HENRY, 1 },		/* H       */
+		{ SR_UNIT_HENRY, 1e3 },		/* kH      */
+		{ SR_UNIT_FARAD, 1e-12 },	/* pF      */
+		{ SR_UNIT_FARAD, 1e-9 },	/* nF      */
+		{ SR_UNIT_FARAD, 1e-6 },	/* uF      */
+		{ SR_UNIT_FARAD, 1e-3 },	/* mF      */
+		{ SR_UNIT_PERCENTAGE, 1 },	/* %       */
+		{ SR_UNIT_DEGREE, 1 }		/* degree  */
+	};
+	const uint8_t *buf;
+	int state;
+
+	buf = pkt_to_buf(pkt, is_secondary);
+
+	analog->mq = -1;
+	analog->mqflags = 0;
+
+	state = buf[4] & 0xf;
+
+	if (state != 0 && state != 3)
+		return;
+
+	if (pkt[2] & 0x18) {
+		/* Calibration and Sorting modes not supported. */
+		return;
+	}
+
+	if (!is_secondary) {
+		if (pkt[2] & 0x01)
+			analog->mqflags |= SR_MQFLAG_HOLD;
+		if (pkt[2] & 0x02)
+			analog->mqflags |= SR_MQFLAG_REFERENCE;
+	} else {
+		if (pkt[2] & 0x04)
+			analog->mqflags |= SR_MQFLAG_RELATIVE;
+	}
+
+	if ((analog->mq = parse_mq(pkt, is_secondary, pkt[2] & 0x80)) < 0)
+		return;
+
+	if ((buf[3] >> 3) >= ARRAY_SIZE(units)) {
+		sr_err("Unknown unit %u.", buf[3] >> 3);
+		analog->mq = -1;
+		return;
+	}
+
+	analog->unit = units[buf[3] >> 3].unit;
+
+	*floatval = parse_value(buf);
+	*floatval *= (state == 0) ? units[buf[3] >> 3].mult : INFINITY;
+}
+
+static unsigned int parse_freq(const uint8_t *pkt)
+{
+	unsigned int freq;
+
+	freq = pkt[3] >> 5;
+
+	if (freq >= ARRAY_SIZE(frequencies)) {
+		sr_err("Unknown frequency %u.", freq);
+		freq = ARRAY_SIZE(frequencies) - 1;
+	}
+
+	return freq;
+}
+
+static unsigned int parse_model(const uint8_t *pkt)
+{
+	if (pkt[2] & 0x40)
+		return MODEL_AUTO;
+	else if (parse_mq(pkt, 0, 0) == SR_MQ_RESISTANCE)
+		return MODEL_NONE;
+	else if (pkt[2] & 0x80)
+		return MODEL_PAR;
+	else
+		return MODEL_SER;
+}
+
+static gboolean packet_valid(const uint8_t *pkt)
+{
+	/*
+	 * If the first two bytes of the packet are indeed a constant
+	 * header, they should be checked too. Since we don't know it
+	 * for sure, we'll just check the last two for now since they
+	 * seem to be constant just like in the other Cyrustek chipset
+	 * protocols.
+	 */
+	if (pkt[15] == 0xd && pkt[16] == 0xa)
+		return TRUE;
+
+	return FALSE;
+}
+
+static int do_config_update(struct sr_dev_inst *sdi, uint32_t key,
+			    GVariant *var)
+{
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+
+	return send_config_update_key(devc->cb_data, key, var);
+}
+
+static int send_freq_update(struct sr_dev_inst *sdi, unsigned int freq)
+{
+	return do_config_update(sdi, SR_CONF_OUTPUT_FREQUENCY,
+				g_variant_new_double(frequencies[freq]));
+}
+
+static int send_model_update(struct sr_dev_inst *sdi, unsigned int model)
+{
+	return do_config_update(sdi, SR_CONF_EQUIV_CIRCUIT_MODEL,
+				g_variant_new_string(models[model]));
+}
+
+static void handle_packet(struct sr_dev_inst *sdi, const uint8_t *pkt)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_analog_old analog;
+	struct dev_context *devc;
+	unsigned int val;
+	float floatval;
+	gboolean frame;
+
+	devc = sdi->priv;
+
+	val = parse_freq(pkt);
+	if (val != devc->freq) {
+		if (send_freq_update(sdi, val) == SR_OK)
+			devc->freq = val;
+		else
+			return;
+	}
+
+	val = parse_model(pkt);
+	if (val != devc->model) {
+		if (send_model_update(sdi, val) == SR_OK)
+			devc->model = val;
+		else
+			return;
+	}
+
+	frame = FALSE;
+
+	memset(&analog, 0, sizeof(analog));
+
+	analog.num_samples = 1;
+	analog.data = &floatval;
+
+	analog.channels = g_slist_append(NULL, sdi->channels->data);
+
+	parse_measurement(pkt, &floatval, &analog, 0);
+	if (analog.mq >= 0) {
+		if (!frame) {
+			packet.type = SR_DF_FRAME_BEGIN;
+			sr_session_send(devc->cb_data, &packet);
+			frame = TRUE;
+		}
+
+		packet.type = SR_DF_ANALOG_OLD;
+		packet.payload = &analog;
+
+		sr_session_send(devc->cb_data, &packet);
+	}
+
+	g_slist_free(analog.channels);
+	analog.channels = g_slist_append(NULL, sdi->channels->next->data);
+
+	parse_measurement(pkt, &floatval, &analog, 1);
+	if (analog.mq >= 0) {
+		if (!frame) {
+			packet.type = SR_DF_FRAME_BEGIN;
+			sr_session_send(devc->cb_data, &packet);
+			frame = TRUE;
+		}
+
+		packet.type = SR_DF_ANALOG_OLD;
+		packet.payload = &analog;
+
+		sr_session_send(devc->cb_data, &packet);
+	}
+
+	g_slist_free(analog.channels);
+
+	if (frame) {
+		packet.type = SR_DF_FRAME_END;
+		sr_session_send(devc->cb_data, &packet);
+		dev_limit_counter_inc(&devc->frame_count);
+	}
+}
+
+static int handle_new_data(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+	uint8_t *pkt;
+	int ret;
+
+	devc = sdi->priv;
+
+	ret = dev_buffer_fill_serial(devc->buf, sdi);
+	if (ret < 0)
+		return ret;
+
+	while ((pkt = dev_buffer_packet_find(devc->buf, packet_valid,
+					     PACKET_SIZE)))
+		handle_packet(sdi, pkt);
+
+	return SR_OK;
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+
+	(void)fd;
+
+	if (!(sdi = cb_data))
+		return TRUE;
+
+	if (!(devc = sdi->priv))
+		return TRUE;
+
+	if (revents == G_IO_IN) {
+		/* Serial data arrived. */
+		handle_new_data(sdi);
+	}
+
+	if (dev_limit_counter_limit_reached(&devc->frame_count) ||
+	    dev_time_limit_reached(&devc->time_count))
+		sdi->driver->dev_acquisition_stop(sdi, cb_data);
+
+	return TRUE;
+}
+
+static const char *const channel_names[] = { "P1", "P2" };
+
+static int setup_channels(struct sr_dev_inst *sdi)
+{
+	unsigned int i;
+	int ret;
+
+	ret = SR_ERR_BUG;
+
+	for (i = 0; i < ARRAY_SIZE(channel_names); i++)
+		sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_names[i]);
+
+	return ret;
+}
+
+SR_PRIV void es51919_serial_clean(void *priv)
+{
+	struct dev_context *devc;
+
+	if (!(devc = priv))
+		return;
+
+	dev_buffer_destroy(devc->buf);
+	g_free(devc);
+}
+
+SR_PRIV struct sr_dev_inst *es51919_serial_scan(GSList *options,
+						const char *vendor,
+						const char *model)
+{
+	struct sr_serial_dev_inst *serial;
+	struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+	int ret;
+
+	serial = NULL;
+	sdi = NULL;
+	devc = NULL;
+
+	if (!(serial = serial_dev_new(options, "9600/8n1/rts=1/dtr=1")))
+		goto scan_cleanup;
+
+	ret = serial_stream_check(serial, PACKET_SIZE, packet_valid,
+				  3000, 9600);
+	if (ret != SR_OK)
+		goto scan_cleanup;
+
+	sr_info("Found device on port %s.", serial->port);
+
+	sdi = g_malloc0(sizeof(struct sr_dev_inst));
+	sdi->status = SR_ST_INACTIVE;
+	sdi->vendor = g_strdup(vendor);
+	sdi->model = g_strdup(model);
+	devc = g_malloc0(sizeof(struct dev_context));
+	devc->buf = dev_buffer_new(PACKET_SIZE * 8);
+	sdi->inst_type = SR_INST_SERIAL;
+	sdi->conn = serial;
+	sdi->priv = devc;
+
+	if (setup_channels(sdi) != SR_OK)
+		goto scan_cleanup;
+
+	return sdi;
+
+scan_cleanup:
+	es51919_serial_clean(devc);
+	if (sdi)
+		sr_dev_inst_free(sdi);
+	if (serial)
+		sr_serial_dev_inst_free(serial);
+
+	return NULL;
+}
+
+SR_PRIV int es51919_serial_config_get(uint32_t key, GVariant **data,
+				      const struct sr_dev_inst *sdi,
+				      const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+
+	(void)cg;
+
+	if (!(devc = sdi->priv))
+		return SR_ERR_BUG;
+
+	switch (key) {
+	case SR_CONF_OUTPUT_FREQUENCY:
+		*data = g_variant_new_double(frequencies[devc->freq]);
+		break;
+	case SR_CONF_EQUIV_CIRCUIT_MODEL:
+		*data = g_variant_new_string(models[devc->model]);
+		break;
+	default:
+		sr_spew("%s: Unsupported key %u", __func__, key);
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV int es51919_serial_config_set(uint32_t key, GVariant *data,
+				      const struct sr_dev_inst *sdi,
+				      const struct sr_channel_group *cg)
+{
+	struct dev_context *devc;
+	uint64_t val;
+
+	(void)cg;
+
+	if (!(devc = sdi->priv))
+		return SR_ERR_BUG;
+
+	switch (key) {
+	case SR_CONF_LIMIT_MSEC:
+		val = g_variant_get_uint64(data);
+		dev_time_limit_set(&devc->time_count, val);
+		sr_dbg("Setting time limit to %" PRIu64 ".", val);
+		break;
+	case SR_CONF_LIMIT_FRAMES:
+		val = g_variant_get_uint64(data);
+		dev_limit_counter_limit_set(&devc->frame_count, val);
+		sr_dbg("Setting frame limit to %" PRIu64 ".", val);
+		break;
+	default:
+		sr_spew("%s: Unsupported key %u", __func__, key);
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+	SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_LCRMETER,
+	SR_CONF_CONTINUOUS,
+	SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
+	SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+	SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET | SR_CONF_LIST,
+	SR_CONF_EQUIV_CIRCUIT_MODEL | SR_CONF_GET | SR_CONF_LIST,
+};
+
+static const struct std_opt_desc opts = {
+	scanopts, ARRAY_SIZE(scanopts),
+	devopts, ARRAY_SIZE(devopts),
+};
+
+SR_PRIV int es51919_serial_config_list(uint32_t key, GVariant **data,
+				       const struct sr_dev_inst *sdi,
+				       const struct sr_channel_group *cg)
+{
+	(void)sdi;
+	(void)cg;
+
+	if (std_config_list(key, data, &opts) == SR_OK)
+		return SR_OK;
+
+	switch (key) {
+	case SR_CONF_OUTPUT_FREQUENCY:
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE,
+			frequencies, ARRAY_SIZE(frequencies), sizeof(double));
+		break;
+	case SR_CONF_EQUIV_CIRCUIT_MODEL:
+		*data = g_variant_new_strv(models, ARRAY_SIZE(models));
+		break;
+	default:
+		sr_spew("%s: Unsupported key %u", __func__, key);
+		return SR_ERR_NA;
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV int es51919_serial_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))
+		return SR_ERR_BUG;
+
+	devc->cb_data = cb_data;
+
+	dev_limit_counter_start(&devc->frame_count);
+	dev_time_counter_start(&devc->time_count);
+
+	/* 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;
+	serial_source_add(sdi->session, serial, G_IO_IN, 50,
+			  receive_data, (void *)sdi);
+
+	return SR_OK;
+}
+
+SR_PRIV int es51919_serial_acquisition_stop(struct sr_dev_inst *sdi,
+					    void *cb_data)
+{
+	return std_serial_dev_acquisition_stop(sdi, cb_data,
+			std_serial_dev_close, sdi->conn, LOG_PREFIX);
+}
diff --git a/src/libsigrok-internal.h b/src/libsigrok-internal.h
new file mode 100644
index 0000000..3c18ffa
--- /dev/null
+++ b/src/libsigrok-internal.h
@@ -0,0 +1,1257 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at 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/>.
+ */
+
+/** @file
+  * @internal
+  */
+
+#ifndef LIBSIGROK_LIBSIGROK_INTERNAL_H
+#define LIBSIGROK_LIBSIGROK_INTERNAL_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <glib.h>
+#ifdef HAVE_LIBUSB_1_0
+#include <libusb.h>
+#endif
+#ifdef HAVE_LIBSERIALPORT
+#include <libserialport.h>
+#endif
+
+struct zip;
+struct zip_stat;
+
+/**
+ * @file
+ *
+ * libsigrok private header file, only to be used internally.
+ */
+
+/*--- Macros ----------------------------------------------------------------*/
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef ARRAY_AND_SIZE
+#define ARRAY_AND_SIZE(a) (a), ARRAY_SIZE(a)
+#endif
+
+/**
+ * Read a 8 bits unsigned integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding unsigned integer
+ */
+#define R8(x)     ((unsigned)((const uint8_t*)(x))[0])
+
+/**
+ * Read a 16 bits big endian unsigned integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding unsigned integer
+ */
+#define RB16(x)  (((unsigned)((const uint8_t*)(x))[0] <<  8) | \
+                   (unsigned)((const uint8_t*)(x))[1])
+
+/**
+ * Read a 16 bits little endian unsigned integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding unsigned integer
+ */
+#define RL16(x)  (((unsigned)((const uint8_t*)(x))[1] <<  8) | \
+                   (unsigned)((const uint8_t*)(x))[0])
+
+/**
+ * Read a 16 bits big endian signed integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding signed integer
+ */
+#define RB16S(x)  ((int16_t) \
+                  (((unsigned)((const uint8_t*)(x))[0] <<  8) | \
+                    (unsigned)((const uint8_t*)(x))[1]))
+
+/**
+ * Read a 16 bits little endian signed integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding signed integer
+ */
+#define RL16S(x)  ((int16_t) \
+                  (((unsigned)((const uint8_t*)(x))[1] <<  8) | \
+                    (unsigned)((const uint8_t*)(x))[0]))
+
+/**
+ * Read a 32 bits big endian unsigned integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding unsigned integer
+ */
+#define RB32(x)  (((unsigned)((const uint8_t*)(x))[0] << 24) | \
+                  ((unsigned)((const uint8_t*)(x))[1] << 16) | \
+                  ((unsigned)((const uint8_t*)(x))[2] <<  8) | \
+                   (unsigned)((const uint8_t*)(x))[3])
+
+/**
+ * Read a 32 bits little endian unsigned integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding unsigned integer
+ */
+#define RL32(x)  (((unsigned)((const uint8_t*)(x))[3] << 24) | \
+                  ((unsigned)((const uint8_t*)(x))[2] << 16) | \
+                  ((unsigned)((const uint8_t*)(x))[1] <<  8) | \
+                   (unsigned)((const uint8_t*)(x))[0])
+
+/**
+ * Read a 32 bits big endian signed integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding signed integer
+ */
+#define RB32S(x)  ((int32_t) \
+                 (((unsigned)((const uint8_t*)(x))[0] << 24) | \
+                  ((unsigned)((const uint8_t*)(x))[1] << 16) | \
+                  ((unsigned)((const uint8_t*)(x))[2] <<  8) | \
+                   (unsigned)((const uint8_t*)(x))[3]))
+
+/**
+ * Read a 32 bits little endian signed integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding signed integer
+ */
+#define RL32S(x)  ((int32_t) \
+                 (((unsigned)((const uint8_t*)(x))[3] << 24) | \
+                  ((unsigned)((const uint8_t*)(x))[2] << 16) | \
+                  ((unsigned)((const uint8_t*)(x))[1] <<  8) | \
+                   (unsigned)((const uint8_t*)(x))[0]))
+
+/**
+ * Read a 64 bits big endian unsigned integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding unsigned integer
+ */
+#define RB64(x)  (((uint64_t)((const uint8_t*)(x))[0] << 56) | \
+                  ((uint64_t)((const uint8_t*)(x))[1] << 48) | \
+                  ((uint64_t)((const uint8_t*)(x))[2] << 40) | \
+                  ((uint64_t)((const uint8_t*)(x))[3] << 32) | \
+                  ((uint64_t)((const uint8_t*)(x))[4] << 24) | \
+                  ((uint64_t)((const uint8_t*)(x))[5] << 16) | \
+                  ((uint64_t)((const uint8_t*)(x))[6] <<  8) | \
+                   (uint64_t)((const uint8_t*)(x))[7])
+
+/**
+ * Read a 64 bits little endian unsigned integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding unsigned integer
+ */
+#define RL64(x)  (((uint64_t)((const uint8_t*)(x))[7] << 56) | \
+                  ((uint64_t)((const uint8_t*)(x))[6] << 48) | \
+                  ((uint64_t)((const uint8_t*)(x))[5] << 40) | \
+                  ((uint64_t)((const uint8_t*)(x))[4] << 32) | \
+                  ((uint64_t)((const uint8_t*)(x))[3] << 24) | \
+                  ((uint64_t)((const uint8_t*)(x))[2] << 16) | \
+                  ((uint64_t)((const uint8_t*)(x))[1] <<  8) | \
+                   (uint64_t)((const uint8_t*)(x))[0])
+
+/**
+ * Read a 64 bits little endian signed integer out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding unsigned integer
+ */
+#define RL64S(x)  ((int64_t) \
+                 (((uint64_t)((const uint8_t*)(x))[7] << 56) | \
+                  ((uint64_t)((const uint8_t*)(x))[6] << 48) | \
+                  ((uint64_t)((const uint8_t*)(x))[5] << 40) | \
+                  ((uint64_t)((const uint8_t*)(x))[4] << 32) | \
+                  ((uint64_t)((const uint8_t*)(x))[3] << 24) | \
+                  ((uint64_t)((const uint8_t*)(x))[2] << 16) | \
+                  ((uint64_t)((const uint8_t*)(x))[1] <<  8) | \
+                   (uint64_t)((const uint8_t*)(x))[0]))
+
+/**
+ * Read a 32 bits big endian float out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding float
+ */
+#define RBFL(x)  ((union { uint32_t u; float f; }) { .u = RB32(x) }.f)
+
+/**
+ * Read a 32 bits little endian float out of memory.
+ * @param x a pointer to the input memory
+ * @return the corresponding float
+ */
+#define RLFL(x)  ((union { uint32_t u; float f; }) { .u = RL32(x) }.f)
+
+/**
+ * Write a 8 bits unsigned integer to memory.
+ * @param p a pointer to the output memory
+ * @param x the input unsigned integer
+ */
+#define W8(p, x)    do { ((uint8_t*)(p))[0] = (uint8_t) (x);      } while (0)
+
+/**
+ * Write a 16 bits unsigned integer to memory stored as big endian.
+ * @param p a pointer to the output memory
+ * @param x the input unsigned integer
+ */
+#define WB16(p, x)  do { ((uint8_t*)(p))[1] = (uint8_t) (x);      \
+                         ((uint8_t*)(p))[0] = (uint8_t)((x)>>8);  } while (0)
+
+/**
+ * Write a 16 bits unsigned integer to memory stored as little endian.
+ * @param p a pointer to the output memory
+ * @param x the input unsigned integer
+ */
+#define WL16(p, x)  do { ((uint8_t*)(p))[0] = (uint8_t) (x);      \
+                         ((uint8_t*)(p))[1] = (uint8_t)((x)>>8);  } while (0)
+
+/**
+ * Write a 32 bits unsigned integer to memory stored as big endian.
+ * @param p a pointer to the output memory
+ * @param x the input unsigned integer
+ */
+#define WB32(p, x)  do { ((uint8_t*)(p))[3] = (uint8_t) (x);      \
+                         ((uint8_t*)(p))[2] = (uint8_t)((x)>>8);  \
+                         ((uint8_t*)(p))[1] = (uint8_t)((x)>>16); \
+                         ((uint8_t*)(p))[0] = (uint8_t)((x)>>24); } while (0)
+
+/**
+ * Write a 32 bits unsigned integer to memory stored as little endian.
+ * @param p a pointer to the output memory
+ * @param x the input unsigned integer
+ */
+#define WL32(p, x)  do { ((uint8_t*)(p))[0] = (uint8_t) (x);      \
+                         ((uint8_t*)(p))[1] = (uint8_t)((x)>>8);  \
+                         ((uint8_t*)(p))[2] = (uint8_t)((x)>>16); \
+                         ((uint8_t*)(p))[3] = (uint8_t)((x)>>24); } while (0)
+
+/**
+ * Write a 32 bits float to memory stored as big endian.
+ * @param p a pointer to the output memory
+ * @param x the input float
+ */
+#define WBFL(p, x)  WB32(p, (union { uint32_t u; float f; }) { .f = x }.u)
+
+/**
+ * Write a 32 bits float to memory stored as little endian.
+ * @param p a pointer to the output memory
+ * @param x the input float
+ */
+#define WLFL(p, x)  WL32(p, (union { uint32_t u; float f; }) { .f = x }.u)
+
+/* Portability fixes for FreeBSD. */
+#ifdef __FreeBSD__
+#define LIBUSB_CLASS_APPLICATION 0xfe
+#define libusb_has_capability(x) 0
+#define libusb_handle_events_timeout_completed(ctx, tv, c) \
+	libusb_handle_events_timeout(ctx, tv)
+#endif
+
+/* Static definitions of structs ending with an all-zero entry are a
+ * problem when compiling with -Wmissing-field-initializers: GCC
+ * suppresses the warning only with { 0 }, clang wants { } */
+#ifdef __clang__
+#define ALL_ZERO { }
+#else
+#define ALL_ZERO { 0 }
+#endif
+
+struct sr_context {
+	struct sr_dev_driver **driver_list;
+#ifdef HAVE_LIBUSB_1_0
+	libusb_context *libusb_ctx;
+#endif
+	sr_resource_open_callback resource_open_cb;
+	sr_resource_close_callback resource_close_cb;
+	sr_resource_read_callback resource_read_cb;
+	void *resource_cb_data;
+};
+
+/** Input module metadata keys. */
+enum sr_input_meta_keys {
+	/** The input filename, if there is one. */
+	SR_INPUT_META_FILENAME = 0x01,
+	/** The input file's size in bytes. */
+	SR_INPUT_META_FILESIZE = 0x02,
+	/** The first 128 bytes of the file, provided as a GString. */
+	SR_INPUT_META_HEADER = 0x04,
+
+	/** The module cannot identify a file without this metadata. */
+	SR_INPUT_META_REQUIRED = 0x80,
+};
+
+/** Input (file) module struct. */
+struct sr_input {
+	/**
+	 * A pointer to this input module's 'struct sr_input_module'.
+	 */
+	const struct sr_input_module *module;
+	GString *buf;
+	struct sr_dev_inst *sdi;
+	gboolean sdi_ready;
+	void *priv;
+};
+
+/** Input (file) module driver. */
+struct sr_input_module {
+	/**
+	 * A unique ID for this input module, suitable for use in command-line
+	 * clients, [a-z0-9-]. Must not be NULL.
+	 */
+	const char *id;
+
+	/**
+	 * A unique name for this input module, suitable for use in GUI
+	 * clients, can contain UTF-8. Must not be NULL.
+	 */
+	const char *name;
+
+	/**
+	 * A short description of the input module. Must not be NULL.
+	 *
+	 * This can be displayed by frontends, e.g. when selecting the input
+	 * module for saving a file.
+	 */
+	const char *desc;
+
+	/**
+	 * A NULL terminated array of strings containing a list of file name
+	 * extensions typical for the input file format, or NULL if there is
+	 * no typical extension for this file format.
+	 */
+	const char *const *exts;
+
+	/**
+	 * Zero-terminated list of metadata items the module needs to be able
+	 * to identify an input stream. Can be all-zero, if the module cannot
+	 * identify streams at all, i.e. has to be forced into use.
+	 *
+	 * Each item is one of:
+	 *   SR_INPUT_META_FILENAME
+	 *   SR_INPUT_META_FILESIZE
+	 *   SR_INPUT_META_HEADER
+	 *
+	 * If the high bit (SR_INPUT META_REQUIRED) is set, the module cannot
+	 * identify a stream without the given metadata.
+	 */
+	const uint8_t metadata[8];
+
+	/**
+	 * Returns a NULL-terminated list of options this module can take.
+	 * Can be NULL, if the module has no options.
+	 */
+	const struct sr_option *(*options) (void);
+
+	/**
+	 * Check if this input module can load and parse the specified stream.
+	 *
+	 * @param[in] metadata Metadata the module can use to identify the stream.
+	 *
+	 * @retval SR_OK This module knows the format.
+	 * @retval SR_ERR_NA There wasn't enough data for this module to
+	 *   positively identify the format.
+	 * @retval SR_ERR_DATA This module knows the format, but cannot handle it.
+	 *   This means the stream is either corrupt, or indicates a feature
+	 *   that the module does not support.
+	 * @retval SR_ERR This module does not know the format.
+	 */
+	int (*format_match) (GHashTable *metadata);
+
+	/**
+	 * Initialize the input module.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	int (*init) (struct sr_input *in, GHashTable *options);
+
+	/**
+	 * Send data to the specified input instance.
+	 *
+	 * When an input module instance is created with sr_input_new(), this
+	 * function is used to feed data to the instance.
+	 *
+	 * As enough data gets fed into this function to completely populate
+	 * the device instance associated with this input instance, this is
+	 * guaranteed to return the moment it's ready. This gives the caller
+	 * the chance to examine the device instance, attach session callbacks
+	 * and so on.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	int (*receive) (struct sr_input *in, GString *buf);
+
+	/**
+	 * Signal the input module no more data will come.
+	 *
+	 * This will cause the module to process any data it may have buffered.
+	 * The SR_DF_END packet will also typically be sent at this time.
+	 */
+	int (*end) (struct sr_input *in);
+
+	/**
+	 * This function is called after the caller is finished using
+	 * the input module, and can be used to free any internal
+	 * resources the module may keep.
+	 *
+	 * This function is optional.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	void (*cleanup) (struct sr_input *in);
+};
+
+/** Output module instance. */
+struct sr_output {
+	/** A pointer to this output's module.  */
+	const struct sr_output_module *module;
+
+	/**
+	 * The device for which this output module is creating output. This
+	 * can be used by the module to find out channel names and numbers.
+	 */
+	const struct sr_dev_inst *sdi;
+
+	/**
+	 * The name of the file that the data should be written to.
+	 */
+	const char *filename;
+
+	/**
+	 * A generic pointer which can be used by the module to keep internal
+	 * state between calls into its callback functions.
+	 *
+	 * For example, the module might store a pointer to a chunk of output
+	 * there, and only flush it when it reaches a certain size.
+	 */
+	void *priv;
+};
+
+/** Output module driver. */
+struct sr_output_module {
+	/**
+	 * A unique ID for this output module, suitable for use in command-line
+	 * clients, [a-z0-9-]. Must not be NULL.
+	 */
+	const char *id;
+
+	/**
+	 * A unique name for this output module, suitable for use in GUI
+	 * clients, can contain UTF-8. Must not be NULL.
+	 */
+	const char *name;
+
+	/**
+	 * A short description of the output module. Must not be NULL.
+	 *
+	 * This can be displayed by frontends, e.g. when selecting the output
+	 * module for saving a file.
+	 */
+	const char *desc;
+
+	/**
+	 * A NULL terminated array of strings containing a list of file name
+	 * extensions typical for the input file format, or NULL if there is
+	 * no typical extension for this file format.
+	 */
+	const char *const *exts;
+
+	/**
+	 * Bitfield containing flags that describe certain properties
+	 * this output module may or may not have.
+	 * @see sr_output_flags
+	 */
+	const uint64_t flags;
+
+	/**
+	 * Returns a NULL-terminated list of options this module can take.
+	 * Can be NULL, if the module has no options.
+	 */
+	const struct sr_option *(*options) (void);
+
+	/**
+	 * This function is called once, at the beginning of an output stream.
+	 *
+	 * The device struct will be available in the output struct passed in,
+	 * as well as the param field -- which may be NULL or an empty string,
+	 * if no parameter was passed.
+	 *
+	 * The module can use this to initialize itself, create a struct for
+	 * keeping state and storing it in the <code>internal</code> field.
+	 *
+	 * @param o Pointer to the respective 'struct sr_output'.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	int (*init) (struct sr_output *o, GHashTable *options);
+
+	/**
+	 * This function is passed a copy of every packet in the data feed.
+	 * Any output generated by the output module in response to the
+	 * packet should be returned in a newly allocated GString
+	 * <code>out</code>, which will be freed by the caller.
+	 *
+	 * Packets not of interest to the output module can just be ignored,
+	 * and the <code>out</code> parameter set to NULL.
+	 *
+	 * @param o Pointer to the respective 'struct sr_output'.
+	 * @param sdi The device instance that generated the packet.
+	 * @param packet The complete packet.
+	 * @param out A pointer where a GString * should be stored if
+	 * the module generates output, or NULL if not.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	int (*receive) (const struct sr_output *o,
+			const struct sr_datafeed_packet *packet, GString **out);
+
+	/**
+	 * This function is called after the caller is finished using
+	 * the output module, and can be used to free any internal
+	 * resources the module may keep.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	int (*cleanup) (struct sr_output *o);
+};
+
+/** Transform module instance. */
+struct sr_transform {
+	/** A pointer to this transform's module.  */
+	const struct sr_transform_module *module;
+
+	/**
+	 * The device for which this transform module is used. This
+	 * can be used by the module to find out channel names and numbers.
+	 */
+	const struct sr_dev_inst *sdi;
+
+	/**
+	 * A generic pointer which can be used by the module to keep internal
+	 * state between calls into its callback functions.
+	 */
+	void *priv;
+};
+
+struct sr_transform_module {
+	/**
+	 * A unique ID for this transform module, suitable for use in
+	 * command-line clients, [a-z0-9-]. Must not be NULL.
+	 */
+	const char *id;
+
+	/**
+	 * A unique name for this transform module, suitable for use in GUI
+	 * clients, can contain UTF-8. Must not be NULL.
+	 */
+	const char *name;
+
+	/**
+	 * A short description of the transform module. Must not be NULL.
+	 *
+	 * This can be displayed by frontends, e.g. when selecting
+	 * which transform module(s) to add.
+	 */
+	const char *desc;
+
+	/**
+	 * Returns a NULL-terminated list of options this transform module
+	 * can take. Can be NULL, if the transform module has no options.
+	 */
+	const struct sr_option *(*options) (void);
+
+	/**
+	 * This function is called once, at the beginning of a stream.
+	 *
+	 * @param t Pointer to the respective 'struct sr_transform'.
+	 * @param options Hash table of options for this transform module.
+	 *                Can be NULL if no options are to be used.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	int (*init) (struct sr_transform *t, GHashTable *options);
+
+	/**
+	 * This function is passed a pointer to every packet in the data feed.
+	 *
+	 * It can either return (in packet_out) a pointer to another packet
+	 * (possibly the exact same packet it got as input), or NULL.
+	 *
+	 * @param t Pointer to the respective 'struct sr_transform'.
+	 * @param packet_in Pointer to a datafeed packet.
+	 * @param packet_out Pointer to the resulting datafeed packet after
+	 *                   this function was run. If NULL, the transform
+	 *                   module intentionally didn't output a new packet.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	int (*receive) (const struct sr_transform *t,
+			struct sr_datafeed_packet *packet_in,
+			struct sr_datafeed_packet **packet_out);
+
+	/**
+	 * This function is called after the caller is finished using
+	 * the transform module, and can be used to free any internal
+	 * resources the module may keep.
+	 *
+	 * @retval SR_OK Success
+	 * @retval other Negative error code.
+	 */
+	int (*cleanup) (struct sr_transform *t);
+};
+
+#ifdef HAVE_LIBUSB_1_0
+/** USB device instance */
+struct sr_usb_dev_inst {
+	/** USB bus */
+	uint8_t bus;
+	/** Device address on USB bus */
+	uint8_t address;
+	/** libusb device handle */
+	struct libusb_device_handle *devhdl;
+};
+#endif
+
+#ifdef HAVE_LIBSERIALPORT
+struct sr_serial_dev_inst {
+	/** Port name, e.g. '/dev/tty42'. */
+	char *port;
+	/** Comm params for serial_set_paramstr(). */
+	char *serialcomm;
+	/** libserialport port handle */
+	struct sp_port *data;
+};
+#endif
+
+struct sr_usbtmc_dev_inst {
+	char *device;
+	int fd;
+};
+
+/* Private driver context. */
+struct drv_context {
+	/** sigrok context */
+	struct sr_context *sr_ctx;
+	GSList *instances;
+};
+
+/*--- log.c -----------------------------------------------------------------*/
+
+#if defined(G_OS_WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
+/*
+ * On MinGW, we need to specify the gnu_printf format flavor or GCC
+ * will assume non-standard Microsoft printf syntax.
+ */
+SR_PRIV int sr_log(int loglevel, const char *format, ...)
+		__attribute__((__format__ (__gnu_printf__, 2, 3)));
+#else
+SR_PRIV int sr_log(int loglevel, const char *format, ...) G_GNUC_PRINTF(2, 3);
+#endif
+
+/* Message logging helpers with subsystem-specific prefix string. */
+#define sr_spew(...)	sr_log(SR_LOG_SPEW, LOG_PREFIX ": " __VA_ARGS__)
+#define sr_dbg(...)	sr_log(SR_LOG_DBG,  LOG_PREFIX ": " __VA_ARGS__)
+#define sr_info(...)	sr_log(SR_LOG_INFO, LOG_PREFIX ": " __VA_ARGS__)
+#define sr_warn(...)	sr_log(SR_LOG_WARN, LOG_PREFIX ": " __VA_ARGS__)
+#define sr_err(...)	sr_log(SR_LOG_ERR,  LOG_PREFIX ": " __VA_ARGS__)
+
+/*--- device.c --------------------------------------------------------------*/
+
+/** Scan options supported by a driver. */
+#define SR_CONF_SCAN_OPTIONS 0x7FFF0000
+
+/** Device options for a particular device. */
+#define SR_CONF_DEVICE_OPTIONS 0x7FFF0001
+
+/** Mask for separating config keys from capabilities. */
+#define SR_CONF_MASK 0x1fffffff
+
+/** Values for the changes argument of sr_dev_driver.config_channel_set. */
+enum {
+	/** The enabled state of the channel has been changed. */
+	SR_CHANNEL_SET_ENABLED = 1 << 0,
+};
+
+SR_PRIV struct sr_channel *sr_channel_new(struct sr_dev_inst *sdi,
+		int index, int type, gboolean enabled, const char *name);
+SR_PRIV struct sr_channel *sr_next_enabled_channel(const struct sr_dev_inst *sdi,
+		struct sr_channel *cur_channel);
+
+/** Device instance data */
+struct sr_dev_inst {
+	/** Device driver. */
+	struct sr_dev_driver *driver;
+	/** Device instance status. SR_ST_NOT_FOUND, etc. */
+	int status;
+	/** Device instance type. SR_INST_USB, etc. */
+	int inst_type;
+	/** Device vendor. */
+	char *vendor;
+	/** Device model. */
+	char *model;
+	/** Device version. */
+	char *version;
+	/** Serial number. */
+	char *serial_num;
+	/** Connection string to uniquely identify devices. */
+	char *connection_id;
+	/** List of channels. */
+	GSList *channels;
+	/** List of sr_channel_group structs */
+	GSList *channel_groups;
+	/** Device instance connection data (used?) */
+	void *conn;
+	/** Device instance private data (used?) */
+	void *priv;
+	/** Session to which this device is currently assigned. */
+	struct sr_session *session;
+};
+
+/* Generic device instances */
+SR_PRIV void sr_dev_inst_free(struct sr_dev_inst *sdi);
+
+#ifdef HAVE_LIBUSB_1_0
+/* USB-specific instances */
+SR_PRIV struct sr_usb_dev_inst *sr_usb_dev_inst_new(uint8_t bus,
+		uint8_t address, struct libusb_device_handle *hdl);
+SR_PRIV void sr_usb_dev_inst_free(struct sr_usb_dev_inst *usb);
+#endif
+
+#ifdef HAVE_LIBSERIALPORT
+/* Serial-specific instances */
+SR_PRIV struct sr_serial_dev_inst *sr_serial_dev_inst_new(const char *port,
+		const char *serialcomm);
+SR_PRIV void sr_serial_dev_inst_free(struct sr_serial_dev_inst *serial);
+#endif
+
+/* USBTMC-specific instances */
+SR_PRIV struct sr_usbtmc_dev_inst *sr_usbtmc_dev_inst_new(const char *device);
+SR_PRIV void sr_usbtmc_dev_inst_free(struct sr_usbtmc_dev_inst *usbtmc);
+
+/*--- hwdriver.c ------------------------------------------------------------*/
+
+extern SR_PRIV struct sr_dev_driver **drivers_lists[];
+
+SR_PRIV const GVariantType *sr_variant_type_get(int datatype);
+SR_PRIV int sr_variant_type_check(uint32_t key, GVariant *data);
+SR_PRIV void sr_hw_cleanup_all(const struct sr_context *ctx);
+SR_PRIV struct sr_config *sr_config_new(uint32_t key, GVariant *data);
+SR_PRIV void sr_config_free(struct sr_config *src);
+
+/*--- session.c -------------------------------------------------------------*/
+
+struct sr_session {
+	/** Context this session exists in. */
+	struct sr_context *ctx;
+	/** List of struct sr_dev_inst pointers. */
+	GSList *devs;
+	/** List of struct sr_dev_inst pointers owned by this session. */
+	GSList *owned_devs;
+	/** List of struct datafeed_callback pointers. */
+	GSList *datafeed_callbacks;
+	GSList *transforms;
+	struct sr_trigger *trigger;
+
+	/** Callback to invoke on session stop. */
+	sr_session_stopped_callback stopped_callback;
+	/** User data to be passed to the session stop callback. */
+	void *stopped_cb_data;
+
+	/** Mutex protecting the main context pointer. */
+	GMutex main_mutex;
+	/** Context of the session main loop. */
+	GMainContext *main_context;
+
+	/** Registered event sources for this session. */
+	GHashTable *event_sources;
+	/** Session main loop. */
+	GMainLoop *main_loop;
+	/** ID of idle source for dispatching the session stop notification. */
+	unsigned int stop_check_id;
+	/** Whether the session has been started. */
+	gboolean running;
+};
+
+SR_PRIV int sr_session_source_add_internal(struct sr_session *session,
+		void *key, GSource *source);
+SR_PRIV int sr_session_source_remove_internal(struct sr_session *session,
+		void *key);
+SR_PRIV int sr_session_source_destroyed(struct sr_session *session,
+		void *key, GSource *source);
+SR_PRIV int sr_session_fd_source_add(struct sr_session *session,
+		void *key, gintptr fd, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data);
+
+SR_PRIV int sr_session_source_add(struct sr_session *session, int fd,
+		int events, int timeout, sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int sr_session_source_add_pollfd(struct sr_session *session,
+		GPollFD *pollfd, int timeout, sr_receive_data_callback cb,
+		void *cb_data);
+SR_PRIV int sr_session_source_add_channel(struct sr_session *session,
+		GIOChannel *channel, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int sr_session_source_remove(struct sr_session *session, int fd);
+SR_PRIV int sr_session_source_remove_pollfd(struct sr_session *session,
+		GPollFD *pollfd);
+SR_PRIV int sr_session_source_remove_channel(struct sr_session *session,
+		GIOChannel *channel);
+
+SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
+		const struct sr_datafeed_packet *packet);
+SR_PRIV int sr_sessionfile_check(const char *filename);
+SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
+		struct sr_datafeed_packet **copy);
+SR_PRIV void sr_packet_free(struct sr_datafeed_packet *packet);
+
+/*--- session_file.c --------------------------------------------------------*/
+
+#if !HAVE_ZIP_DISCARD
+/* Replace zip_discard() if not available. */
+#define zip_discard(zip) sr_zip_discard(zip)
+SR_PRIV void sr_zip_discard(struct zip *archive);
+#endif
+
+SR_PRIV GKeyFile *sr_sessionfile_read_metadata(struct zip *archive,
+			const struct zip_stat *entry);
+
+/*--- analog.c --------------------------------------------------------------*/
+
+SR_PRIV int sr_analog_init(struct sr_datafeed_analog *analog,
+                           struct sr_analog_encoding *encoding,
+                           struct sr_analog_meaning *meaning,
+                           struct sr_analog_spec *spec,
+                           int digits);
+
+/*--- std.c -----------------------------------------------------------------*/
+
+typedef int (*dev_close_callback)(struct sr_dev_inst *sdi);
+typedef void (*std_dev_clear_callback)(void *priv);
+
+SR_PRIV int std_init(struct sr_context *sr_ctx, struct sr_dev_driver *di,
+		const char *prefix);
+#ifdef HAVE_LIBSERIALPORT
+SR_PRIV int std_serial_dev_open(struct sr_dev_inst *sdi);
+SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi,
+		void *cb_data, dev_close_callback dev_close_fn,
+		struct sr_serial_dev_inst *serial, const char *prefix);
+#endif
+SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi,
+		const char *prefix);
+SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
+		std_dev_clear_callback clear_private);
+SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi);
+
+/*--- resource.c ------------------------------------------------------------*/
+
+SR_PRIV int64_t sr_file_get_size(FILE *file);
+
+SR_PRIV int sr_resource_open(struct sr_context *ctx,
+		struct sr_resource *res, int type, const char *name)
+		G_GNUC_WARN_UNUSED_RESULT;
+SR_PRIV int sr_resource_close(struct sr_context *ctx,
+		struct sr_resource *res);
+SR_PRIV gssize sr_resource_read(struct sr_context *ctx,
+		const struct sr_resource *res, void *buf, size_t count)
+		G_GNUC_WARN_UNUSED_RESULT;
+SR_PRIV void *sr_resource_load(struct sr_context *ctx, int type,
+		const char *name, size_t *size, size_t max_size)
+		G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+
+/*--- strutil.c -------------------------------------------------------------*/
+
+SR_PRIV int sr_atol(const char *str, long *ret);
+SR_PRIV int sr_atoi(const char *str, int *ret);
+SR_PRIV int sr_atod(const char *str, double *ret);
+SR_PRIV int sr_atof(const char *str, float *ret);
+SR_PRIV int sr_atof_ascii(const char *str, float *ret);
+
+/*--- soft-trigger.c --------------------------------------------------------*/
+
+struct soft_trigger_logic {
+	const struct sr_dev_inst *sdi;
+	const struct sr_trigger *trigger;
+	int count;
+	int unitsize;
+	int cur_stage;
+	uint8_t *prev_sample;
+	uint8_t *pre_trigger_buffer;
+	uint8_t *pre_trigger_head;
+	int pre_trigger_size;
+	int pre_trigger_fill;
+};
+
+SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
+		const struct sr_dev_inst *sdi, struct sr_trigger *trigger,
+		int pre_trigger_samples);
+SR_PRIV void soft_trigger_logic_free(struct soft_trigger_logic *st);
+SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *st, uint8_t *buf,
+		int len, int *pre_trigger_samples);
+
+/*--- hardware/serial.c -----------------------------------------------------*/
+
+#ifdef HAVE_LIBSERIALPORT
+enum {
+	SERIAL_RDWR = 1,
+	SERIAL_RDONLY = 2,
+};
+
+typedef gboolean (*packet_valid_callback)(const uint8_t *buf);
+
+SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags);
+SR_PRIV int serial_close(struct sr_serial_dev_inst *serial);
+SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial);
+SR_PRIV int serial_drain(struct sr_serial_dev_inst *serial);
+SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial,
+		const void *buf, size_t count, unsigned int timeout_ms);
+SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial,
+		const void *buf, size_t count);
+SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf,
+		size_t count, unsigned int timeout_ms);
+SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf,
+		size_t count);
+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);
+SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
+		const char *paramstr);
+SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
+		int *buflen, gint64 timeout_ms);
+SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
+				 uint8_t *buf, size_t *buflen,
+				 size_t packet_size,
+				 packet_valid_callback is_valid,
+				 uint64_t timeout_ms, int baudrate);
+SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_device,
+				      const char **serial_options);
+SR_PRIV int serial_source_add(struct sr_session *session,
+		struct sr_serial_dev_inst *serial, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int serial_source_remove(struct sr_session *session,
+		struct sr_serial_dev_inst *serial);
+SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id);
+SR_PRIV int serial_timeout(struct sr_serial_dev_inst *port, int num_bytes);
+#endif
+
+/*--- hardware/ezusb.c ------------------------------------------------------*/
+
+#ifdef HAVE_LIBUSB_1_0
+SR_PRIV int ezusb_reset(struct libusb_device_handle *hdl, int set_clear);
+SR_PRIV int ezusb_install_firmware(struct sr_context *ctx, libusb_device_handle *hdl,
+				   const char *name);
+SR_PRIV int ezusb_upload_firmware(struct sr_context *ctx, libusb_device *dev,
+				  int configuration, const char *name);
+#endif
+
+/*--- hardware/usb.c --------------------------------------------------------*/
+
+#ifdef HAVE_LIBUSB_1_0
+SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn);
+SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb);
+SR_PRIV void sr_usb_close(struct sr_usb_dev_inst *usb);
+SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
+		int timeout, sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx);
+SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len);
+#endif
+
+
+/*--- modbus/modbus.c -------------------------------------------------------*/
+
+struct sr_modbus_dev_inst {
+	const char *name;
+	const char *prefix;
+	int priv_size;
+	GSList *(*scan)(int modbusaddr);
+	int (*dev_inst_new)(void *priv, const char *resource,
+		char **params, const char *serialcomm, int modbusaddr);
+	int (*open)(void *priv);
+	int (*source_add)(struct sr_session *session, void *priv, int events,
+		int timeout, sr_receive_data_callback cb, void *cb_data);
+	int (*source_remove)(struct sr_session *session, void *priv);
+	int (*send)(void *priv, const uint8_t *buffer, int buffer_size);
+	int (*read_begin)(void *priv, uint8_t *function_code);
+	int (*read_data)(void *priv, uint8_t *buf, int maxlen);
+	int (*read_end)(void *priv);
+	int (*close)(void *priv);
+	void (*free)(void *priv);
+	unsigned int read_timeout_ms;
+	void *priv;
+};
+
+SR_PRIV GSList *sr_modbus_scan(struct drv_context *drvc, GSList *options,
+		struct sr_dev_inst *(*probe_device)(struct sr_modbus_dev_inst *modbus));
+SR_PRIV struct sr_modbus_dev_inst *modbus_dev_inst_new(const char *resource,
+		const char *serialcomm, int modbusaddr);
+SR_PRIV int sr_modbus_open(struct sr_modbus_dev_inst *modbus);
+SR_PRIV int sr_modbus_source_add(struct sr_session *session,
+		struct sr_modbus_dev_inst *modbus, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int sr_modbus_source_remove(struct sr_session *session,
+		struct sr_modbus_dev_inst *modbus);
+SR_PRIV int sr_modbus_request(struct sr_modbus_dev_inst *modbus,
+                              uint8_t *request, int request_size);
+SR_PRIV int sr_modbus_reply(struct sr_modbus_dev_inst *modbus,
+                            uint8_t *reply, int reply_size);
+SR_PRIV int sr_modbus_request_reply(struct sr_modbus_dev_inst *modbus,
+                                    uint8_t *request, int request_size,
+                                    uint8_t *reply, int reply_size);
+SR_PRIV int sr_modbus_read_coils(struct sr_modbus_dev_inst *modbus,
+                                 int address, int nb_coils, uint8_t *coils);
+SR_PRIV int sr_modbus_read_holding_registers(struct sr_modbus_dev_inst *modbus,
+                                             int address, int nb_registers,
+                                             uint16_t *registers);
+SR_PRIV int sr_modbus_write_coil(struct sr_modbus_dev_inst *modbus,
+                                 int address, int value);
+SR_PRIV int sr_modbus_write_multiple_registers(struct sr_modbus_dev_inst*modbus,
+                                               int address, int nb_registers,
+                                               uint16_t *registers);
+SR_PRIV int sr_modbus_close(struct sr_modbus_dev_inst *modbus);
+SR_PRIV void sr_modbus_free(struct sr_modbus_dev_inst *modbus);
+
+/*--- hardware/dmm/es519xx.c ------------------------------------------------*/
+
+/**
+ * All 11-byte es519xx chips repeat each block twice for each conversion cycle
+ * so always read 2 blocks at a time.
+ */
+#define ES519XX_11B_PACKET_SIZE (11 * 2)
+#define ES519XX_14B_PACKET_SIZE 14
+
+struct es519xx_info {
+	gboolean is_judge, is_voltage, is_auto, is_micro, is_current;
+	gboolean is_milli, is_resistance, is_continuity, is_diode;
+	gboolean is_frequency, is_rpm, is_capacitance, is_duty_cycle;
+	gboolean is_temperature, is_celsius, is_fahrenheit;
+	gboolean is_adp0, is_adp1, is_adp2, is_adp3;
+	gboolean is_sign, is_batt, is_ol, is_pmax, is_pmin, is_apo;
+	gboolean is_dc, is_ac, is_vahz, is_min, is_max, is_rel, is_hold;
+	gboolean is_digit4, is_ul, is_vasel, is_vbar, is_lpf1, is_lpf0, is_rmr;
+	uint32_t baudrate;
+	int packet_size;
+	gboolean alt_functions, fivedigits, clampmeter, selectable_lpf;
+};
+
+SR_PRIV gboolean sr_es519xx_2400_11b_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_2400_11b_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV gboolean sr_es519xx_2400_11b_altfn_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_2400_11b_altfn_parse(const uint8_t *buf,
+		float *floatval, struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_11b_5digits_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_11b_5digits_parse(const uint8_t *buf,
+		float *floatval, struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_11b_clamp_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_11b_clamp_parse(const uint8_t *buf,
+		float *floatval, struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_11b_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_11b_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_14b_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_14b_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV gboolean sr_es519xx_19200_14b_sel_lpf_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_es519xx_19200_14b_sel_lpf_parse(const uint8_t *buf,
+		float *floatval, struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/fs9922.c -------------------------------------------------*/
+
+#define FS9922_PACKET_SIZE 14
+
+struct fs9922_info {
+	gboolean is_auto, is_dc, is_ac, is_rel, is_hold, is_bpn, is_z1, is_z2;
+	gboolean is_max, is_min, is_apo, is_bat, is_nano, is_z3, is_micro;
+	gboolean is_milli, is_kilo, is_mega, is_beep, is_diode, is_percent;
+	gboolean is_z4, is_volt, is_ampere, is_ohm, is_hfe, is_hertz, is_farad;
+	gboolean is_celsius, is_fahrenheit;
+	int bargraph_sign, bargraph_value;
+};
+
+SR_PRIV gboolean sr_fs9922_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_fs9922_parse(const uint8_t *buf, float *floatval,
+			    struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV void sr_fs9922_z1_diode(struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/fs9721.c -------------------------------------------------*/
+
+#define FS9721_PACKET_SIZE 14
+
+struct fs9721_info {
+	gboolean is_ac, is_dc, is_auto, is_rs232, is_micro, is_nano, is_kilo;
+	gboolean is_diode, is_milli, is_percent, is_mega, is_beep, is_farad;
+	gboolean is_ohm, is_rel, is_hold, is_ampere, is_volt, is_hz, is_bat;
+	gboolean is_c2c1_11, is_c2c1_10, is_c2c1_01, is_c2c1_00, is_sign;
+};
+
+SR_PRIV gboolean sr_fs9721_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_fs9721_parse(const uint8_t *buf, float *floatval,
+			    struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV void sr_fs9721_00_temp_c(struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV void sr_fs9721_01_temp_c(struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV void sr_fs9721_10_temp_c(struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV void sr_fs9721_01_10_temp_f_c(struct sr_datafeed_analog_old *analog, void *info);
+SR_PRIV void sr_fs9721_max_c_min(struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/dtm0660.c ------------------------------------------------*/
+
+#define DTM0660_PACKET_SIZE 15
+
+struct dtm0660_info {
+	gboolean is_ac, is_dc, is_auto, is_rs232, is_micro, is_nano, is_kilo;
+	gboolean is_diode, is_milli, is_percent, is_mega, is_beep, is_farad;
+	gboolean is_ohm, is_rel, is_hold, is_ampere, is_volt, is_hz, is_bat;
+	gboolean is_degf, is_degc, is_c2c1_01, is_c2c1_00, is_apo, is_min;
+	gboolean is_minmax, is_max, is_sign;
+};
+
+SR_PRIV gboolean sr_dtm0660_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_dtm0660_parse(const uint8_t *buf, float *floatval,
+			struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/m2110.c --------------------------------------------------*/
+
+#define BBCGM_M2110_PACKET_SIZE 9
+
+SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval,
+			     struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/metex14.c ------------------------------------------------*/
+
+#define METEX14_PACKET_SIZE 14
+
+struct metex14_info {
+	gboolean is_ac, is_dc, is_resistance, is_capacity, is_temperature;
+	gboolean is_diode, is_frequency, is_ampere, is_volt, is_farad;
+	gboolean is_hertz, is_ohm, is_celsius, is_pico, is_nano, is_micro;
+	gboolean is_milli, is_kilo, is_mega, is_gain, is_decibel, is_hfe;
+	gboolean is_unitless, is_logic;
+};
+
+#ifdef HAVE_LIBSERIALPORT
+SR_PRIV int sr_metex14_packet_request(struct sr_serial_dev_inst *serial);
+#endif
+SR_PRIV gboolean sr_metex14_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_metex14_parse(const uint8_t *buf, float *floatval,
+			     struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/rs9lcd.c -------------------------------------------------*/
+
+#define RS9LCD_PACKET_SIZE 9
+
+/* Dummy info struct. The parser does not use it. */
+struct rs9lcd_info { int dummy; };
+
+SR_PRIV gboolean sr_rs9lcd_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval,
+			    struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/bm25x.c --------------------------------------------------*/
+
+#define BRYMEN_BM25X_PACKET_SIZE 15
+
+/* Dummy info struct. The parser does not use it. */
+struct bm25x_info { int dummy; };
+
+SR_PRIV gboolean sr_brymen_bm25x_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_brymen_bm25x_parse(const uint8_t *buf, float *floatval,
+			     struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/ut71x.c --------------------------------------------------*/
+
+#define UT71X_PACKET_SIZE 11
+
+struct ut71x_info {
+	gboolean is_voltage, is_resistance, is_capacitance, is_temperature;
+	gboolean is_celsius, is_fahrenheit, is_current, is_continuity;
+	gboolean is_diode, is_frequency, is_duty_cycle, is_dc, is_ac;
+	gboolean is_auto, is_manual, is_sign, is_power, is_loop_current;
+};
+
+SR_PRIV gboolean sr_ut71x_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_ut71x_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/dmm/vc870.c --------------------------------------------------*/
+
+#define VC870_PACKET_SIZE 23
+
+struct vc870_info {
+	gboolean is_voltage, is_dc, is_ac, is_temperature, is_resistance;
+	gboolean is_continuity, is_capacitance, is_diode, is_loop_current;
+	gboolean is_current, is_micro, is_milli, is_power;
+	gboolean is_power_factor_freq, is_power_apparent_power, is_v_a_rms_value;
+	gboolean is_sign2, is_sign1, is_batt, is_ol1, is_max, is_min;
+	gboolean is_maxmin, is_rel, is_ol2, is_open, is_manu, is_hold;
+	gboolean is_light, is_usb, is_warning, is_auto_power, is_misplug_warn;
+	gboolean is_lo, is_hi, is_open2;
+
+	gboolean is_frequency, is_dual_display, is_auto;
+};
+
+SR_PRIV gboolean sr_vc870_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_vc870_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/lcr/es51919.c ------------------------------------------------*/
+
+SR_PRIV void es51919_serial_clean(void *priv);
+SR_PRIV struct sr_dev_inst *es51919_serial_scan(GSList *options,
+						const char *vendor,
+						const char *model);
+SR_PRIV int es51919_serial_config_get(uint32_t key, GVariant **data,
+				      const struct sr_dev_inst *sdi,
+				      const struct sr_channel_group *cg);
+SR_PRIV int es51919_serial_config_set(uint32_t key, GVariant *data,
+				      const struct sr_dev_inst *sdi,
+				      const struct sr_channel_group *cg);
+SR_PRIV int es51919_serial_config_list(uint32_t key, GVariant **data,
+				       const struct sr_dev_inst *sdi,
+				       const struct sr_channel_group *cg);
+SR_PRIV int es51919_serial_acquisition_start(const struct sr_dev_inst *sdi,
+					     void *cb_data);
+SR_PRIV int es51919_serial_acquisition_stop(struct sr_dev_inst *sdi,
+					    void *cb_data);
+
+/*--- hardware/dmm/ut372.c --------------------------------------------------*/
+
+#define UT372_PACKET_SIZE 27
+
+struct ut372_info {
+	int dummy;
+};
+
+SR_PRIV gboolean sr_ut372_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_ut372_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info);
+
+/*--- hardware/scale/kern.c -------------------------------------------------*/
+
+struct kern_info {
+	gboolean is_gram, is_carat, is_ounce, is_pound, is_troy_ounce;
+	gboolean is_pennyweight, is_grain, is_tael, is_momme, is_tola;
+	gboolean is_percentage, is_piece, is_unstable, is_stable, is_error;
+	int buflen;
+};
+
+SR_PRIV gboolean sr_kern_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_kern_parse(const uint8_t *buf, float *floatval,
+		struct sr_datafeed_analog_old *analog, void *info);
+
+#endif
diff --git a/log.c b/src/log.c
similarity index 61%
rename from log.c
rename to src/log.c
index db16c30..3d65cc4 100644
--- a/log.c
+++ b/src/log.c
@@ -18,14 +18,15 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include "libsigrok.h"
-/** @cond PRIVATE */
-#define NO_LOG_WRAPPERS
-/** @endcond */
+#include <glib/gprintf.h>
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
+#define LOG_PREFIX "log"
+
 /**
  * @file
  *
@@ -56,12 +57,10 @@ static sr_log_callback sr_log_cb = sr_logv;
  */
 static void *sr_log_cb_data = NULL;
 
-/* Log domain (a short string that is used as prefix for all messages). */
 /** @cond PRIVATE */
-#define LOGDOMAIN_MAXLEN 30
-#define LOGDOMAIN_DEFAULT "sr: "
+#define LOGLEVEL_TIMESTAMP SR_LOG_DBG
 /** @endcond */
-static char sr_log_domain[LOGDOMAIN_MAXLEN + 1] = LOGDOMAIN_DEFAULT;
+static int64_t sr_log_start_time = 0;
 
 /**
  * Set the libsigrok loglevel.
@@ -86,6 +85,9 @@ SR_API int sr_log_loglevel_set(int loglevel)
 		sr_err("Invalid loglevel %d.", loglevel);
 		return SR_ERR_ARG;
 	}
+	/* Output time stamps relative to time at startup */
+	if (loglevel >= LOGLEVEL_TIMESTAMP && sr_log_start_time == 0)
+		sr_log_start_time = g_get_monotonic_time();
 
 	cur_loglevel = loglevel;
 
@@ -107,51 +109,6 @@ SR_API int sr_log_loglevel_get(void)
 }
 
 /**
- * Set the libsigrok logdomain string.
- *
- * @param logdomain The string to use as logdomain for libsigrok log
- *                  messages from now on. Must not be NULL. The maximum
- *                  length of the string is 30 characters (this does not
- *                  include the trailing NUL-byte). Longer strings are
- *                  silently truncated.
- *                  In order to not use a logdomain, pass an empty string.
- *                  The function makes its own copy of the input string, i.e.
- *                  the caller does not need to keep it around.
- *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid logdomain.
- *
- * @since 0.1.0
- */
-SR_API int sr_log_logdomain_set(const char *logdomain)
-{
-	if (!logdomain) {
-		sr_err("log: %s: logdomain was NULL", __func__);
-		return SR_ERR_ARG;
-	}
-
-	/* TODO: Error handling. */
-	snprintf((char *)&sr_log_domain, LOGDOMAIN_MAXLEN, "%s", logdomain);
-
-	sr_dbg("Log domain set to '%s'.", (const char *)&sr_log_domain);
-
-	return SR_OK;
-}
-
-/**
- * Get the currently configured libsigrok logdomain.
- *
- * @return A copy of the currently configured libsigrok logdomain
- *         string. The caller is responsible for g_free()ing the string when
- *         it is no longer needed.
- *
- * @since 0.1.0
- */
-SR_API char *sr_log_logdomain_get(void)
-{
-	return g_strdup((const char *)&sr_log_domain);
-}
-
-/**
  * Set the libsigrok log callback to the specified function.
  *
  * @param cb Function pointer to the log callback function to use.
@@ -169,7 +126,7 @@ SR_API char *sr_log_logdomain_get(void)
 SR_API int sr_log_callback_set(sr_log_callback cb, void *cb_data)
 {
 	if (!cb) {
-		sr_err("log: %s: cb was NULL", __func__);
+		sr_err("%s: cb was NULL", __func__);
 		return SR_ERR_ARG;
 	}
 
@@ -204,6 +161,8 @@ SR_API int sr_log_callback_set_default(void)
 
 static int sr_logv(void *cb_data, int loglevel, const char *format, va_list args)
 {
+	uint64_t elapsed_us, minutes;
+	unsigned int rest_us, seconds, microseconds;
 	int ret;
 
 	/* This specific log callback doesn't need the void pointer data. */
@@ -211,89 +170,37 @@ static int sr_logv(void *cb_data, int loglevel, const char *format, va_list args
 
 	/* Only output messages of at least the selected loglevel(s). */
 	if (loglevel > cur_loglevel)
-		return SR_OK; /* TODO? */
+		return SR_OK;
 
-	if (sr_log_domain[0] != '\0')
-		fprintf(stderr, "%s", sr_log_domain);
-	ret = vfprintf(stderr, format, args);
-	fprintf(stderr, "\n");
+	if (cur_loglevel >= LOGLEVEL_TIMESTAMP) {
+		elapsed_us = g_get_monotonic_time() - sr_log_start_time;
 
-	return ret;
-}
+		minutes = elapsed_us / G_TIME_SPAN_MINUTE;
+		rest_us = elapsed_us % G_TIME_SPAN_MINUTE;
+		seconds = rest_us / G_TIME_SPAN_SECOND;
+		microseconds = rest_us % G_TIME_SPAN_SECOND;
 
-/** @private */
-SR_PRIV int sr_log(int loglevel, const char *format, ...)
-{
-	int ret;
-	va_list args;
-
-	va_start(args, format);
-	ret = sr_log_cb(sr_log_cb_data, loglevel, format, args);
-	va_end(args);
-
-	return ret;
-}
-
-/** @private */
-SR_PRIV int sr_spew(const char *format, ...)
-{
-	int ret;
-	va_list args;
-
-	va_start(args, format);
-	ret = sr_log_cb(sr_log_cb_data, SR_LOG_SPEW, format, args);
-	va_end(args);
-
-	return ret;
-}
-
-/** @private */
-SR_PRIV int sr_dbg(const char *format, ...)
-{
-	int ret;
-	va_list args;
-
-	va_start(args, format);
-	ret = sr_log_cb(sr_log_cb_data, SR_LOG_DBG, format, args);
-	va_end(args);
-
-	return ret;
-}
-
-/** @private */
-SR_PRIV int sr_info(const char *format, ...)
-{
-	int ret;
-	va_list args;
-
-	va_start(args, format);
-	ret = sr_log_cb(sr_log_cb_data, SR_LOG_INFO, format, args);
-	va_end(args);
-
-	return ret;
-}
-
-/** @private */
-SR_PRIV int sr_warn(const char *format, ...)
-{
-	int ret;
-	va_list args;
+		ret = g_fprintf(stderr, "sr: [%.2" PRIu64 ":%.2u.%.6u] ",
+				minutes, seconds, microseconds);
+	} else {
+		ret = fputs("sr: ", stderr);
+	}
 
-	va_start(args, format);
-	ret = sr_log_cb(sr_log_cb_data, SR_LOG_WARN, format, args);
-	va_end(args);
+	if (ret < 0 || g_vfprintf(stderr, format, args) < 0
+			|| putc('\n', stderr) < 0)
+		return SR_ERR;
 
-	return ret;
+	return SR_OK;
 }
 
 /** @private */
-SR_PRIV int sr_err(const char *format, ...)
+SR_PRIV int sr_log(int loglevel, const char *format, ...)
 {
 	int ret;
 	va_list args;
 
 	va_start(args, format);
-	ret = sr_log_cb(sr_log_cb_data, SR_LOG_ERR, format, args);
+	ret = sr_log_cb(sr_log_cb_data, loglevel, format, args);
 	va_end(args);
 
 	return ret;
diff --git a/src/modbus/modbus.c b/src/modbus/modbus.c
new file mode 100644
index 0000000..bd8c834
--- /dev/null
+++ b/src/modbus/modbus.c
@@ -0,0 +1,578 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Aurelien Jacobs <aurel at gnuage.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "modbus"
+
+SR_PRIV extern const struct sr_modbus_dev_inst modbus_serial_rtu_dev;
+
+static const struct sr_modbus_dev_inst *modbus_devs[] = {
+#ifdef HAVE_LIBSERIALPORT
+	&modbus_serial_rtu_dev,  /* Must be last as it matches any resource. */
+#endif
+};
+
+static const unsigned int modbus_devs_size = ARRAY_SIZE(modbus_devs);
+
+static struct sr_dev_inst *sr_modbus_scan_resource(const char *resource,
+	const char *serialcomm, int modbusaddr,
+	struct sr_dev_inst *(*probe_device)(struct sr_modbus_dev_inst *modbus))
+{
+	struct sr_modbus_dev_inst *modbus;
+	struct sr_dev_inst *sdi;
+
+	if (!(modbus = modbus_dev_inst_new(resource, serialcomm, modbusaddr)))
+		return NULL;
+
+	if (sr_modbus_open(modbus) != SR_OK) {
+		sr_info("Couldn't open Modbus device.");
+		sr_modbus_free(modbus);
+		return NULL;
+	};
+
+	if ((sdi = probe_device(modbus)))
+		return sdi;
+
+	sr_modbus_close(modbus);
+	sr_modbus_free(modbus);
+
+	return NULL;
+}
+
+/**
+ * Scan for Modbus devices which match a probing function.
+ *
+ * @param drvc The driver context doing the scan.
+ * @param options The scan options to find devies.
+ * @param probe_device The callback function that will be called for each
+ *                     found device to validate whether this device matches
+ *                     what we are scanning for.
+ *
+ * @return A list of the devices found or NULL if no devices were found.
+ */
+SR_PRIV GSList *sr_modbus_scan(struct drv_context *drvc, GSList *options,
+	struct sr_dev_inst *(*probe_device)(struct sr_modbus_dev_inst *modbus))
+{
+	GSList *resources, *l, *devices;
+	struct sr_dev_inst *sdi;
+	const char *resource = NULL;
+	const char *serialcomm = NULL;
+	int modbusaddr = 1;
+	gchar **res;
+	unsigned int i;
+
+	for (l = options; l; l = l->next) {
+		struct sr_config *src = l->data;
+		switch (src->key) {
+		case SR_CONF_CONN:
+			resource = g_variant_get_string(src->data, NULL);
+			break;
+		case SR_CONF_SERIALCOMM:
+			serialcomm = g_variant_get_string(src->data, NULL);
+			break;
+		case SR_CONF_MODBUSADDR:
+			modbusaddr = g_variant_get_uint64(src->data);
+			break;
+		}
+	}
+
+	devices = NULL;
+	for (i = 0; i < modbus_devs_size; i++) {
+		if ((resource && strcmp(resource, modbus_devs[i]->prefix))
+		    || !modbus_devs[i]->scan)
+			continue;
+		resources = modbus_devs[i]->scan(modbusaddr);
+		for (l = resources; l; l = l->next) {
+			res = g_strsplit(l->data, ":", 2);
+			if (res[0] && (sdi = sr_modbus_scan_resource(res[0],
+					serialcomm ? serialcomm : res[1],
+					modbusaddr, probe_device))) {
+				devices = g_slist_append(devices, sdi);
+				sdi->connection_id = g_strdup(l->data);
+			}
+			g_strfreev(res);
+		}
+		g_slist_free_full(resources, g_free);
+	}
+
+	if (!devices && resource) {
+		sdi = sr_modbus_scan_resource(resource, serialcomm, modbusaddr,
+		                              probe_device);
+		if (sdi)
+			devices = g_slist_append(NULL, sdi);
+	}
+
+	/* Tack a copy of the newly found devices onto the driver list. */
+	if (devices)
+		drvc->instances = g_slist_concat(drvc->instances, g_slist_copy(devices));
+
+	return devices;
+}
+
+/**
+ * Allocate and initialize a struct for a Modbus device instance.
+ *
+ * @param resource The resource description string.
+ * @param serialcomm Additionnal parameters for serial port resources.
+ *
+ * @return The allocated sr_modbus_dev_inst structure or NULL on failure.
+ */
+SR_PRIV struct sr_modbus_dev_inst *modbus_dev_inst_new(const char *resource,
+		const char *serialcomm, int modbusaddr)
+{
+	struct sr_modbus_dev_inst *modbus = NULL;
+	const struct sr_modbus_dev_inst *modbus_dev;
+	gchar **params;
+	unsigned int i;
+
+	for (i = 0; i < modbus_devs_size; i++) {
+		modbus_dev = modbus_devs[i];
+		if (!strncmp(resource, modbus_dev->prefix, strlen(modbus_dev->prefix))) {
+			sr_dbg("Opening %s device %s.", modbus_dev->name, resource);
+			modbus = g_malloc(sizeof(*modbus));
+			*modbus = *modbus_dev;
+			modbus->priv = g_malloc0(modbus->priv_size);
+			modbus->read_timeout_ms = 1000;
+			params = g_strsplit(resource, "/", 0);
+			if (modbus->dev_inst_new(modbus->priv, resource,
+			                         params, serialcomm, modbusaddr) != SR_OK) {
+				sr_modbus_free(modbus);
+				modbus = NULL;
+			}
+			g_strfreev(params);
+			break;
+		}
+	}
+
+	return modbus;
+}
+
+/**
+ * Open the specified Modbus device.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_open(struct sr_modbus_dev_inst *modbus)
+{
+	return modbus->open(modbus->priv);
+}
+
+/**
+ * Add an event source for a Modbus device.
+ *
+ * @param session The session to add the event source to.
+ * @param modbus Previously initialized Modbus device structure.
+ * @param events Events to check for.
+ * @param timeout Max time to wait before the callback is called, ignored if 0.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ *         SR_ERR_MALLOC upon memory allocation errors.
+ */
+SR_PRIV int sr_modbus_source_add(struct sr_session *session,
+		struct sr_modbus_dev_inst *modbus, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data)
+{
+	return modbus->source_add(session, modbus->priv, events, timeout, cb, cb_data);
+}
+
+/**
+ * Remove event source for a Modbus device.
+ *
+ * @param session The session to remove the event source from.
+ * @param modbus Previously initialized Modbus device structure.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
+ *         internal errors.
+ */
+SR_PRIV int sr_modbus_source_remove(struct sr_session *session,
+		struct sr_modbus_dev_inst *modbus)
+{
+	return modbus->source_remove(session, modbus->priv);
+}
+
+/**
+ * Send a Modbus command.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ * @param request Buffer containing the Modbus command to send.
+ * @param request_size The size of the request buffer.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ *         SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_request(struct sr_modbus_dev_inst *modbus,
+		uint8_t *request, int request_size)
+{
+	if (!request || request_size < 1)
+		return SR_ERR_ARG;
+
+	return modbus->send(modbus->priv, request, request_size);
+}
+
+/**
+ * Receive a Modbus reply.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ * @param reply Buffer to store the received Modbus reply.
+ * @param reply_size The size of the reply buffer.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ *         SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_reply(struct sr_modbus_dev_inst *modbus,
+		uint8_t *reply, int reply_size)
+{
+	int len, ret;
+	gint64 laststart;
+	unsigned int elapsed_ms;
+
+	if (!reply || reply_size < 2)
+		return SR_ERR_ARG;
+
+	laststart = g_get_monotonic_time();
+
+	ret = modbus->read_begin(modbus->priv, reply);
+	if (ret != SR_OK)
+		return ret;
+	if (*reply & 0x80)
+		reply_size = 2;
+
+	reply++;
+	reply_size--;
+
+	while (reply_size > 0) {
+		len = modbus->read_data(modbus->priv, reply, reply_size);
+		if (len < 0) {
+			sr_err("Incompletely read Modbus response.");
+			return SR_ERR;
+		} else if (len > 0) {
+			laststart = g_get_monotonic_time();
+		}
+		reply += len;
+		reply_size -= len;
+		elapsed_ms = (g_get_monotonic_time() - laststart) / 1000;
+		if (elapsed_ms >= modbus->read_timeout_ms) {
+			sr_err("Timed out waiting for Modbus response.");
+			return SR_ERR;
+		}
+	}
+
+	ret = modbus->read_end(modbus->priv);
+	if (ret != SR_OK)
+		return ret;
+
+	return SR_OK;
+}
+
+/**
+ * Send a Modbus command and receive the corresponding reply.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ * @param request Buffer containing the Modbus command to send.
+ * @param request_size The size of the request buffer.
+ * @param reply Buffer to store the received Modbus reply.
+ * @param reply_size The size of the reply buffer.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ *         SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_request_reply(struct sr_modbus_dev_inst *modbus,
+		uint8_t *request, int request_size, uint8_t *reply, int reply_size)
+{
+	int ret;
+	ret = sr_modbus_request(modbus, request, request_size);
+	if (ret != SR_OK)
+		return ret;
+	return sr_modbus_reply(modbus, reply, reply_size);
+}
+
+enum {
+	MODBUS_READ_COILS = 0x01,
+	MODBUS_READ_HOLDING_REGISTERS = 0x03,
+	MODBUS_WRITE_COIL = 0x05,
+	MODBUS_WRITE_MULTIPLE_REGISTERS = 0x10,
+};
+
+static int sr_modbus_error_check(const uint8_t *reply)
+{
+	const char *function = "UNKNOWN";
+	const char *error = NULL;
+	char buf[8];
+
+	if (!(reply[0] & 0x80))
+		return FALSE;
+
+	switch (reply[0] & ~0x80) {
+	case MODBUS_READ_COILS:
+		function = "MODBUS_READ_COILS";
+		break;
+	case MODBUS_READ_HOLDING_REGISTERS:
+		function = "READ_HOLDING_REGISTERS";
+		break;
+	case MODBUS_WRITE_COIL:
+		function = "WRITE_COIL";
+		break;
+	case MODBUS_WRITE_MULTIPLE_REGISTERS:
+		function = "WRITE_MULTIPLE_REGISTERS";
+		break;
+	}
+
+	switch (reply[1]) {
+	case 0x01:
+		error = "ILLEGAL FUNCTION";
+		break;
+	case 0x02:
+		error = "ILLEGAL DATA ADDRESS";
+		break;
+	case 0x03:
+		error = "ILLEGAL DATA VALUE";
+		break;
+	case 0x04:
+		error = "SLAVE DEVICE FAILURE";
+		break;
+	case 0x05:
+		error = "ACKNOWLEDGE";
+		break;
+	case 0x06:
+		error = "SLAVE DEVICE BUSY";
+		break;
+	case 0x08:
+		error = "MEMORY PARITY ERROR";
+		break;
+	case 0x0A:
+		error = "GATEWAY PATH UNAVAILABLE";
+		break;
+	case 0x0B:
+		error = "GATEWAY TARGET DEVICE FAILED TO RESPOND";
+		break;
+	}
+	if (!error) {
+		snprintf(buf, sizeof(buf), "0x%X", reply[1]);
+		error = buf;
+	}
+
+	sr_err("%s error executing %s function.", error, function);
+
+	return TRUE;
+}
+
+/**
+ * Send a Modbus read coils command and receive the corresponding coils values.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ * @param address The Modbus address of the first coil to read, or -1 to read
+ *                the reply of a previouly sent read coils command.
+ * @param nb_coils The number of coils to read.
+ * @param coils Buffer to store all the received coils values (1 bit per coil),
+ *              or NULL to send the read coil command without reading the reply.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments,
+ *         SR_ERR_DATA upon invalid data, or SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_read_coils(struct sr_modbus_dev_inst *modbus,
+		int address, int nb_coils, uint8_t *coils)
+{
+	uint8_t request[5], reply[2 + (nb_coils + 7) / 8];
+	int ret;
+
+	if (address < -1 || address > 0xFFFF || nb_coils < 1 || nb_coils > 2000)
+		return SR_ERR_ARG;
+
+	W8(request + 0, MODBUS_READ_COILS);
+	WB16(request + 1, address);
+	WB16(request + 3, nb_coils);
+
+	if (address >= 0) {
+		ret = sr_modbus_request(modbus, request, sizeof(request));
+		if (ret != SR_OK)
+			return ret;
+	}
+
+	if (coils) {
+		ret = sr_modbus_reply(modbus, reply, sizeof(reply));
+		if (ret != SR_OK)
+			return ret;
+		if (sr_modbus_error_check(reply))
+			return SR_ERR_DATA;
+		if (reply[0] != request[0] || R8(reply + 1) != (uint8_t)((nb_coils + 7) / 8))
+			return SR_ERR_DATA;
+		memcpy(coils, reply + 2, (nb_coils + 7) / 8);
+	}
+
+	return SR_OK;
+}
+
+/**
+ * Send a Modbus read holding registers command and receive the corresponding
+ * registers values.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ * @param address The Modbus address of the first register to read, or -1 to
+ *                read the reply of a previouly sent read registers command.
+ * @param nb_registers The number of registers to read.
+ * @param registers Buffer to store all the received registers values,
+ *                  or NULL to send the read holding registers command
+ *                  without reading the reply.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments,
+ *         SR_ERR_DATA upon invalid data, or SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_read_holding_registers(struct sr_modbus_dev_inst *modbus,
+		int address, int nb_registers, uint16_t *registers)
+{
+	uint8_t request[5], reply[2 + (2 * nb_registers)];
+	int ret;
+
+	if (address < -1 || address > 0xFFFF
+	    || nb_registers < 1 || nb_registers > 125)
+		return SR_ERR_ARG;
+
+	W8(request + 0, MODBUS_READ_HOLDING_REGISTERS);
+	WB16(request + 1, address);
+	WB16(request + 3, nb_registers);
+
+	if (address >= 0) {
+		ret = sr_modbus_request(modbus, request, sizeof(request));
+		if (ret != SR_OK)
+			return ret;
+	}
+
+	if (registers) {
+		ret = sr_modbus_reply(modbus, reply, sizeof(reply));
+		if (ret != SR_OK)
+			return ret;
+		if (sr_modbus_error_check(reply))
+			return SR_ERR_DATA;
+		if (reply[0] != request[0] || R8(reply + 1) != (uint8_t)(2 * nb_registers))
+			return SR_ERR_DATA;
+		memcpy(registers, reply + 2, 2 * nb_registers);
+	}
+
+	return SR_OK;
+}
+
+/**
+ * Send a Modbus write coil command.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ * @param address The Modbus address of the coil to write.
+ * @param value The new value to assign to this coil.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments,
+ *         SR_ERR_DATA upon invalid data, or SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_write_coil(struct sr_modbus_dev_inst *modbus,
+		int address, int value)
+{
+	uint8_t request[5], reply[5];
+	int ret;
+
+	if (address < 0 || address > 0xFFFF)
+		return SR_ERR_ARG;
+
+	W8(request + 0, MODBUS_WRITE_COIL);
+	WB16(request + 1, address);
+	WB16(request + 3, value ? 0xFF00 : 0);
+
+	ret = sr_modbus_request_reply(modbus, request, sizeof(request),
+				      reply, sizeof(reply));
+	if (ret != SR_OK)
+		return ret;
+	if (sr_modbus_error_check(reply))
+		return SR_ERR_DATA;
+	if (memcmp(request, reply, sizeof(reply)))
+		return SR_ERR_DATA;
+
+	return SR_OK;
+}
+
+/**
+ * Send a Modbus write multiple registers command.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ * @param address The Modbus address of the first register to write.
+ * @param nb_registers The number of registers to write.
+ * @param registers Buffer holding all the registers values to write.
+ *
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments,
+ *         SR_ERR_DATA upon invalid data, or SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_write_multiple_registers(struct sr_modbus_dev_inst*modbus,
+		int address, int nb_registers, uint16_t *registers)
+{
+	uint8_t request[6 + (2 * nb_registers)], reply[5];
+	int ret;
+
+	if (address < 0 || address > 0xFFFF
+	    || nb_registers < 1 || nb_registers > 123 || !registers)
+		return SR_ERR_ARG;
+
+	W8(request + 0, MODBUS_WRITE_MULTIPLE_REGISTERS);
+	WB16(request + 1, address);
+	WB16(request + 3, nb_registers);
+	W8(request + 5, 2 * nb_registers);
+	memcpy(request + 6, registers, 2 * nb_registers);
+
+	ret = sr_modbus_request_reply(modbus, request, sizeof(request),
+				      reply, sizeof(reply));
+	if (ret != SR_OK)
+		return ret;
+	if (sr_modbus_error_check(reply))
+		return SR_ERR_DATA;
+	if (memcmp(request, reply, sizeof(reply)))
+		return SR_ERR_DATA;
+
+	return SR_OK;
+}
+
+/**
+ * Close Modbus device.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV int sr_modbus_close(struct sr_modbus_dev_inst *modbus)
+{
+	return modbus->close(modbus->priv);
+}
+
+/**
+ * Free Modbus device.
+ *
+ * @param modbus Previously initialized Modbus device structure.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+SR_PRIV void sr_modbus_free(struct sr_modbus_dev_inst *modbus)
+{
+	modbus->free(modbus->priv);
+	g_free(modbus->priv);
+	g_free(modbus);
+}
diff --git a/src/modbus/modbus_serial_rtu.c b/src/modbus/modbus_serial_rtu.c
new file mode 100644
index 0000000..2cb589b
--- /dev/null
+++ b/src/modbus/modbus_serial_rtu.c
@@ -0,0 +1,209 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Aurelien Jacobs <aurel at gnuage.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "modbus_serial"
+
+#define BUFFER_SIZE 1024
+
+struct modbus_serial_rtu {
+	struct sr_serial_dev_inst *serial;
+	uint8_t slave_addr;
+	uint16_t crc;
+};
+
+static int modbus_serial_rtu_dev_inst_new(void *priv, const char *resource,
+		char **params, const char *serialcomm, int modbusaddr)
+{
+	struct modbus_serial_rtu *modbus = priv;
+
+	(void)params;
+
+	modbus->serial = sr_serial_dev_inst_new(resource, serialcomm);
+	modbus->slave_addr = modbusaddr;
+
+	return SR_OK;
+}
+
+static int modbus_serial_rtu_open(void *priv)
+{
+	struct modbus_serial_rtu *modbus = priv;
+	struct sr_serial_dev_inst *serial = modbus->serial;
+
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+		return SR_ERR;
+
+	if (serial_flush(serial) != SR_OK)
+		return SR_ERR;
+
+	return SR_OK;
+}
+
+static int modbus_serial_rtu_source_add(struct sr_session *session, void *priv,
+		int events, int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+	struct modbus_serial_rtu *modbus = priv;
+	struct sr_serial_dev_inst *serial = modbus->serial;
+
+	return serial_source_add(session, serial, events, timeout, cb, cb_data);
+}
+
+static int modbus_serial_rtu_source_remove(struct sr_session *session,
+		void *priv)
+{
+	struct modbus_serial_rtu *modbus = priv;
+	struct sr_serial_dev_inst *serial = modbus->serial;
+
+	return serial_source_remove(session, serial);
+}
+
+static uint16_t modbus_serial_rtu_crc(uint16_t crc,
+		const uint8_t *buffer, int len)
+{
+	int i;
+
+	if (!buffer || len < 0)
+		return crc;
+
+	while (len--) {
+		crc ^= *buffer++;
+		for (i = 0; i < 8; i++) {
+			int carry = crc & 1;
+			crc >>= 1;
+			if (carry)
+				crc ^= 0xA001;
+		}
+	}
+
+	return crc;
+}
+
+static int modbus_serial_rtu_send(void *priv,
+		const uint8_t *buffer, int buffer_size)
+{
+	int result;
+	struct modbus_serial_rtu *modbus = priv;
+	struct sr_serial_dev_inst *serial = modbus->serial;
+	uint8_t slave_addr = modbus->slave_addr;
+	uint16_t crc;
+
+	result = serial_write_nonblocking(serial, &slave_addr, sizeof(slave_addr));
+	if (result < 0)
+		return result;
+
+	result = serial_write_nonblocking(serial, buffer, buffer_size);
+	if (result < 0)
+		return result;
+
+	crc = modbus_serial_rtu_crc(0xFFFF, &slave_addr, sizeof(slave_addr));
+	crc = modbus_serial_rtu_crc(crc, buffer, buffer_size);
+
+	result = serial_write_nonblocking(serial, &crc, sizeof(crc));
+	if (result < 0)
+		return result;
+
+	return SR_OK;
+}
+
+static int modbus_serial_rtu_read_begin(void *priv, uint8_t *function_code)
+{
+	struct modbus_serial_rtu *modbus = priv;
+	uint8_t slave_addr;
+	int ret;
+
+	ret = serial_read_blocking(modbus->serial, &slave_addr, 1, 100);
+	if (ret != 1 || slave_addr != modbus->slave_addr)
+		return ret;
+
+	ret = serial_read_blocking(modbus->serial, function_code, 1, 100);
+	if (ret != 1)
+		return ret;
+
+	modbus->crc = modbus_serial_rtu_crc(0xFFFF, &slave_addr, sizeof(slave_addr));
+	modbus->crc = modbus_serial_rtu_crc(modbus->crc, function_code, 1);
+
+	return SR_OK;
+}
+
+static int modbus_serial_rtu_read_data(void *priv, uint8_t *buf, int maxlen)
+{
+	struct modbus_serial_rtu *modbus = priv;
+	int ret;
+
+	ret = serial_read_nonblocking(modbus->serial, buf, maxlen); 
+	if (ret < 0)
+		return ret;
+	modbus->crc = modbus_serial_rtu_crc(modbus->crc, buf, ret); 
+	return ret; 
+}
+
+static int modbus_serial_rtu_read_end(void *priv)
+{
+	struct modbus_serial_rtu *modbus = priv;
+	uint16_t crc;
+	int ret;
+
+	ret = serial_read_blocking(modbus->serial, &crc, sizeof(crc), 100);
+	if (ret != 2)
+		return ret;
+
+	if (crc != modbus->crc) {
+		sr_err("CRC error (0x%04X vs 0x%04X).", crc, modbus->crc);
+		return SR_ERR_DATA;
+	}
+
+	return SR_OK;
+}
+
+static int modbus_serial_rtu_close(void *priv)
+{
+	struct modbus_serial_rtu *modbus = priv;
+
+	return serial_close(modbus->serial);
+}
+
+static void modbus_serial_rtu_free(void *priv)
+{
+	struct modbus_serial_rtu *modbus = priv;
+
+	sr_serial_dev_inst_free(modbus->serial);
+}
+
+SR_PRIV const struct sr_modbus_dev_inst modbus_serial_rtu_dev = {
+	.name          = "serial_rtu",
+	.prefix        = "",
+	.priv_size     = sizeof(struct modbus_serial_rtu),
+	.scan          = NULL,
+	.dev_inst_new  = modbus_serial_rtu_dev_inst_new,
+	.open          = modbus_serial_rtu_open,
+	.source_add    = modbus_serial_rtu_source_add,
+	.source_remove = modbus_serial_rtu_source_remove,
+	.send          = modbus_serial_rtu_send,
+	.read_begin    = modbus_serial_rtu_read_begin,
+	.read_data     = modbus_serial_rtu_read_data,
+	.read_end      = modbus_serial_rtu_read_end,
+	.close         = modbus_serial_rtu_close,
+	.free          = modbus_serial_rtu_free,
+};
diff --git a/output/analog.c b/src/output/analog.c
similarity index 56%
rename from output/analog.c
rename to src/output/analog.c
index bdb5573..5e6e4ac 100644
--- a/output/analog.c
+++ b/src/output/analog.c
@@ -17,11 +17,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/analog"
@@ -29,24 +30,31 @@
 struct context {
 	int num_enabled_channels;
 	GPtrArray *channellist;
+	int digits;
+	float *fdata;
 };
 
-static int init(struct sr_output *o)
+enum {
+	DIGITS_ALL,
+	DIGITS_SPEC,
+};
+
+static int init(struct sr_output *o, GHashTable *options)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
 	GSList *l;
-
-	sr_spew("Initializing output module.");
+	const char *s;
 
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
 
-	if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
-		sr_err("Output module context malloc failed.");
-		return SR_ERR_MALLOC;
-	}
-	o->internal = ctx;
+	o->priv = ctx = g_malloc0(sizeof(struct context));
+	s = g_variant_get_string(g_hash_table_lookup(options, "digits"), NULL);
+	if (!strcmp(s, "all"))
+		ctx->digits = DIGITS_ALL;
+	else
+		ctx->digits = DIGITS_SPEC;
 
 	/* Get the number of channels and their names. */
 	ctx->channellist = g_ptr_array_new();
@@ -57,11 +65,12 @@ static int init(struct sr_output *o)
 		g_ptr_array_add(ctx->channellist, ch->name);
 		ctx->num_enabled_channels++;
 	}
+	ctx->fdata = NULL;
 
 	return SR_OK;
 }
 
-static void si_printf(float value, GString *out, char *unitstr)
+static void si_printf(float value, GString *out, const char *unitstr)
 {
 	float v;
 
@@ -89,6 +98,7 @@ static void si_printf(float value, GString *out, char *unitstr)
 
 }
 
+/* Please use the same order as in enum sr_unit (libsigrok.h). */
 static void fancyprint(int unit, int mqflags, float value, GString *out)
 {
 	switch (unit) {
@@ -142,6 +152,9 @@ static void fancyprint(int unit, int mqflags, float value, GString *out)
 	case SR_UNIT_DECIBEL_VOLT:
 		si_printf(value, out, "dBV");
 		break;
+	case SR_UNIT_UNITLESS:
+		si_printf(value, out, "");
+		break;
 	case SR_UNIT_DECIBEL_SPL:
 		if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A)
 			si_printf(value, out, "dB(A)");
@@ -164,7 +177,7 @@ static void fancyprint(int unit, int mqflags, float value, GString *out)
 			g_string_append(out, " %oA");
 		break;
 	case SR_UNIT_CONCENTRATION:
-		g_string_append_printf(out, "%f ppm", value * 1000000);
+		g_string_append_printf(out, "%f ppm", value * (1000 * 1000));
 		break;
 	case SR_UNIT_REVOLUTIONS_PER_MINUTE:
 		si_printf(value, out, "RPM");
@@ -178,11 +191,61 @@ static void fancyprint(int unit, int mqflags, float value, GString *out)
 	case SR_UNIT_WATT_HOUR:
 		si_printf(value, out, "Wh");
 		break;
+	case SR_UNIT_METER_SECOND:
+		si_printf(value, out, "m/s");
+		break;
+	case SR_UNIT_HECTOPASCAL:
+		si_printf(value, out, "hPa");
+		break;
+	case SR_UNIT_HUMIDITY_293K:
+		si_printf(value, out, "%rF");
+		break;
+	case SR_UNIT_DEGREE:
+		si_printf(value, out, "");
+		g_string_append_unichar(out, 0x00b0);
+		break;
+	case SR_UNIT_HENRY:
+		si_printf(value, out, "H");
+		break;
+	case SR_UNIT_GRAM:
+		si_printf(value, out, "g");
+		break;
+	case SR_UNIT_CARAT:
+		si_printf(value, out, "ct");
+		break;
+	case SR_UNIT_OUNCE:
+		si_printf(value, out, "oz");
+		break;
+	case SR_UNIT_TROY_OUNCE:
+		si_printf(value, out, "oz t");
+		break;
+	case SR_UNIT_POUND:
+		si_printf(value, out, "lb");
+		break;
+	case SR_UNIT_PENNYWEIGHT:
+		si_printf(value, out, "dwt");
+		break;
+	case SR_UNIT_GRAIN:
+		si_printf(value, out, "gr");
+		break;
+	case SR_UNIT_TAEL:
+		si_printf(value, out, "tael");
+		break;
+	case SR_UNIT_MOMME:
+		si_printf(value, out, "momme");
+		break;
+	case SR_UNIT_TOLA:
+		si_printf(value, out, "tola");
+		break;
+	case SR_UNIT_PIECE:
+		si_printf(value, out, "pcs");
+		break;
 	default:
 		si_printf(value, out, "");
 		break;
 	}
 
+	/* Please use the same order as in enum sr_mqflag (libsigrok.h). */
 	if (mqflags & SR_MQFLAG_AC)
 		g_string_append_printf(out, " AC");
 	if (mqflags & SR_MQFLAG_DC)
@@ -201,23 +264,35 @@ static void fancyprint(int unit, int mqflags, float value, GString *out)
 		g_string_append_printf(out, " AUTO");
 	if (mqflags & SR_MQFLAG_RELATIVE)
 		g_string_append_printf(out, " REL");
+	/* Note: SR_MQFLAG_SPL_* is handled above. */
+	if (mqflags & SR_MQFLAG_DURATION)
+		g_string_append_printf(out, " DURATION");
 	if (mqflags & SR_MQFLAG_AVG)
 		g_string_append_printf(out, " AVG");
+	if (mqflags & SR_MQFLAG_REFERENCE)
+		g_string_append_printf(out, " REF");
+	if (mqflags & SR_MQFLAG_UNSTABLE)
+		g_string_append_printf(out, " UNSTABLE");
 	g_string_append_c(out, '\n');
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
+	struct context *ctx;
+	const struct sr_datafeed_analog_old *analog_old;
 	const struct sr_datafeed_analog *analog;
 	struct sr_channel *ch;
 	GSList *l;
-	const float *fdata;
-	int i, p;
+	float *fdata;
+	unsigned int i;
+	int num_channels, c, ret, si, digits;
+	char *number, *suffix;
 
 	*out = NULL;
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
+	ctx = o->priv;
 
 	switch (packet->type) {
 	case SR_DF_FRAME_BEGIN:
@@ -226,42 +301,103 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	case SR_DF_FRAME_END:
 		*out = g_string_new("FRAME-END\n");
 		break;
+	case SR_DF_ANALOG_OLD:
+		analog_old = packet->payload;
+		fdata = (float *)analog_old->data;
+		*out = g_string_sized_new(512);
+		num_channels = g_slist_length(analog_old->channels);
+		for (si = 0; si < analog_old->num_samples; si++) {
+			for (l = analog_old->channels, c = 0; l; l = l->next, c++) {
+				ch = l->data;
+				g_string_append_printf(*out, "%s: ", ch->name);
+				fancyprint(analog_old->unit, analog_old->mqflags,
+						fdata[si * num_channels + c], *out);
+			}
+		}
+		break;
 	case SR_DF_ANALOG:
 		analog = packet->payload;
-		fdata = (const float *)analog->data;
+		num_channels = g_slist_length(analog->meaning->channels);
+		if (!(fdata = g_try_realloc(ctx->fdata,
+						analog->num_samples * num_channels * sizeof(float))))
+			return SR_ERR_MALLOC;
+		ctx->fdata = fdata;
+		if ((ret = sr_analog_to_float(analog, fdata)) != SR_OK)
+			return ret;
 		*out = g_string_sized_new(512);
+		if (analog->encoding->is_digits_decimal) {
+			if (ctx->digits == DIGITS_ALL)
+				digits = analog->encoding->digits;
+			else
+				digits = analog->spec->spec_digits;
+		} else {
+			/* TODO we don't know how to print by number of bits yet. */
+			digits = 6;
+		}
+		sr_analog_unit_to_string(analog, &suffix);
 		for (i = 0; i < analog->num_samples; i++) {
-			for (l = analog->channels, p = 0; l; l = l->next, p++) {
+			for (l = analog->meaning->channels, c = 0; l; l = l->next, c++) {
 				ch = l->data;
 				g_string_append_printf(*out, "%s: ", ch->name);
-				fancyprint(analog->unit, analog->mqflags,
-						fdata[i + p], *out);
+				number = g_strdup_printf("%.*f", digits,
+						fdata[i * num_channels + c]);
+				g_string_append(*out, number);
+				g_free(number);
+				g_string_append(*out, " ");
+				g_string_append(*out, suffix);
+				g_string_append(*out, "\n");
 			}
 		}
+		g_free(suffix);
 		break;
 	}
 
 	return SR_OK;
 }
 
+static struct sr_option options[] = {
+	{ "digits", "Digits", "Digits to show", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_ref_sink(g_variant_new_string("all"));
+		options[0].values = g_slist_append(options[0].values,
+				g_variant_ref_sink(g_variant_new_string("all")));
+		options[0].values = g_slist_append(options[0].values,
+				g_variant_ref_sink(g_variant_new_string("spec")));
+	}
+
+	return options;
+}
+
 static int cleanup(struct sr_output *o)
 {
 	struct context *ctx;
 
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
-	ctx = o->internal;
+	ctx = o->priv;
 
 	g_ptr_array_free(ctx->channellist, 1);
+	g_variant_unref(options[0].def);
+	g_slist_free_full(options[0].values, (GDestroyNotify)g_variant_unref);
+	g_free(ctx->fdata);
 	g_free(ctx);
-	o->internal = NULL;
+	o->priv = NULL;
 
 	return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_analog = {
+SR_PRIV struct sr_output_module output_analog = {
 	.id = "analog",
-	.description = "Analog data",
+	.name = "Analog",
+	.desc = "Analog data and types",
+	.exts = NULL,
+	.flags = 0,
+	.options = get_options,
 	.init = init,
 	.receive = receive,
 	.cleanup = cleanup
diff --git a/output/ascii.c b/src/output/ascii.c
similarity index 84%
rename from output/ascii.c
rename to src/output/ascii.c
index d068942..cc67bd9 100644
--- a/output/ascii.c
+++ b/src/output/ascii.c
@@ -18,10 +18,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/hex"
@@ -30,7 +31,7 @@
 
 struct context {
 	unsigned int num_enabled_channels;
-	int samples_per_line;
+	int spl;
 	int bit_cnt;
 	int spl_cnt;
 	int trigger;
@@ -44,37 +45,20 @@ struct context {
 	GString *header;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
 	GSList *l;
-	GHashTableIter iter;
-	gpointer key, value;
 	unsigned int i, j;
-	int spl;
 
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
 
-	spl = DEFAULT_SAMPLES_PER_LINE;
-	g_hash_table_iter_init(&iter, o->params);
-	while (g_hash_table_iter_next(&iter, &key, &value)) {
-		if (!strcmp(key, "width")) {
-			if ((spl = strtoul(value, NULL, 10)) < 1) {
-				sr_err("Invalid width.");
-				return SR_ERR_ARG;
-			}
-		} else {
-			sr_err("Unknown parameter '%s'.", key);
-			return SR_ERR_ARG;
-		}
-	}
-
 	ctx = g_malloc0(sizeof(struct context));
-	o->internal = ctx;
+	o->priv = ctx;
 	ctx->trigger = -1;
-	ctx->samples_per_line = spl;
+	ctx->spl = g_variant_get_uint32(g_hash_table_lookup(options, "width"));
 
 	for (l = o->sdi->channels; l; l = l->next) {
 		ch = l->data;
@@ -106,7 +90,7 @@ static int init(struct sr_output *o)
 	return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
 	struct context *ctx;
 	GVariant *gvar;
@@ -114,7 +98,7 @@ static GString *gen_header(struct sr_output *o)
 	int num_channels;
 	char *samplerate_s;
 
-	ctx = o->internal;
+	ctx = o->priv;
 	if (ctx->samplerate == 0) {
 		if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
 				&gvar) == SR_OK) {
@@ -124,7 +108,7 @@ static GString *gen_header(struct sr_output *o)
 	}
 
 	header = g_string_sized_new(512);
-	g_string_printf(header, "%s\n", PACKAGE_STRING);
+	g_string_printf(header, "%s %s\n", PACKAGE_NAME, SR_PACKAGE_VERSION_STRING);
 	num_channels = g_slist_length(o->sdi->channels);
 	g_string_append_printf(header, "Acquisition with %d/%d channels",
 			ctx->num_enabled_channels, num_channels);
@@ -138,7 +122,7 @@ static GString *gen_header(struct sr_output *o)
 	return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
 	const struct sr_datafeed_meta *meta;
@@ -153,7 +137,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	*out = NULL;
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
-	if (!(ctx = o->internal))
+	if (!(ctx = o->priv))
 		return SR_ERR_ARG;
 
 	switch (packet->type) {
@@ -194,7 +178,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 				}
 				g_string_append_c(ctx->lines[j], c);
 
-				if (ctx->spl_cnt == ctx->samples_per_line) {
+				if (ctx->spl_cnt == ctx->spl) {
 					/* Flush line buffers. */
 					g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
 					g_string_append_c(*out, '\n');
@@ -206,7 +190,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 					g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
 				}
 			}
-			if (ctx->spl_cnt == ctx->samples_per_line)
+			if (ctx->spl_cnt == ctx->spl)
 				/* Line buffers were already flushed. */
 				ctx->spl_cnt = 0;
 			memcpy(ctx->prev_sample, logic->data + i, logic->unitsize);
@@ -235,7 +219,7 @@ static int cleanup(struct sr_output *o)
 	if (!o)
 		return SR_ERR_ARG;
 
-	if (!(ctx = o->internal))
+	if (!(ctx = o->priv))
 		return SR_OK;
 
 	g_free(ctx->channel_index);
@@ -245,17 +229,34 @@ static int cleanup(struct sr_output *o)
 		g_string_free(ctx->lines[i], TRUE);
 	g_free(ctx->lines);
 	g_free(ctx);
-	o->internal = NULL;
+	o->priv = NULL;
 
 	return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_ascii = {
+static struct sr_option options[] = {
+	{ "width", "Width", "Number of samples per line", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_new_uint32(DEFAULT_SAMPLES_PER_LINE);
+		g_variant_ref_sink(options[0].def);
+	}
+
+	return options;
+}
+
+SR_PRIV struct sr_output_module output_ascii = {
 	.id = "ascii",
-	.description = "ASCII",
+	.name = "ASCII",
+	.desc = "ASCII art",
+	.exts = (const char*[]){"txt", NULL},
+	.flags = 0,
+	.options = get_options,
 	.init = init,
 	.receive = receive,
 	.cleanup = cleanup,
 };
-
-
diff --git a/output/binary.c b/src/output/binary.c
similarity index 81%
rename from output/binary.c
rename to src/output/binary.c
index 6e2531d..943b6eb 100644
--- a/output/binary.c
+++ b/src/output/binary.c
@@ -18,15 +18,16 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/binary"
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
 	const struct sr_datafeed_logic *logic;
@@ -42,8 +43,12 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_binary = {
+SR_PRIV struct sr_output_module output_binary = {
 	.id = "binary",
-	.description = "Raw binary",
+	.name = "Binary",
+	.desc = "Raw binary",
+	.exts = NULL,
+	.flags = 0,
+	.options = NULL,
 	.receive = receive,
 };
diff --git a/output/bits.c b/src/output/bits.c
similarity index 83%
rename from output/bits.c
rename to src/output/bits.c
index b44f105..71cbd95 100644
--- a/output/bits.c
+++ b/src/output/bits.c
@@ -17,10 +17,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/bits"
@@ -29,7 +30,7 @@
 
 struct context {
 	unsigned int num_enabled_channels;
-	int samples_per_line;
+	int spl;
 	int spl_cnt;
 	int trigger;
 	uint64_t samplerate;
@@ -39,37 +40,20 @@ struct context {
 	GString **lines;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
 	GSList *l;
-	GHashTableIter iter;
-	gpointer key, value;
 	unsigned int i, j;
-	int spl;
 
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
 
-	spl = DEFAULT_SAMPLES_PER_LINE;
-	g_hash_table_iter_init(&iter, o->params);
-	while (g_hash_table_iter_next(&iter, &key, &value)) {
-		if (!strcmp(key, "width")) {
-			if ((spl = strtoul(value, NULL, 10)) < 1) {
-				sr_err("Invalid width.");
-				return SR_ERR_ARG;
-			}
-		} else {
-			sr_err("Unknown parameter '%s'.", key);
-			return SR_ERR_ARG;
-		}
-	}
-
 	ctx = g_malloc0(sizeof(struct context));
-	o->internal = ctx;
+	o->priv = ctx;
 	ctx->trigger = -1;
-	ctx->samples_per_line = spl;
+	ctx->spl = g_variant_get_uint32(g_hash_table_lookup(options, "width"));
 
 	for (l = o->sdi->channels; l; l = l->next) {
 		ch = l->data;
@@ -100,7 +84,7 @@ static int init(struct sr_output *o)
 	return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
 	struct context *ctx;
 	GVariant *gvar;
@@ -108,7 +92,7 @@ static GString *gen_header(struct sr_output *o)
 	int num_channels;
 	char *samplerate_s;
 
-	ctx = o->internal;
+	ctx = o->priv;
 	if (ctx->samplerate == 0) {
 		if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
 				&gvar) == SR_OK) {
@@ -118,7 +102,7 @@ static GString *gen_header(struct sr_output *o)
 	}
 
 	header = g_string_sized_new(512);
-	g_string_printf(header, "%s\n", PACKAGE_STRING);
+	g_string_printf(header, "%s %s\n", PACKAGE_NAME, SR_PACKAGE_VERSION_STRING);
 	num_channels = g_slist_length(o->sdi->channels);
 	g_string_append_printf(header, "Acquisition with %d/%d channels",
 			ctx->num_enabled_channels, num_channels);
@@ -132,7 +116,7 @@ static GString *gen_header(struct sr_output *o)
 	return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
 	const struct sr_datafeed_meta *meta;
@@ -147,7 +131,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	*out = NULL;
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
-	if (!(ctx = o->internal))
+	if (!(ctx = o->priv))
 		return SR_ERR_ARG;
 
 	switch (packet->type) {
@@ -179,7 +163,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 				c = (*p & (1 << (idx % 8))) ? '1' : '0';
 				g_string_append_c(ctx->lines[j], c);
 
-				if (ctx->spl_cnt == ctx->samples_per_line) {
+				if (ctx->spl_cnt == ctx->spl) {
 					/* Flush line buffers. */
 					g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
 					g_string_append_c(*out, '\n');
@@ -194,7 +178,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 					g_string_append_c(ctx->lines[j], ' ');
 				}
 			}
-			if (ctx->spl_cnt == ctx->samples_per_line)
+			if (ctx->spl_cnt == ctx->spl)
 				/* Line buffers were already flushed. */
 				ctx->spl_cnt = 0;
 		}
@@ -222,7 +206,7 @@ static int cleanup(struct sr_output *o)
 	if (!o)
 		return SR_ERR_ARG;
 
-	if (!(ctx = o->internal))
+	if (!(ctx = o->priv))
 		return SR_OK;
 
 	g_free(ctx->channel_index);
@@ -231,14 +215,33 @@ static int cleanup(struct sr_output *o)
 		g_string_free(ctx->lines[i], TRUE);
 	g_free(ctx->lines);
 	g_free(ctx);
-	o->internal = NULL;
+	o->priv = NULL;
 
 	return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_bits = {
+static struct sr_option options[] = {
+	{ "width", "Width", "Number of samples per line", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_new_uint32(DEFAULT_SAMPLES_PER_LINE);
+		g_variant_ref_sink(options[0].def);
+	}
+
+	return options;
+}
+
+SR_PRIV struct sr_output_module output_bits = {
 	.id = "bits",
-	.description = "Bits",
+	.name = "Bits",
+	.desc = "0/1 digits",
+	.exts = (const char*[]){"txt", NULL},
+	.flags = 0,
+	.options = get_options,
 	.init = init,
 	.receive = receive,
 	.cleanup = cleanup,
diff --git a/output/chronovu_la8.c b/src/output/chronovu_la8.c
similarity index 89%
rename from output/chronovu_la8.c
rename to src/output/chronovu_la8.c
index ad0b5d3..2793f97 100644
--- a/output/chronovu_la8.c
+++ b/src/output/chronovu_la8.c
@@ -18,10 +18,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/chronovu-la8"
@@ -75,17 +76,19 @@ static uint8_t samplerate_to_divcount(uint64_t samplerate)
 	return (SR_MHZ(100) / samplerate) - 1;
 }
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
 	GSList *l;
 
+	(void)options;
+
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
 
 	ctx = g_malloc0(sizeof(struct context));
-	o->internal = ctx;
+	o->priv = ctx;
 
 	for (l = o->sdi->channels; l; l = l->next) {
 		ch = l->data;
@@ -101,7 +104,7 @@ static int init(struct sr_output *o)
 	return SR_OK;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
 	const struct sr_datafeed_logic *logic;
@@ -113,7 +116,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	*out = NULL;
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
-	if (!(ctx = o->internal))
+	if (!(ctx = o->priv))
 		return SR_ERR_ARG;
 
 	switch (packet->type) {
@@ -170,20 +173,24 @@ static int cleanup(struct sr_output *o)
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
 
-	if (o->internal) {
-		ctx = o->internal;
+	if (o->priv) {
+		ctx = o->priv;
 		g_string_free(ctx->pretrig_buf, TRUE);
 		g_free(ctx->channel_index);
-		g_free(o->internal);
-		o->internal = NULL;
+		g_free(o->priv);
+		o->priv = NULL;
 	}
 
 	return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_chronovu_la8 = {
+SR_PRIV struct sr_output_module output_chronovu_la8 = {
 	.id = "chronovu-la8",
-	.description = "ChronoVu LA8",
+	.name = "ChronoVu LA8",
+	.desc = "ChronoVu LA8 native file format",
+	.exts = (const char*[]){"kdt", NULL},
+	.flags = 0,
+	.options = NULL,
 	.init = init,
 	.receive = receive,
 	.cleanup = cleanup,
diff --git a/src/output/csv.c b/src/output/csv.c
new file mode 100644
index 0000000..db023a4
--- /dev/null
+++ b/src/output/csv.c
@@ -0,0 +1,358 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011 Uwe Hermann <uwe at 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 <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/csv"
+
+struct context {
+	unsigned int num_enabled_channels;
+	uint64_t samplerate;
+	char separator;
+	gboolean header_done;
+	struct sr_channel **channels;
+
+	/* For analog measurements split into frames, not packets. */
+	struct sr_channel **analog_channels;
+	float *analog_vals; /* Analog values stored until the end of the frame. */
+	unsigned int num_analog_channels;
+	gboolean inframe;
+};
+
+/*
+ * TODO:
+ *  - Option to specify delimiter character and/or string.
+ *  - Option to (not) print metadata as comments.
+ *  - Option to specify the comment character(s), e.g. # or ; or C/C++-style.
+ *  - Option to (not) print samplenumber / time as extra column.
+ *  - Option to "compress" output (only print changed samples, VCD-like).
+ *  - Option to print comma-separated bits, or whole bytes/words (for 8/16
+ *    channel LAs) as ASCII/hex etc. etc.
+ *  - Trigger support.
+ */
+
+static int init(struct sr_output *o, GHashTable *options)
+{
+	struct context *ctx;
+	struct sr_channel *ch;
+	GSList *l;
+	int i, j;
+
+	(void)options;
+
+	if (!o || !o->sdi)
+		return SR_ERR_ARG;
+
+	ctx = g_malloc0(sizeof(struct context));
+	o->priv = ctx;
+	ctx->separator = ',';
+
+	/* Get the number of channels, and the unitsize. */
+	for (l = o->sdi->channels; l; l = l->next) {
+		ch = l->data;
+		if (ch->enabled) {
+			if (ch->type == SR_CHANNEL_LOGIC ||
+			    ch->type == SR_CHANNEL_ANALOG)
+				ctx->num_enabled_channels++;
+			if (ch->type == SR_CHANNEL_ANALOG)
+				ctx->num_analog_channels++;
+		}
+	}
+	ctx->channels = g_malloc(sizeof(struct sr_channel *)
+					* ctx->num_enabled_channels);
+	ctx->analog_channels = g_malloc(sizeof(struct sr_channel *)
+					* ctx->num_analog_channels);
+	ctx->analog_vals = g_malloc(sizeof(float) * ctx->num_analog_channels);
+
+	/* Once more to map the enabled channels. */
+	for (i = 0, l = o->sdi->channels, j = 0; l; l = l->next) {
+		ch = l->data;
+		if (ch->enabled) {
+			if (ch->type == SR_CHANNEL_LOGIC ||
+			    ch->type == SR_CHANNEL_ANALOG)
+				ctx->channels[i++] = ch;
+			if (ch->type == SR_CHANNEL_ANALOG)
+				ctx->analog_channels[j++] = ch;
+		}
+
+	}
+
+	return SR_OK;
+}
+
+static GString *gen_header(const struct sr_output *o)
+{
+	struct context *ctx;
+	struct sr_channel *ch;
+	GVariant *gvar;
+	GString *header;
+	GSList *l;
+	time_t t;
+	int num_channels, i;
+	char *samplerate_s;
+
+	ctx = o->priv;
+	header = g_string_sized_new(512);
+
+	/* Some metadata */
+	t = time(NULL);
+	g_string_append_printf(header, "; CSV, generated by %s %s on %s",
+			PACKAGE_NAME, SR_PACKAGE_VERSION_STRING, ctime(&t));
+
+	/* Columns / channels */
+	num_channels = g_slist_length(o->sdi->channels);
+	g_string_append_printf(header, "; Channels (%d/%d):",
+			ctx->num_enabled_channels, num_channels);
+	for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
+		ch = l->data;
+		if (ch->enabled &&
+		    (ch->type == SR_CHANNEL_LOGIC ||
+		     ch->type == SR_CHANNEL_ANALOG))
+			g_string_append_printf(header, " %s,", ch->name);
+	}
+	if (o->sdi->channels)
+		/* Drop last separator. */
+		g_string_truncate(header, header->len - 1);
+	g_string_append_printf(header, "\n");
+
+	if (ctx->samplerate == 0) {
+		if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+				&gvar) == SR_OK) {
+			ctx->samplerate = g_variant_get_uint64(gvar);
+			g_variant_unref(gvar);
+		}
+	}
+	if (ctx->samplerate != 0) {
+		samplerate_s = sr_samplerate_string(ctx->samplerate);
+		g_string_append_printf(header, "; Samplerate: %s\n", samplerate_s);
+		g_free(samplerate_s);
+	}
+
+	return header;
+}
+
+static void init_output(GString **out, struct context *ctx,
+			const struct sr_output *o)
+{
+	if (!ctx->header_done) {
+		*out = gen_header(o);
+		ctx->header_done = TRUE;
+	} else {
+		*out = g_string_sized_new(512);
+	}
+}
+
+static void handle_analog_frame(struct context *ctx, GSList *channels,
+		unsigned int num_samples, float *data)
+{
+	unsigned int numch, nums, i, j, s;
+	GSList *l;
+
+	numch = g_slist_length(channels);
+	if (num_samples > numch)
+		nums = num_samples / numch;
+	else
+		nums = 1;
+
+	s = 0;
+	l = channels;
+	for (i = 0; i < nums; i++) {
+		for (j = 0; j < ctx->num_analog_channels; j++) {
+			if (ctx->analog_channels[j] == l->data)
+				ctx->analog_vals[j] = data[s++];
+		}
+		l = l->next;
+	}
+}
+
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
+		GString **out)
+{
+	const struct sr_datafeed_meta *meta;
+	const struct sr_datafeed_logic *logic;
+	const struct sr_datafeed_analog_old *analog_old;
+	const struct sr_datafeed_analog *analog;
+	const struct sr_config *src;
+	unsigned int num_samples;
+	float *data;
+	GSList *l, *channels;
+	struct context *ctx;
+	int idx;
+	uint64_t i, j, k, nums, numch;
+	gchar *p, c;
+	int ret = SR_OK;
+
+	*out = NULL;
+	if (!o || !o->sdi)
+		return SR_ERR_ARG;
+	if (!(ctx = o->priv))
+		return SR_ERR_ARG;
+
+	switch (packet->type) {
+	case SR_DF_META:
+		meta = packet->payload;
+		for (l = meta->config; l; l = l->next) {
+			src = l->data;
+			if (src->key != SR_CONF_SAMPLERATE)
+				continue;
+			ctx->samplerate = g_variant_get_uint64(src->data);
+		}
+		break;
+	case SR_DF_FRAME_BEGIN:
+		/*
+		 * Special case - we start gathering data from analog channels
+		 * and wait for SR_DF_FRAME_END to dump it.
+		 */
+		memset(ctx->analog_vals, 0, sizeof(float) * ctx->num_analog_channels);
+		ctx->inframe = TRUE;
+		ret = SR_OK;
+		break;
+	case SR_DF_FRAME_END:
+		/*
+		 * Dump gathered data.
+		 */
+		init_output(out, ctx, o);
+
+		for (i = 0, j = 0; i < ctx->num_enabled_channels; i++) {
+			if (ctx->channels[i]->type == SR_CHANNEL_ANALOG) {
+				g_string_append_printf(*out, "%f",
+							ctx->analog_vals[j++]);
+			}
+			g_string_append_c(*out, ctx->separator);
+		}
+		g_string_truncate(*out, (*out)->len - 1);
+		g_string_append_printf(*out, "\n");
+
+		ctx->inframe = FALSE;
+		break;
+	case SR_DF_LOGIC:
+		logic = packet->payload;
+		init_output(out, ctx, o);
+
+		for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+			for (j = 0; j < ctx->num_enabled_channels; j++) {
+				if (ctx->channels[j]->type == SR_CHANNEL_LOGIC) {
+					idx = ctx->channels[j]->index;
+					p = logic->data + i + idx / 8;
+					c = *p & (1 << (idx % 8));
+					g_string_append_c(*out, c ? '1' : '0');
+				}
+				g_string_append_c(*out, ctx->separator);
+			}
+			if (j) {
+				/* Drop last separator. */
+				g_string_truncate(*out, (*out)->len - 1);
+			}
+			g_string_append_printf(*out, "\n");
+		}
+		break;
+	case SR_DF_ANALOG_OLD:
+	case SR_DF_ANALOG:
+		analog_old = packet->payload;
+		analog = packet->payload;
+
+		if (packet->type == SR_DF_ANALOG_OLD) {
+			channels = analog_old->channels;
+			numch = g_slist_length(channels);
+			num_samples = analog_old->num_samples;
+			data = analog_old->data;
+		} else {
+			channels = analog->meaning->channels;
+			numch = g_slist_length(channels);
+			num_samples = analog->num_samples;
+			data = g_malloc(sizeof(float) * num_samples * numch);
+			ret = sr_analog_to_float(analog, data);
+			if (ret != SR_OK)
+				return ret;
+		}
+
+		if (ctx->inframe) {
+			handle_analog_frame(ctx, channels, num_samples, data);
+			break;
+		}
+
+		init_output(out, ctx, o);
+		k = 0;
+		l = NULL;
+
+		if (num_samples > numch)
+			nums = num_samples / numch;
+		else
+			nums = 1;
+
+		for (i = 0; i < nums; i++) {
+			for (j = 0; j < ctx->num_enabled_channels; j++) {
+				if (ctx->channels[j]->type == SR_CHANNEL_ANALOG) {
+					if (!l)
+						l = channels;
+
+					if (ctx->channels[j] == l->data) {
+						g_string_append_printf(*out,
+							"%f", data[k++]);
+					}
+
+					l = l->next;
+				}
+				g_string_append_c(*out, ctx->separator);
+			}
+			g_string_truncate(*out, (*out)->len - 1);
+			g_string_append_printf(*out, "\n");
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int cleanup(struct sr_output *o)
+{
+	struct context *ctx;
+
+	if (!o || !o->sdi)
+		return SR_ERR_ARG;
+
+	if (o->priv) {
+		ctx = o->priv;
+		g_free(ctx->channels);
+		g_free(ctx->analog_channels);
+		g_free(ctx->analog_vals);
+		g_free(o->priv);
+		o->priv = NULL;
+	}
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_output_module output_csv = {
+	.id = "csv",
+	.name = "CSV",
+	.desc = "Comma-separated values",
+	.exts = (const char*[]){"csv", NULL},
+	.flags = 0,
+	.options = NULL,
+	.init = init,
+	.receive = receive,
+	.cleanup = cleanup,
+};
diff --git a/output/gnuplot.c b/src/output/gnuplot.c
similarity index 88%
rename from output/gnuplot.c
rename to src/output/gnuplot.c
index a9feec6..9a2a78c 100644
--- a/output/gnuplot.c
+++ b/src/output/gnuplot.c
@@ -18,11 +18,11 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
-#include "config.h" /* Needed for PACKAGE_STRING and others. */
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/gnuplot"
@@ -43,19 +43,20 @@ static const char *gnuplot_header2 = "\
 # -----------------------------------------------------------------------------\n\
 # 0\t\tSample counter (for internal gnuplot purposes)\n";
 
-
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
 	GSList *l;
 	unsigned int i;
 
+	(void)options;
+
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
 
 	ctx = g_malloc0(sizeof(struct context));
-	o->internal = ctx;
+	o->priv = ctx;
 	ctx->num_enabled_channels = 0;
 	for (l = o->sdi->channels; l; l = l->next) {
 		ch = l->data;
@@ -84,7 +85,7 @@ static int init(struct sr_output *o)
 	return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
@@ -94,7 +95,7 @@ static GString *gen_header(struct sr_output *o)
 	unsigned int num_channels, i;
 	char *samplerate_s;
 
-	ctx = o->internal;
+	ctx = o->priv;
 	if (ctx->samplerate == 0) {
 		if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
 				&gvar) == SR_OK) {
@@ -106,8 +107,8 @@ static GString *gen_header(struct sr_output *o)
 	t = time(NULL);
 	header = g_string_sized_new(512);
 	g_string_printf(header, "%s", gnuplot_header);
-	g_string_append_printf(header, "# Generated by %s on %s",
-			PACKAGE_STRING, ctime(&t));
+	g_string_append_printf(header, "# Generated by %s %s on %s",
+			PACKAGE_NAME, SR_PACKAGE_VERSION_STRING, ctime(&t));
 
 	num_channels = g_slist_length(o->sdi->channels);
 	g_string_append_printf(header, "# Acquisition with %d/%d channels",
@@ -130,7 +131,7 @@ static GString *gen_header(struct sr_output *o)
 	return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
 	const struct sr_datafeed_meta *meta;
@@ -142,9 +143,9 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	unsigned int curbit, p, idx, i;
 
 	*out = NULL;
-	if (!o || !o->internal)
+	if (!o || !o->priv)
 		return SR_ERR_BUG;
-	ctx = o->internal;
+	ctx = o->priv;
 
 	if (packet->type == SR_DF_META) {
 		meta = packet->payload;
@@ -205,9 +206,9 @@ static int cleanup(struct sr_output *o)
 {
 	struct context *ctx;
 
-	if (!o || !o->internal)
+	if (!o || !o->priv)
 		return SR_ERR_BUG;
-	ctx = o->internal;
+	ctx = o->priv;
 	g_free(ctx->channel_index);
 	g_free(ctx->prevsample);
 	g_free(ctx);
@@ -215,9 +216,13 @@ static int cleanup(struct sr_output *o)
 	return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_gnuplot = {
+SR_PRIV struct sr_output_module output_gnuplot = {
 	.id = "gnuplot",
-	.description = "Gnuplot",
+	.name = "Gnuplot",
+	.desc = "Gnuplot data file format",
+	.exts = (const char*[]){"dat", NULL},
+	.flags = 0,
+	.options = NULL,
 	.init = init,
 	.receive = receive,
 	.cleanup = cleanup,
diff --git a/output/hex.c b/src/output/hex.c
similarity index 84%
rename from output/hex.c
rename to src/output/hex.c
index 07a430c..5d9e0f5 100644
--- a/output/hex.c
+++ b/src/output/hex.c
@@ -17,10 +17,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/hex"
@@ -29,7 +30,7 @@
 
 struct context {
 	unsigned int num_enabled_channels;
-	int samples_per_line;
+	int spl;
 	int bit_cnt;
 	int spl_cnt;
 	int trigger;
@@ -42,37 +43,20 @@ struct context {
 	GString **lines;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
 	GSList *l;
-	GHashTableIter iter;
-	gpointer key, value;
 	unsigned int i, j;
-	int spl;
 
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
 
-	spl = DEFAULT_SAMPLES_PER_LINE;
-	g_hash_table_iter_init(&iter, o->params);
-	while (g_hash_table_iter_next(&iter, &key, &value)) {
-		if (!strcmp(key, "width")) {
-			if ((spl = strtoul(value, NULL, 10)) < 1) {
-				sr_err("Invalid width.");
-				return SR_ERR_ARG;
-			}
-		} else {
-			sr_err("Unknown parameter '%s'.", key);
-			return SR_ERR_ARG;
-		}
-	}
-
 	ctx = g_malloc0(sizeof(struct context));
-	o->internal = ctx;
+	o->priv = ctx;
 	ctx->trigger = -1;
-	ctx->samples_per_line = spl;
+	ctx->spl = g_variant_get_uint32(g_hash_table_lookup(options, "width"));
 
 	for (l = o->sdi->channels; l; l = l->next) {
 		ch = l->data;
@@ -105,7 +89,7 @@ static int init(struct sr_output *o)
 	return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
 	struct context *ctx;
 	GVariant *gvar;
@@ -113,7 +97,7 @@ static GString *gen_header(struct sr_output *o)
 	int num_channels;
 	char *samplerate_s;
 
-	ctx = o->internal;
+	ctx = o->priv;
 	if (ctx->samplerate == 0) {
 		if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
 				&gvar) == SR_OK) {
@@ -123,7 +107,7 @@ static GString *gen_header(struct sr_output *o)
 	}
 
 	header = g_string_sized_new(512);
-	g_string_printf(header, "%s\n", PACKAGE_STRING);
+	g_string_printf(header, "%s %s\n", PACKAGE_NAME, SR_PACKAGE_VERSION_STRING);
 	num_channels = g_slist_length(o->sdi->channels);
 	g_string_append_printf(header, "Acquisition with %d/%d channels",
 			ctx->num_enabled_channels, num_channels);
@@ -137,7 +121,7 @@ static GString *gen_header(struct sr_output *o)
 	return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
 	const struct sr_datafeed_meta *meta;
@@ -152,7 +136,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	*out = NULL;
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
-	if (!(ctx = o->internal))
+	if (!(ctx = o->priv))
 		return SR_ERR_ARG;
 
 	switch (packet->type) {
@@ -192,7 +176,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 					ctx->sample_buf[j] = 0;
 				}
 
-				if (ctx->spl_cnt == ctx->samples_per_line) {
+				if (ctx->spl_cnt == ctx->spl) {
 					/* Flush line buffers. */
 					g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
 					g_string_append_c(*out, '\n');
@@ -204,7 +188,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 					g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
 				}
 			}
-			if (ctx->spl_cnt == ctx->samples_per_line)
+			if (ctx->spl_cnt == ctx->spl)
 				/* Line buffers were already flushed. */
 				ctx->spl_cnt = 0;
 		}
@@ -235,7 +219,7 @@ static int cleanup(struct sr_output *o)
 	if (!o)
 		return SR_ERR_ARG;
 
-	if (!(ctx = o->internal))
+	if (!(ctx = o->priv))
 		return SR_OK;
 
 	g_free(ctx->channel_index);
@@ -245,16 +229,34 @@ static int cleanup(struct sr_output *o)
 		g_string_free(ctx->lines[i], TRUE);
 	g_free(ctx->lines);
 	g_free(ctx);
-	o->internal = NULL;
+	o->priv = NULL;
 
 	return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_hex = {
+static struct sr_option options[] = {
+	{ "width", "Width", "Number of samples per line", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def) {
+		options[0].def = g_variant_new_uint32(DEFAULT_SAMPLES_PER_LINE);
+		g_variant_ref_sink(options[0].def);
+	}
+
+	return options;
+}
+
+SR_PRIV struct sr_output_module output_hex = {
 	.id = "hex",
-	.description = "Hexadecimal",
+	.name = "Hexadecimal",
+	.desc = "Hexadecimal digits",
+	.exts = (const char*[]){"txt", NULL},
+	.flags = 0,
+	.options = get_options,
 	.init = init,
 	.receive = receive,
 	.cleanup = cleanup,
 };
-
diff --git a/output/ols.c b/src/output/ols.c
similarity index 87%
rename from output/ols.c
rename to src/output/ols.c
index 78a41bb..5266c27 100644
--- a/output/ols.c
+++ b/src/output/ols.c
@@ -25,10 +25,11 @@
  * https://github.com/jawi/ols/wiki/OLS-data-file-format
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/ols"
@@ -38,16 +39,14 @@ struct context {
 	uint64_t num_samples;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
 	struct context *ctx;
 
-	if (!(ctx = g_try_malloc(sizeof(struct context)))) {
-		sr_err("%s: ctx malloc failed", __func__);
-		return SR_ERR_MALLOC;
-	}
-	o->internal = ctx;
+	(void)options;
 
+	ctx = g_malloc0(sizeof(struct context));
+	o->priv = ctx;
 	ctx->samplerate = 0;
 	ctx->num_samples = 0;
 
@@ -88,7 +87,7 @@ static GString *gen_header(const struct sr_dev_inst *sdi, struct context *ctx)
 	return s;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
 	struct context *ctx;
@@ -102,7 +101,7 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	*out = NULL;
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
-	ctx = o->internal;
+	ctx = o->priv;
 
 	switch (packet->type) {
 	case SR_DF_META:
@@ -141,16 +140,20 @@ static int cleanup(struct sr_output *o)
 	if (!o || !o->sdi)
 		return SR_ERR_ARG;
 
-	ctx = o->internal;
+	ctx = o->priv;
 	g_free(ctx);
-	o->internal = NULL;
+	o->priv = NULL;
 
 	return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_ols = {
+SR_PRIV struct sr_output_module output_ols = {
 	.id = "ols",
-	.description = "OpenBench Logic Sniffer",
+	.name = "OLS",
+	.desc = "OpenBench Logic Sniffer",
+	.exts = (const char*[]){"ols", NULL},
+	.flags = 0,
+	.options = NULL,
 	.init = init,
 	.receive = receive,
 	.cleanup = cleanup
diff --git a/src/output/output.c b/src/output/output.c
new file mode 100644
index 0000000..5d76b1c
--- /dev/null
+++ b/src/output/output.c
@@ -0,0 +1,361 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "output"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Output module handling.
+ */
+
+/**
+ * @defgroup grp_output Output modules
+ *
+ * Output module handling.
+ *
+ * libsigrok supports several output modules for file formats such as binary,
+ * VCD, gnuplot, and so on. It provides an output API that frontends can use.
+ * New output modules can be added/implemented in libsigrok without having
+ * to change the frontends at all.
+ *
+ * All output modules are fed data in a stream. Devices that can stream data
+ * into libsigrok, instead of storing and then transferring the whole buffer,
+ * can thus generate output live.
+ *
+ * Output modules generate a newly allocated GString. The caller is then
+ * expected to free this with g_string_free() when finished with it.
+ *
+ * @{
+ */
+
+/** @cond PRIVATE */
+extern SR_PRIV struct sr_output_module output_bits;
+extern SR_PRIV struct sr_output_module output_hex;
+extern SR_PRIV struct sr_output_module output_ascii;
+extern SR_PRIV struct sr_output_module output_binary;
+extern SR_PRIV struct sr_output_module output_vcd;
+extern SR_PRIV struct sr_output_module output_ols;
+extern SR_PRIV struct sr_output_module output_gnuplot;
+extern SR_PRIV struct sr_output_module output_chronovu_la8;
+extern SR_PRIV struct sr_output_module output_csv;
+extern SR_PRIV struct sr_output_module output_analog;
+extern SR_PRIV struct sr_output_module output_srzip;
+extern SR_PRIV struct sr_output_module output_wav;
+/* @endcond */
+
+static const struct sr_output_module *output_module_list[] = {
+	&output_ascii,
+	&output_binary,
+	&output_bits,
+	&output_csv,
+	&output_gnuplot,
+	&output_hex,
+	&output_ols,
+	&output_vcd,
+	&output_chronovu_la8,
+	&output_analog,
+	&output_srzip,
+	&output_wav,
+	NULL,
+};
+
+/**
+ * Returns a NULL-terminated list of all available output modules.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_output_module **sr_output_list(void)
+{
+	return output_module_list;
+}
+
+/**
+ * Returns the specified output module's ID.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_output_id_get(const struct sr_output_module *omod)
+{
+	if (!omod) {
+		sr_err("Invalid output module NULL!");
+		return NULL;
+	}
+
+	return omod->id;
+}
+
+/**
+ * Returns the specified output module's name.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_output_name_get(const struct sr_output_module *omod)
+{
+	if (!omod) {
+		sr_err("Invalid output module NULL!");
+		return NULL;
+	}
+
+	return omod->name;
+}
+
+/**
+ * Returns the specified output module's description.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_output_description_get(const struct sr_output_module *omod)
+{
+	if (!omod) {
+		sr_err("Invalid output module NULL!");
+		return NULL;
+	}
+
+	return omod->desc;
+}
+
+/**
+ * Returns the specified output module's file extensions typical for the file
+ * format, as a NULL terminated array, or returns a NULL pointer if there is
+ * no preferred extension.
+ * @note these are a suggestions only.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *const *sr_output_extensions_get(
+		const struct sr_output_module *omod)
+{
+	if (!omod) {
+		sr_err("Invalid output module NULL!");
+		return NULL;
+	}
+
+	return omod->exts;
+}
+
+/*
+ * Checks whether a given flag is set.
+ *
+ * @see sr_output_flag
+ * @since 0.4.0
+ */
+SR_API gboolean sr_output_test_flag(const struct sr_output_module *omod,
+		uint64_t flag)
+{
+	return (flag & omod->flags);
+}
+
+/**
+ * Return the output module with the specified ID, or NULL if no module
+ * with that id is found.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_output_module *sr_output_find(char *id)
+{
+	int i;
+
+	for (i = 0; output_module_list[i]; i++) {
+		if (!strcmp(output_module_list[i]->id, id))
+			return output_module_list[i];
+	}
+
+	return NULL;
+}
+
+/**
+ * Returns a NULL-terminated array of struct sr_option, or NULL if the
+ * module takes no options.
+ *
+ * Each call to this function must be followed by a call to
+ * sr_output_options_free().
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_option **sr_output_options_get(const struct sr_output_module *omod)
+{
+	const struct sr_option *mod_opts, **opts;
+	int size, i;
+
+	if (!omod || !omod->options)
+		return NULL;
+
+	mod_opts = omod->options();
+
+	for (size = 0; mod_opts[size].id; size++)
+		;
+	opts = g_malloc((size + 1) * sizeof(struct sr_option *));
+
+	for (i = 0; i < size; i++)
+		opts[i] = &mod_opts[i];
+	opts[i] = NULL;
+
+	return opts;
+}
+
+/**
+ * After a call to sr_output_options_get(), this function cleans up all
+ * resources returned by that call.
+ *
+ * @since 0.4.0
+ */
+SR_API void sr_output_options_free(const struct sr_option **options)
+{
+	int i;
+
+	if (!options)
+		return;
+
+	for (i = 0; options[i]; i++) {
+		if (options[i]->def) {
+			g_variant_unref(options[i]->def);
+			((struct sr_option *)options[i])->def = NULL;
+		}
+
+		if (options[i]->values) {
+			g_slist_free_full(options[i]->values, (GDestroyNotify)g_variant_unref);
+			((struct sr_option *)options[i])->values = NULL;
+		}
+	}
+	g_free(options);
+}
+
+/**
+ * Create a new output instance using the specified output module.
+ *
+ * <code>options</code> is a *HashTable with the keys corresponding with
+ * the module options' <code>id</code> field. The values should be GVariant
+ * pointers with sunk * references, of the same GVariantType as the option's
+ * default value.
+ *
+ * The sr_dev_inst passed in can be used by the instance to determine
+ * channel names, samplerate, and so on.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_output *sr_output_new(const struct sr_output_module *omod,
+		GHashTable *options, const struct sr_dev_inst *sdi,
+		const char *filename)
+{
+	struct sr_output *op;
+	const struct sr_option *mod_opts;
+	const GVariantType *gvt;
+	GHashTable *new_opts;
+	GHashTableIter iter;
+	gpointer key, value;
+	int i;
+
+	op = g_malloc(sizeof(struct sr_output));
+	op->module = omod;
+	op->sdi = sdi;
+	op->filename = g_strdup(filename);
+
+	new_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+			(GDestroyNotify)g_variant_unref);
+	if (omod->options) {
+		mod_opts = omod->options();
+		for (i = 0; mod_opts[i].id; i++) {
+			if (options && g_hash_table_lookup_extended(options,
+					mod_opts[i].id, &key, &value)) {
+				/* Pass option along. */
+				gvt = g_variant_get_type(mod_opts[i].def);
+				if (!g_variant_is_of_type(value, gvt)) {
+					sr_err("Invalid type for '%s' option.",
+						(char *)key);
+					g_free(op);
+					return NULL;
+				}
+				g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
+						g_variant_ref(value));
+			} else {
+				/* Option not given: insert the default value. */
+				g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
+						g_variant_ref(mod_opts[i].def));
+			}
+		}
+
+		/* Make sure no invalid options were given. */
+		if (options) {
+			g_hash_table_iter_init(&iter, options);
+			while (g_hash_table_iter_next(&iter, &key, &value)) {
+				if (!g_hash_table_lookup(new_opts, key)) {
+					sr_err("Output module '%s' has no option '%s'",
+						omod->id, (char *)key);
+					g_hash_table_destroy(new_opts);
+					g_free(op);
+					return NULL;
+				}
+			}
+		}
+	}
+
+	if (op->module->init && op->module->init(op, new_opts) != SR_OK) {
+		g_free(op);
+		op = NULL;
+	}
+	if (new_opts)
+		g_hash_table_destroy(new_opts);
+
+	return op;
+}
+
+/**
+ * Send a packet to the specified output instance.
+ *
+ * The instance's output is returned as a newly allocated GString,
+ * which must be freed by the caller.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_output_send(const struct sr_output *o,
+		const struct sr_datafeed_packet *packet, GString **out)
+{
+	return o->module->receive(o, packet, out);
+}
+
+/**
+ * Free the specified output instance and all associated resources.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_output_free(const struct sr_output *o)
+{
+	int ret;
+
+	if (!o)
+		return SR_ERR_ARG;
+
+	ret = SR_OK;
+	if (o->module->cleanup)
+		ret = o->module->cleanup((struct sr_output *)o);
+	g_free((char *)o->filename);
+	g_free((gpointer)o);
+
+	return ret;
+}
+
+/** @} */
diff --git a/src/output/srzip.c b/src/output/srzip.c
new file mode 100644
index 0000000..787a2ba
--- /dev/null
+++ b/src/output/srzip.c
@@ -0,0 +1,334 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <zip.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/srzip"
+
+struct out_context {
+	gboolean zip_created;
+	uint64_t samplerate;
+	char *filename;
+};
+
+static int init(struct sr_output *o, GHashTable *options)
+{
+	struct out_context *outc;
+
+	(void)options;
+
+	if (!o->filename || o->filename[0] == '\0') {
+		sr_info("srzip output module requires a file name, cannot save.");
+		return SR_ERR_ARG;
+	}
+
+	outc = g_malloc0(sizeof(struct out_context));
+	outc->filename = g_strdup(o->filename);
+	o->priv = outc;
+
+	return SR_OK;
+}
+
+static int zip_create(const struct sr_output *o)
+{
+	struct out_context *outc;
+	struct zip *zipfile;
+	struct zip_source *versrc, *metasrc;
+	struct sr_channel *ch;
+	GVariant *gvar;
+	GKeyFile *meta;
+	GSList *l;
+	const char *devgroup;
+	char *s, *metabuf;
+	gsize metalen;
+
+	outc = o->priv;
+
+	if (outc->samplerate == 0 && sr_config_get(o->sdi->driver, o->sdi, NULL,
+					SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
+		outc->samplerate = g_variant_get_uint64(gvar);
+		g_variant_unref(gvar);
+	}
+
+	/* Quietly delete it first, libzip wants replace ops otherwise. */
+	g_unlink(outc->filename);
+	zipfile = zip_open(outc->filename, ZIP_CREATE, NULL);
+	if (!zipfile)
+		return SR_ERR;
+
+	/* "version" */
+	versrc = zip_source_buffer(zipfile, "2", 1, FALSE);
+	if (zip_add(zipfile, "version", versrc) < 0) {
+		sr_err("Error saving version into zipfile: %s",
+			zip_strerror(zipfile));
+		zip_source_free(versrc);
+		zip_discard(zipfile);
+		return SR_ERR;
+	}
+
+	/* init "metadata" */
+	meta = g_key_file_new();
+
+	g_key_file_set_string(meta, "global", "sigrok version",
+			SR_PACKAGE_VERSION_STRING);
+
+	devgroup = "device 1";
+	g_key_file_set_string(meta, devgroup, "capturefile", "logic-1");
+
+	g_key_file_set_integer(meta, devgroup, "total probes",
+			g_slist_length(o->sdi->channels));
+
+	s = sr_samplerate_string(outc->samplerate);
+	g_key_file_set_string(meta, devgroup, "samplerate", s);
+	g_free(s);
+
+	for (l = o->sdi->channels; l; l = l->next) {
+		ch = l->data;
+		if (ch->enabled && ch->type == SR_CHANNEL_LOGIC) {
+			s = g_strdup_printf("probe%d", ch->index + 1);
+			g_key_file_set_string(meta, devgroup, s, ch->name);
+			g_free(s);
+		}
+	}
+	metabuf = g_key_file_to_data(meta, &metalen, NULL);
+	g_key_file_free(meta);
+
+	metasrc = zip_source_buffer(zipfile, metabuf, metalen, FALSE);
+	if (zip_add(zipfile, "metadata", metasrc) < 0) {
+		sr_err("Error saving metadata into zipfile: %s",
+			zip_strerror(zipfile));
+		zip_source_free(metasrc);
+		zip_discard(zipfile);
+		g_free(metabuf);
+		return SR_ERR;
+	}
+
+	if (zip_close(zipfile) < 0) {
+		sr_err("Error saving zipfile: %s", zip_strerror(zipfile));
+		zip_discard(zipfile);
+		g_free(metabuf);
+		return SR_ERR;
+	}
+	g_free(metabuf);
+
+	return SR_OK;
+}
+
+static int zip_append(const struct sr_output *o, unsigned char *buf,
+		int unitsize, int length)
+{
+	struct out_context *outc;
+	struct zip *archive;
+	struct zip_source *logicsrc;
+	int64_t i, num_files;
+	struct zip_stat zs;
+	struct zip_source *metasrc;
+	GKeyFile *kf;
+	GError *error;
+	uint64_t chunk_num;
+	const char *entry_name;
+	char *metabuf;
+	gsize metalen;
+	char *chunkname;
+	unsigned int next_chunk_num;
+
+	outc = o->priv;
+	if (!(archive = zip_open(outc->filename, 0, NULL)))
+		return SR_ERR;
+
+	if (zip_stat(archive, "metadata", 0, &zs) < 0) {
+		sr_err("Failed to open metadata: %s", zip_strerror(archive));
+		zip_discard(archive);
+		return SR_ERR;
+	}
+	kf = sr_sessionfile_read_metadata(archive, &zs);
+	if (!kf) {
+		zip_discard(archive);
+		return SR_ERR_DATA;
+	}
+	/*
+	 * If the file was only initialized but doesn't yet have any
+	 * data it in, it won't have a unitsize field in metadata yet.
+	 */
+	error = NULL;
+	metabuf = NULL;
+	if (!g_key_file_has_key(kf, "device 1", "unitsize", &error)) {
+		if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
+			sr_err("Failed to check unitsize key: %s", error->message);
+			g_error_free(error);
+			g_key_file_free(kf);
+			zip_discard(archive);
+			return SR_ERR;
+		}
+		g_clear_error(&error);
+
+		/* Add unitsize field. */
+		g_key_file_set_integer(kf, "device 1", "unitsize", unitsize);
+		metabuf = g_key_file_to_data(kf, &metalen, NULL);
+		metasrc = zip_source_buffer(archive, metabuf, metalen, FALSE);
+
+		if (zip_replace(archive, zs.index, metasrc) < 0) {
+			sr_err("Failed to replace metadata: %s",
+				zip_strerror(archive));
+			g_key_file_free(kf);
+			zip_source_free(metasrc);
+			zip_discard(archive);
+			g_free(metabuf);
+			return SR_ERR;
+		}
+	}
+	g_key_file_free(kf);
+
+	next_chunk_num = 1;
+	num_files = zip_get_num_entries(archive, 0);
+	for (i = 0; i < num_files; i++) {
+		entry_name = zip_get_name(archive, i, 0);
+		if (!entry_name || strncmp(entry_name, "logic-1", 7) != 0)
+			continue;
+		if (entry_name[7] == '\0') {
+			/* This file has no extra chunks, just a single "logic-1".
+			 * Rename it to "logic-1-1" * and continue with chunk 2. */
+			if (zip_rename(archive, i, "logic-1-1") < 0) {
+				sr_err("Failed to rename 'logic-1' to 'logic-1-1': %s",
+					zip_strerror(archive));
+				zip_discard(archive);
+				g_free(metabuf);
+				return SR_ERR;
+			}
+			next_chunk_num = 2;
+			break;
+		} else if (entry_name[7] == '-') {
+			chunk_num = g_ascii_strtoull(entry_name + 8, NULL, 10);
+			if (chunk_num < G_MAXINT && chunk_num >= next_chunk_num)
+				next_chunk_num = chunk_num + 1;
+		}
+	}
+
+	if (length % unitsize != 0) {
+		sr_warn("Chunk size %d not a multiple of the"
+			" unit size %d.", length, unitsize);
+	}
+	logicsrc = zip_source_buffer(archive, buf, length, FALSE);
+	chunkname = g_strdup_printf("logic-1-%u", next_chunk_num);
+	i = zip_add(archive, chunkname, logicsrc);
+	g_free(chunkname);
+	if (i < 0) {
+		sr_err("Failed to add chunk 'logic-1-%u': %s",
+			next_chunk_num, zip_strerror(archive));
+		zip_source_free(logicsrc);
+		zip_discard(archive);
+		g_free(metabuf);
+		return SR_ERR;
+	}
+	if (zip_close(archive) < 0) {
+		sr_err("Error saving session file: %s", zip_strerror(archive));
+		zip_discard(archive);
+		g_free(metabuf);
+		return SR_ERR;
+	}
+	g_free(metabuf);
+
+	return SR_OK;
+}
+
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
+		GString **out)
+{
+	struct out_context *outc;
+	const struct sr_datafeed_meta *meta;
+	const struct sr_datafeed_logic *logic;
+	const struct sr_config *src;
+	GSList *l;
+
+	int ret;
+
+	*out = NULL;
+	if (!o || !o->sdi || !(outc = o->priv))
+		return SR_ERR_ARG;
+
+	switch (packet->type) {
+	case SR_DF_META:
+		meta = packet->payload;
+		for (l = meta->config; l; l = l->next) {
+			src = l->data;
+			if (src->key != SR_CONF_SAMPLERATE)
+				continue;
+			outc->samplerate = g_variant_get_uint64(src->data);
+		}
+		break;
+	case SR_DF_LOGIC:
+		if (!outc->zip_created) {
+			if ((ret = zip_create(o)) != SR_OK)
+				return ret;
+			outc->zip_created = TRUE;
+		}
+		logic = packet->payload;
+		ret = zip_append(o, logic->data, logic->unitsize, logic->length);
+		if (ret != SR_OK)
+			return ret;
+		break;
+	}
+
+	return SR_OK;
+}
+
+static struct sr_option options[] = {
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def)
+		options[0].def = g_variant_ref_sink(g_variant_new_string(""));
+
+	return options;
+}
+
+static int cleanup(struct sr_output *o)
+{
+	struct out_context *outc;
+
+	outc = o->priv;
+	g_variant_unref(options[0].def);
+	g_free(outc->filename);
+	g_free(outc);
+	o->priv = NULL;
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_output_module output_srzip = {
+	.id = "srzip",
+	.name = "srzip",
+	.desc = "srzip session file",
+	.exts = (const char*[]){"sr", NULL},
+	.flags = SR_OUTPUT_INTERNAL_IO_HANDLING,
+	.options = get_options,
+	.init = init,
+	.receive = receive,
+	.cleanup = cleanup,
+};
diff --git a/output/vcd.c b/src/output/vcd.c
similarity index 91%
rename from output/vcd.c
rename to src/output/vcd.c
index 87f8049..33eeeb5 100644
--- a/output/vcd.c
+++ b/src/output/vcd.c
@@ -19,11 +19,11 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
-#include "config.h" /* Needed for PACKAGE and others. */
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/vcd"
@@ -39,16 +39,15 @@ struct context {
 	uint64_t samplecount;
 };
 
-static const char *const vcd_header_comment =
-	"$comment\n  Acquisition with %d/%d channels at %s\n$end\n";
-
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
 	GSList *l;
 	int num_enabled_channels, i;
 
+	(void)options;
+
 	num_enabled_channels = 0;
 	for (l = o->sdi->channels; l; l = l->next) {
 		ch = l->data;
@@ -64,7 +63,7 @@ static int init(struct sr_output *o)
 	}
 
 	ctx = g_malloc0(sizeof(struct context));
-	o->internal = ctx;
+	o->priv = ctx;
 	ctx->num_enabled_channels = num_enabled_channels;
 	ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
 
@@ -81,7 +80,7 @@ static int init(struct sr_output *o)
 	return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
 	struct context *ctx;
 	struct sr_channel *ch;
@@ -92,7 +91,7 @@ static GString *gen_header(struct sr_output *o)
 	int num_channels, i;
 	char *samplerate_s, *frequency_s, *timestamp;
 
-	ctx = o->internal;
+	ctx = o->priv;
 	header = g_string_sized_new(512);
 	num_channels = g_slist_length(o->sdi->channels);
 
@@ -105,7 +104,7 @@ static GString *gen_header(struct sr_output *o)
 
 	/* generator */
 	g_string_append_printf(header, "$version %s %s $end\n",
-			PACKAGE, PACKAGE_VERSION);
+			PACKAGE_NAME, SR_PACKAGE_VERSION_STRING);
 	g_string_append_printf(header, "$comment\n  Acquisition with "
 			"%d/%d channels", ctx->num_enabled_channels, num_channels);
 
@@ -136,7 +135,7 @@ static GString *gen_header(struct sr_output *o)
 	g_free(frequency_s);
 
 	/* scope */
-	g_string_append_printf(header, "$scope module %s $end\n", PACKAGE);
+	g_string_append_printf(header, "$scope module %s $end\n", PACKAGE_NAME);
 
 	/* Wires / channels */
 	for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
@@ -154,7 +153,7 @@ static GString *gen_header(struct sr_output *o)
 	return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
 		GString **out)
 {
 	const struct sr_datafeed_meta *meta;
@@ -168,9 +167,9 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
 	gboolean timestamp_written;
 
 	*out = NULL;
-	if (!o || !o->internal)
+	if (!o || !o->priv)
 		return SR_ERR_BUG;
-	ctx = o->internal;
+	ctx = o->priv;
 
 	switch (packet->type) {
 	case SR_DF_META:
@@ -249,10 +248,10 @@ static int cleanup(struct sr_output *o)
 {
 	struct context *ctx;
 
-	if (!o || !o->internal)
+	if (!o || !o->priv)
 		return SR_ERR_ARG;
 
-	ctx = o->internal;
+	ctx = o->priv;
 	g_free(ctx->prevsample);
 	g_free(ctx->channel_index);
 	g_free(ctx);
@@ -260,9 +259,13 @@ static int cleanup(struct sr_output *o)
 	return SR_OK;
 }
 
-struct sr_output_format output_vcd = {
+struct sr_output_module output_vcd = {
 	.id = "vcd",
-	.description = "Value Change Dump (VCD)",
+	.name = "VCD",
+	.desc = "Value Change Dump",
+	.exts = (const char*[]){"vcd", NULL},
+	.flags = 0,
+	.options = NULL,
 	.init = init,
 	.receive = receive,
 	.cleanup = cleanup,
diff --git a/src/output/wav.c b/src/output/wav.c
new file mode 100644
index 0000000..f991cbf
--- /dev/null
+++ b/src/output/wav.c
@@ -0,0 +1,386 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/wav"
+
+/* Minimum/maximum number of samples per channel to put in a data chunk */
+#define MIN_DATA_CHUNK_SAMPLES 10
+
+struct out_context {
+	double scale;
+	gboolean header_done;
+	uint64_t samplerate;
+	int num_channels;
+	GSList *channels;
+	int chanbuf_size;
+	int *chanbuf_used;
+	uint8_t **chanbuf;
+	float *fdata;
+};
+
+static int realloc_chanbufs(const struct sr_output *o, int size)
+{
+	struct out_context *outc;
+	int i;
+
+	outc = o->priv;
+	for (i = 0; i < outc->num_channels; i++) {
+		if (!(outc->chanbuf[i] = g_try_realloc(outc->chanbuf[i], sizeof(float) * size))) { 
+			sr_err("Unable to allocate enough output buffer memory.");
+			return SR_ERR;
+		}
+		outc->chanbuf_used[i] = 0;
+	}
+	outc->chanbuf_size = size;
+
+	return SR_OK;
+}
+
+static int flush_chanbufs(const struct sr_output *o, GString *out)
+{
+	struct out_context *outc;
+	int num_samples, i, j;
+	char *buf, *bufp;
+
+	outc = o->priv;
+
+	/* Any one of them will do. */
+	num_samples = outc->chanbuf_used[0];
+	if (!(buf = g_try_malloc(4 * num_samples * outc->num_channels))) {
+		sr_err("Unable to allocate enough interleaved output buffer memory.");
+		return SR_ERR;
+	}
+
+	bufp = buf;
+	for (i = 0; i < num_samples; i++) {
+		for (j = 0; j < outc->num_channels; j++) {
+			memcpy(bufp, outc->chanbuf[j] + i * 4, 4);
+			bufp += 4;
+		}
+	}
+	g_string_append_len(out, buf, 4 * num_samples * outc->num_channels);
+	g_free(buf);
+
+	for (i = 0; i < outc->num_channels; i++)
+		outc->chanbuf_used[i] = 0;
+
+	return SR_OK;
+}
+
+static int init(struct sr_output *o, GHashTable *options)
+{
+	struct out_context *outc;
+	struct sr_channel *ch;
+	GSList *l;
+
+	outc = g_malloc0(sizeof(struct out_context));
+	o->priv = outc;
+	outc->scale = g_variant_get_double(g_hash_table_lookup(options, "scale"));
+
+	for (l = o->sdi->channels; l; l = l->next) {
+		ch = l->data;
+		if (ch->type != SR_CHANNEL_ANALOG)
+			continue;
+		if (!ch->enabled)
+			continue;
+		outc->channels = g_slist_append(outc->channels, ch);
+		outc->num_channels++;
+	}
+
+	outc->chanbuf = g_malloc0(sizeof(float *) * outc->num_channels);
+	outc->chanbuf_used = g_malloc0(sizeof(int) * outc->num_channels);
+
+	/* Start off the interleaved buffer with 100 samples/channel. */
+	realloc_chanbufs(o, 100);
+
+	return SR_OK;
+}
+
+static void add_data_chunk(const struct sr_output *o, GString *gs)
+{
+	struct out_context *outc;
+	char tmp[4];
+
+	outc = o->priv;
+	g_string_append(gs, "fmt ");
+	/* Remaining chunk size */
+	WL32(tmp, 0x12);
+	g_string_append_len(gs, tmp, 4);
+	/* Format code 3 = IEEE float */
+	WL16(tmp, 0x0003);
+	g_string_append_len(gs, tmp, 2);
+	/* Number of channels */
+	WL16(tmp, outc->num_channels);
+	g_string_append_len(gs, tmp, 2);
+	/* Samplerate */
+	WL32(tmp, outc->samplerate);
+	g_string_append_len(gs, tmp, 4);
+	/* Byterate, using 32-bit floats. */
+	WL32(tmp, outc->samplerate * outc->num_channels * 4);
+	g_string_append_len(gs, tmp, 4);
+	/* Blockalign */
+	WL16(tmp, outc->num_channels * 4);
+	g_string_append_len(gs, tmp, 2);
+	/* Bits per sample */
+	WL16(tmp, 32);
+	g_string_append_len(gs, tmp, 2);
+	WL16(tmp, 0);
+	g_string_append_len(gs, tmp, 2);
+
+	g_string_append(gs, "data");
+	/* Data chunk size, max it out. */
+	WL32(tmp, 0xffffffff);
+	g_string_append_len(gs, tmp, 4);
+}
+
+static GString *gen_header(const struct sr_output *o)
+{
+	struct out_context *outc;
+	GVariant *gvar;
+	GString *header;
+	char tmp[4];
+
+	outc = o->priv;
+	if (outc->samplerate == 0) {
+		if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
+				&gvar) == SR_OK) {
+			outc->samplerate = g_variant_get_uint64(gvar);
+			g_variant_unref(gvar);
+		}
+	}
+
+	header = g_string_sized_new(512);
+	g_string_append(header, "RIFF");
+	/* Total size. Max out the field. */
+	WL32(tmp, 0xffffffff);
+	g_string_append_len(header, tmp, 4);
+	g_string_append(header, "WAVE");
+	add_data_chunk(o, header);
+
+	return header;
+}
+
+/*
+ * Stores the float in little-endian BINARY32 IEEE-754 2008 format.
+ */
+static void float_to_le(uint8_t *buf, float value)
+{
+	char *old;
+
+	old = (char *)&value;
+#ifdef WORDS_BIGENDIAN
+	buf[0] = old[3];
+	buf[1] = old[2];
+	buf[2] = old[1];
+	buf[3] = old[0];
+#else
+	buf[0] = old[0];
+	buf[1] = old[1];
+	buf[2] = old[2];
+	buf[3] = old[3];
+#endif
+}
+
+/*
+ * Returns the number of samples used in the current channel buffers,
+ * or -1 if they're not all the same.
+ */
+static int check_chanbuf_size(const struct sr_output *o)
+{
+	struct out_context *outc;
+	int size, i;
+
+	outc = o->priv;
+	size = 0;
+	for (i = 0; i < outc->num_channels; i++) {
+		if (size == 0) {
+			if (outc->chanbuf_used[i] == 0) {
+				/* Nothing in all the buffers yet. */
+				size = -1;
+				break;
+			} else
+				/* New high water mark. */
+				size = outc->chanbuf_used[i];
+		} else if (outc->chanbuf_used[i] != size) {
+			/* All channel buffers are not equally full yet. */
+			size = -1;
+			break;
+		}
+	}
+
+	return size;
+}
+
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
+		GString **out)
+{
+	struct out_context *outc;
+	const struct sr_datafeed_meta *meta;
+	const struct sr_datafeed_analog_old *analog_old;
+	const struct sr_datafeed_analog *analog;
+	const struct sr_config *src;
+	struct sr_channel *ch;
+	GSList *l;
+	const GSList *channels;
+	float f;
+	int num_channels, num_samples, size, *chan_idx, idx, i, j, ret;
+	float *data;
+	uint8_t *buf;
+
+	*out = NULL;
+	if (!o || !o->sdi || !(outc = o->priv))
+		return SR_ERR_ARG;
+
+	switch (packet->type) {
+	case SR_DF_META:
+		meta = packet->payload;
+		for (l = meta->config; l; l = l->next) {
+			src = l->data;
+			if (src->key != SR_CONF_SAMPLERATE)
+				continue;
+			outc->samplerate = g_variant_get_uint64(src->data);
+		}
+		break;
+	case SR_DF_ANALOG_OLD:
+	case SR_DF_ANALOG:
+		if (!outc->header_done) {
+			*out = gen_header(o);
+			outc->header_done = TRUE;
+		} else
+			*out = g_string_sized_new(512);
+
+		analog_old = packet->payload;
+		analog = packet->payload;
+
+		if (packet->type == SR_DF_ANALOG_OLD) {
+			num_samples = analog_old->num_samples;
+			channels = analog_old->channels;
+			num_channels = g_slist_length(analog_old->channels);
+			data = analog_old->data;
+		} else {
+			num_samples = analog->num_samples;
+			channels = analog->meaning->channels;
+			num_channels = g_slist_length(analog->meaning->channels);
+			if (!(data = g_try_realloc(outc->fdata, sizeof(float) * num_samples * num_channels)))
+				return SR_ERR_MALLOC;
+			outc->fdata = data;
+			ret = sr_analog_to_float(analog, data);
+			if (ret != SR_OK)
+				return ret;
+		}
+
+		if (num_samples == 0)
+			return SR_OK;
+
+		if (num_channels > outc->num_channels) {
+			sr_err("Packet has %d channels, but only %d were enabled.",
+					num_channels, outc->num_channels);
+			return SR_ERR;
+		}
+
+		if (num_samples > outc->chanbuf_size) {
+			if (realloc_chanbufs(o, analog_old->num_samples) != SR_OK)
+				return SR_ERR_MALLOC;
+		}
+
+		/* Index the channels in this packet, so we can interleave quicker. */
+		chan_idx = g_malloc(sizeof(int) * outc->num_channels);
+		for (i = 0; i < num_channels; i++) {
+			ch = g_slist_nth_data((GSList *) channels, i);
+			chan_idx[i] = g_slist_index(outc->channels, ch);
+		}
+
+		for (i = 0; i < num_samples; i++) {
+			for (j = 0; j < num_channels; j++) {
+				idx = chan_idx[j];
+				buf = outc->chanbuf[idx] + outc->chanbuf_used[idx]++ * 4;
+				f = data[i * num_channels + j];
+				if (outc->scale != 0.0)
+					f /= outc->scale;
+				float_to_le(buf, f);
+			}
+		}
+		g_free(chan_idx);
+
+		size = check_chanbuf_size(o);
+		if (size > MIN_DATA_CHUNK_SAMPLES)
+			if (flush_chanbufs(o, *out) != SR_OK)
+				return SR_ERR;
+		break;
+	case SR_DF_END:
+		size = check_chanbuf_size(o);
+		if (size > 0) {
+			*out = g_string_sized_new(4 * size * outc->num_channels);
+			if (flush_chanbufs(o, *out) != SR_OK)
+				return SR_ERR;
+		}
+		break;
+	}
+
+	return SR_OK;
+}
+
+static struct sr_option options[] = {
+	{ "scale", "Scale", "Scale values by factor", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	if (!options[0].def)
+		options[0].def = g_variant_ref_sink(g_variant_new_double(0.0));
+
+	return options;
+}
+
+static int cleanup(struct sr_output *o)
+{
+	struct out_context *outc;
+	int i;
+
+	outc = o->priv;
+	g_slist_free(outc->channels);
+	g_variant_unref(options[0].def);
+	for (i = 0; i < outc->num_channels; i++)
+		g_free(outc->chanbuf[i]);
+	g_free(outc->chanbuf_used);
+	g_free(outc->chanbuf);
+	g_free(outc->fdata);
+	g_free(outc);
+	o->priv = NULL;
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_output_module output_wav = {
+	.id = "wav",
+	.name = "WAV",
+	.desc = "Microsoft WAV file format",
+	.exts = (const char*[]){"wav", NULL},
+	.flags = 0,
+	.options = get_options,
+	.init = init,
+	.receive = receive,
+	.cleanup = cleanup,
+};
diff --git a/src/resource.c b/src/resource.c
new file mode 100644
index 0000000..b921d93
--- /dev/null
+++ b/src/resource.c
@@ -0,0 +1,380 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "resource"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Access to resource files.
+ */
+
+/** Retrieve the size of the open stream @a file.
+ *
+ * This function only works on seekable streams. However, the set of seekable
+ * streams is generally congruent with the set of streams that have a size.
+ * Code that needs to work with any type of stream (including pipes) should
+ * require neither seekability nor advance knowledge of the size.
+ * On failure, the return value is negative and errno is set.
+ *
+ * @param file An I/O stream opened in binary mode.
+ * @return The size of @a file in bytes, or a negative value on failure.
+ *
+ * @private
+ */
+SR_PRIV int64_t sr_file_get_size(FILE *file)
+{
+	off_t filepos, filesize;
+
+	/* ftello() and fseeko() are not standard C, but part of POSIX.1-2001.
+	 * Thus, if these functions are available at all, they can reasonably
+	 * be expected to also conform to POSIX semantics. In particular, this
+	 * means that ftello() after fseeko(..., SEEK_END) has a defined result
+	 * and can be used to get the size of a seekable stream.
+	 * On Windows, the result is fully defined only for binary streams.
+	 */
+	filepos = ftello(file);
+	if (filepos < 0)
+		return -1;
+
+	if (fseeko(file, 0, SEEK_END) < 0)
+		return -1;
+
+	filesize = ftello(file);
+	if (filesize < 0)
+		return -1;
+
+	if (fseeko(file, filepos, SEEK_SET) < 0)
+		return -1;
+
+	return filesize;
+}
+
+static FILE *try_open_file(const char *datadir, const char *subdir,
+		const char *name)
+{
+	char *filename;
+	FILE *file;
+
+	filename = g_build_filename(datadir, subdir, name, NULL);
+	file = g_fopen(filename, "rb");
+
+	if (file)
+		sr_info("Opened '%s'.", filename);
+	else
+		sr_spew("Attempt to open '%s' failed: %s",
+			filename, g_strerror(errno));
+	g_free(filename);
+
+	return file;
+}
+
+static int resource_open_default(struct sr_resource *res,
+		const char *name, void *cb_data)
+{
+	int64_t filesize;
+#ifdef FIRMWARE_DIR
+	const char *builtindir;
+#endif
+	const char *subdir;
+	const char *const *datadirs;
+	FILE *file;
+
+	(void)cb_data;
+
+	switch (res->type) {
+	case SR_RESOURCE_FIRMWARE:
+#ifdef FIRMWARE_DIR
+		builtindir = FIRMWARE_DIR;
+#endif
+		subdir = "sigrok-firmware";
+		break;
+	default:
+		sr_err("%s: unknown type %d.", __func__, res->type);
+		return SR_ERR_ARG;
+	}
+
+	file = try_open_file(g_get_user_data_dir(), subdir, name);
+	/*
+	 * Scan the hard-coded directory before the system directories to
+	 * avoid picking up possibly outdated files from a system install.
+	 */
+#ifdef FIRMWARE_DIR
+	if (!file)
+		file = try_open_file(builtindir, "", name);
+#endif
+	if (!file) {
+		datadirs = g_get_system_data_dirs();
+		while (*datadirs && !file)
+			file = try_open_file(*datadirs++, subdir, name);
+	}
+	if (!file) {
+		sr_err("Failed to locate '%s'.", name);
+		return SR_ERR;
+	}
+
+	filesize = sr_file_get_size(file);
+	if (filesize < 0) {
+		sr_err("Failed to obtain size of '%s': %s",
+			name, g_strerror(errno));
+		fclose(file);
+		return SR_ERR;
+	}
+	res->size = filesize;
+	res->handle = file;
+
+	return SR_OK;
+}
+
+static int resource_close_default(struct sr_resource *res, void *cb_data)
+{
+	FILE *file;
+
+	(void)cb_data;
+
+	file = res->handle;
+	if (!file) {
+		sr_err("%s: invalid handle.", __func__);
+		return SR_ERR_ARG;
+	}
+
+	if (fclose(file) < 0) {
+		sr_err("Failed to close file: %s", g_strerror(errno));
+		return SR_ERR;
+	}
+	res->handle = NULL;
+
+	return SR_OK;
+}
+
+static gssize resource_read_default(const struct sr_resource *res,
+		void *buf, size_t count, void *cb_data)
+{
+	FILE *file;
+	size_t n_read;
+
+	(void)cb_data;
+
+	file = res->handle;
+	if (!file) {
+		sr_err("%s: invalid handle.", __func__);
+		return SR_ERR_ARG;
+	}
+	if (count > G_MAXSSIZE) {
+		sr_err("%s: count %zu too large.", __func__, count);
+		return SR_ERR_ARG;
+	}
+
+	n_read = fread(buf, 1, count, file);
+
+	if (n_read != count && ferror(file)) {
+		sr_err("Failed to read resource file: %s", g_strerror(errno));
+		return SR_ERR;
+	}
+	return n_read;
+}
+
+/**
+ * Install resource access hooks.
+ *
+ * @param ctx libsigrok context. Must not be NULL.
+ * @param open_cb Resource open callback, or NULL to unset.
+ * @param close_cb Resource close callback, or NULL to unset.
+ * @param read_cb Resource read callback, or NULL to unset.
+ * @param cb_data User data pointer passed to callbacks.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_resource_set_hooks(struct sr_context *ctx,
+		sr_resource_open_callback open_cb,
+		sr_resource_close_callback close_cb,
+		sr_resource_read_callback read_cb, void *cb_data)
+{
+	if (!ctx) {
+		sr_err("%s: ctx was NULL.", __func__);
+		return SR_ERR_ARG;
+	}
+	if (open_cb && close_cb && read_cb) {
+		ctx->resource_open_cb  = open_cb;
+		ctx->resource_close_cb = close_cb;
+		ctx->resource_read_cb  = read_cb;
+		ctx->resource_cb_data  = cb_data;
+	} else if (!open_cb && !close_cb && !read_cb) {
+		ctx->resource_open_cb  = &resource_open_default;
+		ctx->resource_close_cb = &resource_close_default;
+		ctx->resource_read_cb  = &resource_read_default;
+		ctx->resource_cb_data  = ctx;
+	} else {
+		sr_err("%s: inconsistent callback pointers.", __func__);
+		return SR_ERR_ARG;
+	}
+	return SR_OK;
+}
+
+/**
+ * Open resource.
+ *
+ * @param ctx libsigrok context. Must not be NULL.
+ * @param[out] res Resource descriptor to fill in. Must not be NULL.
+ * @param type Resource type ID.
+ * @param name Name of the resource. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR Other error.
+ *
+ * @private
+ */
+SR_PRIV int sr_resource_open(struct sr_context *ctx,
+		struct sr_resource *res, int type, const char *name)
+{
+	int ret;
+
+	res->size = 0;
+	res->handle = NULL;
+	res->type = type;
+
+	ret = (*ctx->resource_open_cb)(res, name, ctx->resource_cb_data);
+
+	if (ret != SR_OK)
+		sr_err("Failed to open resource '%s'.", name);
+
+	return ret;
+}
+
+/**
+ * Close resource.
+ *
+ * @param ctx libsigrok context. Must not be NULL.
+ * @param[inout] res Resource descriptor. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR Other error.
+ *
+ * @private
+ */
+SR_PRIV int sr_resource_close(struct sr_context *ctx, struct sr_resource *res)
+{
+	int ret;
+
+	ret = (*ctx->resource_close_cb)(res, ctx->resource_cb_data);
+
+	if (ret != SR_OK)
+		sr_err("Failed to close resource.");
+
+	return ret;
+}
+
+/**
+ * Read resource data.
+ *
+ * @param ctx libsigrok context. Must not be NULL.
+ * @param[in] res Resource descriptor. Must not be NULL.
+ * @param[out] buf Buffer to store @a count bytes into. Must not be NULL.
+ * @param count Number of bytes to read.
+ *
+ * @return The number of bytes actually read, or a negative value on error.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR Other error.
+ *
+ * @private
+ */
+SR_PRIV gssize sr_resource_read(struct sr_context *ctx,
+		const struct sr_resource *res, void *buf, size_t count)
+{
+	gssize n_read;
+
+	n_read = (*ctx->resource_read_cb)(res, buf, count,
+			ctx->resource_cb_data);
+	if (n_read < 0)
+		sr_err("Failed to read resource.");
+
+	return n_read;
+}
+
+/**
+ * Load a resource into memory.
+ *
+ * @param ctx libsigrok context. Must not be NULL.
+ * @param type Resource type ID.
+ * @param name Name of the resource. Must not be NULL.
+ * @param[out] size Size in bytes of the returned buffer. Must not be NULL.
+ * @param max_size Size limit. Error out if the resource is larger than this.
+ *
+ * @return A buffer containing the resource data, or NULL on failure. Must
+ *  be freed by the caller using g_free().
+ *
+ * @private
+ */
+SR_PRIV void *sr_resource_load(struct sr_context *ctx,
+		int type, const char *name, size_t *size, size_t max_size)
+{
+	struct sr_resource res;
+	void *buf;
+	size_t res_size;
+	gssize n_read;
+
+	if (sr_resource_open(ctx, &res, type, name) != SR_OK)
+		return NULL;
+
+	if (res.size > max_size) {
+		sr_err("Size %" PRIu64 " of '%s' exceeds limit %zu.",
+			res.size, name, max_size);
+		sr_resource_close(ctx, &res);
+		return NULL;
+	}
+	res_size = res.size;
+
+	buf = g_try_malloc(res_size);
+	if (!buf) {
+		sr_err("Failed to allocate buffer for '%s'.", name);
+		sr_resource_close(ctx, &res);
+		return NULL;
+	}
+
+	n_read = sr_resource_read(ctx, &res, buf, res_size);
+	sr_resource_close(ctx, &res);
+
+	if (n_read < 0 || (size_t)n_read != res_size) {
+		if (n_read >= 0)
+			sr_err("Failed to read '%s': premature end of file.",
+				name);
+		g_free(buf);
+		return NULL;
+	}
+
+	*size = res_size;
+	return buf;
+}
+
+/** @} */
diff --git a/src/scale/kern.c b/src/scale/kern.c
new file mode 100644
index 0000000..b4872ea
--- /dev/null
+++ b/src/scale/kern.c
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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
+ */
+
+/*
+ * KERN scale protocol parser.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "kern"
+
+static int get_buflen(const uint8_t *buf)
+{
+	/* Find out whether it's a 14-byte or 15-byte packet. */
+	if (buf[12] == '\r' && buf[13] == '\n')
+		return 14;
+	else if (buf[13] == '\r' && buf[14] == '\n')
+		return 15;
+	else
+		return -1;
+}
+
+static int parse_value(const uint8_t *buf, float *result,
+		const struct kern_info *info)
+{
+	char *strval;
+	float floatval;
+	int s2, len;
+
+	s2 = (info->buflen == 14) ? 11 : 12;
+	len = (info->buflen == 14) ? 8 : 9;
+
+	if (buf[s2] == 'E') {
+		/* Display: "o-Err" or "u-Err", but protocol only has 'E'. */
+		sr_spew("Over/under limit.");
+		*result = INFINITY;
+		return SR_OK;
+	}
+
+	strval = g_strndup((const char *)buf, len);
+	floatval = g_ascii_strtod(strval, NULL);
+	g_free(strval);
+	*result = floatval;
+
+	return SR_OK;
+}
+
+static void parse_flags(const uint8_t *buf, struct kern_info *info)
+{
+	int u1, u2, s2;
+
+	u1 = (info->buflen == 14) ? 8 : 9;
+	u2 = (info->buflen == 14) ? 9 : 10;
+	s2 = (info->buflen == 14) ? 11 : 12;
+
+	/* Bytes U1, U2: Unit */
+	info->is_gram         = (buf[u1] == ' ' && buf[u2] == 'G');
+	info->is_carat        = (buf[u1] == 'C' && buf[u2] == 'T');
+	info->is_ounce        = (buf[u1] == 'O' && buf[u2] == 'Z');
+	info->is_pound        = (buf[u1] == 'L' && buf[u2] == 'B');
+	info->is_troy_ounce   = (buf[u1] == 'O' && buf[u2] == 'T');
+	info->is_pennyweight  = (buf[u1] == 'D' && buf[u2] == 'W');
+	info->is_grain        = (buf[u1] == 'G' && buf[u2] == 'R');
+	info->is_tael         = (buf[u1] == 'T' && buf[u2] == 'L');
+	info->is_momme        = (buf[u1] == 'M' && buf[u2] == 'O');
+	info->is_tola         = (buf[u1] == 't' && buf[u2] == 'o');
+	info->is_percentage   = (buf[u1] == ' ' && buf[u2] == '%');
+	info->is_piece        = (buf[u1] == 'P' && buf[u2] == 'C');
+
+	/*
+	 * Note: The display can show 3 different variants for Tael:
+	 * "Hong Kong", "Singapore, Malaysia", and "Taiwan". However,
+	 * in the protocol only one Tael value ('T', 'L') is used, thus
+	 * we cannot distinguish between them.
+	 */
+
+	/* Byte S1: Result / data type (currently unused) */
+
+	/* Byte S2: Status of the data */
+	info->is_unstable     = (buf[s2] == 'U');
+	info->is_stable       = (buf[s2] == 'S');
+	info->is_error        = (buf[s2] == 'E');
+	/* Space: no special status. */
+
+	/* Byte CR: Always '\r' (carriage return, 0x0d, 13) */
+
+	/* Byte LF: Always '\n' (newline, 0x0a, 10) */
+}
+
+static void handle_flags(struct sr_datafeed_analog_old *analog, float *floatval,
+			 const struct kern_info *info)
+{
+	(void)floatval;
+
+	/* Measured quantity: mass. */
+	analog->mq = SR_MQ_MASS;
+
+	/* Unit */
+	if (info->is_gram)
+		analog->unit = SR_UNIT_GRAM;
+	if (info->is_carat)
+		analog->unit = SR_UNIT_CARAT;
+	if (info->is_ounce)
+		analog->unit = SR_UNIT_OUNCE;
+	if (info->is_pound)
+		analog->unit = SR_UNIT_POUND;
+	if (info->is_troy_ounce)
+		analog->unit = SR_UNIT_TROY_OUNCE;
+	if (info->is_pennyweight)
+		analog->unit = SR_UNIT_PENNYWEIGHT;
+	if (info->is_grain)
+		analog->unit = SR_UNIT_GRAIN;
+	if (info->is_tael)
+		analog->unit = SR_UNIT_TAEL;
+	if (info->is_momme)
+		analog->unit = SR_UNIT_MOMME;
+	if (info->is_tola)
+		analog->unit = SR_UNIT_TOLA;
+	if (info->is_percentage)
+		analog->unit = SR_UNIT_PERCENTAGE;
+	if (info->is_piece)
+		analog->unit = SR_UNIT_PIECE;
+
+	/* Measurement related flags */
+	if (info->is_unstable)
+		analog->mqflags |= SR_MQFLAG_UNSTABLE;
+}
+
+SR_PRIV gboolean sr_kern_packet_valid(const uint8_t *buf)
+{
+	int buflen, s1, s2, cr, lf;
+
+	if ((buflen = get_buflen(buf)) < 0)
+		return FALSE;
+
+	s1 = (buflen == 14) ? 10 : 11;
+	s2 = (buflen == 14) ? 11 : 12;
+	cr = (buflen == 14) ? 12 : 13;
+	lf = (buflen == 14) ? 13 : 14;
+
+	/* Byte 0: Sign (must be '+' or '-' or ' '). */
+	if (buf[0] != '+' && buf[0] != '-' && buf[0] != ' ')
+		return FALSE;
+
+	/* Byte S1: Must be 'L' or 'G' or 'H' or ' '. */
+	if (buf[s1] != 'L' && buf[s1] != 'G' && buf[s1] != 'H' && buf[s1] != ' ')
+		return FALSE;
+
+	/* Byte S2: Must be 'U' or 'S' or 'E' or ' '. */
+	if (buf[s2] != 'U' && buf[s2] != 'S' && buf[s2] != 'E' && buf[s2] != ' ')
+		return FALSE;
+
+	/* Byte CR: Always '\r' (carriage return, 0x0d, 13) */
+	/* Byte LF: Always '\n' (newline, 0x0a, 10) */
+	if (buf[cr] != '\r' || buf[lf] != '\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_old. The struct will be
+ *               filled with data according to the protocol packet.
+ *               Must not be NULL.
+ * @param info Pointer to a struct kern_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_kern_parse(const uint8_t *buf, float *floatval,
+			  struct sr_datafeed_analog_old *analog, void *info)
+{
+	int ret;
+	struct kern_info *info_local;
+
+	info_local = (struct kern_info *)info;
+
+	info_local->buflen = get_buflen(buf);
+
+	if ((ret = parse_value(buf, floatval, info_local)) != SR_OK) {
+		sr_dbg("Error parsing value: %d.", ret);
+		return ret;
+	}
+
+	parse_flags(buf, info_local);
+	handle_flags(analog, floatval, info_local);
+
+	return SR_OK;
+}
diff --git a/src/scpi.h b/src/scpi.h
new file mode 100644
index 0000000..c70bd93
--- /dev/null
+++ b/src/scpi.h
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Bert Vermeulen <bert at 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/>.
+ */
+
+/** @file
+  * @internal
+  */
+
+#ifndef LIBSIGROK_SCPI_H
+#define LIBSIGROK_SCPI_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define SCPI_CMD_IDN "*IDN?"
+#define SCPI_CMD_OPC "*OPC?"
+
+enum {
+	SCPI_CMD_SET_TRIGGER_SOURCE = 1,
+	SCPI_CMD_SET_TIMEBASE,
+	SCPI_CMD_SET_VERTICAL_DIV,
+	SCPI_CMD_SET_TRIGGER_SLOPE,
+	SCPI_CMD_SET_COUPLING,
+	SCPI_CMD_SET_HORIZ_TRIGGERPOS,
+	SCPI_CMD_GET_ANALOG_CHAN_STATE,
+	SCPI_CMD_GET_DIG_CHAN_STATE,
+	SCPI_CMD_GET_TIMEBASE,
+	SCPI_CMD_GET_VERTICAL_DIV,
+	SCPI_CMD_GET_VERTICAL_OFFSET,
+	SCPI_CMD_GET_TRIGGER_SOURCE,
+	SCPI_CMD_GET_HORIZ_TRIGGERPOS,
+	SCPI_CMD_GET_TRIGGER_SLOPE,
+	SCPI_CMD_GET_COUPLING,
+	SCPI_CMD_SET_ANALOG_CHAN_STATE,
+	SCPI_CMD_SET_DIG_CHAN_STATE,
+	SCPI_CMD_GET_DIG_POD_STATE,
+	SCPI_CMD_SET_DIG_POD_STATE,
+	SCPI_CMD_GET_ANALOG_DATA,
+	SCPI_CMD_GET_DIG_DATA,
+	SCPI_CMD_GET_SAMPLE_RATE,
+	SCPI_CMD_GET_SAMPLE_RATE_LIVE,
+};
+
+struct scpi_command {
+	int command;
+	const char *string;
+};
+
+struct sr_scpi_hw_info {
+	char *manufacturer;
+	char *model;
+	char *serial_number;
+	char *firmware_version;
+};
+
+struct sr_scpi_dev_inst {
+	const char *name;
+	const char *prefix;
+	int priv_size;
+	GSList *(*scan)(struct drv_context *drvc);
+	int (*dev_inst_new)(void *priv, struct drv_context *drvc,
+		const char *resource, char **params, const char *serialcomm);
+	int (*open)(struct sr_scpi_dev_inst *scpi);
+	int (*source_add)(struct sr_session *session, void *priv, int events,
+		int timeout, sr_receive_data_callback cb, void *cb_data);
+	int (*source_remove)(struct sr_session *session, void *priv);
+	int (*send)(void *priv, const char *command);
+	int (*read_begin)(void *priv);
+	int (*read_data)(void *priv, char *buf, int maxlen);
+	int (*read_complete)(void *priv);
+	int (*close)(struct sr_scpi_dev_inst *scpi);
+	void (*free)(void *priv);
+	unsigned int read_timeout_ms;
+	void *priv;
+	/* Only used for quirk workarounds, notably the Rigol DS1000 series. */
+	uint64_t firmware_version;
+};
+
+SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
+		struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi));
+SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
+		const char *resource, const char *serialcomm);
+SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_source_add(struct sr_session *session,
+		struct sr_scpi_dev_inst *scpi, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data);
+SR_PRIV int sr_scpi_source_remove(struct sr_session *session,
+		struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi,
+		const char *format, ...);
+SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi,
+		const char *format, va_list args);
+SR_PRIV int sr_scpi_read_begin(struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen);
+SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi);
+SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi);
+
+SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
+			const char *command, char **scpi_response);
+SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
+			const char *command, gboolean *scpi_response);
+SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
+			const char *command, int *scpi_response);
+SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
+			const char *command, float *scpi_response);
+SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
+			const char *command, double *scpi_response);
+SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi);
+SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
+			const char *command, GArray **scpi_response);
+SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
+			const char *command, GArray **scpi_response);
+SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
+			struct sr_scpi_hw_info **scpi_response);
+SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info);
+
+SR_PRIV const char *sr_vendor_alias(const char *raw_vendor);
+SR_PRIV const char *scpi_cmd_get(const struct scpi_command *cmdtable, int command);
+SR_PRIV int scpi_cmd(const struct sr_dev_inst *sdi,
+		const struct scpi_command *cmdtable, int command, ...);
+SR_PRIV int scpi_cmd_resp(const struct sr_dev_inst *sdi,
+		const struct scpi_command *cmdtable,
+		GVariant **gvar, const GVariantType *gvtype, int command, ...);
+
+#endif
diff --git a/src/scpi/helpers.c b/src/scpi/helpers.c
new file mode 100644
index 0000000..dc19c3a
--- /dev/null
+++ b/src/scpi/helpers.c
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <strings.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+
+#define LOG_PREFIX "scpi/helpers"
+
+static const char *scpi_vendors[][2] = {
+	{ "HEWLETT-PACKARD", "HP" },
+	{ "Agilent Technologies", "Agilent" },
+	{ "RIGOL TECHNOLOGIES", "Rigol" },
+	{ "PHILIPS", "Philips" },
+	{ "CHROMA", "Chroma" },
+	{ "Chroma ATE", "Chroma" },
+};
+
+SR_PRIV const char *sr_vendor_alias(const char *raw_vendor)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(scpi_vendors); i++) {
+		if (!g_ascii_strcasecmp(raw_vendor, scpi_vendors[i][0]))
+			return scpi_vendors[i][1];
+	}
+
+	return raw_vendor;
+}
+
+SR_PRIV const char *scpi_cmd_get(const struct scpi_command *cmdtable, int command)
+{
+	unsigned int i;
+	const char *cmd;
+
+	if (!cmdtable)
+		return NULL;
+
+	cmd = NULL;
+	for (i = 0; cmdtable[i].string; i++) {
+		if (cmdtable[i].command == command) {
+			cmd = cmdtable[i].string;
+			break;
+		}
+	}
+
+	return cmd;
+}
+
+SR_PRIV int scpi_cmd(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable,
+		int command, ...)
+{
+	struct sr_scpi_dev_inst *scpi;
+	va_list args;
+	int ret;
+	const char *cmd;
+
+	if (!(cmd = scpi_cmd_get(cmdtable, command))) {
+		/* Device does not implement this command, that's OK. */
+		return SR_OK;
+	}
+
+	scpi = sdi->conn;
+	va_start(args, command);
+	ret = sr_scpi_send_variadic(scpi, cmd, args);
+	va_end(args);
+
+	return ret;
+}
+
+SR_PRIV int scpi_cmd_resp(const struct sr_dev_inst *sdi, const struct scpi_command *cmdtable,
+		GVariant **gvar, const GVariantType *gvtype, int command, ...)
+{
+	struct sr_scpi_dev_inst *scpi;
+	va_list args;
+	double d;
+	int ret;
+	char *s;
+	const char *cmd;
+
+	if (!(cmd = scpi_cmd_get(cmdtable, command))) {
+		/* Device does not implement this command. */
+		return SR_ERR_NA;
+	}
+
+	scpi = sdi->conn;
+	va_start(args, command);
+	ret = sr_scpi_send_variadic(scpi, cmd, args);
+	va_end(args);
+	if (ret != SR_OK)
+		return ret;
+
+	/* Straight SCPI getters to GVariant types. */
+	if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_BOOLEAN)) {
+		if ((ret = sr_scpi_get_string(scpi, NULL, &s)) != SR_OK)
+			return ret;
+		if (!g_ascii_strcasecmp(s, "ON") || !g_ascii_strcasecmp(s, "1")
+				|| !g_ascii_strcasecmp(s, "YES"))
+			*gvar = g_variant_new_boolean(TRUE);
+		else if (!g_ascii_strcasecmp(s, "OFF") || !g_ascii_strcasecmp(s, "0")
+				|| !g_ascii_strcasecmp(s, "NO"))
+			*gvar = g_variant_new_boolean(FALSE);
+		else
+			ret = SR_ERR;
+		g_free(s);
+	} else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_DOUBLE)) {
+		if ((ret = sr_scpi_get_double(scpi, NULL, &d)) == SR_OK)
+			*gvar = g_variant_new_double(d);
+	} else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_STRING)) {
+		if ((ret = sr_scpi_get_string(scpi, NULL, &s)) == SR_OK)
+			*gvar = g_variant_new_string(s);
+	} else {
+		sr_err("Unable to convert to desired GVariant type.");
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
diff --git a/hardware/common/scpi.c b/src/scpi/scpi.c
similarity index 80%
rename from hardware/common/scpi.c
rename to src/scpi/scpi.c
index a3101a9..fc7e993 100644
--- a/hardware/common/scpi.c
+++ b/src/scpi/scpi.c
@@ -17,16 +17,17 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
+#include <config.h>
 #include <glib.h>
 #include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
 
 #define LOG_PREFIX "scpi"
 
 #define SCPI_READ_RETRIES 100
-#define SCPI_READ_RETRY_TIMEOUT 10000
+#define SCPI_READ_RETRY_TIMEOUT_US (10 * 1000)
 
 /**
  * Parse a string representation of a boolean-like value into a gboolean.
@@ -71,6 +72,7 @@ SR_PRIV extern const struct sr_scpi_dev_inst scpi_tcp_rigol_dev;
 SR_PRIV extern const struct sr_scpi_dev_inst scpi_usbtmc_libusb_dev;
 SR_PRIV extern const struct sr_scpi_dev_inst scpi_vxi_dev;
 SR_PRIV extern const struct sr_scpi_dev_inst scpi_visa_dev;
+SR_PRIV extern const struct sr_scpi_dev_inst scpi_libgpib_dev;
 
 static const struct sr_scpi_dev_inst *scpi_devs[] = {
 	&scpi_tcp_raw_dev,
@@ -84,12 +86,15 @@ static const struct sr_scpi_dev_inst *scpi_devs[] = {
 #ifdef HAVE_LIBREVISA
 	&scpi_visa_dev,
 #endif
+#ifdef HAVE_LIBGPIB
+	&scpi_libgpib_dev,
+#endif
 #ifdef HAVE_LIBSERIALPORT
 	&scpi_serial_dev,  /* must be last as it matches any resource */
 #endif
 };
 
-static GSList *sr_scpi_scan_resource(struct drv_context *drvc,
+static struct sr_dev_inst *sr_scpi_scan_resource(struct drv_context *drvc,
 		const char *resource, const char *serialcomm,
 		struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi))
 {
@@ -105,18 +110,23 @@ static GSList *sr_scpi_scan_resource(struct drv_context *drvc,
 		return NULL;
 	};
 
-	if ((sdi = probe_device(scpi)))
-		return g_slist_append(NULL, sdi);
+	sdi = probe_device(scpi);
 
 	sr_scpi_close(scpi);
-	sr_scpi_free(scpi);
-	return NULL;
+
+	if (sdi)
+		sdi->status = SR_ST_INACTIVE;
+	else
+		sr_scpi_free(scpi);
+
+	return sdi;
 }
 
 SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
 		struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi))
 {
-	GSList *resources, *l, *d, *devices = NULL;
+	GSList *resources, *l, *devices;
+	struct sr_dev_inst *sdi;
 	const char *resource = NULL;
 	const char *serialcomm = NULL;
 	gchar **res;
@@ -134,6 +144,7 @@ SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
 		}
 	}
 
+	devices = NULL;
 	for (i = 0; i < ARRAY_SIZE(scpi_devs); i++) {
 		if ((resource && strcmp(resource, scpi_devs[i]->prefix))
 		    || !scpi_devs[i]->scan)
@@ -141,22 +152,25 @@ SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
 		resources = scpi_devs[i]->scan(drvc);
 		for (l = resources; l; l = l->next) {
 			res = g_strsplit(l->data, ":", 2);
-			if (res[0] && (d = sr_scpi_scan_resource(drvc, res[0],
-			               serialcomm ? serialcomm : res[1], probe_device)))
-				devices = g_slist_concat(devices, d);
+			if (res[0] && (sdi = sr_scpi_scan_resource(drvc, res[0],
+			               serialcomm ? serialcomm : res[1], probe_device))) {
+				devices = g_slist_append(devices, sdi);
+				sdi->connection_id = g_strdup(l->data);
+			}
 			g_strfreev(res);
 		}
 		g_slist_free_full(resources, g_free);
 	}
 
-	if (!devices && resource)
-		devices = sr_scpi_scan_resource(drvc, resource, serialcomm,
-		                                probe_device);
+	if (!devices && resource) {
+		sdi = sr_scpi_scan_resource(drvc, resource, serialcomm, probe_device);
+		if (sdi)
+			devices = g_slist_append(NULL, sdi);
+	}
 
 	/* Tack a copy of the newly found devices onto the driver list. */
 	if (devices)
-		drvc->instances = g_slist_concat(drvc->instances,
-		                                 g_slist_copy(devices));
+		drvc->instances = g_slist_concat(drvc->instances, g_slist_copy(devices));
 
 	return devices;
 }
@@ -176,6 +190,7 @@ SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
 			scpi = g_malloc(sizeof(*scpi));
 			*scpi = *scpi_dev;
 			scpi->priv = g_malloc0(scpi->priv_size);
+			scpi->read_timeout_ms = 1000;
 			params = g_strsplit(resource, "/", 0);
 			if (scpi->dev_inst_new(scpi->priv, drvc, resource,
 			                       params, serialcomm) != SR_OK) {
@@ -199,12 +214,13 @@ SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc,
  */
 SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi)
 {
-	return scpi->open(scpi->priv);
+	return scpi->open(scpi);
 }
 
 /**
  * Add an event source for an SCPI device.
  *
+ * @param session The session to add the event source to.
  * @param scpi Previously initialized SCPI device structure.
  * @param events Events to check for.
  * @param timeout Max time to wait before the callback is called, ignored if 0.
@@ -214,24 +230,27 @@ SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi)
  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
  *         SR_ERR_MALLOC upon memory allocation errors.
  */
-SR_PRIV int sr_scpi_source_add(struct sr_scpi_dev_inst *scpi, int events,
-		int timeout, sr_receive_data_callback cb, void *cb_data)
+SR_PRIV int sr_scpi_source_add(struct sr_session *session,
+		struct sr_scpi_dev_inst *scpi, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data)
 {
-	return scpi->source_add(scpi->priv, events, timeout, cb, cb_data);
+	return scpi->source_add(session, scpi->priv, events, timeout, cb, cb_data);
 }
 
 /**
  * Remove event source for an SCPI device.
  *
+ * @param session The session to remove the event source from.
  * @param scpi Previously initialized SCPI device structure.
  *
  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
  *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
  *         internal errors.
  */
-SR_PRIV int sr_scpi_source_remove(struct sr_scpi_dev_inst *scpi)
+SR_PRIV int sr_scpi_source_remove(struct sr_session *session,
+		struct sr_scpi_dev_inst *scpi)
 {
-	return scpi->source_remove(scpi->priv);
+	return scpi->source_remove(session, scpi->priv);
 }
 
 /**
@@ -337,7 +356,7 @@ SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi)
  */
 SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi)
 {
-	return scpi->close(scpi->priv);
+	return scpi->close(scpi);
 }
 
 /**
@@ -361,7 +380,7 @@ SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi)
  * @param command The SCPI command to send to the device (can be NULL).
  * @param scpi_response Pointer where to store the SCPI response.
  *
- * @return SR_OK on success, SR_ERR on failure.
+ * @return SR_OK on success, SR_ERR* on failure.
  */
 SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
 			       const char *command, char **scpi_response)
@@ -369,6 +388,8 @@ SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
 	char buf[256];
 	int len;
 	GString *response;
+	gint64 laststart;
+	unsigned int elapsed_ms;
 
 	if (command)
 		if (sr_scpi_send(scpi, command) != SR_OK)
@@ -377,6 +398,8 @@ SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
 	if (sr_scpi_read_begin(scpi) != SR_OK)
 		return SR_ERR;
 
+	laststart = g_get_monotonic_time();
+
 	response = g_string_new("");
 
 	*scpi_response = NULL;
@@ -384,20 +407,33 @@ SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
 	while (!sr_scpi_read_complete(scpi)) {
 		len = sr_scpi_read_data(scpi, buf, sizeof(buf));
 		if (len < 0) {
+			sr_err("Incompletely read SCPI response.");
 			g_string_free(response, TRUE);
 			return SR_ERR;
+		} else if (len > 0) {
+		        laststart = g_get_monotonic_time();
 		}
 		g_string_append_len(response, buf, len);
+		elapsed_ms = (g_get_monotonic_time() - laststart) / 1000;
+		if (elapsed_ms >= scpi->read_timeout_ms) {
+			sr_err("Timed out waiting for SCPI response.");
+			g_string_free(response, TRUE);
+			return SR_ERR;
+		}
 	}
 
 	/* Get rid of trailing linefeed if present */
 	if (response->len >= 1 && response->str[response->len - 1] == '\n')
 		g_string_truncate(response, response->len - 1);
 
-	*scpi_response = response->str;
-	g_string_free(response, FALSE);
+	/* Get rid of trailing carriage return if present */
+	if (response->len >= 1 && response->str[response->len - 1] == '\r')
+		g_string_truncate(response, response->len - 1);
+
+	sr_spew("Got response: '%.70s', length %" G_GSIZE_FORMAT ".",
+		response->str, response->len);
 
-	sr_spew("Got response: '%.70s'.", *scpi_response);
+	*scpi_response = g_string_free(response, FALSE);
 
 	return SR_OK;
 }
@@ -410,7 +446,7 @@ SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi,
  * @param command The SCPI command to send to the device (can be NULL).
  * @param scpi_response Pointer where to store the parsed result.
  *
- * @return SR_OK on success, SR_ERR on failure.
+ * @return SR_OK on success, SR_ERR* on failure.
  */
 SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
 			     const char *command, gboolean *scpi_response)
@@ -420,14 +456,14 @@ SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
 
 	response = NULL;
 
-	if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
-		if (!response)
-			return SR_ERR;
+	ret = sr_scpi_get_string(scpi, command, &response);
+	if (ret != SR_OK && !response)
+		return ret;
 
 	if (parse_strict_bool(response, scpi_response) == SR_OK)
 		ret = SR_OK;
 	else
-		ret = SR_ERR;
+		ret = SR_ERR_DATA;
 
 	g_free(response);
 
@@ -442,7 +478,7 @@ SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi,
  * @param command The SCPI command to send to the device (can be NULL).
  * @param scpi_response Pointer where to store the parsed result.
  *
- * @return SR_OK on success, SR_ERR on failure.
+ * @return SR_OK on success, SR_ERR* on failure.
  */
 SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
 			    const char *command, int *scpi_response)
@@ -452,14 +488,14 @@ SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
 
 	response = NULL;
 
-	if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
-		if (!response)
-			return SR_ERR;
+	ret = sr_scpi_get_string(scpi, command, &response);
+	if (ret != SR_OK && !response)
+		return ret;
 
 	if (sr_atoi(response, scpi_response) == SR_OK)
 		ret = SR_OK;
 	else
-		ret = SR_ERR;
+		ret = SR_ERR_DATA;
 
 	g_free(response);
 
@@ -474,7 +510,7 @@ SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi,
  * @param command The SCPI command to send to the device (can be NULL).
  * @param scpi_response Pointer where to store the parsed result.
  *
- * @return SR_OK on success, SR_ERR on failure.
+ * @return SR_OK on success, SR_ERR* on failure.
  */
 SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
 			      const char *command, float *scpi_response)
@@ -484,14 +520,14 @@ SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
 
 	response = NULL;
 
-	if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
-		if (!response)
-			return SR_ERR;
+	ret = sr_scpi_get_string(scpi, command, &response);
+	if (ret != SR_OK && !response)
+		return ret;
 
 	if (sr_atof_ascii(response, scpi_response) == SR_OK)
 		ret = SR_OK;
 	else
-		ret = SR_ERR;
+		ret = SR_ERR_DATA;
 
 	g_free(response);
 
@@ -506,7 +542,7 @@ SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi,
  * @param command The SCPI command to send to the device (can be NULL).
  * @param scpi_response Pointer where to store the parsed result.
  *
- * @return SR_OK on success, SR_ERR on failure.
+ * @return SR_OK on success, SR_ERR* on failure.
  */
 SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
 			       const char *command, double *scpi_response)
@@ -516,14 +552,14 @@ SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
 
 	response = NULL;
 
-	if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
-		if (!response)
-			return SR_ERR;
+	ret = sr_scpi_get_string(scpi, command, &response);
+	if (ret != SR_OK && !response)
+		return ret;
 
 	if (sr_atod(response, scpi_response) == SR_OK)
 		ret = SR_OK;
 	else
-		ret = SR_ERR;
+		ret = SR_ERR_DATA;
 
 	g_free(response);
 
@@ -536,18 +572,18 @@ SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi,
  *
  * @param scpi Previously initialised SCPI device structure.
  *
- * @return SR_OK on success, SR_ERR on failure.
+ * @return SR_OK on success, SR_ERR* on failure.
  */
 SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi)
 {
 	unsigned int i;
 	gboolean opc;
 
-	for (i = 0; i < SCPI_READ_RETRIES; ++i) {
+	for (i = 0; i < SCPI_READ_RETRIES; i++) {
 		sr_scpi_get_bool(scpi, SCPI_CMD_OPC, &opc);
 		if (opc)
 			return SR_OK;
-		g_usleep(SCPI_READ_RETRY_TIMEOUT);
+		g_usleep(SCPI_READ_RETRY_TIMEOUT_US);
 	}
 
 	return SR_ERR;
@@ -561,7 +597,7 @@ SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi)
  * @param command The SCPI command to send to the device (can be NULL).
  * @param scpi_response Pointer where to store the parsed result.
  *
- * @return SR_OK upon successfully parsing all values, SR_ERR upon a parsing
+ * @return SR_OK upon successfully parsing all values, SR_ERR* upon a parsing
  *         error or upon no response. The allocated response must be freed by
  *         the caller in the case of an SR_OK as well as in the case of
  *         parsing error.
@@ -575,13 +611,12 @@ SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
 	gchar **ptr, **tokens;
 	GArray *response_array;
 
-	ret = SR_OK;
 	response = NULL;
 	tokens = NULL;
 
-	if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
-		if (!response)
-			return SR_ERR;
+	ret = sr_scpi_get_string(scpi, command, &response);
+	if (ret != SR_OK && !response)
+		return ret;
 
 	tokens = g_strsplit(response, ",", 0);
 	ptr = tokens;
@@ -593,17 +628,17 @@ SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
 			response_array = g_array_append_val(response_array,
 							    tmp);
 		else
-			ret = SR_ERR;
+			ret = SR_ERR_DATA;
 
 		ptr++;
 	}
 	g_strfreev(tokens);
 	g_free(response);
 
-	if (ret == SR_ERR && response_array->len == 0) {
+	if (ret != SR_OK && response_array->len == 0) {
 		g_array_free(response_array, TRUE);
 		*scpi_response = NULL;
-		return SR_ERR;
+		return SR_ERR_DATA;
 	}
 
 	*scpi_response = response_array;
@@ -619,7 +654,7 @@ SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi,
  * @param command The SCPI command to send to the device (can be NULL).
  * @param scpi_response Pointer where to store the parsed result.
  *
- * @return SR_OK upon successfully parsing all values, SR_ERR upon a parsing
+ * @return SR_OK upon successfully parsing all values, SR_ERR* upon a parsing
  *         error or upon no response. The allocated response must be freed by
  *         the caller in the case of an SR_OK as well as in the case of
  *         parsing error.
@@ -632,13 +667,12 @@ SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
 	gchar **ptr, **tokens;
 	GArray *response_array;
 
-	ret = SR_OK;
 	response = NULL;
 	tokens = NULL;
 
-	if (sr_scpi_get_string(scpi, command, &response) != SR_OK)
-		if (!response)
-			return SR_ERR;
+	ret = sr_scpi_get_string(scpi, command, &response);
+	if (ret != SR_OK && !response)
+		return ret;
 
 	tokens = g_strsplit(response, ",", 0);
 	ptr = tokens;
@@ -650,7 +684,7 @@ SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
 			response_array = g_array_append_val(response_array,
 							    tmp);
 		else
-			ret = SR_ERR;
+			ret = SR_ERR_DATA;
 
 		ptr++;
 	}
@@ -660,7 +694,7 @@ SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
 	if (response_array->len == 0) {
 		g_array_free(response_array, TRUE);
 		*scpi_response = NULL;
-		return SR_ERR;
+		return SR_ERR_DATA;
 	}
 
 	*scpi_response = response_array;
@@ -677,12 +711,12 @@ SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi,
  * @param scpi Previously initialised SCPI device structure.
  * @param scpi_response Pointer where to store the hw_info structure.
  *
- * @return SR_OK upon success, SR_ERR on failure.
+ * @return SR_OK upon success, SR_ERR* on failure.
  */
 SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
 			      struct sr_scpi_hw_info **scpi_response)
 {
-	int num_tokens;
+	int num_tokens, ret;
 	char *response;
 	gchar **tokens;
 	struct sr_scpi_hw_info *hw_info;
@@ -690,9 +724,9 @@ SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
 	response = NULL;
 	tokens = NULL;
 
-	if (sr_scpi_get_string(scpi, SCPI_CMD_IDN, &response) != SR_OK)
-		if (!response)
-			return SR_ERR;
+	ret = sr_scpi_get_string(scpi, SCPI_CMD_IDN, &response);
+	if (ret != SR_OK && !response)
+		return ret;
 
 	sr_info("Got IDN string: '%s'", response);
 
@@ -705,24 +739,19 @@ SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi,
 
 	for (num_tokens = 0; tokens[num_tokens] != NULL; num_tokens++);
 
-	if (num_tokens != 4) {
+	if (num_tokens < 4) {
 		sr_dbg("IDN response not according to spec: %80.s.", response);
 		g_strfreev(tokens);
 		g_free(response);
-		return SR_ERR;
+		return SR_ERR_DATA;
 	}
 	g_free(response);
 
-	hw_info = g_try_malloc(sizeof(struct sr_scpi_hw_info));
-	if (!hw_info) {
-		g_strfreev(tokens);
-		return SR_ERR_MALLOC;
-	}
-
-	hw_info->manufacturer = g_strdup(tokens[0]);
-	hw_info->model = g_strdup(tokens[1]);
-	hw_info->serial_number = g_strdup(tokens[2]);
-	hw_info->firmware_version = g_strdup(tokens[3]);
+	hw_info = g_malloc0(sizeof(struct sr_scpi_hw_info));
+	hw_info->manufacturer = g_strstrip(g_strdup(tokens[0]));
+	hw_info->model = g_strstrip(g_strdup(tokens[1]));
+	hw_info->serial_number = g_strstrip(g_strdup(tokens[2]));
+	hw_info->firmware_version = g_strstrip(g_strdup(tokens[3]));
 
 	g_strfreev(tokens);
 
diff --git a/src/scpi/scpi_libgpib.c b/src/scpi/scpi_libgpib.c
new file mode 100644
index 0000000..7632f74
--- /dev/null
+++ b/src/scpi/scpi_libgpib.c
@@ -0,0 +1,167 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Martin Ling <martin-sigrok at 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 <config.h>
+#include <gpib/ib.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+
+#define LOG_PREFIX "scpi_gpib"
+
+struct scpi_gpib {
+	char *name;
+	int descriptor;
+	int read_started;
+};
+
+static int scpi_gpib_dev_inst_new(void *priv, struct drv_context *drvc,
+		const char *resource, char **params, const char *serialcomm)
+{
+	struct scpi_gpib *gscpi = priv;
+
+	(void)drvc;
+	(void)resource;
+	(void)serialcomm;
+
+	if (!params || !params[1])
+			return SR_ERR;
+
+	gscpi->name = g_strdup(params[1]);
+
+	return SR_OK;
+}
+
+static int scpi_gpib_open(struct sr_scpi_dev_inst *scpi)
+{
+	struct scpi_gpib *gscpi = scpi->priv;
+
+	if ((gscpi->descriptor = ibfind(gscpi->name)) < 0)
+		return SR_ERR;
+
+	return SR_OK;
+}
+
+static int scpi_gpib_source_add(struct sr_session *session, void *priv,
+		int events, int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+	(void) priv;
+
+	/* Hook up a dummy handler to receive data from the device. */
+	return sr_session_source_add(session, -1, events, timeout, cb, cb_data);
+}
+
+static int scpi_gpib_source_remove(struct sr_session *session, void *priv)
+{
+	(void) priv;
+
+	return sr_session_source_remove(session, -1);
+}
+
+static int scpi_gpib_send(void *priv, const char *command)
+{
+	struct scpi_gpib *gscpi = priv;
+	int len = strlen(command);
+
+	ibwrt(gscpi->descriptor, command, len);
+
+	if (ibsta & ERR)
+	{
+		sr_err("Error while sending SCPI command: '%s': iberr = %d.",
+				command, iberr);
+		return SR_ERR;
+	}
+
+	if (ibcnt < len)
+	{
+		sr_err("Failed to send all of SCPI command: '%s': "
+				"len = %d, ibcnt = %d.", command, len, ibcnt);
+		return SR_ERR;
+	}
+
+	sr_spew("Successfully sent SCPI command: '%s'.", command);
+
+	return SR_OK;
+}
+
+static int scpi_gpib_read_begin(void *priv)
+{
+	struct scpi_gpib *gscpi = priv;
+
+	gscpi->read_started = 0;
+
+	return SR_OK;
+}
+
+static int scpi_gpib_read_data(void *priv, char *buf, int maxlen)
+{
+	struct scpi_gpib *gscpi = priv;
+
+	ibrd(gscpi->descriptor, buf, maxlen);
+
+	if (ibsta & ERR)
+	{
+		sr_err("Error while reading SCPI response: iberr = %d.", iberr);
+		return SR_ERR;
+	}
+
+	gscpi->read_started = 1;
+
+	return ibcnt;
+}
+
+static int scpi_gpib_read_complete(void *priv)
+{
+	struct scpi_gpib *gscpi = priv;
+
+	return gscpi->read_started && (ibsta & END);
+}
+
+static int scpi_gpib_close(struct sr_scpi_dev_inst *scpi)
+{
+	struct scpi_gpib *gscpi = scpi->priv;
+
+	ibonl(gscpi->descriptor, 0);
+
+	return SR_OK;
+}
+
+static void scpi_gpib_free(void *priv)
+{
+	struct scpi_gpib *gscpi = priv;
+
+	g_free(gscpi->name);
+}
+
+SR_PRIV const struct sr_scpi_dev_inst scpi_libgpib_dev = {
+	.name = "GPIB",
+	.prefix = "libgpib",
+	.priv_size = sizeof(struct scpi_gpib),
+	.dev_inst_new = scpi_gpib_dev_inst_new,
+	.open = scpi_gpib_open,
+	.source_add = scpi_gpib_source_add,
+	.source_remove = scpi_gpib_source_remove,
+	.send = scpi_gpib_send,
+	.read_begin = scpi_gpib_read_begin,
+	.read_data = scpi_gpib_read_data,
+	.read_complete = scpi_gpib_read_complete,
+	.close = scpi_gpib_close,
+	.free = scpi_gpib_free,
+};
diff --git a/hardware/common/scpi_serial.c b/src/scpi/scpi_serial.c
similarity index 83%
rename from hardware/common/scpi_serial.c
rename to src/scpi/scpi_serial.c
index 7000962..ef507bb 100644
--- a/hardware/common/scpi_serial.c
+++ b/src/scpi/scpi_serial.c
@@ -18,12 +18,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
+#include <config.h>
 #include <glib.h>
 #include <stdlib.h>
 #include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
 
 #define LOG_PREFIX "scpi_serial"
 
@@ -36,7 +37,7 @@ struct scpi_serial {
 	size_t read;
 };
 
-static struct {
+static const struct {
 	uint16_t vendor_id;
 	uint16_t product_id;
 	const char *serialcomm;
@@ -54,8 +55,8 @@ static GSList *scpi_serial_scan(struct drv_context *drvc)
 	(void)drvc;
 
 	for (i = 0; i < ARRAY_SIZE(scpi_serial_usb_ids); i++) {
-		if ((l = sr_serial_find_usb(scpi_serial_usb_ids[i].vendor_id,
-		                            scpi_serial_usb_ids[i].product_id)) == NULL)
+		if (!(l = sr_serial_find_usb(scpi_serial_usb_ids[i].vendor_id,
+					scpi_serial_usb_ids[i].product_id)))
 			continue;
 		for (r = l; r; r = r->next) {
 			if (scpi_serial_usb_ids[i].serialcomm)
@@ -79,18 +80,17 @@ static int scpi_serial_dev_inst_new(void *priv, struct drv_context *drvc,
 	(void)drvc;
 	(void)params;
 
-	if (!(sscpi->serial = sr_serial_dev_inst_new(resource, serialcomm)))
-		return SR_ERR;
+	sscpi->serial = sr_serial_dev_inst_new(resource, serialcomm);
 
 	return SR_OK;
 }
 
-static int scpi_serial_open(void *priv)
+static int scpi_serial_open(struct sr_scpi_dev_inst *scpi)
 {
-	struct scpi_serial *sscpi = priv;
+	struct scpi_serial *sscpi = scpi->priv;
 	struct sr_serial_dev_inst *serial = sscpi->serial;
 
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return SR_ERR;
 
 	if (serial_flush(serial) != SR_OK)
@@ -102,21 +102,21 @@ static int scpi_serial_open(void *priv)
 	return SR_OK;
 }
 
-static int scpi_serial_source_add(void *priv, int events, int timeout,
-			sr_receive_data_callback cb, void *cb_data)
+static int scpi_serial_source_add(struct sr_session *session, void *priv,
+		int events, int timeout, sr_receive_data_callback cb, void *cb_data)
 {
 	struct scpi_serial *sscpi = priv;
 	struct sr_serial_dev_inst *serial = sscpi->serial;
 
-	return serial_source_add(serial, events, timeout, cb, cb_data);
+	return serial_source_add(session, serial, events, timeout, cb, cb_data);
 }
 
-static int scpi_serial_source_remove(void *priv)
+static int scpi_serial_source_remove(struct sr_session *session, void *priv)
 {
 	struct scpi_serial *sscpi = priv;
 	struct sr_serial_dev_inst *serial = sscpi->serial;
 
-	return serial_source_remove(serial);
+	return serial_source_remove(session, serial);
 }
 
 static int scpi_serial_send(void *priv, const char *command)
@@ -130,7 +130,8 @@ static int scpi_serial_send(void *priv, const char *command)
 	len = strlen(terminated_command);
 	written = 0;
 	while (written < len) {
-		result = serial_write(serial, terminated_command + written, len - written);
+		result = serial_write_nonblocking(serial,
+				terminated_command + written, len - written);
 		if (result < 0) {
 			sr_err("Error while sending SCPI command: '%s'.", command);
 			g_free(terminated_command);
@@ -162,7 +163,7 @@ static int scpi_serial_read_data(void *priv, char *buf, int maxlen)
 
 	/* Try to read new data into the buffer if there is space. */
 	if (len > 0) {
-		ret = serial_read(sscpi->serial, sscpi->buffer + sscpi->read,
+		ret = serial_read_nonblocking(sscpi->serial, sscpi->buffer + sscpi->count,
 				BUFFER_SIZE - sscpi->count);
 
 		if (ret < 0)
@@ -208,9 +209,9 @@ static int scpi_serial_read_complete(void *priv)
 	}
 }
 
-static int scpi_serial_close(void *priv)
+static int scpi_serial_close(struct sr_scpi_dev_inst *scpi)
 {
-	struct scpi_serial *sscpi = priv;
+	struct scpi_serial *sscpi = scpi->priv;
 
 	return serial_close(sscpi->serial);
 }
diff --git a/hardware/common/scpi_tcp.c b/src/scpi/scpi_tcp.c
similarity index 86%
rename from hardware/common/scpi_tcp.c
rename to src/scpi/scpi_tcp.c
index 8ac2200..6297468 100644
--- a/hardware/common/scpi_tcp.c
+++ b/src/scpi/scpi_tcp.c
@@ -17,15 +17,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #ifdef _WIN32
 #define _WIN32_WINNT 0x0501
 #include <winsock2.h>
 #include <ws2tcpip.h>
 #endif
-
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
 #include <glib.h>
 #include <string.h>
 #include <unistd.h>
@@ -36,6 +33,9 @@
 #include <netdb.h>
 #endif
 #include <errno.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
 
 #define LOG_PREFIX "scpi_tcp"
 
@@ -72,9 +72,9 @@ static int scpi_tcp_dev_inst_new(void *priv, struct drv_context *drvc,
 	return SR_OK;
 }
 
-static int scpi_tcp_open(void *priv)
+static int scpi_tcp_open(struct sr_scpi_dev_inst *scpi)
 {
-	struct scpi_tcp *tcp = priv;
+	struct scpi_tcp *tcp = scpi->priv;
 	struct addrinfo hints;
 	struct addrinfo *results, *res;
 	int err;
@@ -87,7 +87,7 @@ static int scpi_tcp_open(void *priv)
 	err = getaddrinfo(tcp->address, tcp->port, &hints, &results);
 
 	if (err) {
-		sr_err("Address lookup failed: %s:%d: %s", tcp->address, tcp->port,
+		sr_err("Address lookup failed: %s:%s: %s", tcp->address, tcp->port,
 			gai_strerror(err));
 		return SR_ERR;
 	}
@@ -108,26 +108,27 @@ static int scpi_tcp_open(void *priv)
 
 	if (tcp->socket < 0) {
 		sr_err("Failed to connect to %s:%s: %s", tcp->address, tcp->port,
-				strerror(errno));
+				g_strerror(errno));
 		return SR_ERR;
 	}
 
 	return SR_OK;
 }
 
-static int scpi_tcp_source_add(void *priv, int events, int timeout,
-			sr_receive_data_callback cb, void *cb_data)
+static int scpi_tcp_source_add(struct sr_session *session, void *priv,
+		int events, int timeout, sr_receive_data_callback cb, void *cb_data)
 {
 	struct scpi_tcp *tcp = priv;
 
-	return sr_source_add(tcp->socket, events, timeout, cb, cb_data);
+	return sr_session_source_add(session, tcp->socket, events, timeout,
+			cb, cb_data);
 }
 
-static int scpi_tcp_source_remove(void *priv)
+static int scpi_tcp_source_remove(struct sr_session *session, void *priv)
 {
 	struct scpi_tcp *tcp = priv;
 
-	return sr_source_remove(tcp->socket);
+	return sr_session_source_remove(session, tcp->socket);
 }
 
 static int scpi_tcp_send(void *priv, const char *command)
@@ -142,7 +143,7 @@ static int scpi_tcp_send(void *priv, const char *command)
 	g_free(terminated_command);
 
 	if (out < 0) {
-		sr_err("Send error: %s", strerror(errno));
+		sr_err("Send error: %s", g_strerror(errno));
 		return SR_ERR;
 	}
 
@@ -174,7 +175,7 @@ static int scpi_tcp_raw_read_data(void *priv, char *buf, int maxlen)
 	len = recv(tcp->socket, buf, maxlen, 0);
 
 	if (len < 0) {
-		sr_err("Receive error: %s", strerror(errno));
+		sr_err("Receive error: %s", g_strerror(errno));
 		return SR_ERR;
 	}
 
@@ -194,7 +195,7 @@ static int scpi_tcp_rigol_read_data(void *priv, char *buf, int maxlen)
 		len = recv(tcp->socket, tcp->length_buf + tcp->length_bytes_read,
 				LENGTH_BYTES - tcp->length_bytes_read, 0);
 		if (len < 0) {
-			sr_err("Receive error: %s", strerror(errno));
+			sr_err("Receive error: %s", g_strerror(errno));
 			return SR_ERR;
 		}
 
@@ -212,7 +213,7 @@ static int scpi_tcp_rigol_read_data(void *priv, char *buf, int maxlen)
 	len = recv(tcp->socket, buf, maxlen, 0);
 
 	if (len < 0) {
-		sr_err("Receive error: %s", strerror(errno));
+		sr_err("Receive error: %s", g_strerror(errno));
 		return SR_ERR;
 	}
 
@@ -229,9 +230,9 @@ static int scpi_tcp_read_complete(void *priv)
 			tcp->response_bytes_read >= tcp->response_length);
 }
 
-static int scpi_tcp_close(void *priv)
+static int scpi_tcp_close(struct sr_scpi_dev_inst *scpi)
 {
-	struct scpi_tcp *tcp = priv;
+	struct scpi_tcp *tcp = scpi->priv;
 
 	if (close(tcp->socket) < 0)
 		return SR_ERR;
diff --git a/hardware/common/scpi_usbtmc_libusb.c b/src/scpi/scpi_usbtmc_libusb.c
similarity index 78%
rename from hardware/common/scpi_usbtmc_libusb.c
rename to src/scpi/scpi_usbtmc_libusb.c
index 653a0d8..3dd622a 100644
--- a/hardware/common/scpi_usbtmc_libusb.c
+++ b/src/scpi/scpi_usbtmc_libusb.c
@@ -17,9 +17,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
+#include "scpi.h"
 
 #define LOG_PREFIX "scpi_usbtmc"
 
@@ -43,7 +45,6 @@ struct scpi_usbtmc_libusb {
 	int response_length;
 	int response_bytes_read;
 	int remaining_length;
-	int rigol_ds1000;
 };
 
 /* Some USBTMC-specific enums, as defined in the USBTMC standard. */
@@ -68,6 +69,9 @@ enum {
 	LOCAL_LOCKOUT               = 162,
 };
 
+/* USBTMC status codes */
+#define USBTMC_STATUS_SUCCESS      0x01
+
 /* USBTMC capabilities */
 #define USBTMC_INT_CAP_LISTEN_ONLY 0x01
 #define USBTMC_INT_CAP_TALK_ONLY   0x02
@@ -92,6 +96,19 @@ enum {
 #define EOM                0x01
 #define TERM_CHAR_ENABLED  0x02
 
+struct usbtmc_blacklist {
+	uint16_t vid;
+	uint16_t pid;
+};
+
+/* Devices that publish RL1 support, but don't support it. */
+static struct usbtmc_blacklist blacklist_remote[] = {
+	{ 0x1ab1, 0x0588 },  /* Rigol DS1000 series */
+	{ 0x1ab1, 0x04b0 },  /* Rigol DS2000 series */
+	{ 0x0957, 0x0588 },  /* Agilent DSO1000 series (rebadged Rigol DS1000) */
+	{ 0x0b21, 0xffff },  /* All Yokogawa devices */
+	ALL_ZERO
+};
 
 static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
 {
@@ -110,11 +127,7 @@ static GSList *scpi_usbtmc_libusb_scan(struct drv_context *drvc)
 		return NULL;
 	}
 	for (i = 0; devlist[i]; i++) {
-		if ((ret = libusb_get_device_descriptor(devlist[i], &des)) < 0) {
-			sr_err("Failed to get device descriptor: %s.",
-			       libusb_error_name(ret));
-			continue;
-		}
+		libusb_get_device_descriptor(devlist[i], &des);
 
 		for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
 			if ((ret = libusb_get_config_descriptor(devlist[i], confidx, &confdes)) < 0) {
@@ -174,16 +187,121 @@ static int scpi_usbtmc_libusb_dev_inst_new(void *priv, struct drv_context *drvc,
 	return SR_OK;
 }
 
-static int scpi_usbtmc_libusb_open(void *priv)
+static int check_usbtmc_blacklist(struct usbtmc_blacklist *blacklist,
+		uint16_t vid, uint16_t pid)
+{
+	int i;
+
+	for (i = 0; blacklist[i].vid; i++) {
+		if ((blacklist[i].vid == vid && blacklist[i].pid == 0xFFFF) ||
+			(blacklist[i].vid == vid && blacklist[i].pid == pid))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static int scpi_usbtmc_remote(struct scpi_usbtmc_libusb *uscpi)
+{
+	struct sr_usb_dev_inst *usb = uscpi->usb;
+	struct libusb_device *dev;
+	struct libusb_device_descriptor des;
+	int ret;
+	uint8_t status;
+
+	if (!(uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1))
+		return SR_OK;
+
+	dev = libusb_get_device(usb->devhdl);
+	libusb_get_device_descriptor(dev, &des);
+	if (check_usbtmc_blacklist(blacklist_remote, des.idVendor, des.idProduct))
+		return SR_OK;
+
+	sr_dbg("Locking out local control.");
+	ret = libusb_control_transfer(usb->devhdl,
+			LIBUSB_ENDPOINT_IN         |
+			LIBUSB_REQUEST_TYPE_CLASS  |
+			LIBUSB_RECIPIENT_INTERFACE,
+			REN_CONTROL, 1,
+			uscpi->interface,
+			&status, 1,
+			TRANSFER_TIMEOUT);
+	if (ret < 0 || status != USBTMC_STATUS_SUCCESS) {
+		if (ret < 0)
+			sr_dbg("Failed to enter REN state: %s.", libusb_error_name(ret));
+		else
+			sr_dbg("Failed to enter REN state: USBTMC status %d.", status);
+		return SR_ERR;
+	}
+
+	ret = libusb_control_transfer(usb->devhdl,
+			LIBUSB_ENDPOINT_IN         |
+			LIBUSB_REQUEST_TYPE_CLASS  |
+			LIBUSB_RECIPIENT_INTERFACE,
+			LOCAL_LOCKOUT, 1,
+			uscpi->interface,
+			&status, 1,
+			TRANSFER_TIMEOUT);
+	if (ret < 0 || status != USBTMC_STATUS_SUCCESS) {
+		if (ret < 0)
+			sr_dbg("Failed to enter local lockout state: %s.",
+					libusb_error_name(ret));
+		else
+			sr_dbg("Failed to enter local lockout state: USBTMC "
+					"status %d.", status);
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+static void scpi_usbtmc_local(struct scpi_usbtmc_libusb *uscpi)
 {
-	struct scpi_usbtmc_libusb *uscpi = priv;
+	struct sr_usb_dev_inst *usb = uscpi->usb;
+	struct libusb_device *dev;
+	struct libusb_device_descriptor des;
+	int ret;
+	uint8_t status;
+
+	if (!(uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1))
+		return;
+
+	dev = libusb_get_device(usb->devhdl);
+	libusb_get_device_descriptor(dev, &des);
+	if (check_usbtmc_blacklist(blacklist_remote, des.idVendor, des.idProduct))
+		return;
+
+	sr_dbg("Returning local control.");
+	ret = libusb_control_transfer(usb->devhdl,
+			LIBUSB_ENDPOINT_IN         |
+			LIBUSB_REQUEST_TYPE_CLASS  |
+			LIBUSB_RECIPIENT_INTERFACE,
+			GO_TO_LOCAL, 1,
+			uscpi->interface,
+			&status, 1,
+			TRANSFER_TIMEOUT);
+	if (ret < 0 || status != USBTMC_STATUS_SUCCESS) {
+		if (ret < 0)
+			sr_dbg("Failed to clear local lockout state: %s.",
+					libusb_error_name(ret));
+		else
+			sr_dbg("Failed to clear local lockout state: USBTMC "
+					"status %d.", status);
+	}
+
+	return;
+}
+
+static int scpi_usbtmc_libusb_open(struct sr_scpi_dev_inst *scpi)
+{
+	struct scpi_usbtmc_libusb *uscpi = scpi->priv;
 	struct sr_usb_dev_inst *usb = uscpi->usb;
 	struct libusb_device *dev;
 	struct libusb_device_descriptor des;
 	struct libusb_config_descriptor *confdes;
 	const struct libusb_interface_descriptor *intfdes;
 	const struct libusb_endpoint_descriptor *ep;
-	int confidx, intfidx, epidx, config = 0;
+	int confidx, intfidx, epidx, config = 0, current_config;
 	uint8_t capabilities[24];
 	int ret, found = 0;
 
@@ -194,11 +312,7 @@ static int scpi_usbtmc_libusb_open(void *priv)
 		return SR_ERR;
 
 	dev = libusb_get_device(usb->devhdl);
-	if ((ret = libusb_get_device_descriptor(dev, &des)) < 0) {
-		sr_err("Failed to get device descriptor: %s.",
-		       libusb_error_name(ret));
-		return SR_ERR;
-	}
+	libusb_get_device_descriptor(dev, &des);
 
 	for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
 		if ((ret = libusb_get_config_descriptor(dev, confidx, &confdes)) < 0) {
@@ -213,9 +327,8 @@ static int scpi_usbtmc_libusb_open(void *priv)
 			    intfdes->bInterfaceProtocol != USBTMC_USB488)
 				continue;
 			uscpi->interface = intfdes->bInterfaceNumber;
-			sr_dbg("Interface %d", uscpi->interface);
 			config = confdes->bConfigurationValue;
-			sr_dbg("Configuration %d", config);
+			sr_dbg("Interface %d configuration %d.", uscpi->interface, config);
 			for (epidx = 0; epidx < intfdes->bNumEndpoints; epidx++) {
 				ep = &intfdes->endpoint[epidx];
 				if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK &&
@@ -226,17 +339,15 @@ static int scpi_usbtmc_libusb_open(void *priv)
 				if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_BULK &&
 				    ep->bEndpointAddress & (LIBUSB_ENDPOINT_DIR_MASK)) {
 					uscpi->bulk_in_ep = ep->bEndpointAddress;
-					sr_dbg("Bulk IN EP %d", uscpi->bulk_in_ep);
+					sr_dbg("Bulk IN EP %d", uscpi->bulk_in_ep & 0x7f);
 				}
 				if (ep->bmAttributes == LIBUSB_TRANSFER_TYPE_INTERRUPT &&
 				    ep->bEndpointAddress & (LIBUSB_ENDPOINT_DIR_MASK)) {
 					uscpi->interrupt_ep = ep->bEndpointAddress;
-					sr_dbg("Interrupt EP %d", uscpi->interrupt_ep);
+					sr_dbg("Interrupt EP %d", uscpi->interrupt_ep & 0x7f);
 				}
 			}
 			found = 1;
-			uscpi->rigol_ds1000 = des.idVendor  == 0x1ab1 &&
-			                      des.idProduct == 0x0588;
 		}
 		libusb_free_config_descriptor(confdes);
 		if (found)
@@ -258,10 +369,13 @@ static int scpi_usbtmc_libusb_open(void *priv)
 		uscpi->detached_kernel_driver = 1;
 	}
 
-	if ((ret = libusb_set_configuration(usb->devhdl, config)) < 0) {
-		sr_err("Failed to set configuration: %s.",
-		       libusb_error_name(ret));
-		return SR_ERR;
+	if (libusb_get_configuration(usb->devhdl, &current_config) == 0
+	    && current_config != config) {
+		if ((ret = libusb_set_configuration(usb->devhdl, config)) < 0) {
+			sr_err("Failed to set configuration: %s.",
+			       libusb_error_name(ret));
+			return SR_ERR;
+		}
 	}
 
 	if ((ret = libusb_claim_interface(usb->devhdl, uscpi->interface)) < 0) {
@@ -270,24 +384,6 @@ static int scpi_usbtmc_libusb_open(void *priv)
 		return SR_ERR;
 	}
 
-	if (!uscpi->rigol_ds1000) {
-	if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_in_ep)) < 0) {
-		sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-		       uscpi->bulk_in_ep, libusb_error_name(ret));
-		return SR_ERR;
-	}
-	if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_out_ep)) < 0) {
-		sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-		       uscpi->bulk_out_ep, libusb_error_name(ret));
-		return SR_ERR;
-	}
-	if ((ret = libusb_clear_halt(usb->devhdl, uscpi->interrupt_ep)) < 0) {
-		sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-		       uscpi->interrupt_ep, libusb_error_name(ret));
-		return SR_ERR;
-	}
-	}
-
 	/* Get capabilities. */
 	ret = libusb_control_transfer(usb->devhdl,
 	                              LIBUSB_ENDPOINT_IN         |
@@ -313,21 +409,25 @@ static int scpi_usbtmc_libusb_open(void *priv)
 	       uscpi->usb488_dev_cap & USB488_DEV_CAP_RL1        ? "RL1"  : "RL0",
 	       uscpi->usb488_dev_cap & USB488_DEV_CAP_DT1        ? "DT1"  : "DT0");
 
+	scpi_usbtmc_remote(uscpi);
+
 	return SR_OK;
 }
 
-static int scpi_usbtmc_libusb_source_add(void *priv, int events, int timeout,
-			sr_receive_data_callback cb, void *cb_data)
+static int scpi_usbtmc_libusb_source_add(struct sr_session *session,
+		void *priv, int events, int timeout, sr_receive_data_callback cb,
+		void *cb_data)
 {
 	struct scpi_usbtmc_libusb *uscpi = priv;
 	(void)events;
-	return usb_source_add(uscpi->ctx, timeout, cb, cb_data);
+	return usb_source_add(session, uscpi->ctx, timeout, cb, cb_data);
 }
 
-static int scpi_usbtmc_libusb_source_remove(void *priv)
+static int scpi_usbtmc_libusb_source_remove(struct sr_session *session,
+		void *priv)
 {
 	struct scpi_usbtmc_libusb *uscpi = priv;
-	return usb_source_remove(uscpi->ctx);
+	return usb_source_remove(session, uscpi->ctx);
 }
 
 static void usbtmc_bulk_out_header_write(void *header, uint8_t MsgID,
@@ -520,26 +620,16 @@ static int scpi_usbtmc_libusb_read_complete(void *priv)
 	       uscpi->bulkin_attributes & EOM;
 }
 
-static int scpi_usbtmc_libusb_close(void *priv)
+static int scpi_usbtmc_libusb_close(struct sr_scpi_dev_inst *scpi)
 {
-	int ret;
-	struct scpi_usbtmc_libusb *uscpi = priv;
+	struct scpi_usbtmc_libusb *uscpi = scpi->priv;
 	struct sr_usb_dev_inst *usb = uscpi->usb;
+	int ret;
 
 	if (!usb->devhdl)
 		return SR_ERR;
 
-	if (!uscpi->rigol_ds1000) {
-	if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_in_ep)) < 0)
-		sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-		       uscpi->bulk_in_ep, libusb_error_name(ret));
-	if ((ret = libusb_clear_halt(usb->devhdl, uscpi->bulk_out_ep)) < 0)
-		sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-		       uscpi->bulk_out_ep, libusb_error_name(ret));
-	if ((ret = libusb_clear_halt(usb->devhdl, uscpi->interrupt_ep)) < 0)
-		sr_err("Failed to clear halt/stall condition for EP %d: %s.",
-		       uscpi->interrupt_ep, libusb_error_name(ret));
-	}
+	scpi_usbtmc_local(uscpi);
 
 	if ((ret = libusb_release_interface(usb->devhdl, uscpi->interface)) < 0)
 		sr_err("Failed to release interface: %s.",
@@ -553,8 +643,7 @@ static int scpi_usbtmc_libusb_close(void *priv)
 
 		uscpi->detached_kernel_driver = 0;
 	}
-	libusb_close(usb->devhdl);
-	usb->devhdl = NULL;
+	sr_usb_close(usb);
 
 	return SR_OK;
 }
diff --git a/hardware/common/scpi_visa.c b/src/scpi/scpi_visa.c
similarity index 85%
rename from hardware/common/scpi_visa.c
rename to src/scpi/scpi_visa.c
index b342a91..220ead4 100644
--- a/hardware/common/scpi_visa.c
+++ b/src/scpi/scpi_visa.c
@@ -17,11 +17,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
+#include <config.h>
 #include <visa.h>
 #include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
 
 #define LOG_PREFIX "scpi_visa"
 
@@ -50,9 +51,9 @@ static int scpi_visa_dev_inst_new(void *priv, struct drv_context *drvc,
 	return SR_OK;
 }
 
-static int scpi_visa_open(void *priv)
+static int scpi_visa_open(struct sr_scpi_dev_inst *scpi)
 {
-	struct scpi_visa *vscpi = priv;
+	struct scpi_visa *vscpi = scpi->priv;
 
 	if (viOpenDefaultRM(&vscpi->rmgr) != VI_SUCCESS) {
 		sr_err("Cannot open default resource manager.");
@@ -67,20 +68,20 @@ static int scpi_visa_open(void *priv)
 	return SR_OK;
 }
 
-static int scpi_visa_source_add(void *priv, int events, int timeout,
-			sr_receive_data_callback cb, void *cb_data)
+static int scpi_visa_source_add(struct sr_session *session, void *priv,
+		int events, int timeout, sr_receive_data_callback cb, void *cb_data)
 {
 	(void) priv;
 
 	/* Hook up a dummy handler to receive data from the device. */
-	return sr_source_add(-1, events, timeout, cb, cb_data);
+	return sr_session_source_add(session, -1, events, timeout, cb, cb_data);
 }
 
-static int scpi_visa_source_remove(void *priv)
+static int scpi_visa_source_remove(struct sr_session *session, void *priv)
 {
 	(void) priv;
 
-	return sr_source_remove(-1);
+	return sr_session_source_remove(session, -1);
 }
 
 static int scpi_visa_send(void *priv, const char *command)
@@ -139,9 +140,9 @@ static int scpi_visa_read_complete(void *priv)
 	return !(status & 16);
 }
 
-static int scpi_visa_close(void *priv)
+static int scpi_visa_close(struct sr_scpi_dev_inst *scpi)
 {
-	struct scpi_visa *vscpi = priv;
+	struct scpi_visa *vscpi = scpi->priv;
 
 	viClose(vscpi->vi);
 	viClose(vscpi->rmgr);
diff --git a/hardware/common/scpi_vxi.c b/src/scpi/scpi_vxi.c
similarity index 81%
rename from hardware/common/scpi_vxi.c
rename to src/scpi/scpi_vxi.c
index 639b3b5..8a99ec7 100644
--- a/hardware/common/scpi_vxi.c
+++ b/src/scpi/scpi_vxi.c
@@ -20,15 +20,16 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
+#include "vxi.h"
 #include <rpc/rpc.h>
 #include <string.h>
-
-#include "vxi.h"
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
+#include "scpi.h"
 
 #define LOG_PREFIX "scpi_vxi"
-#define VXI_DEFAULT_TIMEOUT  2000  /* in ms */
+#define VXI_DEFAULT_TIMEOUT_MS 2000
 
 struct scpi_vxi {
 	char *address;
@@ -59,14 +60,14 @@ static int scpi_vxi_dev_inst_new(void *priv, struct drv_context *drvc,
 	return SR_OK;
 }
 
-static int scpi_vxi_open(void *priv)
+static int scpi_vxi_open(struct sr_scpi_dev_inst *scpi)
 {
-	struct scpi_vxi *vxi = priv;
+	struct scpi_vxi *vxi = scpi->priv;
 	Create_LinkParms link_parms;
 	Create_LinkResp *link_resp;
 
 	vxi->client = clnt_create(vxi->address, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
-	if (vxi->client == NULL) {
+	if (!vxi->client) {
 		sr_err("Client creation failed for %s", vxi->address);
 		return SR_ERR;
 	}
@@ -74,8 +75,8 @@ static int scpi_vxi_open(void *priv)
 	/* Set link parameters */
 	link_parms.clientId = (long) vxi->client;
 	link_parms.lockDevice = 0;
-	link_parms.lock_timeout = VXI_DEFAULT_TIMEOUT;
-	link_parms.device = "inst0";
+	link_parms.lock_timeout = VXI_DEFAULT_TIMEOUT_MS;
+	link_parms.device = (char *)"inst0";
 
 	if (!(link_resp = create_link_1(&link_parms, vxi->client))) {
 		sr_err("Link creation failed for %s", vxi->address);
@@ -91,20 +92,20 @@ static int scpi_vxi_open(void *priv)
 	return SR_OK;
 }
 
-static int scpi_vxi_source_add(void *priv, int events, int timeout,
-			sr_receive_data_callback cb, void *cb_data)
+static int scpi_vxi_source_add(struct sr_session *session, void *priv,
+		int events, int timeout, sr_receive_data_callback cb, void *cb_data)
 {
 	(void)priv;
 
 	/* Hook up a dummy handler to receive data from the device. */
-	return sr_source_add(-1, events, timeout, cb, cb_data);
+	return sr_session_source_add(session, -1, events, timeout, cb, cb_data);
 }
 
-static int scpi_vxi_source_remove(void *priv)
+static int scpi_vxi_source_remove(struct sr_session *session, void *priv)
 {
 	(void)priv;
 
-	return sr_source_remove(-1);
+	return sr_session_source_remove(session, -1);
 }
 
 /* Operation Flags */
@@ -118,29 +119,29 @@ static int scpi_vxi_send(void *priv, const char *command)
 	Device_WriteResp *write_resp;
 	Device_WriteParms write_parms;
 	char *terminated_command;
-	unsigned int len;
+	unsigned long len;
 
 	terminated_command = g_strdup_printf("%s\r\n", command);
 	len = strlen(terminated_command);
 
 	write_parms.lid           = vxi->link;
-	write_parms.io_timeout    = VXI_DEFAULT_TIMEOUT;
-	write_parms.lock_timeout  = VXI_DEFAULT_TIMEOUT;
+	write_parms.io_timeout    = VXI_DEFAULT_TIMEOUT_MS;
+	write_parms.lock_timeout  = VXI_DEFAULT_TIMEOUT_MS;
 	write_parms.flags         = DF_END;
 	write_parms.data.data_len = MIN(len, vxi->max_send_size);
 	write_parms.data.data_val = terminated_command;
 
 	if (!(write_resp = device_write_1(&write_parms, vxi->client))
 	    || write_resp->error) {
-		sr_err("Device write failed for %s with error %d",
-		       vxi->address, write_resp->error);
+		sr_err("Device write failed for %s with error %ld",
+		       vxi->address, write_resp ? write_resp->error : 0);
 		return SR_ERR;
 	}
 
 	g_free(terminated_command);
 
 	if (write_resp->size < len)
-		sr_dbg("Only sent %d/%d bytes of SCPI command: '%s'.",
+		sr_dbg("Only sent %lu/%lu bytes of SCPI command: '%s'.",
 		       write_resp->size, len, command);
 	else
 		sr_spew("Successfully sent SCPI command: '%s'.", command);
@@ -169,16 +170,16 @@ static int scpi_vxi_read_data(void *priv, char *buf, int maxlen)
 	Device_ReadResp *read_resp;
 
 	read_parms.lid          = vxi->link;
-	read_parms.io_timeout   = VXI_DEFAULT_TIMEOUT;
-	read_parms.lock_timeout = VXI_DEFAULT_TIMEOUT;
+	read_parms.io_timeout   = VXI_DEFAULT_TIMEOUT_MS;
+	read_parms.lock_timeout = VXI_DEFAULT_TIMEOUT_MS;
 	read_parms.flags        = 0;
 	read_parms.termChar     = 0;
 	read_parms.requestSize  = maxlen;
 
 	if (!(read_resp = device_read_1(&read_parms, vxi->client))
 	    || read_resp->error) {
-		sr_err("Device read failed for %s with error %d",
-		       vxi->address, read_resp->error);
+		sr_err("Device read failed for %s with error %ld",
+		       vxi->address, read_resp ? read_resp->error : 0);
 		return SR_ERR;
 	}
 
@@ -194,9 +195,9 @@ static int scpi_vxi_read_complete(void *priv)
 	return vxi->read_complete;
 }
 
-static int scpi_vxi_close(void *priv)
+static int scpi_vxi_close(struct sr_scpi_dev_inst *scpi)
 {
-	struct scpi_vxi *vxi = priv;
+	struct scpi_vxi *vxi = scpi->priv;
 	Device_Error *dev_error;
 
 	if (!vxi->client)
diff --git a/hardware/common/vxi.h b/src/scpi/vxi.h
similarity index 99%
rename from hardware/common/vxi.h
rename to src/scpi/vxi.h
index 61a51f6..59ec4df 100644
--- a/hardware/common/vxi.h
+++ b/src/scpi/vxi.h
@@ -3,6 +3,8 @@
  * It was generated using rpcgen.
  */
 
+#undef _POSIX_C_SOURCE
+
 #ifndef _VXI_H_RPCGEN
 #define _VXI_H_RPCGEN
 
diff --git a/hardware/common/vxi_clnt.c b/src/scpi/vxi_clnt.c
similarity index 99%
rename from hardware/common/vxi_clnt.c
rename to src/scpi/vxi_clnt.c
index 55a72dc..a7c2446 100644
--- a/hardware/common/vxi_clnt.c
+++ b/src/scpi/vxi_clnt.c
@@ -3,8 +3,9 @@
  * It was generated using rpcgen.
  */
 
-#include <memory.h> /* for memset */
+#include <config.h>
 #include "vxi.h"
+#include <memory.h> /* for memset */
 
 /* Default timeout can be changed using clnt_control() */
 static struct timeval TIMEOUT = { 25, 0 };
diff --git a/hardware/common/vxi_xdr.c b/src/scpi/vxi_xdr.c
similarity index 99%
rename from hardware/common/vxi_xdr.c
rename to src/scpi/vxi_xdr.c
index bc7d79f..502769d 100644
--- a/hardware/common/vxi_xdr.c
+++ b/src/scpi/vxi_xdr.c
@@ -3,6 +3,7 @@
  * It was generated using rpcgen.
  */
 
+#include <config.h>
 #include "vxi.h"
 
 bool_t
diff --git a/hardware/common/serial.c b/src/serial.c
similarity index 63%
rename from hardware/common/serial.c
rename to src/serial.c
index e99b768..27244da 100644
--- a/hardware/common/serial.c
+++ b/src/serial.c
@@ -4,6 +4,7 @@
  * Copyright (C) 2010-2012 Bert Vermeulen <bert at biot.com>
  * Copyright (C) 2010-2012 Uwe Hermann <uwe at hermann-uwe.de>
  * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ * Copyright (C) 2014 Uffe Jakobsen <uffe at uffe.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
@@ -19,27 +20,50 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
 #include <stdlib.h>
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <libserialport.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
+#ifdef G_OS_WIN32
+#include <windows.h> /* for HANDLE */
+#endif
 
+/** @cond PRIVATE */
 #define LOG_PREFIX "serial"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Serial port handling.
+ */
+
+/**
+ * @defgroup grp_serial Serial port handling
+ *
+ * Serial port handling functions.
+ *
+ * @{
+ */
 
 /**
  * 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.
+ * @param[in] flags Flags to use when opening the serial port. Possible flags
+ *              include SERIAL_RDWR, SERIAL_RDONLY.
  *
  * 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.
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ *
+ * @private
  */
 SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags)
 {
@@ -61,8 +85,6 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags)
 	else if (flags & SERIAL_RDONLY)
 		sp_flags = SP_MODE_READ;
 
-	serial->nonblocking = (flags & SERIAL_NONBLOCK) ? 1 : 0;
-
 	ret = sp_open(serial->data, sp_flags);
 
 	switch (ret) {
@@ -88,7 +110,10 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags)
  *
  * @param serial Previously initialized serial port structure.
  *
- * @return SR_OK on success, SR_ERR on failure.
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ *
+ * @private
  */
 SR_PRIV int serial_close(struct sr_serial_dev_inst *serial)
 {
@@ -132,7 +157,10 @@ SR_PRIV int serial_close(struct sr_serial_dev_inst *serial)
  *
  * @param serial Previously initialized serial port structure.
  *
- * @return SR_OK on success, SR_ERR on failure.
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ *
+ * @private
  */
 SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial)
 {
@@ -168,8 +196,48 @@ SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial)
 	return SR_OK;
 }
 
+/**
+ * Drain serial port buffers.
+ *
+ * @param serial Previously initialized serial port structure.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR Failure.
+ *
+ * @private
+ */
+SR_PRIV int serial_drain(struct sr_serial_dev_inst *serial)
+{
+	int ret;
+	char *error;
+
+	if (!serial) {
+		sr_dbg("Invalid serial port.");
+		return SR_ERR;
+	}
+
+	if (!serial->data) {
+		sr_dbg("Cannot drain unopened serial port %s.", serial->port);
+		return SR_ERR;
+	}
+
+	sr_spew("Draining serial port %s.", serial->port);
+
+	ret = sp_drain(serial->data);
+
+	if (ret == SP_ERR_FAIL) {
+		error = sp_last_error_message();
+		sr_err("Error draining port (%d): %s.",
+			sp_last_error_code(), error);
+		sp_free_error_message(error);
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
 static int _serial_write(struct sr_serial_dev_inst *serial,
-		const void *buf, size_t count, int nonblocking)
+		const void *buf, size_t count, int nonblocking, unsigned int timeout_ms)
 {
 	ssize_t ret;
 	char *error;
@@ -187,7 +255,7 @@ static int _serial_write(struct sr_serial_dev_inst *serial,
 	if (nonblocking)
 		ret = sp_nonblocking_write(serial->data, buf, count);
 	else
-		ret = sp_blocking_write(serial->data, buf, count, 0);
+		ret = sp_blocking_write(serial->data, buf, count, timeout_ms);
 
 	switch (ret) {
 	case SP_ERR_ARG:
@@ -200,40 +268,53 @@ static int _serial_write(struct sr_serial_dev_inst *serial,
 		return SR_ERR;
 	}
 
-	sr_spew("Wrote %d/%d bytes.", ret, count);
+	sr_spew("Wrote %zd/%zu bytes.", ret, count);
 
 	return ret;
 }
 
 /**
- * Write a number of bytes to the specified serial port.
+ * Write a number of bytes to the specified serial port, blocking until finished.
  *
  * @param serial Previously initialized serial port structure.
- * @param buf Buffer containing the bytes to write.
- * @param count Number of bytes to write.
+ * @param[in] buf Buffer containing the bytes to write.
+ * @param[in] count Number of bytes to write.
+ * @param[in] timeout_ms Timeout in ms, or 0 for no timeout.
+ *
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR Other error.
+ * @retval other The number of bytes written. If this is less than the number
+ * specified in the call, the timeout was reached.
  *
- * @return The number of bytes written, or a negative error code upon failure.
+ * @private
  */
-SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
-		const void *buf, size_t count)
-{
-	return _serial_write(serial, buf, count, serial->nonblocking);
-}
-
 SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial,
-		const void *buf, size_t count)
+		const void *buf, size_t count, unsigned int timeout_ms)
 {
-	return _serial_write(serial, buf, count, 0);
+	return _serial_write(serial, buf, count, 0, timeout_ms);
 }
 
+/**
+ * Write a number of bytes to the specified serial port, return immediately.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param[in] buf Buffer containing the bytes to write.
+ * @param[in] count Number of bytes to write.
+ *
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR Other error.
+ * @retval other The number of bytes written.
+ *
+ * @private
+ */
 SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial,
 		const void *buf, size_t count)
 {
-	return _serial_write(serial, buf, count, 1);
+	return _serial_write(serial, buf, count, 1, 0);
 }
 
 static int _serial_read(struct sr_serial_dev_inst *serial, void *buf,
-		size_t count, int nonblocking)
+		size_t count, int nonblocking, unsigned int timeout_ms)
 {
 	ssize_t ret;
 	char *error;
@@ -251,7 +332,7 @@ static int _serial_read(struct sr_serial_dev_inst *serial, void *buf,
 	if (nonblocking)
 		ret = sp_nonblocking_read(serial->data, buf, count);
 	else
-		ret = sp_blocking_read(serial->data, buf, count, 0);
+		ret = sp_blocking_read(serial->data, buf, count, timeout_ms);
 
 	switch (ret) {
 	case SP_ERR_ARG:
@@ -265,36 +346,50 @@ static int _serial_read(struct sr_serial_dev_inst *serial, void *buf,
 	}
 
 	if (ret > 0)
-		sr_spew("Read %d/%d bytes.", ret, count);
+		sr_spew("Read %zd/%zu bytes.", ret, count);
 
 	return ret;
 }
 
 /**
- * Read a number of bytes from the specified serial port.
+ * Read a number of bytes from the specified serial port, block until finished.
  *
  * @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.
+ * @param[in] count The number of bytes to read.
+ * @param[in] timeout_ms Timeout in ms, or 0 for no timeout.
+ *
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR     Other error.
+ * @retval other      The number of bytes read. If this is less than the number
+ * requested, the timeout was reached.
  *
- * @return The number of bytes read, or a negative error code upon failure.
+ * @private
  */
-SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
-		size_t count)
-{
-	return _serial_read(serial, buf, count, serial->nonblocking);
-}
-
 SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf,
-		size_t count)
+		size_t count, unsigned int timeout_ms)
 {
-	return _serial_read(serial, buf, count, 0);
+	return _serial_read(serial, buf, count, 0, timeout_ms);
 }
 
+/**
+ * Try to read up to @a count bytes from the specified serial port, return
+ * immediately with what's available.
+ *
+ * @param serial Previously initialized serial port structure.
+ * @param buf Buffer where to store the bytes that are read.
+ * @param[in] count The number of bytes to read.
+ *
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR     Other error.
+ * @retval other      The number of bytes read.
+ *
+ * @private
+ */
 SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf,
 		size_t count)
 {
-	return _serial_read(serial, buf, count, 1);
+	return _serial_read(serial, buf, count, 1, 0);
 }
 
 /**
@@ -310,8 +405,10 @@ SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf
  * @param[in] rts Status of RTS line (0 or 1; required by some interfaces).
  * @param[in] dtr Status of DTR line (0 or 1; required by some interfaces).
  *
- * @retval SR_OK Success
+ * @retval SR_OK Success.
  * @retval SR_ERR Failure.
+ *
+ * @private
  */
 SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
 			      int bits, int parity, int stopbits,
@@ -391,13 +488,18 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
  * rts=0|1 Set RTS off resp. on.\n
  * Please note that values and combinations of these parameters must be
  * supported by the concrete serial interface hardware and the drivers for it.
+ *
  * @retval SR_OK Success.
  * @retval SR_ERR Failure.
+ *
+ * @private
  */
 SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
 		const char *paramstr)
 {
+/** @cond PRIVATE */
 #define SERIAL_COMM_SPEC "^(\\d+)/([5678])([neo])([12])(.*)$"
+/** @endcond */
 
 	GRegex *reg;
 	GMatchInfo *match;
@@ -418,13 +520,13 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
 		if ((mstr = g_match_info_fetch(match, 3))) {
 			switch (mstr[0]) {
 			case 'n':
-				parity = SERIAL_PARITY_NONE;
+				parity = SP_PARITY_NONE;
 				break;
 			case 'e':
-				parity = SERIAL_PARITY_EVEN;
+				parity = SP_PARITY_EVEN;
 				break;
 			case 'o':
-				parity = SERIAL_PARITY_ODD;
+				parity = SP_PARITY_ODD;
 				break;
 			}
 		}
@@ -502,11 +604,13 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
  *
  * @retval SR_OK Success.
  * @retval SR_ERR Failure.
+ *
+ * @private
  */
 SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
 		int *buflen, gint64 timeout_ms)
 {
-	gint64 start;
+	gint64 start, remaining;
 	int maxlen, len;
 
 	if (!serial) {
@@ -519,16 +623,16 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
 		return -1;
 	}
 
-	timeout_ms *= 1000;
 	start = g_get_monotonic_time();
+	remaining = timeout_ms;
 
 	maxlen = *buflen;
 	*buflen = len = 0;
-	while(1) {
+	while (1) {
 		len = maxlen - *buflen - 1;
 		if (len < 1)
 			break;
-		len = serial_read(serial, *buf + *buflen, 1);
+		len = sp_blocking_read(serial->data, *buf + *buflen, 1, remaining);
 		if (len > 0) {
 			*buflen += len;
 			*(*buf + *buflen) = '\0';
@@ -539,7 +643,9 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
 				break;
 			}
 		}
-		if (g_get_monotonic_time() - start > timeout_ms)
+		/* Reduce timeout by time elapsed. */
+		remaining = timeout_ms - ((g_get_monotonic_time() - start) / 1000);
+		if (remaining <= 0)
 			/* Timeout */
 			break;
 		if (len < 1)
@@ -565,8 +671,10 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf,
  *                 critical, but it helps fine tune the serial port polling
  *                 delay.
  *
- * @retval SR_OK Valid packet was found within the given timeout
+ * @retval SR_OK Valid packet was found within the given timeout.
  * @retval SR_ERR Failure.
+ *
+ * @private
  */
 SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
 				 uint8_t *buf, size_t *buflen,
@@ -576,7 +684,7 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
 {
 	uint64_t start, time, byte_delay_us;
 	size_t ibuf, i, maxlen;
-	int len;
+	ssize_t len;
 
 	maxlen = *buflen;
 
@@ -589,12 +697,12 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
 	}
 
 	/* Assume 8n1 transmission. That is 10 bits for every byte. */
-	byte_delay_us = 10 * (1000000 / baudrate);
+	byte_delay_us = 10 * ((1000 * 1000) / baudrate);
 	start = g_get_monotonic_time();
 
 	i = ibuf = len = 0;
 	while (ibuf < maxlen) {
-		len = serial_read(serial, &buf[ibuf], 1);
+		len = serial_read_nonblocking(serial, &buf[ibuf], 1);
 		if (len > 0) {
 			ibuf += len;
 		} else if (len == 0) {
@@ -609,12 +717,12 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
 		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 "
+				sr_spew("Found valid %zu-byte packet after "
 					"%" PRIu64 "ms.", (ibuf - i), time);
 				*buflen = ibuf;
 				return SR_OK;
 			} else {
-				sr_spew("Got %d bytes, but not a valid "
+				sr_spew("Got %zu bytes, but not a valid "
 					"packet.", (ibuf - i));
 			}
 			/* Not a valid packet. Continue searching. */
@@ -622,7 +730,7 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
 		}
 		if (time >= timeout_ms) {
 			/* Timeout */
-			sr_dbg("Detection timed out after %dms.", time);
+			sr_dbg("Detection timed out after %" PRIu64 "ms.", time);
 			break;
 		}
 		if (len < 1)
@@ -631,7 +739,7 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
 
 	*buflen = ibuf;
 
-	sr_err("Didn't find a valid packet (read %d bytes).", *buflen);
+	sr_err("Didn't find a valid packet (read %zu bytes).", *buflen);
 
 	return SR_ERR;
 }
@@ -640,12 +748,14 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial,
  * Extract the serial device and options from the options linked list.
  *
  * @param options List of options passed from the command line.
- * @param serial_device Pointer where to store the exctracted serial device.
+ * @param serial_device Pointer where to store the extracted serial device.
  * @param serial_options Pointer where to store the optional extracted serial
  * options.
  *
  * @return SR_OK if a serial_device is found, SR_ERR if no device is found. The
  * returned string should not be freed by the caller.
+ *
+ * @private
  */
 SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_device,
 				      const char **serial_options)
@@ -660,37 +770,47 @@ SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_devic
 		switch (src->key) {
 		case SR_CONF_CONN:
 			*serial_device = g_variant_get_string(src->data, NULL);
-			sr_dbg("Parsed serial device: %s", *serial_device);
+			sr_dbg("Parsed serial device: %s.", *serial_device);
 			break;
-
 		case SR_CONF_SERIALCOMM:
 			*serial_options = g_variant_get_string(src->data, NULL);
-			sr_dbg("Parsed serial options: %s", *serial_options);
+			sr_dbg("Parsed serial options: %s.", *serial_options);
 			break;
 		}
 	}
 
 	if (!*serial_device) {
-		sr_dbg("No serial device specified");
+		sr_dbg("No serial device specified.");
 		return SR_ERR;
 	}
 
 	return SR_OK;
 }
 
-#ifdef _WIN32
+/** @cond PRIVATE */
+#ifdef G_OS_WIN32
 typedef HANDLE event_handle;
 #else
 typedef int event_handle;
 #endif
+/** @endcond */
 
-SR_PRIV int serial_source_add(struct sr_serial_dev_inst *serial, int events,
-		int timeout, sr_receive_data_callback cb, void *cb_data)
+/** @private */
+SR_PRIV int serial_source_add(struct sr_session *session,
+		struct sr_serial_dev_inst *serial, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data)
 {
+	struct sp_event_set *event_set;
+	gintptr poll_fd;
+	unsigned int poll_events;
 	enum sp_event mask = 0;
-	unsigned int i;
 
-	if (sp_new_event_set(&serial->event_set) != SP_OK)
+	if ((events & (G_IO_IN|G_IO_ERR)) && (events & G_IO_OUT)) {
+		sr_err("Cannot poll input/error and output simultaneously.");
+		return SR_ERR_ARG;
+	}
+
+	if (sp_new_event_set(&event_set) != SP_OK)
 		return SR_ERR;
 
 	if (events & G_IO_IN)
@@ -700,186 +820,189 @@ SR_PRIV int serial_source_add(struct sr_serial_dev_inst *serial, int events,
 	if (events & G_IO_ERR)
 		mask |= SP_EVENT_ERROR;
 
-	if (sp_add_port_events(serial->event_set, serial->data, mask) != SP_OK) {
-		sp_free_event_set(serial->event_set);
+	if (sp_add_port_events(event_set, serial->data, mask) != SP_OK) {
+		sp_free_event_set(event_set);
+		return SR_ERR;
+	}
+	if (event_set->count != 1) {
+		sr_err("Unexpected number (%u) of event handles to poll.",
+			event_set->count);
+		sp_free_event_set(event_set);
 		return SR_ERR;
 	}
 
-	serial->pollfds = (GPollFD *) g_malloc0(sizeof(GPollFD) * serial->event_set->count);
+	poll_fd = (gintptr) ((event_handle *)event_set->handles)[0];
+	mask = event_set->masks[0];
 
-	for (i = 0; i < serial->event_set->count; i++) {
+	sp_free_event_set(event_set);
 
-		serial->pollfds[i].fd = ((event_handle *) serial->event_set->handles)[i];
+	poll_events = 0;
+	if (mask & SP_EVENT_RX_READY)
+		poll_events |= G_IO_IN;
+	if (mask & SP_EVENT_TX_READY)
+		poll_events |= G_IO_OUT;
+	if (mask & SP_EVENT_ERROR)
+		poll_events |= G_IO_ERR;
+	/*
+	 * Using serial->data as the key for the event source is not quite
+	 * proper, as it makes it impossible to create another event source
+	 * for the same serial port. However, these fixed keys will soon be
+	 * removed from the API anyway, so this is OK for now.
+	 */
+	return sr_session_fd_source_add(session, serial->data,
+			poll_fd, poll_events, timeout, cb, cb_data);
+}
 
-		mask = serial->event_set->masks[i];
+/** @private */
+SR_PRIV int serial_source_remove(struct sr_session *session,
+		struct sr_serial_dev_inst *serial)
+{
+	return sr_session_source_remove_internal(session, serial->data);
+}
 
-		if (mask & SP_EVENT_RX_READY)
-			serial->pollfds[i].events |= G_IO_IN;
-		if (mask & SP_EVENT_TX_READY)
-			serial->pollfds[i].events |= G_IO_OUT;
-		if (mask & SP_EVENT_ERROR)
-			serial->pollfds[i].events |= G_IO_ERR;
+/**
+ * Create/allocate a new sr_serial_port structure.
+ *
+ * @param name The OS dependent name of the serial port. Must not be NULL.
+ * @param description An end user friendly description for the serial port.
+ *                    Can be NULL (in that case the empty string is used
+ *                    as description).
+ *
+ * @return The newly allocated sr_serial_port struct.
+ */
+static struct sr_serial_port *sr_serial_new(const char *name,
+		const char *description)
+{
+	struct sr_serial_port *serial;
 
-		if (sr_session_source_add_pollfd(&serial->pollfds[i],
-				timeout, cb, cb_data) != SR_OK)
-			return SR_ERR;
-	}
+	if (!name)
+		return NULL;
 
-	return SR_OK;
+	serial = g_malloc(sizeof(struct sr_serial_port));
+	serial->name = g_strdup(name);
+	serial->description = g_strdup(description ? description : "");
+
+	return serial;
 }
 
-SR_PRIV int serial_source_remove(struct sr_serial_dev_inst *serial)
+/**
+ * Free a previously allocated sr_serial_port structure.
+ *
+ * @param serial The sr_serial_port struct to free. Must not be NULL.
+ */
+SR_API void sr_serial_free(struct sr_serial_port *serial)
 {
-	unsigned int i;
+	if (!serial)
+		return;
+	g_free(serial->name);
+	g_free(serial->description);
+	g_free(serial);
+}
 
-	for (i = 0; i < serial->event_set->count; i++)
-		if (sr_session_source_remove_pollfd(&serial->pollfds[i]) != SR_OK)
-			return SR_ERR;
+/**
+ * List available serial devices.
+ *
+ * @return A GSList of strings containing the path of the serial devices or
+ *         NULL if no serial device is found. The returned list must be freed
+ *         by the caller.
+ */
+SR_API GSList *sr_serial_list(const struct sr_dev_driver *driver)
+{
+	GSList *tty_devs = NULL;
+	struct sp_port **ports;
+	struct sr_serial_port *port;
+	int i;
 
-	g_free(serial->pollfds);
-	sp_free_event_set(serial->event_set);
+	/* Currently unused, but will be used by some drivers later on. */
+	(void)driver;
 
-	serial->pollfds = NULL;
-	serial->event_set = NULL;
+	if (sp_list_ports(&ports) != SP_OK)
+		return NULL;
 
-	return SR_OK;
+	for (i = 0; ports[i]; i++) {
+		port = sr_serial_new(sp_get_port_name(ports[i]),
+				     sp_get_port_description(ports[i]));
+		tty_devs = g_slist_append(tty_devs, port);
+	}
+
+	sp_free_port_list(ports);
+
+	return tty_devs;
 }
 
 /**
  * Find USB serial devices via the USB vendor ID and product ID.
  *
- * @param vendor_id Vendor ID of the USB device.
- * @param product_id Product ID of the USB device.
+ * @param[in] vendor_id Vendor ID of the USB device.
+ * @param[in] product_id Product ID of the USB device.
  *
  * @return A GSList of strings containing the path of the serial device or
  *         NULL if no serial device is found. The returned list must be freed
  *         by the caller.
+ *
+ * @private
  */
 SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id)
 {
-#ifdef __linux__
-	const gchar *usb_dev;
-	const char device_tree[] = "/sys/bus/usb/devices/";
-	GDir *devices_dir, *device_dir;
-	GSList *l = NULL;
-	GSList *tty_devs;
-	GSList *matched_paths;
-	FILE *fd;
-	char tmp[5];
-	gchar *vendor_path, *product_path, *path_copy;
-	gchar *prefix, *subdir_path, *device_path, *tty_path;
-	unsigned long read_vendor_id, read_product_id;
-	const char *file;
-
-	l = NULL;
-	tty_devs = NULL;
-	matched_paths = NULL;
-
-	if (!(devices_dir = g_dir_open(device_tree, 0, NULL)))
-		return NULL;
+	GSList *tty_devs = NULL;
+	struct sp_port **ports;
+	int i, vid, pid;
 
-	/*
-	 * Find potential candidates using the vendor ID and product ID
-	 * and store them in matched_paths.
-	 */
-	while ((usb_dev = g_dir_read_name(devices_dir))) {
-		vendor_path = g_strconcat(device_tree,
-					  usb_dev, "/idVendor", NULL);
-		product_path = g_strconcat(device_tree,
-					   usb_dev, "/idProduct", NULL);
-
-		if (!g_file_test(vendor_path, G_FILE_TEST_EXISTS) ||
-		    !g_file_test(product_path, G_FILE_TEST_EXISTS))
-			goto skip_device;
-
-		if ((fd = g_fopen(vendor_path, "r")) == NULL)
-			goto skip_device;
-
-		if (fgets(tmp, sizeof(tmp), fd) == NULL) {
-			fclose(fd);
-			goto skip_device;
-		}
-		read_vendor_id = strtoul(tmp, NULL, 16);
-
-		fclose(fd);
-
-		if ((fd = g_fopen(product_path, "r")) == NULL)
-			goto skip_device;
-
-		if (fgets(tmp, sizeof(tmp), fd) == NULL) {
-			fclose(fd);
-			goto skip_device;
-		}
-		read_product_id = strtoul(tmp, NULL, 16);
-
-		fclose(fd);
+	if (sp_list_ports(&ports) != SP_OK)
+		return NULL;
 
-		if (vendor_id == read_vendor_id &&
-		    product_id == read_product_id) {
-			path_copy = g_strdup(usb_dev);
-			matched_paths = g_slist_prepend(matched_paths,
-							path_copy);
+	for (i = 0; ports[i]; i++)
+		if (sp_get_port_transport(ports[i]) == SP_TRANSPORT_USB &&
+		    sp_get_port_usb_vid_pid(ports[i], &vid, &pid) == SP_OK &&
+		    vid == vendor_id && pid == product_id) {
+			tty_devs = g_slist_prepend(tty_devs,
+					g_strdup(sp_get_port_name(ports[i])));
 		}
 
-skip_device:
-		g_free(vendor_path);
-		g_free(product_path);
-	}
-	g_dir_close(devices_dir);
+	sp_free_port_list(ports);
 
-	/* For every matched device try to find a ttyUSBX subfolder. */
-	for (l = matched_paths; l; l = l->next) {
-		subdir_path = NULL;
+	return tty_devs;
+}
 
-		device_path = g_strconcat(device_tree, l->data, NULL);
+/** @private */
+SR_PRIV int serial_timeout(struct sr_serial_dev_inst *port, int num_bytes)
+{
+	struct sp_port_config *config;
+	int timeout_ms, bits, baud, tmp;
 
-		if (!(device_dir = g_dir_open(device_path, 0, NULL))) {
-			g_free(device_path);
-			continue;
-		}
+	/* Default to 1s. */
+	timeout_ms = 1000;
 
-		prefix = g_strconcat(l->data, ":", NULL);
+	if (sp_new_config(&config) < 0)
+		return timeout_ms;
 
-		while ((file = g_dir_read_name(device_dir))) {
-			if (g_str_has_prefix(file, prefix)) {
-				subdir_path = g_strconcat(device_path,
-						"/", file, NULL);
-				break;
-			}
-		}
-		g_dir_close(device_dir);
+	bits = baud = 0;
+	do {
+		if (sp_get_config(port->data, config) < 0)
+			break;
 
-		g_free(prefix);
-		g_free(device_path);
+		/* Start bit. */
+		bits = 1;
+		if (sp_get_config_bits(config, &tmp) < 0)
+			break;
+		bits += tmp;
+		if (sp_get_config_stopbits(config, &tmp) < 0)
+			break;
+		bits += tmp;
+		if (sp_get_config_baudrate(config, &tmp) < 0)
+			break;
+		baud = tmp;
+	} while (FALSE);
 
-		if (subdir_path) {
-			if (!(device_dir = g_dir_open(subdir_path, 0, NULL))) {
-				g_free(subdir_path);
-				continue;
-			}
-			g_free(subdir_path);
-
-			while ((file = g_dir_read_name(device_dir))) {
-				if (g_str_has_prefix(file, "ttyUSB")) {
-					tty_path = g_strconcat("/dev/",
-							       file, NULL);
-					sr_dbg("Found USB device %04x:%04x attached to %s.",
-					       vendor_id, product_id, tty_path);
-					tty_devs = g_slist_prepend(tty_devs,
-							tty_path);
-					break;
-				}
-			}
-			g_dir_close(device_dir);
-		}
+	if (bits && baud) {
+		/* Throw in 10ms for misc OS overhead. */
+		timeout_ms = 10;
+		timeout_ms += ((1000.0 / baud) * bits) * num_bytes;
 	}
-	g_slist_free_full(matched_paths, g_free);
 
-	return tty_devs;
-#else
-	(void)vendor_id;
-	(void)product_id;
+	sp_free_config(config);
 
-	return NULL;
-#endif
+	return timeout_ms;
 }
+
+/** @} */
diff --git a/src/session.c b/src/session.c
new file mode 100644
index 0000000..4aeddcc
--- /dev/null
+++ b/src/session.c
@@ -0,0 +1,1628 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2010-2012 Bert Vermeulen <bert at biot.com>
+ * Copyright (C) 2015 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "session"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Creating, using, or destroying libsigrok sessions.
+ */
+
+/**
+ * @defgroup grp_session Session handling
+ *
+ * Creating, using, or destroying libsigrok sessions.
+ *
+ * @{
+ */
+
+struct datafeed_callback {
+	sr_datafeed_callback cb;
+	void *cb_data;
+};
+
+/** Custom GLib event source for generic descriptor I/O.
+ * @see https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html
+ * @internal
+ */
+struct fd_source {
+	GSource base;
+
+	int64_t timeout_us;
+	int64_t due_us;
+
+	/* Meta-data needed to keep track of installed sources */
+	struct sr_session *session;
+	void *key;
+
+	GPollFD pollfd;
+};
+
+/** FD event source prepare() method.
+ * This is called immediately before poll().
+ */
+static gboolean fd_source_prepare(GSource *source, int *timeout)
+{
+	int64_t now_us;
+	struct fd_source *fsource;
+	int remaining_ms;
+
+	fsource = (struct fd_source *)source;
+
+	if (fsource->timeout_us >= 0) {
+		now_us = g_source_get_time(source);
+
+		if (fsource->due_us == 0) {
+			/* First-time initialization of the expiration time */
+			fsource->due_us = now_us + fsource->timeout_us;
+		}
+		remaining_ms = (MAX(0, fsource->due_us - now_us) + 999) / 1000;
+	} else {
+		remaining_ms = -1;
+	}
+	*timeout = remaining_ms;
+
+	return (remaining_ms == 0);
+}
+
+/** FD event source check() method.
+ * This is called after poll() returns to check whether an event fired.
+ */
+static gboolean fd_source_check(GSource *source)
+{
+	struct fd_source *fsource;
+	unsigned int revents;
+
+	fsource = (struct fd_source *)source;
+	revents = fsource->pollfd.revents;
+
+	return (revents != 0 || (fsource->timeout_us >= 0
+			&& fsource->due_us <= g_source_get_time(source)));
+}
+
+/** FD event source dispatch() method.
+ * This is called if either prepare() or check() returned TRUE.
+ */
+static gboolean fd_source_dispatch(GSource *source,
+		GSourceFunc callback, void *user_data)
+{
+	struct fd_source *fsource;
+	unsigned int revents;
+	gboolean keep;
+
+	fsource = (struct fd_source *)source;
+	revents = fsource->pollfd.revents;
+
+	if (!callback) {
+		sr_err("Callback not set, cannot dispatch event.");
+		return G_SOURCE_REMOVE;
+	}
+	keep = (*(sr_receive_data_callback)callback)
+			(fsource->pollfd.fd, revents, user_data);
+
+	if (fsource->timeout_us >= 0 && G_LIKELY(keep)
+			&& G_LIKELY(!g_source_is_destroyed(source)))
+		fsource->due_us = g_source_get_time(source)
+				+ fsource->timeout_us;
+	return keep;
+}
+
+/** FD event source finalize() method.
+ */
+static void fd_source_finalize(GSource *source)
+{
+	struct fd_source *fsource;
+
+	fsource = (struct fd_source *)source;
+
+	sr_dbg("%s: key %p", __func__, fsource->key);
+
+	sr_session_source_destroyed(fsource->session, fsource->key, source);
+}
+
+/** Create an event source for I/O on a file descriptor.
+ *
+ * In order to maintain API compatibility, this event source also doubles
+ * as a timer event source.
+ *
+ * @param session The session the event source belongs to.
+ * @param key The key used to identify this source.
+ * @param fd The file descriptor or HANDLE.
+ * @param timeout_ms The timeout interval in ms, or -1 to wait indefinitely.
+ * @return A new event source object, or NULL on failure.
+ */
+static GSource *fd_source_new(struct sr_session *session, void *key,
+		gintptr fd, int events, int timeout_ms)
+{
+	static GSourceFuncs fd_source_funcs = {
+		.prepare  = &fd_source_prepare,
+		.check    = &fd_source_check,
+		.dispatch = &fd_source_dispatch,
+		.finalize = &fd_source_finalize
+	};
+	GSource *source;
+	struct fd_source *fsource;
+
+	source = g_source_new(&fd_source_funcs, sizeof(struct fd_source));
+	fsource = (struct fd_source *)source;
+
+	g_source_set_name(source, (fd < 0) ? "timer" : "fd");
+
+	if (timeout_ms >= 0) {
+		fsource->timeout_us = 1000 * (int64_t)timeout_ms;
+		fsource->due_us = 0;
+	} else {
+		fsource->timeout_us = -1;
+		fsource->due_us = INT64_MAX;
+	}
+	fsource->session = session;
+	fsource->key = key;
+
+	fsource->pollfd.fd = fd;
+	fsource->pollfd.events = events;
+	fsource->pollfd.revents = 0;
+
+	if (fd >= 0)
+		g_source_add_poll(source, &fsource->pollfd);
+
+	return source;
+}
+
+/**
+ * Create a new session.
+ *
+ * @param ctx         The context in which to create the new session.
+ * @param new_session This will contain a pointer to the newly created
+ *                    session if the return value is SR_OK, otherwise the value
+ *                    is undefined and should not be used. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_new(struct sr_context *ctx,
+		struct sr_session **new_session)
+{
+	struct sr_session *session;
+
+	if (!new_session)
+		return SR_ERR_ARG;
+
+	session = g_malloc0(sizeof(struct sr_session));
+
+	session->ctx = ctx;
+
+	g_mutex_init(&session->main_mutex);
+
+	/* To maintain API compatibility, we need a lookup table
+	 * which maps poll_object IDs to GSource* pointers.
+	 */
+	session->event_sources = g_hash_table_new(NULL, NULL);
+
+	*new_session = session;
+
+	return SR_OK;
+}
+
+/**
+ * Destroy a session.
+ * This frees up all memory used by the session.
+ *
+ * @param session The session to destroy. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_destroy(struct sr_session *session)
+{
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	sr_session_dev_remove_all(session);
+	g_slist_free_full(session->owned_devs, (GDestroyNotify)sr_dev_inst_free);
+
+	sr_session_datafeed_callback_remove_all(session);
+
+	g_hash_table_unref(session->event_sources);
+
+	g_mutex_clear(&session->main_mutex);
+
+	g_free(session);
+
+	return SR_OK;
+}
+
+/**
+ * Remove all the devices from a session.
+ *
+ * The session itself (i.e., the struct sr_session) is not free'd and still
+ * exists after this function returns.
+ *
+ * @param session The session to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_BUG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_dev_remove_all(struct sr_session *session)
+{
+	struct sr_dev_inst *sdi;
+	GSList *l;
+
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	for (l = session->devs; l; l = l->next) {
+		sdi = (struct sr_dev_inst *) l->data;
+		sdi->session = NULL;
+	}
+
+	g_slist_free(session->devs);
+	session->devs = NULL;
+
+	return SR_OK;
+}
+
+/**
+ * Add a device instance to a session.
+ *
+ * @param session The session to add to. Must not be NULL.
+ * @param sdi The device instance to add to a session. Must not
+ *            be NULL. Also, sdi->driver and sdi->driver->dev_open must
+ *            not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_dev_add(struct sr_session *session,
+		struct sr_dev_inst *sdi)
+{
+	int ret;
+
+	if (!sdi) {
+		sr_err("%s: sdi was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	/* If sdi->session is not NULL, the device is already in this or
+	 * another session. */
+	if (sdi->session) {
+		sr_err("%s: already assigned to session", __func__);
+		return SR_ERR_ARG;
+	}
+
+	/* If sdi->driver is NULL, this is a virtual device. */
+	if (!sdi->driver) {
+		/* Just add the device, don't run dev_open(). */
+		session->devs = g_slist_append(session->devs, sdi);
+		sdi->session = session;
+		return SR_OK;
+	}
+
+	/* sdi->driver is non-NULL (i.e. we have a real device). */
+	if (!sdi->driver->dev_open) {
+		sr_err("%s: sdi->driver->dev_open was NULL", __func__);
+		return SR_ERR_BUG;
+	}
+
+	session->devs = g_slist_append(session->devs, sdi);
+	sdi->session = session;
+
+	/* TODO: This is invalid if the session runs in a different thread.
+	 * The usage semantics and restrictions need to be documented.
+	 */
+	if (session->running) {
+		/* Adding a device to a running session. Commit settings
+		 * and start acquisition on that device now. */
+		if ((ret = sr_config_commit(sdi)) != SR_OK) {
+			sr_err("Failed to commit device settings before "
+			       "starting acquisition in running session (%s)",
+			       sr_strerror(ret));
+			return ret;
+		}
+		if ((ret = sdi->driver->dev_acquisition_start(sdi,
+					sdi)) != SR_OK) {
+			sr_err("Failed to start acquisition of device in "
+			       "running session (%s)", sr_strerror(ret));
+			return ret;
+		}
+	}
+
+	return SR_OK;
+}
+
+/**
+ * List all device instances attached to a session.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param devlist A pointer where the device instance list will be
+ *                stored on return. If no devices are in the session,
+ *                this will be NULL. Each element in the list points
+ *                to a struct sr_dev_inst *.
+ *                The list must be freed by the caller, but not the
+ *                elements pointed to.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_dev_list(struct sr_session *session, GSList **devlist)
+{
+	if (!session)
+		return SR_ERR_ARG;
+
+	if (!devlist)
+		return SR_ERR_ARG;
+
+	*devlist = g_slist_copy(session->devs);
+
+	return SR_OK;
+}
+
+/**
+ * Remove a device instance from a session.
+ *
+ * @param session The session to remove from. Must not be NULL.
+ * @param sdi The device instance to remove from a session. Must not
+ *            be NULL. Also, sdi->driver and sdi->driver->dev_open must
+ *            not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_dev_remove(struct sr_session *session,
+		struct sr_dev_inst *sdi)
+{
+	if (!sdi) {
+		sr_err("%s: sdi was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	/* If sdi->session is not session, the device is not in this
+	 * session. */
+	if (sdi->session != session) {
+		sr_err("%s: not assigned to this session", __func__);
+		return SR_ERR_ARG;
+	}
+
+	session->devs = g_slist_remove(session->devs, sdi);
+	sdi->session = NULL;
+
+	return SR_OK;
+}
+
+/**
+ * Remove all datafeed callbacks in a session.
+ *
+ * @param session The session to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_datafeed_callback_remove_all(struct sr_session *session)
+{
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	g_slist_free_full(session->datafeed_callbacks, g_free);
+	session->datafeed_callbacks = NULL;
+
+	return SR_OK;
+}
+
+/**
+ * Add a datafeed callback to a session.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param cb Function to call when a chunk of data is received.
+ *           Must not be NULL.
+ * @param cb_data Opaque pointer passed in by the caller.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_BUG No session exists.
+ *
+ * @since 0.3.0
+ */
+SR_API int sr_session_datafeed_callback_add(struct sr_session *session,
+		sr_datafeed_callback cb, void *cb_data)
+{
+	struct datafeed_callback *cb_struct;
+
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_BUG;
+	}
+
+	if (!cb) {
+		sr_err("%s: cb was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	cb_struct = g_malloc0(sizeof(struct datafeed_callback));
+	cb_struct->cb = cb;
+	cb_struct->cb_data = cb_data;
+
+	session->datafeed_callbacks =
+	    g_slist_append(session->datafeed_callbacks, cb_struct);
+
+	return SR_OK;
+}
+
+/**
+ * Get the trigger assigned to this session.
+ *
+ * @param session The session to use.
+ *
+ * @retval NULL Invalid (NULL) session was passed to the function.
+ * @retval other The trigger assigned to this session (can be NULL).
+ *
+ * @since 0.4.0
+ */
+SR_API struct sr_trigger *sr_session_trigger_get(struct sr_session *session)
+{
+	if (!session)
+		return NULL;
+
+	return session->trigger;
+}
+
+/**
+ * Set the trigger of this session.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param trig The trigger to assign to this session. Can be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_trigger_set(struct sr_session *session, struct sr_trigger *trig)
+{
+	if (!session)
+		return SR_ERR_ARG;
+
+	session->trigger = trig;
+
+	return SR_OK;
+}
+
+static int verify_trigger(struct sr_trigger *trigger)
+{
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	GSList *l, *m;
+
+	if (!trigger->stages) {
+		sr_err("No trigger stages defined.");
+		return SR_ERR;
+	}
+
+	sr_spew("Checking trigger:");
+	for (l = trigger->stages; l; l = l->next) {
+		stage = l->data;
+		if (!stage->matches) {
+			sr_err("Stage %d has no matches defined.", stage->stage);
+			return SR_ERR;
+		}
+		for (m = stage->matches; m; m = m->next) {
+			match = m->data;
+			if (!match->channel) {
+				sr_err("Stage %d match has no channel.", stage->stage);
+				return SR_ERR;
+			}
+			if (!match->match) {
+				sr_err("Stage %d match is not defined.", stage->stage);
+				return SR_ERR;
+			}
+			sr_spew("Stage %d match on channel %s, match %d", stage->stage,
+					match->channel->name, match->match);
+		}
+	}
+
+	return SR_OK;
+}
+
+/** Set up the main context the session will be executing in.
+ *
+ * Must be called just before the session starts, by the thread which
+ * will execute the session main loop. Once acquired, the main context
+ * pointer is immutable for the duration of the session run.
+ */
+static int set_main_context(struct sr_session *session)
+{
+	GMainContext *main_context;
+
+	g_mutex_lock(&session->main_mutex);
+
+	/* May happen if sr_session_start() is called a second time
+	 * while the session is still running.
+	 */
+	if (session->main_context) {
+		sr_err("Main context already set.");
+
+		g_mutex_unlock(&session->main_mutex);
+		return SR_ERR;
+	}
+	main_context = g_main_context_ref_thread_default();
+	/*
+	 * Try to use an existing main context if possible, but only if we
+	 * can make it owned by the current thread. Otherwise, create our
+	 * own main context so that event source callbacks can execute in
+	 * the session thread.
+	 */
+	if (g_main_context_acquire(main_context)) {
+		g_main_context_release(main_context);
+
+		sr_dbg("Using thread-default main context.");
+	} else {
+		g_main_context_unref(main_context);
+
+		sr_dbg("Creating our own main context.");
+		main_context = g_main_context_new();
+	}
+	session->main_context = main_context;
+
+	g_mutex_unlock(&session->main_mutex);
+
+	return SR_OK;
+}
+
+/** Unset the main context used for the current session run.
+ *
+ * Must be called right after stopping the session. Note that if the
+ * session is stopped asynchronously, the main loop may still be running
+ * after the main context has been unset. This is OK as long as no new
+ * event sources are created -- the main loop holds its own reference
+ * to the main context.
+ */
+static int unset_main_context(struct sr_session *session)
+{
+	int ret;
+
+	g_mutex_lock(&session->main_mutex);
+
+	if (session->main_context) {
+		g_main_context_unref(session->main_context);
+		session->main_context = NULL;
+		ret = SR_OK;
+	} else {
+		/* May happen if the set/unset calls are not matched.
+		 */
+		sr_err("No main context to unset.");
+		ret = SR_ERR;
+	}
+	g_mutex_unlock(&session->main_mutex);
+
+	return ret;
+}
+
+static unsigned int session_source_attach(struct sr_session *session,
+		GSource *source)
+{
+	unsigned int id = 0;
+
+	g_mutex_lock(&session->main_mutex);
+
+	if (session->main_context)
+		id = g_source_attach(source, session->main_context);
+	else
+		sr_err("Cannot add event source without main context.");
+
+	g_mutex_unlock(&session->main_mutex);
+
+	return id;
+}
+
+/* Idle handler; invoked when the number of registered event sources
+ * for a running session drops to zero.
+ */
+static gboolean delayed_stop_check(void *data)
+{
+	struct sr_session *session;
+
+	session = data;
+	session->stop_check_id = 0;
+
+	/* Session already ended? */
+	if (!session->running)
+		return G_SOURCE_REMOVE;
+
+	/* New event sources may have been installed in the meantime. */
+	if (g_hash_table_size(session->event_sources) != 0)
+		return G_SOURCE_REMOVE;
+
+	session->running = FALSE;
+	unset_main_context(session);
+
+	sr_info("Stopped.");
+
+	/* This indicates a bug in user code, since it is not valid to
+	 * restart or destroy a session while it may still be running.
+	 */
+	if (!session->main_loop && !session->stopped_callback) {
+		sr_err("BUG: Session stop left unhandled.");
+		return G_SOURCE_REMOVE;
+	}
+	if (session->main_loop)
+		g_main_loop_quit(session->main_loop);
+
+	if (session->stopped_callback)
+		(*session->stopped_callback)(session->stopped_cb_data);
+
+	return G_SOURCE_REMOVE;
+}
+
+static int stop_check_later(struct sr_session *session)
+{
+	GSource *source;
+	unsigned int source_id;
+
+	if (session->stop_check_id != 0)
+		return SR_OK; /* idle handler already installed */
+
+	source = g_idle_source_new();
+	g_source_set_callback(source, &delayed_stop_check, session, NULL);
+
+	source_id = session_source_attach(session, source);
+	session->stop_check_id = source_id;
+
+	g_source_unref(source);
+
+	return (source_id != 0) ? SR_OK : SR_ERR;
+}
+
+/**
+ * Start a session.
+ *
+ * When this function returns with a status code indicating success, the
+ * session is running. Use sr_session_stopped_callback_set() to receive
+ * notification upon completion, or call sr_session_run() to block until
+ * the session stops.
+ *
+ * Session events will be processed in the context of the current thread.
+ * If a thread-default GLib main context has been set, and is not owned by
+ * any other thread, it will be used. Otherwise, libsigrok will create its
+ * own main context for the current thread.
+ *
+ * @param session The session to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ * @retval SR_ERR Other error.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_start(struct sr_session *session)
+{
+	struct sr_dev_inst *sdi;
+	struct sr_channel *ch;
+	GSList *l, *c, *lend;
+	int ret;
+
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	if (!session->devs) {
+		sr_err("%s: session->devs was NULL; a session "
+		       "cannot be started without devices.", __func__);
+		return SR_ERR_ARG;
+	}
+
+	if (session->running) {
+		sr_err("Cannot (re-)start session while it is still running.");
+		return SR_ERR;
+	}
+
+	if (session->trigger) {
+		ret = verify_trigger(session->trigger);
+		if (ret != SR_OK)
+			return ret;
+	}
+
+	/* Check enabled channels and commit settings of all devices. */
+	for (l = session->devs; l; l = l->next) {
+		sdi = l->data;
+		for (c = sdi->channels; c; c = c->next) {
+			ch = c->data;
+			if (ch->enabled)
+				break;
+		}
+		if (!c) {
+			sr_err("%s device %s has no enabled channels.",
+				sdi->driver->name, sdi->connection_id);
+			return SR_ERR;
+		}
+
+		ret = sr_config_commit(sdi);
+		if (ret != SR_OK) {
+			sr_err("Failed to commit %s device %s settings "
+				"before starting acquisition.",
+				sdi->driver->name, sdi->connection_id);
+			return ret;
+		}
+	}
+
+	ret = set_main_context(session);
+	if (ret != SR_OK)
+		return ret;
+
+	sr_info("Starting.");
+
+	session->running = TRUE;
+
+	/* Have all devices start acquisition. */
+	for (l = session->devs; l; l = l->next) {
+		sdi = l->data;
+		ret = sdi->driver->dev_acquisition_start(sdi, sdi);
+		if (ret != SR_OK) {
+			sr_err("Could not start %s device %s acquisition.",
+				sdi->driver->name, sdi->connection_id);
+			break;
+		}
+	}
+
+	if (ret != SR_OK) {
+		/* If there are multiple devices, some of them may already have
+		 * started successfully. Stop them now before returning. */
+		lend = l->next;
+		for (l = session->devs; l != lend; l = l->next) {
+			sdi = l->data;
+			if (sdi->driver->dev_acquisition_stop)
+				sdi->driver->dev_acquisition_stop(sdi, sdi);
+		}
+		/* TODO: Handle delayed stops. Need to iterate the event
+		 * sources... */
+		session->running = FALSE;
+
+		unset_main_context(session);
+		return ret;
+	}
+
+	if (g_hash_table_size(session->event_sources) == 0)
+		stop_check_later(session);
+
+	return SR_OK;
+}
+
+/**
+ * Block until the running session stops.
+ *
+ * This is a convenience function which creates a GLib main loop and runs
+ * it to process session events until the session stops.
+ *
+ * Instead of using this function, applications may run their own GLib main
+ * loop, and use sr_session_stopped_callback_set() to receive notification
+ * when the session finished running.
+ *
+ * @param session The session to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ * @retval SR_ERR Other error.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_run(struct sr_session *session)
+{
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+	if (!session->running) {
+		sr_err("No session running.");
+		return SR_ERR;
+	}
+	if (session->main_loop) {
+		sr_err("Main loop already created.");
+		return SR_ERR;
+	}
+
+	g_mutex_lock(&session->main_mutex);
+
+	if (!session->main_context) {
+		sr_err("Cannot run without main context.");
+		g_mutex_unlock(&session->main_mutex);
+		return SR_ERR;
+	}
+	session->main_loop = g_main_loop_new(session->main_context, FALSE);
+
+	g_mutex_unlock(&session->main_mutex);
+
+	g_main_loop_run(session->main_loop);
+
+	g_main_loop_unref(session->main_loop);
+	session->main_loop = NULL;
+
+	return SR_OK;
+}
+
+static gboolean session_stop_sync(void *user_data)
+{
+	struct sr_session *session;
+	struct sr_dev_inst *sdi;
+	GSList *node;
+
+	session = user_data;
+
+	if (!session->running)
+		return G_SOURCE_REMOVE;
+
+	sr_info("Stopping.");
+
+	for (node = session->devs; node; node = node->next) {
+		sdi = node->data;
+		if (sdi->driver && sdi->driver->dev_acquisition_stop)
+			sdi->driver->dev_acquisition_stop(sdi, sdi);
+	}
+
+	return G_SOURCE_REMOVE;
+}
+
+/**
+ * Stop a session.
+ *
+ * This requests the drivers of each device participating in the session to
+ * abort the acquisition as soon as possible. Even after this function returns,
+ * event processing still continues until all devices have actually stopped.
+ *
+ * Use sr_session_stopped_callback_set() to receive notification when the event
+ * processing finished.
+ *
+ * This function is reentrant. That is, it may be called from a different
+ * thread than the one executing the session, as long as it can be ensured
+ * that the session object is valid.
+ *
+ * If the session is not running, sr_session_stop() silently does nothing.
+ *
+ * @param session The session to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_stop(struct sr_session *session)
+{
+	GMainContext *main_context;
+
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	g_mutex_lock(&session->main_mutex);
+
+	main_context = (session->main_context)
+		? g_main_context_ref(session->main_context)
+		: NULL;
+
+	g_mutex_unlock(&session->main_mutex);
+
+	if (!main_context) {
+		sr_dbg("No main context set; already stopped?");
+		/* Not an error; as it would be racy. */
+		return SR_OK;
+	}
+	g_main_context_invoke(main_context, &session_stop_sync, session);
+	g_main_context_unref(main_context);
+
+	return SR_OK;
+}
+
+/**
+ * Return whether the session is currently running.
+ *
+ * Note that this function should be called from the same thread
+ * the session was started in.
+ *
+ * @param session The session to use. Must not be NULL.
+ *
+ * @retval TRUE Session is running.
+ * @retval FALSE Session is not running.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_is_running(struct sr_session *session)
+{
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+	return session->running;
+}
+
+/**
+ * Set the callback to be invoked after a session stopped running.
+ *
+ * Install a callback to receive notification when a session run stopped.
+ * This can be used to integrate session execution with an existing main
+ * loop, without having to block in sr_session_run().
+ *
+ * Note that the callback will be invoked in the context of the thread
+ * that calls sr_session_start().
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param cb The callback to invoke on session stop. May be NULL to unset.
+ * @param cb_data User data pointer to be passed to the callback.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid session passed.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_session_stopped_callback_set(struct sr_session *session,
+		sr_session_stopped_callback cb, void *cb_data)
+{
+	if (!session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+	session->stopped_callback = cb;
+	session->stopped_cb_data = cb_data;
+
+	return SR_OK;
+}
+
+/**
+ * Debug helper.
+ *
+ * @param packet The packet to show debugging information for.
+ */
+static void datafeed_dump(const struct sr_datafeed_packet *packet)
+{
+	const struct sr_datafeed_logic *logic;
+	const struct sr_datafeed_analog_old *analog_old;
+	const struct sr_datafeed_analog *analog;
+
+	/* Please use the same order as in libsigrok.h. */
+	switch (packet->type) {
+	case SR_DF_HEADER:
+		sr_dbg("bus: Received SR_DF_HEADER packet.");
+		break;
+	case SR_DF_END:
+		sr_dbg("bus: Received SR_DF_END packet.");
+		break;
+	case SR_DF_META:
+		sr_dbg("bus: Received SR_DF_META packet.");
+		break;
+	case SR_DF_TRIGGER:
+		sr_dbg("bus: Received SR_DF_TRIGGER packet.");
+		break;
+	case SR_DF_LOGIC:
+		logic = packet->payload;
+		sr_dbg("bus: Received SR_DF_LOGIC packet (%" PRIu64 " bytes, "
+		       "unitsize = %d).", logic->length, logic->unitsize);
+		break;
+	case SR_DF_ANALOG_OLD:
+		analog_old = packet->payload;
+		sr_dbg("bus: Received SR_DF_ANALOG_OLD packet (%d samples).",
+		       analog_old->num_samples);
+		break;
+	case SR_DF_FRAME_BEGIN:
+		sr_dbg("bus: Received SR_DF_FRAME_BEGIN packet.");
+		break;
+	case SR_DF_FRAME_END:
+		sr_dbg("bus: Received SR_DF_FRAME_END packet.");
+		break;
+	case SR_DF_ANALOG:
+		analog = packet->payload;
+		sr_dbg("bus: Received SR_DF_ANALOG packet (%d samples).",
+		       analog->num_samples);
+		break;
+	default:
+		sr_dbg("bus: Received unknown packet type: %d.", packet->type);
+		break;
+	}
+}
+
+/**
+ * Send a packet to whatever is listening on the datafeed bus.
+ *
+ * Hardware drivers use this to send a data packet to the frontend.
+ *
+ * @param sdi TODO.
+ * @param packet The datafeed packet to send to the session bus.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @private
+ */
+SR_PRIV int sr_session_send(const struct sr_dev_inst *sdi,
+		const struct sr_datafeed_packet *packet)
+{
+	GSList *l;
+	struct datafeed_callback *cb_struct;
+	struct sr_datafeed_packet *packet_in, *packet_out;
+	struct sr_transform *t;
+	int ret;
+
+	if (!sdi) {
+		sr_err("%s: sdi was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	if (!packet) {
+		sr_err("%s: packet was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+
+	if (!sdi->session) {
+		sr_err("%s: session was NULL", __func__);
+		return SR_ERR_BUG;
+	}
+
+	if (packet->type == SR_DF_ANALOG_OLD) {
+		/* Convert to SR_DF_ANALOG. */
+		const struct sr_datafeed_analog_old *analog_old = packet->payload;
+		struct sr_analog_encoding encoding;
+		struct sr_analog_meaning meaning;
+		struct sr_analog_spec spec;
+		struct sr_datafeed_analog analog;
+		struct sr_datafeed_packet new_packet;
+		new_packet.type = SR_DF_ANALOG;
+		new_packet.payload = &analog;
+		analog.data = analog_old->data;
+		analog.num_samples = analog_old->num_samples;
+		analog.encoding = &encoding;
+		analog.meaning = &meaning;
+		analog.spec = &spec;
+		encoding.unitsize = sizeof(float);
+		encoding.is_signed = TRUE;
+		encoding.is_float = TRUE;
+#ifdef WORDS_BIGENDIAN
+		encoding.is_bigendian = TRUE;
+#else
+		encoding.is_bigendian = FALSE;
+#endif
+		encoding.digits = 0;
+		encoding.is_digits_decimal = FALSE;
+		encoding.scale.p = 1;
+		encoding.scale.q = 1;
+		encoding.offset.p = 0;
+		encoding.offset.q = 1;
+		meaning.mq = analog_old->mq;
+		meaning.unit = analog_old->unit;
+		meaning.mqflags = analog_old->mqflags;
+		meaning.channels = analog_old->channels;
+		spec.spec_digits = 0;
+		return sr_session_send(sdi, &new_packet);
+	}
+
+	/*
+	 * Pass the packet to the first transform module. If that returns
+	 * another packet (instead of NULL), pass that packet to the next
+	 * transform module in the list, and so on.
+	 */
+	packet_in = (struct sr_datafeed_packet *)packet;
+	for (l = sdi->session->transforms; l; l = l->next) {
+		t = l->data;
+		sr_spew("Running transform module '%s'.", t->module->id);
+		ret = t->module->receive(t, packet_in, &packet_out);
+		if (ret < 0) {
+			sr_err("Error while running transform module: %d.", ret);
+			return SR_ERR;
+		}
+		if (!packet_out) {
+			/*
+			 * If any of the transforms don't return an output
+			 * packet, abort.
+			 */
+			sr_spew("Transform module didn't return a packet, aborting.");
+			return SR_OK;
+		} else {
+			/*
+			 * Use this transform module's output packet as input
+			 * for the next transform module.
+			 */
+			packet_in = packet_out;
+		}
+	}
+	packet = packet_in;
+
+	/*
+	 * If the last transform did output a packet, pass it to all datafeed
+	 * callbacks.
+	 */
+	for (l = sdi->session->datafeed_callbacks; l; l = l->next) {
+		if (sr_log_loglevel_get() >= SR_LOG_DBG)
+			datafeed_dump(packet);
+		cb_struct = l->data;
+		cb_struct->cb(sdi, packet, cb_struct->cb_data);
+	}
+
+	return SR_OK;
+}
+
+/**
+ * Add an event source for a file descriptor.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param key The key which identifies the event source.
+ * @param source An event source object. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR_BUG Event source with @a key already installed.
+ * @retval SR_ERR Other error.
+ *
+ * @private
+ */
+SR_PRIV int sr_session_source_add_internal(struct sr_session *session,
+		void *key, GSource *source)
+{
+	/*
+	 * This must not ever happen, since the source has already been
+	 * created and its finalize() method will remove the key for the
+	 * already installed source. (Well it would, if we did not have
+	 * another sanity check there.)
+	 */
+	if (g_hash_table_contains(session->event_sources, key)) {
+		sr_err("Event source with key %p already exists.", key);
+		return SR_ERR_BUG;
+	}
+	g_hash_table_insert(session->event_sources, key, source);
+
+	if (session_source_attach(session, source) == 0)
+		return SR_ERR;
+
+	return SR_OK;
+}
+
+SR_PRIV int sr_session_fd_source_add(struct sr_session *session,
+		void *key, gintptr fd, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data)
+{
+	GSource *source;
+	int ret;
+
+	source = fd_source_new(session, key, fd, events, timeout);
+	if (!source)
+		return SR_ERR;
+
+	g_source_set_callback(source, (GSourceFunc)cb, cb_data, NULL);
+
+	ret = sr_session_source_add_internal(session, key, source);
+	g_source_unref(source);
+
+	return ret;
+}
+
+/**
+ * Add an event source for a file descriptor.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param fd The file descriptor, or a negative value to create a timer source.
+ * @param events Events to check for.
+ * @param timeout Max time in ms to wait before the callback is called,
+ *                or -1 to wait indefinitely.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.3.0
+ * @private
+ */
+SR_PRIV int sr_session_source_add(struct sr_session *session, int fd,
+		int events, int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+	if (fd < 0 && timeout < 0) {
+		sr_err("Cannot create timer source without timeout.");
+		return SR_ERR_ARG;
+	}
+	return sr_session_fd_source_add(session, GINT_TO_POINTER(fd),
+			fd, events, timeout, cb, cb_data);
+}
+
+/**
+ * Add an event source for a GPollFD.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param pollfd The GPollFD. Must not be NULL.
+ * @param timeout Max time in ms to wait before the callback is called,
+ *                or -1 to wait indefinitely.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.3.0
+ * @private
+ */
+SR_PRIV int sr_session_source_add_pollfd(struct sr_session *session,
+		GPollFD *pollfd, int timeout, sr_receive_data_callback cb,
+		void *cb_data)
+{
+	if (!pollfd) {
+		sr_err("%s: pollfd was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+	return sr_session_fd_source_add(session, pollfd, pollfd->fd,
+			pollfd->events, timeout, cb, cb_data);
+}
+
+/**
+ * Add an event source for a GIOChannel.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param channel The GIOChannel.
+ * @param events Events to poll on.
+ * @param timeout Max time in ms to wait before the callback is called,
+ *                or -1 to wait indefinitely.
+ * @param cb Callback function to add. Must not be NULL.
+ * @param cb_data Data for the callback function. Can be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ *
+ * @since 0.3.0
+ * @private
+ */
+SR_PRIV int sr_session_source_add_channel(struct sr_session *session,
+		GIOChannel *channel, int events, int timeout,
+		sr_receive_data_callback cb, void *cb_data)
+{
+	GPollFD pollfd;
+
+	if (!channel) {
+		sr_err("%s: channel was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+	/* We should be using g_io_create_watch(), but can't without
+	 * changing the driver API, as the callback signature is different.
+	 */
+#ifdef G_OS_WIN32
+	g_io_channel_win32_make_pollfd(channel, events, &pollfd);
+#else
+	pollfd.fd = g_io_channel_unix_get_fd(channel);
+	pollfd.events = events;
+#endif
+	return sr_session_fd_source_add(session, channel, pollfd.fd,
+			pollfd.events, timeout, cb, cb_data);
+}
+
+/**
+ * Remove the source identified by the specified poll object.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param key The key by which the source is identified.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_BUG No event source for poll_object found.
+ *
+ * @private
+ */
+SR_PRIV int sr_session_source_remove_internal(struct sr_session *session,
+		void *key)
+{
+	GSource *source;
+
+	source = g_hash_table_lookup(session->event_sources, key);
+	/*
+	 * Trying to remove an already removed event source is problematic
+	 * since the poll_object handle may have been reused in the meantime.
+	 */
+	if (!source) {
+		sr_warn("Cannot remove non-existing event source %p.", key);
+		return SR_ERR_BUG;
+	}
+	g_source_destroy(source);
+
+	return SR_OK;
+}
+
+/**
+ * Remove the source belonging to the specified file descriptor.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param fd The file descriptor for which the source should be removed.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_ARG Invalid argument
+ * @retval SR_ERR_BUG Internal error.
+ *
+ * @since 0.3.0
+ * @private
+ */
+SR_PRIV int sr_session_source_remove(struct sr_session *session, int fd)
+{
+	return sr_session_source_remove_internal(session, GINT_TO_POINTER(fd));
+}
+
+/**
+ * Remove the source belonging to the specified poll descriptor.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param pollfd The poll descriptor for which the source should be removed.
+ *               Must not be NULL.
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
+ *         SR_ERR_MALLOC upon memory allocation errors, SR_ERR_BUG upon
+ *         internal errors.
+ *
+ * @since 0.2.0
+ * @private
+ */
+SR_PRIV int sr_session_source_remove_pollfd(struct sr_session *session,
+		GPollFD *pollfd)
+{
+	if (!pollfd) {
+		sr_err("%s: pollfd was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+	return sr_session_source_remove_internal(session, pollfd);
+}
+
+/**
+ * Remove the source belonging to the specified channel.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param channel The channel for which the source should be removed.
+ *                Must not be NULL.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @return SR_ERR_BUG Internal error.
+ *
+ * @since 0.2.0
+ * @private
+ */
+SR_PRIV int sr_session_source_remove_channel(struct sr_session *session,
+		GIOChannel *channel)
+{
+	if (!channel) {
+		sr_err("%s: channel was NULL", __func__);
+		return SR_ERR_ARG;
+	}
+	return sr_session_source_remove_internal(session, channel);
+}
+
+/** Unregister an event source that has been destroyed.
+ *
+ * This is intended to be called from a source's finalize() method.
+ *
+ * @param session The session to use. Must not be NULL.
+ * @param key The key used to identify @a source.
+ * @param source The source object that was destroyed.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_BUG Event source for @a key does not match @a source.
+ * @retval SR_ERR Other error.
+ *
+ * @private
+ */
+SR_PRIV int sr_session_source_destroyed(struct sr_session *session,
+		void *key, GSource *source)
+{
+	GSource *registered_source;
+
+	registered_source = g_hash_table_lookup(session->event_sources, key);
+	/*
+	 * Trying to remove an already removed event source is problematic
+	 * since the poll_object handle may have been reused in the meantime.
+	 */
+	if (!registered_source) {
+		sr_err("No event source for key %p found.", key);
+		return SR_ERR_BUG;
+	}
+	if (registered_source != source) {
+		sr_err("Event source for key %p does not match"
+			" destroyed source.", key);
+		return SR_ERR_BUG;
+	}
+	g_hash_table_remove(session->event_sources, key);
+
+	if (g_hash_table_size(session->event_sources) > 0)
+		return SR_OK;
+
+	/* If no event sources are left, consider the acquisition finished.
+	 * This is pretty crude, as it requires all event sources to be
+	 * registered via the libsigrok API.
+	 */
+	return stop_check_later(session);
+}
+
+static void copy_src(struct sr_config *src, struct sr_datafeed_meta *meta_copy)
+{
+	g_variant_ref(src->data);
+	meta_copy->config = g_slist_append(meta_copy->config,
+	                                   g_memdup(src, sizeof(struct sr_config)));
+}
+
+SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
+		struct sr_datafeed_packet **copy)
+{
+	const struct sr_datafeed_meta *meta;
+	struct sr_datafeed_meta *meta_copy;
+	const struct sr_datafeed_logic *logic;
+	struct sr_datafeed_logic *logic_copy;
+	const struct sr_datafeed_analog_old *analog_old;
+	struct sr_datafeed_analog_old *analog_old_copy;
+	const struct sr_datafeed_analog *analog;
+	struct sr_datafeed_analog *analog_copy;
+	uint8_t *payload;
+
+	*copy = g_malloc0(sizeof(struct sr_datafeed_packet));
+	(*copy)->type = packet->type;
+
+	switch (packet->type) {
+	case SR_DF_TRIGGER:
+	case SR_DF_END:
+		/* No payload. */
+		break;
+	case SR_DF_HEADER:
+		payload = g_malloc(sizeof(struct sr_datafeed_header));
+		memcpy(payload, packet->payload, sizeof(struct sr_datafeed_header));
+		(*copy)->payload = payload;
+		break;
+	case SR_DF_META:
+		meta = packet->payload;
+		meta_copy = g_malloc0(sizeof(struct sr_datafeed_meta));
+		g_slist_foreach(meta->config, (GFunc)copy_src, meta_copy->config);
+		(*copy)->payload = meta_copy;
+		break;
+	case SR_DF_LOGIC:
+		logic = packet->payload;
+		logic_copy = g_malloc(sizeof(logic));
+		logic_copy->length = logic->length;
+		logic_copy->unitsize = logic->unitsize;
+		memcpy(logic_copy->data, logic->data, logic->length * logic->unitsize);
+		(*copy)->payload = logic_copy;
+		break;
+	case SR_DF_ANALOG_OLD:
+		analog_old = packet->payload;
+		analog_old_copy = g_malloc(sizeof(analog_old));
+		analog_old_copy->channels = g_slist_copy(analog_old->channels);
+		analog_old_copy->num_samples = analog_old->num_samples;
+		analog_old_copy->mq = analog_old->mq;
+		analog_old_copy->unit = analog_old->unit;
+		analog_old_copy->mqflags = analog_old->mqflags;
+		analog_old_copy->data = g_malloc(analog_old->num_samples * sizeof(float));
+		memcpy(analog_old_copy->data, analog_old->data,
+				analog_old->num_samples * sizeof(float));
+		(*copy)->payload = analog_old_copy;
+		break;
+	case SR_DF_ANALOG:
+		analog = packet->payload;
+		analog_copy = g_malloc(sizeof(analog));
+		analog_copy->data = g_malloc(
+				analog->encoding->unitsize * analog->num_samples);
+		memcpy(analog_copy->data, analog->data,
+				analog->encoding->unitsize * analog->num_samples);
+		analog_copy->num_samples = analog->num_samples;
+		analog_copy->encoding = g_memdup(analog->encoding,
+				sizeof(struct sr_analog_encoding));
+		analog_copy->meaning = g_memdup(analog->meaning,
+				sizeof(struct sr_analog_meaning));
+		analog_copy->meaning->channels = g_slist_copy(
+				analog->meaning->channels);
+		analog_copy->spec = g_memdup(analog->spec,
+				sizeof(struct sr_analog_spec));
+		(*copy)->payload = analog_copy;
+		break;
+	default:
+		sr_err("Unknown packet type %d", packet->type);
+		return SR_ERR;
+	}
+
+	return SR_OK;
+}
+
+void sr_packet_free(struct sr_datafeed_packet *packet)
+{
+	const struct sr_datafeed_meta *meta;
+	const struct sr_datafeed_logic *logic;
+	const struct sr_datafeed_analog_old *analog_old;
+	const struct sr_datafeed_analog *analog;
+	struct sr_config *src;
+	GSList *l;
+
+	switch (packet->type) {
+	case SR_DF_TRIGGER:
+	case SR_DF_END:
+		/* No payload. */
+		break;
+	case SR_DF_HEADER:
+		/* Payload is a simple struct. */
+		g_free((void *)packet->payload);
+		break;
+	case SR_DF_META:
+		meta = packet->payload;
+		for (l = meta->config; l; l = l->next) {
+			src = l->data;
+			g_variant_unref(src->data);
+			g_free(src);
+		}
+		g_slist_free(meta->config);
+		g_free((void *)packet->payload);
+		break;
+	case SR_DF_LOGIC:
+		logic = packet->payload;
+		g_free(logic->data);
+		g_free((void *)packet->payload);
+		break;
+	case SR_DF_ANALOG_OLD:
+		analog_old = packet->payload;
+		g_slist_free(analog_old->channels);
+		g_free(analog_old->data);
+		g_free((void *)packet->payload);
+		break;
+	case SR_DF_ANALOG:
+		analog = packet->payload;
+		g_free(analog->data);
+		g_free(analog->encoding);
+		g_slist_free(analog->meaning->channels);
+		g_free(analog->meaning);
+		g_free(analog->spec);
+		g_free((void *)packet->payload);
+		break;
+	default:
+		sr_err("Unknown packet type %d", packet->type);
+	}
+	g_free(packet);
+
+}
+
+/** @} */
diff --git a/session_driver.c b/src/session_driver.c
similarity index 51%
rename from session_driver.c
rename to src/session_driver.c
index fe0db3e..6befa78 100644
--- a/session_driver.c
+++ b/src/session_driver.c
@@ -17,13 +17,14 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <sys/time.h>
 #include <zip.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "virtual-session"
@@ -33,6 +34,8 @@
 #define CHUNKSIZE (512 * 1024)
 /** @endcond */
 
+SR_PRIV struct sr_dev_driver session_driver_info;
+
 struct session_vdev {
 	char *sessionfile;
 	char *capturefile;
@@ -46,154 +49,165 @@ struct session_vdev {
 	gboolean finished;
 };
 
-static GSList *dev_insts = NULL;
-static const int hwcaps[] = {
-	SR_CONF_CAPTUREFILE,
-	SR_CONF_CAPTURE_UNITSIZE,
-	SR_CONF_SAMPLERATE,
+static const uint32_t devopts[] = {
+	SR_CONF_CAPTUREFILE | SR_CONF_SET,
+	SR_CONF_CAPTURE_UNITSIZE | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_NUM_LOGIC_CHANNELS | SR_CONF_SET,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_SESSIONFILE | SR_CONF_SET,
 };
 
-static int receive_data(int fd, int revents, void *cb_data)
+static gboolean stream_session_data(struct sr_dev_inst *sdi)
 {
-	struct sr_dev_inst *sdi;
 	struct session_vdev *vdev;
 	struct sr_datafeed_packet packet;
 	struct sr_datafeed_logic logic;
 	struct zip_stat zs;
-	GSList *l;
 	int ret, got_data;
 	char capturefile[16];
 	void *buf;
 
-	(void)fd;
-	(void)revents;
-
 	got_data = FALSE;
-	for (l = dev_insts; l; l = l->next) {
-		sdi = l->data;
-		vdev = sdi->priv;
-		if (vdev->finished)
-			/* Already done with this instance. */
-			continue;
-
-		if (!vdev->capfile) {
-			/* No capture file opened yet, or finished with the last
-			 * chunked one. */
-			if (vdev->cur_chunk == 0) {
-				/* capturefile is always the unchunked base name. */
-				if (zip_stat(vdev->archive, vdev->capturefile, 0, &zs) != -1) {
-					/* No chunks, just a single capture file. */
-					vdev->cur_chunk = 0;
-					if (!(vdev->capfile = zip_fopen(vdev->archive,
-							vdev->capturefile, 0)))
-						return FALSE;
-						sr_dbg("Opened %s.", vdev->capturefile);
-				} else {
-					/* Try as first chunk filename. */
-					snprintf(capturefile, 15, "%s-1", vdev->capturefile);
-					if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
-						vdev->cur_chunk = 1;
-						if (!(vdev->capfile = zip_fopen(vdev->archive,
-								capturefile, 0)))
-							return FALSE;
-						sr_dbg("Opened %s.", capturefile);
-					} else {
-						sr_err("No capture file '%s' in " "session file '%s'.",
-								vdev->capturefile, vdev->sessionfile);
-						return FALSE;
-					}
-				}
+	vdev = sdi->priv;
+	if (!vdev->capfile) {
+		/* No capture file opened yet, or finished with the last
+		 * chunked one. */
+		if (vdev->cur_chunk == 0) {
+			/* capturefile is always the unchunked base name. */
+			if (zip_stat(vdev->archive, vdev->capturefile, 0, &zs) != -1) {
+				/* No chunks, just a single capture file. */
+				vdev->cur_chunk = 0;
+				if (!(vdev->capfile = zip_fopen(vdev->archive,
+						vdev->capturefile, 0)))
+					return FALSE;
+				sr_dbg("Opened %s.", vdev->capturefile);
 			} else {
-				/* Capture data is chunked, advance to the next chunk. */
-				vdev->cur_chunk++;
-				snprintf(capturefile, 15, "%s-%d", vdev->capturefile,
-						vdev->cur_chunk);
+				/* Try as first chunk filename. */
+				snprintf(capturefile, 15, "%s-1", vdev->capturefile);
 				if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
+					vdev->cur_chunk = 1;
 					if (!(vdev->capfile = zip_fopen(vdev->archive,
 							capturefile, 0)))
 						return FALSE;
 					sr_dbg("Opened %s.", capturefile);
 				} else {
-					/* We got all the chunks, finish up. */
-					vdev->finished = TRUE;
-					continue;
+					sr_err("No capture file '%s' in " "session file '%s'.",
+							vdev->capturefile, vdev->sessionfile);
+					return FALSE;
 				}
 			}
-		}
-
-		if (!(buf = g_try_malloc(CHUNKSIZE))) {
-			sr_err("%s: buf malloc failed", __func__);
-			return FALSE;
-		}
-
-		ret = zip_fread(vdev->capfile, buf,
-				CHUNKSIZE / vdev->unitsize * vdev->unitsize);
-		if (ret > 0) {
-			if (ret % vdev->unitsize != 0)
-				sr_warn("Read size %d not a multiple of the"
-					" unit size %d.", ret, vdev->unitsize);
-			got_data = TRUE;
-			packet.type = SR_DF_LOGIC;
-			packet.payload = &logic;
-			logic.length = ret;
-			logic.unitsize = vdev->unitsize;
-			logic.data = buf;
-			vdev->bytes_read += ret;
-			sr_session_send(cb_data, &packet);
 		} else {
-			/* done with this capture file */
-			zip_fclose(vdev->capfile);
-			vdev->capfile = NULL;
-			if (vdev->cur_chunk == 0) {
-				/* It was the only file. */
-				vdev->finished = TRUE;
+			/* Capture data is chunked, advance to the next chunk. */
+			vdev->cur_chunk++;
+			snprintf(capturefile, 15, "%s-%d", vdev->capturefile,
+					vdev->cur_chunk);
+			if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
+				if (!(vdev->capfile = zip_fopen(vdev->archive,
+						capturefile, 0)))
+					return FALSE;
+				sr_dbg("Opened %s.", capturefile);
 			} else {
-				/* There might be more chunks, so don't fall through
-				 * to the SR_DF_END here. */
-				g_free(buf);
-				return TRUE;
+				/* We got all the chunks, finish up. */
+				return FALSE;
 			}
 		}
-		g_free(buf);
 	}
 
-	if (!got_data) {
-		packet.type = SR_DF_END;
-		sr_session_send(cb_data, &packet);
-		sr_session_source_remove(-1);
+	buf = g_malloc(CHUNKSIZE);
+
+	ret = zip_fread(vdev->capfile, buf,
+			CHUNKSIZE / vdev->unitsize * vdev->unitsize);
+	if (ret > 0) {
+		if (ret % vdev->unitsize != 0)
+			sr_warn("Read size %d not a multiple of the"
+				" unit size %d.", ret, vdev->unitsize);
+		got_data = TRUE;
+		packet.type = SR_DF_LOGIC;
+		packet.payload = &logic;
+		logic.length = ret;
+		logic.unitsize = vdev->unitsize;
+		logic.data = buf;
+		vdev->bytes_read += ret;
+		sr_session_send(sdi, &packet);
+	} else {
+		/* done with this capture file */
+		zip_fclose(vdev->capfile);
+		vdev->capfile = NULL;
+		if (vdev->cur_chunk != 0) {
+			/* There might be more chunks, so don't fall through
+			 * to the SR_DF_END here. */
+			got_data = TRUE;
+		}
+	}
+	g_free(buf);
+
+	return got_data;
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct session_vdev *vdev;
+	struct sr_datafeed_packet packet;
+
+	(void)fd;
+	(void)revents;
+
+	sdi = cb_data;
+	vdev = sdi->priv;
+
+	if (!vdev->finished && !stream_session_data(sdi))
+		vdev->finished = TRUE;
+	if (!vdev->finished)
+		return G_SOURCE_CONTINUE;
+
+	if (vdev->capfile) {
+		zip_fclose(vdev->capfile);
+		vdev->capfile = NULL;
+	}
+	if (vdev->archive) {
+		zip_discard(vdev->archive);
+		vdev->archive = NULL;
 	}
+	packet.type = SR_DF_END;
+	packet.payload = NULL;
+	sr_session_send(sdi, &packet);
 
-	return TRUE;
+	return G_SOURCE_REMOVE;
 }
 
 /* driver callbacks */
 
-static int init(struct sr_context *sr_ctx)
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 {
-	(void)sr_ctx;
-
-	return SR_OK;
+	return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static int dev_clear(void)
+static int dev_clear(const struct sr_dev_driver *di)
 {
+	struct drv_context *drvc;
 	GSList *l;
 
-	for (l = dev_insts; l; l = l->next)
+	drvc = di->context;
+	for (l = drvc->instances; l; l = l->next)
 		sr_dev_inst_free(l->data);
-	g_slist_free(dev_insts);
-	dev_insts = NULL;
+	g_slist_free(drvc->instances);
+	drvc->instances = NULL;
 
 	return SR_OK;
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
+	struct sr_dev_driver *di;
+	struct drv_context *drvc;
 	struct session_vdev *vdev;
 
-	vdev = g_try_malloc0(sizeof(struct session_vdev));
+	di = sdi->driver;
+	drvc = di->context;
+	vdev = g_malloc0(sizeof(struct session_vdev));
 	sdi->priv = vdev;
-	dev_insts = g_slist_append(dev_insts, sdi);
+	drvc->instances = g_slist_append(drvc->instances, sdi);
 
 	return SR_OK;
 }
@@ -210,20 +224,24 @@ static int dev_close(struct sr_dev_inst *sdi)
 	return SR_OK;
 }
 
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct session_vdev *vdev;
 
 	(void)cg;
 
-	switch (id) {
+	if (!sdi)
+		return SR_ERR;
+
+	vdev = sdi->priv;
+
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
-		if (sdi) {
-			vdev = sdi->priv;
-			*data = g_variant_new_uint64(vdev->samplerate);
-		} else
-			return SR_ERR;
+		*data = g_variant_new_uint64(vdev->samplerate);
+		break;
+	case SR_CONF_CAPTURE_UNITSIZE:
+		*data = g_variant_new_uint64(vdev->unitsize);
 		break;
 	default:
 		return SR_ERR_NA;
@@ -232,7 +250,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	struct session_vdev *vdev;
@@ -241,7 +259,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
 	vdev = sdi->priv;
 
-	switch (id) {
+	switch (key) {
 	case SR_CONF_SAMPLERATE:
 		vdev->samplerate = g_variant_get_uint64(data);
 		sr_info("Setting samplerate to %" PRIu64 ".", vdev->samplerate);
@@ -260,7 +278,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 		vdev->unitsize = g_variant_get_uint64(data);
 		break;
 	case SR_CONF_NUM_LOGIC_CHANNELS:
-		vdev->num_channels = g_variant_get_uint64(data);
+		vdev->num_channels = g_variant_get_int32(data);
 		break;
 	default:
 		return SR_ERR_NA;
@@ -269,7 +287,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 	return SR_OK;
 }
 
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
 		const struct sr_channel_group *cg)
 {
 	(void)sdi;
@@ -277,8 +295,8 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *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));
+		*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+				devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
 		break;
 	default:
 		return SR_ERR_NA;
@@ -292,8 +310,9 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	struct session_vdev *vdev;
 	int ret;
 
-	vdev = sdi->priv;
+	(void)cb_data;
 
+	vdev = sdi->priv;
 	vdev->bytes_read = 0;
 	vdev->cur_chunk = 0;
 	vdev->finished = FALSE;
@@ -308,10 +327,22 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 	}
 
 	/* Send header packet to the session bus. */
-	std_session_send_df_header(cb_data, LOG_PREFIX);
+	std_session_send_df_header(sdi, LOG_PREFIX);
 
 	/* freewheeling source */
-	sr_session_source_add(-1, 0, 0, receive_data, cb_data);
+	sr_session_source_add(sdi->session, -1, 0, 0, receive_data, (void *)sdi);
+
+	return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+	struct session_vdev *vdev;
+
+	(void)cb_data;
+	vdev = sdi->priv;
+
+	vdev->finished = TRUE;
 
 	return SR_OK;
 }
@@ -332,6 +363,6 @@ SR_PRIV struct sr_dev_driver session_driver = {
 	.dev_open = dev_open,
 	.dev_close = dev_close,
 	.dev_acquisition_start = dev_acquisition_start,
-	.dev_acquisition_stop = NULL,
-	.priv = NULL,
+	.dev_acquisition_stop = dev_acquisition_stop,
+	.context = NULL,
 };
diff --git a/src/session_file.c b/src/session_file.c
new file mode 100644
index 0000000..149e6d1
--- /dev/null
+++ b/src/session_file.c
@@ -0,0 +1,322 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+#include <zip.h>
+#include <errno.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "session-file"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Loading and saving libsigrok session files.
+ */
+
+/**
+ * @addtogroup grp_session
+ *
+ * @{
+ */
+
+extern SR_PRIV struct sr_dev_driver session_driver;
+static int session_driver_initialized = 0;
+
+#if !HAVE_ZIP_DISCARD
+/* Replacement for zip_discard() if it isn't available.
+ */
+SR_PRIV void sr_zip_discard(struct zip *archive)
+{
+	if (zip_unchange_all(archive) < 0 || zip_close(archive) < 0)
+		sr_err("Failed to discard ZIP archive: %s", zip_strerror(archive));
+}
+#endif
+
+/** Read metadata entries from a session archive.
+ * @param[in] archive An open ZIP archive.
+ * @param[in] entry Stat buffer filled in for the metadata archive member.
+ * @return A new key/value store containing the session metadata.
+ */
+SR_PRIV GKeyFile *sr_sessionfile_read_metadata(struct zip *archive,
+			const struct zip_stat *entry)
+{
+	GKeyFile *keyfile;
+	GError *error;
+	struct zip_file *zf;
+	char *metabuf;
+	int metalen;
+
+	if (entry->size > G_MAXINT || !(metabuf = g_try_malloc(entry->size))) {
+		sr_err("Metadata buffer allocation failed.");
+		return NULL;
+	}
+	zf = zip_fopen_index(archive, entry->index, 0);
+	if (!zf) {
+		sr_err("Failed to open metadata: %s", zip_strerror(archive));
+		g_free(metabuf);
+		return NULL;
+	}
+	metalen = zip_fread(zf, metabuf, entry->size);
+	if (metalen < 0) {
+		sr_err("Failed to read metadata: %s", zip_file_strerror(zf));
+		zip_fclose(zf);
+		g_free(metabuf);
+		return NULL;
+	}
+	zip_fclose(zf);
+
+	keyfile = g_key_file_new();
+	error = NULL;
+	g_key_file_load_from_data(keyfile, metabuf, metalen,
+			G_KEY_FILE_NONE, &error);
+	g_free(metabuf);
+
+	if (error) {
+		sr_err("Failed to parse metadata: %s", error->message);
+		g_error_free(error);
+		g_key_file_free(keyfile);
+		return NULL;
+	}
+	return keyfile;
+}
+
+/** @private */
+SR_PRIV int sr_sessionfile_check(const char *filename)
+{
+	struct zip *archive;
+	struct zip_file *zf;
+	struct zip_stat zs;
+	uint64_t version;
+	int ret;
+	char s[11];
+
+	if (!filename)
+		return SR_ERR_ARG;
+
+	if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
+		sr_err("Not a regular file: %s.", filename);
+		return SR_ERR;
+	}
+
+	if (!(archive = zip_open(filename, 0, NULL)))
+		/* No logging: this can be used just to check if it's
+		 * a sigrok session file or not. */
+		return SR_ERR;
+
+	/* check "version" */
+	if (!(zf = zip_fopen(archive, "version", 0))) {
+		sr_dbg("Not a sigrok session file: no version found.");
+		zip_discard(archive);
+		return SR_ERR;
+	}
+	ret = zip_fread(zf, s, sizeof(s) - 1);
+	if (ret < 0) {
+		sr_err("Failed to read version file: %s",
+			zip_file_strerror(zf));
+		zip_fclose(zf);
+		zip_discard(archive);
+		return SR_ERR;
+	}
+	zip_fclose(zf);
+	s[ret] = '\0';
+	version = g_ascii_strtoull(s, NULL, 10);
+	if (version == 0 || version > 2) {
+		sr_dbg("Cannot handle sigrok session file version %" PRIu64 ".",
+			version);
+		zip_discard(archive);
+		return SR_ERR;
+	}
+	sr_spew("Detected sigrok session file version %" PRIu64 ".", version);
+
+	/* read "metadata" */
+	if (zip_stat(archive, "metadata", 0, &zs) < 0) {
+		sr_dbg("Not a valid sigrok session file.");
+		zip_discard(archive);
+		return SR_ERR;
+	}
+	zip_discard(archive);
+
+	return SR_OK;
+}
+
+/**
+ * Load the session from the specified filename.
+ *
+ * @param ctx The context in which to load the session.
+ * @param filename The name of the session file to load.
+ * @param session The session to load the file into.
+ *
+ * @retval SR_OK Success
+ * @retval SR_ERR_MALLOC Memory allocation error
+ * @retval SR_ERR_DATA Malformed session file
+ * @retval SR_ERR This is not a session file
+ */
+SR_API int sr_session_load(struct sr_context *ctx, const char *filename,
+		struct sr_session **session)
+{
+	GKeyFile *kf;
+	GError *error;
+	struct zip *archive;
+	struct zip_stat zs;
+	struct sr_dev_inst *sdi;
+	struct sr_channel *ch;
+	int ret, i, j;
+	uint64_t tmp_u64;
+	int total_channels, k;
+	int unitsize;
+	char **sections, **keys, *val;
+	char channelname[SR_MAX_CHANNELNAME_LEN + 1];
+
+	if ((ret = sr_sessionfile_check(filename)) != SR_OK)
+		return ret;
+
+	if (!(archive = zip_open(filename, 0, NULL)))
+		return SR_ERR;
+
+	if (zip_stat(archive, "metadata", 0, &zs) < 0) {
+		zip_discard(archive);
+		return SR_ERR;
+	}
+	kf = sr_sessionfile_read_metadata(archive, &zs);
+	zip_discard(archive);
+	if (!kf)
+		return SR_ERR_DATA;
+
+	if ((ret = sr_session_new(ctx, session)) != SR_OK) {
+		g_key_file_free(kf);
+		return ret;
+	}
+
+	error = NULL;
+	ret = SR_OK;
+	sections = g_key_file_get_groups(kf, NULL);
+	for (i = 0; sections[i] && ret == SR_OK; i++) {
+		if (!strcmp(sections[i], "global"))
+			/* nothing really interesting in here yet */
+			continue;
+		if (!strncmp(sections[i], "device ", 7)) {
+			/* device section */
+			sdi = NULL;
+			keys = g_key_file_get_keys(kf, sections[i], NULL, NULL);
+			for (j = 0; keys[j]; j++) {
+				if (!strcmp(keys[j], "capturefile")) {
+					val = g_key_file_get_string(kf, sections[i],
+							keys[j], &error);
+					if (!val) {
+						ret = SR_ERR_DATA;
+						break;
+					}
+					sdi = g_malloc0(sizeof(struct sr_dev_inst));
+					sdi->driver = &session_driver;
+					sdi->status = SR_ST_ACTIVE;
+					if (!session_driver_initialized) {
+						/* first device, init the driver */
+						session_driver_initialized = 1;
+						sdi->driver->init(sdi->driver, NULL);
+					}
+					sr_dev_open(sdi);
+					sr_session_dev_add(*session, sdi);
+					(*session)->owned_devs = g_slist_append(
+							(*session)->owned_devs, sdi);
+					sr_config_set(sdi, NULL, SR_CONF_SESSIONFILE,
+							g_variant_new_string(filename));
+					sr_config_set(sdi, NULL, SR_CONF_CAPTUREFILE,
+							g_variant_new_string(val));
+					g_free(val);
+				} else if (!strcmp(keys[j], "samplerate")) {
+					val = g_key_file_get_string(kf, sections[i],
+							keys[j], &error);
+					if (!sdi || !val || sr_parse_sizestring(val,
+								&tmp_u64) != SR_OK) {
+						g_free(val);
+						ret = SR_ERR_DATA;
+						break;
+					}
+					g_free(val);
+					sr_config_set(sdi, NULL, SR_CONF_SAMPLERATE,
+							g_variant_new_uint64(tmp_u64));
+				} else if (!strcmp(keys[j], "unitsize")) {
+					unitsize = g_key_file_get_integer(kf, sections[i],
+							keys[j], &error);
+					if (!sdi || unitsize <= 0 || error) {
+						ret = SR_ERR_DATA;
+						break;
+					}
+					sr_config_set(sdi, NULL, SR_CONF_CAPTURE_UNITSIZE,
+							g_variant_new_uint64(unitsize));
+				} else if (!strcmp(keys[j], "total probes")) {
+					total_channels = g_key_file_get_integer(kf,
+							sections[i], keys[j], &error);
+					if (!sdi || total_channels < 0 || error) {
+						ret = SR_ERR_DATA;
+						break;
+					}
+					sr_config_set(sdi, NULL, SR_CONF_NUM_LOGIC_CHANNELS,
+							g_variant_new_int32(total_channels));
+					for (k = 0; k < total_channels; k++) {
+						g_snprintf(channelname, sizeof(channelname),
+								"%d", k);
+						sr_channel_new(sdi, k, SR_CHANNEL_LOGIC,
+								FALSE, channelname);
+					}
+				} else if (!strncmp(keys[j], "probe", 5)) {
+					tmp_u64 = g_ascii_strtoull(keys[j] + 5, NULL, 10);
+					if (!sdi || tmp_u64 == 0 || tmp_u64 > G_MAXINT) {
+						ret = SR_ERR_DATA;
+						break;
+					}
+					ch = g_slist_nth_data(sdi->channels, tmp_u64 - 1);
+					if (!ch) {
+						ret = SR_ERR_DATA;
+						break;
+					}
+					val = g_key_file_get_string(kf, sections[i],
+							keys[j], &error);
+					if (!val) {
+						ret = SR_ERR_DATA;
+						break;
+					}
+					/* sr_session_save() */
+					sr_dev_channel_name_set(ch, val);
+					g_free(val);
+					sr_dev_channel_enable(ch, TRUE);
+				}
+			}
+			g_strfreev(keys);
+		}
+	}
+	g_strfreev(sections);
+	g_key_file_free(kf);
+
+	if (error) {
+		sr_err("Failed to parse metadata: %s", error->message);
+		g_error_free(error);
+	}
+	return ret;
+}
+
+/** @} */
diff --git a/src/soft-trigger.c b/src/soft-trigger.c
new file mode 100644
index 0000000..50fdd41
--- /dev/null
+++ b/src/soft-trigger.c
@@ -0,0 +1,222 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/* @cond PRIVATE */
+#define LOG_PREFIX "soft-trigger"
+/* @endcond */
+
+SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
+		const struct sr_dev_inst *sdi, struct sr_trigger *trigger,
+		int pre_trigger_samples)
+{
+	struct soft_trigger_logic *stl;
+
+	stl = g_malloc0(sizeof(struct soft_trigger_logic));
+	stl->sdi = sdi;
+	stl->trigger = trigger;
+	stl->unitsize = (g_slist_length(sdi->channels) + 7) / 8;
+	stl->prev_sample = g_malloc0(stl->unitsize);
+	stl->pre_trigger_size = stl->unitsize * pre_trigger_samples;
+	stl->pre_trigger_buffer = g_malloc(stl->pre_trigger_size);
+	stl->pre_trigger_head = stl->pre_trigger_buffer;
+
+	if (stl->pre_trigger_size > 0 && !stl->pre_trigger_buffer) {
+		soft_trigger_logic_free(stl);
+		return NULL;
+	}
+
+	return stl;
+}
+
+SR_PRIV void soft_trigger_logic_free(struct soft_trigger_logic *stl)
+{
+	g_free(stl->pre_trigger_buffer);
+	g_free(stl->prev_sample);
+	g_free(stl);
+}
+
+static void pre_trigger_append(struct soft_trigger_logic *stl,
+		uint8_t *buf, int len)
+{
+	/* Avoid uselessly copying more than the pre-trigger size. */
+	if (len > stl->pre_trigger_size) {
+		buf += len - stl->pre_trigger_size;
+		len = stl->pre_trigger_size;
+	}
+
+	/* Update the filling level of the pre-trigger circular buffer. */
+	stl->pre_trigger_fill = MIN(stl->pre_trigger_fill + len,
+	                            stl->pre_trigger_size);
+
+	/* Actually copy data to the pre-trigger circular buffer. */
+	while (len > 0) {
+		size_t size = MIN(stl->pre_trigger_buffer + stl->pre_trigger_size
+		                  - stl->pre_trigger_head, len);
+		memcpy(stl->pre_trigger_head, buf, size);
+		stl->pre_trigger_head += size;
+		if (stl->pre_trigger_head >= stl->pre_trigger_buffer
+		                             + stl->pre_trigger_size)
+			stl->pre_trigger_head = stl->pre_trigger_buffer;
+		buf += size;
+		len -= size;
+	}
+}
+
+static void pre_trigger_send(struct soft_trigger_logic *stl,
+		int *pre_trigger_samples)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_datafeed_logic logic;
+
+	packet.type = SR_DF_LOGIC;
+	packet.payload = &logic;
+	logic.unitsize = stl->unitsize;
+
+	if (pre_trigger_samples)
+		*pre_trigger_samples = 0;
+
+	/* If pre-trigger buffer not full, rewind head to the first valid sample. */
+	if (stl->pre_trigger_fill < stl->pre_trigger_size)
+		stl->pre_trigger_head = stl->pre_trigger_buffer;
+
+	/* Send logic packets for the pre-trigger circular buffer content. */
+	while (stl->pre_trigger_fill > 0) {
+		size_t size = MIN(stl->pre_trigger_buffer + stl->pre_trigger_size
+		                  - stl->pre_trigger_head, stl->pre_trigger_fill);
+		logic.length = size;
+		logic.data = stl->pre_trigger_head;
+		sr_session_send(stl->sdi, &packet);
+		stl->pre_trigger_head = stl->pre_trigger_buffer;
+		stl->pre_trigger_fill -= size;
+		if (pre_trigger_samples)
+			*pre_trigger_samples += size / stl->unitsize;
+	}
+}
+
+static gboolean logic_check_match(struct soft_trigger_logic *stl,
+		uint8_t *sample, struct sr_trigger_match *match)
+{
+	int bit, prev_bit;
+	gboolean result;
+
+	stl->count++;
+	result = FALSE;
+	bit = *(sample + match->channel->index / 8)
+			& (1 << (match->channel->index % 8));
+	if (match->match == SR_TRIGGER_ZERO)
+		result = bit == 0;
+	else if (match->match == SR_TRIGGER_ONE)
+		result = bit != 0;
+	else {
+		/* Edge matches. */
+		if (stl->count == 1)
+			/* First sample, don't have enough for an edge match yet. */
+			return FALSE;
+		prev_bit = *(stl->prev_sample + match->channel->index / 8)
+				& (1 << (match->channel->index % 8));
+		if (match->match == SR_TRIGGER_RISING)
+			result = prev_bit == 0 && bit != 0;
+		else if (match->match == SR_TRIGGER_FALLING)
+			result = prev_bit != 0 && bit == 0;
+		else if (match->match == SR_TRIGGER_EDGE)
+			result = prev_bit != bit;
+	}
+
+	return result;
+}
+
+/* Returns the offset (in samples) within buf of where the trigger
+ * occurred, or -1 if not triggered. */
+SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *stl,
+		uint8_t *buf, int len, int *pre_trigger_samples)
+{
+	struct sr_datafeed_packet packet;
+	struct sr_trigger_stage *stage;
+	struct sr_trigger_match *match;
+	GSList *l, *l_stage;
+	int offset;
+	int i;
+	gboolean match_found;
+
+	offset = -1;
+	for (i = 0; i < len; i += stl->unitsize) {
+		l_stage = g_slist_nth(stl->trigger->stages, stl->cur_stage);
+		stage = l_stage->data;
+		if (!stage->matches)
+			/* No matches supplied, client error. */
+			return SR_ERR_ARG;
+
+		match_found = TRUE;
+		for (l = stage->matches; l; l = l->next) {
+			match = l->data;
+			if (!match->channel->enabled)
+				/* Ignore disabled channels with a trigger. */
+				continue;
+			if (!logic_check_match(stl, buf + i, match)) {
+				match_found = FALSE;
+				break;
+			}
+		}
+		memcpy(stl->prev_sample, buf + i, stl->unitsize);
+		if (match_found) {
+			/* Matched on the current stage. */
+			if (l_stage->next) {
+				/* Advance to next stage. */
+				stl->cur_stage++;
+			} else {
+				/* Matched on last stage, send pre-trigger data. */
+				pre_trigger_append(stl, buf, i);
+				pre_trigger_send(stl, pre_trigger_samples);
+
+				/* Fire trigger. */
+				offset = i / stl->unitsize;
+
+				packet.type = SR_DF_TRIGGER;
+				packet.payload = NULL;
+				sr_session_send(stl->sdi, &packet);
+				break;
+			}
+		} else if (stl->cur_stage > 0) {
+			/*
+			 * We had a match at an earlier stage, but failed on the
+			 * current stage. However, we may have a match on this
+			 * stage in the next bit -- trigger on 0001 will fail on
+			 * seeing 00001, so we need to go back to stage 0 -- but
+			 * at the next sample from the one that matched originally,
+			 * which the counter increment at the end of the loop
+			 * takes care of.
+			 */
+			i -= stl->cur_stage * stl->unitsize;
+			if (i < -1)
+				i = -1; /* Oops, went back past this buffer. */
+			/* Reset trigger stage. */
+			stl->cur_stage = 0;
+		}
+	}
+
+	if (offset == -1)
+		pre_trigger_append(stl, buf, len);
+
+	return offset;
+}
diff --git a/std.c b/src/std.c
similarity index 94%
rename from std.c
rename to src/std.c
index 1b9c600..5595224 100644
--- a/std.c
+++ b/src/std.c
@@ -23,9 +23,11 @@
   * @internal
   */
 
+#include <config.h>
 #include <glib.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
+#include "scpi.h"
 
 #define LOG_PREFIX "std"
 
@@ -41,8 +43,7 @@
  * @param di The driver instance to use.
  * @param[in] prefix A driver-specific prefix string used for log messages.
  *
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- *         SR_ERR_MALLOC upon memory allocation errors.
+ * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
  */
 SR_PRIV int std_init(struct sr_context *sr_ctx, struct sr_dev_driver *di,
 		     const char *prefix)
@@ -54,14 +55,10 @@ SR_PRIV int std_init(struct sr_context *sr_ctx, struct sr_dev_driver *di,
 		return SR_ERR_ARG;
 	}
 
-	if (!(drvc = g_try_malloc(sizeof(struct drv_context)))) {
-		sr_err("%s: Driver context malloc failed.", prefix);
-		return SR_ERR_MALLOC;
-	}
-
+	drvc = g_malloc0(sizeof(struct drv_context));
 	drvc->sr_ctx = sr_ctx;
 	drvc->instances = NULL;
-	di->priv = drvc;
+	di->context = drvc;
 
 	return SR_OK;
 }
@@ -115,7 +112,7 @@ SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi,
  *
  * This function can be used to implement the dev_open() driver API
  * callback in drivers that use a serial port. The port is opened
- * with the SERIAL_RDWR and SERIAL_NONBLOCK flags.
+ * with the SERIAL_RDWR flag.
  *
  * If the open succeeded, the status field of the given sdi is set
  * to SR_ST_ACTIVE.
@@ -128,7 +125,7 @@ SR_PRIV int std_serial_dev_open(struct sr_dev_inst *sdi)
 	struct sr_serial_dev_inst *serial;
 
 	serial = sdi->conn;
-	if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+	if (serial_open(serial, SERIAL_RDWR) != SR_OK)
 		return SR_ERR;
 
 	sdi->status = SR_ST_ACTIVE;
@@ -200,7 +197,7 @@ SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi,
 
 	sr_dbg("%s: Stopping acquisition.", prefix);
 
-	if ((ret = serial_source_remove(serial)) < 0) {
+	if ((ret = serial_source_remove(sdi->session, serial)) < 0) {
 		sr_err("%s: Failed to remove source: %d.", prefix, ret);
 		return ret;
 	}
@@ -252,7 +249,7 @@ SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
 	GSList *l;
 	int ret;
 
-	if (!(drvc = driver->priv))
+	if (!(drvc = driver->context))
 		/* Driver was never initialized, nothing to do. */
 		return SR_OK;
 
@@ -276,11 +273,16 @@ SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
 #endif
 			if (sdi->inst_type == SR_INST_SCPI)
 				sr_scpi_free(sdi->conn);
+			if (sdi->inst_type == SR_INST_MODBUS)
+				sr_modbus_free(sdi->conn);
 		}
 		if (clear_private)
+			/* The helper function is responsible for freeing
+			 * its own sdi->priv! */
 			clear_private(sdi->priv);
 		else
 			g_free(sdi->priv);
+
 		sr_dev_inst_free(sdi);
 	}
 
diff --git a/strutil.c b/src/strutil.c
similarity index 73%
rename from strutil.c
rename to src/strutil.c
index e63f12e..d817e64 100644
--- a/strutil.c
+++ b/src/strutil.c
@@ -18,11 +18,13 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 #include <errno.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 /** @cond PRIVATE */
@@ -46,7 +48,7 @@
 /**
  * @private
  *
- * Convert a string representation of a numeric value to a long integer. The
+ * Convert a string representation of a numeric value (base 10) to a long integer. The
  * conversion is strict and will fail if the complete string does not represent
  * a valid long integer. The function sets errno according to the details of the
  * failure.
@@ -54,9 +56,8 @@
  * @param str The string representation to convert.
  * @param ret Pointer to long where the result of the conversion will be stored.
  *
- * @return SR_OK if conversion is successful, otherwise SR_ERR.
- *
- * @since 0.3.0
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
  */
 SR_PRIV int sr_atol(const char *str, long *ret)
 {
@@ -64,7 +65,7 @@ SR_PRIV int sr_atol(const char *str, long *ret)
 	char *endptr = NULL;
 
 	errno = 0;
-	tmp = strtol(str, &endptr, 0);
+	tmp = strtol(str, &endptr, 10);
 
 	if (!endptr || *endptr || errno) {
 		if (!errno)
@@ -79,7 +80,7 @@ SR_PRIV int sr_atol(const char *str, long *ret)
 /**
  * @private
  *
- * Convert a string representation of a numeric value to an integer. The
+ * Convert a string representation of a numeric value (base 10) to an integer. The
  * conversion is strict and will fail if the complete string does not represent
  * a valid integer. The function sets errno according to the details of the
  * failure.
@@ -87,9 +88,8 @@ SR_PRIV int sr_atol(const char *str, long *ret)
  * @param str The string representation to convert.
  * @param ret Pointer to int where the result of the conversion will be stored.
  *
- * @return SR_OK if conversion is successful, otherwise SR_ERR.
- *
- * @since 0.3.0
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
  */
 SR_PRIV int sr_atoi(const char *str, int *ret)
 {
@@ -118,9 +118,8 @@ SR_PRIV int sr_atoi(const char *str, int *ret)
  * @param str The string representation to convert.
  * @param ret Pointer to double where the result of the conversion will be stored.
  *
- * @return SR_OK if conversion is successful, otherwise SR_ERR.
- *
- * @since 0.3.0
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
  */
 SR_PRIV int sr_atod(const char *str, double *ret)
 {
@@ -151,9 +150,8 @@ SR_PRIV int sr_atod(const char *str, double *ret)
  * @param str The string representation to convert.
  * @param ret Pointer to float where the result of the conversion will be stored.
  *
- * @return SR_OK if conversion is successful, otherwise SR_ERR.
- *
- * @since 0.3.0
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
  */
 SR_PRIV int sr_atof(const char *str, float *ret)
 {
@@ -182,9 +180,8 @@ SR_PRIV int sr_atof(const char *str, float *ret)
  * @param str The string representation to convert.
  * @param ret Pointer to float where the result of the conversion will be stored.
  *
- * @return SR_OK if conversion is successful, otherwise SR_ERR.
- *
- * @since 0.3.0
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
  */
 SR_PRIV int sr_atof_ascii(const char *str, float *ret)
 {
@@ -225,7 +222,7 @@ SR_PRIV int sr_atof_ascii(const char *str, float *ret)
  * @param unit The unit to append to the string, or NULL if the string
  *             has no units.
  *
- * @return A g_try_malloc()ed string representation of the samplerate value,
+ * @return A newly allocated string representation of the samplerate value,
  *         or NULL upon errors. The caller is responsible to g_free() the
  *         memory.
  *
@@ -241,7 +238,7 @@ SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
 	const char *p, prefix[] = "\0kMGTPE";
 	char fmt[16], fract[20] = "", *f;
 
-	if (unit == NULL)
+	if (!unit)
 		unit = "";
 
 	for (i = 0; (quot = x / divisor[i]) >= 1000; i++);
@@ -267,7 +264,7 @@ SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
  *
  * @param samplerate The samplerate in Hz.
  *
- * @return A g_try_malloc()ed string representation of the samplerate value,
+ * @return A newly allocated string representation of the samplerate value,
  *         or NULL upon errors. The caller is responsible to g_free() the
  *         memory.
  *
@@ -286,7 +283,7 @@ SR_API char *sr_samplerate_string(uint64_t samplerate)
  *
  * @param frequency The frequency in Hz.
  *
- * @return A g_try_malloc()ed string representation of the frequency value,
+ * @return A newly allocated string representation of the frequency value,
  *         or NULL upon errors. The caller is responsible to g_free() the
  *         memory.
  *
@@ -298,10 +295,7 @@ SR_API char *sr_period_string(uint64_t frequency)
 	int r;
 
 	/* Allocate enough for a uint64_t as string + " ms". */
-	if (!(o = g_try_malloc0(30 + 1))) {
-		sr_err("%s: o malloc failed", __func__);
-		return NULL;
-	}
+	o = g_malloc0(30 + 1);
 
 	if (frequency >= SR_GHZ(1))
 		r = snprintf(o, 30, "%" PRIu64 " ns", frequency / 1000000000);
@@ -331,7 +325,7 @@ SR_API char *sr_period_string(uint64_t frequency)
  * @param v_p The voltage numerator.
  * @param v_q The voltage denominator.
  *
- * @return A g_try_malloc()ed string representation of the voltage value,
+ * @return A newly allocated string representation of the voltage value,
  *         or NULL upon errors. The caller is responsible to g_free() the
  *         memory.
  *
@@ -342,10 +336,7 @@ SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
 	int r;
 	char *o;
 
-	if (!(o = g_try_malloc0(30 + 1))) {
-		sr_err("%s: o malloc failed", __func__);
-		return NULL;
-	}
+	o = g_malloc0(30 + 1);
 
 	if (v_q == 1000)
 		r = snprintf(o, 30, "%" PRIu64 "mV", v_p);
@@ -364,102 +355,6 @@ SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
 }
 
 /**
- * Parse a trigger specification string.
- *
- * @param sdi The device instance for which the trigger specification is
- *            intended. Must not be NULL. Also, sdi->driver and
- *            sdi->driver->info_get must not be NULL.
- * @param triggerstring The string containing the trigger specification for
- *        one or more channels of this device. Entries for multiple channels are
- *        comma-separated. Triggers are specified in the form key=value,
- *        where the key is a channel number (or channel name) and the value is
- *        the requested trigger type. Valid trigger types currently
- *        include 'r' (rising edge), 'f' (falling edge), 'c' (any pin value
- *        change), '0' (low value), or '1' (high value).
- *        Example: "1=r,sck=f,miso=0,7=c"
- *
- * @return Pointer to a list of trigger types (strings), or NULL upon errors.
- *         The pointer list (if non-NULL) has as many entries as the
- *         respective device has channels (all physically available channels,
- *         not just enabled ones). Entries of the list which don't have
- *         a trigger value set in 'triggerstring' are NULL, the other entries
- *         contain the respective trigger type which is requested for the
- *         respective channel (e.g. "r", "c", and so on).
- *
- * @since 0.2.0
- */
-SR_API char **sr_parse_triggerstring(const struct sr_dev_inst *sdi,
-				     const char *triggerstring)
-{
-	GSList *l;
-	GVariant *gvar;
-	struct sr_channel *ch;
-	int max_channels, channelnum, i;
-	char **tokens, **triggerlist, *trigger, *tc;
-	const char *trigger_types;
-	gboolean error;
-
-	max_channels = g_slist_length(sdi->channels);
-	error = FALSE;
-
-	if (!(triggerlist = g_try_malloc0(max_channels * sizeof(char *)))) {
-		sr_err("%s: triggerlist malloc failed", __func__);
-		return NULL;
-	}
-
-	if (sdi->driver->config_list(SR_CONF_TRIGGER_TYPE,
-				&gvar, sdi, NULL) != SR_OK) {
-		sr_err("%s: Device doesn't support any triggers.", __func__);
-		return NULL;
-	}
-	trigger_types = g_variant_get_string(gvar, NULL);
-
-	tokens = g_strsplit(triggerstring, ",", max_channels);
-	for (i = 0; tokens[i]; i++) {
-		channelnum = -1;
-		for (l = sdi->channels; l; l = l->next) {
-			ch = (struct sr_channel *)l->data;
-			if (ch->enabled
-				&& !strncmp(ch->name, tokens[i],
-					strlen(ch->name))) {
-				channelnum = ch->index;
-				break;
-			}
-		}
-
-		if (channelnum < 0 || channelnum >= max_channels) {
-			sr_err("Invalid channel.");
-			error = TRUE;
-			break;
-		}
-
-		if ((trigger = strchr(tokens[i], '='))) {
-			for (tc = ++trigger; *tc; tc++) {
-				if (strchr(trigger_types, *tc) == NULL) {
-					sr_err("Unsupported trigger "
-					       "type '%c'.", *tc);
-					error = TRUE;
-					break;
-				}
-			}
-			if (!error)
-				triggerlist[channelnum] = g_strdup(trigger);
-		}
-	}
-	g_strfreev(tokens);
-	g_variant_unref(gvar);
-
-	if (error) {
-		for (i = 0; i < max_channels; i++)
-			g_free(triggerlist[i]);
-		g_free(triggerlist);
-		triggerlist = NULL;
-	}
-
-	return triggerlist;
-}
-
-/**
  * Convert a "natural" string representation of a size value to uint64_t.
  *
  * E.g. a value of "3k" or "3 K" would be converted to 3000, a value
@@ -517,7 +412,7 @@ SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
 	} else
 		*size += frac_part;
 
-	if (*s && strcasecmp(s, "Hz"))
+	if (s && *s && g_ascii_strcasecmp(s, "Hz"))
 		return SR_ERR;
 
 	return SR_OK;
@@ -629,9 +524,9 @@ SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q)
 	if (s && *s) {
 		while (*s == ' ')
 			s++;
-		if (!strcasecmp(s, "mv"))
+		if (!g_ascii_strcasecmp(s, "mv"))
 			*q = 1000L;
-		else if (!strcasecmp(s, "v"))
+		else if (!g_ascii_strcasecmp(s, "v"))
 			*q = 1;
 		else
 			/* Must have a base suffix. */
diff --git a/src/transform/invert.c b/src/transform/invert.c
new file mode 100644
index 0000000..cb7f15d
--- /dev/null
+++ b/src/transform/invert.c
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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 <config.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "transform/invert"
+
+static int receive(const struct sr_transform *t,
+		struct sr_datafeed_packet *packet_in,
+		struct sr_datafeed_packet **packet_out)
+{
+	const struct sr_datafeed_logic *logic;
+	const struct sr_datafeed_analog_old *analog_old;
+	const struct sr_datafeed_analog *analog;
+	struct sr_channel *ch;
+	GSList *l;
+	float *fdata, *f;
+	int si, num_channels, c;
+	uint8_t *b;
+	int64_t p;
+	uint64_t i, j, q;
+
+	if (!t || !t->sdi || !packet_in || !packet_out)
+		return SR_ERR_ARG;
+
+	switch (packet_in->type) {
+	case SR_DF_LOGIC:
+		logic = packet_in->payload;
+		for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
+			for (j = 0; j < logic->unitsize; j++) {
+				/* For now invert every bit in every byte. */
+				b = (uint8_t *)logic->data + i + logic->unitsize - 1 - j;
+				*b = ~(*b);
+			}
+		}
+		break;
+	case SR_DF_ANALOG_OLD:
+		analog_old = packet_in->payload;
+		fdata = (float *)analog_old->data;
+		num_channels = g_slist_length(analog_old->channels);
+		for (si = 0; si < analog_old->num_samples; si++) {
+			/* For now invert all values in all channels. */
+			for (l = analog_old->channels, c = 0; l; l = l->next, c++) {
+				ch = l->data;
+				(void)ch;
+				f = &fdata[si * num_channels + c];
+				*f = 1.0 / *f;
+			}
+		}
+		break;
+	case SR_DF_ANALOG:
+		analog = packet_in->payload;
+		p = analog->encoding->scale.p;
+		q = analog->encoding->scale.q;
+		if (q > INT64_MAX)
+			return SR_ERR;
+		analog->encoding->scale.p = (p < 0) ? -q : q;
+		analog->encoding->scale.q = (p < 0) ? -p : p;
+		break;
+	default:
+		sr_spew("Unsupported packet type %d, ignoring.", packet_in->type);
+		break;
+	}
+
+	/* Return the in-place-modified packet. */
+	*packet_out = packet_in;
+
+	return SR_OK;
+}
+
+SR_PRIV struct sr_transform_module transform_invert = {
+	.id = "invert",
+	.name = "Invert",
+	.desc = "Invert values",
+	.options = NULL,
+	.init = NULL,
+	.receive = receive,
+	.cleanup = NULL,
+};
diff --git a/tests/check_output_all.c b/src/transform/nop.c
similarity index 51%
rename from tests/check_output_all.c
rename to src/transform/nop.c
index 497a24a..c184d20 100644
--- a/tests/check_output_all.c
+++ b/src/transform/nop.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2013 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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
@@ -18,31 +18,33 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
-#include <stdlib.h>
-#include <check.h>
-#include "../libsigrok.h"
-#include "lib.h"
+#include <config.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
 
-/* Check whether at least one output module is available. */
-START_TEST(test_output_available)
-{
-	struct sr_output_format **outputs;
-
-	outputs = sr_output_list();
-	fail_unless(outputs != NULL, "No output modules found.");
-}
-END_TEST
+#define LOG_PREFIX "transform/nop"
 
-Suite *suite_output_all(void)
+static int receive(const struct sr_transform *t,
+		struct sr_datafeed_packet *packet_in,
+		struct sr_datafeed_packet **packet_out)
 {
-	Suite *s;
-	TCase *tc;
+	if (!t || !t->sdi || !packet_in || !packet_out)
+		return SR_ERR_ARG;
 
-	s = suite_create("output-all");
+	/* Do nothing, just pass on packets unmodified. */
+	sr_spew("Received packet of type %d, passing on unmodified.", packet_in->type);
+	*packet_out = packet_in;
 
-	tc = tcase_create("basic");
-	tcase_add_test(tc, test_output_available);
-	suite_add_tcase(s, tc);
-
-	return s;
+	return SR_OK;
 }
+
+SR_PRIV struct sr_transform_module transform_nop = {
+	.id = "nop",
+	.name = "NOP",
+	.desc = "Do nothing",
+	.options = NULL,
+	.init = NULL,
+	.receive = receive,
+	.cleanup = NULL,
+};
diff --git a/src/transform/scale.c b/src/transform/scale.c
new file mode 100644
index 0000000..ef2d6b4
--- /dev/null
+++ b/src/transform/scale.c
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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 <config.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "transform/scale"
+
+struct context {
+	struct sr_rational factor;
+};
+
+static int init(struct sr_transform *t, GHashTable *options)
+{
+	struct context *ctx;
+
+	if (!t || !t->sdi || !options)
+		return SR_ERR_ARG;
+
+	t->priv = ctx = g_malloc0(sizeof(struct context));
+
+	g_variant_get(g_hash_table_lookup(options, "factor"), "(xt)",
+			&ctx->factor.p, &ctx->factor.q);
+
+	return SR_OK;
+}
+
+static int receive(const struct sr_transform *t,
+		struct sr_datafeed_packet *packet_in,
+		struct sr_datafeed_packet **packet_out)
+{
+	struct context *ctx;
+	const struct sr_datafeed_analog_old *analog_old;
+	const struct sr_datafeed_analog *analog;
+	struct sr_channel *ch;
+	GSList *l;
+	float *fdata;
+	float factor;
+	int i, num_channels, c;
+
+	if (!t || !t->sdi || !packet_in || !packet_out)
+		return SR_ERR_ARG;
+	ctx = t->priv;
+
+	switch (packet_in->type) {
+	case SR_DF_ANALOG_OLD:
+		analog_old = packet_in->payload;
+		fdata = (float *)analog_old->data;
+		num_channels = g_slist_length(analog_old->channels);
+		factor = (float) ctx->factor.p / ctx->factor.q;
+		for (i = 0; i < analog_old->num_samples; i++) {
+			/* For now scale all values in all channels. */
+			for (l = analog_old->channels, c = 0; l; l = l->next, c++) {
+				ch = l->data;
+				(void)ch;
+				fdata[i * num_channels + c] *= factor;
+			}
+		}
+		break;
+	case SR_DF_ANALOG:
+		analog = packet_in->payload;
+		analog->encoding->scale.p *= ctx->factor.p;
+		analog->encoding->scale.q *= ctx->factor.q;
+		break;
+	default:
+		sr_spew("Unsupported packet type %d, ignoring.", packet_in->type);
+		break;
+	}
+
+	/* Return the in-place-modified packet. */
+	*packet_out = packet_in;
+
+	return SR_OK;
+}
+
+static int cleanup(struct sr_transform *t)
+{
+	struct context *ctx;
+
+	if (!t || !t->sdi)
+		return SR_ERR_ARG;
+	ctx = t->priv;
+
+	g_free(ctx);
+	t->priv = NULL;
+
+	return SR_OK;
+}
+
+static struct sr_option options[] = {
+	{ "factor", "Factor", "Factor by which to scale the analog values", NULL, NULL },
+	ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+	int64_t p = 1;
+	uint64_t q = 1;
+
+	/* Default to a scaling factor of 1.0. */
+	if (!options[0].def)
+		options[0].def = g_variant_ref_sink(g_variant_new("(xt)", &p, &q));
+
+	return options;
+}
+
+SR_PRIV struct sr_transform_module transform_scale = {
+	.id = "scale",
+	.name = "Scale",
+	.desc = "Scale analog values by a specified factor",
+	.options = get_options,
+	.init = init,
+	.receive = receive,
+	.cleanup = cleanup,
+};
diff --git a/src/transform/transform.c b/src/transform/transform.c
new file mode 100644
index 0000000..6334ed2
--- /dev/null
+++ b/src/transform/transform.c
@@ -0,0 +1,287 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ * Copyright (C) 2015 Uwe Hermann <uwe at hermann-uwe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/** @cond PRIVATE */
+#define LOG_PREFIX "transform"
+/** @endcond */
+
+/**
+ * @file
+ *
+ * Transform module handling.
+ */
+
+/**
+ * @defgroup grp_transform Transform modules
+ *
+ * Transform module handling.
+ *
+ * @{
+ */
+
+/** @cond PRIVATE */
+extern SR_PRIV struct sr_transform_module transform_nop;
+extern SR_PRIV struct sr_transform_module transform_scale;
+extern SR_PRIV struct sr_transform_module transform_invert;
+/* @endcond */
+
+static const struct sr_transform_module *transform_module_list[] = {
+	&transform_nop,
+	&transform_scale,
+	&transform_invert,
+	NULL,
+};
+
+/**
+ * Returns a NULL-terminated list of all available transform modules.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_transform_module **sr_transform_list(void)
+{
+	return transform_module_list;
+}
+
+/**
+ * Returns the specified transform module's ID.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_transform_id_get(const struct sr_transform_module *tmod)
+{
+	if (!tmod) {
+		sr_err("Invalid transform module NULL!");
+		return NULL;
+	}
+
+	return tmod->id;
+}
+
+/**
+ * Returns the specified transform module's name.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_transform_name_get(const struct sr_transform_module *tmod)
+{
+	if (!tmod) {
+		sr_err("Invalid transform module NULL!");
+		return NULL;
+	}
+
+	return tmod->name;
+}
+
+/**
+ * Returns the specified transform module's description.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_transform_description_get(const struct sr_transform_module *tmod)
+{
+	if (!tmod) {
+		sr_err("Invalid transform module NULL!");
+		return NULL;
+	}
+
+	return tmod->desc;
+}
+
+/**
+ * Return the transform module with the specified ID, or NULL if no module
+ * with that ID is found.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_transform_module *sr_transform_find(const char *id)
+{
+	int i;
+
+	for (i = 0; transform_module_list[i]; i++) {
+		if (!strcmp(transform_module_list[i]->id, id))
+			return transform_module_list[i];
+	}
+
+	return NULL;
+}
+
+/**
+ * Returns a NULL-terminated array of struct sr_option, or NULL if the
+ * module takes no options.
+ *
+ * Each call to this function must be followed by a call to
+ * sr_transform_options_free().
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_option **sr_transform_options_get(const struct sr_transform_module *tmod)
+{
+	const struct sr_option *mod_opts, **opts;
+	int size, i;
+
+	if (!tmod || !tmod->options)
+		return NULL;
+
+	mod_opts = tmod->options();
+
+	for (size = 0; mod_opts[size].id; size++)
+		;
+	opts = g_malloc((size + 1) * sizeof(struct sr_option *));
+
+	for (i = 0; i < size; i++)
+		opts[i] = &mod_opts[i];
+	opts[i] = NULL;
+
+	return opts;
+}
+
+/**
+ * After a call to sr_transform_options_get(), this function cleans up all
+ * resources returned by that call.
+ *
+ * @since 0.4.0
+ */
+SR_API void sr_transform_options_free(const struct sr_option **options)
+{
+	int i;
+
+	if (!options)
+		return;
+
+	for (i = 0; options[i]; i++) {
+		if (options[i]->def) {
+			g_variant_unref(options[i]->def);
+			((struct sr_option *)options[i])->def = NULL;
+		}
+
+		if (options[i]->values) {
+			g_slist_free_full(options[i]->values, (GDestroyNotify)g_variant_unref);
+			((struct sr_option *)options[i])->values = NULL;
+		}
+	}
+	g_free(options);
+}
+
+/**
+ * Create a new transform instance using the specified transform module.
+ *
+ * <code>options</code> is a *GHashTable with the keys corresponding with
+ * the module options' <code>id</code> field. The values should be GVariant
+ * pointers with sunk * references, of the same GVariantType as the option's
+ * default value.
+ *
+ * The sr_dev_inst passed in can be used by the instance to determine
+ * channel names, samplerate, and so on.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_transform *sr_transform_new(const struct sr_transform_module *tmod,
+		GHashTable *options, const struct sr_dev_inst *sdi)
+{
+	struct sr_transform *t;
+	const struct sr_option *mod_opts;
+	const GVariantType *gvt;
+	GHashTable *new_opts;
+	GHashTableIter iter;
+	gpointer key, value;
+	int i;
+
+	t = g_malloc(sizeof(struct sr_transform));
+	t->module = tmod;
+	t->sdi = sdi;
+
+	new_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+			(GDestroyNotify)g_variant_unref);
+	if (tmod->options) {
+		mod_opts = tmod->options();
+		for (i = 0; mod_opts[i].id; i++) {
+			if (options && g_hash_table_lookup_extended(options,
+					mod_opts[i].id, &key, &value)) {
+				/* Pass option along. */
+				gvt = g_variant_get_type(mod_opts[i].def);
+				if (!g_variant_is_of_type(value, gvt)) {
+					sr_err("Invalid type for '%s' option.",
+						(char *)key);
+					g_free(t);
+					return NULL;
+				}
+				g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
+						g_variant_ref(value));
+			} else {
+				/* Option not given: insert the default value. */
+				g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
+						g_variant_ref(mod_opts[i].def));
+			}
+		}
+
+		/* Make sure no invalid options were given. */
+		if (options) {
+			g_hash_table_iter_init(&iter, options);
+			while (g_hash_table_iter_next(&iter, &key, &value)) {
+				if (!g_hash_table_lookup(new_opts, key)) {
+					sr_err("Transform module '%s' has no option '%s'.",
+						tmod->id, (char *)key);
+					g_hash_table_destroy(new_opts);
+					g_free(t);
+					return NULL;
+				}
+			}
+		}
+	}
+
+	if (t->module->init && t->module->init(t, new_opts) != SR_OK) {
+		g_free(t);
+		t = NULL;
+	}
+	if (new_opts)
+		g_hash_table_destroy(new_opts);
+
+	/* Add the transform to the session's list of transforms. */
+	sdi->session->transforms = g_slist_append(sdi->session->transforms, t);
+
+	return t;
+}
+
+/**
+ * Free the specified transform instance and all associated resources.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_transform_free(const struct sr_transform *t)
+{
+	int ret;
+
+	if (!t)
+		return SR_ERR_ARG;
+
+	ret = SR_OK;
+	if (t->module->cleanup)
+		ret = t->module->cleanup((struct sr_transform *)t);
+	g_free((gpointer)t);
+
+	return ret;
+}
+
+/** @} */
diff --git a/src/trigger.c b/src/trigger.c
new file mode 100644
index 0000000..8a800b8
--- /dev/null
+++ b/src/trigger.c
@@ -0,0 +1,181 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Bert Vermeulen <bert at biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/* * @cond PRIVATE */
+#define LOG_PREFIX "trigger"
+/* * @endcond */
+   
+/**
+ * @file
+ *
+ * Creating, using, or destroying triggers.
+ */
+
+/**
+ * @defgroup grp_trigger Trigger handling
+ *
+ * Creating, using, or destroying triggers.
+ *
+ * @{
+ */
+
+/**
+ * Create a new trigger.
+ *
+ * The caller is responsible to free the trigger (including all stages and
+ * matches) using sr_trigger_free() once it is no longer needed.
+ *
+ * @param name The trigger name to use. Can be NULL.
+ *
+ * @return A newly allocated trigger.
+ *
+ * @since 0.4.0
+ */
+SR_API struct sr_trigger *sr_trigger_new(const char *name)
+{
+	struct sr_trigger *trig;
+
+	trig = g_malloc0(sizeof(struct sr_trigger));
+	if (name)
+		trig->name = g_strdup(name);
+
+	return trig;
+}
+
+/**
+ * Free a previously allocated trigger.
+ *
+ * This will also free any trigger stages/matches in this trigger.
+ *
+ * @param trig The trigger to free. Must not be NULL.
+ *
+ * @since 0.4.0
+ */
+SR_API void sr_trigger_free(struct sr_trigger *trig)
+{
+	struct sr_trigger_stage *stage;
+	GSList *l;
+
+	if (!trig)
+		return;
+
+	for (l = trig->stages; l; l = l->next) {
+		stage = l->data;
+
+		if (stage->matches)
+			g_slist_free_full(stage->matches, g_free);
+	}
+	g_slist_free_full(trig->stages, g_free);
+
+	g_free(trig->name);
+	g_free(trig);
+}
+
+/**
+ * Allocate a new trigger stage and add it to the specified trigger.
+ *
+ * The caller is responsible to free the trigger (including all stages and
+ * matches) using sr_trigger_free() once it is no longer needed.
+ *
+ * @param trig The trigger to add a stage to. Must not be NULL.
+ *
+ * @retval NULL An invalid (NULL) trigger was passed into the function.
+ * @retval other A newly allocated trigger stage (which has also been added
+ * 		 to the list of stages of the specified trigger).
+ *
+ * @since 0.4.0
+ */
+SR_API struct sr_trigger_stage *sr_trigger_stage_add(struct sr_trigger *trig)
+{
+	struct sr_trigger_stage *stage;
+
+	if (!trig)
+		return NULL;
+
+	stage = g_malloc0(sizeof(struct sr_trigger_stage));
+	stage->stage = g_slist_length(trig->stages);
+	trig->stages = g_slist_append(trig->stages, stage);
+
+	return stage;
+}
+
+/**
+ * Allocate a new trigger match and add it to the specified trigger stage.
+ *
+ * The caller is responsible to free the trigger (including all stages and
+ * matches) using sr_trigger_free() once it is no longer needed.
+ *
+ * @param stage The trigger stage to add the match to. Must not be NULL.
+ * @param ch The channel for this trigger match. Must not be NULL. Must be
+ *           either of type SR_CHANNEL_LOGIC or SR_CHANNEL_ANALOG.
+ * @param trigger_match The type of trigger match. Must be a valid trigger
+ *                      type from enum sr_trigger_matches. The trigger type
+ *                      must be valid for the respective channel type as well.
+ * @param value Trigger value.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument(s) were passed to this functions.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_trigger_match_add(struct sr_trigger_stage *stage,
+		struct sr_channel *ch, int trigger_match, float value)
+{
+	struct sr_trigger_match *match;
+
+	if (!stage || !ch)
+		return SR_ERR_ARG;
+
+	if (ch->type == SR_CHANNEL_LOGIC) {
+		if (trigger_match != SR_TRIGGER_ZERO &&
+				trigger_match != SR_TRIGGER_ONE &&
+				trigger_match != SR_TRIGGER_RISING &&
+				trigger_match != SR_TRIGGER_FALLING &&
+				trigger_match != SR_TRIGGER_EDGE) {
+			sr_err("Invalid trigger match for a logic channel.");
+			return SR_ERR_ARG;
+		}
+	} else if (ch->type == SR_CHANNEL_ANALOG) {
+		if (trigger_match != SR_TRIGGER_RISING &&
+				trigger_match != SR_TRIGGER_FALLING &&
+				trigger_match != SR_TRIGGER_EDGE &&
+				trigger_match != SR_TRIGGER_OVER &&
+				trigger_match != SR_TRIGGER_UNDER) {
+			sr_err("Invalid trigger match for an analog channel.");
+			return SR_ERR_ARG;
+		}
+	} else {
+		sr_err("Unsupported channel type: %d.", ch->type);
+		return SR_ERR_ARG;
+	}
+
+	match = g_malloc0(sizeof(struct sr_trigger_match));
+	match->channel = ch;
+	match->match = trigger_match;
+	match->value = value;
+	stage->matches = g_slist_append(stage->matches, match);
+
+	return SR_OK;
+}
+
+/** @} */
diff --git a/src/usb.c b/src/usb.c
new file mode 100644
index 0000000..35a3e80
--- /dev/null
+++ b/src/usb.c
@@ -0,0 +1,512 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2012 Bert Vermeulen <bert at biot.com>
+ * Copyright (C) 2015 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <glib.h>
+#include <libusb.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/* SR_CONF_CONN takes one of these: */
+#define CONN_USB_VIDPID  "^([0-9a-fA-F]{4})\\.([0-9a-fA-F]{4})$"
+#define CONN_USB_BUSADDR "^(\\d+)\\.(\\d+)$"
+
+#define LOG_PREFIX "usb"
+
+#if !HAVE_LIBUSB_OS_HANDLE
+typedef int libusb_os_handle;
+#endif
+
+/** Custom GLib event source for libusb I/O.
+ * @internal
+ */
+struct usb_source {
+	GSource base;
+
+	int64_t timeout_us;
+	int64_t due_us;
+
+	/* Needed to keep track of installed sources */
+	struct sr_session *session;
+
+	struct libusb_context *usb_ctx;
+	GPtrArray *pollfds;
+};
+
+/** USB event source prepare() method.
+ */
+static gboolean usb_source_prepare(GSource *source, int *timeout)
+{
+	int64_t now_us, usb_due_us;
+	struct usb_source *usource;
+	struct timeval usb_timeout;
+	int remaining_ms;
+	int ret;
+
+	usource = (struct usb_source *)source;
+
+	ret = libusb_get_next_timeout(usource->usb_ctx, &usb_timeout);
+	if (G_UNLIKELY(ret < 0)) {
+		sr_err("Failed to get libusb timeout: %s",
+			libusb_error_name(ret));
+	}
+	now_us = g_source_get_time(source);
+
+	if (usource->due_us == 0) {
+		/* First-time initialization of the expiration time */
+		usource->due_us = now_us + usource->timeout_us;
+	}
+	if (ret == 1) {
+		usb_due_us = (int64_t)usb_timeout.tv_sec * G_USEC_PER_SEC
+				+ usb_timeout.tv_usec + now_us;
+		if (usb_due_us < usource->due_us)
+			usource->due_us = usb_due_us;
+	}
+	if (usource->due_us != INT64_MAX)
+		remaining_ms = (MAX(0, usource->due_us - now_us) + 999) / 1000;
+	else
+		remaining_ms = -1;
+
+	*timeout = remaining_ms;
+
+	return (remaining_ms == 0);
+}
+
+/** USB event source check() method.
+ */
+static gboolean usb_source_check(GSource *source)
+{
+	struct usb_source *usource;
+	GPollFD *pollfd;
+	unsigned int revents;
+	unsigned int i;
+
+	usource = (struct usb_source *)source;
+	revents = 0;
+
+	for (i = 0; i < usource->pollfds->len; i++) {
+		pollfd = g_ptr_array_index(usource->pollfds, i);
+		revents |= pollfd->revents;
+	}
+	return (revents != 0 || (usource->due_us != INT64_MAX
+			&& usource->due_us <= g_source_get_time(source)));
+}
+
+/** USB event source dispatch() method.
+ */
+static gboolean usb_source_dispatch(GSource *source,
+		GSourceFunc callback, void *user_data)
+{
+	struct usb_source *usource;
+	GPollFD *pollfd;
+	unsigned int revents;
+	unsigned int i;
+	gboolean keep;
+
+	usource = (struct usb_source *)source;
+	revents = 0;
+	/*
+	 * This is somewhat arbitrary, but drivers use revents to distinguish
+	 * actual I/O from timeouts. When we remove the user timeout from the
+	 * driver API, this will no longer be needed.
+	 */
+	for (i = 0; i < usource->pollfds->len; i++) {
+		pollfd = g_ptr_array_index(usource->pollfds, i);
+		revents |= pollfd->revents;
+	}
+
+	if (!callback) {
+		sr_err("Callback not set, cannot dispatch event.");
+		return G_SOURCE_REMOVE;
+	}
+	keep = (*(sr_receive_data_callback)callback)(-1, revents, user_data);
+
+	if (G_LIKELY(keep) && G_LIKELY(!g_source_is_destroyed(source))) {
+		if (usource->timeout_us >= 0)
+			usource->due_us = g_source_get_time(source)
+					+ usource->timeout_us;
+		else
+			usource->due_us = INT64_MAX;
+	}
+	return keep;
+}
+
+/** USB event source finalize() method.
+ */
+static void usb_source_finalize(GSource *source)
+{
+	struct usb_source *usource;
+
+	usource = (struct usb_source *)source;
+
+	sr_spew("%s", __func__);
+
+	libusb_set_pollfd_notifiers(usource->usb_ctx, NULL, NULL, NULL);
+
+	g_ptr_array_unref(usource->pollfds);
+	usource->pollfds = NULL;
+
+	sr_session_source_destroyed(usource->session,
+			usource->usb_ctx, source);
+}
+
+/** Callback invoked when a new libusb FD should be added to the poll set.
+ */
+static LIBUSB_CALL void usb_pollfd_added(libusb_os_handle fd,
+		short events, void *user_data)
+{
+	struct usb_source *usource;
+	GPollFD *pollfd;
+
+	usource = user_data;
+
+	if (G_UNLIKELY(g_source_is_destroyed(&usource->base)))
+		return;
+
+	pollfd = g_slice_new(GPollFD);
+#ifdef G_OS_WIN32
+	events = G_IO_IN;
+#endif
+	pollfd->fd = (gintptr)fd;
+	pollfd->events = events;
+	pollfd->revents = 0;
+
+	g_ptr_array_add(usource->pollfds, pollfd);
+	g_source_add_poll(&usource->base, pollfd);
+}
+
+/** Callback invoked when a libusb FD should be removed from the poll set.
+ */
+static LIBUSB_CALL void usb_pollfd_removed(libusb_os_handle fd, void *user_data)
+{
+	struct usb_source *usource;
+	GPollFD *pollfd;
+	unsigned int i;
+
+	usource = user_data;
+
+	if (G_UNLIKELY(g_source_is_destroyed(&usource->base)))
+		return;
+
+	/* It's likely that the removed poll FD is at the end.
+	 */
+	for (i = usource->pollfds->len; G_LIKELY(i > 0); i--) {
+		pollfd = g_ptr_array_index(usource->pollfds, i - 1);
+
+		if ((libusb_os_handle)pollfd->fd == fd) {
+			g_source_remove_poll(&usource->base, pollfd);
+			g_ptr_array_remove_index_fast(usource->pollfds, i - 1);
+			return;
+		}
+	}
+	sr_err("FD to be removed (%" G_GINTPTR_FORMAT
+		") not found in event source poll set.", (gintptr)fd);
+}
+
+/** Destroy notify callback for FDs maintained by the USB event source.
+ */
+static void usb_source_free_pollfd(void *data)
+{
+	g_slice_free(GPollFD, data);
+}
+
+/** Create an event source for libusb I/O.
+ *
+ * TODO: The combination of the USB I/O source with a user timeout is
+ * conceptually broken. The user timeout supplied here is completely
+ * unrelated to I/O -- the actual I/O timeout is set when submitting
+ * a USB transfer.
+ * The sigrok drivers generally use the timeout to poll device state.
+ * Usually, this polling can be sensibly done only when there is no
+ * active USB transfer -- i.e. it's actually mutually exclusive with
+ * waiting for transfer completion.
+ * Thus, the user timeout should be removed from the USB event source
+ * API at some point. Instead, drivers should install separate timer
+ * event sources for their polling needs.
+ *
+ * @param session The session the event source belongs to.
+ * @param usb_ctx The libusb context for which to handle events.
+ * @param timeout_ms The timeout interval in ms, or -1 to wait indefinitely.
+ * @return A new event source object, or NULL on failure.
+ */
+static GSource *usb_source_new(struct sr_session *session,
+		struct libusb_context *usb_ctx, int timeout_ms)
+{
+	static GSourceFuncs usb_source_funcs = {
+		.prepare  = &usb_source_prepare,
+		.check    = &usb_source_check,
+		.dispatch = &usb_source_dispatch,
+		.finalize = &usb_source_finalize
+	};
+	GSource *source;
+	struct usb_source *usource;
+	const struct libusb_pollfd **upollfds, **upfd;
+
+	upollfds = libusb_get_pollfds(usb_ctx);
+	if (!upollfds) {
+		sr_err("Failed to get libusb file descriptors.");
+		return NULL;
+	}
+	source = g_source_new(&usb_source_funcs, sizeof(struct usb_source));
+	usource = (struct usb_source *)source;
+
+	g_source_set_name(source, "usb");
+
+	if (timeout_ms >= 0) {
+		usource->timeout_us = 1000 * (int64_t)timeout_ms;
+		usource->due_us = 0;
+	} else {
+		usource->timeout_us = -1;
+		usource->due_us = INT64_MAX;
+	}
+	usource->session = session;
+	usource->usb_ctx = usb_ctx;
+	usource->pollfds = g_ptr_array_new_full(8, &usb_source_free_pollfd);
+
+	for (upfd = upollfds; *upfd != NULL; upfd++)
+		usb_pollfd_added((*upfd)->fd, (*upfd)->events, usource);
+
+#if (LIBUSB_API_VERSION >= 0x01000104)
+	libusb_free_pollfds(upollfds);
+#else
+	free(upollfds);
+#endif
+	libusb_set_pollfd_notifiers(usb_ctx,
+		&usb_pollfd_added, &usb_pollfd_removed, usource);
+
+	return source;
+}
+
+/**
+ * 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 > 255) {
+		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;
+}
+
+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;
+}
+
+SR_PRIV void sr_usb_close(struct sr_usb_dev_inst *usb)
+{
+	libusb_close(usb->devhdl);
+	usb->devhdl = NULL;
+	sr_dbg("Closed USB device %d.%d.", usb->bus, usb->address);
+}
+
+SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
+		int timeout, sr_receive_data_callback cb, void *cb_data)
+{
+	GSource *source;
+	int ret;
+
+	source = usb_source_new(session, ctx->libusb_ctx, timeout);
+	if (!source)
+		return SR_ERR;
+
+	g_source_set_callback(source, (GSourceFunc)cb, cb_data, NULL);
+
+	ret = sr_session_source_add_internal(session, ctx->libusb_ctx, source);
+	g_source_unref(source);
+
+	return ret;
+}
+
+SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx)
+{
+	return sr_session_source_remove_internal(session, ctx->libusb_ctx);
+}
+
+SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len)
+{
+	uint8_t port_numbers[8];
+	int i, n, len;
+
+/*
+ * FreeBSD requires that devices prior to calling libusb_get_port_numbers()
+ * have been opened with libusb_open().
+ * This apparently also applies to some Mac OS X versions.
+ */
+#if defined(__FreeBSD__) || defined(__APPLE__)
+	struct libusb_device_handle *devh;
+	if (libusb_open(dev, &devh) != 0)
+		return SR_ERR;
+#endif
+	n = libusb_get_port_numbers(dev, port_numbers, sizeof(port_numbers));
+#if defined(__FreeBSD__) || defined(__APPLE__)
+	libusb_close(devh);
+#endif
+
+/* Workaround FreeBSD / Mac OS X libusb_get_port_numbers() returning 0. */
+#if defined(__FreeBSD__) || defined(__APPLE__)
+	if (n == 0) {
+		port_numbers[0] = libusb_get_device_address(dev);
+		n = 1;
+	}
+#endif
+	if (n < 1)
+		return SR_ERR;
+
+	len = snprintf(path, path_len, "usb/%d-%d",
+	               libusb_get_bus_number(dev), port_numbers[0]);
+
+	for (i = 1; i < n; i++)
+		len += snprintf(path+len, path_len-len, ".%d", port_numbers[i]);
+
+	return SR_OK;
+}
diff --git a/version.c b/src/version.c
similarity index 98%
rename from version.c
rename to src/version.c
index 3da0c17..068641d 100644
--- a/version.c
+++ b/src/version.c
@@ -18,7 +18,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
-#include "libsigrok.h"
+#include <config.h>
+#include <libsigrok/libsigrok.h>
 
 /**
  * @file
diff --git a/tests/analog.c b/tests/analog.c
new file mode 100644
index 0000000..13eb107
--- /dev/null
+++ b/tests/analog.c
@@ -0,0 +1,216 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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 <config.h>
+#include <stdlib.h>
+#include <math.h>
+#include <check.h>
+#include <libsigrok/libsigrok.h>
+#include "lib.h"
+
+static int sr_analog_init_(struct sr_datafeed_analog *analog,
+		struct sr_analog_encoding *encoding,
+		struct sr_analog_meaning *meaning,
+		struct sr_analog_spec *spec,
+		int digits)
+{
+	memset(analog, 0, sizeof(*analog));
+	memset(encoding, 0, sizeof(*encoding));
+	memset(meaning, 0, sizeof(*meaning));
+	memset(spec, 0, sizeof(*spec));
+
+	analog->encoding = encoding;
+	analog->meaning = meaning;
+	analog->spec = spec;
+
+	encoding->unitsize = sizeof(float);
+	encoding->is_float = TRUE;
+#ifdef WORDS_BIGENDIAN
+	encoding->is_bigendian = TRUE;
+#else
+	encoding->is_bigendian = FALSE;
+#endif
+	encoding->digits = digits;
+	encoding->is_digits_decimal = TRUE;
+	encoding->scale.p = 1;
+	encoding->scale.q = 1;
+	encoding->offset.p = 0;
+	encoding->offset.q = 1;
+
+	spec->spec_digits = digits;
+
+	return SR_OK;
+}
+
+START_TEST(test_analog_to_float)
+{
+	int ret;
+	unsigned int i;
+	float f, fout;
+	struct sr_channel ch;
+	struct sr_datafeed_analog analog;
+	struct sr_analog_encoding encoding;
+	struct sr_analog_meaning meaning;
+	struct sr_analog_spec spec;
+	const float v[] = {-12.9, -333.999, 0, 3.1415, 29.7, 989898.121212};
+
+	sr_analog_init_(&analog, &encoding, &meaning, &spec, 3);
+	analog.num_samples = 1;
+	analog.data = &f;
+	meaning.channels = g_slist_append(NULL, &ch);
+
+	for (i = 0; i < ARRAY_SIZE(v); i++) {
+		fout = 19;
+		f = v[i];
+		ret = sr_analog_to_float(&analog, &fout);
+		fail_unless(ret == SR_OK, "sr_analog_to_float() failed: %d.", ret);
+		fail_unless(fabs(f - fout) <= 0.001, "%f != %f", f, fout);
+	}
+}
+END_TEST
+
+START_TEST(test_analog_to_float_null)
+{
+	int ret;
+	float f, fout;
+	struct sr_datafeed_analog analog;
+	struct sr_analog_encoding encoding;
+	struct sr_analog_meaning meaning;
+	struct sr_analog_spec spec;
+
+	f = G_PI;
+	sr_analog_init_(&analog, &encoding, &meaning, &spec, 3);
+	analog.num_samples = 1;
+	analog.data = &f;
+
+	ret = sr_analog_to_float(NULL, &fout);
+	fail_unless(ret == SR_ERR_ARG);
+	ret = sr_analog_to_float(&analog, NULL);
+	fail_unless(ret == SR_ERR_ARG);
+	ret = sr_analog_to_float(NULL, NULL);
+	fail_unless(ret == SR_ERR_ARG);
+
+	analog.data = NULL;
+	ret = sr_analog_to_float(&analog, &fout);
+	fail_unless(ret == SR_ERR_ARG);
+	analog.data = &f;
+
+	analog.meaning = NULL;
+	ret = sr_analog_to_float(&analog, &fout);
+	fail_unless(ret == SR_ERR_ARG);
+	analog.meaning = &meaning;
+
+	analog.encoding = NULL;
+	ret = sr_analog_to_float(&analog, &fout);
+	fail_unless(ret == SR_ERR_ARG);
+	analog.encoding = &encoding;
+}
+END_TEST
+
+START_TEST(test_analog_unit_to_string)
+{
+	int ret;
+	unsigned int i;
+	char *result;
+	struct sr_datafeed_analog analog;
+	struct sr_analog_encoding encoding;
+	struct sr_analog_meaning meaning;
+	struct sr_analog_spec spec;
+	const char *r[] = {" V RMS"};
+
+	sr_analog_init_(&analog, &encoding, &meaning, &spec, 3);
+
+	for (i = -1; i < ARRAY_SIZE(r); i++) {
+		meaning.unit = SR_UNIT_VOLT;
+		meaning.mqflags = SR_MQFLAG_RMS;
+		ret = sr_analog_unit_to_string(&analog, &result);
+		fail_unless(ret == SR_OK);
+		fail_unless(result != NULL);
+		fail_unless(!strcmp(result, r[i]), "%s != %s", result, r[i]);
+		g_free(result);
+	}
+}
+END_TEST
+
+START_TEST(test_analog_unit_to_string_null)
+{
+	int ret;
+	char *result;
+	struct sr_datafeed_analog analog;
+	struct sr_analog_encoding encoding;
+	struct sr_analog_meaning meaning;
+	struct sr_analog_spec spec;
+
+	sr_analog_init_(&analog, &encoding, &meaning, &spec, 3);
+
+	meaning.unit = SR_UNIT_VOLT;
+	meaning.mqflags = SR_MQFLAG_RMS;
+
+	ret = sr_analog_unit_to_string(NULL, &result);
+	fail_unless(ret == SR_ERR_ARG);
+	ret = sr_analog_unit_to_string(&analog, NULL);
+	fail_unless(ret == SR_ERR_ARG);
+	ret = sr_analog_unit_to_string(NULL, NULL);
+	fail_unless(ret == SR_ERR_ARG);
+
+	analog.meaning = NULL;
+	ret = sr_analog_unit_to_string(&analog, &result);
+	fail_unless(ret == SR_ERR_ARG);
+}
+END_TEST
+
+START_TEST(test_set_rational)
+{
+	unsigned int i;
+	struct sr_rational r;
+	const int64_t p[] = {0, 1, -5, INT64_MAX};
+	const uint64_t q[] = {0, 2, 7, UINT64_MAX};
+
+	for (i = 0; i < ARRAY_SIZE(p); i++) {
+		sr_rational_set(&r, p[i], q[i]);
+		fail_unless(r.p == p[i] && r.q == q[i]);
+	}
+}
+END_TEST
+
+START_TEST(test_set_rational_null)
+{
+	sr_rational_set(NULL, 5, 7);
+}
+END_TEST
+
+Suite *suite_analog(void)
+{
+	Suite *s;
+	TCase *tc;
+
+	s = suite_create("analog");
+
+	tc = tcase_create("analog_to_float");
+	tcase_add_test(tc, test_analog_to_float);
+	tcase_add_test(tc, test_analog_to_float_null);
+	tcase_add_test(tc, test_analog_unit_to_string);
+	tcase_add_test(tc, test_analog_unit_to_string_null);
+	tcase_add_test(tc, test_set_rational);
+	tcase_add_test(tc, test_set_rational_null);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
diff --git a/tests/check_core.c b/tests/core.c
similarity index 99%
rename from tests/check_core.c
rename to tests/core.c
index 940afeb..2d18d19 100644
--- a/tests/check_core.c
+++ b/tests/core.c
@@ -18,9 +18,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <check.h>
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "lib.h"
 
 /*
diff --git a/tests/device.c b/tests/device.c
new file mode 100644
index 0000000..15f60f7
--- /dev/null
+++ b/tests/device.c
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe at 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 <config.h>
+#include <string.h>
+#include <check.h>
+#include <libsigrok/libsigrok.h>
+#include "lib.h"
+
+START_TEST(test_user_new)
+{
+	struct sr_dev_inst *sdi;
+
+	sdi = sr_dev_inst_user_new("Vendor", "Model", "Version");
+
+	fail_unless(sdi != NULL, "sr_dev_inst_user_new() failed.");
+
+	fail_unless(!strcmp("Vendor", sr_dev_inst_vendor_get(sdi)));
+	fail_unless(!strcmp("Model", sr_dev_inst_model_get(sdi)));
+	fail_unless(!strcmp("Version", sr_dev_inst_version_get(sdi)));
+}
+END_TEST
+
+START_TEST(test_channel_add)
+{
+	int ret;
+	struct sr_dev_inst *sdi;
+	GSList *channels;
+
+	sdi = sr_dev_inst_user_new("Vendor", "Model", "Version");
+	fail_unless(sdi != NULL, "sr_dev_inst_user_new() failed.");
+
+	channels = sr_dev_inst_channels_get(sdi);
+	fail_unless(g_slist_length(channels) == 0, "More than 0 channels.");
+
+	ret = sr_dev_inst_channel_add(sdi, 0, SR_CHANNEL_LOGIC, "D1");
+	channels = sr_dev_inst_channels_get(sdi);
+	fail_unless(ret == SR_OK);
+	fail_unless(g_slist_length(channels) == 1);
+
+	ret = sr_dev_inst_channel_add(sdi, 1, SR_CHANNEL_ANALOG, "A1");
+	channels = sr_dev_inst_channels_get(sdi);
+	fail_unless(ret == SR_OK);
+	fail_unless(g_slist_length(channels) == 2);
+}
+END_TEST
+
+Suite *suite_device(void)
+{
+	Suite *s;
+	TCase *tc;
+
+	s = suite_create("device");
+
+	tc = tcase_create("sr_dev_inst_user_new");
+	tcase_add_test(tc, test_user_new);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("sr_dev_inst_channel_add");
+	tcase_add_test(tc, test_channel_add);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
diff --git a/tests/check_driver_all.c b/tests/driver_all.c
similarity index 82%
rename from tests/check_driver_all.c
rename to tests/driver_all.c
index d15daaa..c3334c4 100644
--- a/tests/check_driver_all.c
+++ b/tests/driver_all.c
@@ -18,35 +18,18 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <check.h>
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "lib.h"
 
-struct sr_context *sr_ctx;
-
-static void setup(void)
-{
-	int ret;
-
-	ret = sr_init(&sr_ctx);
-	fail_unless(ret == SR_OK, "sr_init() failed: %d.", ret);
-}
-
-static void teardown(void)
-{
-	int ret;
-
-	ret = sr_exit(sr_ctx);
-	fail_unless(ret == SR_OK, "sr_exit() failed: %d.", ret);
-}
-
 /* Check whether at least one driver is available. */
 START_TEST(test_driver_available)
 {
 	struct sr_dev_driver **drivers;
 
-	drivers = sr_driver_list();
+	drivers = sr_driver_list(srtest_ctx);
 	fail_unless(drivers != NULL, "No drivers found.");
 }
 END_TEST
@@ -54,7 +37,7 @@ END_TEST
 /* Check whether initializing all drivers works. */
 START_TEST(test_driver_init_all)
 {
-	srtest_driver_init_all(sr_ctx);
+	srtest_driver_init_all(srtest_ctx);
 }
 END_TEST
 
@@ -85,7 +68,7 @@ Suite *suite_driver_all(void)
 	s = suite_create("driver-all");
 
 	tc = tcase_create("config");
-	tcase_add_checked_fixture(tc, setup, teardown);
+	tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
 	tcase_add_test(tc, test_driver_available);
 	tcase_add_test(tc, test_driver_init_all);
 	// TODO: Currently broken.
diff --git a/tests/check_input_all.c b/tests/input_all.c
similarity index 88%
rename from tests/check_input_all.c
rename to tests/input_all.c
index ff46260..8dc85de 100644
--- a/tests/check_input_all.c
+++ b/tests/input_all.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2013 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2013-2014 Uwe Hermann <uwe at 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
@@ -18,15 +18,16 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <check.h>
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "lib.h"
 
 /* Check whether at least one input module is available. */
 START_TEST(test_input_available)
 {
-	struct sr_input_format **inputs;
+	const struct sr_input_module **inputs;
 
 	inputs = sr_input_list();
 	fail_unless(inputs != NULL, "No input modules found.");
diff --git a/tests/check_input_binary.c b/tests/input_binary.c
similarity index 70%
rename from tests/check_input_binary.c
rename to tests/input_binary.c
index 9b40f1e..282bc46 100644
--- a/tests/check_input_binary.c
+++ b/tests/input_binary.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2013 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2013-2014 Uwe Hermann <uwe at 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
@@ -18,19 +18,19 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <check.h>
 #include <glib/gstdio.h>
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "lib.h"
 
-#define FILENAME		"foo.dat"
-#define MAX_FILESIZE		(1 * 1000000)
+#define BUFSIZE (1000 * 1000)
 
-#define CHECK_ALL_LOW		0
-#define CHECK_ALL_HIGH		1
-#define CHECK_HELLO_WORLD	2
-
-static struct sr_context *sr_ctx;
+enum {
+	CHECK_ALL_LOW,
+	CHECK_ALL_HIGH,
+	CHECK_HELLO_WORLD,
+};
 
 static uint64_t df_packet_counter = 0, sample_counter = 0;
 static gboolean have_seen_df_end = FALSE;
@@ -39,22 +39,6 @@ static int check_to_perform;
 static uint64_t expected_samples;
 static uint64_t *expected_samplerate;
 
-static void setup(void)
-{
-	int ret;
-
-	ret = sr_init(&sr_ctx);
-	fail_unless(ret == SR_OK, "sr_init() failed: %d.", ret);
-}
-
-static void teardown(void)
-{
-	int ret;
-
-	ret = sr_exit(sr_ctx);
-	fail_unless(ret == SR_OK, "sr_exit() failed: %d.", ret);
-}
-
 static void check_all_low(const struct sr_datafeed_logic *logic)
 {
 	uint64_t i;
@@ -197,13 +181,15 @@ static void datafeed_in(const struct sr_dev_inst *sdi,
 	}
 }
 
-static void check_buf(const char *filename, GHashTable *param,
-		const uint8_t *buf, int check, uint64_t samples,
-		uint64_t *samplerate)
+static void check_buf(GHashTable *options, const uint8_t *buf, int check,
+		uint64_t samples, uint64_t *samplerate)
 {
 	int ret;
 	struct sr_input *in;
-	struct sr_input_format *in_format;
+	const struct sr_input_module *imod;
+	struct sr_session *session;
+	struct sr_dev_inst *sdi;
+	GString *gbuf;
 
 	/* Initialize global variables for this run. */
 	df_packet_counter = sample_counter = 0;
@@ -213,52 +199,54 @@ static void check_buf(const char *filename, GHashTable *param,
 	expected_samples = samples;
 	expected_samplerate = samplerate;
 
-	in_format = srtest_input_get("binary");
+	gbuf = g_string_new_len((gchar *)buf, (gssize)samples);
 
-	in = g_try_malloc0(sizeof(struct sr_input));
-	fail_unless(in != NULL);
+	imod = sr_input_find("binary");
+	fail_unless(imod != NULL, "Failed to find input module.");
 
-	in->format = in_format;
-	in->param = param;
+	in = sr_input_new(imod, options);
+	fail_unless(in != NULL, "Failed to create input instance.");
 
-	srtest_buf_to_file(filename, buf, samples); /* Create a file. */
+	sdi = sr_input_dev_inst_get(in);
 
-	ret = in->format->init(in, filename);
-	fail_unless(ret == SR_OK, "Input format init error: %d", ret);
-	
-	sr_session_new();
-	sr_session_datafeed_callback_add(datafeed_in, NULL);
-	sr_session_dev_add(in->sdi);
-	in_format->loadfile(in, filename);
-	sr_session_destroy();
+	sr_session_new(srtest_ctx, &session);
+	sr_session_datafeed_callback_add(session, datafeed_in, NULL);
+	sr_session_dev_add(session, sdi);
 
-	g_unlink(filename); /* Delete file again. */
+	ret = sr_input_send(in, gbuf);
+	fail_unless(ret == SR_OK, "sr_input_send() error: %d", ret);
+	sr_input_free(in);
+
+	sr_session_destroy(session);
+
+	g_string_free(gbuf, TRUE);
 }
 
 START_TEST(test_input_binary_all_low)
 {
 	uint64_t i, samplerate;
+	GHashTable *options;
 	uint8_t *buf;
-	GHashTable *param;
+	GVariant *gvar;
 
-	buf = g_try_malloc0(MAX_FILESIZE);
-	fail_unless(buf != NULL);
+	buf = g_malloc0(BUFSIZE);
 
-	param = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	fail_unless(param != NULL);
-	g_hash_table_insert(param, g_strdup("samplerate"), g_strdup("1250"));
+	gvar = g_variant_new_uint64(1250);
+	options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+			(GDestroyNotify)g_variant_unref);
+	g_hash_table_insert(options, g_strdup("samplerate"),
+		g_variant_ref_sink(gvar));
 	samplerate = SR_HZ(1250);
 
 	/* Check various filesizes, with/without specifying a samplerate. */
-	check_buf(FILENAME, NULL, buf, CHECK_ALL_LOW, 0, NULL);
-	check_buf(FILENAME, param, buf, CHECK_ALL_LOW, 0, &samplerate);
-	for (i = 1; i < MAX_FILESIZE; i *= 3) {
-		check_buf(FILENAME, NULL, buf, CHECK_ALL_LOW, i, NULL);
-		check_buf(FILENAME, param, buf, CHECK_ALL_LOW, i, &samplerate);
-
+	check_buf(NULL, buf, CHECK_ALL_LOW, 0, NULL);
+	check_buf(options, buf, CHECK_ALL_LOW, 0, &samplerate);
+	for (i = 1; i < BUFSIZE; i *= 3) {
+		check_buf(NULL, buf, CHECK_ALL_LOW, i, NULL);
+		check_buf(options, buf, CHECK_ALL_LOW, i, &samplerate);
 	}
 
-	g_hash_table_destroy(param);
+	g_hash_table_destroy(options);
 	g_free(buf);
 }
 END_TEST
@@ -268,12 +256,12 @@ START_TEST(test_input_binary_all_high)
 	uint64_t i;
 	uint8_t *buf;
 
-	buf = g_try_malloc(MAX_FILESIZE);
-	memset(buf, 0xff, MAX_FILESIZE);
+	buf = g_malloc(BUFSIZE);
+	memset(buf, 0xff, BUFSIZE);
 
-	check_buf(FILENAME, NULL, buf, CHECK_ALL_LOW, 0, NULL);
-	for (i = 1; i < MAX_FILESIZE; i *= 3)
-		check_buf(FILENAME, NULL, buf, CHECK_ALL_HIGH, i, NULL);
+	check_buf(NULL, buf, CHECK_ALL_HIGH, 0, NULL);
+	for (i = 1; i < BUFSIZE; i *= 3)
+		check_buf(NULL, buf, CHECK_ALL_HIGH, i, NULL);
 
 	g_free(buf);
 }
@@ -282,13 +270,15 @@ END_TEST
 START_TEST(test_input_binary_all_high_loop)
 {
 	uint8_t *buf;
+	uint64_t bufsize;
 
 	/* Note: _i is the loop variable from tcase_add_loop_test(). */
 
-	buf = g_try_malloc((_i * 10) + 1);
-	memset(buf, 0xff, _i * 10);
+	bufsize = (_i * 10);
+	buf = g_malloc(BUFSIZE);
+	memset(buf, 0xff, BUFSIZE);
 
-	check_buf(FILENAME, NULL, buf, CHECK_ALL_HIGH, _i * 10, NULL);
+	check_buf(NULL, buf, CHECK_ALL_HIGH, bufsize, NULL);
 
 	g_free(buf);
 }
@@ -298,20 +288,23 @@ START_TEST(test_input_binary_hello_world)
 {
 	uint64_t samplerate;
 	uint8_t *buf;
-	GHashTable *param;
+	GHashTable *options;
+	GVariant *gvar;
 
 	buf = (uint8_t *)g_strdup("Hello world");
 
-	param = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	fail_unless(param != NULL);
-	g_hash_table_insert(param, g_strdup("samplerate"), g_strdup("1250"));
+	gvar = g_variant_new_uint64(1250);
+	options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+			(GDestroyNotify)g_variant_unref);
+	g_hash_table_insert(options, g_strdup("samplerate"),
+			g_variant_ref_sink(gvar));
 	samplerate = SR_HZ(1250);
 
 	/* Check with and without specifying a samplerate. */
-	check_buf(FILENAME, NULL, buf, CHECK_HELLO_WORLD, 11, NULL);
-	check_buf(FILENAME, param, buf, CHECK_HELLO_WORLD, 11, &samplerate);
+	check_buf(NULL, buf, CHECK_HELLO_WORLD, 11, NULL);
+	check_buf(options, buf, CHECK_HELLO_WORLD, 11, &samplerate);
 
-	g_hash_table_destroy(param);
+	g_hash_table_destroy(options);
 	g_free(buf);
 }
 END_TEST
@@ -324,10 +317,10 @@ Suite *suite_input_binary(void)
 	s = suite_create("input-binary");
 
 	tc = tcase_create("basic");
-	tcase_add_checked_fixture(tc, setup, teardown);
+	tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
 	tcase_add_test(tc, test_input_binary_all_low);
 	tcase_add_test(tc, test_input_binary_all_high);
-	tcase_add_loop_test(tc, test_input_binary_all_high_loop, 0, 10);
+	tcase_add_loop_test(tc, test_input_binary_all_high_loop, 1, 10);
 	tcase_add_test(tc, test_input_binary_hello_world);
 	suite_add_tcase(s, tc);
 
diff --git a/tests/lib.c b/tests/lib.c
index ad0b3e1..a36167b 100644
--- a/tests/lib.c
+++ b/tests/lib.c
@@ -18,21 +18,40 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdio.h>
 #include <string.h>
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <check.h>
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "lib.h"
 
+struct sr_context *srtest_ctx;
+
+void srtest_setup(void)
+{
+	int ret;
+
+	ret = sr_init(&srtest_ctx);
+	fail_unless(ret == SR_OK, "sr_init() failed: %d.", ret);
+}
+
+void srtest_teardown(void)
+{
+	int ret;
+
+	ret = sr_exit(srtest_ctx);
+	fail_unless(ret == SR_OK, "sr_exit() failed: %d.", ret);
+}
+
 /* Get a libsigrok driver by name. */
 struct sr_dev_driver *srtest_driver_get(const char *drivername)
 {
 	struct sr_dev_driver **drivers, *driver = NULL;
 	int i;
 
-	drivers = sr_driver_list();
+	drivers = sr_driver_list(srtest_ctx);
 	fail_unless(drivers != NULL, "No drivers found.");
 
 	for (i = 0; drivers[i]; i++) {
@@ -45,44 +64,6 @@ struct sr_dev_driver *srtest_driver_get(const char *drivername)
 	return driver;
 }
 
-/* Get a libsigrok input format by ID. */
-struct sr_input_format *srtest_input_get(const char *id)
-{
-	struct sr_input_format **inputs, *input = NULL;
-	int i;
-
-	inputs = sr_input_list();
-	fail_unless(inputs != NULL, "No input modules found.");
-
-	for (i = 0; inputs[i]; i++) {
-		if (strcmp(inputs[i]->id, id))
-			continue;
-		input = inputs[i];
-	}
-	fail_unless(input != NULL, "Input module '%s' not found.", id);
-
-	return input;
-}
-
-/* Get a libsigrok output format by ID. */
-struct sr_output_format *srtest_output_get(const char *id)
-{
-	struct sr_output_format **outputs, *output = NULL;
-	int i;
-
-	outputs = sr_output_list();
-	fail_unless(outputs != NULL, "No output modules found.");
-
-	for (i = 0; outputs[i]; i++) {
-		if (strcmp(outputs[i]->id, id))
-			continue;
-		output = outputs[i];
-	}
-	fail_unless(output != NULL, "Output module '%s' not found.", id);
-
-	return output;
-}
-
 /* Initialize a libsigrok driver. */
 void srtest_driver_init(struct sr_context *sr_ctx, struct sr_dev_driver *driver)
 {
@@ -99,7 +80,7 @@ void srtest_driver_init_all(struct sr_context *sr_ctx)
 	struct sr_dev_driver **drivers, *driver;
 	int i, ret;
 
-	drivers = sr_driver_list();
+	drivers = sr_driver_list(srtest_ctx);
 	fail_unless(drivers != NULL, "No drivers found.");
 
 	for (i = 0; drivers[i]; i++) {
@@ -110,40 +91,6 @@ void srtest_driver_init_all(struct sr_context *sr_ctx)
 	}
 }
 
-/* Initialize a libsigrok input module. */
-void srtest_input_init(struct sr_context *sr_ctx, struct sr_input_format *input)
-{
-	int ret;
-	struct sr_input *in;
-
-	(void)sr_ctx;
-
-	in = g_try_malloc0(sizeof(struct sr_input));
-	fail_unless(in != NULL);
-
-	in->format = input;
-	in->param = NULL;
-
-	ret = in->format->init(in, "nonexisting.dat");
-	fail_unless(ret == SR_OK, "Failed to init '%s' input module: %d.",
-		    input->id, ret);
-
-	g_free(in);
-}
-
-/* Initialize all libsigrok input modules. */
-void srtest_input_init_all(struct sr_context *sr_ctx)
-{
-	struct sr_input_format **inputs;
-	int i;
-
-	inputs = sr_input_list();
-	fail_unless(inputs != NULL, "No input modules found.");
-
-	for (i = 0; inputs[i]; i++)
-		srtest_input_init(sr_ctx, inputs[i]);
-}
-
 /* Set the samplerate for the respective driver to the specified value. */
 void srtest_set_samplerate(struct sr_dev_driver *driver, uint64_t samplerate)
 {
@@ -151,7 +98,7 @@ void srtest_set_samplerate(struct sr_dev_driver *driver, uint64_t samplerate)
 	struct sr_dev_inst *sdi;
 	GVariant *gvar;
 
-	sdi = g_slist_nth_data(driver->priv, 0);
+	sdi = g_slist_nth_data(driver->context, 0);
 
 	gvar = g_variant_new_uint64(samplerate);
 	ret = driver->config_set(SR_CONF_SAMPLERATE, gvar, sdi, NULL);
@@ -169,7 +116,7 @@ uint64_t srtest_get_samplerate(struct sr_dev_driver *driver)
 	struct sr_dev_inst *sdi;
 	GVariant *gvar;
 
-	sdi = g_slist_nth_data(driver->priv, 0);
+	sdi = g_slist_nth_data(driver->context, 0);
 
 	ret = driver->config_get(SR_CONF_SAMPLERATE, &gvar, sdi, NULL);
 	samplerate = g_variant_get_uint64(gvar);
@@ -196,21 +143,6 @@ void srtest_check_samplerate(struct sr_context *sr_ctx, const char *drivername,
 		    drivername, s);
 }
 
-void srtest_buf_to_file(const char *filename, const uint8_t *buf, uint64_t len)
-{
-	FILE *f;
-	GError *error = NULL;
-	gboolean ret;
-
-	f = g_fopen(filename, "wb");
-	fail_unless(f != NULL);
-
-	ret = g_file_set_contents(filename, (const gchar *)buf, len, &error);
-	fail_unless(ret == TRUE);
-
-	fclose(f);
-}
-
 GArray *srtest_get_enabled_logic_channels(const struct sr_dev_inst *sdi)
 {
 	struct sr_channel *ch;
@@ -218,7 +150,7 @@ GArray *srtest_get_enabled_logic_channels(const struct sr_dev_inst *sdi)
 	GSList *l;
 
 	channels = g_array_new(FALSE, FALSE, sizeof(int));
-	for (l = sdi->channels; l; l = l->next) {
+	for (l = sr_dev_inst_channels_get(sdi); l; l = l->next) {
 		ch = l->data;
 		if (ch->type != SR_CHANNEL_LOGIC)
 			continue;
diff --git a/tests/lib.h b/tests/lib.h
index e2e607e..576e068 100644
--- a/tests/lib.h
+++ b/tests/lib.h
@@ -21,24 +21,25 @@
 #ifndef LIBSIGROK_TESTS_LIB_H
 #define LIBSIGROK_TESTS_LIB_H
 
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+extern struct sr_context *srtest_ctx;
+
+void srtest_setup(void);
+void srtest_teardown(void);
 
 struct sr_dev_driver *srtest_driver_get(const char *drivername);
-struct sr_input_format *srtest_input_get(const char *id);
-struct sr_output_format *srtest_output_get(const char *id);
 
 void srtest_driver_init(struct sr_context *sr_ctx, struct sr_dev_driver *driver);
 void srtest_driver_init_all(struct sr_context *sr_ctx);
 
-void srtest_input_init(struct sr_context *sr_ctx, struct sr_input_format *input);
-void srtest_input_init_all(struct sr_context *sr_ctx);
-
 void srtest_set_samplerate(struct sr_dev_driver *driver, uint64_t samplerate);
 uint64_t srtest_get_samplerate(struct sr_dev_driver *driver);
 void srtest_check_samplerate(struct sr_context *sr_ctx, const char *drivername,
 			     uint64_t samplerate);
 
-void srtest_buf_to_file(const char *filename, const uint8_t *buf, uint64_t len);
 GArray *srtest_get_enabled_logic_channels(const struct sr_dev_inst *sdi);
 
 Suite *suite_core(void);
@@ -46,7 +47,12 @@ Suite *suite_driver_all(void);
 Suite *suite_input_all(void);
 Suite *suite_input_binary(void);
 Suite *suite_output_all(void);
+Suite *suite_transform_all(void);
+Suite *suite_session(void);
 Suite *suite_strutil(void);
 Suite *suite_version(void);
+Suite *suite_device(void);
+Suite *suite_trigger(void);
+Suite *suite_analog(void);
 
 #endif
diff --git a/tests/check_main.c b/tests/main.c
similarity index 84%
rename from tests/check_main.c
rename to tests/main.c
index c4ce143..cdd7b4f 100644
--- a/tests/check_main.c
+++ b/tests/main.c
@@ -18,9 +18,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <check.h>
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "lib.h"
 
 int main(void)
@@ -38,8 +39,13 @@ int main(void)
 	srunner_add_suite(srunner, suite_input_all());
 	srunner_add_suite(srunner, suite_input_binary());
 	srunner_add_suite(srunner, suite_output_all());
+	srunner_add_suite(srunner, suite_transform_all());
+	srunner_add_suite(srunner, suite_session());
 	srunner_add_suite(srunner, suite_strutil());
 	srunner_add_suite(srunner, suite_version());
+	srunner_add_suite(srunner, suite_device());
+	srunner_add_suite(srunner, suite_trigger());
+	srunner_add_suite(srunner, suite_analog());
 
 	srunner_run_all(srunner, CK_VERBOSE);
 	ret = srunner_ntests_failed(srunner);
diff --git a/tests/output_all.c b/tests/output_all.c
new file mode 100644
index 0000000..a1f6f76
--- /dev/null
+++ b/tests/output_all.c
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Uwe Hermann <uwe at 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 <config.h>
+#include <stdlib.h>
+#include <check.h>
+#include <libsigrok/libsigrok.h>
+#include "lib.h"
+
+/* Check whether at least one output module is available. */
+START_TEST(test_output_available)
+{
+	const struct sr_output_module **outputs;
+
+	outputs = sr_output_list();
+	fail_unless(outputs != NULL, "No output modules found.");
+}
+END_TEST
+
+/* Check whether sr_output_id_get() works. */
+START_TEST(test_output_id)
+{
+	const struct sr_output_module **outputs;
+	const char *id;
+
+	outputs = sr_output_list();
+
+	id = sr_output_id_get(outputs[0]);
+	fail_unless(id != NULL, "No id found in output module.");
+}
+END_TEST
+
+/* Check whether sr_output_name_get() works. */
+START_TEST(test_output_name)
+{
+	const struct sr_output_module **outputs;
+	const char *name;
+
+	outputs = sr_output_list();
+
+	name = sr_output_name_get(outputs[0]);
+	fail_unless(name != NULL, "No name found in output module.");
+}
+END_TEST
+
+/* Check whether sr_output_description_get() works. */
+START_TEST(test_output_desc)
+{
+	const struct sr_output_module **outputs;
+	const char *desc;
+
+	outputs = sr_output_list();
+
+	desc = sr_output_description_get(outputs[0]);
+	fail_unless(desc != NULL, "No description found in output module.");
+}
+END_TEST
+
+/* Check whether sr_output_find() works. */
+START_TEST(test_output_find)
+{
+	const struct sr_output_module *omod;
+	const char *id;
+
+	omod = sr_output_find("bits");
+	fail_unless(omod != NULL, "Couldn't find the 'bits' output module.");
+	id = sr_output_id_get(omod);
+	fail_unless(!strcmp(id, "bits"), "That is not the 'bits' module!");
+}
+END_TEST
+
+/* Check whether sr_output_options_get() works. */
+START_TEST(test_output_options)
+{
+	const struct sr_option **opt;
+
+	opt = sr_output_options_get(sr_output_find("bits"));
+	fail_unless(opt != NULL, "Couldn't find 'bits' options.");
+	fail_unless(!strcmp((*opt)->id, "width"), "Wrong 'bits' option found!");
+}
+END_TEST
+
+Suite *suite_output_all(void)
+{
+	Suite *s;
+	TCase *tc;
+
+	s = suite_create("output-all");
+
+	tc = tcase_create("basic");
+	tcase_add_test(tc, test_output_available);
+	tcase_add_test(tc, test_output_id);
+	tcase_add_test(tc, test_output_name);
+	tcase_add_test(tc, test_output_desc);
+	tcase_add_test(tc, test_output_find);
+	tcase_add_test(tc, test_output_options);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
diff --git a/tests/session.c b/tests/session.c
new file mode 100644
index 0000000..cfdf53f
--- /dev/null
+++ b/tests/session.c
@@ -0,0 +1,214 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe at 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 <config.h>
+#include <stdlib.h>
+#include <check.h>
+#include <libsigrok/libsigrok.h>
+#include "lib.h"
+
+/*
+ * Check whether sr_session_new() works.
+ * If it returns != SR_OK (or segfaults) this test will fail.
+ */
+START_TEST(test_session_new)
+{
+	int ret;
+	struct sr_session *sess;
+
+	ret = sr_session_new(srtest_ctx, &sess);
+	fail_unless(ret == SR_OK, "sr_session_new() failed: %d.", ret);
+	sr_session_destroy(sess);
+}
+END_TEST
+
+/*
+ * Check whether sr_session_new() fails for bogus parameters.
+ * If it returns SR_OK (or segfaults) this test will fail.
+ */
+START_TEST(test_session_new_bogus)
+{
+	int ret;
+
+	ret = sr_session_new(srtest_ctx, NULL);
+	fail_unless(ret != SR_OK, "sr_session_new(NULL) worked.");
+}
+END_TEST
+
+/*
+ * Check whether multiple sr_session_new() calls work.
+ * If any call returns != SR_OK (or segfaults) this test will fail.
+ */
+START_TEST(test_session_new_multiple)
+{
+	int ret;
+	struct sr_session *sess1, *sess2, *sess3;
+
+	sess1 = sess2 = sess3 = NULL;
+
+	/* Multiple sr_session_new() calls must work. */
+	ret = sr_session_new(srtest_ctx, &sess1);
+	fail_unless(ret == SR_OK, "sr_session_new() 1 failed: %d.", ret);
+	ret = sr_session_new(srtest_ctx, &sess2);
+	fail_unless(ret == SR_OK, "sr_session_new() 2 failed: %d.", ret);
+	ret = sr_session_new(srtest_ctx, &sess3);
+	fail_unless(ret == SR_OK, "sr_session_new() 3 failed: %d.", ret);
+
+	/* The returned session pointers must all be non-NULL. */
+	fail_unless(sess1 != NULL);
+	fail_unless(sess2 != NULL);
+	fail_unless(sess3 != NULL);
+
+	/* The returned session pointers must not be the same. */
+	fail_unless(sess1 != sess2);
+	fail_unless(sess1 != sess3);
+	fail_unless(sess2 != sess3);
+
+	/* Destroying any of the sessions must work. */
+	ret = sr_session_destroy(sess1);
+	fail_unless(ret == SR_OK, "sr_session_destroy() 1 failed: %d.", ret);
+	ret = sr_session_destroy(sess2);
+	fail_unless(ret == SR_OK, "sr_session_destroy() 2 failed: %d.", ret);
+	ret = sr_session_destroy(sess3);
+	fail_unless(ret == SR_OK, "sr_session_destroy() 3 failed: %d.", ret);
+}
+END_TEST
+
+/*
+ * Check whether sr_session_destroy() works.
+ * If it returns != SR_OK (or segfaults) this test will fail.
+ */
+START_TEST(test_session_destroy)
+{
+	int ret;
+	struct sr_session *sess;
+
+	sr_session_new(srtest_ctx, &sess);
+	ret = sr_session_destroy(sess);
+	fail_unless(ret == SR_OK, "sr_session_destroy() failed: %d.", ret);
+}
+END_TEST
+
+/*
+ * Check whether sr_session_destroy() fails for bogus sessions.
+ * If it returns SR_OK (or segfaults) this test will fail.
+ */
+START_TEST(test_session_destroy_bogus)
+{
+	int ret;
+
+	ret = sr_session_destroy(NULL);
+	fail_unless(ret != SR_OK, "sr_session_destroy() worked.");
+}
+END_TEST
+
+START_TEST(test_session_trigger_set_get)
+{
+	int ret;
+	struct sr_session *sess;
+	struct sr_trigger *t1, *t2;
+
+	sr_session_new(srtest_ctx, &sess);
+	t1 = sr_trigger_new("T1");
+
+	/* Set a trigger and see if getting it works OK. */
+	ret = sr_session_trigger_set(sess, t1);
+	fail_unless(ret == SR_OK);
+	t2 = sr_session_trigger_get(sess);
+	fail_unless(t2 != NULL);
+	fail_unless(t1 == t2);
+	fail_unless(g_slist_length(t1->stages) == g_slist_length(t2->stages));
+	fail_unless(!strcmp(t1->name, t2->name));
+
+	sr_session_destroy(sess);
+}
+END_TEST
+
+START_TEST(test_session_trigger_set_get_null)
+{
+	int ret;
+	struct sr_session *sess;
+	struct sr_trigger *t;
+
+	sr_session_new(srtest_ctx, &sess);
+
+	/* Adding a NULL trigger is allowed. */
+	ret = sr_session_trigger_set(sess, NULL);
+	fail_unless(ret == SR_OK);
+	t = sr_session_trigger_get(sess);
+	fail_unless(t == NULL);
+
+	sr_session_destroy(sess);
+}
+END_TEST
+
+START_TEST(test_session_trigger_set_null)
+{
+	int ret;
+	struct sr_trigger *t;
+
+	t = sr_trigger_new("T1");
+
+	/* NULL session, must not segfault. */
+	ret = sr_session_trigger_set(NULL, t);
+	fail_unless(ret == SR_ERR_ARG);
+
+	/* NULL session and NULL trigger, must not segfault. */
+	ret = sr_session_trigger_set(NULL, NULL);
+	fail_unless(ret == SR_ERR_ARG);
+}
+END_TEST
+
+START_TEST(test_session_trigger_get_null)
+{
+	struct sr_trigger *t;
+
+	/* NULL session, must not segfault. */
+	t = sr_session_trigger_get(NULL);
+	fail_unless(t == NULL);
+}
+END_TEST
+
+Suite *suite_session(void)
+{
+	Suite *s;
+	TCase *tc;
+
+	s = suite_create("session");
+
+	tc = tcase_create("new_destroy");
+	tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
+	tcase_add_test(tc, test_session_new);
+	tcase_add_test(tc, test_session_new_bogus);
+	tcase_add_test(tc, test_session_new_multiple);
+	tcase_add_test(tc, test_session_destroy);
+	tcase_add_test(tc, test_session_destroy_bogus);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("trigger");
+	tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
+	tcase_add_test(tc, test_session_trigger_set_get);
+	tcase_add_test(tc, test_session_trigger_set_get_null);
+	tcase_add_test(tc, test_session_trigger_set_null);
+	tcase_add_test(tc, test_session_trigger_get_null);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
diff --git a/tests/check_strutil.c b/tests/strutil.c
similarity index 94%
rename from tests/check_strutil.c
rename to tests/strutil.c
index ad67ef2..53417fb 100644
--- a/tests/check_strutil.c
+++ b/tests/strutil.c
@@ -18,28 +18,11 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <check.h>
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "lib.h"
 
-struct sr_context *sr_ctx;
-
-static void setup(void)
-{
-	int ret;
-
-	ret = sr_init(&sr_ctx);
-	fail_unless(ret == SR_OK, "sr_init() failed: %d.", ret);
-}
-
-static void teardown(void)
-{
-	int ret;
-
-	ret = sr_exit(sr_ctx);
-	fail_unless(ret == SR_OK, "sr_exit() failed: %d.", ret);
-}
-
 static void test_samplerate(uint64_t samplerate, const char *expected)
 {
 	char *s;
@@ -180,7 +163,7 @@ Suite *suite_strutil(void)
 	s = suite_create("strutil");
 
 	tc = tcase_create("sr_samplerate_string");
-	tcase_add_checked_fixture(tc, setup, teardown);
+	tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
 	tcase_add_test(tc, test_hz);
 	tcase_add_test(tc, test_khz);
 	tcase_add_test(tc, test_mhz);
diff --git a/tests/transform_all.c b/tests/transform_all.c
new file mode 100644
index 0000000..785d99d
--- /dev/null
+++ b/tests/transform_all.c
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Uwe Hermann <uwe at 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 <config.h>
+#include <stdlib.h>
+#include <check.h>
+#include <libsigrok/libsigrok.h>
+#include "lib.h"
+
+/* Check whether at least one transform module is available. */
+START_TEST(test_transform_available)
+{
+	const struct sr_transform_module **transforms;
+
+	transforms = sr_transform_list();
+	fail_unless(transforms != NULL, "No transform modules found.");
+}
+END_TEST
+
+/* Check whether sr_transform_id_get() works. */
+START_TEST(test_transform_id)
+{
+	const struct sr_transform_module **transforms;
+	const char *id;
+
+	transforms = sr_transform_list();
+
+	id = sr_transform_id_get(transforms[0]);
+	fail_unless(id != NULL, "No ID found in transform module.");
+}
+END_TEST
+
+/* Check whether sr_transform_name_get() works. */
+START_TEST(test_transform_name)
+{
+	const struct sr_transform_module **transforms;
+	const char *name;
+
+	transforms = sr_transform_list();
+
+	name = sr_transform_name_get(transforms[0]);
+	fail_unless(name != NULL, "No name found in transform module.");
+}
+END_TEST
+
+/* Check whether sr_transform_description_get() works. */
+START_TEST(test_transform_desc)
+{
+	const struct sr_transform_module **transforms;
+	const char *desc;
+
+	transforms = sr_transform_list();
+
+	desc = sr_transform_description_get(transforms[0]);
+	fail_unless(desc != NULL, "No description found in transform module.");
+}
+END_TEST
+
+/* Check whether sr_transform_find() works. */
+START_TEST(test_transform_find)
+{
+	const struct sr_transform_module *tmod;
+	const char *id;
+
+	tmod = sr_transform_find("nop");
+	fail_unless(tmod != NULL, "Couldn't find the 'nop' transform module.");
+	id = sr_transform_id_get(tmod);
+	fail_unless(id != NULL, "No ID found in transform module.");
+	fail_unless(!strcmp(id, "nop"), "That is not the 'nop' module!");
+}
+END_TEST
+
+/* Check whether sr_transform_options_get() works. */
+START_TEST(test_transform_options)
+{
+	const struct sr_option **opt;
+
+	opt = sr_transform_options_get(sr_transform_find("nop"));
+	fail_unless(opt == NULL, "Transform module 'nop' doesn't have options.");
+}
+END_TEST
+
+Suite *suite_transform_all(void)
+{
+	Suite *s;
+	TCase *tc;
+
+	s = suite_create("transform-all");
+
+	tc = tcase_create("basic");
+	tcase_add_test(tc, test_transform_available);
+	tcase_add_test(tc, test_transform_id);
+	tcase_add_test(tc, test_transform_name);
+	tcase_add_test(tc, test_transform_desc);
+	tcase_add_test(tc, test_transform_find);
+	tcase_add_test(tc, test_transform_options);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
diff --git a/tests/trigger.c b/tests/trigger.c
new file mode 100644
index 0000000..1f28919
--- /dev/null
+++ b/tests/trigger.c
@@ -0,0 +1,281 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014 Uwe Hermann <uwe at 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 <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <check.h>
+#include <libsigrok/libsigrok.h>
+#include "lib.h"
+
+/* Test lots of triggers/stages/matches/channels */
+#define NUM_TRIGGERS 70
+#define NUM_STAGES 30
+#define NUM_MATCHES 70
+#define NUM_CHANNELS NUM_MATCHES
+
+/* Check whether creating/freeing triggers with valid names works. */
+START_TEST(test_trigger_new_free)
+{
+	int i;
+	struct sr_trigger *t[NUM_TRIGGERS];
+	char name[10];
+
+	/* Create a few triggers with a valid name. */
+	for (i = 0; i < NUM_TRIGGERS; i++) {
+		sprintf((char *)&name, "T%d", i);
+		t[i] = sr_trigger_new((const char *)&name);
+		fail_unless(t[i] != NULL);
+		fail_unless(!strcmp(t[i]->name, (const char *)&name));
+		fail_unless(t[i]->stages == NULL);
+	}
+
+	/* Free the triggers again (must not segfault). */
+	for (i = 0; i < NUM_TRIGGERS; i++)
+		sr_trigger_free(t[i]);
+}
+END_TEST
+
+/* Check whether creating/freeing triggers with NULL names works. */
+START_TEST(test_trigger_new_free_null)
+{
+	int i;
+	struct sr_trigger *t[NUM_TRIGGERS];
+
+	/* Create a few triggers with a NULL name (which is allowed). */
+	for (i = 0; i < NUM_TRIGGERS; i++) {
+		t[i] = sr_trigger_new(NULL);
+		fail_unless(t[i] != NULL);
+		fail_unless(t[i]->name == NULL);
+		fail_unless(t[i]->stages == NULL);
+	}
+
+	/* Free the triggers again (must not segfault). */
+	for (i = 0; i < NUM_TRIGGERS; i++)
+		sr_trigger_free(t[i]);
+}
+END_TEST
+
+/* Check whether sr_trigger_free(NULL) works without segfaulting. */
+START_TEST(test_trigger_free_null)
+{
+	sr_trigger_free(NULL);
+}
+END_TEST
+
+/* Check whether creating/freeing triggers with stages works. */
+START_TEST(test_trigger_stage_add)
+{
+	int i, j;
+	struct sr_trigger *t[NUM_TRIGGERS];
+	struct sr_trigger_stage *s[NUM_STAGES];
+
+	/* Create a few triggers with a valid name. */
+	for (i = 0; i < NUM_TRIGGERS; i++) {
+		t[i] = sr_trigger_new("T");
+
+		/* Add a bunch of trigger stages to this trigger. */
+		for (j = 0; j < NUM_STAGES; j++) {
+			s[j] = sr_trigger_stage_add(t[i]);
+			fail_unless(s[j] != NULL);
+			fail_unless(t[i]->stages != NULL);
+			fail_unless((int)g_slist_length(t[i]->stages) == (j + 1));
+			fail_unless(s[j]->stage == j);
+			fail_unless(s[j]->matches == NULL);
+		}
+	}
+
+	/* Free the triggers again (must not segfault). */
+	for (i = 0; i < NUM_TRIGGERS; i++)
+		sr_trigger_free(t[i]);
+}
+END_TEST
+
+/* Check whether creating NULL trigger stages fails (as it should). */
+START_TEST(test_trigger_stage_add_null)
+{
+	/* Should not segfault, but rather return NULL. */
+	fail_unless(sr_trigger_stage_add(NULL) == NULL);
+}
+END_TEST
+
+/* Check whether creating/freeing triggers with matches works. */
+START_TEST(test_trigger_match_add)
+{
+	int i, j, k, tm, ret;
+	struct sr_trigger *t[NUM_TRIGGERS];
+	struct sr_trigger_stage *s[NUM_STAGES];
+	struct sr_channel *chl[NUM_CHANNELS];
+	struct sr_channel *cha[NUM_CHANNELS];
+	char name[10];
+
+	/* Create a bunch of logic and analog channels. */
+	for (i = 0; i < NUM_CHANNELS; i++) {
+		sprintf((char *)&name, "L%d", i);
+		chl[i] = g_malloc0(sizeof(struct sr_channel));
+		chl[i]->index = i;
+		chl[i]->type = SR_CHANNEL_LOGIC;
+		chl[i]->enabled = TRUE;
+		chl[i]->name = g_strdup((const char *)&name);
+
+		sprintf((char *)&name, "A%d", i);
+		cha[i] = g_malloc0(sizeof(struct sr_channel));
+		cha[i]->index = i;
+		cha[i]->type = SR_CHANNEL_ANALOG;
+		cha[i]->enabled = TRUE;
+		cha[i]->name = g_strdup((const char *)&name);
+	}
+
+	/* Create a few triggers with a valid name. */
+	for (i = 0; i < NUM_TRIGGERS; i++) {
+		t[i] = sr_trigger_new("T");
+
+		/* Add a bunch of trigger stages to this trigger. */
+		for (j = 0; j < NUM_STAGES; j++) {
+			s[j] = sr_trigger_stage_add(t[i]);
+
+			/* Add a bunch of matches to this stage. */
+			for (k = 0; k < NUM_MATCHES; k++) {
+				/* Logic channel matches. */
+				tm = 1 + (k % 5); /* *_ZERO .. *_EDGE */
+				ret = sr_trigger_match_add(s[j], chl[k], tm, 0);
+				fail_unless(ret == SR_OK);
+
+				/* Analog channel matches. */
+				tm = 3 + (k % 4); /* *_RISING .. *_UNDER */
+				ret = sr_trigger_match_add(s[j], cha[k],
+					tm, ((rand() % 500) - 500) * 1.739);
+				fail_unless(ret == SR_OK);
+			}
+		}
+	}
+
+	/* Free the triggers again (must not segfault). */
+	for (i = 0; i < NUM_TRIGGERS; i++)
+		sr_trigger_free(t[i]);
+
+	/* Free the channels. */
+	for (i = 0; i < NUM_CHANNELS; i++) {
+		g_free(chl[i]->name);
+		g_free(chl[i]);
+		g_free(cha[i]->name);
+		g_free(cha[i]);
+	}
+}
+END_TEST
+
+/* Check whether trigger_match_add() copes well with incorrect input. */
+START_TEST(test_trigger_match_add_bogus)
+{
+	int ret;
+	struct sr_trigger *t;
+	struct sr_trigger_stage *s, *sl;
+	struct sr_channel *chl, *cha;
+
+	t = sr_trigger_new("T");
+	s = sr_trigger_stage_add(t);
+	chl = g_malloc0(sizeof(struct sr_channel));
+	chl->index = 0;
+	chl->type = SR_CHANNEL_LOGIC;
+	chl->enabled = TRUE;
+	chl->name = g_strdup("L0");
+	cha = g_malloc0(sizeof(struct sr_channel));
+	cha->index = 1;
+	cha->type = SR_CHANNEL_ANALOG;
+	cha->enabled = TRUE;
+	cha->name = g_strdup("A0");
+
+	/* Initially we have no matches at all. */
+	sl = t->stages->data;
+	fail_unless(g_slist_length(sl->matches) == 0);
+
+	/* NULL stage */
+	ret = sr_trigger_match_add(NULL, chl, SR_TRIGGER_ZERO, 0);
+	fail_unless(ret == SR_ERR_ARG);
+	fail_unless(g_slist_length(sl->matches) == 0);
+
+	/* NULL channel */
+	ret = sr_trigger_match_add(s, NULL, SR_TRIGGER_ZERO, 0);
+	fail_unless(ret == SR_ERR_ARG);
+	fail_unless(g_slist_length(sl->matches) == 0);
+
+	/* Invalid trigger matches for logic channels. */
+	ret = sr_trigger_match_add(s, chl, SR_TRIGGER_OVER, 0);
+	fail_unless(ret == SR_ERR_ARG);
+	fail_unless(g_slist_length(sl->matches) == 0);
+	ret = sr_trigger_match_add(s, chl, SR_TRIGGER_UNDER, 0);
+	fail_unless(ret == SR_ERR_ARG);
+	fail_unless(g_slist_length(sl->matches) == 0);
+
+	/* Invalid trigger matches for analog channels. */
+	ret = sr_trigger_match_add(s, cha, SR_TRIGGER_ZERO, 9.4);
+	fail_unless(ret == SR_ERR_ARG);
+	fail_unless(g_slist_length(sl->matches) == 0);
+	ret = sr_trigger_match_add(s, cha, SR_TRIGGER_ONE, -9.4);
+	fail_unless(ret == SR_ERR_ARG);
+	fail_unless(g_slist_length(sl->matches) == 0);
+
+	/* Invalid channel type. */
+	chl->type = -1;
+	ret = sr_trigger_match_add(s, chl, SR_TRIGGER_ZERO, 0);
+	fail_unless(ret == SR_ERR_ARG);
+	fail_unless(g_slist_length(sl->matches) == 0);
+	chl->type = 270;
+	ret = sr_trigger_match_add(s, chl, SR_TRIGGER_ZERO, 0);
+	fail_unless(ret == SR_ERR_ARG);
+	fail_unless(g_slist_length(sl->matches) == 0);
+
+	sr_trigger_free(t);
+	g_free(chl->name);
+	g_free(chl);
+	g_free(cha->name);
+	g_free(cha);
+}
+END_TEST
+
+Suite *suite_trigger(void)
+{
+	Suite *s;
+	TCase *tc;
+
+	s = suite_create("trigger");
+
+	tc = tcase_create("new_free");
+	tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
+	tcase_add_test(tc, test_trigger_new_free);
+	tcase_add_test(tc, test_trigger_new_free_null);
+	tcase_add_test(tc, test_trigger_free_null);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("stage");
+	tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
+	tcase_add_test(tc, test_trigger_stage_add);
+	tcase_add_test(tc, test_trigger_stage_add_null);
+	suite_add_tcase(s, tc);
+
+	tc = tcase_create("match");
+	tcase_set_timeout(tc, 0);
+	tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
+	tcase_add_test(tc, test_trigger_match_add);
+	tcase_add_test(tc, test_trigger_match_add_bogus);
+	suite_add_tcase(s, tc);
+
+	return s;
+}
diff --git a/tests/check_version.c b/tests/version.c
similarity index 98%
rename from tests/check_version.c
rename to tests/version.c
index 37cf370..6280680 100644
--- a/tests/check_version.c
+++ b/tests/version.c
@@ -18,9 +18,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <check.h>
-#include "../libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "lib.h"
 
 /*
diff --git a/version.h.in b/version.h.in
deleted file mode 100644
index d044091..0000000
--- a/version.h.in
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2010-2012 Bert Vermeulen <bert at 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_VERSION_H
-#define LIBSIGROK_VERSION_H
-
-/**
- * @file
- *
- * Version number definitions and macros.
- */
-
-/**
- * @ingroup grp_versions
- *
- * @{
- */
-
-/*
- * Package version macros (can be used for conditional compilation).
- */
-
-/** The libsigrok package 'major' version number. */
-#define SR_PACKAGE_VERSION_MAJOR @SR_PACKAGE_VERSION_MAJOR@
-
-/** The libsigrok package 'minor' version number. */
-#define SR_PACKAGE_VERSION_MINOR @SR_PACKAGE_VERSION_MINOR@
-
-/** The libsigrok package 'micro' version number. */
-#define SR_PACKAGE_VERSION_MICRO @SR_PACKAGE_VERSION_MICRO@
-
-/** The libsigrok package version ("major.minor.micro") as string. */
-#define SR_PACKAGE_VERSION_STRING "@SR_PACKAGE_VERSION@"
-
-/*
- * Library/libtool version macros (can be used for conditional compilation).
- */
-
-/** The libsigrok libtool 'current' version number. */
-#define SR_LIB_VERSION_CURRENT @SR_LIB_VERSION_CURRENT@
-
-/** The libsigrok libtool 'revision' version number. */
-#define SR_LIB_VERSION_REVISION @SR_LIB_VERSION_REVISION@
-
-/** The libsigrok libtool 'age' version number. */
-#define SR_LIB_VERSION_AGE @SR_LIB_VERSION_AGE@
-
-/** The libsigrok libtool version ("current:revision:age") as string. */
-#define SR_LIB_VERSION_STRING "@SR_LIB_VERSION@"
-
-/** @} */
-
-#endif

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/libsigrok.git



More information about the debian-science-commits mailing list