# Editor/IDE cruft
*.kate-swp
*~
+.*.sw*
/*.kdev4
/Makefile.am.user
# Backend files
libsigrok_la_SOURCES = \
src/backend.c \
+ src/conversion.c \
src/device.c \
src/session.c \
src/session_file.c \
src/input/binary.c \
src/input/chronovu_la8.c \
src/input/csv.c \
+ src/input/logicport.c \
src/input/raw_analog.c \
src/input/trace32_ad.c \
src/input/vcd.c \
- src/input/wav.c
+ src/input/wav.c \
+ src/input/null.c
# Output modules
libsigrok_la_SOURCES += \
src/output/hex.c \
src/output/ols.c \
src/output/srzip.c \
- src/output/vcd.c
+ src/output/vcd.c \
+ src/output/null.c
# Transform modules
libsigrok_la_SOURCES += \
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 (DMM chip parsers)
libsigrok_la_SOURCES += \
+ src/dmm/asycii.c \
+ src/dmm/bm25x.c \
+ src/dmm/dtm0660.c \
+ src/dmm/eev121gw.c \
src/dmm/es519xx.c \
src/dmm/fs9721.c \
src/dmm/fs9922.c \
src/dmm/m2110.c \
src/dmm/metex14.c \
- src/dmm/asycii.c \
+ src/dmm/ms8250d.c \
src/dmm/rs9lcd.c \
- src/dmm/bm25x.c \
- src/dmm/ut71x.c \
src/dmm/ut372.c \
+ src/dmm/ut71x.c \
src/dmm/vc870.c \
- src/dmm/dtm0660.c
+ src/dmm/vc96.c
# Hardware (LCR chip parsers)
if NEED_SERIAL
src/scale/kern.c
# Hardware drivers
-noinst_LTLIBRARIES = src/libdrivers.la
+noinst_LTLIBRARIES = src/libdrivers.la \
+ src/libdrivers_head.la src/libdrivers_tail.la
-src/libdrivers.o: src/libdrivers.la
- $(AM_V_CCLD)$(LINK) src/libdrivers.la
+src/libdrivers.o: src/libdrivers.la \
+ src/libdrivers_head.la src/libdrivers_tail.la
+ $(AM_V_CCLD)$(LINK) src/libdrivers_head.la src/libdrivers.la \
+ src/libdrivers_tail.la
src/libdrivers.lo: src/libdrivers.o
$(AM_V_GEN)echo "# Generated by libtool" > $@
$(AM_V_at)echo "pic_object='libdrivers.o'" >> $@
$(AM_V_at)echo "non_pic_object='libdrivers.o'" >> $@
+src_libdrivers_head_la_SOURCES = src/driver_list_start.c
+
+src_libdrivers_tail_la_SOURCES = src/driver_list_stop.c
+
src_libdrivers_la_SOURCES = src/drivers.c
if HW_AGILENT_DMM
src/hardware/beaglelogic/beaglelogic.h \
src/hardware/beaglelogic/protocol.h \
src/hardware/beaglelogic/protocol.c \
- src/hardware/beaglelogic/api.c
+ src/hardware/beaglelogic/api.c \
+ src/hardware/beaglelogic/beaglelogic_native.c \
+ src/hardware/beaglelogic/beaglelogic_tcp.c
endif
if HW_BRYMEN_BM86X
src_libdrivers_la_SOURCES += \
src/hardware/demo/protocol.c \
src/hardware/demo/api.c
endif
+if HW_DREAMSOURCELAB_DSLOGIC
+src_libdrivers_la_SOURCES += \
+ src/hardware/dreamsourcelab-dslogic/protocol.h \
+ src/hardware/dreamsourcelab-dslogic/protocol.c \
+ src/hardware/dreamsourcelab-dslogic/api.c
+endif
+if HW_FLUKE_45
+src_libdrivers_la_SOURCES += \
+ src/hardware/fluke-45/protocol.h \
+ src/hardware/fluke-45/protocol.c \
+ src/hardware/fluke-45/api.c
+endif
if HW_FLUKE_DMM
src_libdrivers_la_SOURCES += \
src/hardware/fluke-dmm/protocol.h \
src_libdrivers_la_SOURCES += \
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/fx2lafw/api.c
endif
if HW_GMC_MH_1X_2X
src_libdrivers_la_SOURCES += \
src/hardware/gwinstek-gds-800/protocol.c \
src/hardware/gwinstek-gds-800/api.c
endif
+if HW_GWINSTEK_GPD
+src_libdrivers_la_SOURCES += \
+ src/hardware/gwinstek-gpd/protocol.h \
+ src/hardware/gwinstek-gpd/protocol.c \
+ src/hardware/gwinstek-gpd/api.c
+endif
if HW_HAMEG_HMO
src_libdrivers_la_SOURCES += \
src/hardware/hameg-hmo/protocol.h \
src/hardware/hameg-hmo/protocol.c \
src/hardware/hameg-hmo/api.c
endif
+if HW_HANTEK_4032L
+src_libdrivers_la_SOURCES += \
+ src/hardware/hantek-4032l/protocol.h \
+ src/hardware/hantek-4032l/protocol.c \
+ src/hardware/hantek-4032l/api.c
+endif
if HW_HANTEK_6XXX
src_libdrivers_la_SOURCES += \
src/hardware/hantek-6xxx/protocol.h \
src/hardware/hp-3457a/protocol.c \
src/hardware/hp-3457a/api.c
endif
+if HW_HP_3478A
+src_libdrivers_la_SOURCES += \
+ src/hardware/hp-3478a/protocol.h \
+ src/hardware/hp-3478a/protocol.c \
+ src/hardware/hp-3478a/api.c
+endif
if HW_HUNG_CHANG_DSO_2100
src_libdrivers_la_SOURCES += \
src/hardware/hung-chang-dso-2100/protocol.h \
src/hardware/ikalogic-scanaplus/protocol.c \
src/hardware/ikalogic-scanaplus/api.c
endif
+if HW_IPDBG_LA
+src_libdrivers_la_SOURCES += \
+ src/hardware/ipdbg-la/protocol.h \
+ src/hardware/ipdbg-la/protocol.c \
+ src/hardware/ipdbg-la/api.c
+endif
if HW_KECHENG_KC_330B
src_libdrivers_la_SOURCES += \
src/hardware/kecheng-kc-330b/protocol.h \
src/hardware/pipistrello-ols/protocol.c \
src/hardware/pipistrello-ols/api.c
endif
+if HW_RDTECH_DPS
+src_libdrivers_la_SOURCES += \
+ src/hardware/rdtech-dps/protocol.h \
+ src/hardware/rdtech-dps/protocol.c \
+ src/hardware/rdtech-dps/api.c
+endif
if HW_RIGOL_DS
src_libdrivers_la_SOURCES += \
src/hardware/rigol-ds/protocol.h \
src/hardware/saleae-logic16/protocol.c \
src/hardware/saleae-logic16/api.c
endif
+if HW_SALEAE_LOGIC_PRO
+src_libdrivers_la_SOURCES += \
+ src/hardware/saleae-logic-pro/protocol.h \
+ src/hardware/saleae-logic-pro/protocol.c \
+ src/hardware/saleae-logic-pro/api.c
+endif
if HW_SCPI_PPS
src_libdrivers_la_SOURCES += \
src/hardware/scpi-pps/protocol.h \
src_libdrivers_la_SOURCES += \
src/hardware/serial-lcr/api.c
endif
+if HW_SIGLENT_SDS
+src_libdrivers_la_SOURCES += \
+ src/hardware/siglent-sds/protocol.h \
+ src/hardware/siglent-sds/protocol.c \
+ src/hardware/siglent-sds/api.c
+endif
if HW_SYSCLK_LWLA
src_libdrivers_la_SOURCES += \
src/hardware/sysclk-lwla/lwla.h \
src/hardware/zeroplus-logic-cube/protocol.c \
src/hardware/zeroplus-logic-cube/api.c
endif
+if HW_ZKETECH_EBD_USB
+src_libdrivers_la_SOURCES += \
+ src/hardware/zketech-ebd-usb/protocol.h \
+ src/hardware/zketech-ebd-usb/protocol.c \
+ src/hardware/zketech-ebd-usb/api.c
+endif
libsigrok_la_LIBADD = src/libdrivers.lo $(SR_EXTRA_LIBS) $(LIBSIGROK_LIBS)
libsigrok_la_LDFLAGS = -version-info $(SR_LIB_VERSION) -no-undefined
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsigrok.pc
-mimeappdir = $(datadir)/mime/application
+mimeappdir = $(datadir)/mime/packages
mimeapp_DATA = contrib/vnd.sigrok.session.xml
mimeicondir = $(datadir)/icons/hicolor/48x48/mimetypes
contrib/libsigrok.png \
contrib/libsigrok.svg \
contrib/vnd.sigrok.session.xml \
- contrib/z60_libsigrok.rules
+ contrib/60-libsigrok.rules \
+ contrib/61-libsigrok-plugdev.rules \
+ contrib/61-libsigrok-uaccess.rules
if HAVE_CHECK
TESTS = tests/main
- 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 or libftdi1 >= 1.0 (optional, used by some drivers)
+ - 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)
The default locations where libsigrok expects the firmware files are:
+ $SIGROK_FIRMWARE_DIR (environment variable)
$HOME/.local/share/sigrok-firmware
$prefix/share/sigrok-firmware
/usr/local/share/sigrok-firmware
- scpi-pps
- serial-dmm (including all subdrivers)
- serial-lcr (including all subdrivers)
+ - siglent-sds
- teleinfo
- testo
- tondaj-sl-814
$ sigrok-cli --driver <somedriver>:conn=/dev/ttyUSB0 ...
-The following drivers/devices require a serial port specification:
+The following drivers/devices require a serial port specification. Some of
+the drivers implement a default for the connection.
- agilent-dmm
- appa-55ii
- yokogawa-dlm (USBTMC or TCP)
- zeroplus-logic-cube
+Beyond strict serial communication over COM ports (e.g. /dev/ttyUSB0), the
+conn= property can also address specific USB devices, as well as specify TCP
+or VXI communication parameters. See these examples:
+
+ $ sigrok-cli --driver <somedriver>:conn=<vid>.<pid> ...
+ $ sigrok-cli --driver <somedriver>:conn=tcp-raw/<ipaddr>/<port> ...
+ $ sigrok-cli --driver <somedriver>:conn=vxi/<ipaddr> ...
+
+The following drivers/devices accept network communication parameters:
+
+ - hameg-hmo
+ - rigol-ds
+ - siglent-sds
+ - yokogawa-dlm
+
Specifying serial port parameters
---------------------------------
(see below for details).
-Permissions for USB devices (udev rules file)
----------------------------------------------
+Permissions for USB devices (udev rules files)
+----------------------------------------------
When using USB-based devices supported by libsigrok, the user running the
libsigrok frontend (e.g. sigrok-cli) has to have (read/write) permissions
for the respective USB device.
-On Linux, this is accomplished using either 'chmod' (not recommended) or
-using the udev rules file shipped with libsigrok (recommended).
+On Linux, this is accomplished using udev rules. libsigrok ships a rules
+file containing all supported devices which can be detected reliably
+(generic USB-to-serial converters are omitted, as these are used for a wide
+range of devices, e.g. GPS receivers, which are not handled by libsigrok).
-The file is available in contrib/z60_libsigrok.rules. It contains entries
-for all libsigrok-supported (USB-based) devices and changes their group
-to 'plugdev' and the permissions to '664'.
+The file is available in contrib/60-libsigrok.rules. This file just contains
+the list of devices and flags these devices with ID_SIGROK="1". Access is
+granted by the 61-libsigrok-plugdev.rules or 61-libsigrok-uaccess.rules files,
+allowing access to members of the plugdev group or to currently logged in
+users, respectively.
When using a libsigrok package from your favorite Linux distribution, the
-packager will have already taken care of properly installing the udev file
-in the correct (distro-specific) place, and you don't have to do anything.
-The packager might also have adapted 'plugdev' and '664' as needed.
+files should already be installed in /usr/lib/udev/rules.d/, i.e.
+60-libsigrok.rules and one of the access granting rules files. Use of
+61-libsigrok-uaccess.rules is encouraged on systemd distributions.
+
+The access policy can be locally overridden by placing appropriate rules in
+/etc/udev/rules.d/, disabling or ammending the default policy. See the
+udev documentation, e.g. man 7 udev, for details.
If you're building from source, you need to copy the file to the place
-where your distro expects such files. This is beyond the scope of this README,
-but generally the location could be e.g. /etc/udev/rules.d, or maybe
-/lib/udev/rules.d, or something else. Afterwards you might have to restart
-udev, e.g. via '/etc/init.d/udev restart' or similar, and you'll have to
-re-attach your device via USB.
+where udev will read these rules. Local rules should go to /etc/udev/rules.d.
+Keep the file naming, otherwise interaction between the libsigrok rules and
+rules shipped by the system will be broken.
-Please consult the udev docs of your distro for details.
+Please consult the udev docs for details.
Cypress FX2 based devices
'SI232 online' (28-29S) or 'SI232 store' (22-26x). The interface must
be configured to the same baud rate as the host (default 9600).
Multimeter and interface must be configured to the same address.
+ - Metrix MX56C: Press the PRINT button to have the meter send acquisition
+ data via IR. Hold the PRINT button to adjust the meter's transmission
+ interval.
- Norma DM950: If the interface doesn't work (e.g. USB-RS232 converter), power
on the device with "FUNC" pressed (to power the interface from the DMM).
- PCE PCE-DM32: Briefly press the "RS232" button.
+#include <config.h>
+
const DataType *ConfigKey::data_type() const
{
const struct sr_key_info *info = sr_key_info_get(SR_KEY_CONFIG, id());
return get(info->key);
}
-#include <config.h>
-
#ifndef HAVE_STOI_STOD
/* Fallback implementation of stoi and stod */
}
#endif
-Glib::VariantBase ConfigKey::parse_string(string value) const
+Glib::VariantBase ConfigKey::parse_string(string value, enum sr_datatype dt)
{
GVariant *variant;
uint64_t p, q;
- switch (data_type()->id())
+ switch (dt)
{
case SR_T_UINT64:
check(sr_parse_sizestring(value.c_str(), &p));
case SR_T_FLOAT:
try {
variant = g_variant_new_double(stod(value));
- } catch (invalid_argument) {
+ } catch (invalid_argument&) {
throw Error(SR_ERR_ARG);
}
break;
case SR_T_INT32:
try {
variant = g_variant_new_int32(stoi(value));
- } catch (invalid_argument) {
+ } catch (invalid_argument&) {
throw Error(SR_ERR_ARG);
}
break;
return Glib::VariantBase(variant, false);
}
+Glib::VariantBase ConfigKey::parse_string(string value) const
+{
+ enum sr_datatype dt = (enum sr_datatype)(data_type()->id());
+ return parse_string(value, dt);
+}
/** 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. */
+ static Glib::VariantBase parse_string(string value, enum sr_datatype dt);
Glib::VariantBase parse_string(string value) const;
*/
/* Needed for isascii(), as used in the GNU libstdc++ headers */
+/* Needed in strutil.c for POSIX.1-2008 locale functions */
#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 600
+#define _XOPEN_SOURCE 700
#endif
#include <config.h>
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)));
+ _drivers.emplace(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)));
+ _input_formats.emplace(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)));
+ _output_formats.emplace(output->name(), move(output));
}
}
map<string, shared_ptr<Driver>> Context::drivers()
{
map<string, shared_ptr<Driver>> result;
- for (const auto &entry: _drivers)
- {
+ 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())});
+ result.emplace(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)
- {
+ 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())});
+ result.emplace(name, input_format->share_owned_by(shared_from_this()));
}
return result;
}
+shared_ptr<InputFormat> Context::input_format_match(string filename)
+{
+ const struct sr_input *input;
+ const struct sr_input_module *imod;
+ int rc;
+
+ /*
+ * Have the input module looked up for the specified file.
+ * Failed lookup (or "successful lookup" with an empty result)
+ * are non-fatal. Free the sr_input that was created by the
+ * lookup routine, but grab the input module kind and return an
+ * InputFormat instance to the application. This works because
+ * the application passes a filename, no input data got buffered
+ * in the sr_input that we release.
+ */
+ input = NULL;
+ rc = sr_input_scan_file(filename.c_str(), &input);
+ if (rc != SR_OK)
+ return nullptr;
+ if (!input)
+ return nullptr;
+ imod = sr_input_module_get(input);
+ sr_input_free(input);
+ return shared_ptr<InputFormat>{new InputFormat{imod}, default_delete<InputFormat>{}};
+}
+
map<string, shared_ptr<OutputFormat>> Context::output_formats()
{
map<string, shared_ptr<OutputFormat>> result;
- for (const auto &entry: _output_formats)
- {
+ 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())});
+ result.emplace(name, output_format->share_owned_by(shared_from_this()));
}
return result;
}
auto *const callback = static_cast<LogCallbackFunction *>(cb_data);
- try
- {
+ try {
(*callback)(LogLevel::get(loglevel), message.get());
- }
- catch (Error e)
- {
+ } catch (Error &e) {
return e.result;
}
map<const ConfigKey *, Glib::VariantBase> config)
{
auto meta = g_new0(struct sr_datafeed_meta, 1);
- for (const auto &input : config)
- {
+ for (const auto &input : config) {
const auto &key = input.first;
const auto &value = input.second;
auto *const output = g_new(struct sr_config, 1);
shared_ptr<Packet> Context::create_analog_packet(
vector<shared_ptr<Channel> > channels,
- float *data_pointer, unsigned int num_samples, const Quantity *mq,
+ const 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);
spec->spec_digits = 0;
analog->num_samples = num_samples;
- analog->data = data_pointer;
+ analog->data = (float*)data_pointer;
auto packet = g_new(struct sr_datafeed_packet, 1);
packet->type = SR_DF_ANALOG;
packet->payload = analog;
map<const ConfigKey *, Glib::VariantBase> options)
{
/* Initialise the driver if not yet done. */
- if (!_initialized)
- {
+ 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)
- {
+ for (const auto &entry : options) {
const auto &key = entry.first;
const auto &value = entry.second;
auto *const config = g_new(struct sr_config, 1);
/* Create device objects. */
vector<shared_ptr<HardwareDevice>> result;
- for (GSList *device = device_list; device; device = device->next)
- {
+ 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},
Configurable(sr_dev_inst_driver_get(structure), structure, nullptr),
_structure(structure)
{
- for (GSList *entry = sr_dev_inst_channels_get(structure); entry; entry = entry->next)
- {
+ 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)));
+ _channels.emplace(ch, move(channel));
}
- for (GSList *entry = sr_dev_inst_channel_groups_get(structure); entry; entry = entry->next)
- {
+ 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)));
+ _channel_groups.emplace(group->name(), move(group));
}
}
Device::~Device()
-{}
+{
+}
string Device::vendor() const
{
Device::channel_groups()
{
map<string, shared_ptr<ChannelGroup>> result;
- for (const auto &entry: _channel_groups)
- {
+ 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())});
+ result.emplace(name, channel_group->share_owned_by(get_shared_from_this()));
}
return result;
}
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)));
+ _channels.emplace(ch, move(channel));
return get_channel(ch);
}
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)));
+ _owned_devices.emplace(sdi, move(device));
}
_context->_session = this;
}
return QuantityFlag::flags_from_mask(_structure->meaning->mqflags);
}
+shared_ptr<Logic> Analog::get_logic_via_threshold(float threshold,
+ uint8_t *data_ptr) const
+{
+ auto datafeed = g_new(struct sr_datafeed_logic, 1);
+ datafeed->length = num_samples();
+ datafeed->unitsize = 1;
+
+ if (data_ptr)
+ datafeed->data = data_ptr;
+ else
+ datafeed->data = g_malloc(datafeed->length);
+
+ shared_ptr<Logic> logic =
+ shared_ptr<Logic>{new Logic{datafeed}, default_delete<Logic>{}};
+
+ check(sr_a2l_threshold(_structure, threshold,
+ (uint8_t*)datafeed->data, datafeed->length));
+
+ return logic;
+}
+
+shared_ptr<Logic> Analog::get_logic_via_schmitt_trigger(float lo_thr,
+ float hi_thr, uint8_t *state, uint8_t *data_ptr) const
+{
+ auto datafeed = g_new(struct sr_datafeed_logic, 1);
+ datafeed->length = num_samples();
+ datafeed->unitsize = 1;
+
+ if (data_ptr)
+ datafeed->data = data_ptr;
+ else
+ datafeed->data = g_malloc(datafeed->length);
+
+ shared_ptr<Logic> logic =
+ shared_ptr<Logic>{new Logic{datafeed}, default_delete<Logic>{}};
+
+ check(sr_a2l_schmitt_trigger(_structure, lo_thr, hi_thr, state,
+ (uint8_t*)datafeed->data, datafeed->length));
+
+ return logic;
+}
+
Rational::Rational(const struct sr_rational *structure) :
_structure(structure)
{
{
map<string, shared_ptr<Option>> result;
- if (const struct sr_option **options = sr_input_options_get(_structure))
- {
+ 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)});
+ result.emplace(opt->id(), move(opt));
}
}
return result;
shared_ptr<InputDevice> Input::device()
{
- if (!_device)
- {
+ if (!_device) {
auto sdi = sr_input_dev_inst_get(_structure);
if (!sdi)
throw Error(SR_ERR_NA);
return result;
}
+Glib::VariantBase Option::parse_string(string value)
+{
+ enum sr_datatype dt;
+ Glib::VariantBase dflt = default_value();
+ GVariant *tmpl = dflt.gobj();
+
+ if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_UINT64)) {
+ dt = SR_T_UINT64;
+ } else if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_STRING)) {
+ dt = SR_T_STRING;
+ } else if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_BOOLEAN)) {
+ dt = SR_T_BOOL;
+ } else if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_DOUBLE)) {
+ dt = SR_T_FLOAT;
+ } else if (g_variant_is_of_type(tmpl, G_VARIANT_TYPE_INT32)) {
+ dt = SR_T_INT32;
+ } else {
+ throw Error(SR_ERR_BUG);
+ }
+ return ConfigKey::parse_string(value, dt);
+}
+
OutputFormat::OutputFormat(const struct sr_output_module *structure) :
_structure(structure)
{
{
map<string, shared_ptr<Option>> result;
- if (const struct sr_option **options = sr_output_options_get(_structure))
- {
+ 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)});
+ result.emplace(opt->id(), move(opt));
}
}
return result;
{
GString *out;
check(sr_output_send(_structure, packet->_structure, &out));
- if (out)
- {
+ if (out) {
auto result = string(out->str, out->str + out->len);
g_string_free(out, true);
return result;
- }
- else
- {
+ } else {
return string();
}
}
map<string, shared_ptr<Driver> > drivers();
/** Available input formats, indexed by name. */
map<string, shared_ptr<InputFormat> > input_formats();
+ /** Lookup the responsible input module for an input file. */
+ shared_ptr<InputFormat> input_format_match(string filename);
/** Available output formats, indexed by name. */
map<string, shared_ptr<OutputFormat> > output_formats();
/** Current log level. */
/** 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 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. */
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. */
+ * @param capability Capability to check for. */
bool config_check(const ConfigKey *key, const Capability *capability) const;
protected:
Configurable(
const struct sr_datafeed_logic *_structure;
friend class Packet;
+ friend class Analog;
+ friend struct std::default_delete<Logic>;
};
/** Payload of a datafeed packet with analog data */
const Unit *unit() const;
/** Measurement flags associated with the samples in this packet. */
vector<const QuantityFlag *> mq_flags() const;
+ /**
+ * Provides a Logic packet that contains a conversion of the analog
+ * data using a simple threshold.
+ *
+ * @param threshold Threshold to use.
+ * @param data_ptr Pointer to num_samples() bytes where the logic
+ * samples are stored. When nullptr, memory for
+ * logic->data_pointer() will be allocated and must
+ * be freed by the caller.
+ */
+ shared_ptr<Logic> get_logic_via_threshold(float threshold,
+ uint8_t *data_ptr=nullptr) const;
+ /**
+ * Provides a Logic packet that contains a conversion of the analog
+ * data using a Schmitt-Trigger.
+ *
+ * @param lo_thr Low threshold to use (anything below this is low).
+ * @param hi_thr High threshold to use (anything above this is high).
+ * @param state Points to a byte that contains the current state of the
+ * converter. For best results, set to value of logic
+ * sample n-1.
+ * @param data_ptr Pointer to num_samples() bytes where the logic
+ * samples are stored. When nullptr, memory for
+ * logic->data_pointer() will be allocated and must be
+ * freed by the caller.
+ */
+ shared_ptr<Logic> get_logic_via_schmitt_trigger(float lo_thr,
+ float hi_thr, uint8_t *state, uint8_t *data_ptr=nullptr) const;
private:
explicit Analog(const struct sr_datafeed_analog *structure);
~Analog();
/** 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. */
+ * @note This list is a recommendation only. */
vector<string> extensions() const;
/** Options supported by this input format. */
map<string, shared_ptr<Option> > options();
Glib::VariantBase default_value() const;
/** Possible values for this option, if a limited set. */
vector<Glib::VariantBase> values() const;
+ /** Parse a string argument into the appropriate type for this option. */
+ Glib::VariantBase parse_string(string value);
private:
Option(const struct sr_option *structure,
shared_ptr<const struct sr_option *> structure_array);
/** 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. */
+ * @note This list is a recommendation only. */
vector<string> extensions() const;
/** Options supported by this output format. */
map<string, shared_ptr<Option> > options();
%module(docstring=DOCSTRING) classes
%{
+#include "config.h"
+
#include <stdio.h>
#include <pygobject.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
PyObject *PyGObject_lib;
PyObject *GLib;
-#include "config.h"
-
#if PYGOBJECT_FLAGS_SIGNED
typedef gint pyg_flags_type;
#else
%module(docstring=DOCSTRING) sigrok
%{
+#include "config.h"
+
#include <stdio.h>
#include <glibmm.h>
-
-#include "config.h"
%}
%include "../swig/templates.i"
SR_ARG_OPT_PKG([libserialport], [LIBSERIALPORT], [NEED_SERIAL],
[libserialport >= 0.1.1])
-SR_ARG_OPT_PKG([libftdi], [LIBFTDI],,
- [libftdi1 >= 1.0], [libftdi >= 0.16])
+SR_ARG_OPT_PKG([libftdi], [LIBFTDI], , [libftdi1 >= 1.0])
# 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.
SR_DRIVER([Colead SLM], [colead-slm], [libserialport])
SR_DRIVER([Conrad DIGI 35 CPU], [conrad-digi-35-cpu], [libserialport])
SR_DRIVER([demo], [demo])
+SR_DRIVER([DreamSourceLab DSLogic], [dreamsourcelab-dslogic], [libusb])
+SR_DRIVER([Fluke 45], [fluke-45])
SR_DRIVER([Fluke DMM], [fluke-dmm], [libserialport])
SR_DRIVER([FTDI LA], [ftdi-la], [libusb libftdi])
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([GW Instek GPD], [gwinstek-gpd], [libserialport])
SR_DRIVER([Hameg HMO], [hameg-hmo], [libserialport])
+SR_DRIVER([Hantek 4032L], [hantek-4032l], [libusb])
SR_DRIVER([Hantek 6xxx], [hantek-6xxx], [libusb])
SR_DRIVER([Hantek DSO], [hantek-dso], [libusb])
SR_DRIVER([HP 3457A], [hp-3457a])
+SR_DRIVER([HP 3478A], [hp-3478a], [libgpib])
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([IPDBG LA], [ipdbg-la])
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([OpenBench Logic Sniffer], [openbench-logic-sniffer], [libserialport])
SR_DRIVER([PCE PCE-322A], [pce-322a], [libserialport])
SR_DRIVER([Pipistrello-OLS], [pipistrello-ols], [libftdi])
+SR_DRIVER([RDTech DPSxxxx/DPHxxxx], [rdtech-dps], [libserialport])
SR_DRIVER([Rigol DS], [rigol-ds])
SR_DRIVER([Rohde&Schwarz SME-0x], [rohde-schwarz-sme-0x], [libserialport])
SR_DRIVER([Saleae Logic16], [saleae-logic16], [libusb])
+SR_DRIVER([Saleae Logic Pro], [saleae-logic-pro], [libusb])
SR_DRIVER([SCPI PPS], [scpi-pps])
SR_DRIVER([serial DMM], [serial-dmm], [libserialport])
SR_DRIVER([serial LCR], [serial-lcr], [libserialport])
+SR_DRIVER([Siglent SDS], [siglent-sds])
SR_DRIVER([Sysclk LWLA], [sysclk-lwla], [libusb])
SR_DRIVER([Teleinfo], [teleinfo], [libserialport])
SR_DRIVER([Testo], [testo], [libusb])
SR_DRIVER([Victor DMM], [victor-dmm], [libusb])
SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm])
SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb])
+SR_DRIVER([ZKETECH EBD-USB], [zketech-ebd-usb], [libserialport])
###############################
## Language bindings setup ##
- Java............................ $BINDINGS_JAVA$sr_report_java
_EOF
+
+# Emit a warning if the C++ bindings are not being built.
+AM_COND_IF([BINDINGS_CXX], [], [
+cat >&AS_MESSAGE_FD <<_EOF
+===============================================================================
+WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+===============================================================================
+=== ===
+=== The libsigrok C++ bindings are not being built since you ===
+=== are missing one or more dependencies (see above)! ===
+=== ===
+=== This means you won't be able to compile frontends that require ===
+=== the C++ bindings (such as PulseView)! You also won't be able to build ===
+=== other bindings and frontends using those (such as sigrok-meter)! ===
+=== ===
+===============================================================================
+WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+===============================================================================
+
+_EOF
+])
--- /dev/null
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2010-2013 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+#
+# These rules do not grant any permission by itself, but flag devices
+# supported by libsigrok.
+# The access policy is stored in the 61-libsigrok-plugdev.rules and
+# 61-libsigrok-uaccess.rules.
+#
+# Note: Any syntax changes here will need to be tested against the
+# 'update-device-filter' Makefile target in the sigrok-androidutils
+# repo, since that parses this file.
+#
+
+#
+# Please keep this list sorted alphabetically by vendor/device name.
+#
+
+ACTION!="add|change", GOTO="libsigrok_rules_end"
+SUBSYSTEM!="usb|usbmisc|usb_device", GOTO="libsigrok_rules_end"
+
+# Agilent USBTMC-connected devices
+# 34405A
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0618", ENV{ID_SIGROK}="1"
+# 34410A
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0607", ENV{ID_SIGROK}="1"
+# DSO1000 series
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", ENV{ID_SIGROK}="1"
+# MSO7000A series
+ATTRS{idVendor}=="0957", ATTRS{idProduct}=="1735", ENV{ID_SIGROK}="1"
+
+# ASIX SIGMA
+# ASIX SIGMA2
+ATTRS{idVendor}=="a600", ATTRS{idProduct}=="a000", ENV{ID_SIGROK}="1"
+
+# Braintechnology USB-LPS
+ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0498", ENV{ID_SIGROK}="1"
+
+# Brymen BU-86X adapter (e.g. for Brymen BM867/BM869 and possibly others).
+ATTRS{idVendor}=="0820", ATTRS{idProduct}=="0001", ENV{ID_SIGROK}="1"
+
+# CEM DT-8852
+ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ENV{ID_SIGROK}="1"
+
+# ChronoVu LA8 (new VID/PID)
+# ChronoVu LA16 (new VID/PID)
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8867", ENV{ID_SIGROK}="1"
+
+# CWAV USBee AX
+# ARMFLY AX-Pro (clone of the CWAV USBee AX)
+# ARMFLY Mini-Logic (clone of the CWAV USBee AX)
+# EE Electronics ESLA201A (clone of the CWAV USBee AX)
+# HT USBee-AxPro (clone of the CWAV USBee AX)
+# MCU123 USBee AX Pro clone (clone of the CWAV USBee AX)
+# Noname LHT00SU1 (clone of the CWAV USBee AX)
+# XZL_Studio AX (clone of the CWAV USBee AX)
+ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0014", ENV{ID_SIGROK}="1"
+
+# CWAV USBee DX
+# HT USBee-DxPro (clone of the CWAV USBee DX), not yet supported!
+# XZL_Studio DX (clone of the CWAV USBee DX)
+ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0015", ENV{ID_SIGROK}="1"
+
+# CWAV USBee SX
+ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0009", ENV{ID_SIGROK}="1"
+
+# CWAV USBee ZX
+ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0005", ENV{ID_SIGROK}="1"
+
+# Cypress FX2 eval boards without EEPROM:
+# Lcsoft Mini Board
+# Braintechnology USB Interface V2.x
+# fx2grok-tiny
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", ENV{ID_SIGROK}="1"
+
+# Dangerous Prototypes Buspirate (v3)
+# ChronoVu LA8 (old VID/PID)
+# ChronoVu LA16 (old VID/PID)
+# ftdi-la
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ENV{ID_SIGROK}="1"
+
+# Dangerous Prototypes Buspirate (v4)
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00", ENV{ID_SIGROK}="1"
+
+# DreamSourceLab DSLogic
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0001", ENV{ID_SIGROK}="1"
+# DreamSourceLab DSLogic Pro
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0003", ENV{ID_SIGROK}="1"
+# DreamSourceLab DScope
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0002", ENV{ID_SIGROK}="1"
+# DreamSourceLab DSLogic Plus
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0020", ENV{ID_SIGROK}="1"
+# DreamSourceLab DSLogic Basic
+ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0021", ENV{ID_SIGROK}="1"
+
+# HAMEG HO720
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed72", ENV{ID_SIGROK}="1"
+
+# HAMEG HO730
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed73", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-2090
+# lsusb: "04b4:2090 Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:2090 ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2090", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2090", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-2150
+# lsusb: "04b4:2150 Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:2150 ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2150", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2150", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-2250
+# lsusb: "04b4:2250 Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:2250 ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2250", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2250", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-5200
+# lsusb: "04b4:5200 Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:5200 ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="5200", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="5200", ENV{ID_SIGROK}="1"
+
+# Hantek DSO-5200A
+# lsusb: "04b4:520a Cypress Semiconductor Corp."
+# lsusb after FW upload: "04b5:520a ROHM LSI Systems USA, LLC"
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="520a", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="520a", ENV{ID_SIGROK}="1"
+
+# Hantek 6022BE, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Hantek 6022BE
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6022", ENV{ID_SIGROK}="1"
+
+# Hantek 6022BL, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Hantek 6022BL
+ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="602a", ENV{ID_SIGROK}="1"
+
+# Hantek 4032L
+ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="4032", ENV{ID_SIGROK}="1"
+
+# IKALOGIC Scanalogic-2
+ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4123", ENV{ID_SIGROK}="1"
+
+# IKALOGIC ScanaPLUS
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", ENV{ID_SIGROK}="1"
+
+# Kecheng KC-330B
+ATTRS{idVendor}=="1041", ATTRS{idProduct}=="8101", ENV{ID_SIGROK}="1"
+
+# Lascar Electronics EL-USB-2
+# Lascar Electronics EL-USB-CO
+# This is actually the generic SiLabs (Cygnal) F32x USBXpress VID:PID.
+ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="0002", ENV{ID_SIGROK}="1"
+
+# LeCroy LogicStudio16
+ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a001", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a002", ENV{ID_SIGROK}="1"
+
+# Link Instruments MSO-19
+ATTRS{idVendor}=="3195", ATTRS{idProduct}=="f190", ENV{ID_SIGROK}="1"
+
+# Logic Shrimp
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa95", ENV{ID_SIGROK}="1"
+
+# MiniLA Mockup
+# ftdi-la
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ENV{ID_SIGROK}="1"
+
+# MIC 98581
+# MIC 98583
+# Tondaj SL-814
+ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", ENV{ID_SIGROK}="1"
+
+# Openbench Logic Sniffer
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", ENV{ID_SIGROK}="1"
+
+# Rigol DS1000 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0588", ENV{ID_SIGROK}="1"
+
+# Rigol 1000Z series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04ce", ENV{ID_SIGROK}="1"
+
+# Rigol DS2000 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b0", ENV{ID_SIGROK}="1"
+
+# Rigol DS4000 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b1", ENV{ID_SIGROK}="1"
+
+# Rigol DG4000 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0641", ENV{ID_SIGROK}="1"
+
+# Rigol DP800 series
+ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0e11", ENV{ID_SIGROK}="1"
+
+# Rohde&Schwarz HMO1002 Series VCP/USBTMC mode
+ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0118", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0119", ENV{ID_SIGROK}="1"
+
+# Sainsmart DDS120 / Rocktech BM102, renumerates as 1d50:608e "sigrok fx2lafw", Serial: Sainsmart DDS120
+ATTRS{idVendor}=="8102", ATTRS{idProduct}=="8102", ENV{ID_SIGROK}="1"
+
+# Saleae Logic
+# EE Electronics ESLA100 (clone of the Saleae Logic)
+# Hantek 6022BL in LA mode (clone of the Saleae Logic)
+# Instrustar ISDS205X in LA mode (clone of the Saleae Logic)
+# Robomotic MiniLogic (clone of the Saleae Logic)
+# Robomotic BugLogic 3 (clone of the Saleae Logic)
+# MCU123 Saleae Logic clone (clone of the Saleae Logic)
+ATTRS{idVendor}=="0925", ATTRS{idProduct}=="3881", ENV{ID_SIGROK}="1"
+
+# Saleae Logic16
+ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1001", ENV{ID_SIGROK}="1"
+
+# Saleae Logic Pro 16
+ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1006", ENV{ID_SIGROK}="1"
+
+# Siglent USBTMC devices.
+# f4ec:ee3a: E.g. SDS1052DL+ scope
+# f4ed:ee3a: E.g. SDS1202X-E scope or SDG1010 waveform generator
+ATTRS{idVendor}=="f4ec", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="f4ed", ATTRS{idProduct}=="ee3a", ENV{ID_SIGROK}="1"
+
+# sigrok FX2 LA (8ch)
+# fx2grok-flat (before and after renumeration)
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608c", ENV{ID_SIGROK}="1"
+
+# sigrok FX2 LA (16ch)
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608d", ENV{ID_SIGROK}="1"
+
+# sigrok FX2 DSO (2ch)
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", ENV{ID_SIGROK}="1"
+
+# sigrok usb-c-grok
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608f", ENV{ID_SIGROK}="1"
+
+# SysClk LWLA1016
+ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6688", ENV{ID_SIGROK}="1"
+
+# SysClk LWLA1034
+ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6689", ENV{ID_SIGROK}="1"
+
+# Testo 435
+ATTRS{idVendor}=="128d", ATTRS{idProduct}=="0003", ENV{ID_SIGROK}="1"
+
+# UNI-T UT-D04 multimeter cable (for various UNI-T and rebranded DMMs)
+# http://sigrok.org/wiki/Device_cables#UNI-T_UT-D04
+# UNI-T UT325
+ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="e008", ENV{ID_SIGROK}="1"
+
+# V&A VA4000 multimeter cable (for various V&A DMMs)
+# http://sigrok.org/wiki/Device_cables#V.26A_VA4000
+ATTRS{idVendor}=="04fc", ATTRS{idProduct}=="0201", ENV{ID_SIGROK}="1"
+
+# Victor 70C
+# Victor 86C
+ATTRS{idVendor}=="1244", ATTRS{idProduct}=="d237", ENV{ID_SIGROK}="1"
+
+# ZEROPLUS Logic Cube LAP-C series
+# There are various devices in the ZEROPLUS Logic Cube series:
+# 0c12:7002: LAP-16128U
+# 0c12:7009: LAP-C(16064)
+# 0c12:700a: LAP-C(16128)
+# 0c12:700b: LAP-C(32128)
+# 0c12:700c: LAP-C(321000)
+# 0c12:700d: LAP-C(322000)
+# 0c12:700e: LAP-C(16032)
+# 0c12:7016: LAP-C(162000)
+# 0c12:7025: LAP-C(16128+)
+# 0c12:7100: AKIP-9101
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7002", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7007", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7009", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700a", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700b", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700c", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700d", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700e", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7016", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7025", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7100", ENV{ID_SIGROK}="1"
+
+LABEL="libsigrok_rules_end"
--- /dev/null
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2017 Stefan Bruens <stefan.bruens@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 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/>.
+##
+
+# Grant access permissions to users who are in the "plugdev" group.
+# "plugdev" is typically used on Debian based distributions and may not
+# exist elsewhere.
+#
+# This file, when installed, must be installed with a name lexicographically
+# sorted later than the accompanied 60-libsigrok.rules
+
+ACTION!="add|change", GOTO="libsigrok_rules_plugdev_end"
+
+ENV{ID_SIGROK}=="1", MODE="660", GROUP="plugdev"
+
+LABEL="libsigrok_rules_plugdev_end"
--- /dev/null
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2017 Stefan Bruens <stefan.bruens@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 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/>.
+##
+
+# Grant access permissions to users who are currently logged in locally.
+# This is the default policy for systems using systemd-logind (or a
+# compatible replacement).
+#
+# This file, when installed, must be installed with a name lexicographically
+# sorted later than the accompanied 60-libsigrok.rules, and earlier than
+# the systemd upstream 71-seat.rules.
+
+ACTION!="add|change", GOTO="libsigrok_rules_uaccess_end"
+
+ENV{ID_SIGROK}=="1", TAG+="uaccess"
+
+LABEL="libsigrok_rules_uaccess_end"
+++ /dev/null
-##
-## This file is part of the libsigrok project.
-##
-## Copyright (C) 2010-2013 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-##
-
-# NOTE: On a systemd based system, using tag=uaccess, this file _must_ be
-# named/numbered to come before the seat rules are applied in 70-xxx.
-
-##
-## Please keep this list sorted alphabetically by vendor/device name.
-##
-
-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="660", GROUP="plugdev", TAG+="uaccess"
-# 34410A
-ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0607", MODE="660", GROUP="plugdev", TAG+="uaccess"
-# DSO1000 series
-ATTRS{idVendor}=="0957", ATTRS{idProduct}=="0588", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# ASIX SIGMA
-# ASIX SIGMA2
-ATTRS{idVendor}=="a600", ATTRS{idProduct}=="a000", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Braintechnology USB-LPS
-ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0498", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# CEM DT-8852
-ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# ChronoVu LA8 (new VID/PID)
-# ChronoVu LA16 (new VID/PID)
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8867", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# CWAV USBee AX
-# ARMFLY AX-Pro (clone of the CWAV USBee AX)
-# ARMFLY Mini-Logic (clone of the CWAV USBee AX)
-# EE Electronics ESLA201A (clone of the CWAV USBee AX)
-# MCU123 USBee AX Pro clone (clone of the CWAV USBee AX)
-# XZL_Studio AX (clone of the CWAV USBee AX)
-ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0014", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# CWAV USBee DX
-# XZL_Studio DX (clone of the CWAV USBee DX)
-ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0015", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# CWAV USBee SX
-ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0009", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Cypress FX2 eval boards without EEPROM:
-# Lcsoft Mini Board
-# Braintechnology USB Interface V2.x
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Dangerous Prototypes Buspirate (v3)
-# ChronoVu LA8 (old VID/PID)
-# ChronoVu LA16 (old VID/PID)
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Dangerous Prototypes Buspirate (v4)
-ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# DreamSourceLab DSLogic
-ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0001", MODE="660", GROUP="plugdev", TAG+="uaccess"
-# DreamSourceLab DSLogic Pro
-ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0003", MODE="660", GROUP="plugdev", TAG+="uaccess"
-# DreamSourceLab DScope
-ATTRS{idVendor}=="2a0e", ATTRS{idProduct}=="0002", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# HAMEG HO720
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed72", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# HAMEG HO730
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="ed73", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-2090
-# lsusb: "04b4:2090 Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:2090 ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2090", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2090", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-2150
-# lsusb: "04b4:2150 Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:2150 ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2150", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2150", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-2250
-# lsusb: "04b4:2250 Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:2250 ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="2250", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="2250", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-5200
-# lsusb: "04b4:5200 Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:5200 ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="5200", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="5200", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek DSO-5200A
-# lsusb: "04b4:520a Cypress Semiconductor Corp."
-# lsusb after FW upload: "04b5:520a ROHM LSI Systems USA, LLC"
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="520a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="04b5", ATTRS{idProduct}=="520a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek 6022BE
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="6022", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Hantek 6022BL
-ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="602a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# IKALOGIC Scanalogic-2
-ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4123", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# IKALOGIC ScanaPLUS
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Kecheng KC-330B
-ATTRS{idVendor}=="1041", ATTRS{idProduct}=="8101", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Lascar Electronics EL-USB-2
-# Lascar Electronics EL-USB-CO
-# This is actually the generic SiLabs (Cygnal) F32x USBXpress VID:PID.
-ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="0002", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# LeCroy LogicStudio16
-ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a001", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="05ff", ATTRS{idProduct}=="a002", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Link Instruments MSO-19
-ATTRS{idVendor}=="3195", ATTRS{idProduct}=="f190", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Logic Shrimp
-ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fa95", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# MiniLA Mockup
-ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# MIC 98581
-# MIC 98583
-# Tondaj SL-814
-ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Openbench Logic Sniffer
-ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rigol DS1000 series
-ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0588", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rigol 1000Z series
-ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04ce", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rigol DS2000 series
-ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="04b0", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rigol DG4000 series
-ATTRS{idVendor}=="1ab1", ATTRS{idProduct}=="0641", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Rohde&Schwarz HMO1002 Series VCP/USBTMC mode
-ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0118", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0aad", ATTRS{idProduct}=="0119", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Sainsmart DDS120 / Rocktech BM102
-ATTRS{idVendor}=="8102", ATTRS{idProduct}=="8102", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Saleae Logic
-# EE Electronics ESLA100 (clone of the Saleae Logic)
-# Robomotic MiniLogic (clone of the Saleae Logic)
-# Robomotic BugLogic 3 (clone of the Saleae Logic)
-# MCU123 Saleae Logic clone (clone of the Saleae Logic)
-ATTRS{idVendor}=="0925", ATTRS{idProduct}=="3881", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Saleae Logic16
-ATTRS{idVendor}=="21a9", ATTRS{idProduct}=="1001", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# sigrok FX2 LA (8ch)
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608c", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# sigrok FX2 LA (16ch)
-ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608d", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# SysClk LWLA1016
-ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6688", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# SysClk LWLA1034
-ATTRS{idVendor}=="2961", ATTRS{idProduct}=="6689", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Testo 435
-ATTRS{idVendor}=="128d", ATTRS{idProduct}=="0003", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# 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
-ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="e008", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# V&A VA4000 multimeter cable (for various V&A DMMs)
-# http://sigrok.org/wiki/Device_cables#V.26A_VA4000
-ATTRS{idVendor}=="04fc", ATTRS{idProduct}=="0201", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# Victor 70C
-# Victor 86C
-ATTRS{idVendor}=="1244", ATTRS{idProduct}=="d237", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-# ZEROPLUS Logic Cube LAP-C series
-# There are various devices in the ZEROPLUS Logic Cube series:
-# 0c12:7002: LAP-16128U
-# 0c12:7009: LAP-C(16064)
-# 0c12:700a: LAP-C(16128)
-# 0c12:700b: LAP-C(32128)
-# 0c12:700c: LAP-C(321000)
-# 0c12:700d: LAP-C(322000)
-# 0c12:700e: LAP-C(16032)
-# 0c12:7016: LAP-C(162000)
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7002", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7009", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700a", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700b", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700c", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700d", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="700e", MODE="660", GROUP="plugdev", TAG+="uaccess"
-ATTRS{idVendor}=="0c12", ATTRS{idProduct}=="7016", MODE="660", GROUP="plugdev", TAG+="uaccess"
-
-LABEL="libsigrok_rules_end"
/* Handy little macros */
#define SR_HZ(n) (n)
-#define SR_KHZ(n) ((n) * (uint64_t)(1000ULL))
-#define SR_MHZ(n) ((n) * (uint64_t)(1000000ULL))
-#define SR_GHZ(n) ((n) * (uint64_t)(1000000000ULL))
+#define SR_KHZ(n) ((n) * UINT64_C(1000))
+#define SR_MHZ(n) ((n) * UINT64_C(1000000))
+#define SR_GHZ(n) ((n) * UINT64_C(1000000000))
-#define SR_HZ_TO_NS(n) ((uint64_t)(1000000000ULL) / (n))
+#define SR_HZ_TO_NS(n) (UINT64_C(1000000000) / (n))
/** libsigrok loglevels. */
enum sr_loglevel {
/** Configuration capabilities. */
enum sr_configcap {
/** Value can be read. */
- SR_CONF_GET = (1 << 31),
+ SR_CONF_GET = (1UL << 31),
/** Value can be written. */
- SR_CONF_SET = (1 << 30),
+ SR_CONF_SET = (1UL << 30),
/** Possible values can be enumerated. */
- SR_CONF_LIST = (1 << 29),
+ SR_CONF_LIST = (1UL << 29),
};
/** Configuration keys */
/** The device can act as a function generator. */
SR_CONF_SIGNAL_GENERATOR,
+ /** The device can measure power. */
+ SR_CONF_POWERMETER,
+
/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
/*--- Driver scan options -------------------------------------------*/
/** Trigger level. */
SR_CONF_TRIGGER_LEVEL,
+ /** Under-voltage condition threshold. */
+ SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD,
+
+ /**
+ * Which external clock source to use if the device supports
+ * multiple external clock channels.
+ */
+ SR_CONF_EXTERNAL_CLOCK_SOURCE,
+
/* Update sr_key_info_config[] (hwdriver.c) upon changes! */
/*--- Special stuff -------------------------------------------------*/
SR_API char *sr_buildinfo_host_get(void);
SR_API char *sr_buildinfo_scpi_backends_get(void);
+/*--- conversion.c ----------------------------------------------------------*/
+
+SR_API int sr_a2l_threshold(const struct sr_datafeed_analog *analog,
+ float threshold, uint8_t *output, uint64_t count);
+SR_API int sr_a2l_schmitt_trigger(const struct sr_datafeed_analog *analog,
+ float lo_thr, float hi_thr, uint8_t *state, uint8_t *output,
+ uint64_t count);
+
/*--- log.c -----------------------------------------------------------------*/
typedef int (*sr_log_callback)(void *cb_data, 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_callback_get(sr_log_callback *cb, void **cb_data);
/*--- device.c --------------------------------------------------------------*/
SR_API int sr_session_stopped_callback_set(struct sr_session *session,
sr_session_stopped_callback cb, void *cb_data);
+SR_API int sr_packet_copy(const struct sr_datafeed_packet *packet,
+ struct sr_datafeed_packet **copy);
+SR_API void sr_packet_free(struct sr_datafeed_packet *packet);
+
/*--- input/input.c ---------------------------------------------------------*/
SR_API const struct sr_input_module **sr_input_list(void);
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 const struct sr_input_module *sr_input_module_get(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 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 gboolean sr_output_test_flag(const struct sr_output_module *omod,
+ uint64_t flag);
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);
typedef gssize (*sr_resource_read_callback)(const struct sr_resource *res,
void *buf, size_t count, void *cb_data);
+SR_API GSList *sr_resourcepaths_get(int res_type);
+
SR_API int sr_resource_set_hooks(struct sr_context *ctx,
sr_resource_open_callback open_cb,
sr_resource_close_callback close_cb,
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);
+SR_API int sr_sprintf_ascii(char *buf, const char *format, ...);
+SR_API int sr_vsprintf_ascii(char *buf, const char *format, va_list args);
+SR_API int sr_snprintf_ascii(char *buf, size_t buf_size,
+ const char *format, ...);
+SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size,
+ const char *format, va_list args);
SR_API int sr_parse_rational(const char *str, struct sr_rational *ret);
/*--- version.c -------------------------------------------------------------*/
SR_API int sr_analog_to_float(const struct sr_datafeed_analog *analog,
float *outbuf)
{
- float offset;
- unsigned int b, i, count;
+ unsigned int b, count;
gboolean bigendian;
if (!analog || !(analog->data) || !(analog->meaning)
/* 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 (unsigned int 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] =
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);
+ float offset = ((float)analog->encoding->offset.p / (float)analog->encoding->offset.q);
outbuf[i] += offset;
}
}
SR_API const char *sr_analog_si_prefix(float *value, int *digits)
{
/** @cond PRIVATE */
-#define NEG_PREFIX_COUNT 5 /* number of prefixes below unity */
+#define NEG_PREFIX_COUNT 5 /* number of prefixes below unity */
#define POS_PREFIX_COUNT (int)(ARRAY_SIZE(prefixes) - NEG_PREFIX_COUNT - 1)
/** @endcond */
static const char *prefixes[] = { "f", "p", "n", "µ", "m", "", "k", "M", "G", "T" };
for (i = 0; i < ARRAY_SIZE(prefix_friendly_units); i++)
if (unit == prefix_friendly_units[i])
- break;
-
- if (unit != prefix_friendly_units[i])
- return FALSE;
+ return TRUE;
- return TRUE;
+ return FALSE;
}
/**
return SR_ERR_ARG;
}
- res->p = (int64_t)(p);
- res->q = (uint64_t)(q);
+ res->p = (int64_t)p;
+ res->q = (uint64_t)q;
return SR_OK;
m = g_slist_append(m, g_strdup_printf("%s", CONF_LIBUSB_1_0_VERSION));
#else
lv = libusb_get_version();
- m = g_slist_append(m, g_strdup_printf("%d.%d.%d.%d%s",
- lv->major, lv->minor, lv->micro, lv->nano, lv->rc));
+ m = g_slist_append(m, g_strdup_printf("%d.%d.%d.%d%s API 0x%08x",
+ lv->major, lv->minor, lv->micro, lv->nano, lv->rc,
+#if defined(LIBUSB_API_VERSION)
+ LIBUSB_API_VERSION
+#elif defined(LIBUSBX_API_VERSION)
+ LIBUSBX_API_VERSION
+#endif
+ ));
#endif
l = g_slist_append(l, m);
#endif
g_free(str);
}
+static void print_resourcepaths(void)
+{
+ GSList *l, *l_orig;
+
+ sr_dbg("Firmware search paths:");
+ l_orig = sr_resourcepaths_get(SR_RESOURCE_FIRMWARE);
+ for (l = l_orig; l; l = l->next)
+ sr_dbg(" - %s", (const char *)l->data);
+ g_slist_free_full(l_orig, g_free);
+}
+
/**
* Sanity-check all libsigrok drivers.
*
sr_err("No dev_list in driver %d ('%s').", i, d);
errors++;
}
- /* Note: dev_clear() is optional. */
+ if (!drivers[i]->dev_clear) {
+ sr_err("No dev_clear in driver %d ('%s').", i, d);
+ errors++;
+ }
/* Note: config_get() is optional. */
if (!drivers[i]->config_set) {
sr_err("No config_set in driver %d ('%s').", i, d);
print_versions();
+ print_resourcepaths();
+
if (!ctx) {
sr_err("%s(): libsigrok context was NULL.", __func__);
return SR_ERR;
if (sanity_check_all_drivers(context) < 0) {
sr_err("Internal driver error(s), aborting.");
- return ret;
+ goto done;
}
if (sanity_check_all_input_modules() < 0) {
sr_err("Internal input module error(s), aborting.");
- return ret;
+ goto done;
}
if (sanity_check_all_output_modules() < 0) {
sr_err("Internal output module error(s), aborting.");
- return ret;
+ goto done;
}
if (sanity_check_all_transform_modules() < 0) {
sr_err("Internal transform module error(s), aborting.");
- return ret;
+ goto done;
}
#ifdef _WIN32
context = NULL;
ret = SR_OK;
-#if defined(HAVE_LIBUSB_1_0) || defined(_WIN32)
done:
-#endif
g_free(context);
return ret;
}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Soeren Apel <soeren@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 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/>.
+ */
+
+/**
+ * @file
+ *
+ * Conversion helper functions.
+ */
+
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "conv"
+
+/**
+ * Convert analog values to logic values by using a fixed threshold.
+ *
+ * @param[in] analog The analog input values.
+ * @param[in] threshold The threshold to use.
+ * @param[out] output The converted output values; either 0 or 1. Must provide
+ * space for count bytes.
+ * @param[in] count The number of samples to process.
+ *
+ * @return SR_OK on success or SR_ERR on failure.
+ */
+SR_API int sr_a2l_threshold(const struct sr_datafeed_analog *analog,
+ float threshold, uint8_t *output, uint64_t count)
+{
+ float *input;
+
+ if (!analog->encoding->is_float) {
+ input = g_try_malloc(sizeof(float) * count);
+ if (!input)
+ return SR_ERR;
+
+ sr_analog_to_float(analog, input);
+ } else
+ input = analog->data;
+
+ for (uint64_t i = 0; i < count; i++)
+ output[i] = (input[i] >= threshold) ? 1 : 0;
+
+ if (!analog->encoding->is_float)
+ g_free(input);
+
+ return SR_OK;
+}
+
+/**
+ * Convert analog values to logic values by using a Schmitt-trigger algorithm.
+ *
+ * @param analog The analog input values.
+ * @param lo_thr The low threshold - result becomes 0 below it.
+ * @param lo_thr The high threshold - result becomes 1 above it.
+ * @param state The internal converter state. Must contain the state of logic
+ * sample n-1, will contain the state of logic sample n+count upon exit.
+ * @param output The converted output values; either 0 or 1. Must provide
+ * space for count bytes.
+ * @param count The number of samples to process.
+ *
+ * @return SR_OK on success or SR_ERR on failure.
+ */
+SR_API int sr_a2l_schmitt_trigger(const struct sr_datafeed_analog *analog,
+ float lo_thr, float hi_thr, uint8_t *state, uint8_t *output,
+ uint64_t count)
+{
+ float *input;
+
+ if (!analog->encoding->is_float) {
+ input = g_try_malloc(sizeof(float) * count);
+ if (!input)
+ return SR_ERR;
+
+ sr_analog_to_float(analog, input);
+ } else
+ input = analog->data;
+
+ for (uint64_t i = 0; i < count; i++) {
+ if (input[i] < lo_thr)
+ *state = 0;
+ else if (input[i] > hi_thr)
+ *state = 1;
+
+ output[i] = *state;
+ }
+
+ if (!analog->encoding->is_float)
+ g_free(input);
+
+ return SR_OK;
+}
*/
#include <config.h>
-#include <stdio.h>
#include <glib.h>
+#include <stdio.h>
+#include <string.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
return ch;
}
+/**
+ * Release a previously allocated struct sr_channel.
+ *
+ * @param[in] ch Pointer to struct sr_channel.
+ *
+ * @private
+ */
+SR_PRIV void sr_channel_free(struct sr_channel *ch)
+{
+ if (!ch)
+ return;
+ g_free(ch->name);
+ g_free(ch->priv);
+ g_free(ch);
+}
+
+/**
+ * Wrapper around @ref sr_channel_free(), suitable for glib iterators.
+ */
+SR_PRIV void sr_channel_free_cb(void *p)
+{
+ sr_channel_free(p);
+}
+
/**
* Set the name of the specified channel.
*
return SR_OK;
}
-/* Returns the next enabled channel, wrapping around if necessary. */
-/** @private */
+/**
+ * Returns the next enabled channel, wrapping around if necessary.
+ *
+ * @param[in] sdi The device instance the channel is connected to.
+ * Must not be NULL.
+ * @param[in] cur_channel The current channel.
+ *
+ * @return A pointer to the next enabled channel of this device.
+ *
+ * @private
+ */
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;
return next_channel;
}
+/**
+ * Compare two channels, return whether they differ.
+ *
+ * The channels' names and types are checked. The enabled state is not
+ * considered a condition for difference. The test is motivated by the
+ * desire to detect changes in the configuration of acquisition setups
+ * between re-reads of an input file.
+ *
+ * @param[in] ch1 First channel.
+ * @param[in] ch2 Second channel.
+ *
+ * @return #TRUE upon differences or unexpected input, #FALSE otherwise.
+ *
+ * @internal
+ */
+SR_PRIV gboolean sr_channels_differ(struct sr_channel *ch1, struct sr_channel *ch2)
+{
+ if (!ch1 || !ch2)
+ return TRUE;
+
+ if (ch1->type != ch2->type)
+ return TRUE;
+ if (strcmp(ch1->name, ch2->name))
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * Compare two channel lists, return whether they differ.
+ *
+ * Listing the same set of channels but in a different order is considered
+ * a difference in the lists.
+ *
+ * @param[in] l1 First channel list.
+ * @param[in] l2 Second channel list.
+ *
+ * @return #TRUE upon differences or unexpected input, #FALSE otherwise.
+ *
+ * @internal
+ */
+SR_PRIV gboolean sr_channel_lists_differ(GSList *l1, GSList *l2)
+{
+ struct sr_channel *ch1, *ch2;
+
+ while (l1 && l2) {
+ ch1 = l1->data;
+ ch2 = l2->data;
+ l1 = l1->next;
+ l2 = l2->next;
+ if (!ch1 || !ch2)
+ return TRUE;
+ if (sr_channels_differ(ch1, ch2))
+ return TRUE;
+ if (ch1->index != ch2->index)
+ return TRUE;
+ }
+ if (l1 || l2)
+ return TRUE;
+
+ return FALSE;
+}
+
/**
* Determine whether the specified device instance has the specified
* capability.
for (l = sdi->channels; l; l = l->next) {
ch = l->data;
- g_free(ch->name);
- g_free(ch->priv);
- g_free(ch);
+ sr_channel_free(ch);
}
g_slist_free(sdi->channels);
*/
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);
+ if (!driver->context) {
+ /*
+ * Driver was never initialized, nothing to do.
+ *
+ * No log message since this usually gets called for all
+ * drivers, whether they were initialized or not.
+ */
+ return SR_OK;
+ }
- return ret;
+ /* No log message here, too verbose and not very useful. */
+
+ return driver->dev_clear(driver);
}
/**
- * Open the specified device.
+ * Open the specified device instance.
+ *
+ * If the device instance is already open (sdi->status == SR_ST_ACTIVE),
+ * SR_ERR will be returned and no re-opening of the device will be attempted.
+ *
+ * If opening was successful, sdi->status is set to SR_ST_ACTIVE, otherwise
+ * it will be left unchanged.
*
* @param sdi Device instance to use. Must not be NULL.
*
- * @return SR_OK upon success, a negative error code upon errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid arguments.
+ * @retval SR_ERR Device instance was already active, or other error.
*
* @since 0.2.0
*/
int ret;
if (!sdi || !sdi->driver || !sdi->driver->dev_open)
+ return SR_ERR_ARG;
+
+ if (sdi->status == SR_ST_ACTIVE) {
+ sr_err("%s: Device instance already active, can't re-open.",
+ sdi->driver->name);
return SR_ERR;
+ }
+
+ sr_dbg("%s: Opening device instance.", sdi->driver->name);
ret = sdi->driver->dev_open(sdi);
+ if (ret == SR_OK)
+ sdi->status = SR_ST_ACTIVE;
+
return ret;
}
/**
- * Close the specified device.
+ * Close the specified device instance.
+ *
+ * If the device instance is not open (sdi->status != SR_ST_ACTIVE),
+ * SR_ERR_DEV_CLOSED will be returned and no closing will be attempted.
+ *
+ * Note: sdi->status will be set to SR_ST_INACTIVE, regardless of whether
+ * there are any errors during closing of the device instance (any errors
+ * will be reported via error code and log message, though).
*
* @param sdi Device instance to use. Must not be NULL.
*
- * @return SR_OK upon success, a negative error code upon errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid arguments.
+ * @retval SR_ERR_DEV_CLOSED Device instance was not active.
+ * @retval SR_ERR Other error.
*
* @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;
+ return SR_ERR_ARG;
- ret = sdi->driver->dev_close(sdi);
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("%s: Device instance not active, can't close.",
+ sdi->driver->name);
+ return SR_ERR_DEV_CLOSED;
+ }
- return ret;
+ sdi->status = SR_ST_INACTIVE;
+
+ sr_dbg("%s: Closing device instance.", sdi->driver->name);
+
+ return sdi->driver->dev_close(sdi);
}
/**
if (b != usb->bus || a != usb->address)
continue;
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
((struct sr_dev_inst *)sdi)->connection_id = g_strdup(connection_id);
break;
}
if (info->is_dc)
analog->meaning->mqflags |= SR_MQFLAG_DC;
if (info->is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
if (info->is_peak_max)
analog->meaning->mqflags |= SR_MQFLAG_MAX;
if (info->is_peak_min)
int ret, exponent;
struct asycii_info *info_local;
- info_local = (struct asycii_info *)info;
+ info_local = info;
/* Don't print byte 15. That one contains the carriage return. */
sr_dbg("DMM packet: \"%.15s\"", buf);
analog->meaning->mq = SR_MQ_VOLTAGE;
analog->meaning->unit = SR_UNIT_VOLT;
if ((analog->meaning->mqflags & (SR_MQFLAG_DC | SR_MQFLAG_AC)) == 0)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
}
if (buf[14] & 2) {
analog->meaning->mq = SR_MQ_CURRENT;
static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
int *exponent, const struct dtm0660_info *info)
{
+ int initial_exponent = *exponent;
+
/* Factors */
if (info->is_nano)
*exponent -= 9;
*exponent += 3;
if (info->is_mega)
*exponent += 6;
- *floatval *= powf(10, *exponent);
+ *floatval *= powf(10, (*exponent - initial_exponent));
/* Measurement modes */
if (info->is_volt) {
if (info->is_auto)
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
if (info->is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
if (info->is_hold)
analog->meaning->mqflags |= SR_MQFLAG_HOLD;
if (info->is_rel)
int ret, exponent = 0;
struct dtm0660_info *info_local;
- info_local = (struct dtm0660_info *)info;
+ info_local = info;
if ((ret = parse_value(buf, floatval, &exponent)) != SR_OK) {
sr_dbg("Error parsing value: %d.", ret);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/>.
+ */
+
+/**
+ * @file
+ *
+ * EEVblog 121GW 19-bytes binary protocol parser.
+ *
+ * @internal
+ *
+ * Note that this protocol is different from other meters. We need not
+ * decode the LCD presentation (segments a-g and dot of seven segment
+ * displays). Neither need we decode a textual presentation consisting
+ * of number strings with decimals, and scale/quantity suffixes. Instead
+ * a binary packet is received which contains an unsigned mantissa for
+ * the value, and a number of boolean flags as well as bitfields for modes
+ * and ranges.
+ *
+ * But the protocol is also similar to the four-display variant of the
+ * metex14 protocol. A single DMM packet contains information for two
+ * displays and a bargraph, as well as several flags corresponding to
+ * display indicators and global device state. The vendor's documentation
+ * refers to these sections as "main", "sub", "bar", and "icon".
+ *
+ * It's essential to understand that the serial-dmm API is only able to
+ * communicate a single float value (including its precision and quantity
+ * details) in a single parse call. Which is why we keep a channel index
+ * in the 'info' structure, and run the parse routine several times upon
+ * reception of a single packet. This approach is shared with the metex14
+ * parser.
+ *
+ * The parse routine here differs from other DMM parsers which typically
+ * are split into routines which parse a value (get a number and exponent),
+ * parse flags, and handle flags which were parsed before. The 121GW
+ * meter's packets don't fit this separation naturally, getting the value
+ * and related flags heavily depends on which display shall get inspected,
+ * thus should be done at the same time. Filling in an 'info' structure
+ * from packet content first, and mapping this 'info' to the 'analog'
+ * details then still is very useful for maintainability.
+ *
+ * TODO:
+ * - The meter is feature packed. This implementation does support basic
+ * operation (voltage, current, power, resistance, continuity, diode,
+ * capacitance, temperature). Support for remaining modes, previously
+ * untested ranges, and advanced features (DC+AC, VA power, dB gain,
+ * burden voltage) may be missing or incomplete. Ranges support and
+ * value scaling should be considered "under development" in general
+ * until test coverage was increased. Some flags are not evaluated
+ * correctly yet, or not at all (min/max/avg, memory).
+ * - Test previously untested modes: current, power, gain, sub display
+ * modes. Test untested ranges (voltage above 30V, temperature above
+ * 30deg (into the hundreds), negative temperatures, large resistors,
+ * large capacitors). Test untested features (min/max/avg, 1ms peak,
+ * log memory).
+ * - It's assumed that a continuous data stream was arranged for. This
+ * implementation does not support the "packet request" API. Also I
+ * was to understand that once the request was sent (write 0300 to
+ * handle 9, after connecting) no further request is needed. Only
+ * the loss of communication may need recovery, which we leave as an
+ * option for later improvement, or as a feature of an external helper
+ * which feeds the COM port from Bluetooth communication data, or
+ * abstracts away the BLE communication.
+ *
+ * Implementation notes:
+ * - Yes some ranges seem duplicate but that's fine. The meter's packets
+ * do provide multiple range indices for some of the modes which do
+ * communicate values in the same range of values.
+ * - Some of the packet's bits don't match the available documentation.
+ * Some of the meter's features are not available to the PC side by
+ * means of inspecting packets.
+ * - Bit 5 of "bar value" was seen with value 1 in FREQ and OHM:
+ * f2 17 84 21 21 08 00 00 00 64 01 01 17 12 37 02 40 00 7d
+ * So we keep the test around but accept when it fails.
+ * - The "gotta beep" activity of continuity/break test mode is not
+ * available in the packets.
+ * - The interpretation of range indices depends on the specific mode
+ * (meter's function, and range when selectable by the user like mV).
+ * As does the precision of results.
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include <glib.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include "libsigrok/libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "eev121gw"
+
+/*
+ * TODO:
+ * When these bit field extraction helpers move to some common location,
+ * their names may need adjustment to reduce the potential for conflicts.
+ */
+// #define BIT(n) (1UL << (n))
+#define MASK(len) ((1UL << (len)) - 1)
+#define FIELD_PL(v, pos, len) (((v) >> (pos)) & MASK(len))
+#define FIELD_NL(v, name) FIELD_PL(v, POS_ ## name, LEN_ ## name)
+#define FIELD_NB(v, name) FIELD_PL(v, POS_ ## name, 1)
+
+/*
+ * Support compile time checks for expected sizeof() results etc, like
+ * STATIC_ASSERT(sizeof(struct packet) == 19, "packet size");
+ * Probably should go to some common location.
+ * See http://www.pixelbeat.org/programming/gcc/static_assert.html for details.
+ */
+#define ASSERT_CONCAT_(a, b) a ## b
+#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
+/* These can't be used after statements in c89. */
+#ifdef __COUNTER__
+ #define STATIC_ASSERT(e, m) \
+ ; enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1 / (int)(!!(e)) }
+#else
+ /*
+ * This can't be used twice on the same line so ensure if using in headers
+ * that the headers are not included twice (by wrapping in #ifndef...#endif).
+ * Note it doesn't cause an issue when used on same line of separate modules
+ * compiled with gcc -combine -fwhole-program.
+ */
+ #define STATIC_ASSERT(e, m) \
+ ; enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1 / (int)(!!(e)) }
+#endif
+
+/*
+ * Symbolic identifiers for access to the packet's payload. "Offsets"
+ * address bytes within the packet. "Positions" specify the (lowest)
+ * bit number of a field, "lengths" specify the fields' number of bits.
+ * "Values" specify magic values or fixed content (SBZ, RSV, etc).
+ */
+enum eev121gw_packet_offs {
+ OFF_START_CMD,
+#define VAL_START_CMD 0xf2
+ OFF_SERIAL_3,
+ OFF_SERIAL_2,
+ OFF_SERIAL_1,
+ OFF_SERIAL_0,
+#define POS_SERIAL_YEAR 24
+#define LEN_SERIAL_YEAR 8
+#define POS_SERIAL_MONTH 20
+#define LEN_SERIAL_MONTH 4
+#define POS_SERIAL_NUMBER 0
+#define LEN_SERIAL_NUMBER 20
+ OFF_MAIN_MODE,
+#define POS_MAIN_MODE_VAL_U 6
+#define LEN_MAIN_MODE_VAL_U 2
+#define POS_MAIN_MODE_RSV_5 5
+#define POS_MAIN_MODE_MODE 0
+#define LEN_MAIN_MODE_MODE 5
+ OFF_MAIN_RANGE,
+#define POS_MAIN_RANGE_OFL 7
+#define POS_MAIN_RANGE_SIGN 6
+#define POS_MAIN_RANGE_DEGC 5
+#define POS_MAIN_RANGE_DEGF 4
+#define POS_MAIN_RANGE_RANGE 0
+#define LEN_MAIN_RANGE_RANGE 4
+ OFF_MAIN_VAL_H,
+ OFF_MAIN_VAL_L,
+ OFF_SUB_MODE,
+#define POS_SUB_MODE_MODE 0
+#define LEN_SUB_MODE_MODE 8
+ OFF_SUB_RANGE,
+#define POS_SUB_RANGE_OFL 7
+#define POS_SUB_RANGE_SIGN 6
+#define POS_SUB_RANGE_K 5
+#define POS_SUB_RANGE_HZ 4
+#define POS_SUB_RANGE_RSV_3 3
+#define POS_SUB_RANGE_POINT 0
+#define LEN_SUB_RANGE_POINT 3
+ OFF_SUB_VAL_H,
+ OFF_SUB_VAL_L,
+ OFF_BAR_STATUS,
+#define POS_BAR_STATUS_RSV_5 5
+#define LEN_BAR_STATUS_RSV_5 3
+#define POS_BAR_STATUS_USE 4
+#define POS_BAR_STATUS_150 3
+#define POS_BAR_STATUS_SIGN 2
+#define POS_BAR_STATUS_1K_500 0
+#define LEN_BAR_STATUS_1K_500 2
+ OFF_BAR_VALUE,
+#define POS_BAR_VALUE_RSV_6 6
+#define LEN_BAR_VALUE_RSV_6 2
+#define POS_BAR_VALUE_RSV_5 5
+#define POS_BAR_VALUE_VALUE 0
+#define LEN_BAR_VALUE_VALUE 5
+ OFF_ICON_STS_1,
+#define POS_ICON_STS1_DEGC 7
+#define POS_ICON_STS1_1KHZ 6
+#define POS_ICON_STS1_1MSPK 5
+#define POS_ICON_STS1_DCAC 3
+#define LEN_ICON_STS1_DCAC 2
+#define POS_ICON_STS1_AUTO 2
+#define POS_ICON_STS1_APO 1
+#define POS_ICON_STS1_BAT 0
+ OFF_ICON_STS_2,
+#define POS_ICON_STS2_DEGF 7
+#define POS_ICON_STS2_BT 6
+#define POS_ICON_STS2_UNK 5 /* TODO: What is this flag? 20mA loop current? */
+#define POS_ICON_STS2_REL 4
+#define POS_ICON_STS2_DBM 3
+#define POS_ICON_STS2_MINMAX 0 /* TODO: How to interpret the 3-bit field? */
+#define LEN_ICON_STS2_MINMAX 3
+ OFF_ICON_STS_3,
+#define POS_ICON_STS3_RSV_7 7
+#define POS_ICON_STS3_TEST 6
+#define POS_ICON_STS3_MEM 4 /* TODO: How to interpret the 2-bit field? */
+#define LEN_ICON_STS3_MEM 2
+#define POS_ICON_STS3_AHOLD 2
+#define LEN_ICON_STS3_AHOLD 2
+#define POS_ICON_STS3_AC 1
+#define POS_ICON_STS3_DC 0
+ OFF_CHECKSUM,
+ /* This is not an offset, but the packet's "byte count". */
+ PACKET_LAST_OFF,
+};
+
+STATIC_ASSERT(PACKET_LAST_OFF == EEV121GW_PACKET_SIZE,
+ "byte offsets vs packet length mismatch");
+
+enum mode_codes {
+ /* Modes for 'main' and 'sub' displays. */
+ MODE_LOW_Z = 0,
+ MODE_DC_V = 1,
+ MODE_AC_V = 2,
+ MODE_DC_MV = 3,
+ MODE_AC_MV = 4,
+ MODE_TEMP = 5,
+ MODE_FREQ = 6,
+ MODE_PERIOD = 7,
+ MODE_DUTY = 8,
+ MODE_RES = 9,
+ MODE_CONT = 10,
+ MODE_DIODE = 11,
+ MODE_CAP = 12,
+ MODE_AC_UVA = 13,
+ MODE_AC_MVA = 14,
+ MODE_AC_VA = 15,
+ MODE_AC_UA = 16,
+ MODE_DC_UA = 17,
+ MODE_AC_MA = 18,
+ MODE_DC_MA = 19,
+ MODE_AC_A = 20,
+ MODE_DC_A = 21,
+ MODE_DC_UVA = 22,
+ MODE_DC_MVA = 23,
+ MODE_DC_VA = 24,
+ /* More modes for 'sub' display. */
+ MODE_SUB_TEMPC = 100,
+ MODE_SUB_TEMPF = 105,
+ MODE_SUB_BATT = 110,
+ MODE_SUB_APO_ON = 120,
+ MODE_SUB_APO_OFF = 125,
+ MODE_SUB_YEAR = 130,
+ MODE_SUB_DATE = 135,
+ MODE_SUB_TIME = 140,
+ MODE_SUB_B_VOLT = 150,
+ MODE_SUB_LCD = 160,
+ MODE_SUB_CONT_PARM_0 = 170,
+ MODE_SUB_CONT_PARM_1 = 171,
+ MODE_SUB_CONT_PARM_2 = 172,
+ MODE_SUB_CONT_PARM_3 = 173,
+ MODE_SUB_DBM = 180,
+ MODE_SUB_IVAL = 190,
+};
+
+enum range_codes {
+ RANGE_0,
+ RANGE_1,
+ RANGE_2,
+ RANGE_3,
+ RANGE_4,
+ RANGE_5,
+ RANGE_6,
+ RANGE_MAX,
+};
+
+enum bar_range_codes {
+ BAR_RANGE_5,
+ BAR_RANGE_50,
+ BAR_RANGE_500,
+ BAR_RANGE_1000,
+};
+#define BAR_VALUE_MAX 25
+
+enum acdc_codes {
+ ACDC_NONE,
+ ACDC_DC,
+ ACDC_AC,
+ ACDC_ACDC,
+};
+
+SR_PRIV const char *eev121gw_channel_formats[EEV121GW_DISPLAY_COUNT] = {
+ /*
+ * TODO:
+ * The "main", "sub", "bar" names were taken from the packet
+ * description. Will users prefer "primary", "secondary", and
+ * "bargraph" names? Or even-length "pri", "sec", "bar" instead?
+ */
+ "main", "sub", "bar",
+};
+
+/*
+ * See page 69 in the 2018-09-24 manual for a table of modes and their
+ * respective ranges ("Calibration Reference Table"). This is the input
+ * to get the number of significant digits, and the decimal's position.
+ */
+struct mode_range_item {
+ const char *desc; /* Description, for diagnostics. */
+ int digits; /* Number of significant digits, see @ref sr_analog_encoding. */
+ int factor; /* Factor to convert the uint to a float. */
+};
+
+struct mode_range_items {
+ size_t range_count;
+ const struct mode_range_item ranges[RANGE_MAX];
+};
+
+static const struct mode_range_items mode_ranges_lowz = {
+ .range_count = 1,
+ .ranges = {
+ { .desc = "600.0V", .digits = 1, .factor = 1, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_volts = {
+ .range_count = 4,
+ .ranges = {
+ { .desc = "5.0000V", .digits = 4, .factor = 4, },
+ { .desc = "50.000V", .digits = 3, .factor = 3, },
+ { .desc = "500.00V", .digits = 2, .factor = 2, },
+ { .desc = "600.0V", .digits = 1, .factor = 1, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_millivolts = {
+ .range_count = 2,
+ .ranges = {
+ { .desc = "50.000mV", .digits = 6, .factor = 6, },
+ { .desc = "500.00mV", .digits = 5, .factor = 5, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_temp = {
+ .range_count = 1,
+ .ranges = {
+ { .desc = "-200.0C ~ 1350.0C", .digits = 1, .factor = 1, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_freq = {
+ .range_count = 5,
+ .ranges = {
+ { .desc = "99.999Hz", .digits = 3, .factor = 3, },
+ { .desc = "999.99Hz", .digits = 2, .factor = 2, },
+ { .desc = "9.9999kHz", .digits = 1, .factor = 1, },
+ { .desc = "99.999kHz", .digits = 0, .factor = 0, },
+ { .desc = "999.99kHz", .digits = -1, .factor = -1, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_period = {
+ .range_count = 3,
+ .ranges = {
+ { .desc = "9.9999ms", .digits = 7, .factor = 7, },
+ { .desc = "99.999ms", .digits = 6, .factor = 6, },
+ { .desc = "999.99ms", .digits = 5, .factor = 5, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_duty = {
+ .range_count = 1,
+ .ranges = {
+ { .desc = "99.9%", .digits = 1, .factor = 1, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_res = {
+ .range_count = 7,
+ .ranges = {
+ { .desc = "50.000R", .digits = 3, .factor = 3, },
+ { .desc = "500.00R", .digits = 2, .factor = 2, },
+ { .desc = "5.0000k", .digits = 1, .factor = 1, },
+ { .desc = "50.000k", .digits = 0, .factor = 0, },
+ { .desc = "500.00k", .digits = -1, .factor = -1, },
+ { .desc = "5.0000M", .digits = -2, .factor = -2, },
+ { .desc = "50.000M", .digits = -3, .factor = -3, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_cont = {
+ .range_count = 1,
+ .ranges = {
+ { .desc = "500.00R", .digits = 2, .factor = 2, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_diode = {
+ .range_count = 2,
+ .ranges = {
+ { .desc = "3.0000V", .digits = 4, .factor = 4, },
+ { .desc = "15.000V", .digits = 3, .factor = 3, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_cap = {
+ .range_count = 6,
+ .ranges = {
+ { .desc = "10.00n", .digits = 11, .factor = 11, },
+ { .desc = "100.0n", .digits = 10, .factor = 10, },
+ { .desc = "1.000u", .digits = 9, .factor = 9, },
+ { .desc = "10.00u", .digits = 8, .factor = 8, },
+ { .desc = "100.0u", .digits = 7, .factor = 7, },
+ { .desc = "10.00m", .digits = 5, .factor = 5, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_pow_va = {
+ .range_count = 4,
+ .ranges = {
+ { .desc = "2500.0mVA", .digits = 4, .factor = 4, },
+ { .desc = "25000.mVA", .digits = 3, .factor = 3, },
+ { .desc = "25.000VA", .digits = 3, .factor = 3, },
+ { .desc = "500.00VA", .digits = 2, .factor = 2, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_pow_mva = {
+ .range_count = 4,
+ .ranges = {
+ { .desc = "25.000mVA", .digits = 6, .factor = 6, },
+ { .desc = "250.00mVA", .digits = 5, .factor = 5, },
+ { .desc = "250.00mVA", .digits = 5, .factor = 5, },
+ { .desc = "2500.0mVA", .digits = 4, .factor = 4, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_pow_uva = {
+ .range_count = 4,
+ .ranges = {
+ { .desc = "250.00uVA", .digits = 8, .factor = 8, },
+ { .desc = "2500.0uVA", .digits = 7, .factor = 7, },
+ { .desc = "2500.0uVA", .digits = 7, .factor = 7, },
+ { .desc = "25000.uVA", .digits = 6, .factor = 6, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_curr_a = {
+ .range_count = 3,
+ .ranges = {
+ { .desc = "500.00mA", .digits = 5, .factor = 5, },
+ { .desc = "5.0000A", .digits = 4, .factor = 4, },
+ { .desc = "10.000A", .digits = 3, .factor = 3, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_curr_ma = {
+ .range_count = 2,
+ .ranges = {
+ { .desc = "5.0000mA", .digits = 7, .factor = 7, },
+ { .desc = "50.000mA", .digits = 6, .factor = 6, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_curr_ua = {
+ .range_count = 2,
+ .ranges = {
+ { .desc = "50.000uA", .digits = 9, .factor = 9, },
+ { .desc = "500.00uA", .digits = 8, .factor = 8, },
+ },
+};
+
+static const struct mode_range_items *mode_ranges_main[] = {
+ [MODE_LOW_Z] = &mode_ranges_lowz,
+ [MODE_DC_V] = &mode_ranges_volts,
+ [MODE_AC_V] = &mode_ranges_volts,
+ [MODE_DC_MV] = &mode_ranges_millivolts,
+ [MODE_AC_MV] = &mode_ranges_millivolts,
+ [MODE_TEMP] = &mode_ranges_temp,
+ [MODE_FREQ] = &mode_ranges_freq,
+ [MODE_PERIOD] = &mode_ranges_period,
+ [MODE_DUTY] = &mode_ranges_duty,
+ [MODE_RES] = &mode_ranges_res,
+ [MODE_CONT] = &mode_ranges_cont,
+ [MODE_DIODE] = &mode_ranges_diode,
+ [MODE_CAP] = &mode_ranges_cap,
+ [MODE_DC_VA] = &mode_ranges_pow_va,
+ [MODE_AC_VA] = &mode_ranges_pow_va,
+ [MODE_DC_MVA] = &mode_ranges_pow_mva,
+ [MODE_AC_MVA] = &mode_ranges_pow_mva,
+ [MODE_DC_UVA] = &mode_ranges_pow_uva,
+ [MODE_AC_UVA] = &mode_ranges_pow_uva,
+ [MODE_DC_A] = &mode_ranges_curr_a,
+ [MODE_AC_A] = &mode_ranges_curr_a,
+ [MODE_DC_MA] = &mode_ranges_curr_ma,
+ [MODE_AC_MA] = &mode_ranges_curr_ma,
+ [MODE_DC_UA] = &mode_ranges_curr_ua,
+ [MODE_AC_UA] = &mode_ranges_curr_ua,
+};
+
+/*
+ * The secondary display encodes SI units / scaling differently from the
+ * main display, and fewer ranges are available. So we share logic between
+ * displays for scaling, but have to keep separate tables for the displays.
+ */
+
+static const struct mode_range_items mode_ranges_temp_sub = {
+ .range_count = 2,
+ .ranges = {
+ [1] = { .desc = "sub 100.0C", .digits = 1, .factor = 1, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_freq_sub = {
+ .range_count = 4,
+ .ranges = {
+ [1] = { .desc = "999.9Hz", .digits = 1, .factor = 1, },
+ [2] = { .desc = "99.99Hz", .digits = 2, .factor = 2, },
+ [3] = { .desc = "9.999kHz", .digits = 3, .factor = 3, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_batt_sub = {
+ .range_count = 2,
+ .ranges = {
+ [1] = { .desc = "sub 10.0V", .digits = 1, .factor = 1, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_gain_sub = {
+ .range_count = 4,
+ .ranges = {
+ [1] = { .desc = "dbm 5000.0dBm", .digits = 1, .factor = 1, },
+ [2] = { .desc = "dbm 500.00dBm", .digits = 2, .factor = 2, },
+ [3] = { .desc = "dbm 50.000dBm", .digits = 3, .factor = 3, },
+ },
+};
+
+static const struct mode_range_items mode_ranges_diode_sub = {
+ .range_count = 1,
+ .ranges = {
+ [0] = { .desc = "diode 15.0V", .digits = 0, .factor = 0, },
+ },
+};
+
+/* TODO: Complete the list of ranges. Only tested with low voltages so far. */
+static const struct mode_range_items mode_ranges_volts_sub = {
+ .range_count = 5,
+ .ranges = {
+ [4] = { .desc = "5.0000V", .digits = 4, .factor = 4, },
+ },
+};
+
+/* TODO: Complete the list of ranges. Only tested with low voltages so far. */
+static const struct mode_range_items mode_ranges_mamps_sub = {
+ .range_count = 3,
+ .ranges = {
+ [2] = { .desc = "500.00mA", .digits = 5, .factor = 5, },
+ },
+};
+
+static const struct mode_range_items *mode_ranges_sub[] = {
+ [MODE_DC_V] = &mode_ranges_volts_sub,
+ [MODE_AC_V] = &mode_ranges_volts_sub,
+ [MODE_DC_A] = &mode_ranges_mamps_sub,
+ [MODE_AC_A] = &mode_ranges_mamps_sub,
+ [MODE_FREQ] = &mode_ranges_freq_sub,
+ [MODE_DIODE] = &mode_ranges_diode_sub,
+ [MODE_SUB_TEMPC] = &mode_ranges_temp_sub,
+ [MODE_SUB_TEMPF] = &mode_ranges_temp_sub,
+ [MODE_SUB_BATT] = &mode_ranges_batt_sub,
+ [MODE_SUB_DBM] = &mode_ranges_gain_sub,
+};
+
+static const struct mode_range_item *mode_range_get_scale(
+ enum eev121gw_display display,
+ enum mode_codes mode, enum range_codes range)
+{
+ const struct mode_range_items *items;
+ const struct mode_range_item *item;
+
+ if (display == EEV121GW_DISPLAY_MAIN) {
+ if (mode >= ARRAY_SIZE(mode_ranges_main))
+ return NULL;
+ items = mode_ranges_main[mode];
+ if (!items || !items->range_count)
+ return NULL;
+ if (range >= items->range_count)
+ return NULL;
+ item = &items->ranges[range];
+ return item;
+ }
+ if (display == EEV121GW_DISPLAY_SUB) {
+ if (mode >= ARRAY_SIZE(mode_ranges_sub))
+ return NULL;
+ items = mode_ranges_sub[mode];
+ if (!items || !items->range_count)
+ return NULL;
+ if (range >= items->range_count)
+ return NULL;
+ item = &items->ranges[range];
+ if (!item->desc || !*item->desc)
+ return NULL;
+ return item;
+ }
+
+ return NULL;
+}
+
+SR_PRIV gboolean sr_eev121gw_packet_valid(const uint8_t *buf)
+{
+ uint8_t csum;
+ size_t idx;
+
+ /* Leading byte, literal / fixed value. */
+ if (buf[OFF_START_CMD] != VAL_START_CMD)
+ return FALSE;
+
+ /* Check some always-zero bits in reserved locations. */
+ if (FIELD_NB(buf[OFF_MAIN_MODE], MAIN_MODE_RSV_5))
+ return FALSE;
+ if (FIELD_NB(buf[OFF_SUB_RANGE], SUB_RANGE_RSV_3))
+ return FALSE;
+ if (FIELD_NL(buf[OFF_BAR_STATUS], BAR_STATUS_RSV_5))
+ return FALSE;
+ if (FIELD_NL(buf[OFF_BAR_VALUE], BAR_VALUE_RSV_6))
+ return FALSE;
+ /* See TODO for bit 5 of "bar value" not always being 0. */
+ if (0 && FIELD_NB(buf[OFF_BAR_VALUE], BAR_VALUE_RSV_5))
+ return FALSE;
+ if (FIELD_NB(buf[OFF_ICON_STS_3], ICON_STS3_RSV_7))
+ return FALSE;
+
+ /* Checksum, XOR over all previous bytes. */
+ csum = 0x00;
+ for (idx = OFF_START_CMD; idx < OFF_CHECKSUM; idx++)
+ csum ^= buf[idx];
+ if (csum != buf[OFF_CHECKSUM]) {
+ /* Non-critical condition, almost expected to see invalid data. */
+ sr_spew("Packet csum: want %02x, got %02x.", csum, buf[OFF_CHECKSUM]);
+ return FALSE;
+ }
+
+ sr_spew("Packet valid.");
+
+ return TRUE;
+}
+
+/**
+ * Parse a protocol packet.
+ *
+ * @param[in] buf Buffer containing the protocol packet. Must not be NULL.
+ * @param[out] floatval Pointer to a float variable. That variable will be
+ * modified in-place depending on the protocol packet.
+ * Must not be NULL.
+ * @param[out] analog Pointer to a struct sr_datafeed_analog. The struct will
+ * be filled with data according to the protocol packet.
+ * Must not be NULL.
+ * @param[out] info Pointer to a struct eevblog_121gw_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_eev121gw_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ struct eev121gw_info *info_local;
+ enum eev121gw_display display;
+ const char *channel_name;
+ uint32_t raw_serial;
+ uint8_t raw_main_mode, raw_main_range;
+ uint16_t raw_main_value;
+ uint8_t raw_sub_mode, raw_sub_range;
+ uint16_t raw_sub_value;
+ uint8_t raw_bar_status, raw_bar_value;
+ uint8_t raw_icon_stat_1, raw_icon_stat_2, raw_icon_stat_3;
+ uint32_t uint_value;
+ enum mode_codes main_mode;
+ enum range_codes main_range;
+ enum mode_codes sub_mode;
+ enum range_codes sub_range;
+ const struct mode_range_item *scale;
+ gboolean is_dc, is_sign, use_sign;
+ gboolean is_k;
+ unsigned int cont_code;
+
+ info_local = info;
+ display = info_local->ch_idx;
+ channel_name = eev121gw_channel_formats[display];
+ memset(info_local, 0, sizeof(*info_local));
+ *floatval = 0.0f;
+
+ /*
+ * Get the packet's bytes into native C language typed variables.
+ * This keeps byte position references out of logic/calculations.
+ * The start command and CRC were verified before we get here.
+ */
+ raw_serial = RB32(&buf[OFF_SERIAL_3]);
+ raw_main_mode = R8(&buf[OFF_MAIN_MODE]);
+ raw_main_range = R8(&buf[OFF_MAIN_RANGE]);
+ raw_main_value = RB16(&buf[OFF_MAIN_VAL_H]);
+ raw_sub_mode = R8(&buf[OFF_SUB_MODE]);
+ raw_sub_range = R8(&buf[OFF_SUB_RANGE]);
+ raw_sub_value = RB16(&buf[OFF_SUB_VAL_H]);
+ raw_bar_status = R8(&buf[OFF_BAR_STATUS]);
+ raw_bar_value = R8(&buf[OFF_BAR_VALUE]);
+ raw_icon_stat_1 = R8(&buf[OFF_ICON_STS_1]);
+ raw_icon_stat_2 = R8(&buf[OFF_ICON_STS_2]);
+ raw_icon_stat_3 = R8(&buf[OFF_ICON_STS_3]);
+
+ /*
+ * Packets contain a YEAR-MONTH date spec. It's uncertain how
+ * this data relates to the device's production or the firmware
+ * version. It certainly is not the current date either. Only
+ * optionally log this information, it's consistent across all
+ * packets (won't change within a session), and will be noisy if
+ * always enabled.
+ *
+ * Packets also contain a user adjustable device identification
+ * number (see the SETUP options). This is motivated by support
+ * for multiple devices, but won't change here within a session.
+ * The user chose to communicate to one specific device when the
+ * session started, by means of the conn= spec.
+ *
+ * It was suggested that this 'serial' field might be used as an
+ * additional means to check for a packet's validity (or absence
+ * of communication errors). This remains as an option for future
+ * improvement.
+ */
+ if (0) {
+ unsigned int ser_year, ser_mon, ser_nr;
+
+ ser_year = FIELD_NL(raw_serial, SERIAL_YEAR);
+ ser_mon = FIELD_NL(raw_serial, SERIAL_MONTH);
+ ser_nr = FIELD_NL(raw_serial, SERIAL_NUMBER);
+ sr_spew("Packet: Y-M %x-%x, nr %x.", ser_year, ser_mon, ser_nr);
+ }
+
+ switch (display) {
+
+ case EEV121GW_DISPLAY_MAIN:
+ /*
+ * Get those fields which correspond to the main display.
+ * The value's mantissa has 18 bits. The sign is separate
+ * (and is not universally applicable, mode needs to get
+ * inspected). The range's scaling and precision also
+ * depend on the mode.
+ */
+ main_mode = FIELD_NL(raw_main_mode, MAIN_MODE_MODE);
+ main_range = FIELD_NL(raw_main_range, MAIN_RANGE_RANGE);
+ scale = mode_range_get_scale(EEV121GW_DISPLAY_MAIN,
+ main_mode, main_range);
+ if (!scale)
+ return SR_ERR_NA;
+ info_local->factor = scale->factor;
+ info_local->digits = scale->digits;
+
+ uint_value = raw_main_value;
+ uint_value |= FIELD_NL(raw_main_mode, MAIN_MODE_VAL_U) << 16;
+ info_local->uint_value = uint_value;
+ info_local->is_ofl = FIELD_NB(raw_main_range, MAIN_RANGE_OFL);
+
+ switch (main_mode) {
+ case MODE_LOW_Z:
+ is_dc = FALSE;
+ if (FIELD_NB(raw_icon_stat_3, ICON_STS3_DC))
+ is_dc = TRUE;
+ if (FIELD_NB(raw_icon_stat_3, ICON_STS3_AC))
+ is_dc = FALSE;
+ use_sign = is_dc;
+ break;
+ case MODE_DC_V:
+ case MODE_DC_MV:
+ case MODE_TEMP:
+ case MODE_DC_UVA:
+ case MODE_DC_MVA:
+ case MODE_DC_VA:
+ case MODE_DC_UA:
+ case MODE_DC_MA:
+ case MODE_DC_A:
+ use_sign = TRUE;
+ break;
+ default:
+ use_sign = FALSE;
+ break;
+ }
+ if (use_sign) {
+ is_sign = FIELD_NB(raw_main_range, MAIN_RANGE_SIGN);
+ info_local->is_neg = is_sign;
+ }
+
+ switch (main_mode) {
+ case MODE_LOW_Z:
+ info_local->is_voltage = TRUE;
+ /* TODO: Need to determine AC/DC here? */
+ info_local->is_volt = TRUE;
+ info_local->is_low_pass = TRUE;
+ break;
+ case MODE_DC_V:
+ info_local->is_voltage = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_volt = TRUE;
+ break;
+ case MODE_AC_V:
+ info_local->is_voltage = TRUE;
+ info_local->is_volt = TRUE;
+ info_local->is_ac = TRUE;
+ break;
+ case MODE_DC_MV:
+ info_local->is_voltage = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_volt = TRUE;
+ break;
+ case MODE_AC_MV:
+ info_local->is_voltage = TRUE;
+ info_local->is_volt = TRUE;
+ info_local->is_ac = TRUE;
+ break;
+ case MODE_TEMP:
+ info_local->is_temperature = TRUE;
+ if (FIELD_NB(raw_main_range, MAIN_RANGE_DEGC))
+ info_local->is_celsius = TRUE;
+ if (FIELD_NB(raw_main_range, MAIN_RANGE_DEGF))
+ info_local->is_fahrenheit = TRUE;
+ break;
+ case MODE_FREQ:
+ info_local->is_frequency = TRUE;
+ info_local->is_hertz = TRUE;
+ break;
+ case MODE_PERIOD:
+ info_local->is_period = TRUE;
+ info_local->is_seconds = TRUE;
+ break;
+ case MODE_DUTY:
+ info_local->is_duty_cycle = TRUE;
+ info_local->is_percent = TRUE;
+ break;
+ case MODE_RES:
+ info_local->is_resistance = TRUE;
+ info_local->is_ohm = TRUE;
+ break;
+ case MODE_CONT:
+ info_local->is_continuity = TRUE;
+ info_local->is_ohm = TRUE;
+ /*
+ * In continuity mode the packet provides the
+ * resistance in ohms (500R range), but seems to
+ * _not_ carry the "boolean" open/closed state
+ * which controls the beeper. Users can select
+ * whether to trigger at 30R or 300R, and whether
+ * to trigger on values below (continuity) or
+ * above (cable break) the limit, but we cannot
+ * tell what the currently used setting is. So
+ * we neither get the beeper's state, nor can we
+ * derive it from other information.
+ */
+ break;
+ case MODE_DIODE:
+ info_local->is_diode = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_volt = TRUE;
+ break;
+ case MODE_CAP:
+ info_local->is_capacitance = TRUE;
+ info_local->is_farad = TRUE;
+ break;
+ case MODE_AC_UVA:
+ info_local->is_power = TRUE;
+ info_local->is_ac = TRUE;
+ info_local->is_volt_ampere = TRUE;
+ break;
+ case MODE_AC_MVA:
+ info_local->is_power = TRUE;
+ info_local->is_ac = TRUE;
+ info_local->is_volt_ampere = TRUE;
+ break;
+ case MODE_AC_VA:
+ info_local->is_power = TRUE;
+ info_local->is_ac = TRUE;
+ info_local->is_volt_ampere = TRUE;
+ break;
+ case MODE_AC_UA:
+ info_local->is_current = TRUE;
+ info_local->is_ac = TRUE;
+ info_local->is_ampere = TRUE;
+ break;
+ case MODE_DC_UA:
+ info_local->is_current = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_ampere = TRUE;
+ break;
+ case MODE_AC_MA:
+ info_local->is_current = TRUE;
+ info_local->is_ac = TRUE;
+ info_local->is_ampere = TRUE;
+ break;
+ case MODE_DC_MA:
+ info_local->is_current = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_ampere = TRUE;
+ break;
+ case MODE_AC_A:
+ info_local->is_current = TRUE;
+ info_local->is_ac = TRUE;
+ info_local->is_ampere = TRUE;
+ break;
+ case MODE_DC_A:
+ info_local->is_current = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_ampere = TRUE;
+ break;
+ case MODE_DC_UVA:
+ info_local->is_power = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_volt_ampere = TRUE;
+ break;
+ case MODE_DC_MVA:
+ info_local->is_power = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_volt_ampere = TRUE;
+ break;
+ case MODE_DC_VA:
+ info_local->is_power = TRUE;
+ info_local->is_dc = TRUE;
+ info_local->is_volt_ampere = TRUE;
+ break;
+ /* Modes 100-199 only apply to the secondary display. */
+ default:
+ return SR_ERR_NA;
+ }
+
+ /*
+ * Inspect the "icons" section, since it is associated
+ * with the primary display and global device state.
+ */
+ if (FIELD_NB(raw_icon_stat_1, ICON_STS1_1KHZ))
+ info_local->is_low_pass = TRUE;
+ if (FIELD_NB(raw_icon_stat_1, ICON_STS1_1MSPK))
+ info_local->is_1ms_peak = TRUE;
+ switch (FIELD_NL(raw_icon_stat_1, ICON_STS1_DCAC)) {
+ case ACDC_ACDC:
+ info_local->is_ac = TRUE;
+ info_local->is_dc = TRUE;
+ break;
+ case ACDC_AC:
+ info_local->is_ac = TRUE;
+ break;
+ case ACDC_DC:
+ info_local->is_dc = TRUE;
+ break;
+ case ACDC_NONE:
+ /* EMPTY */
+ break;
+ }
+ if (FIELD_NB(raw_icon_stat_1, ICON_STS1_AUTO))
+ info_local->is_auto_range = TRUE;
+ if (FIELD_NB(raw_icon_stat_1, ICON_STS1_APO))
+ info_local->is_auto_poweroff = TRUE;
+ if (FIELD_NB(raw_icon_stat_1, ICON_STS1_BAT))
+ info_local->is_low_batt = TRUE;
+ if (FIELD_NB(raw_icon_stat_2, ICON_STS2_BT))
+ info_local->is_bt = TRUE;
+ /* TODO: Is this the "20mA loop current" flag? */
+ if (FIELD_NB(raw_icon_stat_2, ICON_STS2_UNK))
+ info_local->is_loop_current = TRUE;
+ if (FIELD_NB(raw_icon_stat_2, ICON_STS2_REL))
+ info_local->is_rel = TRUE;
+ /* dBm only applies to secondary display, not main. */
+ switch (FIELD_NL(raw_icon_stat_2, ICON_STS2_MINMAX)) {
+ /* TODO: Do inspect the min/max/avg flags. */
+ default:
+ /* EMPTY */
+ break;
+ }
+ if (FIELD_NB(raw_icon_stat_3, ICON_STS3_TEST))
+ info_local->is_test = TRUE;
+ /* TODO: How to interpret the 2-bit MEM field? */
+ if (FIELD_NL(raw_icon_stat_3, ICON_STS3_MEM))
+ info_local->is_mem = TRUE;
+ if (FIELD_NL(raw_icon_stat_3, ICON_STS3_AHOLD))
+ info_local->is_hold = TRUE;
+ /* TODO: Are these for the secondary display? See status-2 ACDC. */
+ if (FIELD_NB(raw_icon_stat_3, ICON_STS3_AC))
+ info_local->is_ac = TRUE;
+ if (FIELD_NB(raw_icon_stat_3, ICON_STS3_DC))
+ info_local->is_dc = TRUE;
+
+ sr_spew("Disp '%s', value: %lu (ov %d, neg %d), mode %d, range %d.",
+ channel_name,
+ (unsigned long)info_local->uint_value,
+ info_local->is_ofl, info_local->is_neg,
+ (int)main_mode, (int)main_range);
+ /* Advance to the number and units conversion below. */
+ break;
+
+ case EEV121GW_DISPLAY_SUB:
+ /*
+ * Get those fields which correspond to the secondary
+ * display. The value's mantissa has 16 bits. The sign
+ * is separate is only applies to some of the modes.
+ * Scaling and precision also depend on the mode. The
+ * interpretation of the secondary display is different
+ * from the main display: The 'range' is not an index
+ * into ranges, instead it's the decimal's position.
+ * Yet more scaling depends on the mode, to complicate
+ * matters. The secondary display uses modes 100-199,
+ * and some of the 0-24 modes as well.
+ */
+ sub_mode = FIELD_NL(raw_sub_mode, SUB_MODE_MODE);
+ sub_range = FIELD_NL(raw_sub_range, SUB_RANGE_POINT);
+ scale = mode_range_get_scale(EEV121GW_DISPLAY_SUB,
+ sub_mode, sub_range);
+ if (!scale)
+ return SR_ERR_NA;
+ info_local->factor = scale->factor;
+ info_local->digits = scale->digits;
+
+ info_local->uint_value = raw_sub_value;
+ info_local->is_ofl = FIELD_NB(raw_sub_range, SUB_RANGE_OFL);
+
+ switch (sub_mode) {
+ case MODE_DC_V:
+ case MODE_AC_V:
+ case MODE_DC_A:
+ case MODE_AC_A:
+ case MODE_SUB_TEMPC:
+ case MODE_SUB_TEMPF:
+ case MODE_SUB_B_VOLT:
+ case MODE_SUB_DBM:
+ use_sign = TRUE;
+ break;
+ default:
+ use_sign = FALSE;
+ break;
+ }
+ if (use_sign) {
+ is_sign = FIELD_NB(raw_sub_range, SUB_RANGE_SIGN);
+ info_local->is_neg = is_sign;
+ }
+ is_k = FIELD_NB(raw_sub_range, SUB_RANGE_K);
+
+ /*
+ * TODO: Re-check the power mode display as more data becomes
+ * available.
+ *
+ * The interpretation of the secondary display in power (VA)
+ * modes is uncertain. The mode suggests A or uA units but the
+ * value is supposed to be mA without a reliable condition
+ * for us to check...
+ *
+ * f2 17 84 21 21 18 02 00 00 01 04 00 0b 00 00 0a 40 00 3f
+ * f2 17 84 21 21 18 02 00 00 15 03 00 00 00 00 0a 40 00 27
+ * DC VA DC V / DC A
+ * 25.000VA dot 4 / dot 3
+ *
+ * f2 17 84 21 21 18 00 00 26 01 04 4c 57 00 00 0e 40 00 0f
+ * f2 17 84 21 21 18 00 00 26 15 02 00 c7 00 00 0e 40 00 c1
+ * 3.8mVA DC 1.9543V
+ * 1.98mA (!) DC A + dot 2 -> milli(!) amps?
+ *
+ * f2 17 84 21 21 17 00 07 85 01 04 4c 5a 00 00 0e 40 00 a9
+ * f2 17 84 21 21 17 00 07 85 13 04 26 7b 00 00 0e 40 00 f0
+ * 1.925mVA DC 1.9546V
+ * 0.9852mA
+ *
+ * f2 17 84 21 21 16 02 11 e0 01 04 26 39 00 02 0e 40 00 d2
+ * f2 17 84 21 21 16 02 11 e0 11 04 12 44 00 02 0e 40 00 8b
+ * 457.6uVA DC 0.9785V
+ * 0.4676mA (!) DC uA + dot 4 -> milli(!) amps?
+ */
+
+ switch (sub_mode) {
+ case MODE_DC_V:
+ info_local->is_voltage = TRUE;
+ info_local->is_volt = TRUE;
+ break;
+ case MODE_DC_A:
+ info_local->is_current = TRUE;
+ info_local->is_ampere = TRUE;
+ break;
+ case MODE_FREQ:
+ info_local->is_frequency = TRUE;
+ info_local->is_hertz = TRUE;
+ if (is_k) {
+ info_local->factor -= 3;
+ info_local->digits -= 3;
+ }
+ info_local->is_ofl = FALSE;
+ break;
+ case MODE_SUB_TEMPC:
+ info_local->is_temperature = TRUE;
+ info_local->is_celsius = TRUE;
+ break;
+ case MODE_SUB_TEMPF:
+ info_local->is_temperature = TRUE;
+ info_local->is_fahrenheit = TRUE;
+ break;
+ case MODE_SUB_BATT:
+ /* TODO: How to communicate it's the *battery* voltage? */
+ info_local->is_voltage = TRUE;
+ info_local->is_volt = TRUE;
+ break;
+ case MODE_SUB_DBM:
+ info_local->is_gain = TRUE;
+ info_local->is_dbm = TRUE;
+ break;
+ case MODE_SUB_CONT_PARM_0:
+ case MODE_SUB_CONT_PARM_1:
+ case MODE_SUB_CONT_PARM_2:
+ case MODE_SUB_CONT_PARM_3:
+ /*
+ * These "continuity parameters" are special. The
+ * least significant bits represent the options:
+ *
+ * 0xaa = 170 => down 30
+ * 0xab = 171 => up 30
+ * 0xac = 172 => down 300
+ * 0xad = 173 => up 300
+ *
+ * bit 0 value 0 -> close (cont)
+ * bit 0 value 1 -> open (break)
+ * bit 1 value 0 -> 30R limit
+ * bit 1 value 1 -> 300R limit
+ *
+ * This "display value" is only seen during setup
+ * but not during regular operation of continuity
+ * mode. :( In theory we could somehow pass the
+ * 30/300 ohm limit to sigrok, but that'd be of
+ * somewhat limited use.
+ */
+ cont_code = sub_mode - MODE_SUB_CONT_PARM_0;
+ info_local->is_resistance = TRUE;
+ info_local->is_ohm = TRUE;
+ info_local->uint_value = (cont_code & 0x02) ? 300 : 30;
+ info_local->is_neg = FALSE;
+ info_local->is_ofl = FALSE;
+ info_local->factor = 0;
+ info_local->digits = 0;
+ break;
+ case MODE_DIODE:
+ /* Displays configured diode test voltage. */
+ info_local->is_voltage = TRUE;
+ info_local->is_volt = TRUE;
+ break;
+
+ /* Reflecting these to users seems pointless, ignore them. */
+ case MODE_SUB_APO_ON:
+ case MODE_SUB_APO_OFF:
+ case MODE_SUB_LCD:
+ case MODE_SUB_YEAR:
+ case MODE_SUB_DATE:
+ case MODE_SUB_TIME:
+ return SR_ERR_NA;
+
+ /* Unknown / unsupported sub display mode. */
+ default:
+ return SR_ERR_NA;
+ }
+
+ sr_spew("disp '%s', value: %lu (ov %d, neg %d), mode %d, range %d",
+ channel_name,
+ (unsigned long)info_local->uint_value,
+ info_local->is_ofl, info_local->is_neg,
+ (int)sub_mode, (int)sub_range);
+ /* Advance to the number and units conversion below. */
+ break;
+
+ case EEV121GW_DISPLAY_BAR:
+ /*
+ * Get those fields which correspond to the bargraph.
+ * There are 26 segments (ticks 0-25), several ranges
+ * apply (up to 5, or up to 10, several decades). The
+ * bargraph does not apply to all modes and ranges,
+ * hence there is a "use" flag (negative logic, blank
+ * signal). Bit 5 was also found to have undocumented
+ * values, we refuse to use the bargraph value then.
+ */
+ if (FIELD_NB(raw_bar_status, BAR_STATUS_USE))
+ return SR_ERR_NA;
+ if (FIELD_NB(raw_bar_value, BAR_VALUE_RSV_5))
+ return SR_ERR_NA;
+ uint_value = FIELD_NL(raw_bar_value, BAR_VALUE_VALUE);
+ if (uint_value > BAR_VALUE_MAX)
+ uint_value = BAR_VALUE_MAX;
+ info_local->is_neg = FIELD_NB(raw_bar_status, BAR_STATUS_SIGN);
+ switch (FIELD_NL(raw_bar_status, BAR_STATUS_1K_500)) {
+ case BAR_RANGE_5:
+ /* Full range 5.0, in steps of 0.2 each. */
+ uint_value *= 5000 / BAR_VALUE_MAX;
+ info_local->factor = 3;
+ info_local->digits = 1;
+ break;
+ case BAR_RANGE_50:
+ /* Full range 50, in steps of 2 each. */
+ uint_value *= 50 / BAR_VALUE_MAX;
+ info_local->factor = 0;
+ info_local->digits = 0;
+ break;
+ case BAR_RANGE_500:
+ /* Full range 500, in steps of 20 each. */
+ uint_value *= 500 / BAR_VALUE_MAX;
+ info_local->factor = 0;
+ info_local->digits = -1;
+ break;
+ case BAR_RANGE_1000:
+ /* Full range 1000, in steps of 40 each. */
+ uint_value *= 1000 / BAR_VALUE_MAX;
+ info_local->factor = 0;
+ info_local->digits = -1;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ info_local->uint_value = uint_value;
+ info_local->is_unitless = TRUE;
+ sr_spew("Disp '%s', value: %u.", channel_name,
+ (unsigned int)info_local->uint_value);
+ /* Advance to the number and units conversion below. */
+ break;
+
+ default:
+ /* Unknown display, programmer's error, ShouldNotHappen(TM). */
+ sr_err("Disp '-?-'.");
+ return SR_ERR_ARG;
+ }
+
+ /*
+ * Convert the unsigned mantissa and its modifiers to a float
+ * analog value, including scale and quantity. Do the conversion
+ * first, and optionally override the result with 'inf' later.
+ * Apply the sign last so that +inf and -inf are supported.
+ */
+ *floatval = info_local->uint_value;
+ if (info_local->factor)
+ *floatval *= powf(10, -info_local->factor);
+ if (info_local->is_ofl)
+ *floatval = INFINITY;
+ if (info_local->is_neg)
+ *floatval = -*floatval;
+
+ analog->encoding->digits = info_local->digits;
+ analog->spec->spec_digits = info_local->digits;
+
+ /*
+ * Communicate the measured quantity.
+ */
+ /* Determine the quantity itself. */
+ if (info_local->is_voltage)
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ if (info_local->is_current)
+ analog->meaning->mq = SR_MQ_CURRENT;
+ if (info_local->is_power)
+ analog->meaning->mq = SR_MQ_POWER;
+ if (info_local->is_gain)
+ analog->meaning->mq = SR_MQ_GAIN;
+ if (info_local->is_resistance)
+ analog->meaning->mq = SR_MQ_RESISTANCE;
+ if (info_local->is_capacitance)
+ analog->meaning->mq = SR_MQ_CAPACITANCE;
+ if (info_local->is_diode)
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ if (info_local->is_temperature)
+ analog->meaning->mq = SR_MQ_TEMPERATURE;
+ if (info_local->is_continuity)
+ analog->meaning->mq = SR_MQ_CONTINUITY;
+ if (info_local->is_frequency)
+ analog->meaning->mq = SR_MQ_FREQUENCY;
+ if (info_local->is_period)
+ analog->meaning->mq = SR_MQ_TIME;
+ if (info_local->is_duty_cycle)
+ analog->meaning->mq = SR_MQ_DUTY_CYCLE;
+ if (info_local->is_unitless)
+ analog->meaning->mq = SR_MQ_COUNT;
+ /* Add AC / DC / DC+AC flags. */
+ if (info_local->is_ac)
+ analog->meaning->mqflags |= SR_MQFLAG_AC;
+ if (info_local->is_dc)
+ analog->meaning->mqflags |= SR_MQFLAG_DC;
+ /* Specify units. */
+ if (info_local->is_ampere)
+ analog->meaning->unit = SR_UNIT_AMPERE;
+ if (info_local->is_volt)
+ analog->meaning->unit = SR_UNIT_VOLT;
+ if (info_local->is_volt_ampere)
+ analog->meaning->unit = SR_UNIT_VOLT_AMPERE;
+ if (info_local->is_dbm)
+ analog->meaning->unit = SR_UNIT_DECIBEL_VOLT;
+ if (info_local->is_ohm)
+ analog->meaning->unit = SR_UNIT_OHM;
+ if (info_local->is_farad)
+ analog->meaning->unit = SR_UNIT_FARAD;
+ if (info_local->is_celsius)
+ analog->meaning->unit = SR_UNIT_CELSIUS;
+ if (info_local->is_fahrenheit)
+ analog->meaning->unit = SR_UNIT_FAHRENHEIT;
+ if (info_local->is_hertz)
+ analog->meaning->unit = SR_UNIT_HERTZ;
+ if (info_local->is_seconds)
+ analog->meaning->unit = SR_UNIT_SECOND;
+ if (info_local->is_percent)
+ analog->meaning->unit = SR_UNIT_PERCENTAGE;
+ if (info_local->is_loop_current)
+ analog->meaning->unit = SR_UNIT_PERCENTAGE;
+ if (info_local->is_unitless)
+ analog->meaning->unit = SR_UNIT_UNITLESS;
+ if (info_local->is_logic)
+ analog->meaning->unit = SR_UNIT_UNITLESS;
+ /* Add other indicator flags. */
+ if (info_local->is_diode) {
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DC;
+ }
+ if (info_local->is_min)
+ analog->meaning->mqflags |= SR_MQFLAG_MIN;
+ if (info_local->is_max)
+ analog->meaning->mqflags |= SR_MQFLAG_MAX;
+ if (info_local->is_avg)
+ analog->meaning->mqflags |= SR_MQFLAG_AVG;
+ /* TODO: How to communicate info_local->is_1ms_peak? */
+ if (info_local->is_rel)
+ analog->meaning->mqflags |= SR_MQFLAG_RELATIVE;
+ if (info_local->is_hold)
+ analog->meaning->mqflags |= SR_MQFLAG_HOLD;
+ /* TODO: How to communicate info_local->is_low_pass? */
+ if (info_local->is_mem) /* XXX Is REF appropriate here? */
+ analog->meaning->mqflags |= SR_MQFLAG_REFERENCE;
+ if (info_local->is_auto_range)
+ analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
+ /* TODO: How to communicate info->is_test? What's its meaning at all? */
+ /* TODO: How to communicate info->is_auto_poweroff? */
+ /* TODO: How to communicate info->is_low_batt? */
+
+ return SR_OK;
+}
+
+/*
+ * Parse the same packet multiple times, to extract individual analog
+ * values which correspond to several displays of the device. Make sure
+ * to keep the channel index in place, even if the parse routine will
+ * clear the info structure.
+ */
+SR_PRIV int sr_eev121gw_3displays_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ struct eev121gw_info *info_local;
+ size_t ch_idx;
+ int rc;
+
+ info_local = info;
+ ch_idx = info_local->ch_idx;
+ rc = sr_eev121gw_parse(buf, floatval, analog, info);
+ info_local->ch_idx = ch_idx + 1;
+
+ return rc;
+}
if (info->is_auto)
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
if (info->is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
if (info->is_hold)
/*
* Note: HOLD only affects the number displayed on the LCD,
if (info->is_auto)
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
if (info->is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
if (info->is_hold)
analog->meaning->mqflags |= SR_MQFLAG_HOLD;
if (info->is_rel)
int ret, exponent = 0;
struct fs9721_info *info_local;
- info_local = (struct fs9721_info *)info;
+ info_local = info;
if ((ret = parse_value(buf, floatval, &exponent)) != SR_OK) {
sr_dbg("Error parsing value: %d.", ret);
{
struct fs9721_info *info_local;
- info_local = (struct fs9721_info *)info;
+ info_local = info;
/* User-defined FS9721_LP3 flag 'c2c1_00' means temperature (C). */
if (info_local->is_c2c1_00) {
{
struct fs9721_info *info_local;
- info_local = (struct fs9721_info *)info;
+ info_local = info;
/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (C). */
if (info_local->is_c2c1_01) {
{
struct fs9721_info *info_local;
- info_local = (struct fs9721_info *)info;
+ info_local = info;
/* User-defined FS9721_LP3 flag 'c2c1_10' means temperature (C). */
if (info_local->is_c2c1_10) {
{
struct fs9721_info *info_local;
- info_local = (struct fs9721_info *)info;
+ info_local = info;
/* User-defined FS9721_LP3 flag 'c2c1_01' means temperature (F). */
if (info_local->is_c2c1_01) {
{
struct fs9721_info *info_local;
- info_local = (struct fs9721_info *)info;
+ info_local = info;
/* User-defined FS9721_LP3 flag 'c2c1_00' means MAX. */
if (info_local->is_c2c1_00)
if (info->is_auto)
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
if (info->is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
if (info->is_hold)
analog->meaning->mqflags |= SR_MQFLAG_HOLD;
if (info->is_max)
int ret, exponent = 0;
struct fs9922_info *info_local;
- info_local = (struct fs9922_info *)info;
+ info_local = info;
if ((ret = parse_value(buf, floatval, &exponent)) != SR_OK) {
sr_dbg("Error parsing value: %d.", ret);
{
struct fs9922_info *info_local;
- info_local = (struct fs9922_info *)info;
+ info_local = info;
/* User-defined z1 flag means "diode mode". */
if (info_local->is_z1) {
analog->meaning->mq = SR_MQ_VOLTAGE;
analog->meaning->unit = SR_UNIT_VOLT;
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
}
}
if (!strncmp((const char *)buf, "OVERRNG", 7))
return TRUE;
- if (sscanf((const char *)buf, "%f", &val) == 1)
+ if (sr_atof_ascii((const char *)buf, &val) == SR_OK)
return TRUE;
else
return FALSE;
if (!strncmp((const char *)buf, "OVERRNG", 7))
*floatval = INFINITY;
- else if (sscanf((const char *)buf, "%f", &val) == 1) {
+ else if (sr_atof_ascii((const char *)buf, &val) == SR_OK) {
*floatval = val;
dot_pos = strcspn((const char *)buf, ".");
if (dot_pos < 7)
return SR_OK;
/* Bytes 2-8: Sign, value (up to 5 digits) and decimal point */
- sscanf((const char *)&valstr, "%f", result);
+ sr_atof_ascii((const char *)&valstr, result);
dot_pos = strcspn(valstr, ".");
if (dot_pos < cnt)
info->is_kilo = info->is_hertz = TRUE;
else if (!g_ascii_strcasecmp(u, "C"))
info->is_celsius = TRUE;
+ else if (!g_ascii_strcasecmp(u, "F"))
+ info->is_fahrenheit = TRUE;
else if (!g_ascii_strcasecmp(u, "DB"))
info->is_decibel = TRUE;
+ else if (!g_ascii_strcasecmp(u, "dBm"))
+ info->is_decibel_mw = TRUE;
+ else if (!g_ascii_strcasecmp(u, "W"))
+ info->is_watt = TRUE;
else if (!g_ascii_strcasecmp(u, ""))
info->is_unitless = TRUE;
(!strncmp(buf, " ", 2) && info->is_ohm);
info->is_capacity = !strncmp(buf, "CA", 2) ||
(!strncmp(buf, " ", 2) && info->is_farad);
- info->is_temperature = !strncmp(buf, "TE", 2);
+ info->is_temperature = !strncmp(buf, "TE", 2) ||
+ info->is_celsius || info->is_fahrenheit;
info->is_diode = !strncmp(buf, "DI", 2) ||
(!strncmp(buf, " ", 2) && info->is_volt && info->is_milli);
info->is_frequency = !strncmp(buf, "FR", 2) ||
(!strncmp(buf, " ", 2) && info->is_hertz);
- info->is_gain = !strncmp(buf, "DB", 2);
+ info->is_gain = !strncmp(buf, "DB", 2) && info->is_decibel;
+ info->is_power = (!strncmp(buf, "dB", 2) && info->is_decibel_mw) ||
+ ((!strncmp(buf, "WT", 2) && info->is_watt));
+ info->is_power_factor = !strncmp(buf, "CO", 2) && info->is_unitless;
info->is_hfe = !strncmp(buf, "HF", 2) ||
- (!strncmp(buf, " ", 2) && !info->is_volt && !info->is_ohm &&
- !info->is_logic && !info->is_farad && !info->is_hertz);
+ (!strncmp(buf, " ", 2) && !info->is_ampere &&!info->is_volt &&
+ !info->is_resistance && !info->is_capacity && !info->is_frequency &&
+ !info->is_temperature && !info->is_power && !info->is_power_factor &&
+ !info->is_gain && !info->is_logic && !info->is_diode);
+ info->is_min = !strncmp(buf, "MN", 2);
+ info->is_max = !strncmp(buf, "MX", 2);
+ info->is_avg = !strncmp(buf, "AG", 2);
+
/*
* Note:
* - Protocol doesn't distinguish "resistance" from "beep" mode.
static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
int *exponent, const struct metex14_info *info)
{
- int factor = 0;
+ int factor;
+
+ (void)exponent;
+
/* Factors */
+ factor = 0;
if (info->is_pico)
factor -= 12;
if (info->is_nano)
if (info->is_mega)
factor += 6;
*floatval *= powf(10, factor);
- *exponent += factor;
/* Measurement modes */
if (info->is_volt) {
analog->meaning->mq = SR_MQ_CAPACITANCE;
analog->meaning->unit = SR_UNIT_FARAD;
}
- if (info->is_celsius) {
+ if (info->is_temperature) {
analog->meaning->mq = SR_MQ_TEMPERATURE;
- analog->meaning->unit = SR_UNIT_CELSIUS;
+ if (info->is_celsius)
+ analog->meaning->unit = SR_UNIT_CELSIUS;
+ else if (info->is_fahrenheit)
+ analog->meaning->unit = SR_UNIT_FAHRENHEIT;
+ else
+ analog->meaning->unit = SR_UNIT_UNITLESS;
}
if (info->is_diode) {
analog->meaning->mq = SR_MQ_VOLTAGE;
analog->meaning->unit = SR_UNIT_VOLT;
}
+ if (info->is_power) {
+ analog->meaning->mq = SR_MQ_POWER;
+ if (info->is_decibel_mw)
+ analog->meaning->unit = SR_UNIT_DECIBEL_MW;
+ else if (info->is_watt)
+ analog->meaning->unit = SR_UNIT_WATT;
+ else
+ analog->meaning->unit = SR_UNIT_UNITLESS;
+ }
+ if (info->is_power_factor) {
+ analog->meaning->mq = SR_MQ_POWER_FACTOR;
+ analog->meaning->unit = SR_UNIT_UNITLESS;
+ }
if (info->is_gain) {
analog->meaning->mq = SR_MQ_GAIN;
analog->meaning->unit = SR_UNIT_DECIBEL_VOLT;
if (info->is_dc)
analog->meaning->mqflags |= SR_MQFLAG_DC;
if (info->is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
+ if (info->is_min)
+ analog->meaning->mqflags |= SR_MQFLAG_MIN;
+ if (info->is_max)
+ analog->meaning->mqflags |= SR_MQFLAG_MAX;
+ if (info->is_avg)
+ analog->meaning->mqflags |= SR_MQFLAG_AVG;
}
static gboolean flags_valid(const struct metex14_info *info)
sr_spew("Requesting DMM packet.");
- return (serial_write_nonblocking(serial, &wbuf, 1) == 1) ? SR_OK : SR_ERR;
+ return serial_write_blocking(serial, &wbuf, 1, 0);
}
#endif
return TRUE;
}
+SR_PRIV gboolean sr_metex14_4packets_valid(const uint8_t *buf)
+{
+ struct metex14_info info;
+ size_t ch_idx;
+ const uint8_t *ch_buf;
+
+ ch_buf = buf;
+ for (ch_idx = 0; ch_idx < 4; ch_idx++) {
+ if (ch_buf[13] != '\r')
+ return FALSE;
+ memset(&info, 0x00, sizeof(info));
+ parse_flags((const char *)ch_buf, &info);
+ if (!flags_valid(&info))
+ return FALSE;
+ ch_buf += METEX14_PACKET_SIZE;
+ }
+ return TRUE;
+}
+
/**
* Parse a protocol packet.
*
int ret, exponent = 0;
struct metex14_info *info_local;
- info_local = (struct metex14_info *)info;
+ info_local = info;
/* Don't print byte 13. That one contains the carriage return. */
sr_dbg("DMM packet: \"%.13s\"", buf);
return SR_OK;
}
+
+/**
+ * Parse one out of four values of a four-display Metex14 variant.
+ *
+ * The caller's 'info' parameter can be used to track the channel index,
+ * as long as the information is kept across calls to the 14-byte packet
+ * parse routine (which clears the 'info' container).
+ *
+ * Since analog values have further details in the 'analog' parameter,
+ * passing multiple values per parse routine call is problematic. So we
+ * prefer the approach of passing one value per call, which is most
+ * reliable and shall fit every similar device with multiple displays.
+ *
+ * The meters which use this parse routine send one 14-byte packet per
+ * display. Each packet has the regular Metex14 layout.
+ */
+SR_PRIV int sr_metex14_4packets_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ struct metex14_info *info_local;
+ size_t ch_idx;
+ const uint8_t *ch_buf;
+ int rc;
+
+ info_local = info;
+ ch_idx = info_local->ch_idx;
+ ch_buf = buf + ch_idx * METEX14_PACKET_SIZE;
+ rc = sr_metex14_parse(ch_buf, floatval, analog, info);
+ info_local->ch_idx = ch_idx + 1;
+ return rc;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Copyright (C) 2018 Stefan Mandl
+ *
+ * This program is free software; you can 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/>.
+ */
+
+/*
+ * MASTECH MS8250D protocol parser.
+ *
+ * Sends 18 bytes.
+ * 40 02 32 75 53 33 35 5303 10 00 00 00 00 00 00 10 00
+ *
+ * - Communication parameters: Unidirectional, 2400/8n1
+ * - CP2102 USB to UART bridge controller
+ */
+
+#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 "ms8250d"
+
+/*
+ * Main display (7-segment LCD value): xxDGA xxEF xxxx xxCB
+ * https://en.wikipedia.org/wiki/Seven-segment_display
+ */
+static int parse_digit(uint16_t b)
+{
+ switch (b) {
+ case 0x0: /* 7-segment not active */
+ return 0;
+ case 0x430: /* Overflow */
+ return 0xF;
+ case 0x533:
+ return 0;
+ case 0x003:
+ return 1;
+ case 0x721:
+ return 2;
+ case 0x703:
+ return 3;
+ case 0x213:
+ return 4;
+ case 0x712:
+ return 5;
+ case 0x732:
+ return 6;
+ case 0x103:
+ return 7;
+ case 0x733:
+ return 8;
+ case 0x713:
+ return 9;
+ default:
+ sr_dbg("Invalid digit word: 0x%04x.", b);
+ return -1;
+ }
+}
+
+/* Parse second display. */
+static int parse_digit2(uint16_t b)
+{
+ switch (b) {
+ case 0x00:
+ return 0;
+ case 0x7D:
+ return 0;
+ case 0x05:
+ return 1;
+ case 0x1B:
+ return 2;
+ case 0x1F:
+ return 3;
+ case 0x27:
+ return 4;
+ case 0x3E:
+ return 5;
+ case 0x7E:
+ return 6;
+ case 0x15:
+ return 7;
+ case 0x7F:
+ return 8;
+ case 0x3F:
+ return 9;
+ default:
+ sr_dbg("Invalid second display digit word: 0x%04x.", b);
+ return -1;
+ }
+}
+
+static void parse_flags(const uint8_t *buf, struct ms8250d_info *info)
+{
+ info->is_volt = (buf[9] & (1 << 4)) ? 1 : 0;
+ info->is_ohm = (buf[9] & (1 << 6)) ? 1 : 0;
+ info->is_ampere = (buf[10] & (1 << 0)) ? 1 : 0;
+ info->is_hz = (buf[10] & (1 << 2)) ? 1 : 0;
+ info->is_farad = (buf[10] & (1 << 1)) ? 1 : 0;
+
+ /* Micro */
+ if (!info->is_farad)
+ info->is_micro = (buf[8] & (1 << 4)) ? 1 : 0;
+ else
+ info->is_micro = (buf[9] & (1 << 1)) ? 1 : 0; /* uF */
+
+ info->is_nano = (buf[8] & (1 << 5)) ? 1 : 0;
+ info->is_milli = (buf[9] & (1 << 0)) ? 1 : 0;
+ info->is_kilo = (buf[9] & (1 << 2)) ? 1 : 0;
+ info->is_mega = (buf[8] & (1 << 6)) ? 1 : 0;
+
+ info->is_autotimer = (buf[1] & (1 << 0)) ? 1 : 0; /* Auto off timer */
+ info->is_rs232 = (buf[1] & (1 << 1)) ? 1 : 0; /* RS232 via USB */
+ info->is_ac = (buf[1] & (1 << 4)) ? 1 : 0;
+ info->is_dc = (buf[2] & (1 << 1)) ? 1 : 0;
+ info->is_auto = (buf[16] & (1 << 4)) ? 1 : 0;
+ info->is_bat = (buf[1] & (1 << 5)) ? 1 : 0; /* Low battery */
+ info->is_min = (buf[16] & (1 << 2)) ? 1 : 0;
+ info->is_max = (buf[16] & (1 << 1)) ? 1 : 0;
+ info->is_rel = (buf[15] & (1 << 7)) ? 1 : 0;
+ info->is_hold = (buf[16] & (1 << 3)) ? 1 : 0;
+ info->is_diode = (buf[11] & (1 << 0)) ? 1 : 0;
+ info->is_beep = (buf[11] & (1 << 1)) ? 1 : 0;
+ info->is_ncv = (buf[0] & (1 << 0)) ? 1 : 0;
+}
+
+static gboolean flags_valid(const struct ms8250d_info *info)
+{
+ int count;
+
+ /* Does the packet have more than one multiplier? */
+ count = 0;
+ count += (info->is_nano) ? 1 : 0;
+ count += (info->is_micro) ? 1 : 0;
+ count += (info->is_milli) ? 1 : 0;
+ count += (info->is_kilo) ? 1 : 0;
+ count += (info->is_mega) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one multiplier detected in packet.");
+ return FALSE;
+ }
+
+ /* Does the packet "measure" more than one type of value? */
+ count = 0;
+ count += (info->is_hz) ? 1 : 0;
+ count += (info->is_ohm) ? 1 : 0;
+ count += (info->is_farad) ? 1 : 0;
+ count += (info->is_ampere) ? 1 : 0;
+ count += (info->is_volt) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one measurement type detected in packet.");
+ return FALSE;
+ }
+
+ /* Both AC and DC set? */
+ if (info->is_ac && info->is_dc) {
+ sr_dbg("Both AC and DC flags detected in packet.");
+ return FALSE;
+ }
+
+ /* RS232 flag set? */
+ if (!info->is_rs232) {
+ sr_dbg("No RS232 flag detected in packet.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+ int *exponent, const struct ms8250d_info *info)
+{
+ /* Factors */
+ if (info->is_nano)
+ *exponent -= 9;
+ if (info->is_micro)
+ *exponent -= 6;
+ if (info->is_milli)
+ *exponent -= 3;
+ if (info->is_kilo)
+ *exponent += 3;
+ if (info->is_mega)
+ *exponent += 6;
+ *floatval *= powf(10, *exponent);
+
+ /* Measurement modes */
+ if (info->is_volt) {
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ analog->meaning->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_ampere) {
+ analog->meaning->mq = SR_MQ_CURRENT;
+ analog->meaning->unit = SR_UNIT_AMPERE;
+ }
+ if (info->is_ohm) {
+ analog->meaning->mq = SR_MQ_RESISTANCE;
+ analog->meaning->unit = SR_UNIT_OHM;
+ }
+ if (info->is_hz) {
+ analog->meaning->mq = SR_MQ_FREQUENCY;
+ analog->meaning->unit = SR_UNIT_HERTZ;
+ }
+ if (info->is_farad) {
+ analog->meaning->mq = SR_MQ_CAPACITANCE;
+ analog->meaning->unit = SR_UNIT_FARAD;
+ }
+ if (info->is_beep) {
+ analog->meaning->mq = SR_MQ_CONTINUITY;
+ analog->meaning->unit = SR_UNIT_BOOLEAN;
+ *floatval = (*floatval == INFINITY) ? 0.0 : 1.0;
+ }
+ if (info->is_diode) {
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ analog->meaning->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_percent) {
+ analog->meaning->mq = SR_MQ_DUTY_CYCLE;
+ analog->meaning->unit = SR_UNIT_PERCENTAGE;
+ }
+
+ /* Measurement related flags */
+ if (info->is_ac)
+ analog->meaning->mqflags |= SR_MQFLAG_AC;
+ if (info->is_dc)
+ analog->meaning->mqflags |= SR_MQFLAG_DC;
+ if (info->is_auto)
+ analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
+ if (info->is_diode)
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
+ if (info->is_hold)
+ analog->meaning->mqflags |= SR_MQFLAG_HOLD;
+ if (info->is_rel)
+ analog->meaning->mqflags |= SR_MQFLAG_RELATIVE;
+
+ /* Other flags */
+ if (info->is_rs232)
+ sr_spew("RS232 enabled.");
+ if (info->is_bat)
+ sr_spew("Battery is low.");
+ if (info->is_beep)
+ sr_spew("Beep is active");
+}
+
+SR_PRIV gboolean sr_ms8250d_packet_valid(const uint8_t *buf)
+{
+ struct ms8250d_info info;
+
+ sr_dbg("DMM packet: %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[14], buf[15], buf[16], buf[17]);
+
+ parse_flags(buf, &info);
+
+ if ((buf[17] == 0x00) && flags_valid(&info))
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * Parse a protocol packet.
+ *
+ * @param buf Buffer containing the 18-byte protocol packet. Must not be NULL.
+ * @param floatval Pointer to a float variable. That variable will contain the
+ * result value upon parsing success. Must not be NULL.
+ * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
+ * filled with data according to the protocol packet.
+ * Must not be NULL.
+ * @param info Pointer to a struct ms8250d_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_ms8250d_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ int exponent = 0, sec_exponent = 0, sign;
+ float sec_floatval;
+
+ /* buf[0] bar display. */
+ /* buf[1] bar display. */
+
+ /* Parse seven segment digit. */
+ int16_t digit4 = parse_digit(((buf[7] & 0x73) << 4) | (buf[8] & 0x3));
+
+ int16_t digit3 = parse_digit(((buf[6] & 0x07) << 8) | (buf[5] & 0x30) \
+ | ((buf[6] & 0x30) >> 4));
+
+ int16_t digit2 = parse_digit(((buf[4] & 0x73) << 4) | (buf[5] & 0x03));
+
+ int16_t digit1 = parse_digit(((buf[3] & 0x07) << 8) | (buf[2] & 0x30) \
+ | ((buf[3] & 0x30) >> 4));
+
+ sr_dbg("Digits: %d %d %d %d.", digit1, digit2, digit3, digit4);
+
+ /* Decimal point position. */
+ if ((buf[3] & (1 << 6)) != 0) {
+ exponent = -3;
+ sr_spew("Decimal point after first digit.");
+ } else if ((buf[5] & (1 << 6)) != 0) {
+ exponent = -2;
+ sr_spew("Decimal point after second digit.");
+ } else if ((buf[7] & (1 << 2)) != 0) {
+ exponent = -1;
+ sr_spew("Decimal point after third digit.");
+ } else {
+ exponent = 0;
+ sr_spew("No decimal point in the number.");
+ }
+
+ struct ms8250d_info *info_local;
+
+ info_local = info;
+
+ parse_flags(buf, info_local);
+
+ /* Sign */
+ sign = (buf[0] & (1 << 2)) ? -1 : 1;
+
+ /* Parse second display. */
+ int16_t sec_digit4 = parse_digit2(buf[12] & 0x7F);
+ int16_t sec_digit3 = parse_digit2(buf[13] & 0x7F);
+ int16_t sec_digit2 = parse_digit2(buf[14] & 0x7F);
+ int16_t sec_digit1 = parse_digit2(buf[15] & 0x7F);
+
+ sr_dbg("Digits (2nd display): %d %d %d %d.",
+ sec_digit1, sec_digit2, sec_digit3, sec_digit4);
+
+ /* Second display decimal point position. */
+ if ((buf[14] & (1 << 7)) != 0) {
+ sec_exponent = -3;
+ sr_spew("Sec decimal point after first digit.");
+ } else if ((buf[13] & (1 << 7)) != 0) {
+ sec_exponent = -2;
+ sr_spew("Sec decimal point after second digit.");
+ } else if ((buf[12] & (1 << 7)) != 0) {
+ sec_exponent = -1;
+ sr_spew("Sec decimal point after third digit.");
+ } else {
+ sec_exponent = 0;
+ sr_spew("Sec no decimal point in the number.");
+ }
+
+ *floatval = (double)((digit1 * 1000) + (digit2 * 100) + (digit3 * 10) + digit4);
+
+ sec_floatval = (double)(sec_digit1 * 1000) + (sec_digit2 * 100) + (sec_digit3 * 10) + sec_digit4;
+ sec_floatval *= powf(10, sec_exponent);
+
+ /* Apply sign. */
+ *floatval *= sign;
+
+ handle_flags(analog, floatval, &exponent, info_local);
+
+ /* Check for "OL". */
+ if (digit3 == 0x0F) {
+ sr_spew("Over limit.");
+ *floatval = INFINITY;
+ return SR_OK;
+ }
+
+ sr_spew("The display value is %f.", (double)*floatval);
+ sr_spew("The 2nd display value is %f.", sec_floatval);
+
+ analog->encoding->digits = -exponent;
+ analog->spec->spec_digits = -exponent;
+
+ return SR_OK;
+}
*floatval = (float) value * powf(10, exponent);
- analog->encoding->digits = -exponent;
+ analog->encoding->digits = -exponent;
analog->spec->spec_digits = -exponent;
return SR_OK;
if (info->is_auto)
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
if (info->is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
}
static gboolean flags_valid(const struct ut71x_info *info)
int ret, exponent = 0;
struct ut71x_info *info_local;
- info_local = (struct ut71x_info *)info;
+ info_local = info;
memset(info_local, 0, sizeof(struct ut71x_info));
if (!sr_ut71x_packet_valid(buf))
if (info->is_auto)
analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE;
if (info->is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
if (info->is_hold)
/*
* Note: HOLD only affects the number displayed on the LCD,
int ret, exponent = 0;
struct vc870_info *info_local;
- info_local = (struct vc870_info *)info;
+ info_local = info;
memset(info_local, 0, sizeof(struct vc870_info));
if (!sr_vc870_packet_valid(buf))
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2018 Matthias Schulz <matthschulz@arcor.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Voltcraft 13-bytes ASCII protocol parser.
+ *
+ * Bytes 1-3 measuring mode, byte 4 '-' for negative,
+ * bytes 5-9 value, bytes 10-11 unit, bytes 12-13 CRLF 0d 0a.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <math.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "vc96"
+
+/** Parse value from buf, byte 3-8. */
+static int parse_value(const uint8_t *buf, struct vc96_info *info,
+ float *result, int *exponent)
+{
+ int i, is_ol, cnt, dot_pos;
+ char valstr[8 + 1];
+
+ (void)info;
+
+ /* Strip all spaces from bytes 3-8. */
+ memset(&valstr, 0, 6 + 1);
+ for (i = 0, cnt = 0; i < 6; i++) {
+ if (buf[3 + i] != ' ')
+ valstr[cnt++] = buf[3 + i];
+ }
+
+ /* Bytes 5-7: Over limit (various forms) */
+ is_ol = 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;
+ return SR_OK;
+ }
+
+ /* Bytes 3-10: Sign, value (up to 5 digits) and decimal point */
+ sr_atof_ascii((const char *)&valstr, result);
+
+ dot_pos = strcspn(valstr, ".");
+ if (dot_pos < cnt)
+ *exponent = -(cnt - dot_pos - 1);
+ else
+ *exponent = 0;
+
+ sr_spew("The display value is %f.", *result);
+
+ return SR_OK;
+}
+
+static void parse_flags(const char *buf, struct vc96_info *info)
+{
+ int i, cnt;
+ char unit[4 + 1];
+ const char *u;
+
+ /* Bytes 0-1: Measurement mode AC, DC */
+ info->is_ac = !strncmp(buf, "AC", 2);
+ info->is_dc = !strncmp(buf, "DC", 2);
+
+ /* Bytes 0-2: Measurement mode DIO, OHM */
+ info->is_ohm = !strncmp(buf, "OHM", 3);
+ info->is_diode = !strncmp(buf, "DIO", 3);
+ info->is_hfe = !strncmp(buf, "hfe", 3);
+
+ /* Bytes 3-8: See parse_value(). */
+
+ /* Strip all spaces from bytes 9-10. */
+ memset(&unit, 0, 2 + 1);
+ for (i = 0, cnt = 0; i < 2; i++) {
+ if (buf[9 + i] != ' ')
+ unit[cnt++] = buf[9 + i];
+ }
+ sr_spew("Bytes 9..10 without spaces \"%.4s\".", unit);
+
+ /* Bytes 9-10: Unit */
+ u = (const char *)&unit;
+ if (!g_ascii_strcasecmp(u, "A"))
+ info->is_ampere = TRUE;
+ else if (!g_ascii_strcasecmp(u, "mA"))
+ info->is_milli = info->is_ampere = TRUE;
+ else if (!g_ascii_strcasecmp(u, "uA"))
+ info->is_micro = info->is_ampere = TRUE;
+ else if (!g_ascii_strcasecmp(u, "V"))
+ info->is_volt = TRUE;
+ else if (!g_ascii_strcasecmp(u, "mV"))
+ info->is_milli = info->is_volt = TRUE;
+ else if (!g_ascii_strcasecmp(u, "K"))
+ info->is_kilo = TRUE;
+ else if (!g_ascii_strcasecmp(u, "M"))
+ info->is_mega = TRUE;
+ else if (!g_ascii_strcasecmp(u, ""))
+ info->is_unitless = TRUE;
+
+ /* Bytes 0-2: Measurement mode, except AC/DC */
+ info->is_resistance = !strncmp(buf, "OHM", 3) ||
+ (!strncmp(buf, " ", 3) && info->is_ohm);
+ info->is_diode = !strncmp(buf, "DIO", 3) ||
+ (!strncmp(buf, " ", 3) && info->is_volt && info->is_milli);
+ info->is_hfe = !strncmp(buf, "hfe", 3) ||
+ (!strncmp(buf, " ", 3) && !info->is_ampere && !info->is_volt &&
+ !info->is_resistance && !info->is_diode);
+
+ /*
+ * Note:
+ * - Protocol doesn't distinguish "resistance" from "beep" mode.
+ */
+
+ /* Byte 12: Always '\r' (carriage return, 0x0d, 12) */
+ /* Byte 13: Always '\n' (carriage return, 0x0a, 13) */
+}
+
+static void handle_flags(struct sr_datafeed_analog *analog, float *floatval,
+ int *exponent, const struct vc96_info *info)
+{
+ int factor;
+
+ (void)exponent;
+
+ /* Factors */
+ factor = 0;
+ if (info->is_micro)
+ factor -= 6;
+ if (info->is_milli)
+ factor -= 3;
+ if (info->is_kilo)
+ factor += 3;
+ if (info->is_mega)
+ factor += 6;
+ *floatval *= powf(10, factor);
+
+ /* Measurement modes */
+ if (info->is_volt) {
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ analog->meaning->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_ampere) {
+ analog->meaning->mq = SR_MQ_CURRENT;
+ analog->meaning->unit = SR_UNIT_AMPERE;
+ }
+ if (info->is_ohm) {
+ analog->meaning->mq = SR_MQ_RESISTANCE;
+ analog->meaning->unit = SR_UNIT_OHM;
+ }
+ if (info->is_diode) {
+ analog->meaning->mq = SR_MQ_VOLTAGE;
+ analog->meaning->unit = SR_UNIT_VOLT;
+ }
+ if (info->is_hfe) {
+ analog->meaning->mq = SR_MQ_GAIN;
+ analog->meaning->unit = SR_UNIT_UNITLESS;
+ }
+
+ /* Measurement related flags */
+ if (info->is_ac)
+ analog->meaning->mqflags |= SR_MQFLAG_AC;
+ if (info->is_dc)
+ analog->meaning->mqflags |= SR_MQFLAG_DC;
+ if (info->is_diode)
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
+}
+
+static gboolean flags_valid(const struct vc96_info *info)
+{
+ int count;
+
+ /* Does the packet have more than one multiplier? */
+ count = 0;
+ count += (info->is_micro) ? 1 : 0;
+ count += (info->is_milli) ? 1 : 0;
+ count += (info->is_kilo) ? 1 : 0;
+ count += (info->is_mega) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one multiplier detected in packet.");
+ return FALSE;
+ }
+
+ /* Does the packet "measure" more than one type of value? */
+ count = 0;
+ count += (info->is_ac) ? 1 : 0;
+ count += (info->is_dc) ? 1 : 0;
+ count += (info->is_resistance) ? 1 : 0;
+ count += (info->is_diode) ? 1 : 0;
+ if (count > 1) {
+ sr_dbg("More than one measurement type detected in packet.");
+ return FALSE;
+ }
+
+ /* Both AC and DC set? */
+ if (info->is_ac && info->is_dc) {
+ sr_dbg("Both AC and DC flags detected in packet.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+SR_PRIV gboolean sr_vc96_packet_valid(const uint8_t *buf)
+{
+ struct vc96_info info;
+
+ memset(&info, 0x00, sizeof(struct vc96_info));
+ parse_flags((const char *)buf, &info);
+
+ if (!flags_valid(&info))
+ return FALSE;
+
+ if ((buf[11] != '\r') || (buf[12] != '\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 be modified
+ * in-place depending on the protocol packet. Must not be NULL.
+ * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
+ * filled with data according to the protocol packet.
+ * Must not be NULL.
+ * @param info Pointer to a struct vc96_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_vc96_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info)
+{
+ int ret, exponent = 0;
+ struct vc96_info *info_local;
+
+ info_local = info;
+
+ /* Don't print byte 12 + 13. Those contain the CR LF. */
+ sr_dbg("DMM packet: \"%.11s\".", buf);
+
+ memset(info_local, 0x00, sizeof(struct vc96_info));
+
+ if ((ret = parse_value(buf, info_local, floatval, &exponent)) < 0) {
+ sr_dbg("Error parsing value: %d.", ret);
+ return ret;
+ }
+
+ parse_flags((const char *)buf, info_local);
+ handle_flags(analog, floatval, &exponent, info_local);
+
+ analog->encoding->digits = -exponent;
+ analog->spec->spec_digits = -exponent;
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/*
+ * This marks the start of the driver list. This file must be linked
+ * before any actual drivers.
+ */
+
+SR_PRIV const struct sr_dev_driver *sr_driver_list__start[]
+ __attribute__((section (SR_DRIVER_LIST_SECTION),
+ used, aligned(sizeof(struct sr_dev_driver *))))
+ = { NULL /* Dummy item, as zero length arrays are not allowed by C99 */ };
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/*
+ * This marks the end of the driver list. This file must be linked
+ * after any actual drivers.
+ */
+
+SR_PRIV const struct sr_dev_driver *sr_driver_list__stop[]
+ __attribute__((section (SR_DRIVER_LIST_SECTION),
+ used, aligned(sizeof(struct sr_dev_driver *))))
+ = { NULL /* Dummy item, as zero length arrays are not allowed by C99 */ };
*
* Copyright (C) 2016 Lars-Peter Clausen <lars@metafoo.de>
* Copyright (C) 2016 Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
/*
* sr_driver_list is a special section contains pointers to all the hardware
* drivers built into the library. The __start and __stop symbols are
- * auto-generated by the linker (OSX needs a little help) and point to the start
- * and end of the section. They are used to iterate over the list of all
- * drivers.
+ * created from driver_list_start.c and driver_list_stop.c, and point to the
+ * start and end of the section. They are used to iterate over the list of
+ * all drivers.
*/
-#ifdef __APPLE__
-extern struct sr_dev_driver *__start_sr_driver_list __asm("section$start$__DATA$__sr_driver_list");
-extern struct sr_dev_driver *__stop_sr_driver_list __asm("section$end$__DATA$__sr_driver_list");
-#else
-extern struct sr_dev_driver *__start_sr_driver_list;
-extern struct sr_dev_driver *__stop_sr_driver_list;
-#endif
+SR_PRIV extern const struct sr_dev_driver *sr_driver_list__start[];
+SR_PRIV extern const struct sr_dev_driver *sr_driver_list__stop[];
/** @private
* Initialize the driver list in a fresh libsigrok context.
array = g_array_new(TRUE, FALSE, sizeof(struct sr_dev_driver *));
#ifdef HAVE_DRIVERS
- for (struct sr_dev_driver **drivers = &__start_sr_driver_list;
- drivers < &__stop_sr_driver_list; drivers++)
+ for (const struct sr_dev_driver **drivers = sr_driver_list__start + 1;
+ drivers < sr_driver_list__stop; drivers++)
g_array_append_val(array, *drivers);
#endif
ctx->driver_list = (struct sr_dev_driver **)array->data;
};
static const char *data_sources[] = {
- "Live",
- "Log-Hand",
- "Log-Trig",
- "Log-Auto",
- "Log-Export",
+ "Live", "Log-Hand", "Log-Trig", "Log-Auto", "Log-Export",
};
extern const struct agdmm_job agdmm_jobs_live[];
return std_scan_complete(di, devices);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
- int ret;
(void)cg;
devc = sdi->priv;
- ret = SR_OK;
switch (key) {
case SR_CONF_SAMPLERATE:
*data = g_variant_new_uint64(devc->cur_samplerate);
break;
case SR_CONF_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
- ret = sr_sw_limits_config_get(&devc->limits, key, data);
- break;
+ return sr_sw_limits_config_get(&devc->limits, key, data);
case SR_CONF_DATA_SOURCE:
*data = g_variant_new_string(data_sources[devc->data_source]);
break;
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
- const char *tmp_str;
- unsigned int i;
- int ret;
+ int idx;
(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:
samplerate = g_variant_get_uint64(data);
if (samplerate < samplerates[0] || samplerate > samplerates[1])
- ret = SR_ERR_ARG;
- else
- devc->cur_samplerate = g_variant_get_uint64(data);
+ return SR_ERR_ARG;
+ devc->cur_samplerate = g_variant_get_uint64(data);
break;
case SR_CONF_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
- ret = sr_sw_limits_config_set(&devc->limits, key, data);
- break;
- case SR_CONF_DATA_SOURCE: {
- tmp_str = g_variant_get_string(data, NULL);
- for (i = 0; i < ARRAY_SIZE(data_sources); i++)
- if (!strcmp(tmp_str, data_sources[i])) {
- devc->data_source = i;
- break;
- }
- if (i == ARRAY_SIZE(data_sources))
- return SR_ERR;
+ return sr_sw_limits_config_set(&devc->limits, key, data);
+ case SR_CONF_DATA_SOURCE:
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->data_source = idx;
break;
- }
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *gvar;
- GVariantBuilder gvb;
-
- 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;
- }
-
- 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 || cg)
- return SR_ERR_ARG;
-
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
break;
case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
break;
default:
return SR_ERR_NA;
struct dev_context *devc = sdi->priv;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc->cur_channel = sr_next_enabled_channel(sdi, NULL);
devc->cur_conf = sr_next_enabled_channel(sdi, NULL);
devc->cur_sample = 1;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* Poll every 10ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 10,
agdmm_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
devc->current_job++;
if (!job_current(devc)->send)
devc->current_job = 0;
- } while(job_in_interval(devc) && devc->current_job != current_job);
+ } while (job_in_interval(devc) && devc->current_job != current_job);
return job_current(devc);
}
}
if (sr_sw_limits_check(&devc->limits) || stop)
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
else
dispatch(sdi);
} else if (!strcmp(mstr, "DIOD")) {
devc->cur_mq[i] = SR_MQ_VOLTAGE;
devc->cur_unit[i] = SR_UNIT_VOLT;
- devc->cur_mqflags[i] = SR_MQFLAG_DIODE;
+ devc->cur_mqflags[i] = SR_MQFLAG_DIODE | SR_MQFLAG_DC;
devc->cur_exponent[i] = 0;
devc->cur_digits[i] = 3;
} else if (!strcmp(mstr, "CAP")) {
} else if (!strcmp(mstr, "DC")) {
devc->cur_mqflags[i] |= SR_MQFLAG_DC;
} else {
- sr_dbg("Unknown first argument '%s'.", mstr);
+ sr_dbg("Unknown first argument '%s'.", mstr);
}
g_free(mstr);
} else
} else if (!strcmp(mstr, "DIOD")) {
devc->cur_mq[i] = SR_MQ_VOLTAGE;
devc->cur_unit[i] = SR_UNIT_VOLT;
- devc->cur_mqflags[i] = SR_MQFLAG_DIODE;
+ devc->cur_mqflags[i] = SR_MQFLAG_DIODE | SR_MQFLAG_DC;
devc->cur_exponent[i] = 0;
if (devc->profile->model == KEYSIGHT_U1281 ||
devc->profile->model == KEYSIGHT_U1282) {
if (mstr[12] & 1) mqflags |= SR_MQFLAG_AVG;
if (mstr[12] & 2) mqflags |= SR_MQFLAG_MIN;
if (mstr[12] & 4) mqflags |= SR_MQFLAG_MAX;
- if (function == 5) mqflags |= SR_MQFLAG_DIODE;
+ if (function == 5) mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
g_free(mstr);
mq = mqs[function];
const struct agdmm_recv *recvs;
};
-/* Private, per-device-instance driver context. */
struct dev_context {
const struct agdmm_profile *profile;
struct sr_sw_limits limits;
int data_source;
- /* Runtime. */
const struct agdmm_job *jobs;
int current_job;
gboolean job_running;
return std_scan_complete(di, devices);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc = sdi->priv;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
- const char *tmp_str;
- unsigned int i;
+ int idx;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
return sr_sw_limits_config_set(&devc->limits, key, data);
- case SR_CONF_DATA_SOURCE: {
- tmp_str = g_variant_get_string(data, NULL);
- for (i = 0; i < ARRAY_SIZE(data_sources); i++)
- if (!strcmp(tmp_str, data_sources[i])) {
- devc->data_source = i;
- break;
- }
- if (i == ARRAY_SIZE(data_sources))
- return SR_ERR;
+ case SR_CONF_DATA_SOURCE:
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->data_source = idx;
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)
+static int config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- (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:
- 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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
break;
default:
return SR_ERR_NA;
struct dev_context *devc;
serial = sdi->conn;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* Poll every 50ms, or whenever some data comes in. */
serial_source_add(sdi->session, serial, G_IO_IN, 50,
appa_55ii_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
if (devc->data_source != DATA_SOURCE_MEMORY)
return;
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
static const uint8_t *appa_55ii_parse_data(struct sr_dev_inst *sdi,
}
if (sr_sw_limits_check(&devc->limits)) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
DATA_SOURCE_MEMORY,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Acquisition settings */
struct sr_sw_limits limits;
gboolean data_source; /**< Whether to read live samples or memory */
- /* Temporary state across callbacks */
uint8_t buf[APPA_55II_BUF_SIZE];
unsigned int buf_len;
uint8_t log_buf[64];
#define CMD_VERSION "version\r\n"
#define CMD_MONITOR "monitor 200\r\n"
+#define CMD_MONITOR_STOP "monitor 0\r\n"
static const uint32_t scanopts[] = {
SR_CONF_CONN,
static const uint32_t devopts_cg[] = {
SR_CONF_ENABLED | SR_CONF_SET,
- SR_CONF_REGULATION | SR_CONF_GET,
+ SR_CONF_REGULATION | SR_CONF_GET | SR_CONF_LIST,
SR_CONF_VOLTAGE | SR_CONF_GET,
SR_CONF_CURRENT | SR_CONF_GET,
SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET,
SR_CONF_UNDER_VOLTAGE_CONDITION | SR_CONF_GET,
SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE | SR_CONF_GET,
+ SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const char *regulation[] = {
+ /* CC mode only. */
+ "CC",
};
static GSList *scan(struct sr_dev_driver *di, GSList *options)
serial_flush(serial);
+ /*
+ * First stop potentially running monitoring and wait for 50ms before
+ * next command can be sent.
+ */
+ if (serial_write_blocking(serial, CMD_MONITOR_STOP,
+ strlen(CMD_MONITOR_STOP), serial_timeout(serial,
+ strlen(CMD_MONITOR_STOP))) < (int)strlen(CMD_MONITOR_STOP)) {
+ sr_dbg("Unable to write while probing for hardware.");
+ serial_close(serial);
+ return NULL;
+ }
+ g_usleep(50 * 1000);
+
if (serial_write_blocking(serial, CMD_VERSION,
strlen(CMD_VERSION), serial_timeout(serial,
strlen(CMD_VERSION))) < (int)strlen(CMD_VERSION)) {
ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
cg->channels = g_slist_append(cg->channels, ch);
- ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "I");
+ ch = sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I");
cg->channels = g_slist_append(cg->channels, ch);
devc = g_malloc0(sizeof(struct dev_context));
static int config_list(uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- GVariantBuilder gvb;
- int ret;
-
- /* Always available. */
- 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;
- }
-
- 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;
-
- 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;
- }
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
} 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));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
+ break;
+ case SR_CONF_REGULATION:
+ *data = std_gvar_array_str(ARRAY_AND_SIZE(regulation));
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(6.0));
- g_variant_builder_add_value(&gvb, g_variant_new_double(0.001)); /* 1mA steps */
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_min_max_step(0.0, 6.0, 0.001);
+ break;
+ case SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD:
+ *data = std_gvar_min_max_step(0.0, 60.0, 0.001);
break;
default:
return SR_ERR_NA;
}
}
- return ret;
+ 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;
float fvalue;
(void)cg;
* - SR_CONF_ENABLED (state cannot be queried, only set)
*/
- ret = SR_OK;
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
*data = g_variant_new_boolean(devc->otp_active);
break;
case SR_CONF_UNDER_VOLTAGE_CONDITION:
- *data = g_variant_new_boolean(TRUE); /* Always on. */
+ if (reloadpro_get_under_voltage_threshold(sdi, &fvalue) == SR_OK)
+ *data = g_variant_new_boolean(fvalue != 0.0);
break;
case SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE:
*data = g_variant_new_boolean(devc->uvc_active);
break;
+ case SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD:
+ if (reloadpro_get_under_voltage_threshold(sdi, &fvalue) == SR_OK)
+ *data = g_variant_new_double(fvalue);
+ break;
default:
return SR_ERR_NA;
}
- return ret;
+ 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;
- 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_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
return sr_sw_limits_config_set(&devc->limits, key, data);
case SR_CONF_ENABLED:
- ret = reloadpro_set_on_off(sdi, g_variant_get_boolean(data));
- break;
+ return reloadpro_set_on_off(sdi, g_variant_get_boolean(data));
case SR_CONF_CURRENT_LIMIT:
- ret = reloadpro_set_current_limit(sdi,
+ return reloadpro_set_current_limit(sdi, g_variant_get_double(data));
+ case SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD:
+ return reloadpro_set_under_voltage_threshold(sdi,
g_variant_get_double(data));
- break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ if (serial_write_blocking(sdi->conn, CMD_MONITOR_STOP,
+ strlen(CMD_MONITOR_STOP), serial_timeout(sdi->conn,
+ strlen(CMD_MONITOR_STOP))) < (int)strlen(CMD_MONITOR_STOP)) {
+ sr_dbg("Unable to stop monitoring.");
+ }
+
+ return std_serial_dev_close(sdi);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
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->acquisition_running = TRUE;
+
serial = sdi->conn;
/* Send the 'monitor <ms>' command (doesn't have a reply). */
return SR_ERR;
}
- /* Poll every 100ms, or whenever some data comes in. */
- serial_source_add(sdi->session, serial, G_IO_IN, 100,
- reloadpro_receive_data, (void *)sdi);
-
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
memset(devc->buf, 0, RELOADPRO_BUFSIZE);
devc->buflen = 0;
+ g_mutex_init(&devc->acquisition_mutex);
+
+ serial_source_add(sdi->session, serial, G_IO_IN, 100,
+ reloadpro_receive_data, (void *)sdi);
+
return SR_OK;
}
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int ret;
+
+ devc = sdi->priv;
+ devc->acquisition_running = FALSE;
+
+ ret = std_serial_dev_acquisition_stop(sdi);
+ g_mutex_clear(&devc->acquisition_mutex);
+
+ return ret;
+}
+
static struct sr_dev_driver arachnid_labs_re_load_pro_driver_info = {
.name = "arachnid-labs-re-load-pro",
.longname = "Arachnid Labs Re:load Pro",
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
.dev_open = std_serial_dev_open,
- .dev_close = std_serial_dev_close,
+ .dev_close = dev_close,
.dev_acquisition_start = dev_acquisition_start,
- .dev_acquisition_stop = std_serial_dev_acquisition_stop,
+ .dev_acquisition_stop = dev_acquisition_stop,
.context = NULL,
};
SR_REGISTER_DEV_DRIVER(arachnid_labs_re_load_pro_driver_info);
*/
#include <config.h>
+#include <math.h>
#include <string.h>
#include "protocol.h"
-#define READ_TIMEOUT_MS 1000
+#define READ_TIMEOUT_MS 500
static int send_cmd(const struct sr_dev_inst *sdi, const char *cmd,
char *replybuf, int replybufsize)
{
char *bufptr;
int len, ret;
+ struct dev_context *devc;
struct sr_serial_dev_inst *serial;
+ devc = sdi->priv;
serial = sdi->conn;
/* Send the command (blocking, with timeout). */
return SR_ERR;
}
- /* Read the reply (blocking, with timeout). */
- memset(replybuf, 0, replybufsize);
- bufptr = replybuf;
- len = replybufsize;
- ret = serial_readline(serial, &bufptr, &len, READ_TIMEOUT_MS);
-
- /* If we got 0 characters (possibly one \r or \n), retry once. */
- if (len == 0) {
+ if (!devc->acquisition_running) {
+ /* Read the reply (blocking, with timeout). */
+ memset(replybuf, 0, replybufsize);
+ bufptr = replybuf;
len = replybufsize;
ret = serial_readline(serial, &bufptr, &len, READ_TIMEOUT_MS);
- }
- if (g_str_has_prefix((const char *)&bufptr, "err ")) {
- sr_err("Device replied with an error: '%s'.", bufptr);
- return SR_ERR;
+ /* If we got 0 characters (possibly one \r or \n), retry once. */
+ if (len == 0) {
+ len = replybufsize;
+ ret = serial_readline(serial, &bufptr, &len, READ_TIMEOUT_MS);
+ }
+
+ if (g_str_has_prefix((const char *)&bufptr, "err ")) {
+ sr_err("Device replied with an error: '%s'.", bufptr);
+ return SR_ERR;
+ }
}
return ret;
}
SR_PRIV int reloadpro_set_current_limit(const struct sr_dev_inst *sdi,
- float current)
+ float current_limit)
{
+ struct dev_context *devc;
int ret, ma;
char buf[100];
char *cmd;
- if (current < 0 || current > 6) {
- sr_err("The current limit must be 0-6 A (was %f A).", current);
+ devc = sdi->priv;
+
+ if (current_limit < 0 || current_limit > 6) {
+ sr_err("The current limit must be 0-6 A (was %f A).", current_limit);
return SR_ERR_ARG;
}
- /* Hardware expects current in mA, integer (0..6000). */
- ma = (int)(current * 1000);
-
- sr_err("Setting current limit to %f A (%d mA).", current, ma);
+ /* Hardware expects current limit in mA, integer (0..6000). */
+ ma = (int)round(current_limit * 1000);
cmd = g_strdup_printf("set %d\n", ma);
- if ((ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf))) < 0) {
+ g_mutex_lock(&devc->acquisition_mutex);
+ ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf));
+ g_mutex_unlock(&devc->acquisition_mutex);
+ g_free(cmd);
+
+ if (ret < 0) {
sr_err("Error sending current limit command: %d.", ret);
- g_free(cmd);
return SR_ERR;
}
- g_free(cmd);
-
return SR_OK;
}
SR_PRIV int reloadpro_set_on_off(const struct sr_dev_inst *sdi, gboolean on)
{
+ struct dev_context *devc;
int ret;
char buf[100];
const char *cmd;
+ devc = sdi->priv;
+
cmd = (on) ? "on\n" : "off\n";
- if ((ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf))) < 0) {
+ g_mutex_lock(&devc->acquisition_mutex);
+ ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf));
+ g_mutex_unlock(&devc->acquisition_mutex);
+
+ if (ret < 0) {
sr_err("Error sending on/off command: %d.", ret);
return SR_ERR;
}
+ return SR_OK;
+}
+
+SR_PRIV int reloadpro_set_under_voltage_threshold(const struct sr_dev_inst *sdi,
+ float voltage)
+{
+ struct dev_context *devc;
+ int ret, mv;
+ char buf[100];
+ char *cmd;
+
+ devc = sdi->priv;
+
+ if (voltage < 0 || voltage > 60) {
+ sr_err("The under voltage threshold must be 0-60 V (was %f V).",
+ voltage);
+ return SR_ERR_ARG;
+ }
+
+ /* Hardware expects voltage in mV, integer (0..60000). */
+ mv = (int)round(voltage * 1000);
+
+ sr_spew("Setting under voltage threshold to %f V (%d mV).", voltage, mv);
+
+ cmd = g_strdup_printf("uvlo %d\n", mv);
+ g_mutex_lock(&devc->acquisition_mutex);
+ ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf));
+ g_mutex_unlock(&devc->acquisition_mutex);
+ g_free(cmd);
+ if (ret < 0) {
+ sr_err("Error sending under voltage threshold command: %d.", ret);
+ return SR_ERR;
+ }
return SR_OK;
}
SR_PRIV int reloadpro_get_current_limit(const struct sr_dev_inst *sdi,
- float *current)
+ float *current_limit)
{
+ struct dev_context *devc;
int ret;
char buf[100];
+ gint64 end_time;
+ devc = sdi->priv;
+
+ g_mutex_lock(&devc->acquisition_mutex);
if ((ret = send_cmd(sdi, "set\n", (char *)&buf, sizeof(buf))) < 0) {
sr_err("Error sending current limit query: %d.", ret);
return SR_ERR;
}
- /* Hardware sends current in mA, integer (0..6000). */
- *current = g_ascii_strtod(buf + 4, NULL) / 1000;
+ if (devc->acquisition_running) {
+ end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
+ if (!g_cond_wait_until(&devc->current_limit_cond,
+ &devc->acquisition_mutex, end_time)) {
+ /* Timeout has passed. */
+ g_mutex_unlock(&devc->acquisition_mutex);
+ return SR_ERR;
+ }
+ } else {
+ /* Hardware sends current limit in mA, integer (0..6000). */
+ devc->current_limit = g_ascii_strtod(buf + 4, NULL) / 1000;
+ }
+ g_mutex_unlock(&devc->acquisition_mutex);
+
+ if (current_limit)
+ *current_limit = devc->current_limit;
+
+ return SR_OK;
+}
+
+SR_PRIV int reloadpro_get_under_voltage_threshold(const struct sr_dev_inst *sdi,
+ float *uvc_threshold)
+{
+ struct dev_context *devc;
+ int ret;
+ char buf[100];
+ gint64 end_time;
+
+ devc = sdi->priv;
+
+ g_mutex_lock(&devc->acquisition_mutex);
+ if ((ret = send_cmd(sdi, "uvlo\n", (char *)&buf, sizeof(buf))) < 0) {
+ sr_err("Error sending under voltage threshold query: %d.", ret);
+ return SR_ERR;
+ }
+
+ if (devc->acquisition_running) {
+ end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
+ if (!g_cond_wait_until(&devc->uvc_threshold_cond,
+ &devc->acquisition_mutex, end_time)) {
+ /* Timeout has passed. */
+ g_mutex_unlock(&devc->acquisition_mutex);
+ return SR_ERR;
+ }
+ } else {
+ /* Hardware sends voltage in mV, integer (0..60000). */
+ devc->uvc_threshold = g_ascii_strtod(buf + 5, NULL) / 1000;
+ }
+ g_mutex_unlock(&devc->acquisition_mutex);
+
+ if (uvc_threshold)
+ *uvc_threshold = devc->uvc_threshold;
return SR_OK;
}
SR_PRIV int reloadpro_get_voltage_current(const struct sr_dev_inst *sdi,
float *voltage, float *current)
{
+ struct dev_context *devc;
int ret;
char buf[100];
char **tokens;
+ gint64 end_time;
+
+ devc = sdi->priv;
+ g_mutex_lock(&devc->acquisition_mutex);
if ((ret = send_cmd(sdi, "read\n", (char *)&buf, sizeof(buf))) < 0) {
sr_err("Error sending voltage/current query: %d.", ret);
return SR_ERR;
}
- /* Reply: "read <current> <voltage>". */
- tokens = g_strsplit((const char *)&buf, " ", 3);
+ if (devc->acquisition_running) {
+ end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
+ if (!g_cond_wait_until(&devc->voltage_cond,
+ &devc->acquisition_mutex, end_time)) {
+ /* Timeout has passed. */
+ g_mutex_unlock(&devc->acquisition_mutex);
+ return SR_ERR;
+ }
+ } else {
+ /* Reply: "read <current> <voltage>". */
+ tokens = g_strsplit((const char *)&buf, " ", 3);
+ devc->voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
+ devc->current = g_ascii_strtod(tokens[1], NULL) / 1000;
+ g_strfreev(tokens);
+ }
+ g_mutex_unlock(&devc->acquisition_mutex);
+
if (voltage)
- *voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
+ *voltage = devc->voltage;
if (current)
- *current = g_ascii_strtod(tokens[1], NULL) / 1000;
- g_strfreev(tokens);
+ *current = devc->current;
return SR_OK;
}
+static int send_config_update_key(const struct sr_dev_inst *sdi,
+ uint32_t key, GVariant *var)
+{
+ struct sr_config *cfg;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ int ret;
+
+ cfg = sr_config_new(key, var);
+ if (!cfg)
+ return SR_ERR;
+
+ memset(&meta, 0, sizeof(meta));
+
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+
+ meta.config = g_slist_append(meta.config, cfg);
+
+ ret = sr_session_send(sdi, &packet);
+ sr_config_free(cfg);
+
+ return ret;
+}
+
static void handle_packet(const struct sr_dev_inst *sdi)
{
- float voltage, current;
struct sr_datafeed_packet packet;
struct sr_datafeed_analog analog;
struct sr_analog_encoding encoding;
if (g_str_has_prefix((const char *)devc->buf, "overtemp")) {
sr_warn("Overtemperature condition!");
devc->otp_active = TRUE;
+ send_config_update_key(sdi, SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE,
+ g_variant_new_boolean(TRUE));
return;
}
if (g_str_has_prefix((const char *)devc->buf, "undervolt")) {
sr_warn("Undervoltage condition!");
devc->uvc_active = TRUE;
+ send_config_update_key(sdi, SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE,
+ g_variant_new_boolean(TRUE));
+ return;
+ }
+
+ if (g_str_has_prefix((const char *)devc->buf, "err ")) {
+ sr_err("Device replied with an error: '%s'.", devc->buf);
+ return;
+ }
+
+ if (g_str_has_prefix((const char *)devc->buf, "set ")) {
+ tokens = g_strsplit((const char *)devc->buf, " ", 2);
+ devc->current_limit = g_ascii_strtod(tokens[1], NULL) / 1000;
+ g_strfreev(tokens);
+ g_cond_signal(&devc->current_limit_cond);
+ send_config_update_key(sdi, SR_CONF_CURRENT_LIMIT,
+ g_variant_new_double(devc->current_limit));
+ return;
+ }
+
+ if (g_str_has_prefix((const char *)devc->buf, "uvlo ")) {
+ tokens = g_strsplit((const char *)devc->buf, " ", 2);
+ devc->uvc_threshold = g_ascii_strtod(tokens[1], NULL) / 1000;
+ g_strfreev(tokens);
+ g_cond_signal(&devc->uvc_threshold_cond);
+ if (devc->uvc_threshold == .0) {
+ send_config_update_key(sdi, SR_CONF_UNDER_VOLTAGE_CONDITION,
+ g_variant_new_boolean(FALSE));
+ } else {
+ send_config_update_key(sdi, SR_CONF_UNDER_VOLTAGE_CONDITION,
+ g_variant_new_boolean(TRUE));
+ send_config_update_key(sdi,
+ SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD,
+ g_variant_new_double(devc->uvc_threshold));
+ }
return;
}
}
tokens = g_strsplit((const char *)devc->buf, " ", 3);
- voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
- current = g_ascii_strtod(tokens[1], NULL) / 1000;
+ devc->voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
+ devc->current = g_ascii_strtod(tokens[1], NULL) / 1000;
g_strfreev(tokens);
/* Begin frame. */
meaning.mq = SR_MQ_VOLTAGE;
meaning.mqflags = SR_MQFLAG_DC;
meaning.unit = SR_UNIT_VOLT;
- analog.data = &voltage;
+ encoding.digits = 3;
+ analog.data = &devc->voltage;
sr_session_send(sdi, &packet);
g_slist_free(l);
meaning.mq = SR_MQ_CURRENT;
meaning.mqflags = SR_MQFLAG_DC;
meaning.unit = SR_UNIT_AMPERE;
- analog.data = ¤t;
+ encoding.digits = 3;
+ analog.data = &devc->current;
sr_session_send(sdi, &packet);
g_slist_free(l);
int len;
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
+ char *buf;
devc = sdi->priv;
serial = sdi->conn;
- /* Try to get as much data as the buffer can hold. */
len = RELOADPRO_BUFSIZE - devc->buflen;
- len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
- if (len == 0)
+ buf = devc->buf;
+ g_mutex_lock(&devc->acquisition_mutex);
+ if (serial_readline(serial, &buf, &len, 250) != SR_OK) {
+ g_mutex_unlock(&devc->acquisition_mutex);
+ return;
+ }
+
+ if (len == 0) {
+ g_mutex_unlock(&devc->acquisition_mutex);
return; /* No new bytes, nothing to do. */
+ }
if (len < 0) {
sr_err("Serial port read error: %d.", len);
+ g_mutex_unlock(&devc->acquisition_mutex);
return;
}
devc->buflen += len;
- if (g_str_has_suffix((const char *)devc->buf, "\n")) {
- handle_packet(sdi);
- memset(devc->buf, 0, RELOADPRO_BUFSIZE);
- devc->buflen = 0;
- }
+ handle_packet(sdi);
+ g_mutex_unlock(&devc->acquisition_mutex);
+ memset(devc->buf, 0, RELOADPRO_BUFSIZE);
+ devc->buflen = 0;
}
SR_PRIV int reloadpro_receive_data(int fd, int revents, void *cb_data)
handle_new_data(sdi);
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
-#define LOG_PREFIX "re-load-pro"
+#define LOG_PREFIX "arachnid-labs-re-load-pro"
#define RELOADPRO_BUFSIZE 100
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits limits;
- uint8_t buf[RELOADPRO_BUFSIZE];
+
+ char buf[RELOADPRO_BUFSIZE];
int buflen;
+
+ float current_limit;
+ float voltage;
+ float current;
gboolean otp_active;
gboolean uvc_active;
+ float uvc_threshold;
+
+ gboolean acquisition_running;
+ GMutex acquisition_mutex;
+
+ GCond current_limit_cond;
+ GCond voltage_cond;
+ GCond uvc_threshold_cond;
};
SR_PRIV int reloadpro_set_current_limit(const struct sr_dev_inst *sdi,
float current);
SR_PRIV int reloadpro_set_on_off(const struct sr_dev_inst *sdi, gboolean on);
+SR_PRIV int reloadpro_set_under_voltage_threshold(const struct sr_dev_inst *sdi,
+ float uvc_threshold);
SR_PRIV int reloadpro_get_current_limit(const struct sr_dev_inst *sdi,
- float *current);
+ float *current_limit);
+SR_PRIV int reloadpro_get_under_voltage_threshold(const struct sr_dev_inst *sdi,
+ float *uvc_threshold);
SR_PRIV int reloadpro_get_voltage_current(const struct sr_dev_inst *sdi,
float *voltage, float *current);
SR_PRIV int reloadpro_receive_data(int fd, int revents, void *cb_data);
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/*
- * ASIX SIGMA/SIGMA2 logic analyzer driver
- */
-
#include <config.h>
#include "protocol.h"
};
#endif
+static void clear_helper(struct dev_context *devc)
+{
+ ftdi_deinit(&devc->ftdic);
+}
+
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, sigma_clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static GSList *scan(struct sr_dev_driver *di, GSList *options)
ftdi_init(&devc->ftdic);
- /* Look for SIGMAs. */
-
if ((ret = ftdi_usb_find_all(&devc->ftdic, &devlist,
USB_VENDOR, USB_PRODUCT)) <= 0) {
if (ret < 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->vendor = g_strdup("ASIX");
+ sdi->model = g_strdup("SIGMA");
for (i = 0; i < ARRAY_SIZE(channel_names); i++)
sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_names[i]);
sdi->priv = devc;
- /* We will open the device again when we need it. */
ftdi_list_free(&devlist);
return std_scan_complete(di, g_slist_append(NULL, sdi));
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;
+ USB_VENDOR, USB_PRODUCT, USB_DESCRIPTION, NULL)) < 0) {
+ sr_err("Failed to open device (%d): %s.",
+ ret, ftdi_get_error_string(&devc->ftdic));
+ return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
-
return SR_OK;
}
devc = sdi->priv;
- /* TODO */
- if (sdi->status == SR_ST_ACTIVE)
- ftdi_usb_close(&devc->ftdic);
-
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
+ return (ftdi_usb_close(&devc->ftdic) == 0) ? SR_OK : SR_ERR;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
+ return sigma_set_samplerate(sdi, g_variant_get_uint64(data));
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;
+ devc->limit_msec = g_variant_get_uint64(data);
break;
case SR_CONF_LIMIT_SAMPLES:
- tmp = g_variant_get_uint64(data);
- devc->limit_samples = tmp;
- devc->limit_msec = sigma_limit_samples_to_msec(devc, tmp);
+ devc->limit_samples = g_variant_get_uint64(data);
+ devc->limit_msec = sigma_limit_samples_to_msec(devc,
+ devc->limit_samples);
break;
#if ASIX_SIGMA_WITH_TRIGGER
case SR_CONF_CAPTURE_RATIO:
- tmp = g_variant_get_uint64(data);
- if (tmp > 100)
- return SR_ERR;
- devc->capture_ratio = tmp;
+ devc->capture_ratio = g_variant_get_uint64(data);
break;
#endif
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- 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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
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(samplerates[0]));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_samplerates(samplerates, samplerates_count);
break;
#if ASIX_SIGMA_WITH_TRIGGER
case SR_CONF_TRIGGER_MATCH:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- trigger_matches, ARRAY_SIZE(trigger_matches),
- sizeof(int32_t));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
#endif
default:
uint8_t clock_bytes[sizeof(clockselect)];
size_t clock_idx;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
if (sigma_convert_trigger(sdi) != SR_OK) {
struct dev_context *devc;
devc = sdi->priv;
- devc->state.state = SIGMA_IDLE;
- sr_session_source_remove(sdi->session, -1);
+ /*
+ * When acquisition is currently running, keep the receive
+ * routine registered and have it stop the acquisition upon the
+ * next invocation. Else unregister the receive routine here
+ * already. The detour is required to have sample data retrieved
+ * for forced acquisition stops.
+ */
+ if (devc->state.state == SIGMA_CAPTURE) {
+ devc->state.state = SIGMA_STOPPING;
+ } else {
+ devc->state.state = SIGMA_IDLE;
+ sr_session_source_remove(sdi->session, -1);
+ }
return SR_OK;
}
SR_PRIV const size_t samplerates_count = ARRAY_SIZE(samplerates);
-static const char sigma_firmware_files[][24] = {
+static const char firmware_files[][24] = {
/* 50 MHz, supports 8 bit fractions */
"asix-sigma-50.fw",
/* 100 MHz */
int ret;
ret = ftdi_write_data(&devc->ftdic, (unsigned char *)buf, size);
- if (ret < 0) {
+ if (ret < 0)
sr_err("ftdi_write_data failed: %s",
ftdi_get_error_string(&devc->ftdic));
- } else if ((size_t) ret != size) {
+ else if ((size_t) ret != size)
sr_err("ftdi_write_data did not complete write.");
- }
return ret;
}
return SR_OK;
}
-SR_PRIV void sigma_clear_helper(void *priv)
-{
- struct dev_context *devc;
-
- devc = priv;
-
- ftdi_deinit(&devc->ftdic);
-}
-
/*
* Configure the FPGA for bitbang mode.
* This sequence is documented in section 2. of the ASIX Sigma programming
unsigned char pins;
size_t buf_size;
const char *firmware;
- struct ftdi_context *ftdic;
/* Avoid downloading the same firmware multiple times. */
- firmware = sigma_firmware_files[firmware_idx];
+ firmware = firmware_files[firmware_idx];
if (devc->cur_firmware == firmware_idx) {
sr_info("Not uploading firmware file '%s' again.", firmware);
return SR_OK;
}
- /* Make sure it's an ASIX SIGMA. */
- ftdic = &devc->ftdic;
- ret = ftdi_usb_open_desc(ftdic, USB_VENDOR, USB_PRODUCT,
- USB_DESCRIPTION, NULL);
- if (ret < 0) {
- sr_err("ftdi_usb_open failed: %s",
- ftdi_get_error_string(ftdic));
- return 0;
- }
-
- ret = ftdi_set_bitmode(ftdic, 0xdf, BITMODE_BITBANG);
+ ret = ftdi_set_bitmode(&devc->ftdic, 0xdf, BITMODE_BITBANG);
if (ret < 0) {
sr_err("ftdi_set_bitmode failed: %s",
- ftdi_get_error_string(ftdic));
- return 0;
+ ftdi_get_error_string(&devc->ftdic));
+ return SR_ERR;
}
/* Four times the speed of sigmalogan - Works well. */
- ret = ftdi_set_baudrate(ftdic, 750 * 1000);
+ ret = ftdi_set_baudrate(&devc->ftdic, 750 * 1000);
if (ret < 0) {
sr_err("ftdi_set_baudrate failed: %s",
- ftdi_get_error_string(ftdic));
- return 0;
+ ftdi_get_error_string(&devc->ftdic));
+ return SR_ERR;
}
/* Initialize the FPGA for firmware upload. */
g_free(buf);
- ret = ftdi_set_bitmode(ftdic, 0x00, BITMODE_RESET);
+ ret = ftdi_set_bitmode(&devc->ftdic, 0x00, BITMODE_RESET);
if (ret < 0) {
sr_err("ftdi_set_bitmode failed: %s",
- ftdi_get_error_string(ftdic));
+ ftdi_get_error_string(&devc->ftdic));
return SR_ERR;
}
- ftdi_usb_purge_buffers(ftdic);
+ ftdi_usb_purge_buffers(&devc->ftdic);
/* Discard garbage. */
while (sigma_read(&pins, 1, devc) == 1)
struct drv_context *drvc;
size_t i;
int ret;
+ int num_channels;
devc = sdi->priv;
drvc = sdi->driver->context;
* firmware is required and higher rates might limit the set
* of available channels.
*/
+ num_channels = devc->num_channels;
if (samplerate <= SR_MHZ(50)) {
ret = upload_firmware(drvc->sr_ctx, 0, devc);
- devc->num_channels = 16;
+ num_channels = 16;
} else if (samplerate == SR_MHZ(100)) {
ret = upload_firmware(drvc->sr_ctx, 1, devc);
- devc->num_channels = 8;
+ num_channels = 8;
} else if (samplerate == SR_MHZ(200)) {
ret = upload_firmware(drvc->sr_ctx, 2, devc);
- devc->num_channels = 4;
+ num_channels = 4;
}
/*
* an "event" (memory organization internal to the device).
*/
if (ret == SR_OK) {
+ devc->num_channels = num_channels;
devc->cur_samplerate = samplerate;
devc->samples_per_event = 16 / devc->num_channels;
devc->state.state = SIGMA_IDLE;
if (match->match == SR_TRIGGER_ONE) {
devc->trigger.simplevalue |= channelbit;
devc->trigger.simplemask |= channelbit;
- }
- else if (match->match == SR_TRIGGER_ZERO) {
+ } else if (match->match == SR_TRIGGER_ZERO) {
devc->trigger.simplevalue &= ~channelbit;
devc->trigger.simplemask |= channelbit;
- }
- else if (match->match == SR_TRIGGER_FALLING) {
+ } else if (match->match == SR_TRIGGER_FALLING) {
devc->trigger.fallingmask |= channelbit;
trigger_set++;
- }
- else if (match->match == SR_TRIGGER_RISING) {
+ } else if (match->match == SR_TRIGGER_RISING) {
devc->trigger.risingmask |= channelbit;
trigger_set++;
}
return SR_OK;
}
-
/* Software trigger to determine exact trigger position. */
static int get_trigger_offset(uint8_t *samples, uint16_t last_sample,
struct sigma_trigger *t)
return FALSE;
sr_info("Downloading sample data.");
+ devc->state.state = SIGMA_DOWNLOAD;
/*
* Ask the hardware to stop data acquisition. Reception of the
dl_lines_done += dl_lines_curr;
}
+ g_free(dram_line);
std_session_send_df_end(sdi);
- sdi->driver->dev_acquisition_stop(sdi);
-
- g_free(dram_line);
+ devc->state.state = SIGMA_IDLE;
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
if (devc->state.state == SIGMA_IDLE)
return TRUE;
+ /*
+ * When the application has requested to stop the acquisition,
+ * then immediately start downloading sample data. Otherwise
+ * keep checking configured limits which will terminate the
+ * acquisition and initiate download.
+ */
+ if (devc->state.state == SIGMA_STOPPING)
+ return download_capture(sdi);
if (devc->state.state == SIGMA_CAPTURE)
return sigma_capture_mode(sdi);
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
+#define LOG_PREFIX "asix-sigma"
+
/*
* Triggers are not working in this implementation. Stop claiming
* support for the feature which effectively is not available, until
*/
#define ASIX_SIGMA_WITH_TRIGGER 0
-#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,
SIGMA_UNINITIALIZED = 0,
SIGMA_IDLE,
SIGMA_CAPTURE,
+ SIGMA_STOPPING,
SIGMA_DOWNLOAD,
} state;
-
uint16_t lastts;
uint16_t lastsample;
};
-/* Private, per-device-instance driver context. */
struct dev_context {
struct ftdi_context ftdic;
uint64_t cur_samplerate;
int num_channels;
int cur_channels;
int samples_per_event;
- int capture_ratio;
+ uint64_t capture_ratio;
struct sigma_trigger trigger;
int use_triggers;
struct sigma_state state;
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 uint64_t sigma_limit_samples_to_msec(const struct dev_context *devc,
uint64_t limit_samples);
SR_PRIV int sigma_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
return scan(di, options, PPS_3203T_3S);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
struct sr_channel *ch;
- int channel, ret;
+ int channel;
if (!sdi)
return SR_ERR_ARG;
devc = sdi->priv;
- ret = SR_OK;
if (!cg) {
- /* No channel group: global options. */
switch (key) {
case SR_CONF_CHANNEL_CONFIG:
*data = g_variant_new_string(channel_modes[devc->channel_mode]);
}
}
- return ret;
-}
-
-static int find_str(const char *str, const char **strings, int array_size)
-{
- int idx, i;
-
- idx = -1;
- for (i = 0; i < array_size; i++) {
- if (!strcmp(str, strings[i])) {
- idx = i;
- break;
- }
- }
-
- return idx;
+ return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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 channel, ret, ival;
- const char *sval;
+ int channel, ival;
gboolean bval;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- ret = SR_OK;
devc = sdi->priv;
+
if (!cg) {
- /* No channel group: global options. */
switch (key) {
case SR_CONF_CHANNEL_CONFIG:
- sval = g_variant_get_string(data, NULL);
- if ((ival = find_str(sval, channel_modes,
- ARRAY_SIZE(channel_modes))) == -1) {
- ret = SR_ERR_ARG;
- break;
- }
- if (devc->model->channel_modes && (1 << ival) == 0) {
- /* Not supported on this model. */
- ret = SR_ERR_ARG;
- }
+ if ((ival = std_str_idx(data, ARRAY_AND_SIZE(channel_modes))) < 0)
+ return SR_ERR_ARG;
+ if (devc->model->channel_modes && (1 << ival) == 0)
+ return SR_ERR_ARG; /* Not supported on this model. */
if (ival == devc->channel_mode_set)
- /* Nothing to do. */
- break;
+ break; /* Nothing to do. */
devc->channel_mode_set = ival;
devc->config_dirty = TRUE;
break;
case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
bval = g_variant_get_boolean(data);
if (bval == devc->over_current_protection_set)
- /* Nothing to do. */
- break;
+ break; /* Nothing to do. */
devc->over_current_protection_set = bval;
devc->config_dirty = TRUE;
break;
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;
channel = ch->index;
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;
+ return SR_ERR_ARG;
devc->config[channel].output_voltage_max = dval;
devc->config_dirty = TRUE;
break;
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;
+ return SR_ERR_ARG;
devc->config[channel].output_current_max = dval;
devc->config_dirty = TRUE;
break;
case SR_CONF_ENABLED:
bval = g_variant_get_boolean(data);
if (bval == devc->config[channel].output_enabled_set)
- /* Nothing to do. */
- break;
+ break; /* Nothing to do. */
devc->config[channel].output_enabled_set = bval;
devc->config_dirty = TRUE;
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
struct dev_context *devc;
struct sr_channel *ch;
- GVariant *gvar;
- GVariantBuilder gvb;
- int channel, ret, i;
-
- /* Always available. */
- 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;
- }
+ int channel;
- 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;
- }
+ devc = (sdi) ? sdi->priv : NULL;
- if (!sdi)
- return SR_ERR_ARG;
-
- devc = sdi->priv;
- ret = SR_OK;
if (!cg) {
- /* No channel group: global options. */
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_CHANNEL_CONFIG:
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
if (devc->model->channel_modes == CHANMODE_INDEPENDENT) {
/* 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));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(channel_modes));
}
break;
default:
return SR_ERR_NA;
}
} else {
- /* Channel group specified: per-channel options. */
- if (!sdi)
- return SR_ERR_ARG;
/* We only ever have one channel per channel group in this driver. */
ch = cg->channels->data;
channel = ch->index;
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));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
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[channel].voltage[i]);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ *data = std_gvar_min_max_step_array(devc->model->channels[channel].voltage);
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[channel].current[i]);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ *data = std_gvar_min_max_step_array(devc->model->channels[channel].current);
break;
default:
return SR_ERR_NA;
}
}
- return ret;
+ return SR_OK;
}
static int dev_close(struct sr_dev_inst *sdi)
struct dev_context *devc;
devc = sdi->priv;
+
if (devc->config_dirty)
/* Some configuration changes were queued up but didn't
* get sent to the device, likely because we were never
struct sr_serial_dev_inst *serial;
uint8_t packet[PACKET_SIZE];
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
memset(devc->packet, 0x44, PACKET_SIZE);
devc->packet_size = 0;
{
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
devc->acquisition_running = FALSE;
.cleanup = std_cleanup,
.scan = scan_3203,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
gboolean output_enabled_set;
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
const struct pps_model *model;
- /* Acquisition state */
gboolean acquisition_running;
- /* Operational state */
gboolean config_dirty;
struct per_channel_config *config;
/* Blocking write timeout for packet. */
int channel_mode_set;
gboolean over_current_protection_set;
- /* Temporary state across callbacks */
uint8_t packet[PACKET_SIZE];
int packet_size;
#include <time.h>
#include <sys/timerfd.h>
+static const uint32_t drvopts[] = {
+ SR_CONF_THERMOMETER,
+ SR_CONF_POWERMETER,
+};
+
static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
return NULL;
}
-static int dev_open(struct sr_dev_inst *sdi)
-{
- (void)sdi;
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- (void)sdi;
-
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
static int config_get(uint32_t key, GVariant **data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
int ret;
devc = sdi->priv;
ret = SR_OK;
+
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
- ret = sr_sw_limits_config_get(&devc->limits, key, data);
- break;
+ return sr_sw_limits_config_get(&devc->limits, key, data);
case SR_CONF_SAMPLERATE:
*data = g_variant_new_uint64(devc->samplerate);
break;
}
static int config_set(uint32_t key, GVariant *data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
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:
case SR_CONF_LIMIT_MSEC:
- ret = sr_sw_limits_config_set(&devc->limits, key, data);
- break;
+ return sr_sw_limits_config_set(&devc->limits, key, data);
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;
+ return SR_ERR_SAMPLERATE;
}
devc->samplerate = samplerate;
bl_acme_maybe_set_update_interval(sdi, samplerate);
case SR_CONF_PROBE_FACTOR:
if (!cg)
return SR_ERR_CHANNEL_GROUP;
- ret = bl_acme_set_shunt(cg, g_variant_get_uint64(data));
- break;
+ return bl_acme_set_shunt(cg, g_variant_get_uint64(data));
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;
+ return bl_acme_set_power_off(cg, g_variant_get_boolean(data));
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static int config_list(uint32_t key, GVariant **data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
uint32_t devopts_cg[MAX_DEVOPTS_CG];
- GVariant *gvar;
- GVariantBuilder gvb;
- int ret, num_devopts_cg = 0;
-
- (void)sdi;
- (void)cg;
+ int num_devopts_cg = 0;
- 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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
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);
+ *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
break;
default:
return SR_ERR_NA;
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));
+ *data = std_gvar_array_u32(devopts_cg, num_devopts_cg);
break;
default:
return SR_ERR_NA;
}
}
- return ret;
+ return SR_OK;
}
static void dev_acquisition_close(const struct sr_dev_inst *sdi)
.it_value = { 0, 0 }
};
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
if (dev_acquisition_open(sdi))
return SR_ERR;
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);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
- .dev_open = dev_open,
- .dev_close = dev_close,
+ .dev_open = std_dummy_dev_open,
+ .dev_close = std_dummy_dev_close,
.dev_acquisition_start = dev_acquisition_start,
.dev_acquisition_stop = dev_acquisition_stop,
.context = NULL,
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);
+ /* Don't log "No such file or directory" messages. */
+ if (err->code != G_FILE_ERROR_NOENT)
+ sr_dbg("Name for probe %d can't be read (%d): %s",
+ prb_num, err->code, err->message);
g_string_free(path, TRUE);
g_error_free(err);
return ret;
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;
+ case 0x44: return 5;
+ case 0x45: return 6;
+ case 0x42: return 3;
+ case 0x43: return 4;
+ default: return addr - 0x3f;
}
}
sr_sw_limits_update_samples_read(&devc->limits, 1);
if (sr_sw_limits_check(&devc->limits)) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
PROBE_TEMP,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
uint64_t samplerate;
struct sr_sw_limits limits;
/*
* This file is part of the libsigrok project.
*
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Copyright (C) 2014-2017 Kumar Abhishek <abhishek@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
#include "protocol.h"
#include "beaglelogic.h"
-/* Scan options */
static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
SR_CONF_NUM_LOGIC_CHANNELS,
};
-/* Hardware capabilities */
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
- SR_CONF_SAMPLERATE | 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_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[] = {
+static const int32_t trigger_matches[] = {
SR_TRIGGER_ZERO,
SR_TRIGGER_ONE,
SR_TRIGGER_RISING,
SR_HZ(1),
};
-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)
{
GSList *l;
struct sr_config *src;
struct sr_dev_inst *sdi;
struct dev_context *devc;
+ const char *conn;
+ gchar **params;
int i, maxch;
- /* 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");
-
- /* Unless explicitly specified, keep max channels to 8 only */
- maxch = 8;
+ maxch = NUM_CHANNELS;
+ conn = NULL;
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);
+ if (src->key == SR_CONF_CONN)
+ conn = g_variant_get_string(src->data, NULL);
}
- /* We need to test for number of channels by opening the node */
- devc = beaglelogic_devc_alloc();
+ /* Probe for /dev/beaglelogic if not connecting via TCP */
+ if (!conn) {
+ params = NULL;
+ if (!g_file_test(BEAGLELOGIC_DEV_NODE, G_FILE_TEST_EXISTS))
+ return NULL;
+ } else {
+ params = g_strsplit(conn, "/", 0);
+ if (!params || !params[1] || !params[2]) {
+ sr_err("Invalid Parameters.");
+ g_strfreev(params);
+ return NULL;
+ }
+ if (g_ascii_strncasecmp(params[0], "tcp", 3)) {
+ sr_err("Only TCP (tcp-raw) protocol is currently supported.");
+ g_strfreev(params);
+ return NULL;
+ }
+ }
- if (beaglelogic_open_nonblock(devc) != SR_OK) {
- g_free(devc);
- sr_dev_inst_free(sdi);
+ maxch = (maxch > 8) ? NUM_CHANNELS : 8;
- return NULL;
- }
+ sdi = g_new0(struct sr_dev_inst, 1);
+ sdi->status = SR_ST_INACTIVE;
+ sdi->model = g_strdup("BeagleLogic");
+ sdi->version = g_strdup("1.0");
- if (maxch > 8) {
- maxch = NUM_CHANNELS;
- devc->sampleunit = BL_SAMPLEUNIT_16_BITS;
- } else {
- maxch = 8;
- devc->sampleunit = BL_SAMPLEUNIT_8_BITS;
- }
+ devc = g_malloc0(sizeof(struct dev_context));
- beaglelogic_set_sampleunit(devc);
- beaglelogic_close(devc);
+ /* Default non-zero values (if any) */
+ devc->fd = -1;
+ devc->limit_samples = 10000000;
+ devc->tcp_buffer = 0;
- /* Signal */
- sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE);
+ if (!conn) {
+ devc->beaglelogic = &beaglelogic_native_ops;
+ sr_info("BeagleLogic device found at "BEAGLELOGIC_DEV_NODE);
+ } else {
+ devc->read_timeout = 1000 * 1000;
+ devc->beaglelogic = &beaglelogic_tcp_ops;
+ devc->address = g_strdup(params[1]);
+ devc->port = g_strdup(params[2]);
+ g_strfreev(params);
+
+ if (devc->beaglelogic->open(devc) != SR_OK)
+ goto err_free;
+ if (beaglelogic_tcp_detect(devc) != SR_OK)
+ goto err_free;
+ if (devc->beaglelogic->close(devc) != SR_OK)
+ goto err_free;
+ sr_info("BeagleLogic device found at %s : %s",
+ devc->address, devc->port);
+ }
/* Fill the channels */
for (i = 0; i < maxch; i++)
sdi->priv = devc;
return std_scan_complete(di, g_slist_append(NULL, sdi));
+
+err_free:
+ g_free(sdi->model);
+ g_free(sdi->version);
+ g_free(devc->address);
+ g_free(devc->port);
+ g_free(devc);
+ g_free(sdi);
+
+ return NULL;
}
static int dev_open(struct sr_dev_inst *sdi)
struct dev_context *devc = sdi->priv;
/* Open BeagleLogic */
- if (beaglelogic_open_nonblock(devc))
+ if (devc->beaglelogic->open(devc))
return SR_ERR;
/* Set fd and local attributes */
- devc->pollfd.fd = devc->fd;
+ if (devc->beaglelogic == &beaglelogic_tcp_ops)
+ devc->pollfd.fd = devc->socket;
+ else
+ 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);
+ devc->beaglelogic->get_samplerate(devc);
+ devc->beaglelogic->get_sampleunit(devc);
+ devc->beaglelogic->get_buffersize(devc);
+ devc->beaglelogic->get_bufunitsize(devc);
+
+ /* Set the triggerflags to default for continuous capture unless we
+ * explicitly limit samples using SR_CONF_LIMIT_SAMPLES */
+ devc->triggerflags = BL_TRIGGERFLAGS_CONTINUOUS;
+ devc->beaglelogic->set_triggerflags(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;
+ if (devc->beaglelogic == &beaglelogic_native_ops) {
+ if (devc->beaglelogic->mmap(devc) != SR_OK) {
+ sr_err("Unable to map capture buffer");
+ devc->beaglelogic->close(devc);
+ return SR_ERR;
+ }
+ } else {
+ devc->tcp_buffer = g_malloc(TCP_BUFFER_SIZE);
}
- /* We're good to go now */
- sdi->status = SR_ST_ACTIVE;
return SR_OK;
}
{
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;
+ /* Close the memory mapping and the file */
+ if (devc->beaglelogic == &beaglelogic_native_ops)
+ devc->beaglelogic->munmap(devc);
+ devc->beaglelogic->close(devc);
+
return SR_OK;
}
-static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+static void clear_helper(struct dev_context *devc)
+{
+ g_free(devc->tcp_buffer);
+ g_free(devc->address);
+ g_free(devc->port);
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)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 = sdi->priv;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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);
+ return devc->beaglelogic->set_samplerate(devc);
case SR_CONF_LIMIT_SAMPLES:
tmp_u64 = g_variant_get_uint64(data);
devc->limit_samples = tmp_u64;
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"\
+ "\' 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);
+ return devc->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;
+ 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)
+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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
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));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
default:
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
/* get a sane timeout for poll() */
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
{
struct dev_context *devc = sdi->priv;
+ GSList *l;
struct sr_trigger *trigger;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
+ struct sr_channel *channel;
/* 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);
+ devc->sampleunit = BL_SAMPLEUNIT_8_BITS;
+
+ for (l = sdi->channels; l; l = l->next) {
+ channel = l->data;
+ if (channel->index >= 8 && channel->enabled)
+ devc->sampleunit = BL_SAMPLEUNIT_16_BITS;
+ }
+ devc->beaglelogic->set_sampleunit(devc);
+
+ /* If continuous sampling, set the limit_samples to max possible value */
+ if (devc->triggerflags == BL_TRIGGERFLAGS_CONTINUOUS)
+ devc->limit_samples = UINT64_MAX;
/* 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;
+ 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;
std_session_send_df_header(sdi);
/* 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,
+ devc->beaglelogic->start(devc);
+ if (devc->beaglelogic == &beaglelogic_native_ops)
+ sr_session_source_add_pollfd(sdi->session, &devc->pollfd,
+ BUFUNIT_TIMEOUT_MS(devc), beaglelogic_native_receive_data,
+ (void *)sdi);
+ else
+ sr_session_source_add_pollfd(sdi->session, &devc->pollfd,
+ BUFUNIT_TIMEOUT_MS(devc), beaglelogic_tcp_receive_data,
(void *)sdi);
return SR_OK;
{
struct dev_context *devc = sdi->priv;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
/* Execute a stop on BeagleLogic */
- beaglelogic_stop(devc);
+ devc->beaglelogic->stop(devc);
- /* lseek to offset 0, flushes the cache */
- lseek(devc->fd, 0, SEEK_SET);
+ /* Flush the cache */
+ if (devc->beaglelogic == &beaglelogic_native_ops)
+ lseek(devc->fd, 0, SEEK_SET);
+ else
+ beaglelogic_tcp_drain(devc);
/* Remove session source and send EOT packet */
sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
/*
* This file is part of the libsigrok project.
*
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Copyright (C) 2014-2017 Kumar Abhishek <abhishek@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
#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_GET_CUR_INDEX _IOR('k', 0x24, 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_SET_BUFUNIT_SIZE _IOW('k', 0x27, uint32_t)
#define IOCTL_BL_FILL_TEST_PATTERN _IO('k', 0x28)
* SR_OK or SR_ERR
*/
-SR_PRIV int beaglelogic_open_nonblock(struct dev_context *devc);
-SR_PRIV int beaglelogic_close(struct dev_context *devc);
+struct beaglelogic_ops {
+ int (*open)(struct dev_context *devc);
+ int (*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);
+ int (*get_buffersize)(struct dev_context *devc);
+ int (*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);
+ int (*get_samplerate)(struct dev_context *devc);
+ int (*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);
+ int (*get_sampleunit)(struct dev_context *devc);
+ int (*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);
+ int (*get_triggerflags)(struct dev_context *devc);
+ int (*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);
+ /* Start and stop the capture operation */
+ int (*start)(struct dev_context *devc);
+ int (*stop)(struct dev_context *devc);
-/* Get the last error size */
-SR_PRIV int beaglelogic_getlasterror(struct dev_context *devc);
+ /* Get the last error size */
+ int (*get_lasterror)(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);
+ /* Gets the unit size of the capture buffer (usually 4 or 8 MB) */
+ int (*get_bufunitsize)(struct dev_context *devc);
+ int (*set_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);
-}
+ int (*mmap)(struct dev_context *devc);
+ int (*munmap)(struct dev_context *devc);
+};
-SR_PRIV inline int beaglelogic_stop(struct dev_context *devc) {
- return ioctl(devc->fd, IOCTL_BL_STOP);
-}
+SR_PRIV extern const struct beaglelogic_ops beaglelogic_native_ops;
+SR_PRIV extern const struct beaglelogic_ops beaglelogic_tcp_ops;
-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);
-}
+SR_PRIV int beaglelogic_tcp_detect(struct dev_context *devc);
+SR_PRIV int beaglelogic_tcp_drain(struct dev_context *devc);
#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2014-2017 Kumar Abhishek <abhishek@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 "protocol.h"
+#include "beaglelogic.h"
+
+static 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);
+}
+
+static int beaglelogic_close(struct dev_context *devc)
+{
+ return close(devc->fd);
+}
+
+static int beaglelogic_get_buffersize(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_GET_BUFFER_SIZE, &devc->buffersize);
+}
+
+static 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 */
+static 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;
+}
+
+static int beaglelogic_set_samplerate(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_RATE,
+ (uint32_t)devc->cur_samplerate);
+}
+
+static int beaglelogic_get_sampleunit(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_GET_SAMPLE_UNIT, &devc->sampleunit);
+}
+
+static int beaglelogic_set_sampleunit(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_SET_SAMPLE_UNIT, devc->sampleunit);
+}
+
+static int beaglelogic_get_triggerflags(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_GET_TRIGGER_FLAGS, &devc->triggerflags);
+}
+
+static int beaglelogic_set_triggerflags(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_SET_TRIGGER_FLAGS, devc->triggerflags);
+}
+
+static int beaglelogic_get_lasterror(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;
+}
+
+static int beaglelogic_start(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_START);
+}
+
+static int beaglelogic_stop(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_STOP);
+}
+
+static int beaglelogic_get_bufunitsize(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_GET_BUFUNIT_SIZE, &devc->bufunitsize);
+}
+
+static int beaglelogic_set_bufunitsize(struct dev_context *devc)
+{
+ return ioctl(devc->fd, IOCTL_BL_SET_BUFUNIT_SIZE, devc->bufunitsize);
+}
+
+static 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);
+}
+
+static int beaglelogic_munmap(struct dev_context *devc)
+{
+ return munmap(devc->sample_buf, devc->buffersize);
+}
+
+SR_PRIV const struct beaglelogic_ops beaglelogic_native_ops = {
+ .open = beaglelogic_open_nonblock,
+ .close = beaglelogic_close,
+ .get_buffersize = beaglelogic_get_buffersize,
+ .set_buffersize = beaglelogic_set_buffersize,
+ .get_samplerate = beaglelogic_get_samplerate,
+ .set_samplerate = beaglelogic_set_samplerate,
+ .get_sampleunit = beaglelogic_get_sampleunit,
+ .set_sampleunit = beaglelogic_set_sampleunit,
+ .get_triggerflags = beaglelogic_get_triggerflags,
+ .set_triggerflags = beaglelogic_set_triggerflags,
+ .start = beaglelogic_start,
+ .stop = beaglelogic_stop,
+ .get_lasterror = beaglelogic_get_lasterror,
+ .get_bufunitsize = beaglelogic_get_bufunitsize,
+ .set_bufunitsize = beaglelogic_set_bufunitsize,
+ .mmap = beaglelogic_mmap,
+ .munmap = beaglelogic_munmap,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Portions of the code are adapted from scpi_tcp.c and scpi.c, their
+ * copyright notices are listed below:
+ *
+ * Copyright (C) 2013 Martin Ling <martin-sigrok@earth.li>
+ * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@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>
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#include <errno.h>
+
+#include "protocol.h"
+#include "beaglelogic.h"
+
+static int beaglelogic_tcp_open(struct dev_context *devc)
+{
+ struct addrinfo hints;
+ struct addrinfo *results, *res;
+ int err;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ err = getaddrinfo(devc->address, devc->port, &hints, &results);
+
+ if (err) {
+ sr_err("Address lookup failed: %s:%s: %s", devc->address,
+ devc->port, gai_strerror(err));
+ return SR_ERR;
+ }
+
+ for (res = results; res; res = res->ai_next) {
+ if ((devc->socket = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ continue;
+ if (connect(devc->socket, res->ai_addr, res->ai_addrlen) != 0) {
+ close(devc->socket);
+ devc->socket = -1;
+ continue;
+ }
+ break;
+ }
+
+ freeaddrinfo(results);
+
+ if (devc->socket < 0) {
+ sr_err("Failed to connect to %s:%s: %s", devc->address,
+ devc->port, g_strerror(errno));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int beaglelogic_tcp_send_cmd(struct dev_context *devc,
+ const char *format, ...)
+{
+ int len, out;
+ va_list args, args_copy;
+ char *buf;
+
+ va_start(args, format);
+ va_copy(args_copy, args);
+ len = vsnprintf(NULL, 0, format, args_copy);
+ va_end(args_copy);
+
+ buf = g_malloc0(len + 2);
+ vsprintf(buf, format, args);
+ va_end(args);
+
+ if (buf[len - 1] != '\n')
+ buf[len] = '\n';
+
+ out = send(devc->socket, buf, strlen(buf), 0);
+
+ if (out < 0) {
+ sr_err("Send error: %s", g_strerror(errno));
+ g_free(buf);
+ return SR_ERR;
+ }
+
+ if (out < (int)strlen(buf)) {
+ sr_dbg("Only sent %d/%zu bytes of command: '%s'.", out,
+ strlen(buf), buf);
+ }
+
+ sr_spew("Sent command: '%s'.", buf);
+
+ g_free(buf);
+
+ return SR_OK;
+}
+
+static int beaglelogic_tcp_read_data(struct dev_context *devc, char *buf,
+ int maxlen)
+{
+ int len;
+
+ len = recv(devc->socket, buf, maxlen, 0);
+
+ if (len < 0) {
+ sr_err("Receive error: %s", g_strerror(errno));
+ return SR_ERR;
+ }
+
+ return len;
+}
+
+SR_PRIV int beaglelogic_tcp_drain(struct dev_context *devc)
+{
+ char *buf = g_malloc(1024);
+ fd_set rset;
+ int ret, len = 0;
+ struct timeval tv;
+
+ FD_ZERO(&rset);
+ FD_SET(devc->socket, &rset);
+
+ /* 25ms timeout */
+ tv.tv_sec = 0;
+ tv.tv_usec = 25 * 1000;
+
+ do {
+ ret = select(devc->socket + 1, &rset, NULL, NULL, &tv);
+ if (ret > 0)
+ len += beaglelogic_tcp_read_data(devc, buf, 1024);
+ } while (ret > 0);
+
+ sr_spew("Drained %d bytes of data.", len);
+
+ g_free(buf);
+
+ return SR_OK;
+}
+
+static int beaglelogic_tcp_get_string(struct dev_context *devc, const char *cmd,
+ char **tcp_resp)
+{
+ GString *response = g_string_sized_new(1024);
+ int len;
+ gint64 timeout;
+
+ *tcp_resp = NULL;
+ if (cmd) {
+ if (beaglelogic_tcp_send_cmd(devc, cmd) != SR_OK)
+ return SR_ERR;
+ }
+
+ timeout = g_get_monotonic_time() + devc->read_timeout;
+ len = beaglelogic_tcp_read_data(devc, response->str,
+ response->allocated_len);
+
+ if (len < 0) {
+ g_string_free(response, TRUE);
+ return SR_ERR;
+ }
+
+ if (len > 0)
+ g_string_set_size(response, len);
+
+ if (g_get_monotonic_time() > timeout) {
+ sr_err("Timed out waiting for response.");
+ g_string_free(response, TRUE);
+ return SR_ERR_TIMEOUT;
+ }
+
+ /* Remove trailing newline if present */
+ if (response->len >= 1 && response->str[response->len - 1] == '\n')
+ g_string_truncate(response, response->len - 1);
+
+ /* Remove 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);
+
+ *tcp_resp = g_string_free(response, FALSE);
+
+ return SR_OK;
+}
+
+static int beaglelogic_tcp_get_int(struct dev_context *devc,
+ const char *cmd, int *response)
+{
+ int ret;
+ char *resp = NULL;
+
+ ret = beaglelogic_tcp_get_string(devc, cmd, &resp);
+ if (!resp && ret != SR_OK)
+ return ret;
+
+ if (sr_atoi(resp, response) == SR_OK)
+ ret = SR_OK;
+ else
+ ret = SR_ERR_DATA;
+
+ g_free(resp);
+
+ return ret;
+}
+
+SR_PRIV int beaglelogic_tcp_detect(struct dev_context *devc)
+{
+ char *resp = NULL;
+ int ret;
+
+ ret = beaglelogic_tcp_get_string(devc, "version", &resp);
+ if (ret == SR_OK && !g_ascii_strncasecmp(resp, "BeagleLogic", 11))
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(resp);
+
+ return ret;
+}
+
+static int beaglelogic_open(struct dev_context *devc)
+{
+ return beaglelogic_tcp_open(devc);
+}
+
+static int beaglelogic_close(struct dev_context *devc)
+{
+ if (close(devc->socket) < 0)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int beaglelogic_get_buffersize(struct dev_context *devc)
+{
+ return beaglelogic_tcp_get_int(devc, "memalloc",
+ (int *)&devc->buffersize);
+}
+
+static int beaglelogic_set_buffersize(struct dev_context *devc)
+{
+ int ret;
+ char *resp;
+
+ beaglelogic_tcp_send_cmd(devc, "memalloc %lu", devc->buffersize);
+ ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+ if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(resp);
+
+ return ret;
+}
+
+static int beaglelogic_get_samplerate(struct dev_context *devc)
+{
+ int arg, err;
+
+ err = beaglelogic_tcp_get_int(devc, "samplerate", &arg);
+ if (err)
+ return err;
+
+ devc->cur_samplerate = arg;
+ return SR_OK;
+}
+
+static int beaglelogic_set_samplerate(struct dev_context *devc)
+{
+ int ret;
+ char *resp;
+
+ beaglelogic_tcp_send_cmd(devc, "samplerate %lu",
+ (uint32_t)devc->cur_samplerate);
+ ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+ if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(resp);
+
+ return ret;
+}
+
+static int beaglelogic_get_sampleunit(struct dev_context *devc)
+{
+ return beaglelogic_tcp_get_int(devc, "sampleunit",
+ (int *)&devc->sampleunit);
+}
+
+static int beaglelogic_set_sampleunit(struct dev_context *devc)
+{
+ int ret;
+ char *resp;
+
+ beaglelogic_tcp_send_cmd(devc, "sampleunit %lu", devc->sampleunit);
+ ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+ if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(resp);
+
+ return ret;
+}
+
+static int beaglelogic_get_triggerflags(struct dev_context *devc)
+{
+ return beaglelogic_tcp_get_int(devc, "triggerflags",
+ (int *)&devc->triggerflags);
+}
+
+static int beaglelogic_set_triggerflags(struct dev_context *devc)
+{
+ int ret;
+ char *resp;
+
+ beaglelogic_tcp_send_cmd(devc, "triggerflags %lu", devc->triggerflags);
+ ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+ if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(resp);
+
+ return ret;
+}
+
+static int beaglelogic_get_lasterror(struct dev_context *devc)
+{
+ devc->last_error = 0;
+
+ return SR_OK;
+}
+
+static int beaglelogic_start(struct dev_context *devc)
+{
+ beaglelogic_tcp_drain(devc);
+
+ return beaglelogic_tcp_send_cmd(devc, "get");
+}
+
+static int beaglelogic_stop(struct dev_context *devc)
+{
+ return beaglelogic_tcp_send_cmd(devc, "close");
+}
+
+static int beaglelogic_get_bufunitsize(struct dev_context *devc)
+{
+ return beaglelogic_tcp_get_int(devc, "bufunitsize",
+ (int *)&devc->bufunitsize);
+}
+
+static int beaglelogic_set_bufunitsize(struct dev_context *devc)
+{
+ int ret;
+ char *resp;
+
+ beaglelogic_tcp_send_cmd(devc, "bufunitsize %ld", devc->bufunitsize);
+ ret = beaglelogic_tcp_get_string(devc, NULL, &resp);
+ if (ret == SR_OK && !g_ascii_strncasecmp(resp, "ok", 2))
+ ret = SR_OK;
+ else
+ ret = SR_ERR;
+
+ g_free(resp);
+
+ return ret;
+}
+
+static int dummy(struct dev_context *devc)
+{
+ (void)devc;
+
+ return SR_ERR_NA;
+}
+
+SR_PRIV const struct beaglelogic_ops beaglelogic_tcp_ops = {
+ .open = beaglelogic_open,
+ .close = beaglelogic_close,
+ .get_buffersize = beaglelogic_get_buffersize,
+ .set_buffersize = beaglelogic_set_buffersize,
+ .get_samplerate = beaglelogic_get_samplerate,
+ .set_samplerate = beaglelogic_set_samplerate,
+ .get_sampleunit = beaglelogic_get_sampleunit,
+ .set_sampleunit = beaglelogic_set_sampleunit,
+ .get_triggerflags = beaglelogic_get_triggerflags,
+ .set_triggerflags = beaglelogic_set_triggerflags,
+ .start = beaglelogic_start,
+ .stop = beaglelogic_stop,
+ .get_lasterror = beaglelogic_get_lasterror,
+ .get_bufunitsize = beaglelogic_get_bufunitsize,
+ .set_bufunitsize = beaglelogic_set_bufunitsize,
+ .mmap = dummy,
+ .munmap = dummy,
+};
/*
* This file is part of the libsigrok project.
*
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Copyright (C) 2014-2017 Kumar Abhishek <abhishek@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
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#endif
+#include <errno.h>
#include "protocol.h"
+#include "beaglelogic.h"
/* Define data packet size independent of packet (bufunitsize bytes) size
* from the BeagleLogic kernel module */
* 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)
+SR_PRIV int beaglelogic_native_receive_data(int fd, int revents, void *cb_data)
{
const struct sr_dev_inst *sdi;
struct dev_context *devc;
if ((devc->offset += packetsize) >= devc->buffersize) {
/* One shot capture, we abort and settle with less than
* the required number of samples */
- if (devc->triggerflags)
+ if (devc->triggerflags == BL_TRIGGERFLAGS_CONTINUOUS)
devc->offset = 0;
else
packetsize = 0;
return TRUE;
}
+
+SR_PRIV int beaglelogic_tcp_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 len;
+ int pre_trigger_samples;
+ int trigger_offset;
+ uint32_t packetsize;
+ uint64_t bytes_remaining;
+
+ if (!(sdi = cb_data) || !(devc = sdi->priv))
+ return TRUE;
+
+ packetsize = TCP_BUFFER_SIZE;
+ logic.unitsize = SAMPLEUNIT_TO_BYTES(devc->sampleunit);
+
+ if (revents == G_IO_IN) {
+ sr_info("In callback G_IO_IN");
+
+ len = recv(fd, devc->tcp_buffer, TCP_BUFFER_SIZE, 0);
+ if (len < 0) {
+ sr_err("Receive error: %s", g_strerror(errno));
+ return SR_ERR;
+ }
+
+ packetsize = len;
+
+ bytes_remaining = (devc->limit_samples * logic.unitsize) -
+ devc->bytes_read;
+
+ /* Configure data packet */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.data = devc->tcp_buffer;
+ logic.length = MIN(packetsize, bytes_remaining);
+
+ if (devc->trigger_fired) {
+ /* Send the incoming transfer to the session bus. */
+ sr_session_send(sdi, &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(sdi, &packet);
+
+ devc->trigger_fired = TRUE;
+ }
+ }
+
+ /* 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 == BL_TRIGGERFLAGS_CONTINUOUS)
+ 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 */
+ std_session_send_df_end(sdi);
+ devc->beaglelogic->stop(devc);
+
+ /* Drain the receive buffer */
+ beaglelogic_tcp_drain(devc);
+
+ sr_session_source_remove_pollfd(sdi->session, &devc->pollfd);
+ }
+
+ return TRUE;
+}
/*
* This file is part of the libsigrok project.
*
- * Copyright (C) 2014 Kumar Abhishek <abhishek@theembeddedkitchen.net>
+ * Copyright (C) 2014-2017 Kumar Abhishek <abhishek@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
#define SAMPLEUNIT_TO_BYTES(x) ((x) == 1 ? 1 : 2)
+#define TCP_BUFFER_SIZE (128 * 1024)
+
/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
int max_channels;
uint32_t fw_ver;
+ /* Operations */
+ const struct beaglelogic_ops *beaglelogic;
+
+ /* TCP Settings */
+ char *address;
+ char *port;
+ int socket;
+ unsigned int read_timeout;
+ unsigned char *tcp_buffer;
+
/* Acquisition settings: see beaglelogic.h */
uint64_t cur_samplerate;
uint64_t limit_samples;
uint32_t bufunitsize;
uint32_t buffersize;
- /* Operational state */
int fd;
GPollFD pollfd;
int last_error;
gboolean trigger_fired;
};
-SR_PRIV int beaglelogic_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int beaglelogic_native_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int beaglelogic_tcp_receive_data(int fd, int revents, void *cb_data);
#endif
SR_CONF_CONN,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
usb = sdi->conn;
devc = sdi->priv;
- if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) == SR_OK)
- sdi->status = SR_ST_ACTIVE;
- else
+ if ((ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb)) < 0)
return SR_ERR;
- /* Detach kernel drivers which grabbed this device (if any). */
if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
ret = libusb_detach_kernel_driver(usb->devhdl, 0);
if (ret < 0) {
return SR_ERR;
}
devc->detached_kernel_driver = 1;
- sr_dbg("Successfully detached kernel driver.");
- } else {
- sr_dbg("No need to detach a kernel driver.");
}
- /* Claim interface 0. */
if ((ret = libusb_claim_interface(usb->devhdl, 0)) < 0) {
sr_err("Failed to claim interface 0: %s.",
libusb_error_name(ret));
return SR_ERR;
}
- sr_dbg("Successfully claimed interface 0.");
- return ret;
+ return SR_OK;
}
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;
+ if (!usb->devhdl)
+ return SR_OK;
+
if ((ret = libusb_release_interface(usb->devhdl, 0)))
sr_err("Failed to release interface 0: %s.\n", libusb_error_name(ret));
- else
- sr_dbg("Successfully released interface 0.\n");
if (!ret && devc->detached_kernel_driver) {
- if ((ret = libusb_attach_kernel_driver(usb->devhdl, 0))) {
+ if ((ret = libusb_attach_kernel_driver(usb->devhdl, 0)))
sr_err("Failed to attach kernel driver: %s.\n",
libusb_error_name(ret));
- } else {
+ else
devc->detached_kernel_driver = 0;
- sr_dbg("Successfully attached kernel driver.\n");
- }
}
libusb_close(usb->devhdl);
- sdi->status = SR_ST_INACTIVE;
-
- return ret;
+ return (ret == 0) ? SR_OK : SR_ERR;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc = sdi->priv;
return sr_sw_limits_config_get(&devc->sw_limits, key, data);
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->sw_limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->sw_limits);
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
std_session_send_df_end(sdi);
sr_session_source_remove(sdi->session, -1);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
if (buf[8] & 0x01) {
analog[0].meaning->mq = SR_MQ_VOLTAGE;
analog[0].meaning->unit = SR_UNIT_VOLT;
- if (!strcmp(str, "diod"))
+ if (!strcmp(str, "diod")) {
analog[0].meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog[0].meaning->mqflags |= SR_MQFLAG_DC;
+ }
} else if (buf[14] & 0x80) {
analog[0].meaning->mq = SR_MQ_CURRENT;
analog[0].meaning->unit = SR_UNIT_AMPERE;
struct sr_analog_encoding encoding[2];
struct sr_analog_meaning meaning[2];
struct sr_analog_spec spec[2];
+ struct sr_channel *channel;
+ int sent_ch1, sent_ch2;
float floatval[2];
devc = sdi->priv;
sr_analog_init(&analog[1], &encoding[1], &meaning[1], &spec[1], 0);
brymen_bm86x_parse(buf, floatval, analog);
+ sent_ch1 = sent_ch2 = 0;
- if (analog[0].meaning->mq != 0) {
+ channel = sdi->channels->data;
+ if (analog[0].meaning->mq != 0 && channel->enabled) {
/* Got a measurement. */
+ sent_ch1 = 1;
analog[0].num_samples = 1;
analog[0].data = &floatval[0];
- analog[0].meaning->channels = g_slist_append(NULL, sdi->channels->data);
+ analog[0].meaning->channels = g_slist_append(NULL, channel);
packet.type = SR_DF_ANALOG;
packet.payload = &analog[0];
sr_session_send(sdi, &packet);
g_slist_free(analog[0].meaning->channels);
}
- if (analog[1].meaning->mq != 0) {
+ channel = sdi->channels->next->data;
+ if (analog[1].meaning->mq != 0 && channel->enabled) {
/* Got a measurement. */
+ sent_ch2 = 1;
analog[1].num_samples = 1;
analog[1].data = &floatval[1];
- analog[1].meaning->channels = g_slist_append(NULL, sdi->channels->next->data);
+ analog[1].meaning->channels = g_slist_append(NULL, channel);
packet.type = SR_DF_ANALOG;
packet.payload = &analog[1];
sr_session_send(sdi, &packet);
g_slist_free(analog[1].meaning->channels);
}
- if (analog[0].meaning->mq != 0 || analog[1].meaning->mq != 0)
+ if (sent_ch1 || sent_ch2)
sr_sw_limits_update_samples_read(&devc->sw_limits, 1);
}
return FALSE;
if (sr_sw_limits_check(&devc->sw_limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#define LOG_PREFIX "brymen-bm86x"
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Acquisition settings */
struct sr_sw_limits sw_limits;
-
- /* Operational state */
- int detached_kernel_driver;/**< Whether kernel driver was detached or not */
-
- /* Temporary state across callbacks */
+ int detached_kernel_driver; /**< Whether kernel driver was detached or not */
int interrupt_pending;
};
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
goto scan_cleanup;
}
- len = 128;
+ len = sizeof(buf);
ret = brymen_stream_detect(serial, buf, &len, brymen_packet_length,
brymen_packet_is_valid, 1000, 9600);
if (ret != SR_OK)
if (!conn)
return NULL;
- if (serialcomm) {
- /* Use the provided comm specs. */
+ if (serialcomm)
devices = brymen_scan(di, conn, serialcomm);
- } else {
- /* But 9600/8n1 should work all of the time. */
+ else
devices = brymen_scan(di, conn, "9600/8n1/dtr=1/rts=1");
- }
return devices;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->sw_limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->sw_limits);
std_session_send_df_header(sdi);
- /* Poll every 50ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 50,
brymen_dmm_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = NULL,
.config_set = config_set,
.config_list = config_list,
}
if (flags.is_diode)
- analog->meaning->mqflags |= SR_MQFLAG_DIODE;
+ analog->meaning->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
/* We can have both AC+DC in a single measurement. */
if (flags.is_ac)
analog->meaning->mqflags |= SR_MQFLAG_AC;
}
if (sr_sw_limits_check(&devc->sw_limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
PACKET_INVALID_HEADER,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits sw_limits;
return std_scan_complete(di, devices);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
- GVariant *range[2];
uint64_t low, high;
int tmp, ret;
return SR_ERR_ARG;
devc = sdi->priv;
+
ret = SR_OK;
+
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
*data = g_variant_new_uint64(devc->limit_samples);
*data = g_variant_new_boolean(tmp == SR_MQFLAG_MIN);
break;
case SR_CONF_SPL_MEASUREMENT_RANGE:
- if ((ret = cem_dt_885x_meas_range_get(sdi, &low, &high)) == SR_OK) {
- range[0] = g_variant_new_uint64(low);
- range[1] = g_variant_new_uint64(high);
- *data = g_variant_new_tuple(range, 2);
- }
+ if ((ret = cem_dt_885x_meas_range_get(sdi, &low, &high)) == SR_OK)
+ *data = std_gvar_tuple_u64(low, high);
break;
case SR_CONF_POWER_OFF:
*data = g_variant_new_boolean(FALSE);
return ret;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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_u64, low, high;
- unsigned int i;
- int tmp, ret;
- const char *tmp_str;
+ int tmp, idx;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
- ret = SR_OK;
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
- tmp_u64 = g_variant_get_uint64(data);
- devc->limit_samples = tmp_u64;
- ret = SR_OK;
+ devc->limit_samples = g_variant_get_uint64(data);
break;
case SR_CONF_DATALOG:
- ret = cem_dt_885x_recording_set(sdi, g_variant_get_boolean(data));
- break;
+ return cem_dt_885x_recording_set(sdi, g_variant_get_boolean(data));
case SR_CONF_SPL_WEIGHT_FREQ:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "A"))
- ret = cem_dt_885x_weight_freq_set(sdi,
- SR_MQFLAG_SPL_FREQ_WEIGHT_A);
- else if (!strcmp(tmp_str, "C"))
- ret = cem_dt_885x_weight_freq_set(sdi,
- SR_MQFLAG_SPL_FREQ_WEIGHT_C);
- else
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_freq))) < 0)
return SR_ERR_ARG;
- break;
+ return cem_dt_885x_weight_freq_set(sdi, (weight_freq[idx][0] == 'A') ?
+ SR_MQFLAG_SPL_FREQ_WEIGHT_A : SR_MQFLAG_SPL_FREQ_WEIGHT_C);
case SR_CONF_SPL_WEIGHT_TIME:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "F"))
- ret = cem_dt_885x_weight_time_set(sdi,
- SR_MQFLAG_SPL_TIME_WEIGHT_F);
- else if (!strcmp(tmp_str, "S"))
- ret = cem_dt_885x_weight_time_set(sdi,
- SR_MQFLAG_SPL_TIME_WEIGHT_S);
- else
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_time))) < 0)
return SR_ERR_ARG;
- break;
+ return cem_dt_885x_weight_time_set(sdi, (weight_time[idx][0] == 'F') ?
+ SR_MQFLAG_SPL_TIME_WEIGHT_F : SR_MQFLAG_SPL_TIME_WEIGHT_S);
case SR_CONF_HOLD_MAX:
tmp = g_variant_get_boolean(data) ? SR_MQFLAG_MAX : 0;
- ret = cem_dt_885x_holdmode_set(sdi, tmp);
- break;
+ return cem_dt_885x_holdmode_set(sdi, tmp);
case SR_CONF_HOLD_MIN:
tmp = g_variant_get_boolean(data) ? SR_MQFLAG_MIN : 0;
- ret = cem_dt_885x_holdmode_set(sdi, tmp);
- break;
+ return cem_dt_885x_holdmode_set(sdi, tmp);
case SR_CONF_SPL_MEASUREMENT_RANGE:
- g_variant_get(data, "(tt)", &low, &high);
- ret = SR_ERR_ARG;
- for (i = 0; i < ARRAY_SIZE(meas_ranges); i++) {
- if (meas_ranges[i][0] == low && meas_ranges[i][1] == high) {
- ret = cem_dt_885x_meas_range_set(sdi, low, high);
- break;
- }
- }
- break;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(meas_ranges))) < 0)
+ return SR_ERR_ARG;
+ return cem_dt_885x_meas_range_set(sdi, meas_ranges[idx][0], meas_ranges[idx][1]);
case SR_CONF_POWER_OFF:
if (g_variant_get_boolean(data))
- ret = cem_dt_885x_power_off(sdi);
+ return cem_dt_885x_power_off(sdi);
break;
case SR_CONF_DATA_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "Live"))
- devc->cur_data_source = DATA_SOURCE_LIVE;
- else if (!strcmp(tmp_str, "Memory"))
- devc->cur_data_source = DATA_SOURCE_MEMORY;
- else
- return SR_ERR;
- devc->enable_data_source_memory = devc->cur_data_source == DATA_SOURCE_MEMORY;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->cur_data_source = idx;
+ devc->enable_data_source_memory = (idx == DATA_SOURCE_MEMORY);
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *tuple, *range[2];
- GVariantBuilder gvb;
- unsigned int i;
- int ret;
-
- (void)cg;
-
- ret = SR_OK;
- 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;
- }
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_SPL_WEIGHT_FREQ:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_freq));
+ break;
+ case SR_CONF_SPL_WEIGHT_TIME:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_time));
+ break;
+ case SR_CONF_SPL_MEASUREMENT_RANGE:
+ *data = std_gvar_tuple_array(ARRAY_AND_SIZE(meas_ranges));
+ break;
+ case SR_CONF_DATA_SOURCE:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
+ break;
+ default:
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
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->state = ST_INIT;
devc->num_samples = 0;
std_session_send_df_header(sdi);
- /* Poll every 100ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 150,
cem_dt_885x_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
devc->num_samples++;
if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
+ sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
break;
case TOKEN_RECORDING_ON:
devc->recording = TRUE;
devc->num_samples += analog.num_samples;
if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
+ sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
return;
}
* records. Otherwise the frontend would have no
* way to tell where stored data ends and live
* measurements begin. */
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
+ sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
} else if (c == RECORD_DATA) {
devc->buf_len = 0;
devc->state = ST_GET_LOG_RECORD_DATA;
} else {
/* Tell device to start transferring from memory. */
cmd = CMD_TRANSFER_MEMORY;
- serial_write_nonblocking(serial, &cmd, 1);
+ serial_write_blocking(serial, &cmd, 1, 0);
}
}
}
* 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_nonblocking(serial, (const void *)&cmd, 1) != 1)
+ if (serial_write_blocking(serial, (const void *)&cmd, 1, 0) < 0)
return SR_ERR;
if (wait_for_token(sdi, tokens, timeout) == SR_ERR)
return SR_ERR;
cmd = CMD_TOGGLE_POWER_OFF;
while (TRUE) {
serial_flush(serial);
- if (serial_write_nonblocking(serial, (const void *)&cmd, 1) != 1)
+ if (serial_write_blocking(serial, (const void *)&cmd, 1, 0) < 0)
return SR_ERR;
/* It never takes more than 23ms for the next token to arrive. */
g_usleep(25 * 1000);
DATA_SOURCE_MEMORY,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Device state */
enum sr_mqflag cur_mqflags;
int recording;
int cur_meas_range;
int cur_data_source;
- /* Acquisition settings */
uint64_t limit_samples;
- /* Operational state */
int state;
uint64_t num_samples;
gboolean enable_data_source_memory;
- /* Temporary state across callbacks */
unsigned char cmd;
unsigned char token;
int buf_len;
if (!conn)
return NULL;
- if (serialcomm) {
- /* Use the provided comm specs. */
+ if (serialcomm)
devices = center_scan(conn, serialcomm, idx);
- } else {
- /* Try the default. */
+ else
devices = center_scan(conn, center_devs[idx].conn, idx);
- }
return std_scan_complete(center_devs[idx].di, devices);
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->sw_limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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:
- 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;
- }
-
- return SR_OK;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi, int idx)
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->sw_limits);
std_session_send_df_header(sdi);
- /* Poll every 500ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 500,
center_devs[idx].receive_data, (void *)sdi);
.cleanup = std_cleanup, \
.scan = scan_##ID_UPPER, \
.dev_list = std_dev_list, \
+ .dev_clear = std_dev_clear, \
.config_get = NULL, \
.config_set = config_set, \
.config_list = config_list, \
{
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- int len, i, offset = 0, ret = FALSE;
+ int len, offset, ret = FALSE;
devc = sdi->priv;
serial = sdi->conn;
devc->buflen += len;
/* Now look for packets in that data. */
+ offset = 0;
while ((devc->buflen - offset) >= center_devs[idx].packet_size) {
if (center_devs[idx].packet_valid(devc->buf + offset)) {
handle_packet(devc->buf + offset, sdi, idx);
}
/* 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];
+ if (offset < devc->buflen)
+ memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
devc->buflen -= offset;
return ret;
}
if (sr_sw_limits_check(&devc->sw_limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#define SERIAL_BUFSIZE 256
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits sw_limits;
#include <config.h>
#include "protocol.h"
-static const uint32_t drvopts[] = {
- SR_CONF_LOGIC_ANALYZER,
-};
-
static const uint32_t scanopts[] = {
SR_CONF_CONN,
};
+static const uint32_t drvopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
+};
+
static const uint32_t devopts[] = {
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST,
SR_TRIGGER_FALLING,
};
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
-
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
-
ftdi_free(devc->ftdic);
g_free(devc->final_buf);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static int add_device(int model, struct libusb_device_descriptor *des,
ret = SR_OK;
- /* Allocate memory for our private device context. */
devc = g_malloc0(sizeof(struct dev_context));
/* Set some sane defaults. */
/* We now know the device, set its max. samplerate as default. */
devc->cur_samplerate = devc->prof->max_samplerate;
- /* Register the device with libsigrok. */
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
sdi->vendor = g_strdup("ChronoVu");
continue;
}
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
-
libusb_close(hdl);
- if (!strcmp(product, "ChronoVu LA8")) {
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
+ if (!strcmp(product, "ChronoVu LA8"))
model = 0;
- } else if (!strcmp(product, "ChronoVu LA16")) {
+ else if (!strcmp(product, "ChronoVu LA16"))
model = 1;
- } else {
- sr_spew("Unknown iProduct string '%s'.", product);
- continue;
- }
+ else
+ continue; /* Unknown iProduct string, ignore. */
sr_dbg("Found %s (%04x:%04x, %d.%d, %s).",
product, des.idVendor, des.idProduct,
devc = sdi->priv;
- /* Allocate memory for the FTDI context and initialize it. */
if (!(devc->ftdic = ftdi_new())) {
sr_err("Failed to initialize libftdi.");
return SR_ERR;
sr_dbg("Opening %s device (%04x:%04x).", devc->prof->modelname,
devc->usb_vid, devc->usb_pid);
- /* Open the device. */
if ((ret = ftdi_usb_open_desc(devc->ftdic, devc->usb_vid,
devc->usb_pid, devc->prof->iproduct, NULL)) < 0) {
sr_err("Failed to open FTDI device (%d): %s.",
ret, ftdi_get_error_string(devc->ftdic));
goto err_ftdi_free;
}
- sr_dbg("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 buffers (%d): %s.",
ret, ftdi_get_error_string(devc->ftdic));
goto err_ftdi_free;
}
- sr_dbg("FTDI buffers purged successfully.");
- /* Enable flow control in the FTDI chip. */
if ((ret = ftdi_setflowctrl(devc->ftdic, SIO_RTS_CTS_HS)) < 0) {
sr_err("Failed to enable FTDI flow control (%d): %s.",
ret, ftdi_get_error_string(devc->ftdic));
goto err_ftdi_free;
}
- sr_dbg("FTDI flow control enabled successfully.");
- /* Wait 100ms. */
g_usleep(100 * 1000);
- sdi->status = SR_ST_ACTIVE;
-
- if (ret == SR_OK)
- return SR_OK;
+ return SR_OK;
err_ftdi_free:
ftdi_free(devc->ftdic); /* Close device (if open), free FTDI context. */
devc->ftdic = NULL;
- return ret;
+ return SR_ERR;
}
static int dev_close(struct sr_dev_inst *sdi)
int ret;
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_OK;
-
devc = sdi->priv;
- if (devc->ftdic && (ret = ftdi_usb_close(devc->ftdic)) < 0)
+ if (!devc->ftdic)
+ return SR_ERR_BUG;
+
+ if ((ret = ftdi_usb_close(devc->ftdic)) < 0)
sr_err("Failed to close FTDI device (%d): %s.",
ret, ftdi_get_error_string(devc->ftdic));
- sdi->status = SR_ST_INACTIVE;
- return SR_OK;
+ return (ret == 0) ? SR_OK : SR_ERR;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
- char str[128];
(void)cg;
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);
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
break;
case SR_CONF_SAMPLERATE:
if (!sdi)
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
switch (key) {
return SR_ERR;
break;
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;
default:
return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
struct dev_context *devc;
- (void)cg;
+ devc = (sdi) ? sdi->priv : NULL;
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
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_SAMPLERATE:
- if (!sdi)
- return SR_ERR_BUG;
- devc = sdi->priv;
cv_fill_samplerates_if_needed(sdi);
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- devc->samplerates,
- ARRAY_SIZE(devc->samplerates),
- sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(devc->samplerates));
break;
case SR_CONF_LIMIT_SAMPLES:
- if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
+ if (!devc || !devc->prof)
return SR_ERR_BUG;
- grange[0] = g_variant_new_uint64(0);
- if (devc->prof->model == CHRONOVU_LA8)
- grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES);
- else
- grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES / 2);
- *data = g_variant_new_tuple(grange, 2);
+ *data = std_gvar_tuple_u64(0, (devc->prof->model == CHRONOVU_LA8) ? MAX_NUM_SAMPLES : MAX_NUM_SAMPLES / 2);
break;
case SR_CONF_TRIGGER_MATCH:
- if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
+ if (!devc || !devc->prof)
return SR_ERR_BUG;
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
- trigger_matches, devc->prof->num_trigger_matches,
- sizeof(int32_t));
+ *data = std_gvar_array_i32(trigger_matches, devc->prof->num_trigger_matches);
break;
default:
return SR_ERR_NA;
/* Get one block of data. */
if ((ret = cv_read_block(devc)) < 0) {
sr_err("Failed to read data block: %d.", ret);
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return FALSE;
}
for (i = 0; i < NUM_BLOCKS; i++)
cv_send_block_to_session_bus(sdi, i);
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
uint8_t buf[8];
int bytes_to_write, bytes_written;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
if (!devc->ftdic) {
return SR_ERR;
}
- sr_dbg("Hardware acquisition started successfully.");
-
std_session_send_df_header(sdi);
/* Time when we should be done (for detecting trigger timeouts). */
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- sr_dbg("Stopping acquisition.");
sr_session_source_remove(sdi->session, -1);
std_session_send_df_end(sdi);
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
-#define LOG_PREFIX "la8/la16"
+#define LOG_PREFIX "chronovu-la"
#define SDRAM_SIZE (8 * 1024 * 1024)
#define MAX_NUM_SAMPLES SDRAM_SIZE
struct cv_profile {
int model;
const char *modelname;
- const char *iproduct; /* USB iProduct string */
+ const char *iproduct;
unsigned int num_channels;
uint64_t max_samplerate;
const int num_trigger_matches;
float trigger_constant;
};
-/* Private, per-device-instance driver context. */
struct dev_context {
- /** Device profile struct for this device. */
const struct cv_profile *prof;
-
- /** FTDI device context (used by libftdi). */
struct ftdi_context *ftdic;
-
- /** The currently configured samplerate of the device. */
uint64_t cur_samplerate;
-
- /** The current sampling limit (in ms). */
uint64_t limit_msec;
-
- /** The current sampling limit (in number of samples). */
uint64_t limit_samples;
/**
uint64_t samplerates[255];
};
-/* protocol.c */
extern SR_PRIV const char *cv_channel_names[];
extern const struct cv_profile cv_profiles[];
SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi);
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_SOUNDLEVELMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
return std_scan_complete(di, g_slist_append(NULL, sdi));
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct dev_context *devc = sdi->priv;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* Poll every 150ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 150,
colead_slm_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = NULL,
.config_set = config_set,
.config_list = config_list,
sr_sw_limits_update_samples_read(&devc->limits, 1);
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
+ sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
}
SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
serial = sdi->conn;
if (devc->state == IDLE) {
- if (serial_read_nonblocking(serial, buf, 128) != 1 || buf[0] != 0x10)
+ if (serial_read_nonblocking(serial, buf, sizeof(buf)) != 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. */
COMMAND_SENT,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits limits;
* 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 "protocol.h"
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_POWER_SUPPLY,
- SR_CONF_VOLTAGE | SR_CONF_SET,
- SR_CONF_CURRENT | SR_CONF_SET,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_VOLTAGE_TARGET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_CURRENT_LIMIT | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_SET,
};
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
+ struct dev_context *devc;
struct sr_dev_inst *sdi;
struct sr_config *src;
struct sr_serial_dev_inst *serial;
sdi->status = SR_ST_INACTIVE;
sdi->vendor = g_strdup("Conrad");
sdi->model = g_strdup("DIGI 35 CPU");
+ devc = g_malloc0(sizeof(struct dev_context));
+ sr_sw_limits_init(&devc->limits);
+ sdi->inst_type = SR_INST_SERIAL;
sdi->conn = serial;
- sdi->priv = NULL;
+ sdi->priv = devc;
sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
return std_scan_complete(di, g_slist_append(NULL, sdi));
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+static int config_set(uint32_t key, GVariant *data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- int ret;
double dblval;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
switch (key) {
- case SR_CONF_VOLTAGE:
+ case SR_CONF_VOLTAGE_TARGET:
dblval = g_variant_get_double(data);
if ((dblval < 0.0) || (dblval > 35.0)) {
sr_err("Voltage out of range (0 - 35.0)!");
return SR_ERR_ARG;
}
- ret = send_msg1(sdi, 'V', (int) (dblval * 10 + 0.5));
- break;
- case SR_CONF_CURRENT:
+ return send_msg1(sdi, 'V', (int) (dblval * 10 + 0.5));
+ case SR_CONF_CURRENT_LIMIT:
dblval = g_variant_get_double(data);
- if ((dblval < 0.01) || (dblval > 2.55)) {
+ if ((dblval < 0.00) || (dblval > 2.55)) {
sr_err("Current out of range (0 - 2.55)!");
return SR_ERR_ARG;
}
- ret = send_msg1(sdi, 'C', (int) (dblval * 100 + 0.5));
- break;
+ return send_msg1(sdi, 'C', (int) (dblval * 100 + 0.5));
case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
if (g_variant_get_boolean(data))
- ret = send_msg1(sdi, 'V', 900);
+ return send_msg1(sdi, 'V', 900);
else /* Constant current mode */
- ret = send_msg1(sdi, 'V', 901);
- break;
+ return send_msg1(sdi, 'V', 901);
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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 ret;
-
- (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));
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_VOLTAGE_TARGET:
+ *data = std_gvar_min_max_step(0.0, 35.0, 0.1);
+ break;
+ case SR_CONF_CURRENT_LIMIT:
+ *data = std_gvar_min_max_step(0.0, 2.55, 0.01);
break;
default:
return SR_ERR_NA;
}
- return ret;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi)
-{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi)
-{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
return SR_OK;
}
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_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,
+ .dev_acquisition_start = std_dummy_dev_acquisition_start,
+ .dev_acquisition_stop = std_dummy_dev_acquisition_stop,
.context = NULL,
};
SR_REGISTER_DEV_DRIVER(conrad_digi_35_cpu_driver_info);
* 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 "protocol.h"
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/**
- * @file
- *
- * <em>Conrad DIGI 35 CPU</em> power supply driver
- *
- * @internal
- */
-
#ifndef LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
#define LIBSIGROK_HARDWARE_CONRAD_DIGI_35_CPU_PROTOCOL_H
#define LOG_PREFIX "conrad-digi-35-cpu"
+struct dev_context {
+ struct sr_sw_limits limits;
+};
+
SR_PRIV int send_msg1(const struct sr_dev_inst *sdi, char cmd, int param);
#endif
#include "protocol.h"
#define DEFAULT_NUM_LOGIC_CHANNELS 8
-#define DEFAULT_ENABLED_LOGIC_CHANNELS 8
#define DEFAULT_LOGIC_PATTERN PATTERN_SIGROK
#define DEFAULT_NUM_ANALOG_CHANNELS 4
-#define DEFAULT_ENABLED_ANALOG_CHANNELS 4
#define DEFAULT_ANALOG_AMPLITUDE 10
/* Note: No spaces allowed because of sigrok-cli. */
"all-low",
"all-high",
"squid",
+ "graycode",
+};
+
+static const uint32_t scanopts[] = {
+ SR_CONF_NUM_LOGIC_CHANNELS,
+ SR_CONF_NUM_ANALOG_CHANNELS,
+ SR_CONF_LIMIT_FRAMES,
};
static const uint32_t drvopts[] = {
SR_CONF_OSCILLOSCOPE,
};
-static const uint32_t scanopts[] = {
- SR_CONF_NUM_LOGIC_CHANNELS,
- SR_CONF_NUM_ANALOG_CHANNELS,
-};
-
static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_FRAMES | 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,
+ SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+ SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
};
static const uint32_t devopts_cg_logic[] = {
SR_CONF_AMPLITUDE | SR_CONF_GET | SR_CONF_SET,
};
+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(1),
SR_GHZ(1),
struct analog_gen *ag;
GSList *l;
int num_logic_channels, num_analog_channels, pattern, i;
+ uint64_t limit_frames;
char channel_name[16];
- gboolean enabled;
num_logic_channels = DEFAULT_NUM_LOGIC_CHANNELS;
num_analog_channels = DEFAULT_NUM_ANALOG_CHANNELS;
+ limit_frames = DEFAULT_LIMIT_FRAMES;
for (l = options; l; l = l->next) {
src = l->data;
switch (src->key) {
case SR_CONF_NUM_ANALOG_CHANNELS:
num_analog_channels = g_variant_get_int32(src->data);
break;
+ case SR_CONF_LIMIT_FRAMES:
+ limit_frames = g_variant_get_uint64(src->data);
+ break;
}
}
devc->cur_samplerate = SR_KHZ(200);
devc->num_logic_channels = num_logic_channels;
devc->logic_unitsize = (devc->num_logic_channels + 7) / 8;
+ devc->all_logic_channels_mask = 1UL << 0;
+ devc->all_logic_channels_mask <<= devc->num_logic_channels;
+ devc->all_logic_channels_mask--;
devc->logic_pattern = DEFAULT_LOGIC_PATTERN;
devc->num_analog_channels = num_analog_channels;
+ devc->limit_frames = limit_frames;
+ devc->capture_ratio = 20;
+ devc->stl = NULL;
if (num_logic_channels > 0) {
/* Logic channels, all in one channel group. */
cg->name = g_strdup("Logic");
for (i = 0; i < num_logic_channels; i++) {
sprintf(channel_name, "D%d", i);
- enabled = (i < DEFAULT_ENABLED_LOGIC_CHANNELS) ? TRUE : FALSE;
- ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, enabled, channel_name);
+ 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. */
+ devc->ch_ag = g_hash_table_new(g_direct_hash, g_direct_equal);
if (num_analog_channels > 0) {
pattern = 0;
/* An "Analog" channel group with all analog channels in it. */
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++) {
snprintf(channel_name, 16, "A%d", i);
- enabled = (i < DEFAULT_ENABLED_ANALOG_CHANNELS) ? TRUE : FALSE;
ch = sr_channel_new(sdi, i + num_logic_channels, SR_CHANNEL_ANALOG,
- enabled, channel_name);
+ TRUE, channel_name);
acg->channels = g_slist_append(acg->channels, ch);
/* Every analog channel gets its own channel group as well. */
/* Every channel gets a generator struct. */
ag = g_malloc(sizeof(struct analog_gen));
+ ag->ch = ch;
ag->amplitude = DEFAULT_ANALOG_AMPLITUDE;
sr_analog_init(&ag->packet, &ag->encoding, &ag->meaning, &ag->spec, 2);
ag->packet.meaning->channels = cg->channels;
return std_scan_complete(di, g_slist_append(NULL, sdi));
}
-static int dev_open(struct sr_dev_inst *sdi)
-{
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
- sdi->status = SR_ST_INACTIVE;
-
- return SR_OK;
-}
-
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- 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 dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
struct sr_channel *ch;
case SR_CONF_LIMIT_MSEC:
*data = g_variant_new_uint64(devc->limit_msec);
break;
+ case SR_CONF_LIMIT_FRAMES:
+ *data = g_variant_new_uint64(devc->limit_frames);
+ break;
case SR_CONF_AVERAGING:
*data = g_variant_new_boolean(devc->avg);
break;
ag = g_hash_table_lookup(devc->ch_ag, ch);
*data = g_variant_new_double(ag->amplitude);
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)
+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;
GSList *l;
- int logic_pattern, analog_pattern, ret;
- unsigned int i;
- const char *stropt;
+ int logic_pattern, analog_pattern;
devc = sdi->priv;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- ret = SR_OK;
switch (key) {
case SR_CONF_SAMPLERATE:
devc->cur_samplerate = g_variant_get_uint64(data);
devc->limit_msec = g_variant_get_uint64(data);
devc->limit_samples = 0;
break;
+ case SR_CONF_LIMIT_FRAMES:
+ devc->limit_frames = g_variant_get_uint64(data);
+ break;
case SR_CONF_AVERAGING:
devc->avg = g_variant_get_boolean(data);
sr_dbg("%s averaging", devc->avg ? "Enabling" : "Disabling");
case SR_CONF_PATTERN_MODE:
if (!cg)
return SR_ERR_CHANNEL_GROUP;
- stropt = g_variant_get_string(data, NULL);
- 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;
- }
- }
- for (i = 0; i < ARRAY_SIZE(analog_pattern_str); i++) {
- if (!strcmp(stropt, analog_pattern_str[i])) {
- analog_pattern = i;
- break;
- }
- }
- if (logic_pattern == -1 && analog_pattern == -1)
+ logic_pattern = std_str_idx(data, ARRAY_AND_SIZE(logic_pattern_str));
+ analog_pattern = std_str_idx(data, ARRAY_AND_SIZE(analog_pattern_str));
+ if (logic_pattern < 0 && analog_pattern < 0)
return SR_ERR_ARG;
for (l = cg->channels; l; l = l->next) {
ch = l->data;
ag->amplitude = g_variant_get_double(data);
}
break;
+ case SR_CONF_CAPTURE_RATIO:
+ devc->capture_ratio = g_variant_get_uint64(data);
+ break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
struct sr_channel *ch;
- GVariant *gvar;
- GVariantBuilder gvb;
-
- 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;
- }
-
- 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;
if (!cg) {
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
default:
return SR_ERR_NA;
switch (key) {
case SR_CONF_DEVICE_OPTIONS:
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));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_logic));
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));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog_group));
else
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts_cg_analog_channel, ARRAY_SIZE(devopts_cg_analog_channel),
- sizeof(uint32_t));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog_channel));
}
else
return SR_ERR_BUG;
return SR_ERR_NA;
if (ch->type == SR_CHANNEL_LOGIC)
- *data = g_variant_new_strv(logic_pattern_str,
- ARRAY_SIZE(logic_pattern_str));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(logic_pattern_str));
else if (ch->type == SR_CHANNEL_ANALOG)
- *data = g_variant_new_strv(analog_pattern_str,
- ARRAY_SIZE(analog_pattern_str));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(analog_pattern_str));
else
return SR_ERR_BUG;
break;
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
+ GSList *l;
+ struct sr_channel *ch;
+ int bitpos;
+ uint8_t mask;
GHashTableIter iter;
void *value;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
+ struct sr_trigger *trigger;
devc = sdi->priv;
devc->sent_samples = 0;
-
+ devc->sent_frame_samples = 0;
+
+ /* Setup triggers */
+ 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;
+
+ /* Disable all analog channels since using them when there are logic
+ * triggers set up would require having pre-trigger sample buffers
+ * for analog sample data.
+ */
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type == SR_CHANNEL_ANALOG)
+ ch->enabled = FALSE;
+ }
+ }
+ devc->trigger_fired = FALSE;
+
+ /*
+ * Determine the numbers of logic and analog channels that are
+ * involved in the acquisition. Determine an offset and a mask to
+ * remove excess logic data content before datafeed submission.
+ */
+ devc->enabled_logic_channels = 0;
+ devc->enabled_analog_channels = 0;
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (!ch->enabled)
+ continue;
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ devc->enabled_analog_channels++;
+ continue;
+ }
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ /*
+ * TODO: Need we create a channel map here, such that the
+ * session datafeed packets will have a dense representation
+ * of the enabled channels' data? For example store channels
+ * D3 and D5 in bit positions 0 and 1 respectively, when all
+ * other channels are disabled? The current implementation
+ * generates a sparse layout, might provide data for logic
+ * channels that are disabled while it might suppress data
+ * from enabled channels at the same time.
+ */
+ devc->enabled_logic_channels++;
+ }
+ devc->first_partial_logic_index = devc->enabled_logic_channels / 8;
+ bitpos = devc->enabled_logic_channels % 8;
+ mask = (1 << bitpos) - 1;
+ devc->first_partial_logic_mask = mask;
+ sr_dbg("num logic %zu, partial off %zu, mask 0x%02x.",
+ devc->enabled_logic_channels,
+ devc->first_partial_logic_index,
+ devc->first_partial_logic_mask);
+
+ /*
+ * Have the waveform for analog patterns pre-generated. It's
+ * supposed to be periodic, so the generator just needs to
+ * access the prepared sample data (DDS style).
+ */
g_hash_table_iter_init(&iter, devc->ch_ag);
while (g_hash_table_iter_next(&iter, NULL, &value))
demo_generate_analog_pattern(value, devc->cur_samplerate);
std_session_send_df_header(sdi);
+ if (devc->limit_frames > 0)
+ std_session_send_frame_begin(sdi);
+
/* We use this timestamp to decide how many more samples to send. */
devc->start_us = g_get_monotonic_time();
devc->spent_us = 0;
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- sr_dbg("Stopping acquisition.");
+ struct dev_context *devc;
+
sr_session_source_remove(sdi->session, -1);
+
+ devc = sdi->priv;
+ if (devc->limit_frames > 0)
+ std_session_send_frame_end(sdi);
+
std_session_send_df_end(sdi);
+ if (devc->stl) {
+ soft_trigger_logic_free(devc->stl);
+ devc->stl = NULL;
+ }
+
return SR_OK;
}
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
- .dev_open = dev_open,
- .dev_close = dev_close,
+ .dev_open = std_dummy_dev_open,
+ .dev_close = std_dummy_dev_close,
.dev_acquisition_start = dev_acquisition_start,
.dev_acquisition_stop = dev_acquisition_stop,
.context = NULL,
}
}
+static uint64_t encode_number_to_gray(uint64_t nr)
+{
+ return nr ^ (nr >> 1);
+}
+
+static void set_logic_data(uint64_t bits, uint8_t *data, size_t len)
+{
+ while (len--) {
+ *data++ = bits & 0xff;
+ bits >>= 8;
+ }
+}
+
static void logic_generator(struct sr_dev_inst *sdi, uint64_t size)
{
struct dev_context *devc;
uint8_t *sample;
const uint8_t *image_col;
size_t col_count, col_height;
+ uint64_t gray;
devc = sdi->priv;
break;
case PATTERN_INC:
for (i = 0; i < size; i++) {
- for (j = 0; j < devc->logic_unitsize; j++) {
+ for (j = 0; j < devc->logic_unitsize; j++)
devc->logic_data[i + j] = devc->step;
- }
devc->step++;
}
break;
case PATTERN_WALKING_ONE:
/* j contains the value of the highest bit */
- j = 1 << (devc->num_logic_channels - 1);
+ j = 1 << (devc->num_logic_channels - 1);
for (i = 0; i < size; i++) {
devc->logic_data[i] = devc->step;
if (devc->step == 0)
case PATTERN_WALKING_ZERO:
/* Same as walking one, only with inverted output */
/* j contains the value of the highest bit */
- j = 1 << (devc->num_logic_channels - 1);
+ j = 1 << (devc->num_logic_channels - 1);
for (i = 0; i < size; i++) {
devc->logic_data[i] = ~devc->step;
if (devc->step == 0)
devc->step %= col_count;
}
break;
+ case PATTERN_GRAYCODE:
+ for (i = 0; i < size; i += devc->logic_unitsize) {
+ devc->step++;
+ devc->step &= devc->all_logic_channels_mask;
+ gray = encode_number_to_gray(devc->step);
+ gray &= devc->all_logic_channels_mask;
+ set_logic_data(gray, &devc->logic_data[i], devc->logic_unitsize);
+ }
+ break;
default:
sr_err("Unknown pattern: %d.", devc->logic_pattern);
break;
}
}
+/*
+ * Fixup a memory image of generated logic data before it gets sent to
+ * the session's datafeed. Mask out content from disabled channels.
+ *
+ * TODO: Need we apply a channel map, and enforce a dense representation
+ * of the enabled channels' data?
+ */
+static void logic_fixup_feed(struct dev_context *devc,
+ struct sr_datafeed_logic *logic)
+{
+ size_t fp_off;
+ uint8_t fp_mask;
+ size_t off, idx;
+ uint8_t *sample;
+
+ fp_off = devc->first_partial_logic_index;
+ fp_mask = devc->first_partial_logic_mask;
+ if (fp_off == logic->unitsize)
+ return;
+
+ for (off = 0; off < logic->length; off += logic->unitsize) {
+ sample = logic->data + off;
+ sample[fp_off] &= fp_mask;
+ for (idx = fp_off + 1; idx < logic->unitsize; idx++)
+ sample[idx] = 0x00;
+ }
+}
+
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)
int ag_pattern_pos;
unsigned int i;
+ if (!ag->ch || !ag->ch->enabled)
+ return;
+
devc = sdi->priv;
packet.type = SR_DF_ANALOG;
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);
+ 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);
*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);
+ 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_pos + i)) / 2;
ag->num_avgs++;
/* Time to send averaged data? */
- if (devc->avg_samples > 0 &&
- ag->num_avgs >= devc->avg_samples)
+ if ((devc->avg_samples > 0) && (ag->num_avgs >= devc->avg_samples))
goto do_send;
}
void *value;
uint64_t samples_todo, logic_done, analog_done, analog_sent, sending_now;
int64_t elapsed_us, limit_us, todo_us;
+ int64_t trigger_offset;
+ int pre_trigger_samples;
(void)fd;
(void)revents;
if (devc->cur_samplerate <= 0
|| (devc->num_logic_channels <= 0
&& devc->num_analog_channels <= 0)) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return G_SOURCE_CONTINUE;
}
/* 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;
}
+
+ if (samples_todo == 0)
+ return G_SOURCE_CONTINUE;
+
+ if (devc->limit_frames) {
+ /* Never send more samples than a frame can fit... */
+ samples_todo = MIN(samples_todo, SAMPLES_PER_FRAME);
+ /* ...or than we need to finish the current frame. */
+ samples_todo = MIN(samples_todo,
+ SAMPLES_PER_FRAME - devc->sent_frame_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;
- logic_done = devc->num_logic_channels > 0 ? 0 : samples_todo;
+ logic_done = devc->num_logic_channels > 0 ? 0 : samples_todo;
+ if (!devc->enabled_logic_channels)
+ logic_done = samples_todo;
+
analog_done = devc->num_analog_channels > 0 ? 0 : samples_todo;
+ if (!devc->enabled_analog_channels)
+ analog_done = samples_todo;
while (logic_done < samples_todo || analog_done < samples_todo) {
/* Logic */
sending_now = MIN(samples_todo - logic_done,
LOGIC_BUFSIZE / devc->logic_unitsize);
logic_generator(sdi, sending_now * devc->logic_unitsize);
+ /* Check for trigger and send pre-trigger data if needed */
+ if (devc->stl && (!devc->trigger_fired)) {
+ trigger_offset = soft_trigger_logic_check(devc->stl,
+ devc->logic_data, sending_now * devc->logic_unitsize,
+ &pre_trigger_samples);
+ if (trigger_offset > -1) {
+ devc->trigger_fired = TRUE;
+ logic_done = pre_trigger_samples;
+ }
+ } else
+ trigger_offset = 0;
+
+ /* Send logic samples if needed */
packet.type = SR_DF_LOGIC;
packet.payload = &logic;
- logic.length = sending_now * devc->logic_unitsize;
logic.unitsize = devc->logic_unitsize;
- logic.data = devc->logic_data;
- sr_session_send(sdi, &packet);
- logic_done += sending_now;
+
+ if (devc->stl) {
+ if (devc->trigger_fired && (trigger_offset < (int)sending_now)) {
+ /* Send after-trigger data */
+ logic.length = (sending_now - trigger_offset) * devc->logic_unitsize;
+ logic.data = devc->logic_data + trigger_offset * devc->logic_unitsize;
+ logic_fixup_feed(devc, &logic);
+ sr_session_send(sdi, &packet);
+ logic_done += sending_now - trigger_offset;
+ /* End acquisition */
+ sr_dbg("Triggered, stopping acquisition.");
+ sr_dev_acquisition_stop(sdi);
+ break;
+ } else {
+ /* Send nothing */
+ logic_done += sending_now;
+ }
+ } else if (!devc->stl) {
+ /* No trigger defined, send logic samples */
+ logic.length = sending_now * devc->logic_unitsize;
+ logic.data = devc->logic_data;
+ logic_fixup_feed(devc, &logic);
+ sr_session_send(sdi, &packet);
+ logic_done += sending_now;
+ }
}
/* Analog, one channel at a time */
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;
+
+ uint64_t min = MIN(logic_done, analog_done);
+ devc->sent_samples += min;
+ devc->sent_frame_samples += min;
devc->spent_us += todo_us;
+ if (devc->limit_frames && devc->sent_frame_samples >= SAMPLES_PER_FRAME) {
+ std_session_send_frame_end(sdi);
+ devc->sent_frame_samples = 0;
+ devc->limit_frames--;
+ if (!devc->limit_frames) {
+ sr_dbg("Requested number of frames reached.");
+ sr_dev_acquisition_stop(sdi);
+ }
+ }
+
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) {
+ if (devc->avg && devc->avg_samples == 0) {
g_hash_table_iter_init(&iter, devc->ch_ag);
while (g_hash_table_iter_next(&iter, NULL, &value)) {
ag = value;
}
}
sr_dbg("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
+ } else if (devc->limit_frames) {
+ if (devc->sent_frame_samples == 0)
+ std_session_send_frame_begin(sdi);
}
return G_SOURCE_CONTINUE;
#define LOGIC_BUFSIZE 4096
/* Size of the analog pattern space per channel. */
#define ANALOG_BUFSIZE 4096
-
-/* Private, per-device-instance driver context. */
-struct dev_context {
- uint64_t cur_samplerate;
- uint64_t limit_samples;
- uint64_t limit_msec;
- uint64_t sent_samples;
- int64_t start_us;
- int64_t spent_us;
- uint64_t step;
- /* Logic */
- int32_t num_logic_channels;
- unsigned int logic_unitsize;
- /* There is only ever one logic channel group, so its pattern goes here. */
- uint8_t logic_pattern;
- unsigned char logic_data[LOGIC_BUFSIZE];
- /* Analog */
- int32_t num_analog_channels;
- GHashTable *ch_ag;
- gboolean avg; /* True if averaging is enabled */
- uint64_t avg_samples;
-};
+/* This is a development feature: it starts a new frame every n samples. */
+#define SAMPLES_PER_FRAME 1000UL
+#define DEFAULT_LIMIT_FRAMES 0
/* Logic patterns we can generate. */
-enum {
+enum logic_pattern_type {
/**
* Spells "sigrok" across 8 channels using '0's (with '1's as
* "background") when displayed using the 'bits' output format.
* something that can get recognized.
*/
PATTERN_SQUID,
+
+ /** Gray encoded data, like rotary encoder signals. */
+ PATTERN_GRAYCODE,
};
/* Analog patterns we can generate. */
-enum {
- /**
- * Square wave.
- */
+enum analog_pattern_type {
PATTERN_SQUARE,
PATTERN_SINE,
PATTERN_TRIANGLE,
PATTERN_SAWTOOTH,
};
+struct dev_context {
+ uint64_t cur_samplerate;
+ uint64_t limit_samples;
+ uint64_t limit_msec;
+ uint64_t limit_frames;
+ uint64_t sent_samples;
+ uint64_t sent_frame_samples; /* Number of samples that were sent for current frame. */
+ int64_t start_us;
+ int64_t spent_us;
+ uint64_t step;
+ /* Logic */
+ int32_t num_logic_channels;
+ size_t logic_unitsize;
+ uint64_t all_logic_channels_mask;
+ /* There is only ever one logic channel group, so its pattern goes here. */
+ enum logic_pattern_type logic_pattern;
+ uint8_t logic_data[LOGIC_BUFSIZE];
+ /* Analog */
+ int32_t num_analog_channels;
+ GHashTable *ch_ag;
+ gboolean avg; /* True if averaging is enabled */
+ uint64_t avg_samples;
+ size_t enabled_logic_channels;
+ size_t enabled_analog_channels;
+ size_t first_partial_logic_index;
+ uint8_t first_partial_logic_mask;
+ /* Triggers */
+ uint64_t capture_ratio;
+ gboolean trigger_fired;
+ struct soft_trigger_logic *stl;
+};
+
static const char *analog_pattern_str[] = {
"square",
"sine",
};
struct analog_gen {
- int pattern;
+ struct sr_channel *ch;
+ enum analog_pattern_type pattern;
float amplitude;
float pattern_data[ANALOG_BUFSIZE];
unsigned int num_samples;
struct sr_analog_meaning meaning;
struct sr_analog_spec spec;
float avg_val; /* Average value */
- unsigned num_avgs; /* Number of samples averaged */
+ unsigned int num_avgs; /* Number of samples averaged */
};
SR_PRIV void demo_generate_analog_pattern(struct analog_gen *ag, uint64_t sample_rate);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include "protocol.h"
+
+static const struct dslogic_profile supported_device[] = {
+ /* DreamSourceLab DSLogic */
+ { 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL,
+ "dreamsourcelab-dslogic-fx2.fw",
+ 0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024},
+ /* DreamSourceLab DSCope */
+ { 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL,
+ "dreamsourcelab-dscope-fx2.fw",
+ 0, "DreamSourceLab", "DSCope", 256 * 1024 * 1024},
+ /* DreamSourceLab DSLogic Pro */
+ { 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL,
+ "dreamsourcelab-dslogic-pro-fx2.fw",
+ 0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024},
+ /* DreamSourceLab DSLogic Plus */
+ { 0x2a0e, 0x0020, "DreamSourceLab", "DSLogic Plus", NULL,
+ "dreamsourcelab-dslogic-plus-fx2.fw",
+ 0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024},
+ /* DreamSourceLab DSLogic Basic */
+ { 0x2a0e, 0x0021, "DreamSourceLab", "DSLogic Basic", NULL,
+ "dreamsourcelab-dslogic-basic-fx2.fw",
+ 0, "DreamSourceLab", "DSLogic", 256 * 1024},
+
+ ALL_ZERO
+};
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS | SR_CONF_SET | SR_CONF_GET,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_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,
+ SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | 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 char *signal_edges[] = {
+ [DS_EDGE_RISING] = "rising",
+ [DS_EDGE_FALLING] = "falling",
+};
+
+static const double thresholds[][2] = {
+ { 0.7, 1.4 },
+ { 1.4, 3.6 },
+};
+
+static const uint64_t 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),
+};
+
+static gboolean is_plausible(const struct libusb_device_descriptor *des)
+{
+ int i;
+
+ for (i = 0; supported_device[i].vid; i++) {
+ if (des->idVendor != supported_device[i].vid)
+ continue;
+ if (des->idProduct == supported_device[i].pid)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+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_channel_group *cg;
+ struct sr_config *src;
+ const struct dslogic_profile *prof;
+ GSList *l, *devices, *conn_devices;
+ gboolean has_firmware;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ struct libusb_device_handle *hdl;
+ int ret, i, j;
+ const char *conn;
+ char manufacturer[64], product[64], serial_num[64], connection_id[64];
+ char channel_name[16];
+
+ drvc = di->context;
+
+ conn = NULL;
+ for (l = options; l; l = l->next) {
+ src = l->data;
+ switch (src->key) {
+ case SR_CONF_CONN:
+ conn = g_variant_get_string(src->data, NULL);
+ break;
+ }
+ }
+ if (conn)
+ conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+ else
+ conn_devices = NULL;
+
+ /* Find all DSLogic compatible devices and upload firmware to them. */
+ devices = NULL;
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if (conn) {
+ usb = NULL;
+ for (l = conn_devices; l; l = l->next) {
+ usb = l->data;
+ if (usb->bus == libusb_get_bus_number(devlist[i])
+ && usb->address == libusb_get_device_address(devlist[i]))
+ break;
+ }
+ if (!l)
+ /* This device matched none of the ones that
+ * matched the conn specification. */
+ continue;
+ }
+
+ libusb_get_device_descriptor(devlist[i], &des);
+
+ if (!is_plausible(&des))
+ continue;
+
+ if ((ret = libusb_open(devlist[i], &hdl)) < 0) {
+ sr_warn("Failed to open potential device with "
+ "VID:PID %04x:%04x: %s.", des.idVendor,
+ des.idProduct, libusb_error_name(ret));
+ continue;
+ }
+
+ if (des.iManufacturer == 0) {
+ manufacturer[0] = '\0';
+ } else if ((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));
+ continue;
+ }
+
+ 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;
+ }
+
+ libusb_close(hdl);
+
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
+ prof = NULL;
+ for (j = 0; supported_device[j].vid; j++) {
+ if (des.idVendor == supported_device[j].vid &&
+ des.idProduct == supported_device[j].pid) {
+ prof = &supported_device[j];
+ break;
+ }
+ }
+
+ if (!prof)
+ continue;
+
+ 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->serial_num = g_strdup(serial_num);
+ sdi->connection_id = g_strdup(connection_id);
+
+ /* Logic channels, all in one channel group. */
+ cg = g_malloc0(sizeof(struct sr_channel_group));
+ cg->name = g_strdup("Logic");
+ for (j = 0; j < NUM_CHANNELS; j++) {
+ sprintf(channel_name, "%d", j);
+ ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC,
+ TRUE, channel_name);
+ cg->channels = g_slist_append(cg->channels, ch);
+ }
+ sdi->channel_groups = g_slist_append(NULL, cg);
+
+ devc = dslogic_dev_new();
+ devc->profile = prof;
+ sdi->priv = devc;
+ devices = g_slist_append(devices, sdi);
+
+ devc->samplerates = samplerates;
+ devc->num_samplerates = ARRAY_SIZE(samplerates);
+ has_firmware = usb_match_manuf_prod(devlist[i], "DreamSourceLab", "USB-based Instrument");
+
+ if (has_firmware) {
+ /* Already has the firmware, so fix the new address. */
+ sr_dbg("Found a DSLogic device.");
+ sdi->status = SR_ST_INACTIVE;
+ 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);
+ } else {
+ 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.%d (logical), name %s.",
+ libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]),
+ prof->firmware);
+ }
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+ 0xff, NULL);
+ }
+ }
+ libusb_free_device_list(devlist, 1);
+ g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+
+ return std_scan_complete(di, devices);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_dev_driver *di = sdi->driver;
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+ int ret;
+ int64_t timediff_us, timediff_ms;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ /*
+ * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
+ * milliseconds for the FX2 to renumerate.
+ */
+ ret = SR_ERR;
+ if (devc->fw_updated > 0) {
+ sr_info("Waiting for device to reset.");
+ /* 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) {
+ if ((ret = dslogic_dev_open(sdi, di)) == 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);
+ } else {
+ sr_info("Firmware upload was not needed.");
+ ret = dslogic_dev_open(sdi, di);
+ }
+
+ if (ret != SR_OK) {
+ sr_err("Unable to open device.");
+ return SR_ERR;
+ }
+
+ ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+ if (ret != 0) {
+ switch (ret) {
+ case LIBUSB_ERROR_BUSY:
+ sr_err("Unable to claim USB interface. Another "
+ "program or driver has already claimed it.");
+ break;
+ case LIBUSB_ERROR_NO_DEVICE:
+ sr_err("Device has been disconnected.");
+ break;
+ default:
+ sr_err("Unable to claim interface: %s.",
+ libusb_error_name(ret));
+ break;
+ }
+
+ return SR_ERR;
+ }
+
+
+ if ((ret = dslogic_fpga_firmware_upload(sdi)) != SR_OK)
+ return ret;
+
+ if (devc->cur_samplerate == 0) {
+ /* Samplerate hasn't been set; default to the slowest one. */
+ devc->cur_samplerate = devc->samplerates[0];
+ }
+
+ if (devc->cur_threshold == 0.0) {
+ devc->cur_threshold = thresholds[1][0];
+ return dslogic_set_voltage_threshold(sdi, devc->cur_threshold);
+ }
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ usb = sdi->conn;
+
+ if (!usb->devhdl)
+ return SR_ERR_BUG;
+
+ sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
+ usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+
+ return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int idx;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_CONN:
+ if (!sdi->conn)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ if (usb->address == 255)
+ /* Device still needs to re-enumerate after firmware
+ * upload, so we don't know its (future) address. */
+ return SR_ERR;
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ if (!strcmp(devc->profile->model, "DSLogic")) {
+ if ((idx = std_double_tuple_idx_d0(devc->cur_threshold,
+ ARRAY_AND_SIZE(thresholds))) < 0)
+ return SR_ERR_BUG;
+ *data = std_gvar_tuple_double(thresholds[idx][0],
+ thresholds[idx][1]);
+ } else {
+ *data = std_gvar_tuple_double(devc->cur_threshold, devc->cur_threshold);
+ }
+ break;
+ 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_EXTERNAL_CLOCK:
+ *data = g_variant_new_boolean(devc->external_clock);
+ break;
+ case SR_CONF_CONTINUOUS:
+ *data = g_variant_new_boolean(devc->continuous_mode);
+ break;
+ case SR_CONF_CLOCK_EDGE:
+ idx = devc->clock_edge;
+ if (idx >= (int)ARRAY_SIZE(signal_edges))
+ return SR_ERR_BUG;
+ *data = g_variant_new_string(signal_edges[0]);
+ 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;
+ int idx;
+ gdouble low, high;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ if ((idx = std_u64_idx(data, devc->samplerates, devc->num_samplerates)) < 0)
+ return SR_ERR_ARG;
+ devc->cur_samplerate = devc->samplerates[idx];
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ devc->capture_ratio = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ if (!strcmp(devc->profile->model, "DSLogic")) {
+ if ((idx = std_double_tuple_idx(data, ARRAY_AND_SIZE(thresholds))) < 0)
+ return SR_ERR_ARG;
+ devc->cur_threshold = thresholds[idx][0];
+ return dslogic_fpga_firmware_upload(sdi);
+ } else {
+ g_variant_get(data, "(dd)", &low, &high);
+ return dslogic_set_voltage_threshold(sdi, (low + high) / 2.0);
+ }
+ break;
+ case SR_CONF_EXTERNAL_CLOCK:
+ devc->external_clock = g_variant_get_boolean(data);
+ break;
+ case SR_CONF_CONTINUOUS:
+ devc->continuous_mode = g_variant_get_boolean(data);
+ break;
+ case SR_CONF_CLOCK_EDGE:
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edges))) < 0)
+ return SR_ERR_ARG;
+ devc->clock_edge = idx;
+ 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;
+
+ devc = (sdi) ? sdi->priv : NULL;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ if (!devc || !devc->profile)
+ return SR_ERR_ARG;
+ if (!strcmp(devc->profile->model, "DSLogic"))
+ *data = std_gvar_thresholds(ARRAY_AND_SIZE(thresholds));
+ else
+ *data = std_gvar_min_max_step_thresholds(0.0, 5.0, 0.1);
+ break;
+ case SR_CONF_SAMPLERATE:
+ if (!devc)
+ return SR_ERR_ARG;
+ *data = std_gvar_samplerates(devc->samplerates, devc->num_samplerates);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
+ break;
+ case SR_CONF_CLOCK_EDGE:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(signal_edges));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static struct sr_dev_driver dreamsourcelab_dslogic_driver_info = {
+ .name = "dreamsourcelab-dslogic",
+ .longname = "DreamSourceLab DSLogic",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dslogic_acquisition_start,
+ .dev_acquisition_stop = dslogic_acquisition_stop,
+ .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(dreamsourcelab_dslogic_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "protocol.h"
+
+#define DS_CMD_GET_FW_VERSION 0xb0
+#define DS_CMD_GET_REVID_VERSION 0xb1
+#define DS_CMD_START 0xb2
+#define DS_CMD_CONFIG 0xb3
+#define DS_CMD_SETTING 0xb4
+#define DS_CMD_CONTROL 0xb5
+#define DS_CMD_STATUS 0xb6
+#define DS_CMD_STATUS_INFO 0xb7
+#define DS_CMD_WR_REG 0xb8
+#define DS_CMD_WR_NVM 0xb9
+#define DS_CMD_RD_NVM 0xba
+#define DS_CMD_RD_NVM_PRE 0xbb
+#define DS_CMD_GET_HW_INFO 0xbc
+
+#define DS_START_FLAGS_STOP (1 << 7)
+#define DS_START_FLAGS_CLK_48MHZ (1 << 6)
+#define DS_START_FLAGS_SAMPLE_WIDE (1 << 5)
+#define DS_START_FLAGS_MODE_LA (1 << 4)
+
+#define DS_ADDR_COMB 0x68
+#define DS_ADDR_EEWP 0x70
+#define DS_ADDR_VTH 0x78
+
+#define DS_MAX_LOGIC_DEPTH SR_MHZ(16)
+#define DS_MAX_LOGIC_SAMPLERATE SR_MHZ(100)
+#define DS_MAX_TRIG_PERCENT 90
+
+#define DS_MODE_TRIG_EN (1 << 0)
+#define DS_MODE_CLK_TYPE (1 << 1)
+#define DS_MODE_CLK_EDGE (1 << 2)
+#define DS_MODE_RLE_MODE (1 << 3)
+#define DS_MODE_DSO_MODE (1 << 4)
+#define DS_MODE_HALF_MODE (1 << 5)
+#define DS_MODE_QUAR_MODE (1 << 6)
+#define DS_MODE_ANALOG_MODE (1 << 7)
+#define DS_MODE_FILTER (1 << 8)
+#define DS_MODE_INSTANT (1 << 9)
+#define DS_MODE_STRIG_MODE (1 << 11)
+#define DS_MODE_STREAM_MODE (1 << 12)
+#define DS_MODE_LPB_TEST (1 << 13)
+#define DS_MODE_EXT_TEST (1 << 14)
+#define DS_MODE_INT_TEST (1 << 15)
+
+#define DSLOGIC_ATOMIC_SAMPLES (sizeof(uint64_t) * 8)
+#define DSLOGIC_ATOMIC_BYTES sizeof(uint64_t)
+
+/*
+ * The FPGA is configured with TLV tuples. Length is specified as the
+ * number of 16-bit words.
+ */
+#define _DS_CFG(variable, wordcnt) ((variable << 8) | wordcnt)
+#define DS_CFG_START 0xf5a5f5a5
+#define DS_CFG_MODE _DS_CFG(0, 1)
+#define DS_CFG_DIVIDER _DS_CFG(1, 2)
+#define DS_CFG_COUNT _DS_CFG(3, 2)
+#define DS_CFG_TRIG_POS _DS_CFG(5, 2)
+#define DS_CFG_TRIG_GLB _DS_CFG(7, 1)
+#define DS_CFG_CH_EN _DS_CFG(8, 1)
+#define DS_CFG_TRIG _DS_CFG(64, 160)
+#define DS_CFG_END 0xfa5afa5a
+
+#pragma pack(push, 1)
+
+struct version_info {
+ uint8_t major;
+ uint8_t minor;
+};
+
+struct cmd_start_acquisition {
+ uint8_t flags;
+ uint8_t sample_delay_h;
+ uint8_t sample_delay_l;
+};
+
+struct fpga_config {
+ uint32_t sync;
+
+ uint16_t mode_header;
+ uint16_t mode;
+ uint16_t divider_header;
+ uint32_t divider;
+ uint16_t count_header;
+ uint32_t count;
+ uint16_t trig_pos_header;
+ uint32_t trig_pos;
+ uint16_t trig_glb_header;
+ uint16_t trig_glb;
+ uint16_t ch_en_header;
+ uint16_t ch_en;
+
+ uint16_t trig_header;
+ uint16_t trig_mask0[NUM_TRIGGER_STAGES];
+ uint16_t trig_mask1[NUM_TRIGGER_STAGES];
+ uint16_t trig_value0[NUM_TRIGGER_STAGES];
+ uint16_t trig_value1[NUM_TRIGGER_STAGES];
+ uint16_t trig_edge0[NUM_TRIGGER_STAGES];
+ uint16_t trig_edge1[NUM_TRIGGER_STAGES];
+ uint16_t trig_logic0[NUM_TRIGGER_STAGES];
+ uint16_t trig_logic1[NUM_TRIGGER_STAGES];
+ uint32_t trig_count[NUM_TRIGGER_STAGES];
+
+ uint32_t end_sync;
+};
+
+#pragma pack(pop)
+
+/*
+ * This should be larger than the FPGA bitstream image so that it'll get
+ * uploaded in one big operation. There seem to be issues when uploading
+ * it in chunks.
+ */
+#define FW_BUFSIZE (1024 * 1024)
+
+#define FPGA_UPLOAD_DELAY (10 * 1000)
+
+#define USB_TIMEOUT (3 * 1000)
+
+static int command_get_fw_version(libusb_device_handle *devhdl,
+ struct version_info *vi)
+{
+ int ret;
+
+ ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_IN, DS_CMD_GET_FW_VERSION, 0x0000, 0x0000,
+ (unsigned char *)vi, sizeof(struct version_info), USB_TIMEOUT);
+
+ if (ret < 0) {
+ sr_err("Unable to get version info: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid)
+{
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ libusb_device_handle *devhdl = usb->devhdl;
+ int ret;
+
+ ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_IN, DS_CMD_GET_REVID_VERSION, 0x0000, 0x0000,
+ revid, 1, USB_TIMEOUT);
+
+ if (ret < 0) {
+ sr_err("Unable to get REVID: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int command_start_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ struct dslogic_mode mode;
+ int ret;
+
+ mode.flags = DS_START_FLAGS_MODE_LA | DS_START_FLAGS_SAMPLE_WIDE;
+ 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(mode), USB_TIMEOUT);
+ if (ret < 0) {
+ sr_err("Failed to send start command: %s.", libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int command_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_firmware_upload(const struct sr_dev_inst *sdi)
+{
+ const char *name = NULL;
+ uint64_t sum;
+ struct sr_resource bitstream;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ unsigned char *buf;
+ ssize_t chunksize;
+ int transferred;
+ int result, ret;
+ const uint8_t cmd[3] = {0, 0, 0};
+
+ drvc = sdi->driver->context;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (!strcmp(devc->profile->model, "DSLogic")) {
+ if (devc->cur_threshold < 1.40)
+ name = DSLOGIC_FPGA_FIRMWARE_3V3;
+ else
+ name = DSLOGIC_FPGA_FIRMWARE_5V;
+ } else if (!strcmp(devc->profile->model, "DSLogic Pro")){
+ name = DSLOGIC_PRO_FPGA_FIRMWARE;
+ } else if (!strcmp(devc->profile->model, "DSLogic Plus")){
+ name = DSLOGIC_PLUS_FPGA_FIRMWARE;
+ } else if (!strcmp(devc->profile->model, "DSLogic Basic")){
+ name = DSLOGIC_BASIC_FPGA_FIRMWARE;
+ } else if (!strcmp(devc->profile->model, "DSCope")) {
+ name = DSCOPE_FPGA_FIRMWARE;
+ } else {
+ sr_err("Failed to select FPGA firmware.");
+ return SR_ERR;
+ }
+
+ 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. */
+ if ((ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_OUT, DS_CMD_CONFIG, 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;
+}
+
+static unsigned int enabled_channel_count(const struct sr_dev_inst *sdi)
+{
+ unsigned int count = 0;
+ for (const GSList *l = sdi->channels; l; l = l->next) {
+ const struct sr_channel *const probe = (struct sr_channel *)l->data;
+ if (probe->enabled)
+ count++;
+ }
+ return count;
+}
+
+static uint16_t enabled_channel_mask(const struct sr_dev_inst *sdi)
+{
+ unsigned int mask = 0;
+ for (const GSList *l = sdi->channels; l; l = l->next) {
+ const struct sr_channel *const probe = (struct sr_channel *)l->data;
+ if (probe->enabled)
+ mask |= 1 << probe->index;
+ }
+ return mask;
+}
+
+/*
+ * Get the session trigger and configure the FPGA structure
+ * accordingly.
+ * @return @c true if any triggers are enabled, @c false otherwise.
+ */
+static bool set_trigger(const struct sr_dev_inst *sdi, struct fpga_config *cfg)
+{
+ struct sr_trigger *trigger;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ struct dev_context *devc;
+ const GSList *l, *m;
+ const unsigned int num_enabled_channels = enabled_channel_count(sdi);
+ int num_trigger_stages = 0;
+
+ int channelbit, i = 0;
+ uint32_t trigger_point;
+
+ devc = sdi->priv;
+
+ cfg->ch_en = enabled_channel_mask(sdi);
+
+ for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
+ cfg->trig_mask0[i] = 0xffff;
+ cfg->trig_mask1[i] = 0xffff;
+ cfg->trig_value0[i] = 0;
+ cfg->trig_value1[i] = 0;
+ cfg->trig_edge0[i] = 0;
+ cfg->trig_edge1[i] = 0;
+ cfg->trig_logic0[i] = 2;
+ cfg->trig_logic1[i] = 2;
+ cfg->trig_count[i] = 0;
+ }
+
+ trigger_point = (devc->capture_ratio * devc->limit_samples) / 100;
+ if (trigger_point < DSLOGIC_ATOMIC_SAMPLES)
+ trigger_point = DSLOGIC_ATOMIC_SAMPLES;
+ const uint32_t mem_depth = devc->profile->mem_depth;
+ const uint32_t max_trigger_point = devc->continuous_mode ? ((mem_depth * 10) / 100) :
+ ((mem_depth * DS_MAX_TRIG_PERCENT) / 100);
+ if (trigger_point > max_trigger_point)
+ trigger_point = max_trigger_point;
+ cfg->trig_pos = trigger_point & ~(DSLOGIC_ATOMIC_SAMPLES - 1);
+
+ if (!(trigger = sr_session_trigger_get(sdi->session))) {
+ sr_dbg("No session trigger found");
+ return false;
+ }
+
+ for (l = trigger->stages; l; l = l->next) {
+ stage = l->data;
+ num_trigger_stages++;
+ 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);
+ /* Simple trigger support (event). */
+ if (match->match == SR_TRIGGER_ONE) {
+ cfg->trig_mask0[0] &= ~channelbit;
+ cfg->trig_mask1[0] &= ~channelbit;
+ cfg->trig_value0[0] |= channelbit;
+ cfg->trig_value1[0] |= channelbit;
+ } else if (match->match == SR_TRIGGER_ZERO) {
+ cfg->trig_mask0[0] &= ~channelbit;
+ cfg->trig_mask1[0] &= ~channelbit;
+ } else if (match->match == SR_TRIGGER_FALLING) {
+ cfg->trig_mask0[0] &= ~channelbit;
+ cfg->trig_mask1[0] &= ~channelbit;
+ cfg->trig_edge0[0] |= channelbit;
+ cfg->trig_edge1[0] |= channelbit;
+ } else if (match->match == SR_TRIGGER_RISING) {
+ cfg->trig_mask0[0] &= ~channelbit;
+ cfg->trig_mask1[0] &= ~channelbit;
+ cfg->trig_value0[0] |= channelbit;
+ cfg->trig_value1[0] |= channelbit;
+ cfg->trig_edge0[0] |= channelbit;
+ cfg->trig_edge1[0] |= channelbit;
+ } else if (match->match == SR_TRIGGER_EDGE) {
+ cfg->trig_edge0[0] |= channelbit;
+ cfg->trig_edge1[0] |= channelbit;
+ }
+ }
+ }
+
+ cfg->trig_glb = (num_enabled_channels << 4) | (num_trigger_stages - 1);
+
+ return num_trigger_stages != 0;
+}
+
+static int fpga_configure(const struct sr_dev_inst *sdi)
+{
+ const struct dev_context *const devc = sdi->priv;
+ const struct sr_usb_dev_inst *const usb = sdi->conn;
+ uint8_t c[3];
+ struct fpga_config cfg;
+ uint16_t mode = 0;
+ uint32_t divider;
+ int transferred, len, ret;
+
+ sr_dbg("Configuring FPGA.");
+
+ WL32(&cfg.sync, DS_CFG_START);
+ WL16(&cfg.mode_header, DS_CFG_MODE);
+ WL16(&cfg.divider_header, DS_CFG_DIVIDER);
+ WL16(&cfg.count_header, DS_CFG_COUNT);
+ WL16(&cfg.trig_pos_header, DS_CFG_TRIG_POS);
+ WL16(&cfg.trig_glb_header, DS_CFG_TRIG_GLB);
+ WL16(&cfg.ch_en_header, DS_CFG_CH_EN);
+ WL16(&cfg.trig_header, DS_CFG_TRIG);
+ WL32(&cfg.end_sync, DS_CFG_END);
+
+ /* Pass in the length of a fixed-size struct. Really. */
+ len = sizeof(struct 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_SETTING, 0x0000, 0x0000,
+ c, sizeof(c), USB_TIMEOUT);
+ if (ret < 0) {
+ sr_err("Failed to send FPGA configure command: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ if (set_trigger(sdi, &cfg))
+ mode |= DS_MODE_TRIG_EN;
+
+ if (devc->mode == DS_OP_INTERNAL_TEST)
+ mode |= DS_MODE_INT_TEST;
+ else if (devc->mode == DS_OP_EXTERNAL_TEST)
+ mode |= DS_MODE_EXT_TEST;
+ else if (devc->mode == DS_OP_LOOPBACK_TEST)
+ mode |= DS_MODE_LPB_TEST;
+
+ if (devc->cur_samplerate == DS_MAX_LOGIC_SAMPLERATE * 2)
+ mode |= DS_MODE_HALF_MODE;
+ else if (devc->cur_samplerate == DS_MAX_LOGIC_SAMPLERATE * 4)
+ mode |= DS_MODE_QUAR_MODE;
+
+ if (devc->continuous_mode)
+ mode |= DS_MODE_STREAM_MODE;
+ if (devc->external_clock) {
+ mode |= DS_MODE_CLK_TYPE;
+ if (devc->clock_edge == DS_EDGE_FALLING)
+ mode |= DS_MODE_CLK_EDGE;
+ }
+ if (devc->limit_samples > DS_MAX_LOGIC_DEPTH *
+ ceil(devc->cur_samplerate * 1.0 / DS_MAX_LOGIC_SAMPLERATE)
+ && !devc->continuous_mode) {
+ /* Enable RLE for long captures.
+ * Without this, captured data present errors.
+ */
+ mode |= DS_MODE_RLE_MODE;
+ }
+
+ WL16(&cfg.mode, mode);
+ divider = ceil(DS_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate);
+ WL32(&cfg.divider, divider);
+
+ /* Number of 16-sample units. */
+ WL32(&cfg.count, devc->limit_samples / 16);
+
+ len = sizeof(struct 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;
+}
+
+SR_PRIV int dslogic_set_voltage_threshold(const struct sr_dev_inst *sdi, double threshold)
+{
+ int ret;
+ struct dev_context *const devc = sdi->priv;
+ const struct sr_usb_dev_inst *const usb = sdi->conn;
+ const uint8_t value = (threshold / 5.0) * 255;
+ const uint16_t cmd = value | (DS_ADDR_VTH << 8);
+
+ /* Send the control command. */
+ ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT,
+ DS_CMD_WR_REG, 0x0000, 0x0000,
+ (unsigned char *)&cmd, sizeof(cmd), 3000);
+ if (ret < 0) {
+ sr_err("Unable to set voltage-threshold register: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ devc->cur_threshold = threshold;
+
+ return SR_OK;
+}
+
+SR_PRIV int dslogic_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 dev_context *devc;
+ struct drv_context *drvc;
+ struct version_info vi;
+ int ret = SR_ERR, i, device_count;
+ uint8_t revid;
+ char connection_id[64];
+
+ drvc = di->context;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ if (device_count < 0) {
+ sr_err("Failed to get device list: %s.",
+ libusb_error_name(device_count));
+ return SR_ERR;
+ }
+
+ for (i = 0; i < device_count; i++) {
+ libusb_get_device_descriptor(devlist[i], &des);
+
+ if (des.idVendor != devc->profile->vid
+ || des.idProduct != devc->profile->pid)
+ continue;
+
+ if ((sdi->status == SR_ST_INITIALIZING) ||
+ (sdi->status == SR_ST_INACTIVE)) {
+ /* Check device by its physical USB bus/port address. */
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
+ if (strcmp(sdi->connection_id, connection_id))
+ /* This is not the one. */
+ continue;
+ }
+
+ if (!(ret = libusb_open(devlist[i], &usb->devhdl))) {
+ if (usb->address == 0xff)
+ /*
+ * First time we touch this device after FW
+ * upload, so we don't know the address yet.
+ */
+ usb->address = libusb_get_device_address(devlist[i]);
+ } else {
+ sr_err("Failed to open device: %s.",
+ libusb_error_name(ret));
+ ret = SR_ERR;
+ break;
+ }
+
+ 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));
+ ret = SR_ERR;
+ break;
+ }
+ }
+ }
+
+ ret = command_get_fw_version(usb->devhdl, &vi);
+ if (ret != SR_OK) {
+ sr_err("Failed to get firmware version.");
+ break;
+ }
+
+ ret = command_get_revid_version(sdi, &revid);
+ if (ret != SR_OK) {
+ sr_err("Failed to get REVID.");
+ break;
+ }
+
+ /*
+ * Changes in major version mean incompatible/API changes, so
+ * bail out if we encounter an incompatible version.
+ * Different minor versions are OK, they should be compatible.
+ */
+ if (vi.major != DSLOGIC_REQUIRED_VERSION_MAJOR) {
+ sr_err("Expected firmware version %d.x, "
+ "got %d.%d.", DSLOGIC_REQUIRED_VERSION_MAJOR,
+ vi.major, vi.minor);
+ ret = SR_ERR;
+ break;
+ }
+
+ sr_info("Opened device on %d.%d (logical) / %s (physical), "
+ "interface %d, firmware %d.%d.",
+ usb->bus, usb->address, connection_id,
+ USB_INTERFACE, vi.major, vi.minor);
+
+ sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.",
+ revid, (revid != 1) ? " (FX2)" : "A (FX2LP)");
+
+ ret = SR_OK;
+
+ break;
+ }
+
+ libusb_free_device_list(devlist, 1);
+
+ return ret;
+}
+
+SR_PRIV struct dev_context *dslogic_dev_new(void)
+{
+ struct dev_context *devc;
+
+ 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->continuous_mode = FALSE;
+ devc->clock_edge = DS_EDGE_RISING;
+
+ return devc;
+}
+
+static void abort_acquisition(struct dev_context *devc)
+{
+ int i;
+
+ devc->acq_aborted = TRUE;
+
+ for (i = devc->num_transfers - 1; i >= 0; i--) {
+ if (devc->transfers[i])
+ libusb_cancel_transfer(devc->transfers[i]);
+ }
+}
+
+static void finish_acquisition(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ std_session_send_df_end(sdi);
+
+ usb_source_remove(sdi->session, devc->ctx);
+
+ devc->num_transfers = 0;
+ g_free(devc->transfers);
+ g_free(devc->deinterleave_buffer);
+}
+
+static void free_transfer(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ unsigned int i;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+
+ g_free(transfer->buffer);
+ transfer->buffer = NULL;
+ libusb_free_transfer(transfer);
+
+ for (i = 0; i < devc->num_transfers; i++) {
+ if (devc->transfers[i] == transfer) {
+ devc->transfers[i] = NULL;
+ break;
+ }
+ }
+
+ devc->submitted_transfers--;
+ if (devc->submitted_transfers == 0)
+ finish_acquisition(sdi);
+}
+
+static void resubmit_transfer(struct libusb_transfer *transfer)
+{
+ int ret;
+
+ if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS)
+ return;
+
+ sr_err("%s: %s", __func__, libusb_error_name(ret));
+ free_transfer(transfer);
+
+}
+
+static void deinterleave_buffer(const uint8_t *src, size_t length,
+ uint16_t *dst_ptr, size_t channel_count, uint16_t channel_mask)
+{
+ uint16_t sample;
+
+ for (const uint64_t *src_ptr = (uint64_t*)src;
+ src_ptr < (uint64_t*)(src + length);
+ src_ptr += channel_count) {
+ for (int bit = 0; bit != 64; bit++) {
+ const uint64_t *word_ptr = src_ptr;
+ sample = 0;
+ for (unsigned int channel = 0; channel != 16;
+ channel++) {
+ const uint16_t m = channel_mask >> channel;
+ if (!m)
+ break;
+ if ((m & 1) && ((*word_ptr++ >> bit) & UINT64_C(1)))
+ sample |= 1 << channel;
+ }
+ *dst_ptr++ = sample;
+ }
+ }
+}
+
+static void send_data(struct sr_dev_inst *sdi,
+ uint16_t *data, size_t sample_count)
+{
+ const struct sr_datafeed_logic logic = {
+ .length = sample_count * sizeof(uint16_t),
+ .unitsize = sizeof(uint16_t),
+ .data = data
+ };
+
+ const struct sr_datafeed_packet packet = {
+ .type = SR_DF_LOGIC,
+ .payload = &logic
+ };
+
+ sr_session_send(sdi, &packet);
+}
+
+static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *const sdi = transfer->user_data;
+ struct dev_context *const devc = sdi->priv;
+ const size_t channel_count = enabled_channel_count(sdi);
+ const uint16_t channel_mask = enabled_channel_mask(sdi);
+ const unsigned int cur_sample_count = DSLOGIC_ATOMIC_SAMPLES *
+ transfer->actual_length /
+ (DSLOGIC_ATOMIC_BYTES * channel_count);
+
+ gboolean packet_has_error = FALSE;
+ struct sr_datafeed_packet packet;
+ unsigned int num_samples;
+ int trigger_offset;
+
+ /*
+ * If acquisition has already ended, just free any queued up
+ * transfer that come in.
+ */
+ if (devc->acq_aborted) {
+ free_transfer(transfer);
+ return;
+ }
+
+ 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. */
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ abort_acquisition(devc);
+ free_transfer(transfer);
+ return;
+ case LIBUSB_TRANSFER_COMPLETED:
+ case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */
+ break;
+ default:
+ packet_has_error = TRUE;
+ break;
+ }
+
+ if (transfer->actual_length == 0 || packet_has_error) {
+ devc->empty_transfer_count++;
+ if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) {
+ /*
+ * The FX2 gave up. End the acquisition, the frontend
+ * will work out that the samplecount is short.
+ */
+ abort_acquisition(devc);
+ free_transfer(transfer);
+ } else {
+ resubmit_transfer(transfer);
+ }
+ return;
+ } else {
+ devc->empty_transfer_count = 0;
+ }
+
+ if (!devc->limit_samples || devc->sent_samples < devc->limit_samples) {
+ 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;
+
+ /**
+ * The DSLogic emits sample data as sequences of 64-bit sample words
+ * in a round-robin i.e. 64-bits from channel 0, 64-bits from channel 1
+ * etc. for each of the enabled channels, then looping back to the
+ * channel.
+ *
+ * Because sigrok's internal representation is bit-interleaved channels
+ * we must recast the data.
+ *
+ * Hopefully in future it will be possible to pass the data on as-is.
+ */
+ if (transfer->actual_length % (DSLOGIC_ATOMIC_BYTES * channel_count) != 0)
+ sr_err("Invalid transfer length!");
+ deinterleave_buffer(transfer->buffer, transfer->actual_length,
+ devc->deinterleave_buffer, channel_count, channel_mask);
+
+ /* Send the incoming transfer to the session bus. */
+ if (devc->trigger_pos > devc->sent_samples
+ && devc->trigger_pos <= devc->sent_samples + num_samples) {
+ /* DSLogic trigger in this block. Send trigger position. */
+ trigger_offset = devc->trigger_pos - devc->sent_samples;
+ /* Pre-trigger samples. */
+ send_data(sdi, devc->deinterleave_buffer, trigger_offset);
+ devc->sent_samples += trigger_offset;
+ /* Trigger position. */
+ devc->trigger_pos = 0;
+ packet.type = SR_DF_TRIGGER;
+ packet.payload = NULL;
+ sr_session_send(sdi, &packet);
+ /* Post trigger samples. */
+ num_samples -= trigger_offset;
+ send_data(sdi, devc->deinterleave_buffer
+ + trigger_offset, num_samples);
+ devc->sent_samples += num_samples;
+ } else {
+ send_data(sdi, devc->deinterleave_buffer, num_samples);
+ devc->sent_samples += num_samples;
+ }
+ }
+
+ if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) {
+ abort_acquisition(devc);
+ free_transfer(transfer);
+ } else
+ resubmit_transfer(transfer);
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+ struct timeval tv;
+ struct drv_context *drvc;
+
+ (void)fd;
+ (void)revents;
+
+ drvc = (struct drv_context *)cb_data;
+
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+ return TRUE;
+}
+
+static size_t to_bytes_per_ms(const struct sr_dev_inst *sdi)
+{
+ const struct dev_context *const devc = sdi->priv;
+ const size_t ch_count = enabled_channel_count(sdi);
+
+ if (devc->continuous_mode)
+ return (devc->cur_samplerate * ch_count) / (1000 * 8);
+
+
+ /* If we're in buffered mode, the transfer rate is not so important,
+ * but we expect to get at least 10% of the high-speed USB bandwidth.
+ */
+ return 35000000 / (1000 * 10);
+}
+
+static size_t get_buffer_size(const struct sr_dev_inst *sdi)
+{
+ /*
+ * The buffer should be large enough to hold 10ms of data and
+ * a multiple of the size of a data atom.
+ */
+ const size_t block_size = enabled_channel_count(sdi) * 512;
+ const size_t s = 10 * to_bytes_per_ms(sdi);
+ if (!block_size)
+ return s;
+ return ((s + block_size - 1) / block_size) * block_size;
+}
+
+static unsigned int get_number_of_transfers(const struct sr_dev_inst *sdi)
+{
+ /* Total buffer size should be able to hold about 100ms of data. */
+ const unsigned int s = get_buffer_size(sdi);
+ const unsigned int n = (100 * to_bytes_per_ms(sdi) + s - 1) / s;
+ return (n > NUM_SIMUL_TRANSFERS) ? NUM_SIMUL_TRANSFERS : n;
+}
+
+static unsigned int get_timeout(const struct sr_dev_inst *sdi)
+{
+ const size_t total_size = get_buffer_size(sdi) *
+ get_number_of_transfers(sdi);
+ const unsigned int timeout = total_size / to_bytes_per_ms(sdi);
+ return timeout + timeout / 4; /* Leave a headroom of 25% percent. */
+}
+
+static int start_transfers(const struct sr_dev_inst *sdi)
+{
+ const size_t channel_count = enabled_channel_count(sdi);
+ const size_t size = get_buffer_size(sdi);
+ const unsigned int num_transfers = get_number_of_transfers(sdi);
+ const unsigned int timeout = get_timeout(sdi);
+
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_transfer *transfer;
+ unsigned int i;
+ int ret;
+ unsigned char *buf;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ devc->sent_samples = 0;
+ devc->acq_aborted = FALSE;
+ devc->empty_transfer_count = 0;
+ devc->submitted_transfers = 0;
+
+ g_free(devc->transfers);
+ devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
+ if (!devc->transfers) {
+ sr_err("USB transfers malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+
+ devc->deinterleave_buffer = g_try_malloc(DSLOGIC_ATOMIC_SAMPLES *
+ (size / (channel_count * DSLOGIC_ATOMIC_BYTES)) * sizeof(uint16_t));
+ if (!devc->deinterleave_buffer) {
+ sr_err("Deinterleave buffer malloc failed.");
+ g_free(devc->deinterleave_buffer);
+ return SR_ERR_MALLOC;
+ }
+
+ devc->num_transfers = num_transfers;
+ for (i = 0; i < num_transfers; i++) {
+ if (!(buf = g_try_malloc(size))) {
+ sr_err("USB transfer buffer malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 6 | LIBUSB_ENDPOINT_IN, buf, size,
+ receive_transfer, (void *)sdi, timeout);
+ sr_info("submitting transfer: %d", i);
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ abort_acquisition(devc);
+ return SR_ERR;
+ }
+ devc->transfers[i] = transfer;
+ devc->submitted_transfers++;
+ }
+
+ std_session_send_df_header(sdi);
+
+ return SR_OK;
+}
+
+static void LIBUSB_CALL trigger_receive(struct libusb_transfer *transfer)
+{
+ const struct sr_dev_inst *sdi;
+ struct dslogic_trigger_pos *tpos;
+ struct dev_context *devc;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+ if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
+ sr_dbg("Trigger transfer canceled.");
+ /* Terminate session. */
+ std_session_send_df_end(sdi);
+ usb_source_remove(sdi->session, devc->ctx);
+ devc->num_transfers = 0;
+ g_free(devc->transfers);
+ } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED
+ && transfer->actual_length == sizeof(struct dslogic_trigger_pos)) {
+ tpos = (struct dslogic_trigger_pos *)transfer->buffer;
+ sr_info("tpos real_pos %d ram_saddr %d cnt_h %d cnt_l %d", tpos->real_pos,
+ tpos->ram_saddr, tpos->remain_cnt_h, tpos->remain_cnt_l);
+ devc->trigger_pos = tpos->real_pos;
+ g_free(tpos);
+ start_transfers(sdi);
+ }
+ libusb_free_transfer(transfer);
+}
+
+SR_PRIV int dslogic_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ const unsigned int timeout = get_timeout(sdi);
+
+ struct sr_dev_driver *di;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct dslogic_trigger_pos *tpos;
+ struct libusb_transfer *transfer;
+ int ret;
+
+ di = sdi->driver;
+ drvc = di->context;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ devc->ctx = drvc->sr_ctx;
+ devc->sent_samples = 0;
+ devc->empty_transfer_count = 0;
+ devc->acq_aborted = FALSE;
+
+ usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc);
+
+ if ((ret = command_stop_acquisition(sdi)) != SR_OK)
+ return ret;
+
+ if ((ret = fpga_configure(sdi)) != SR_OK)
+ return ret;
+
+ if ((ret = command_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),
+ 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;
+ }
+
+ devc->transfers = g_try_malloc0(sizeof(*devc->transfers));
+ if (!devc->transfers) {
+ sr_err("USB trigger_pos transfer malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+ devc->num_transfers = 1;
+ devc->submitted_transfers++;
+ devc->transfers[0] = transfer;
+
+ return ret;
+}
+
+SR_PRIV int dslogic_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ command_stop_acquisition(sdi);
+ abort_acquisition(sdi->priv);
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_DREAMSOURCELAB_DSLOGIC_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_DREAMSOURCELAB_DSLOGIC_PROTOCOL_H
+
+#include <glib.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libusb.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "dreamsourcelab-dslogic"
+
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 1
+
+#define MAX_RENUM_DELAY_MS 3000
+#define NUM_SIMUL_TRANSFERS 32
+#define MAX_EMPTY_TRANSFERS (NUM_SIMUL_TRANSFERS * 2)
+
+#define NUM_CHANNELS 16
+#define NUM_TRIGGER_STAGES 16
+
+#define DSLOGIC_REQUIRED_VERSION_MAJOR 1
+
+/* 6 delay states of up to 256 clock ticks */
+#define MAX_SAMPLE_DELAY (6 * 256)
+
+#define DSLOGIC_FPGA_FIRMWARE_5V "dreamsourcelab-dslogic-fpga-5v.fw"
+#define DSLOGIC_FPGA_FIRMWARE_3V3 "dreamsourcelab-dslogic-fpga-3v3.fw"
+#define DSCOPE_FPGA_FIRMWARE "dreamsourcelab-dscope-fpga.fw"
+#define DSLOGIC_PRO_FPGA_FIRMWARE "dreamsourcelab-dslogic-pro-fpga.fw"
+#define DSLOGIC_PLUS_FPGA_FIRMWARE "dreamsourcelab-dslogic-plus-fpga.fw"
+#define DSLOGIC_BASIC_FPGA_FIRMWARE "dreamsourcelab-dslogic-basic-fpga.fw"
+
+enum dslogic_operation_modes {
+ DS_OP_NORMAL,
+ DS_OP_INTERNAL_TEST,
+ DS_OP_EXTERNAL_TEST,
+ DS_OP_LOOPBACK_TEST,
+};
+
+enum dslogic_edge_modes {
+ DS_EDGE_RISING,
+ DS_EDGE_FALLING,
+};
+
+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 check_id;
+ uint32_t real_pos;
+ uint32_t ram_saddr;
+ uint32_t remain_cnt_l;
+ uint32_t remain_cnt_h;
+ uint32_t status;
+ uint8_t first_block[488];
+};
+
+struct dslogic_profile {
+ uint16_t vid;
+ uint16_t pid;
+
+ const char *vendor;
+ const char *model;
+ const char *model_version;
+
+ const char *firmware;
+
+ uint32_t dev_caps;
+
+ const char *usb_manufacturer;
+ const char *usb_product;
+
+ /* Memory depth in bits. */
+ uint64_t mem_depth;
+};
+
+struct dev_context {
+ const struct dslogic_profile *profile;
+ /*
+ * Since we can't keep track of a DSLogic device after upgrading
+ * the firmware (it renumerates into a different device address
+ * after the upgrade) this is like a global lock. No device will open
+ * until a proper delay after the last device was upgraded.
+ */
+ int64_t fw_updated;
+
+ const uint64_t *samplerates;
+ int num_samplerates;
+
+ uint64_t cur_samplerate;
+ uint64_t limit_samples;
+ uint64_t capture_ratio;
+
+ gboolean acq_aborted;
+
+ unsigned int sent_samples;
+ int submitted_transfers;
+ int empty_transfer_count;
+
+ unsigned int num_transfers;
+ struct libusb_transfer **transfers;
+ struct sr_context *ctx;
+
+ uint16_t *deinterleave_buffer;
+
+ uint16_t mode;
+ uint32_t trigger_pos;
+ gboolean external_clock;
+ gboolean continuous_mode;
+ int clock_edge;
+ double cur_threshold;
+};
+
+SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi);
+SR_PRIV int dslogic_set_voltage_threshold(const struct sr_dev_inst *sdi, double threshold);
+SR_PRIV int dslogic_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di);
+SR_PRIV struct dev_context *dslogic_dev_new(void);
+SR_PRIV int dslogic_acquisition_start(const struct sr_dev_inst *sdi);
+SR_PRIV int dslogic_acquisition_stop(struct sr_dev_inst *sdi);
+
+#endif
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 John Chajecki <subs@qcontinuum.plus.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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_SET,
+};
+
+/* Vendor, model, number of channels, poll period */
+static const struct fluke_scpi_dmm_model supported_models[] = {
+ { "FLUKE", "45", 2, 0 },
+};
+
+static struct sr_dev_driver fluke_45_driver_info;
+
+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;
+ const struct scpi_command *cmdset = fluke_45_cmdset;
+ unsigned int i;
+ const struct fluke_scpi_dmm_model *model = NULL;
+ gchar *channel_name;
+ char *response;
+
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->conn = scpi;
+
+ /* Test for serial port ECHO enabled. */
+ sr_scpi_get_string(scpi, "ECHO-TEST", &response);
+ if (strcmp(response, "ECHO-TEST") == 0) {
+ sr_err("Serial port ECHO is ON. Please turn it OFF!");
+ return NULL;
+ }
+
+ /* Get device IDN. */
+ if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+ sr_info("Couldn't get IDN response, retrying.");
+ sr_scpi_close(scpi);
+ sr_scpi_open(scpi);
+ if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+ sr_info("Couldn't get IDN response.");
+ return NULL;
+ }
+ }
+
+ /* Check IDN. */
+ for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
+ if (!g_ascii_strcasecmp(hw_info->manufacturer,
+ supported_models[i].vendor) &&
+ !strcmp(hw_info->model, supported_models[i].model)) {
+ model = &supported_models[i];
+ break;
+ }
+ }
+ if (!model) {
+ sr_scpi_hw_info_free(hw_info);
+ return NULL;
+ }
+
+ /* Set up device parameters. */
+ sdi->vendor = g_strdup(model->vendor);
+ sdi->model = g_strdup(model->model);
+ sdi->version = g_strdup(hw_info->firmware_version);
+ sdi->serial_num = g_strdup(hw_info->serial_number);
+ sdi->conn = scpi;
+ sdi->driver = &fluke_45_driver_info;
+ sdi->inst_type = SR_INST_SCPI;
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ devc->num_channels = model->num_channels;
+ devc->cmdset = cmdset;
+
+ /* Create channels. */
+ for (i = 0; i < devc->num_channels; i++) {
+ channel_name = g_strdup_printf("P%d", i + 1);
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, channel_name);
+ }
+
+ sdi->priv = devc;
+
+ return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ return sr_scpi_scan(di->context, options, probe_device);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi;
+ int ret;
+
+ scpi = sdi->conn;
+
+ if ((ret = sr_scpi_open(scpi) < 0)) {
+ sr_err("Failed to open SCPI device: %s.", sr_strerror(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi;
+
+ scpi = sdi->conn;
+
+ if (!scpi)
+ return SR_ERR_BUG;
+
+ return sr_scpi_close(scpi);
+}
+
+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;
+
+ return sr_sw_limits_config_set(&devc->limits, key, data);
+}
+
+static int config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+}
+
+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;
+
+ return sr_sw_limits_config_get(&devc->limits, key, data);
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi;
+ struct dev_context *devc;
+ int ret;
+
+ scpi = sdi->conn;
+ devc = sdi->priv;
+
+ sr_sw_limits_acquisition_start(&devc->limits);
+ std_session_send_df_header(sdi);
+
+ if ((ret = sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 10,
+ fl45_scpi_receive_data, (void *)sdi)) != SR_OK)
+ return ret;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi;
+ double d;
+
+ 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_double(scpi, NULL, &d);
+ sr_scpi_source_remove(sdi->session, scpi);
+
+ std_session_send_df_end(sdi);
+
+ return SR_OK;
+}
+
+static struct sr_dev_driver fluke_45_driver_info = {
+ .name = "fluke-45",
+ .longname = "Fluke 45",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(fluke_45_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 John Chajecki <subs@qcontinuum.plus.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 <config.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <glib.h>
+#include <errno.h>
+#include <scpi.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/* Get the current state of the meter and sets analog object parameters. */
+SR_PRIV int fl45_get_status(const struct sr_dev_inst *sdi,
+ struct sr_datafeed_analog *analog, int idx)
+{
+ struct dev_context *devc;
+ char *cmd, *func;
+ int res;
+
+ res = 0;
+
+ /* Command string to read current function. */
+ cmd = g_strdup_printf("FUNC%d?", idx + 1);
+ sr_dbg("Sent command: %s.", cmd);
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ /* Default settings. */
+ analog[idx].meaning->mq = 0;
+ analog[idx].meaning->unit = 0;
+ analog[idx].meaning->mqflags = 0;
+
+ /* Get a response to the FUNC? command. */
+ res = fl45_scpi_get_response(sdi, cmd);
+ if (res == SR_ERR)
+ return res;
+ sr_dbg("Response to FUNC: %s.", devc->response);
+
+ /* Set up analog mq, unit and flags. */
+ if (res == SR_OK && devc->response != NULL) {
+ func = devc->response;
+ if (strcmp(func, "AAC") == 0) {
+ analog[idx].meaning->mq = SR_MQ_CURRENT;
+ analog[idx].meaning->unit = SR_UNIT_AMPERE;
+ analog[idx].meaning->mqflags = SR_MQFLAG_AC;
+ } else if (strcmp(func, "AACDC") == 0) {
+ analog[idx].meaning->mq = SR_MQ_CURRENT;
+ analog[idx].meaning->unit = SR_UNIT_AMPERE;
+ analog[idx].meaning->mqflags = SR_MQFLAG_AC;
+ } else if (strcmp(func, "ADC") == 0) {
+ analog[idx].meaning->mq = SR_MQ_CURRENT;
+ analog[idx].meaning->unit = SR_UNIT_AMPERE;
+ analog[idx].meaning->mqflags = SR_MQFLAG_DC;
+ } else if (strcmp(func, "CONT") == 0) {
+ analog[idx].meaning->mq = SR_MQ_CONTINUITY;
+ analog->meaning->unit = SR_UNIT_BOOLEAN;
+ } else if (strcmp(func, "DIODE") == 0) {
+ analog[idx].meaning->mq = SR_MQ_VOLTAGE;
+ analog[idx].meaning->unit = SR_UNIT_VOLT;
+ analog[idx].meaning->mqflags = SR_MQFLAG_DIODE;
+ } else if (strcmp(func, "FREQ") == 0) {
+ analog[idx].meaning->mq = SR_MQ_FREQUENCY;
+ analog[idx].meaning->unit = SR_UNIT_HERTZ;
+ } else if (strcmp(func, "OHMS") == 0) {
+ analog[idx].meaning->mq = SR_MQ_RESISTANCE;
+ analog[idx].meaning->unit = SR_UNIT_OHM;
+ } else if (strcmp(func, "VAC") == 0) {
+ analog[idx].meaning->mq = SR_MQ_VOLTAGE;
+ analog[idx].meaning->unit = SR_UNIT_VOLT;
+ analog[idx].meaning->mqflags = SR_MQFLAG_AC;
+ } else if (strcmp(func, "VACDC") == 0) {
+ analog[idx].meaning->mq = SR_MQ_VOLTAGE;
+ analog[idx].meaning->unit = SR_UNIT_VOLT;
+ analog[idx].meaning->mqflags |= SR_MQFLAG_AC;
+ analog[idx].meaning->mqflags |= SR_MQFLAG_DC;
+ } else if (strcmp(func, "VDC") == 0) {
+ analog[idx].meaning->mq = SR_MQ_VOLTAGE;
+ analog[idx].meaning->unit = SR_UNIT_VOLT;
+ analog[idx].meaning->mqflags = SR_MQFLAG_DC;
+ }
+ }
+
+ /* Is the meter in autorange mode? */
+ res = fl45_scpi_get_response(sdi, "AUTO?");
+ if (res == SR_ERR)
+ return res;
+ sr_dbg("Response to AUTO: %s.", devc->response);
+ if (res == SR_OK && devc->response != NULL) {
+ if (strcmp(devc->response, "1") == 0)
+ analog[idx].meaning->mqflags |= SR_MQFLAG_AUTORANGE;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int fl45_get_modifiers(const struct sr_dev_inst *sdi,
+ struct sr_datafeed_analog *analog, int idx)
+{
+ struct dev_context *devc;
+ int res, mod;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ /* Get modifier value. */
+ res = fl45_scpi_get_response(sdi, "MOD?");
+ if (res == SR_ERR)
+ return res;
+ sr_dbg("Response to MOD: %s.", devc->response);
+ if (res == SR_OK && devc->response != NULL) {
+ mod = atoi(devc->response);
+ if (mod & 0x01) {
+ analog[idx].meaning->mqflags |= SR_MQFLAG_MIN;
+ sr_dbg("MIN bit set: %s.", "1");
+ }
+ if (mod & 0x02) {
+ analog[idx].meaning->mqflags |= SR_MQFLAG_MAX;
+ sr_dbg("MAX bit set: %s.", "2");
+ }
+ if (mod & 0x04) {
+ analog[idx].meaning->mqflags |= SR_MQFLAG_HOLD;
+ sr_dbg("HOLD bit set: %s.", "4");
+ }
+ if (mod & 0x08) {
+ sr_dbg("dB bit set: %s.", "8");
+ analog[idx].meaning->mq = SR_MQ_POWER_FACTOR;
+ analog[idx].meaning->unit = SR_UNIT_DECIBEL_MW;
+ analog[idx].meaning->mqflags = 0;
+ analog[idx].encoding->digits = 2;
+ analog[idx].spec->spec_digits = 2;
+ }
+ if (mod & 0x10) {
+ sr_dbg("dB Power mod bit set: %s.", "16");
+ analog[idx].meaning->mq = SR_MQ_POWER;
+ analog[idx].meaning->unit = SR_UNIT_DECIBEL_SPL;
+ analog[idx].meaning->mqflags = 0;
+ analog[idx].encoding->digits = 2;
+ analog[idx].spec->spec_digits = 2;
+ }
+ if (mod & 0x20) {
+ sr_dbg("REL bit set: %s.", "32");
+ analog[idx].meaning->mqflags |= SR_MQFLAG_HOLD;
+ }
+ }
+
+ return SR_OK;
+}
+
+int get_reading_dd(char *reading, size_t size)
+{
+ int pe, pd, digits;
+ unsigned int i;
+ char expstr[3];
+ char *eptr;
+ long exp;
+
+ /* Calculate required precision. */
+
+ pe = pd = digits = 0;
+
+ /* Get positions for '.' end 'E'. */
+ for (i = 0; i < size; i++) {
+ if (reading[i] == '.')
+ pd = i;
+ if (reading[i] == 'E') {
+ pe = i;
+ break;
+ }
+ }
+
+ digits = (pe - pd) - 1;
+
+ /* Get exponent element. */
+ expstr[0] = reading[pe + 1];
+ expstr[1] = reading[pe + 2];
+ expstr[2] = '\0';
+ errno = 0;
+ exp = strtol(expstr, &eptr, 10);
+ if (errno != 0)
+ return 2;
+ /* A negative exponent increses digits, a positive one reduces. */
+ exp = exp * (-1);
+
+ /* Adjust digits taking into account exponent. */
+ digits = digits + exp;
+
+ return digits;
+}
+
+SR_PRIV int fl45_scpi_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog[2];
+ struct sr_analog_encoding encoding[2];
+ struct sr_analog_meaning meaning[2];
+ struct sr_analog_spec spec[2];
+ struct sr_channel *channel;
+ char *reading;
+ float fv;
+ int res, digits;
+ unsigned int i;
+ int sent_ch[2];
+
+ (void)fd;
+ (void)revents;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ res = 0;
+ sent_ch[0] = sent_ch[1] = 0;
+
+ /* Process the list of channels. */
+ for (i = 0; i < devc->num_channels; i++) {
+ /* Note: digits/spec_digits will be overridden later. */
+ sr_analog_init(&analog[i], &encoding[i], &meaning[i], &spec[i], 0);
+
+ /* Detect current meter function. */
+ res = fl45_get_status(sdi, analog, i);
+
+ /* Get channel data. */
+ if (i == 0)
+ channel = sdi->channels->data;
+ else
+ channel = sdi->channels->next->data;
+
+ /* Is channel enabled? */
+ if (analog[i].meaning->mq != 0 && channel->enabled) {
+ /* Then get a reading from it. */
+ if (i == 0)
+ res = fl45_scpi_get_response(sdi, "VAL1?");
+ if (i == 1)
+ res = fl45_scpi_get_response(sdi, "VAL2?");
+ /* Note: Fluke 45 sends all data in text strings. */
+ reading = devc->response;
+
+ /* Deal with OL reading. */
+ if (strcmp(reading, "+1E+9") == 0) {
+ fv = INFINITY;
+ sr_dbg("Reading OL (infinity): %s.",
+ devc->response);
+ } else if (res == SR_OK && reading != NULL) {
+ /* Convert reading to float. */
+ sr_dbg("Meter reading string: %s.", reading);
+ res = sr_atof_ascii(reading, &fv);
+ digits = get_reading_dd(reading, strlen(reading));
+ analog[i].encoding->digits = digits;
+ analog[i].spec->spec_digits = digits;
+
+ } else {
+ sr_dbg("Invalid float string: '%s'.", reading);
+ return SR_ERR;
+ }
+
+ /* Are we on a little or big endian system? */
+#ifdef WORDS_BIGENDIAN
+ analog[i].encoding->is_bigendian = TRUE;
+#else
+ analog[i].encoding->is_bigendian = FALSE;
+#endif
+
+ /* Apply any modifiers. */
+ res = fl45_get_modifiers(sdi, analog, i);
+
+ /* Channal flag. */
+ sent_ch[i] = 1;
+
+ /* Set up analog object. */
+ analog[i].num_samples = 1;
+ analog[i].data = &fv;
+ analog[i].meaning->channels = g_slist_append(NULL, channel);
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog[i];
+
+ sr_session_send(sdi, &packet);
+
+ g_slist_free(analog[i].meaning->channels);
+ }
+ }
+
+ /* Update appropriate channel limits. */
+ if (sent_ch[0] || sent_ch[1])
+ sr_sw_limits_update_samples_read(&devc->limits, 1);
+
+ /* Are we done collecting samples? */
+ if (sr_sw_limits_check(&devc->limits))
+ sr_dev_acquisition_stop(sdi);
+
+ return TRUE;
+}
+
+SR_PRIV int fl45_scpi_get_response(const struct sr_dev_inst *sdi, char *cmd)
+{
+ struct dev_context *devc;
+ devc = sdi->priv;
+
+ /* Attempt to get a SCPI reponse. */
+ if (sr_scpi_get_string(sdi->conn, cmd, &devc->response) != SR_OK)
+ return SR_ERR;
+
+ /* Deal with RS232 '=>' prompt. */
+ if (strcmp(devc->response, "=>") == 0) {
+ /*
+ * If the response is a prompt then ignore and read the next
+ * response in the buffer.
+ */
+ devc->response = NULL;
+ /* Now attempt to read again. */
+ if (sr_scpi_get_string(sdi->conn, NULL, &devc->response) != SR_OK)
+ return SR_ERR;
+ }
+
+ /* NULL RS232 error prompts. */
+ if (strcmp(devc->response, "!>") == 0
+ || (strcmp(devc->response, "?>") == 0)) {
+ /* Unable to execute CMD. */
+ devc->response = NULL;
+ }
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 John Chajecki <subs@qcontinuum.plus.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_FLUKE_45_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_FLUKE_45_PROTOCOL_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <scpi.h>
+
+#define LOG_PREFIX "fluke-45"
+
+#define FLUKEDMM_BUFSIZE 256
+
+/* Always USB-serial, 1ms is plenty. */
+#define SERIAL_WRITE_TIMEOUT_MS 1
+
+enum data_format {
+ /* Fluke 45 uses IEEE488v2. */
+ FORMAT_IEEE488_2,
+};
+
+enum dmm_scpi_cmds {
+ SCPI_CMD_CLS,
+ SCPI_CMD_RST,
+ SCPI_CMD_REMS,
+ SCPI_CMD_RWLS,
+ SCPI_CMD_LOCS,
+ SCPI_CMD_LWLS,
+ SCPI_CMD_REMOTE,
+ SCPI_CMD_LOCAL,
+ SCPI_CMD_SET_ACVOLTAGE,
+ SCPI_CMD_SET_ACDCVOLTAGE,
+ SCPI_CMD_SET_DCVOLTAGE,
+ SCPI_CMD_SET_ACCURRENT,
+ SCPI_CMD_SET_ACDCCURRENT,
+ SCPI_CMD_SET_DCCURRENT,
+ SCPI_CMD_SET_FREQUENCY,
+ SCPI_CMD_SET_RESISTANCE,
+ SCPI_CMD_SET_CONTINUITY,
+ SCPI_CMD_SET_DIODE,
+ SCPI_CMD_SET_AUTO,
+ SCPI_CMD_GET_AUTO,
+ SCPI_CMD_SET_FIXED,
+ SCPI_CMD_SET_RANGE,
+ SCPI_CMD_GET_RANGE_D1,
+ SCPI_CMD_GET_RANGE_D2,
+ SCPI_CMD_SET_DB,
+ SCPI_CMD_SET_DBCLR,
+ SCPI_CMD_SET_DBPOWER,
+ SCPI_CMD_SET_DBREF,
+ SCPI_CMD_GET_DBREF,
+ SCPI_CMD_SET_HOLD,
+ SCPI_CMD_SET_HOLDCLR,
+ SCPI_CMD_SET_MAX,
+ SCPI_CMD_SET_MIN,
+ SCPI_CMD_SET_MMCLR,
+ SCPI_CMD_SET_REL,
+ SCPI_CMD_SET_RELCLR,
+ SCPI_CMD_GET_MEAS_DD,
+ SCPI_CMD_GET_MEAS_D1,
+ SCPI_CMD_GET_MEAS_D2,
+ SCPI_CMD_GET_RATE,
+ SCPI_CMD_SET_RATE,
+ SCPI_CMD_SET_TRIGGER,
+ SCPI_CMD_GET_TRIGGER,
+};
+
+static const struct scpi_command fluke_45_cmdset[] = {
+ { SCPI_CMD_CLS, "*CLS" },
+ { SCPI_CMD_RST, "*RST" },
+ { SCPI_CMD_REMS, "*REMS" },
+ { SCPI_CMD_RWLS, "*RWLS" },
+ { SCPI_CMD_LOCS, "LOCS" },
+ { SCPI_CMD_LWLS, "LWLS" },
+ { SCPI_CMD_REMOTE, "REMS" },
+ { SCPI_CMD_LOCAL, "LOCS" },
+ { SCPI_CMD_SET_ACVOLTAGE, "VAC" },
+ { SCPI_CMD_SET_ACDCVOLTAGE, "VACDC" },
+ { SCPI_CMD_SET_DCVOLTAGE, "VDC" },
+ { SCPI_CMD_SET_ACCURRENT, "AAC" },
+ { SCPI_CMD_SET_ACDCCURRENT, "AACDC" },
+ { SCPI_CMD_SET_DCCURRENT, "ADC" },
+ { SCPI_CMD_SET_FREQUENCY, "FREQ" },
+ { SCPI_CMD_SET_RESISTANCE, "OHMS" },
+ { SCPI_CMD_SET_CONTINUITY, "CONT" },
+ { SCPI_CMD_SET_DIODE, "DIODE" },
+ { SCPI_CMD_SET_AUTO, "AUTO" },
+ { SCPI_CMD_GET_AUTO, "AUTO?" },
+ { SCPI_CMD_SET_FIXED, "FIXED" },
+ { SCPI_CMD_SET_RANGE, "RANGE" },
+ { SCPI_CMD_GET_RANGE_D1, "RANGE1?" },
+ { SCPI_CMD_GET_RANGE_D2, "RANGE2?" },
+ { SCPI_CMD_SET_DB, "DB" },
+ { SCPI_CMD_SET_DBCLR, "DBCLR" },
+ { SCPI_CMD_SET_DBPOWER, "DBPOWER" },
+ { SCPI_CMD_SET_DBREF, "DBREF" },
+ { SCPI_CMD_GET_DBREF, "DBREF?" },
+ { SCPI_CMD_SET_HOLD, "HOLD" },
+ { SCPI_CMD_SET_HOLDCLR, "HOLDCLR" },
+ { SCPI_CMD_SET_MAX, "MAX" },
+ { SCPI_CMD_SET_MIN, "MIN" },
+ { SCPI_CMD_SET_MMCLR, "MMCLR" },
+ { SCPI_CMD_SET_REL, "REL" },
+ { SCPI_CMD_SET_RELCLR, "RELCLR" },
+ { SCPI_CMD_GET_MEAS_DD, "MEAS?" },
+ { SCPI_CMD_GET_MEAS_D1, "MEAS1?" },
+ { SCPI_CMD_GET_MEAS_D2, "MEAS2?" },
+ { SCPI_CMD_SET_RATE, "RATE" },
+ { SCPI_CMD_GET_RATE, "RATE?" },
+ { SCPI_CMD_SET_TRIGGER, "TRIGGER" },
+ { SCPI_CMD_GET_TRIGGER, "TRIGGER?" },
+ ALL_ZERO
+};
+
+struct fluke_scpi_dmm_model {
+ const char *vendor;
+ const char *model;
+ int num_channels;
+ int poll_period; /* How often to poll, in ms. */
+};
+
+struct channel_spec {
+ const char *name;
+ /* Min, max, programming resolution, spec digits, encoding digits. */
+ double voltage[5];
+ double current[5];
+ double resistance[5];
+ double capacitance[5];
+ double conductance[5];
+ double diode[5];
+};
+
+struct channel_group_spec {
+ const char *name;
+ uint64_t channel_index_mask;
+ uint64_t features;
+};
+
+struct dmm_channel {
+ enum sr_mq mq;
+ unsigned int hw_output_idx;
+ const char *hwname;
+ int digits;
+};
+
+struct dmm_channel_instance {
+ enum sr_mq mq;
+ int command;
+ const char *prefix;
+};
+
+struct dmm_channel_group {
+ uint64_t features;
+};
+
+struct dev_context {
+ struct sr_sw_limits limits;
+ unsigned int num_channels;
+ const struct scpi_command *cmdset;
+ char *response;
+ const char *mode1;
+ const char *mode2;
+ long range1;
+ long range2;
+ long autorng;
+ const char *rate;
+ long modifiers;
+ long trigmode;
+};
+
+int get_reading_dd(char *reading, size_t size);
+
+SR_PRIV extern const struct fluke_scpi_dmm_model dmm_profiles[];
+
+SR_PRIV int fl45_scpi_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int fl45_scpi_get_response(const struct sr_dev_inst *sdi, char *cmd);
+SR_PRIV int fl45_get_status(const struct sr_dev_inst *sdi,
+ struct sr_datafeed_analog *analog, int idx);
+SR_PRIV int fl45_get_modifiers(const struct sr_dev_inst *sdi,
+ struct sr_datafeed_analog *analog, int idx);
+
+#endif
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
/* Response is first a CMD_ACK byte (ASCII '0' for OK,
* or '1' to signify an error. */
- len = 128;
+ len = sizeof(buf);
serial_readline(serial, &b, &len, 150);
if (len != 1)
continue;
continue;
/* If CMD_ACK was OK, ID string follows. */
- len = 128;
+ len = sizeof(buf);
serial_readline(serial, &b, &len, 850);
if (len < 10)
continue;
return devices;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* Poll every 100ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 50,
fluke_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = NULL,
.config_set = config_set,
.config_list = config_list,
else if (meas_char == 3)
devc->mqflags |= SR_MQFLAG_DC | SR_MQFLAG_AC;
else if (meas_char == 15)
- devc->mqflags |= SR_MQFLAG_DIODE;
+ devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
break;
case 2:
devc->mq = SR_MQ_CURRENT;
}
if (sr_sw_limits_check(&devc->limits)) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
int timeout;
};
-/* Private, per-device-instance driver context. */
struct dev_context {
const struct flukedmm_profile *profile;
struct sr_sw_limits limits;
- /* Runtime. */
char buf[FLUKEDMM_BUFSIZE];
int buflen;
int64_t cmd_sent_at;
SR_CONF_CONN,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
.product = 0x6010,
.samplerate_div = 20,
.channel_names = {
- "ADBUS0",
- "ADBUS1",
- "ADBUS2",
- "ADBUS3",
- "ADBUS4",
- "ADBUS5",
- "ADBUS6",
- "ADBUS7",
+ "ADBUS0", "ADBUS1", "ADBUS2", "ADBUS3",
+ "ADBUS4", "ADBUS5", "ADBUS6", "ADBUS7",
/* TODO: BDBUS[0..7] channels. */
NULL
}
.product = 0x6001,
.samplerate_div = 30,
.channel_names = {
- "TXD",
- "RXD",
- "RTS#",
- "CTS#",
- "DTR#",
- "DSR#",
- "DCD#",
- "RI#",
+ "TXD", "RXD", "RTS#", "CTS#", "DTR#", "DSR#", "DCD#", "RI#",
NULL
}
};
return;
}
- /* Allocate memory for our private device context. */
devc = g_malloc0(sizeof(struct dev_context));
/* Allocate memory for the incoming data. */
}
sr_dbg("Found an FTDI device: %s.", model);
- /* Register the device with libsigrok. */
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
sdi->vendor = vendor;
return NULL;
}
- sr_dbg("Number of FTDI devices found: %d", ret);
-
curdev = devlist;
while (curdev) {
scan_device(ftdic, curdev->dev, &devices);
}
}
- /* Allocate memory for the FTDI context (ftdic) and initialize it. */
ftdic = ftdi_new();
if (!ftdic) {
sr_err("Failed to initialize libftdi.");
return std_scan_complete(di, devices);
}
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
g_free(devc->data_buf);
- g_free(devc);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static int dev_open(struct sr_dev_inst *sdi)
goto err_ftdi_free;
}
- /* Purge RX/TX buffers in the FTDI chip. */
ret = ftdi_usb_purge_buffers(devc->ftdic);
if (ret < 0) {
sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
ret, ftdi_get_error_string(devc->ftdic));
goto err_dev_open_close_ftdic;
}
- sr_dbg("FTDI chip buffers purged successfully.");
- /* Reset the FTDI bitmode. */
ret = ftdi_set_bitmode(devc->ftdic, 0x00, 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_dev_open_close_ftdic;
}
- sr_dbg("FTDI chip bitmode reset successfully.");
ret = ftdi_set_bitmode(devc->ftdic, 0x00, BITMODE_BITBANG);
if (ret < 0) {
ret, ftdi_get_error_string(devc->ftdic));
goto err_dev_open_close_ftdic;
}
- sr_dbg("FTDI chip bitbang mode entered successfully.");
-
- sdi->status = SR_ST_ACTIVE;
return SR_OK;
+
err_dev_open_close_ftdic:
ftdi_usb_close(devc->ftdic);
+
err_ftdi_free:
ftdi_free(devc->ftdic);
+
return SR_ERR;
}
devc = sdi->priv;
- if (devc->ftdic) {
- ftdi_usb_close(devc->ftdic);
- ftdi_free(devc->ftdic);
- devc->ftdic = NULL;
- }
+ if (!devc->ftdic)
+ return SR_ERR_BUG;
- sdi->status = SR_ST_INACTIVE;
+ ftdi_usb_close(devc->ftdic);
+ ftdi_free(devc->ftdic);
+ devc->ftdic = 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)
{
- int ret;
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
- char str[128];
(void)cg;
devc = sdi->priv;
- ret = SR_OK;
switch (key) {
case SR_CONF_SAMPLERATE:
*data = g_variant_new_uint64(devc->cur_samplerate);
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);
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
break;
default:
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static int config_set(uint32_t key, GVariant *data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- int ret;
struct dev_context *devc;
uint64_t value;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
- ret = SR_OK;
switch (key) {
case SR_CONF_LIMIT_MSEC:
value = g_variant_get_uint64(data);
/* TODO: Implement. */
- ret = SR_ERR_NA;
- break;
+ (void)value;
+ return SR_ERR_NA;
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_SAMPLERATE:
devc->cur_samplerate = value;
return ftdi_la_set_samplerate(devc);
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ 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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
break;
default:
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
devc = sdi->priv;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
if (!devc->ftdic)
return SR_ERR_BUG;
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- sr_dbg("Stopping acquisition.");
sr_session_source_remove(sdi->session, -1);
std_session_send_df_end(sdi);
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <config.h>
#include <ftdi.h>
#include "protocol.h"
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);
+ sr_dev_acquisition_stop(sdi);
return FALSE;
}
if (bytes_read == 0) {
if (devc->limit_samples && (n >= devc->limit_samples)) {
send_samples(sdi, devc->limit_samples - devc->samples_sent);
sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
} else {
send_samples(sdi, devc->bytes_received);
char *channel_names[];
};
-/** Private, per-device-instance driver context. */
struct dev_context {
struct ftdi_context *ftdic;
const struct ftdi_chip_desc *desc;
#include <config.h>
#include "protocol.h"
-#include "dslogic.h"
#include <math.h>
static const struct fx2lafw_profile supported_fx2[] = {
/*
* CWAV USBee AX
- * EE Electronics ESLA201A
- * ARMFLY AX-Pro
+ * ARMFLY AX-Pro (clone of the CWAV USBee AX)
+ * ARMFLY Mini-Logic (clone of the CWAV USBee AX)
+ * EE Electronics ESLA201A (clone of the CWAV USBee AX)
+ * HT USBee-AxPro (clone of the CWAV USBee AX)
+ * MCU123 USBee AX Pro clone (clone of the CWAV USBee AX)
+ * Noname LHT00SU1 (clone of the CWAV USBee AX)
+ * XZL_Studio AX (clone of the CWAV USBee AX)
*/
{ 0x08a9, 0x0014, "CWAV", "USBee AX", NULL,
"fx2lafw-cwav-usbeeax.fw",
DEV_CAPS_AX_ANALOG, NULL, NULL},
+
/*
* CWAV USBee DX
- * XZL-Studio DX
+ * HT USBee-DxPro (clone of the CWAV USBee DX), not yet supported!
+ * XZL-Studio DX (clone of the CWAV USBee DX)
*/
{ 0x08a9, 0x0015, "CWAV", "USBee DX", NULL,
"fx2lafw-cwav-usbeedx.fw",
"fx2lafw-cwav-usbeezx.fw",
0, NULL, NULL},
- /* DreamSourceLab DSLogic (before FW upload) */
- { 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL,
- "dreamsourcelab-dslogic-fx2.fw",
- DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, NULL, NULL},
- /* DreamSourceLab DSLogic (after FW upload) */
- { 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL,
- "dreamsourcelab-dslogic-fx2.fw",
- DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, "DreamSourceLab", "DSLogic"},
-
- /* DreamSourceLab DSCope (before FW upload) */
- { 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL,
- "dreamsourcelab-dscope-fx2.fw",
- DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, NULL, NULL},
- /* DreamSourceLab DSCope (after FW upload) */
- { 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL,
- "dreamsourcelab-dscope-fx2.fw",
- DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, "DreamSourceLab", "DSCope"},
-
- /* DreamSourceLab DSLogic Pro (before FW upload) */
- { 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL,
- "dreamsourcelab-dslogic-pro-fx2.fw",
- DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, NULL, NULL},
- /* DreamSourceLab DSLogic Pro (after FW upload) */
- { 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL,
- "dreamsourcelab-dslogic-pro-fx2.fw",
- DEV_CAPS_16BIT | DEV_CAPS_DSLOGIC_FW, "DreamSourceLab", "DSLogic"},
-
/*
* Saleae Logic
- * EE Electronics ESLA100
- * Robomotic MiniLogic
- * Robomotic BugLogic 3
+ * EE Electronics ESLA100 (clone of the Saleae Logic)
+ * Hantek 6022BL in LA mode (clone of the Saleae Logic)
+ * Instrustar ISDS205X in LA mode (clone of the Saleae Logic)
+ * Robomotic MiniLogic (clone of the Saleae Logic)
+ * Robomotic BugLogic 3 (clone of the Saleae Logic)
+ * MCU123 Saleae Logic clone (clone of the Saleae Logic)
*/
{ 0x0925, 0x3881, "Saleae", "Logic", NULL,
"fx2lafw-saleae-logic.fw",
* Default Cypress FX2 without EEPROM, e.g.:
* Lcsoft Mini Board
* Braintechnology USB Interface V2.x
+ * fx2grok-tiny
*/
{ 0x04B4, 0x8613, "Cypress", "FX2", NULL,
"fx2lafw-cypress-fx2.fw",
/*
* sigrok FX2 based 8-channel logic analyzer
+ * fx2grok-flat (before and after renumeration)
*/
{ 0x1d50, 0x608c, "sigrok", "FX2 LA (8ch)", NULL,
"fx2lafw-sigrok-fx2-8ch.fw",
"fx2lafw-sigrok-fx2-16ch.fw",
DEV_CAPS_16BIT, NULL, NULL },
- ALL_ZERO
-};
+ /*
+ * usb-c-grok
+ */
+ { 0x1d50, 0x608f, "sigrok", "usb-c-grok", NULL,
+ "fx2lafw-usb-c-grok.fw",
+ 0, NULL, NULL},
-static const uint32_t drvopts[] = {
- SR_CONF_LOGIC_ANALYZER,
+ ALL_ZERO
};
static const uint32_t scanopts[] = {
SR_CONF_CONN,
};
-static const uint32_t devopts[] = {
- SR_CONF_CONTINUOUS,
- 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 uint32_t drvopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
};
-static const uint32_t dslogic_devopts[] = {
- SR_CONF_CONTINUOUS | SR_CONF_SET | SR_CONF_GET,
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
- SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_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,
- SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
- SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
-static const int32_t soft_trigger_matches[] = {
+static const int32_t trigger_matches[] = {
SR_TRIGGER_ZERO,
SR_TRIGGER_ONE,
SR_TRIGGER_RISING,
SR_TRIGGER_EDGE,
};
-/* Names assigned to available edge slope choices. */
-static const char *const signal_edge_names[] = {
- [DS_EDGE_RISING] = "rising",
- [DS_EDGE_FALLING] = "falling",
-};
-
-static const struct {
- int range;
- gdouble low;
- gdouble high;
-} volt_thresholds[] = {
- { DS_VOLTAGE_RANGE_18_33_V, 0.7, 1.4 },
- { DS_VOLTAGE_RANGE_5_V, 1.4, 3.6 },
-};
-
static const uint64_t samplerates[] = {
SR_KHZ(20),
SR_KHZ(25),
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),
-};
-
static gboolean is_plausible(const struct libusb_device_descriptor *des)
{
int i;
continue;
}
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
-
libusb_close(hdl);
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
prof = NULL;
for (j = 0; supported_fx2[j].vid; j++) {
if (des.idVendor == supported_fx2[j].vid &&
}
}
- /* Skip if the device was not found. */
if (!prof)
continue;
sdi->priv = devc;
devices = g_slist_append(devices, sdi);
- 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");
- }
+ devc->samplerates = samplerates;
+ devc->num_samplerates = ARRAY_SIZE(samplerates);
+ has_firmware = usb_match_manuf_prod(devlist[i],
+ "sigrok", "fx2lafw");
if (has_firmware) {
/* Already has the firmware, so fix the new address. */
libusb_get_device_address(devlist[i]), NULL);
} else {
if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
- USB_CONFIGURATION, prof->firmware) == SR_OK)
+ USB_CONFIGURATION, prof->firmware) == SR_OK) {
/* Store when this device's FW was updated. */
devc->fw_updated = g_get_monotonic_time();
- else
+ } else {
sr_err("Firmware upload failed for "
- "device %d.%d (logical).",
+ "device %d.%d (logical), name %s.",
libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]));
+ libusb_get_device_address(devlist[i]),
+ prof->firmware);
+ }
sdi->inst_type = SR_INST_USB;
sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
0xff, NULL);
return std_scan_complete(di, devices);
}
-static void clear_dev_context(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
g_slist_free(devc->enabled_analog_channels);
- g_free(devc);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_dev_context);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
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;
return SR_ERR;
}
- if (devc->dslogic) {
- if (!strcmp(devc->profile->model, "DSLogic")) {
- if (devc->dslogic_voltage_threshold == DS_VOLTAGE_RANGE_18_33_V)
- fpga_firmware = DSLOGIC_FPGA_FIRMWARE_3V3;
- else
- fpga_firmware = DSLOGIC_FPGA_FIRMWARE_5V;
- } 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 = devc->samplerates[0];
usb = sdi->conn;
if (!usb->devhdl)
- return SR_ERR;
+ return SR_ERR_BUG;
- sr_info("fx2lafw: Closing device on %d.%d (logical) / %s (physical) interface %d.",
+ 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;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
{
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
- GVariant *range[2];
- unsigned int i;
- char str[128];
(void)cg;
/* 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_VOLTAGE_THRESHOLD:
- for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
- if (volt_thresholds[i].range != devc->dslogic_voltage_threshold)
- continue;
- range[0] = g_variant_new_double(volt_thresholds[i].low);
- range[1] = g_variant_new_double(volt_thresholds[i].high);
- *data = g_variant_new_tuple(range, 2);
- break;
- }
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
break;
case SR_CONF_LIMIT_SAMPLES:
*data = g_variant_new_uint64(devc->limit_samples);
case SR_CONF_CAPTURE_RATIO:
*data = g_variant_new_uint64(devc->capture_ratio);
break;
- case SR_CONF_EXTERNAL_CLOCK:
- *data = g_variant_new_boolean(devc->dslogic_external_clock);
- break;
- case SR_CONF_CONTINUOUS:
- *data = g_variant_new_boolean(devc->dslogic_continuous_mode);
- break;
- case SR_CONF_CLOCK_EDGE:
- i = devc->dslogic_clock_edge;
- if (i >= ARRAY_SIZE(signal_edge_names))
- return SR_ERR_BUG;
- *data = g_variant_new_string(signal_edge_names[0]);
- 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(uint32_t key, GVariant *data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
- uint64_t arg;
- int i, ret;
- gdouble low, high;
+ int idx;
(void)cg;
if (!sdi)
return SR_ERR_ARG;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
-
devc = sdi->priv;
- ret = SR_OK;
-
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;
+ if ((idx = std_u64_idx(data, devc->samplerates, devc->num_samplerates)) < 0)
+ return SR_ERR_ARG;
+ devc->cur_samplerate = devc->samplerates[idx];
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;
- case SR_CONF_VOLTAGE_THRESHOLD:
- g_variant_get(data, "(dd)", &low, &high);
- ret = SR_ERR_ARG;
- for (i = 0; (unsigned int)i < ARRAY_SIZE(volt_thresholds); i++) {
- if (fabs(volt_thresholds[i].low - low) < 0.1 &&
- fabs(volt_thresholds[i].high - high) < 0.1) {
- devc->dslogic_voltage_threshold = volt_thresholds[i].range;
- break;
- }
- }
- if (!strcmp(devc->profile->model, "DSLogic")) {
- if (devc->dslogic_voltage_threshold == DS_VOLTAGE_RANGE_5_V)
- ret = dslogic_fpga_firmware_upload(sdi, DSLOGIC_FPGA_FIRMWARE_5V);
- else
- ret = dslogic_fpga_firmware_upload(sdi, DSLOGIC_FPGA_FIRMWARE_3V3);
- } else if (!strcmp(devc->profile->model, "DSLogic Pro")) {
- ret = dslogic_fpga_firmware_upload(sdi, DSLOGIC_PRO_FPGA_FIRMWARE);
- }
- break;
- case SR_CONF_EXTERNAL_CLOCK:
- devc->dslogic_external_clock = g_variant_get_boolean(data);
- break;
- case SR_CONF_CONTINUOUS:
- devc->dslogic_continuous_mode = g_variant_get_boolean(data);
- break;
- case SR_CONF_CLOCK_EDGE:
- i = lookup_index(data, signal_edge_names,
- ARRAY_SIZE(signal_edge_names));
- if (i < 0)
- return SR_ERR_ARG;
- devc->dslogic_clock_edge = i;
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ 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, *range[2];
- GVariantBuilder gvb;
- unsigned int i;
- (void)cg;
+ devc = (sdi) ? sdi->priv : NULL;
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 {
- devc = sdi->priv;
- if (!devc->dslogic)
- *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,
- dslogic_devopts, ARRAY_SIZE(dslogic_devopts), sizeof(uint32_t));
- }
- break;
- case SR_CONF_VOLTAGE_THRESHOLD:
- if (!sdi->priv)
- return SR_ERR_ARG;
- devc = sdi->priv;
- if (!devc->dslogic)
+ if (cg)
return SR_ERR_NA;
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
- range[0] = g_variant_new_double(volt_thresholds[i].low);
- range[1] = g_variant_new_double(volt_thresholds[i].high);
- gvar = g_variant_new_tuple(range, 2);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_SAMPLERATE:
- devc = sdi->priv;
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- 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);
+ if (!devc)
+ return SR_ERR_NA;
+ *data = std_gvar_samplerates(devc->samplerates, devc->num_samplerates);
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;
- case SR_CONF_CLOCK_EDGE:
- *data = g_variant_new_strv(signal_edge_names,
- ARRAY_SIZE(signal_edge_names));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
default:
return SR_ERR_NA;
return SR_OK;
}
-static int receive_data(int fd, int revents, void *cb_data)
-{
- struct timeval tv;
- struct drv_context *drvc;
-
- (void)fd;
- (void)revents;
-
- drvc = (struct drv_context *)cb_data;
-
- tv.tv_sec = tv.tv_usec = 0;
- libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
-
- return TRUE;
-}
-
-static int start_transfers(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- struct sr_usb_dev_inst *usb;
- struct sr_trigger *trigger;
- struct libusb_transfer *transfer;
- unsigned int i, num_transfers;
- int endpoint, timeout, ret;
- unsigned char *buf;
- size_t size;
-
- devc = sdi->priv;
- usb = sdi->conn;
-
- devc->sent_samples = 0;
- devc->acq_aborted = FALSE;
- devc->empty_transfer_count = 0;
-
- if ((trigger = sr_session_trigger_get(sdi->session)) && !devc->dslogic) {
- 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);
-
- //if (devc->dslogic)
- // num_transfers = dslogic_get_number_of_transfers(devc);
-
- if (devc->dslogic) {
- if (devc->cur_samplerate == SR_MHZ(100))
- num_transfers = 16;
- else if (devc->cur_samplerate == SR_MHZ(200))
- num_transfers = 8;
- else if (devc->cur_samplerate == SR_MHZ(400))
- num_transfers = 4;
- }
-
- size = fx2lafw_get_buffer_size(devc);
- devc->submitted_transfers = 0;
-
- devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
- if (!devc->transfers) {
- sr_err("USB transfers malloc failed.");
- 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))) {
- sr_err("USB transfer buffer malloc failed.");
- return SR_ERR_MALLOC;
- }
- transfer = libusb_alloc_transfer(0);
- libusb_fill_bulk_transfer(transfer, usb->devhdl,
- endpoint | LIBUSB_ENDPOINT_IN, buf, size,
- fx2lafw_receive_transfer, (void *)sdi, timeout);
- sr_info("submitting transfer: %d", i);
- if ((ret = libusb_submit_transfer(transfer)) != 0) {
- sr_err("Failed to submit transfer: %s.",
- libusb_error_name(ret));
- libusb_free_transfer(transfer);
- g_free(buf);
- fx2lafw_abort_acquisition(devc);
- return SR_ERR;
- }
- devc->transfers[i] = transfer;
- devc->submitted_transfers++;
- }
-
- /*
- * If this device has analog channels and at least one of them is
- * enabled, use mso_send_data_proc() to properly handle the analog
- * data. Otherwise use la_send_data_proc().
- */
- if (g_slist_length(devc->enabled_analog_channels) > 0)
- devc->send_data_proc = mso_send_data_proc;
- else
- devc->send_data_proc = la_send_data_proc;
-
- std_session_send_df_header(sdi);
-
- return SR_OK;
-}
-
-static void LIBUSB_CALL dslogic_trigger_receive(struct libusb_transfer *transfer)
-{
- const struct sr_dev_inst *sdi;
- struct dslogic_trigger_pos *tpos;
- struct dev_context *devc;
-
- sdi = transfer->user_data;
- devc = sdi->priv;
- if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
- sr_dbg("Trigger transfer canceled.");
- /* Terminate session. */
- std_session_send_df_end(sdi);
- 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;
- }
- } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED
- && transfer->actual_length == sizeof(struct dslogic_trigger_pos)) {
- tpos = (struct dslogic_trigger_pos *)transfer->buffer;
- sr_info("tpos real_pos %d ram_saddr %d cnt %d", tpos->real_pos,
- tpos->ram_saddr, tpos->remain_cnt);
- devc->trigger_pos = tpos->real_pos;
- 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;
- struct dev_context *devc;
- int ret;
-
- usb = sdi->conn;
- devc = sdi->priv;
-
- if ((ret = dslogic_stop_acquisition(sdi)) != SR_OK)
- return ret;
-
- if ((ret = dslogic_fpga_configure(sdi)) != SR_OK)
- return ret;
-
- /* If this is a DSLogic Pro, set the voltage threshold. */
- if (!strcmp(devc->profile->model, "DSLogic Pro")){
- if (devc->dslogic_voltage_threshold == DS_VOLTAGE_RANGE_18_33_V) {
- dslogic_set_vth(sdi, 1.4);
- } else {
- dslogic_set_vth(sdi, 3.3);
- }
- }
-
- 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;
- }
-
- devc->transfers = g_try_malloc0(sizeof(*devc->transfers));
- if (!devc->transfers) {
- sr_err("USB trigger_pos transfer malloc failed.");
- return SR_ERR_MALLOC;
- }
- devc->num_transfers = 1;
- devc->submitted_transfers++;
- devc->transfers[0] = transfer;
-
- return ret;
-}
-
-static int configure_channels(const struct sr_dev_inst *sdi)
-{
- struct dev_context *devc;
- const GSList *l;
- int p;
- struct sr_channel *ch;
- uint32_t channel_mask = 0, num_analog = 0;
-
- devc = sdi->priv;
-
- g_slist_free(devc->enabled_analog_channels);
- devc->enabled_analog_channels = NULL;
-
- for (l = sdi->channels, p = 0; l; l = l->next, p++) {
- ch = l->data;
- if ((p <= NUM_CHANNELS) && (ch->type == SR_CHANNEL_ANALOG)
- && (ch->enabled)) {
- num_analog++;
- devc->enabled_analog_channels =
- g_slist_append(devc->enabled_analog_channels, ch);
- } else {
- channel_mask |= ch->enabled << p;
- }
- }
-
- /*
- * Use wide sampling if either any of the LA channels 8..15 is enabled,
- * and/or at least one analog channel is enabled, and/or the device
- * is running DSLogic firmware (not fx2lafw).
- */
- devc->sample_wide = (channel_mask > 0xff
- || num_analog > 0
- || (devc->profile->dev_caps & DEV_CAPS_DSLOGIC_FW));
-
- return SR_OK;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi)
-{
- struct sr_dev_driver *di;
- struct drv_context *drvc;
- struct dev_context *devc;
- int timeout, ret;
- size_t size;
-
- 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->sent_samples = 0;
- devc->empty_transfer_count = 0;
- devc->acq_aborted = FALSE;
-
- if (configure_channels(sdi) != SR_OK) {
- sr_err("Failed to configure channels.");
- return SR_ERR;
- }
-
- timeout = fx2lafw_get_timeout(devc);
- usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc);
-
- if (devc->dslogic) {
- dslogic_trigger_request(sdi);
- } else {
- size = fx2lafw_get_buffer_size(devc);
- /* Prepare for analog sampling. */
- if (g_slist_length(devc->enabled_analog_channels) > 0) {
- /* We need a buffer half the size of a transfer. */
- devc->logic_buffer = g_try_malloc(size / 2);
- devc->analog_buffer = g_try_malloc(
- sizeof(float) * size / 2);
- }
- start_transfers(sdi);
- if ((ret = fx2lafw_command_start_acquisition(sdi)) != SR_OK) {
- fx2lafw_abort_acquisition(devc);
- return ret;
- }
- }
-
- return SR_OK;
-}
-
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (devc->dslogic)
- dslogic_stop_acquisition(sdi);
-
fx2lafw_abort_acquisition(sdi->priv);
return SR_OK;
.config_list = config_list,
.dev_open = dev_open,
.dev_close = dev_close,
- .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_start = fx2lafw_start_acquisition,
.dev_acquisition_stop = dev_acquisition_stop,
.context = NULL,
};
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-#include <math.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include "protocol.h"
-#include "dslogic.h"
-
-/*
- * This should be larger than the FPGA bitstream image so that it'll get
- * uploaded in one big operation. There seem to be issues when uploading
- * it in chunks.
- */
-#define FW_BUFSIZE (1024 * 1024)
-
-#define FPGA_UPLOAD_DELAY (10 * 1000)
-
-#define USB_TIMEOUT (3 * 1000)
-
-SR_PRIV int dslogic_set_vth(const struct sr_dev_inst *sdi, double vth)
-{
- struct sr_usb_dev_inst *usb;
- int ret;
- uint8_t cmd;
-
- usb = sdi->conn;
-
- cmd = (vth / 5.0) * 255;
-
- /* Send the control command. */
- ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR |
- LIBUSB_ENDPOINT_OUT, DS_CMD_VTH, 0x0000, 0x0000,
- (unsigned char *)&cmd, sizeof(cmd), 3000);
- if (ret < 0) {
- sr_err("Unable to send VTH command: %s.",
- libusb_error_name(ret));
- return SR_ERR;
- }
-
- return SR_OK;
-}
-
-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 = DS_START_FLAGS_MODE_LA;
- 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;
-}
-
-/*
- * Get the session trigger and configure the FPGA structure
- * accordingly.
- */
-static int dslogic_set_trigger(const struct sr_dev_inst *sdi,
- struct dslogic_fpga_config *cfg)
-{
- struct sr_trigger *trigger;
- struct sr_trigger_stage *stage;
- struct sr_trigger_match *match;
- struct dev_context *devc;
- const GSList *l, *m;
- int channelbit, i = 0;
- uint16_t v16;
-
- devc = sdi->priv;
-
- cfg->trig_mask0[0] = 0xffff;
- cfg->trig_mask1[0] = 0xffff;
-
- cfg->trig_value0[0] = 0;
- cfg->trig_value1[0] = 0;
-
- cfg->trig_edge0[0] = 0;
- cfg->trig_edge1[0] = 0;
-
- cfg->trig_logic0[0] = 0;
- cfg->trig_logic1[0] = 0;
-
- cfg->trig_count0[0] = 0;
- cfg->trig_count1[0] = 0;
-
- cfg->trig_pos = 0;
- cfg->trig_sda = 0;
- cfg->trig_glb = 0;
- cfg->trig_adp = cfg->count - cfg->trig_pos - 1;
-
- for (i = 1; i < 16; i++) {
- cfg->trig_mask0[i] = 0xff;
- cfg->trig_mask1[i] = 0xff;
- cfg->trig_value0[i] = 0;
- cfg->trig_value1[i] = 0;
- cfg->trig_edge0[i] = 0;
- cfg->trig_edge1[i] = 0;
- cfg->trig_count0[i] = 0;
- cfg->trig_count1[i] = 0;
- cfg->trig_logic0[i] = 2;
- cfg->trig_logic1[i] = 2;
- }
-
- cfg->trig_pos = (uint32_t)(devc->capture_ratio / 100.0 * devc->limit_samples);
- sr_dbg("pos: %d", cfg->trig_pos);
-
- sr_dbg("configuring trigger");
-
- if (!(trigger = sr_session_trigger_get(sdi->session))) {
- sr_dbg("No session trigger found");
- 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;
- if (!match->channel->enabled)
- /* Ignore disabled channels with a trigger. */
- continue;
- channelbit = 1 << (match->channel->index);
- /* Simple trigger support (event). */
- if (match->match == SR_TRIGGER_ONE) {
- cfg->trig_mask0[0] &= ~channelbit;
- cfg->trig_mask1[0] &= ~channelbit;
- cfg->trig_value0[0] |= channelbit;
- cfg->trig_value1[0] |= channelbit;
- } else if (match->match == SR_TRIGGER_ZERO) {
- cfg->trig_mask0[0] &= ~channelbit;
- cfg->trig_mask1[0] &= ~channelbit;
- } else if (match->match == SR_TRIGGER_FALLING) {
- cfg->trig_mask0[0] &= ~channelbit;
- cfg->trig_mask1[0] &= ~channelbit;
- cfg->trig_edge0[0] |= channelbit;
- cfg->trig_edge1[0] |= channelbit;
- } else if (match->match == SR_TRIGGER_RISING) {
- cfg->trig_mask0[0] &= ~channelbit;
- cfg->trig_mask1[0] &= ~channelbit;
- cfg->trig_value0[0] |= channelbit;
- cfg->trig_value1[0] |= channelbit;
- cfg->trig_edge0[0] |= channelbit;
- cfg->trig_edge1[0] |= channelbit;
- } else if (match->match == SR_TRIGGER_EDGE) {
- cfg->trig_edge0[0] |= channelbit;
- cfg->trig_edge1[0] |= channelbit;
- }
- }
- }
-
- v16 = RL16(&cfg->mode);
- v16 |= 1 << 0;
- WL16(&cfg->mode, v16);
-
- 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
- * 12 1 = stream mode
- * 11 1 = serial trigger
- * 8-10 unused
- * 7 1 = analog mode
- * 6 1 = samplerate 400MHz
- * 5 1 = samplerate 200MHz or analog mode
- * 4 0 = logic, 1 = dso or analog
- * 3 1 = RLE encoding (enable for more than 16 Megasamples)
- * 1-2 00 = internal clock,
- * 01 = external clock rising,
- * 11 = external clock falling
- * 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_continuous_mode)
- v16 |= 1 << 12;
- if (devc->dslogic_external_clock) {
- v16 |= 1 << 1;
- if (devc->dslogic_clock_edge == DS_EDGE_FALLING)
- v16 |= 1 << 2;
- }
- if (devc->limit_samples > DS_MAX_LOGIC_DEPTH *
- ceil(devc->cur_samplerate * 1.0 / DS_MAX_LOGIC_SAMPLERATE)
- && !devc->dslogic_continuous_mode) {
- /* Enable RLE for long captures.
- * Without this, captured data present errors.
- */
- v16 |= 1 << 3;
- }
-
- WL16(&cfg.mode, v16);
- v32 = ceil(DS_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate);
- WL32(&cfg.divider, v32);
- WL32(&cfg.count, devc->limit_samples);
-
- dslogic_set_trigger(sdi, &cfg);
-
- 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;
-}
-
-static int to_bytes_per_ms(struct dev_context *devc)
-{
- if (devc->cur_samplerate > SR_MHZ(100))
- return SR_MHZ(100) / 1000 * (devc->sample_wide ? 2 : 1);
-
- return devc->cur_samplerate / 1000 * (devc->sample_wide ? 2 : 1);
-}
-
-static size_t get_buffer_size(struct dev_context *devc)
-{
- size_t s;
-
- /*
- * The buffer should be large enough to hold 10ms of data and
- * a multiple of 512.
- */
- s = 10 * to_bytes_per_ms(devc);
- // s = to_bytes_per_ms(devc->cur_samplerate);
- return (s + 511) & ~511;
-}
-
-SR_PRIV int dslogic_get_number_of_transfers(struct dev_context *devc)
-{
- unsigned int n;
-
- /* Total buffer size should be able to hold about 100ms of data. */
- n = (100 * to_bytes_per_ms(devc) / get_buffer_size(devc));
- sr_info("New calculation: %d", n);
-
- if (n > NUM_SIMUL_TRANSFERS)
- return NUM_SIMUL_TRANSFERS;
-
- return n;
-}
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#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_CMD_VTH 0xb8
-
-#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)
-#define DS_START_FLAGS_MODE_LA (1 << 4)
-
-#define DS_MAX_LOGIC_DEPTH SR_MHZ(16)
-#define DS_MAX_LOGIC_SAMPLERATE SR_MHZ(100)
-
-enum dslogic_operation_modes {
- DS_OP_NORMAL,
- DS_OP_INTERNAL_TEST,
- DS_OP_EXTERNAL_TEST,
- DS_OP_LOOPBACK_TEST,
-};
-
-enum {
- DS_VOLTAGE_RANGE_18_33_V, /* 1.8V and 3.3V logic */
- DS_VOLTAGE_RANGE_5_V, /* 5V logic */
-};
-
-enum {
- DS_EDGE_RISING,
- DS_EDGE_FALLING,
-};
-
-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;
- uint32_t remain_cnt;
- uint8_t first_block[500];
-};
-
-/*
- * 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 0xf5a5f5a5
-#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 0xfa5afa5a
-
-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);
-SR_PRIV int dslogic_set_vth(const struct sr_dev_inst *sdi, double vth);
-SR_PRIV int dslogic_get_number_of_transfers(struct dev_context *devc);
-
-#endif
#include <glib.h>
#include <glib/gstdio.h>
#include "protocol.h"
-#include "dslogic.h"
#pragma pack(push, 1)
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 cmd, ret;
+ int 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, 0x0000, 0x0000, revid, 1, USB_TIMEOUT);
+ LIBUSB_ENDPOINT_IN, CMD_GET_REVID_VERSION, 0x0000, 0x0000,
+ revid, 1, USB_TIMEOUT);
if (ret < 0) {
sr_err("Unable to get REVID: %s.", libusb_error_name(ret));
return SR_OK;
}
-SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
+static int command_start_acquisition(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
return SR_OK;
}
-/**
- * Check the USB configuration to determine if this is an fx2lafw device.
- *
- * @return TRUE if the device's configuration profile matches fx2lafw
- * configuration, FALSE otherwise.
- */
-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;
- gboolean ret;
- unsigned char strdesc[64];
-
- hdl = NULL;
- ret = FALSE;
- while (!ret) {
- /* Assume the FW has not been loaded, unless proven wrong. */
- 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)
- break;
- if (strcmp((const char *)strdesc, manufacturer))
- break;
-
- if (libusb_get_string_descriptor_ascii(hdl,
- des.iProduct, strdesc, sizeof(strdesc)) < 0)
- break;
- if (strcmp((const char *)strdesc, product))
- break;
-
- ret = TRUE;
- }
- if (hdl)
- libusb_close(hdl);
-
- return ret;
-}
-
SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
{
libusb_device **devlist;
struct dev_context *devc;
struct drv_context *drvc;
struct version_info vi;
- int ret, i, device_count;
+ int ret = SR_ERR, i, device_count;
uint8_t revid;
char connection_id[64];
devc = sdi->priv;
usb = sdi->conn;
- if (sdi->status == SR_ST_ACTIVE)
- /* Device is already in use. */
- return SR_ERR;
-
device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
if (device_count < 0) {
sr_err("Failed to get device list: %s.",
/*
* Check device by its physical USB bus/port address.
*/
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
if (strcmp(sdi->connection_id, connection_id))
/* This is not the one. */
continue;
} else {
sr_err("Failed to open device: %s.",
libusb_error_name(ret));
+ ret = SR_ERR;
break;
}
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 = SR_ERR;
+ break;
}
}
}
break;
}
- sdi->status = SR_ST_ACTIVE;
sr_info("Opened device on %d.%d (logical) / %s (physical), "
"interface %d, firmware %d.%d.",
usb->bus, usb->address, connection_id,
sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.",
revid, (revid != 1) ? " (FX2)" : "A (FX2LP)");
+ ret = SR_OK;
+
break;
}
- libusb_free_device_list(devlist, 1);
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
+ libusb_free_device_list(devlist, 1);
- return SR_OK;
+ return ret;
}
SR_PRIV struct dev_context *fx2lafw_dev_new(void)
devc->limit_samples = 0;
devc->capture_ratio = 0;
devc->sample_wide = FALSE;
- devc->dslogic_continuous_mode = FALSE;
- devc->dslogic_clock_edge = DS_EDGE_RISING;
devc->stl = NULL;
return devc;
}
-SR_PRIV void mso_send_data_proc(struct sr_dev_inst *sdi,
+static void mso_send_data_proc(struct sr_dev_inst *sdi,
uint8_t *data, size_t length, size_t sample_width)
{
size_t i;
sr_session_send(sdi, &analog_packet);
}
-SR_PRIV void la_send_data_proc(struct sr_dev_inst *sdi,
+static void la_send_data_proc(struct sr_dev_inst *sdi,
uint8_t *data, size_t length, size_t sample_width)
{
const struct sr_datafeed_logic logic = {
sr_session_send(sdi, &packet);
}
-SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transfer)
+static void LIBUSB_CALL 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;
unsigned int num_samples;
int trigger_offset, cur_sample_count, unitsize;
int pre_trigger_samples;
else
num_samples = cur_sample_count;
- if (devc->dslogic && devc->trigger_pos > devc->sent_samples
- && devc->trigger_pos <= devc->sent_samples + num_samples) {
- /* DSLogic trigger in this block. Send trigger position. */
- trigger_offset = devc->trigger_pos - devc->sent_samples;
- /* Pre-trigger samples. */
- devc->send_data_proc(sdi, (uint8_t *)transfer->buffer,
- trigger_offset * unitsize, unitsize);
- devc->sent_samples += trigger_offset;
- /* Trigger position. */
- devc->trigger_pos = 0;
- packet.type = SR_DF_TRIGGER;
- packet.payload = NULL;
- sr_session_send(sdi, &packet);
- /* Post trigger samples. */
- num_samples -= trigger_offset;
- devc->send_data_proc(sdi, (uint8_t *)transfer->buffer
- + trigger_offset * unitsize, num_samples * unitsize, unitsize);
- devc->sent_samples += num_samples;
- } else {
- devc->send_data_proc(sdi, (uint8_t *)transfer->buffer,
- num_samples * unitsize, unitsize);
- devc->sent_samples += num_samples;
- }
+ devc->send_data_proc(sdi, (uint8_t *)transfer->buffer,
+ num_samples * unitsize, unitsize);
+ devc->sent_samples += num_samples;
}
} else {
trigger_offset = soft_trigger_logic_check(devc->stl,
resubmit_transfer(transfer);
}
+static int configure_channels(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ const GSList *l;
+ int p;
+ struct sr_channel *ch;
+ uint32_t channel_mask = 0, num_analog = 0;
+
+ devc = sdi->priv;
+
+ g_slist_free(devc->enabled_analog_channels);
+ devc->enabled_analog_channels = NULL;
+
+ for (l = sdi->channels, p = 0; l; l = l->next, p++) {
+ ch = l->data;
+ if ((p <= NUM_CHANNELS) && (ch->type == SR_CHANNEL_ANALOG)
+ && (ch->enabled)) {
+ num_analog++;
+ devc->enabled_analog_channels =
+ g_slist_append(devc->enabled_analog_channels, ch);
+ } else {
+ channel_mask |= ch->enabled << p;
+ }
+ }
+
+ /*
+ * Use wide sampling if either any of the LA channels 8..15 is enabled,
+ * and/or at least one analog channel is enabled.
+ */
+ devc->sample_wide = channel_mask > 0xff || num_analog > 0;
+
+ return SR_OK;
+}
+
static unsigned int to_bytes_per_ms(unsigned int samplerate)
{
return samplerate / 1000;
}
-SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc)
+static size_t get_buffer_size(struct dev_context *devc)
{
size_t s;
return (s + 511) & ~511;
}
-SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc)
+static unsigned int get_number_of_transfers(struct dev_context *devc)
{
unsigned int n;
/* Total buffer size should be able to hold about 500ms of data. */
n = (500 * to_bytes_per_ms(devc->cur_samplerate) /
- fx2lafw_get_buffer_size(devc));
+ get_buffer_size(devc));
if (n > NUM_SIMUL_TRANSFERS)
return NUM_SIMUL_TRANSFERS;
return n;
}
-SR_PRIV unsigned int fx2lafw_get_timeout(struct dev_context *devc)
+static unsigned int get_timeout(struct dev_context *devc)
{
size_t total_size;
unsigned int timeout;
- total_size = fx2lafw_get_buffer_size(devc) *
- fx2lafw_get_number_of_transfers(devc);
+ total_size = get_buffer_size(devc) *
+ get_number_of_transfers(devc);
timeout = total_size / to_bytes_per_ms(devc->cur_samplerate);
return timeout + timeout / 4; /* Leave a headroom of 25% percent. */
}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+ struct timeval tv;
+ struct drv_context *drvc;
+
+ (void)fd;
+ (void)revents;
+
+ drvc = (struct drv_context *)cb_data;
+
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+ return TRUE;
+}
+
+static int start_transfers(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ struct sr_trigger *trigger;
+ struct libusb_transfer *transfer;
+ unsigned int i, num_transfers;
+ int timeout, ret;
+ unsigned char *buf;
+ size_t size;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ devc->sent_samples = 0;
+ devc->acq_aborted = FALSE;
+ devc->empty_transfer_count = 0;
+
+ 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 = get_number_of_transfers(devc);
+
+ size = get_buffer_size(devc);
+ devc->submitted_transfers = 0;
+
+ devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
+ if (!devc->transfers) {
+ sr_err("USB transfers malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+
+ timeout = get_timeout(devc);
+ devc->num_transfers = num_transfers;
+ for (i = 0; i < num_transfers; i++) {
+ if (!(buf = g_try_malloc(size))) {
+ sr_err("USB transfer buffer malloc failed.");
+ return SR_ERR_MALLOC;
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 2 | LIBUSB_ENDPOINT_IN, buf, size,
+ receive_transfer, (void *)sdi, timeout);
+ sr_info("submitting transfer: %d", i);
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ fx2lafw_abort_acquisition(devc);
+ return SR_ERR;
+ }
+ devc->transfers[i] = transfer;
+ devc->submitted_transfers++;
+ }
+
+ /*
+ * If this device has analog channels and at least one of them is
+ * enabled, use mso_send_data_proc() to properly handle the analog
+ * data. Otherwise use la_send_data_proc().
+ */
+ if (g_slist_length(devc->enabled_analog_channels) > 0)
+ devc->send_data_proc = mso_send_data_proc;
+ else
+ devc->send_data_proc = la_send_data_proc;
+
+ std_session_send_df_header(sdi);
+
+ return SR_OK;
+}
+
+SR_PRIV int fx2lafw_start_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct sr_dev_driver *di;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ int timeout, ret;
+ size_t size;
+
+ di = sdi->driver;
+ drvc = di->context;
+ devc = sdi->priv;
+
+ devc->ctx = drvc->sr_ctx;
+ devc->sent_samples = 0;
+ devc->empty_transfer_count = 0;
+ devc->acq_aborted = FALSE;
+
+ if (configure_channels(sdi) != SR_OK) {
+ sr_err("Failed to configure channels.");
+ return SR_ERR;
+ }
+
+ timeout = get_timeout(devc);
+ usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc);
+
+ size = get_buffer_size(devc);
+ /* Prepare for analog sampling. */
+ if (g_slist_length(devc->enabled_analog_channels) > 0) {
+ /* We need a buffer half the size of a transfer. */
+ devc->logic_buffer = g_try_malloc(size / 2);
+ devc->analog_buffer = g_try_malloc(
+ sizeof(float) * size / 2);
+ }
+ start_transfers(sdi);
+ if ((ret = command_start_acquisition(sdi)) != SR_OK) {
+ fx2lafw_abort_acquisition(devc);
+ return ret;
+ }
+
+ return SR_OK;
+}
#define DEV_CAPS_16BIT_POS 0
#define DEV_CAPS_AX_ANALOG_POS 1
-#define DEV_CAPS_DSLOGIC_FW_POS 2
#define DEV_CAPS_16BIT (1 << DEV_CAPS_16BIT_POS)
#define DEV_CAPS_AX_ANALOG (1 << DEV_CAPS_AX_ANALOG_POS)
-#define DEV_CAPS_DSLOGIC_FW (1 << DEV_CAPS_DSLOGIC_FW_POS)
-
-#define DSLOGIC_FPGA_FIRMWARE_5V "dreamsourcelab-dslogic-fpga-5v.fw"
-#define DSLOGIC_FPGA_FIRMWARE_3V3 "dreamsourcelab-dslogic-fpga-3v3.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
*/
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;
uint8_t *data, size_t length, size_t sample_width);
uint8_t *logic_buffer;
float *analog_buffer;
-
- /* Is this a DSLogic? */
- gboolean dslogic;
- uint16_t dslogic_mode;
- uint32_t trigger_pos;
- gboolean dslogic_external_clock;
- gboolean dslogic_continuous_mode;
- int dslogic_clock_edge;
- int dslogic_voltage_threshold;
};
-SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi);
-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 struct dev_context *fx2lafw_dev_new(void);
+SR_PRIV int fx2lafw_start_acquisition(const struct sr_dev_inst *sdi);
SR_PRIV void fx2lafw_abort_acquisition(struct dev_context *devc);
-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);
-SR_PRIV void la_send_data_proc(struct sr_dev_inst *sdi, uint8_t *data,
- size_t length, size_t sample_width);
-SR_PRIV void mso_send_data_proc(struct sr_dev_inst *sdi, uint8_t *data,
- size_t length, size_t sample_width);
#endif
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/**
- * @file
- *
- * Gossen Metrawatt Metrahit 1x/2x drivers
- *
- * @internal
- */
-
#include <config.h>
#include <string.h>
#include "protocol.h"
#define SERIALCOMM_1X_RS232 "8228/6n1/dtr=1/rts=1/flow=0" /* =8192, closer with divider */
#define SERIALCOMM_2X_RS232 "9600/6n1/dtr=1/rts=1/flow=0"
#define SERIALCOMM_2X "9600/8n1/dtr=1/rts=1/flow=0"
-#define VENDOR_GMC "Gossen Metrawatt"
static const uint32_t scanopts[] = {
SR_CONF_CONN,
SR_CONF_SERIALCOMM,
};
-/** Hardware capabilities for Metrahit 1x/2x devices in send mode. */
-static const uint32_t devopts_sm[] = {
+static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
SR_CONF_THERMOMETER, /**< All GMC 1x/2x multimeters seem to support this */
+};
+
+/** Hardware capabilities for Metrahit 1x/2x devices in send mode. */
+static const uint32_t devopts_sm[] = {
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 uint32_t devopts_bd[] = {
- SR_CONF_MULTIMETER,
- SR_CONF_THERMOMETER, /**< All GMC 1x/2x multimeters seem to support this */
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
conn = serialcomm = NULL;
serialcomm_given = FALSE;
- sr_spew("scan_1x_2x_rs232() called!");
-
for (l = options; l; l = l->next) {
src = l->data;
switch (src->key) {
}
if (model != METRAHIT_NONE) {
- sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(model));
+ sr_spew("%s detected!", gmc_model_str(model));
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(VENDOR_GMC);
+ sdi->vendor = g_strdup("Gossen Metrawatt");
sdi->model = g_strdup(gmc_model_str(model));
devc = g_malloc0(sizeof(struct dev_context));
sr_sw_limits_init(&devc->limits);
conn = serialcomm = NULL;
devices = NULL;
- sr_spew("scan_2x_bd232() called!");
-
for (l = options; l; l = l->next) {
src = l->data;
switch (src->key) {
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(VENDOR_GMC);
+ sdi->vendor = g_strdup("Gossen Metrawatt");
sdi->priv = devc;
/* Send message 03 "Query multimeter version and status" */
devc->buflen = 0;
if (devc->model != METRAHIT_NONE) {
- sr_spew("%s %s detected!", VENDOR_GMC, gmc_model_str(devc->model));
+ sr_spew("%s detected!", gmc_model_str(devc->model));
sr_sw_limits_init(&devc->limits);
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);
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->vendor = g_strdup("Gossen Metrawatt");
}
};
- /* Free last alloc if no device found */
- if (devc->model == METRAHIT_NONE) {
- g_free(devc);
- sr_dev_inst_free(sdi);
- }
+ /* Free last alloc that was done in preparation. */
+ g_free(devc);
+ sr_dev_inst_free(sdi);
return std_scan_complete(di, devices);
exit_err:
- sr_info("scan_2x_bd232(): Error!");
-
sr_serial_dev_inst_free(serial);
g_free(devc);
sr_dev_inst_free(sdi);
{
struct dev_context *devc;
- std_serial_dev_close(sdi);
+ devc = sdi->priv;
- sdi->status = SR_ST_INACTIVE;
- if ((devc = sdi->priv))
- devc->model = METRAHIT_NONE;
+ devc->model = METRAHIT_NONE;
- return SR_OK;
+ return std_serial_dev_close(sdi);
}
-static int config_get(uint32_t 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;
devc = sdi->priv;
- ret = SR_OK;
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
return SR_ERR_NA;
}
- return ret;
-}
-
-/** 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_UINT32,
- scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
- break;
- default:
- return SR_ERR_NA;
- }
-
return SR_OK;
}
/** Implementation of config_list for Metrahit 1x/2x send mode */
-static int config_list_sm(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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_UINT32,
- devopts_sm, ARRAY_SIZE(devopts_sm), sizeof(uint32_t));
- break;
- default:
- return config_list_common(key, data, sdi, cg);
- }
-
- return SR_OK;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts_sm);
}
/** Implementation of config_list for Metrahit 2x bidirectional mode */
-static int config_list_bd(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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_UINT32,
- devopts_bd, ARRAY_SIZE(devopts_bd), sizeof(uint32_t));
- break;
- default:
- return config_list_common(key, data, sdi, cg);
- }
-
- return SR_OK;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts_bd);
}
static int dev_acquisition_start_1x_2x_rs232(const struct sr_dev_inst *sdi)
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->settings_ok = FALSE;
devc->buflen = 0;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* Poll every 40ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 40,
gmc_mh_1x_2x_receive_data, (void *)sdi);
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->settings_ok = FALSE;
devc->buflen = 0;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* Poll every 40ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 40,
gmc_mh_2x_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan_1x_2x_rs232,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list_sm,
.cleanup = std_cleanup,
.scan = scan_2x_bd232,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list_bd,
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/**
- * @file
- *
- * Gossen Metrawatt Metrahit 1x/2x drivers
- *
- * @internal
- */
-
#include <config.h>
#include <math.h>
#include <string.h>
#include "protocol.h"
-/* Internal Headers */
static guchar calc_chksum_14(guchar *dta);
static int chk_msg14(struct sr_dev_inst *sdi);
case 0x08: /* 1000 Diode */
devc->mq = SR_MQ_VOLTAGE;
devc->unit = SR_UNIT_VOLT;
- devc->mqflags |= SR_MQFLAG_DIODE;
+ devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
break;
case 0x09: /* 1001 Ohm, °C */
case 0x0a: /* 1010 kOhm */
case 0x05: /* 0101 Diode/Diode with buzzer */
devc->mq = SR_MQ_VOLTAGE;
devc->unit = SR_UNIT_VOLT;
- devc->mqflags |= SR_MQFLAG_DIODE;
+ devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
break;
case 0x06: /* 0110 °C */
devc->mq = SR_MQ_TEMPERATURE;
devc->unit = SR_UNIT_VOLT;
if (ctmv == 0x0f) {
devc->mq = SR_MQ_VOLTAGE;
- devc->mqflags |= SR_MQFLAG_DIODE;
+ devc->mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
} else {
devc->mq = SR_MQ_CONTINUITY;
devc->scale += -5;
dgt = bc(devc->buf[5 + cnt]);
if (dgt == 11) { /* Empty digit */
dgt = 0;
- }
- else if (dgt >= 12) { /* Overload */
+ } else if (dgt >= 12) { /* Overload */
devc->value = NAN;
devc->scale = 0;
break;
sr_err("Device: Unknown error code!");
}
retc = SR_ERR_ARG;
- }
- else if (!isreq && ((devc->buf[1] != 0x27) || (devc->buf[2] != 0x3f))) {
+ } else if (!isreq && ((devc->buf[1] != 0x27) || (devc->buf[2] != 0x3f))) {
sr_err("process_msg_14(): byte 1/2 unexpected!");
retc = SR_ERR_ARG;
}
devc->value = NAN;
devc->scale = 0;
break;
- }
- else if (dgt == 13) { /* FUSE */
+ } else if (dgt == 13) { /* FUSE */
sr_err("FUSE!");
- }
- else if (dgt == 14) { /* Function recognition mode, OPEN */
+ } else if (dgt == 14) { /* Function recognition mode, OPEN */
sr_info("Function recognition mode, OPEN!");
devc->value = NAN;
devc->scale = 0;
process_msg_inf_10(sdi);
devc->buflen = 0;
continue;
- }
- else if ((devc->buflen >= 5) &&
+ } else if ((devc->buflen >= 5) &&
(devc->buf[devc->buflen - 1] &
MSGID_MASK) != MSGID_DATA) {
/*
* of next message.
*/
process_msg_inf_5(sdi);
- devc->buf[0] =
- devc->buf[devc->buflen - 1];
+ devc->buf[0] = devc->buf[devc->buflen - 1];
devc->buflen = 1;
continue;
}
}
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
}
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
/* Request next data set, if required */
if (sdi->status == SR_ST_ACTIVE) {
if (devc->cmd_seq % 10 == 0) {
if (req_stat14(sdi, FALSE) != SR_OK)
return FALSE;
- }
- else if (req_meas14(sdi) != SR_OK)
+ } else if (req_meas14(sdi) != SR_OK)
return FALSE;
}
}
}
}
-/** Request one measurement from 2x multimeter (msg 8).
- *
- */
+/** Request one measurement from 2x multimeter (msg 8). */
int req_meas14(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
return SR_OK;
}
-/** Decode model in "send mode".
+/**
+ * Decode model in "send mode".
*
* @param[in] mcode Model code.
* @return Model code.
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/**
- * @file
- *
- * Gossen Metrawatt Metrahit 1x/2x drivers
- *
- * @internal
- */
-
#ifndef LIBSIGROK_HARDWARE_GMC_MH_1X_2X_PROTOCOL_H
#define LIBSIGROK_HARDWARE_GMC_MH_1X_2X_PROTOCOL_H
METRAHIT_29S = METRAHIT_28S + 1,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
enum model model; /**< Model code. */
- /* Acquisition settings */
struct sr_sw_limits limits;
- /* Operational state */
gboolean settings_ok; /**< Settings msg received yet. */
int msg_type; /**< Message type (MSGID_INF, ...). */
int msg_len; /**< Message length (valid when msg, curr. type known).*/
int64_t req_sent_at; /**< Request sent. */
gboolean response_pending; /**< Request sent, response is pending. */
- /* Temporary state across callbacks */
uint8_t buf[GMC_BUFSIZE]; /**< Buffer for read callback */
int buflen; /**< Data len in buf */
};
-/* Forward declarations */
SR_PRIV int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg);
SR_PRIV int gmc_decode_model_bd(uint8_t mcode);
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_OSCILLOSCOPE,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
SR_CONF_SAMPLERATE | SR_CONF_GET,
};
return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
-
return SR_OK;
}
{
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;
+ if (!scpi)
+ return SR_ERR_BUG;
+
+ return sr_scpi_close(scpi);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
if (!sdi)
return SR_ERR_ARG;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
return SR_OK;
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
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;
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);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
data_size - devc->cur_rcv_buffer_position);
if (len < 0) {
sr_err("Read data error.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
devc->cur_rcv_buffer_position = 0;
return SR_ERR;
}
return SR_OK;
} else {
sr_err("Too many bytes read.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
devc->cur_rcv_buffer_position = 0;
return SR_ERR;
}
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);
+ sr_dev_acquisition_stop(sdi);
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);
+ sr_dev_acquisition_stop(sdi);
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);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
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);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
if (sr_scpi_read_begin(scpi) != SR_OK) {
sr_err("Could not begin reading SCPI response.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
devc->state = WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE;
/* All frames accquired. */
sr_spew("All frames acquired.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
} else {
/* Start acquiring next frame. */
}
break;
case WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE:
- if (read_data(sdi, scpi, devc, 1) == SR_OK) {
- if (devc->rcv_buffer[0] == '#')
- devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE;
- }
+ if (read_data(sdi, scpi, devc, 1) < 0)
+ break;
+ 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, 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);
- return TRUE;
- } else {
- devc->data_size_digits = devc->rcv_buffer[0] - '0';
- devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE;
- }
+ if (read_data(sdi, scpi, devc, 1) < 0)
+ break;
+ 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]);
+ sr_dev_acquisition_stop(sdi);
+ 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, 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);
- return TRUE;
- } else
- devc->state = WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE;
- }
+ if (read_data(sdi, scpi, devc, devc->data_size_digits) < 0)
+ break;
+ 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);
+ sr_dev_acquisition_stop(sdi);
+ 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, 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 (read_data(sdi, scpi, devc, sizeof(float)) < 0)
+ break;
+ /*
+ * 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);
+ if (!devc->df_started) {
+ std_session_send_df_header(sdi);
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(sdi, &packet);
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
- devc->df_started = TRUE;
- }
+ devc->df_started = TRUE;
}
break;
case WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE:
devc->state = WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE;
break;
case WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE:
- if (read_data(sdi, 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);
- 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);
+ if (read_data(sdi, scpi, devc, devc->data_size - 8) < 0)
+ break;
- num_samples = (devc->data_size - 8) / 2;
- sr_spew("Received %d number of samples from channel "
- "%d.", num_samples, devc->cur_acq_channel + 1);
+ /* 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.");
+ sr_dev_acquisition_stop(sdi);
+ 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);
- float vbit = volts_per_division * VERTICAL_DIVISIONS / 256.0;
- float vbitlog = log10f(vbit);
- int digits = -(int)vbitlog + (vbitlog < 0.0);
+ 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])))) * vbit;
+ float vbit = volts_per_division * VERTICAL_DIVISIONS / 256.0;
+ float vbitlog = log10f(vbit);
+ int digits = -(int)vbitlog + (vbitlog < 0.0);
- /* Fill frame. */
- sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
- analog.meaning->channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, devc->cur_acq_channel));
- analog.num_samples = num_samples;
- analog.data = samples;
- analog.meaning->mq = SR_MQ_VOLTAGE;
- analog.meaning->unit = SR_UNIT_VOLT;
- analog.meaning->mqflags = 0;
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(sdi, &packet);
- g_slist_free(analog.meaning->channels);
+ /* Convert data. */
+ for (i = 0; i < num_samples; i++)
+ samples[i] = ((float) ((int16_t) (RB16(&devc->rcv_buffer[i*2])))) * vbit;
- /* All channels acquired. */
- if (devc->cur_acq_channel == ANALOG_CHANNELS - 1) {
- sr_spew("All channels acquired.");
+ /* Fill frame. */
+ sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+ analog.meaning->channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, devc->cur_acq_channel));
+ analog.num_samples = num_samples;
+ analog.data = samples;
+ analog.meaning->mq = SR_MQ_VOLTAGE;
+ analog.meaning->unit = SR_UNIT_VOLT;
+ analog.meaning->mqflags = 0;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(sdi, &packet);
+ g_slist_free(analog.meaning->channels);
- if (devc->cur_acq_frame == devc->frame_limit - 1) {
- /* All frames acquired. */
- sr_spew("All frames acquired.");
- sdi->driver->dev_acquisition_stop(sdi);
- return TRUE;
- } else {
- /* Start acquiring next frame. */
- if (devc->df_started) {
- packet.type = SR_DF_FRAME_END;
- sr_session_send(sdi, &packet);
+ /* All channels acquired. */
+ if (devc->cur_acq_channel == ANALOG_CHANNELS - 1) {
+ sr_spew("All channels acquired.");
- 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++;
+ if (devc->cur_acq_frame == devc->frame_limit - 1) {
+ /* All frames acquired. */
+ sr_spew("All frames acquired.");
+ sr_dev_acquisition_stop(sdi);
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;
}
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;
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Bastian Schmitz <bastian.schmitz@udo.edu>
+ *
+ * This program is free software: you can 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"
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_POWER_SUPPLY,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_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,
+};
+
+static const char *channel_modes[] = {
+ "Independent",
+};
+
+static const struct gpd_model models[] = {
+ { GPD_2303S, "GPD-2303S",
+ CHANMODE_INDEPENDENT,
+ 2,
+ {
+ /* Channel 1 */
+ { { 0, 30, 0.001 }, { 0, 3, 0.001 } },
+ /* Channel 2 */
+ { { 0, 30, 0.001 }, { 0, 3, 0.001 } },
+ },
+ },
+};
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ const char *conn, *serialcomm;
+ const struct gpd_model *model;
+ const struct sr_config *src;
+ struct sr_channel *ch;
+ struct sr_channel_group *cg;
+ GSList *l;
+ struct sr_serial_dev_inst *serial;
+ struct sr_dev_inst *sdi;
+ char reply[50];
+ unsigned int i;
+ struct dev_context *devc;
+ char channel[10];
+ GRegex *regex;
+ GMatchInfo *match_info;
+ unsigned int cc_cv_ch1, cc_cv_ch2, track1, track2, beep, baud1, baud2;
+
+ serial = NULL;
+ match_info = NULL;
+ regex = NULL;
+ conn = NULL;
+ 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 = "115200/8n1";
+ sr_info("Probing serial port %s.", conn);
+ serial = sr_serial_dev_inst_new(conn, serialcomm);
+ if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+ return NULL;
+
+ serial_flush(serial);
+ gpd_send_cmd(serial, "*IDN?\n");
+ if (gpd_receive_reply(serial, reply, sizeof(reply)) != SR_OK) {
+ sr_err("Device did not reply.");
+ goto error;
+ }
+ serial_flush(serial);
+
+ /*
+ * Returned identification string is for example:
+ * "GW INSTEK,GPD-2303S,SN:ER915277,V2.10"
+ */
+ regex = g_regex_new("GW INSTEK,(.+),SN:(.+),(V.+)", 0, 0, NULL);
+ if (!g_regex_match(regex, reply, 0, &match_info)) {
+ sr_err("Unsupported model '%s'.", reply);
+ goto error;
+ }
+
+ model = NULL;
+ for (i = 0; i < ARRAY_SIZE(models); i++) {
+ if (!strcmp(g_match_info_fetch(match_info, 1), models[i].name)) {
+ model = &models[i];
+ break;
+ }
+ }
+ if (!model) {
+ sr_err("Unsupported model '%s'.", reply);
+ goto error;
+ }
+
+ sr_info("Detected model '%s'.", model->name);
+
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->status = SR_ST_INACTIVE;
+ sdi->vendor = g_strdup("GW Instek");
+ sdi->model = g_strdup(model->name);
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+
+ for (i = 0; i < model->num_channels; i++) {
+ snprintf(channel, sizeof(channel), "CH%d", i + 1);
+ 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);
+ cg->priv = NULL;
+ sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+ }
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ sr_sw_limits_init(&devc->limits);
+ devc->model = model;
+ devc->config = g_malloc0(sizeof(struct per_channel_config)
+ * model->num_channels);
+ sdi->priv = devc;
+
+ serial_flush(serial);
+ gpd_send_cmd(serial, "STATUS?\n");
+ gpd_receive_reply(serial, reply, sizeof(reply));
+
+ if (sscanf(reply, "%1u%1u%1u%1u%1u%1u%1u%1u", &cc_cv_ch1,
+ &cc_cv_ch2, &track1, &track2, &beep,
+ &devc->output_enabled, &baud1, &baud2) != 8) {
+ sr_err("Invalid reply to STATUS: '%s'.", reply);
+ goto error;
+ }
+
+ for (i = 0; i < model->num_channels; ++i) {
+ gpd_send_cmd(serial, "ISET%d?\n", i + 1);
+ gpd_receive_reply(serial, reply, sizeof(reply));
+ if (sscanf(reply, "%f", &devc->config[i].output_current_max) != 1) {
+ sr_err("Invalid reply to ISETn?: '%s'.", reply);
+ goto error;
+ }
+
+ gpd_send_cmd(serial, "VSET%d?\n", i + 1);
+ gpd_receive_reply(serial, reply, sizeof(reply));
+ if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
+ sr_err("Invalid reply to VSETn?: '%s'.", reply);
+ goto error;
+ }
+ gpd_send_cmd(serial, "IOUT%d?\n", i + 1);
+ gpd_receive_reply(serial, reply, sizeof(reply));
+ if (sscanf(reply, "%f", &devc->config[i].output_current_last) != 1) {
+ sr_err("Invalid reply to IOUTn?: '%s'.", reply);
+ goto error;
+ }
+ gpd_send_cmd(serial, "VOUT%d?\n", i + 1);
+ gpd_receive_reply(serial, reply, sizeof(reply));
+ if (sscanf(reply, "%f", &devc->config[i].output_voltage_last) != 1) {
+ sr_err("Invalid reply to VOUTn?: '%s'.", reply);
+ goto error;
+ }
+ }
+
+ serial_close(serial);
+
+ return std_scan_complete(di, g_slist_append(NULL, sdi));
+
+error:
+ if (match_info)
+ g_match_info_free(match_info);
+ if (regex)
+ g_regex_unref(regex);
+ if (serial)
+ serial_close(serial);
+
+ return NULL;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ int ret, channel;
+ const struct dev_context *devc;
+ const struct sr_channel *ch;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ if (!cg) {
+ switch (key) {
+ case SR_CONF_CHANNEL_CONFIG:
+ *data = g_variant_new_string(
+ channel_modes[devc->channel_mode]);
+ break;
+ case SR_CONF_ENABLED:
+ *data = g_variant_new_boolean(devc->output_enabled);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ } else {
+ ch = cg->channels->data;
+ channel = ch->index;
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_VOLTAGE:
+ *data = g_variant_new_double(
+ devc->config[channel].output_voltage_last);
+ break;
+ case SR_CONF_VOLTAGE_TARGET:
+ *data = g_variant_new_double(
+ devc->config[channel].output_voltage_max);
+ break;
+ case SR_CONF_CURRENT:
+ *data = g_variant_new_double(
+ devc->config[channel].output_current_last);
+ break;
+ case SR_CONF_CURRENT_LIMIT:
+ *data = g_variant_new_double(
+ devc->config[channel].output_current_max);
+ 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)
+{
+ int ret, channel;
+ const struct sr_channel *ch;
+ double dval;
+ gboolean bval;
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ ret = SR_OK;
+
+ switch (key) {
+ case SR_CONF_LIMIT_MSEC:
+ case SR_CONF_LIMIT_SAMPLES:
+ return sr_sw_limits_config_set(&devc->limits, key, data);
+ case SR_CONF_ENABLED:
+ bval = g_variant_get_boolean(data);
+ gpd_send_cmd(sdi->conn, "OUT%c\n", bval ? '1' : '0');
+ devc->output_enabled = bval;
+ break;
+ case SR_CONF_VOLTAGE_TARGET:
+ ch = cg->channels->data;
+ channel = ch->index;
+ dval = g_variant_get_double(data);
+ if (dval < devc->model->channels[channel].voltage[0]
+ || dval > devc->model->channels[channel].voltage[1])
+ return SR_ERR_ARG;
+ gpd_send_cmd(sdi->conn, "VSET%d:%05.3lf\n", channel + 1, dval);
+ devc->config[channel].output_voltage_max = dval;
+ break;
+ case SR_CONF_CURRENT_LIMIT:
+ ch = cg->channels->data;
+ channel = ch->index;
+ dval = g_variant_get_double(data);
+ if (dval < devc->model->channels[channel].current[0]
+ || dval > devc->model->channels[channel].current[1])
+ return SR_ERR_ARG;
+ gpd_send_cmd(sdi->conn, "ISET%d:%05.3lf\n", channel + 1, dval);
+ devc->config[channel].output_current_max = dval;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ break;
+ }
+
+ return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ const struct dev_context *devc;
+ const struct sr_channel *ch;
+ int channel;
+
+ devc = (sdi) ? sdi->priv : NULL;
+
+ if (!cg) {
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts,
+ drvopts, devopts);
+ case SR_CONF_CHANNEL_CONFIG:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(channel_modes));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ } else {
+ ch = cg->channels->data;
+ channel = ch->index;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
+ break;
+ case SR_CONF_VOLTAGE_TARGET:
+ *data = std_gvar_min_max_step_array(
+ devc->model->channels[channel].voltage);
+ break;
+ case SR_CONF_CURRENT_LIMIT:
+ *data = std_gvar_min_max_step_array(
+ devc->model->channels[channel].current);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ devc = sdi->priv;
+
+ sr_sw_limits_acquisition_start(&devc->limits);
+ std_session_send_df_header(sdi);
+
+ devc->reply_pending = FALSE;
+ devc->req_sent_at = 0;
+ serial = sdi->conn;
+ serial_source_add(sdi->session, serial, G_IO_IN, 100,
+ gpd_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static struct sr_dev_driver gwinstek_gpd_driver_info = {
+ .name = "gwinstek-gpd",
+ .longname = "GW Instek GPD",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = std_serial_dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = std_serial_dev_acquisition_stop,
+ .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(gwinstek_gpd_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Bastian Schmitz <bastian.schmitz@udo.edu>
+ *
+ * This program is free software: you can 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 gpd_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);
+
+ ret = serial_write_blocking(serial, cmdbuf, strlen(cmdbuf),
+ serial_timeout(serial, strlen(cmdbuf)));
+ if (ret < 0) {
+ sr_err("Error sending command: %d.", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+SR_PRIV int gpd_receive_reply(struct sr_serial_dev_inst *serial, char *buf,
+ int buflen)
+{
+ int l_recv = 0, bufpos = 0, retc, l_startpos = 0, lines = 1;
+ gint64 start, remaining;
+ const int timeout_ms = 100;
+
+ if (!serial || (lines <= 0) || !buf || (buflen <= 0))
+ return SR_ERR_ARG;
+
+ start = g_get_monotonic_time();
+ remaining = timeout_ms;
+
+ while ((l_recv < lines) && (bufpos < (buflen + 1))) {
+ retc = serial_read_blocking(serial, &buf[bufpos], 1, remaining);
+ if (retc != 1)
+ return SR_ERR;
+
+ if (bufpos == 0 && buf[bufpos] == '\r')
+ continue;
+ if (bufpos == 0 && buf[bufpos] == '\n')
+ continue;
+
+ if (buf[bufpos] == '\n') {
+ buf[bufpos] = '\0';
+ sr_dbg("Received line '%s'.", &buf[l_startpos]);
+ buf[bufpos] = '\n';
+ l_startpos = bufpos + 1;
+ l_recv++;
+ }
+ bufpos++;
+
+ /* Reduce timeout by time elapsed. */
+ remaining = timeout_ms - ((g_get_monotonic_time() - start) / 1000);
+ if (remaining <= 0)
+ return SR_ERR; /* Timeout. */
+ }
+
+ buf[bufpos] = '\0';
+
+ if (l_recv == lines)
+ return SR_OK;
+ else
+ return SR_ERR;
+}
+
+SR_PRIV int gpd_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 analog;
+ struct sr_analog_encoding encoding;
+ struct sr_analog_meaning meaning;
+ struct sr_analog_spec spec;
+ struct sr_channel *ch;
+ unsigned int i;
+ char reply[50];
+ char *reply_esc;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) {
+ if (!devc->reply_pending) {
+ sr_err("No reply pending.");
+ gpd_receive_reply(serial, reply, sizeof(reply));
+ reply_esc = g_strescape(reply, NULL);
+ sr_err("Unexpected data '%s'.", reply_esc);
+ g_free(reply_esc);
+ } else {
+ for (i = 0; i < devc->model->num_channels; i++) {
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+
+ reply[0] = '\0';
+ gpd_receive_reply(serial, reply, sizeof(reply));
+ if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
+ sr_err("Invalid reply to VOUT1?: '%s'.",
+ reply);
+ return TRUE;
+ }
+
+ /* Send the value forward. */
+ sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+ analog.num_samples = 1;
+ ch = g_slist_nth_data(sdi->channels, i);
+ analog.meaning->channels =
+ g_slist_append(NULL, ch);
+ analog.meaning->mq = SR_MQ_CURRENT;
+ analog.meaning->unit = SR_UNIT_AMPERE;
+ analog.meaning->mqflags = 0;
+ analog.encoding->digits = 3;
+ analog.spec->spec_digits = 3;
+ analog.data = &devc->config[i].output_current_max;
+ sr_session_send(sdi, &packet);
+
+ reply[0] = '\0';
+ gpd_receive_reply(serial, reply, sizeof(reply));
+ if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
+ sr_err("Invalid reply to VOUT1?: '%s'.",
+ reply);
+ return TRUE;
+ }
+
+ /* Send the value forward. */
+ sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+ analog.num_samples = 1;
+ ch = g_slist_nth_data(sdi->channels, i);
+ analog.meaning->channels =
+ g_slist_append(NULL, ch);
+ analog.meaning->mq = SR_MQ_VOLTAGE;
+ analog.meaning->unit = SR_UNIT_VOLT;
+ analog.meaning->mqflags = SR_MQFLAG_DC;
+ analog.encoding->digits = 3;
+ analog.spec->spec_digits = 3;
+ analog.data = &devc->config[i].output_voltage_max;
+ sr_session_send(sdi, &packet);
+ }
+
+ devc->reply_pending = FALSE;
+ }
+ } else {
+ if (!devc->reply_pending) {
+ for (i = 0; i < devc->model->num_channels; i++)
+ gpd_send_cmd(serial, "IOUT%d?\nVOUT%d?\n",
+ i + 1, i + 1);
+ devc->req_sent_at = g_get_monotonic_time();
+ devc->reply_pending = TRUE;
+ }
+ }
+
+ if (sr_sw_limits_check(&devc->limits)) {
+ sr_dev_acquisition_stop(sdi);
+ return TRUE;
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Bastian Schmitz <bastian.schmitz@udo.edu>
+ *
+ * This program is free software: you can 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_GPD_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_GWINSTEK_GPD_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "gwinstek-gpd"
+
+enum {
+ GPD_2303S,
+};
+
+/* Maximum number of output channels handled by this driver. */
+#define MAX_CHANNELS 2
+
+#define CHANMODE_INDEPENDENT (1 << 0)
+#define CHANMODE_SERIES (1 << 1)
+#define CHANMODE_PARALLEL (1 << 2)
+
+struct channel_spec {
+ /* Min, max, step. */
+ gdouble voltage[3];
+ gdouble current[3];
+};
+
+struct gpd_model {
+ int modelid;
+ const char *name;
+ int channel_modes;
+ unsigned int num_channels;
+ struct channel_spec channels[MAX_CHANNELS];
+};
+
+struct per_channel_config {
+ /* Received from device. */
+ float output_voltage_last;
+ float output_current_last;
+ /* Set by frontend. */
+ float output_voltage_max;
+ float output_current_max;
+};
+
+struct dev_context {
+ /* Received from device. */
+ gboolean output_enabled;
+ int64_t req_sent_at;
+ gboolean reply_pending;
+
+ struct sr_sw_limits limits;
+ int channel_mode;
+ struct per_channel_config *config;
+ const struct gpd_model *model;
+};
+
+SR_PRIV int gpd_send_cmd(struct sr_serial_dev_inst *serial, const char *cmd, ...);
+SR_PRIV int gpd_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int gpd_receive_reply(struct sr_serial_dev_inst *serial, char *buf, int buflen);
+
+#endif
"Rohde&Schwarz",
};
-static const uint32_t drvopts[] = {
- SR_CONF_OSCILLOSCOPE,
-};
-
static const uint32_t scanopts[] = {
SR_CONF_CONN,
SR_CONF_SERIALCOMM,
};
+static const uint32_t drvopts[] = {
+ SR_CONF_OSCILLOSCOPE,
+};
+
enum {
CG_INVALID = -1,
CG_NONE,
CG_DIGITAL,
};
-static int check_manufacturer(const char *manufacturer)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(manufacturers); i++)
- if (!strcmp(manufacturer, manufacturers[i]))
- return SR_OK;
-
- return SR_ERR;
-}
-
-static struct sr_dev_inst *hmo_probe_serial_device(struct sr_scpi_dev_inst *scpi)
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
{
struct sr_dev_inst *sdi;
struct dev_context *devc;
goto fail;
}
- if (check_manufacturer(hw_info->manufacturer) != SR_OK)
+ if (std_str_idx_s(hw_info->manufacturer, ARRAY_AND_SIZE(manufacturers)) < 0)
goto fail;
sdi = g_malloc0(sizeof(struct sr_dev_inst));
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
- return sr_scpi_scan(di->context, options, hmo_probe_serial_device);
+ return sr_scpi_scan(di->context, options, probe_device);
}
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
-
hmo_scope_state_free(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);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static int dev_open(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
+ if (sr_scpi_open(sdi->conn) != SR_OK)
return SR_ERR;
if (hmo_scope_state_get(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;
+ return sr_scpi_close(sdi->conn);
}
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;
+ if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) >= 0)
+ return CG_ANALOG;
- for (i = 0; i < model->digital_pods; i++)
- if (cg == devc->digital_groups[i])
- return CG_DIGITAL;
+ if (std_cg_idx(cg, devc->digital_groups, model->digital_pods) >= 0)
+ 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)
+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;
+ int cg_type, idx;
struct dev_context *devc;
const struct scope_config *model;
struct scope_state *state;
if ((cg_type = check_channel_group(devc, cg)) == CG_INVALID)
return SR_ERR;
- ret = SR_ERR_NA;
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)", (*model->timebases)[state->timebase][0],
(*model->timebases)[state->timebase][1]);
- ret = SR_OK;
break;
case SR_CONF_NUM_VDIV:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
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])
- continue;
- *data = g_variant_new_int32(model->num_ydivs);
- ret = SR_OK;
- break;
- }
-
- } else {
- ret = SR_ERR_NA;
- }
+ if (cg_type != CG_ANALOG)
+ return SR_ERR_NA;
+ if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) < 0)
+ return SR_ERR_ARG;
+ *data = g_variant_new_int32(model->num_ydivs);
break;
case SR_CONF_VDIV:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
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])
- continue;
- *data = g_variant_new("(tt)",
- (*model->vdivs)[state->analog_channels[i].vdiv][0],
- (*model->vdivs)[state->analog_channels[i].vdiv][1]);
- ret = SR_OK;
- break;
- }
-
- } else {
- ret = SR_ERR_NA;
- }
+ if (cg_type != CG_ANALOG)
+ return SR_ERR_NA;
+ if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ *data = g_variant_new("(tt)",
+ (*model->vdivs)[state->analog_channels[idx].vdiv][0],
+ (*model->vdivs)[state->analog_channels[idx].vdiv][1]);
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((*model->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:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
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])
- continue;
- *data = g_variant_new_string((*model->coupling_options)[state->analog_channels[i].coupling]);
- ret = SR_OK;
- break;
- }
-
- } else {
- ret = SR_ERR_NA;
- }
+ if (cg_type != CG_ANALOG)
+ return SR_ERR_NA;
+ if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ *data = g_variant_new_string((*model->coupling_options)[state->analog_channels[idx].coupling]);
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 SR_ERR_NA;
}
- return g_variant_builder_end(&gvb);
+ return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
+ int ret, cg_type, idx, j;
char command[MAX_COMMAND_SIZE], 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;
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;
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SOURCE],
- (*model->trigger_sources)[i]);
-
- ret = sr_scpi_send(sdi->conn, command);
- break;
- }
+ if ((idx = std_str_idx(data, *model->trigger_sources, model->num_trigger_sources)) < 0)
+ return SR_ERR_ARG;
+ state->trigger_source = idx;
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SOURCE],
+ (*model->trigger_sources)[idx]);
+ ret = sr_scpi_send(sdi->conn, command);
break;
case SR_CONF_VDIV:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- }
-
- g_variant_get(data, "(tt)", &p, &q);
-
- for (i = 0; i < model->num_vdivs; i++) {
- 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])
- continue;
- state->analog_channels[j - 1].vdiv = i;
- g_ascii_formatd(float_str, sizeof(float_str), "%E", (float) p / q);
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_VERTICAL_DIV],
- j, float_str);
-
- if (sr_scpi_send(sdi->conn, command) != SR_OK ||
- sr_scpi_get_opc(sdi->conn) != SR_OK)
- return SR_ERR;
-
- break;
- }
-
- ret = SR_OK;
- break;
- }
+ if ((idx = std_u64_tuple_idx(data, *model->vdivs, model->num_vdivs)) < 0)
+ return SR_ERR_ARG;
+ if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ state->analog_channels[j].vdiv = idx;
+ g_ascii_formatd(float_str, sizeof(float_str), "%E",
+ (float) (*model->vdivs)[idx][0] / (*model->vdivs)[idx][1]);
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_VERTICAL_DIV],
+ j + 1, float_str);
+ if (sr_scpi_send(sdi->conn, command) != SR_OK ||
+ sr_scpi_get_opc(sdi->conn) != SR_OK)
+ return SR_ERR;
+ ret = SR_OK;
break;
case SR_CONF_TIMEBASE:
- g_variant_get(data, "(tt)", &p, &q);
-
- for (i = 0; i < model->num_timebases; i++) {
- if (p != (*model->timebases)[i][0] ||
- q != (*model->timebases)[i][1])
- continue;
- state->timebase = i;
- g_ascii_formatd(float_str, sizeof(float_str), "%E", (float) p / q);
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_TIMEBASE],
- float_str);
-
- ret = sr_scpi_send(sdi->conn, command);
- update_sample_rate = TRUE;
- break;
- }
+ if ((idx = std_u64_tuple_idx(data, *model->timebases, model->num_timebases)) < 0)
+ return SR_ERR_ARG;
+ state->timebase = idx;
+ g_ascii_formatd(float_str, sizeof(float_str), "%E",
+ (float) (*model->timebases)[idx][0] / (*model->timebases)[idx][1]);
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_TIMEBASE],
+ float_str);
+ ret = sr_scpi_send(sdi->conn, command);
+ update_sample_rate = TRUE;
break;
case SR_CONF_HORIZ_TRIGGERPOS:
tmp_d = g_variant_get_double(data);
-
if (tmp_d < 0.0 || tmp_d > 1.0)
return SR_ERR;
-
state->horiz_triggerpos = tmp_d;
tmp_d = -(tmp_d - 0.5) *
((double) (*model->timebases)[state->timebase][0] /
(*model->timebases)[state->timebase][1])
* model->num_xdivs;
-
g_ascii_formatd(float_str, sizeof(float_str), "%E", tmp_d);
g_snprintf(command, sizeof(command),
(*model->scpi_dialect)[SCPI_CMD_SET_HORIZ_TRIGGERPOS],
float_str);
-
ret = sr_scpi_send(sdi->conn, command);
break;
case SR_CONF_TRIGGER_SLOPE:
- tmp = g_variant_get_string(data, NULL);
- for (i = 0; (*model->trigger_slopes)[i]; i++) {
- if (g_strcmp0(tmp, (*model->trigger_slopes)[i]) != 0)
- continue;
- state->trigger_slope = i;
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SLOPE],
- (*model->trigger_slopes)[i]);
-
- ret = sr_scpi_send(sdi->conn, command);
- break;
- }
+ if ((idx = std_str_idx(data, *model->trigger_slopes, model->num_trigger_slopes)) < 0)
+ return SR_ERR_ARG;
+ state->trigger_slope = idx;
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_TRIGGER_SLOPE],
+ (*model->trigger_slopes)[idx]);
+ ret = sr_scpi_send(sdi->conn, command);
break;
case SR_CONF_COUPLING:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
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_channels[j-1].coupling = i;
-
- g_snprintf(command, sizeof(command),
- (*model->scpi_dialect)[SCPI_CMD_SET_COUPLING],
- j, tmp);
-
- if (sr_scpi_send(sdi->conn, command) != SR_OK ||
- sr_scpi_get_opc(sdi->conn) != SR_OK)
- return SR_ERR;
- break;
- }
-
- ret = SR_OK;
- break;
- }
+ if ((idx = std_str_idx(data, *model->coupling_options, model->num_coupling_options)) < 0)
+ return SR_ERR_ARG;
+ if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ state->analog_channels[j].coupling = idx;
+ g_snprintf(command, sizeof(command),
+ (*model->scpi_dialect)[SCPI_CMD_SET_COUPLING],
+ j + 1, (*model->coupling_options)[idx]);
+ if (sr_scpi_send(sdi->conn, command) != SR_OK ||
+ sr_scpi_get_opc(sdi->conn) != SR_OK)
+ return SR_ERR;
+ ret = SR_OK;
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)
+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;
switch (key) {
case SR_CONF_SCAN_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(scanopts));
break;
case SR_CONF_DEVICE_OPTIONS:
- if (cg_type == CG_NONE) {
+ if (!cg) {
if (model)
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- model->devopts, model->num_devopts, sizeof(uint32_t));
+ *data = std_gvar_array_u32(*model->devopts, model->num_devopts);
else
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(drvopts));
} else if (cg_type == CG_ANALOG) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- model->analog_devopts, model->num_analog_devopts,
- sizeof(uint32_t));
+ *data = std_gvar_array_u32(*model->devopts_cg_analog, model->num_devopts_cg_analog);
} else {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- NULL, 0, sizeof(uint32_t));
+ *data = std_gvar_array_u32(NULL, 0);
}
break;
case SR_CONF_COUPLING:
- if (cg_type == CG_NONE)
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- *data = g_variant_new_strv(*model->coupling_options,
- g_strv_length((char **)*model->coupling_options));
+ if (!model)
+ return SR_ERR_ARG;
+ *data = g_variant_new_strv(*model->coupling_options, model->num_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));
+ *data = g_variant_new_strv(*model->trigger_sources, model->num_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));
+ *data = g_variant_new_strv(*model->trigger_slopes, model->num_trigger_slopes);
break;
case SR_CONF_TIMEBASE:
if (!model)
return SR_ERR_ARG;
- *data = build_tuples(model->timebases, model->num_timebases);
+ *data = std_gvar_tuple_array(*model->timebases, model->num_timebases);
break;
case SR_CONF_VDIV:
- if (cg_type == CG_NONE)
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- *data = build_tuples(model->vdivs, model->num_vdivs);
+ if (!model)
+ return SR_ERR_ARG;
+ *data = std_gvar_tuple_array(*model->vdivs, model->num_vdivs);
break;
default:
return SR_ERR_NA;
struct sr_channel *ch;
struct dev_context *devc;
struct sr_scpi_dev_inst *scpi;
+ int ret;
devc = sdi->priv;
scpi = sdi->conn;
(*model->scpi_dialect)[SCPI_CMD_SET_ANALOG_CHAN_STATE],
ch->index + 1, ch->enabled);
- if (sr_scpi_send(scpi, command) != SR_OK)
+ if (sr_scpi_send(scpi, command) != SR_OK) {
+ g_free(pod_enabled);
return SR_ERR;
+ }
state->analog_channels[ch->index].state = ch->enabled;
setup_changed = TRUE;
break;
(*model->scpi_dialect)[SCPI_CMD_SET_DIG_CHAN_STATE],
ch->index, ch->enabled);
- if (sr_scpi_send(scpi, command) != SR_OK)
+ if (sr_scpi_send(scpi, command) != SR_OK) {
+ g_free(pod_enabled);
return SR_ERR;
+ }
state->digital_channels[ch->index] = ch->enabled;
setup_changed = TRUE;
break;
default:
+ g_free(pod_enabled);
return SR_ERR;
}
}
- for (i = 1; i <= model->digital_pods; i++) {
- if (state->digital_pods[i - 1] == pod_enabled[i - 1])
+ ret = SR_OK;
+ for (i = 0; i < model->digital_pods; i++) {
+ if (state->digital_pods[i] == pod_enabled[i])
continue;
g_snprintf(command, sizeof(command),
(*model->scpi_dialect)[SCPI_CMD_SET_DIG_POD_STATE],
- i, pod_enabled[i - 1]);
- if (sr_scpi_send(scpi, command) != SR_OK)
- return SR_ERR;
- state->digital_pods[i - 1] = pod_enabled[i - 1];
+ i + 1, pod_enabled[i]);
+ if (sr_scpi_send(scpi, command) != SR_OK) {
+ ret = SR_ERR;
+ break;
+ }
+ state->digital_pods[i] = pod_enabled[i];
setup_changed = TRUE;
}
-
g_free(pod_enabled);
+ if (ret != SR_OK)
+ return ret;
if (setup_changed && hmo_update_sample_rate(sdi) != SR_OK)
return SR_ERR;
struct sr_scpi_dev_inst *scpi;
int ret;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
scpi = sdi->conn;
devc = sdi->priv;
std_session_send_df_end(sdi);
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
devc->num_frames = 0;
[SCPI_CMD_GET_PROBE_UNIT] = ":PROB%d:SET:ATT:UNIT?",
};
-static const uint32_t hmo_devopts[] = {
+static const uint32_t devopts[] = {
SR_CONF_OSCILLOSCOPE,
SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_SAMPLERATE | SR_CONF_GET,
SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
-static const uint32_t hmo_analog_devopts[] = {
+static const uint32_t devopts_cg_analog[] = {
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 char *hmo_coupling_options[] = {
+static const char *coupling_options[] = {
"AC", // AC with 50 Ohm termination (152x, 202x, 30xx, 1202)
"ACL", // AC with 1 MOhm termination
"DC", // DC with 50 Ohm termination
"DCL", // DC with 1 MOhm termination
"GND",
- NULL,
};
static const char *scope_trigger_slopes[] = {
"POS",
"NEG",
"EITH",
- NULL,
};
-static const char *hmo_compact2_trigger_sources[] = {
- "CH1",
- "CH2",
- "LINE",
- "EXT",
- "PATT",
- "BUS1",
- "BUS2",
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- NULL,
+static const char *compact2_trigger_sources[] = {
+ "CH1", "CH2",
+ "LINE", "EXT", "PATT", "BUS1", "BUS2",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
};
-static const char *hmo_compact4_trigger_sources[] = {
- "CH1",
- "CH2",
- "CH3",
- "CH4",
- "LINE",
- "EXT",
- "PATT",
- "BUS1",
- "BUS2",
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- NULL,
+static const char *compact4_trigger_sources[] = {
+ "CH1", "CH2", "CH3", "CH4",
+ "LINE", "EXT", "PATT", "BUS1", "BUS2",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
};
-static const char *hmo_compact4_dig16_trigger_sources[] = {
- "CH1",
- "CH2",
- "CH3",
- "CH4",
- "LINE",
- "EXT",
- "PATT",
- "BUS1",
- "BUS2",
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- "D8",
- "D9",
- "D10",
- "D11",
- "D12",
- "D13",
- "D14",
- "D15",
- NULL,
+static const char *compact4_dig16_trigger_sources[] = {
+ "CH1", "CH2", "CH3", "CH4",
+ "LINE", "EXT", "PATT", "BUS1", "BUS2",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+ "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15",
};
-static const uint64_t hmo_timebases[][2] = {
+static const uint64_t timebases[][2] = {
/* nanoseconds */
{ 2, 1000000000 },
{ 5, 1000000000 },
{ 50, 1 },
};
-static const uint64_t hmo_vdivs[][2] = {
+static const uint64_t vdivs[][2] = {
/* millivolts */
{ 1, 1000 },
{ 2, 1000 },
};
static const char *scope_analog_channel_names[] = {
- "CH1",
- "CH2",
- "CH3",
- "CH4",
+ "CH1", "CH2", "CH3", "CH4",
};
static const char *scope_digital_channel_names[] = {
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- "D8",
- "D9",
- "D10",
- "D11",
- "D12",
- "D13",
- "D14",
- "D15",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+ "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15",
};
static const struct scope_config scope_models[] = {
.analog_names = &scope_analog_channel_names,
.digital_names = &scope_digital_channel_names,
- .devopts = &hmo_devopts,
- .num_devopts = ARRAY_SIZE(hmo_devopts),
+ .devopts = &devopts,
+ .num_devopts = ARRAY_SIZE(devopts),
- .analog_devopts = &hmo_analog_devopts,
- .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+ .devopts_cg_analog = &devopts_cg_analog,
+ .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog),
+
+ .coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+ .trigger_sources = &compact2_trigger_sources,
+ .num_trigger_sources = ARRAY_SIZE(compact2_trigger_sources),
- .coupling_options = &hmo_coupling_options,
- .trigger_sources = &hmo_compact2_trigger_sources,
.trigger_slopes = &scope_trigger_slopes,
+ .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes),
- .timebases = &hmo_timebases,
- .num_timebases = ARRAY_SIZE(hmo_timebases),
+ .timebases = &timebases,
+ .num_timebases = ARRAY_SIZE(timebases),
- .vdivs = &hmo_vdivs,
- .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+ .vdivs = &vdivs,
+ .num_vdivs = ARRAY_SIZE(vdivs),
.num_xdivs = 12,
.num_ydivs = 8,
.analog_names = &scope_analog_channel_names,
.digital_names = &scope_digital_channel_names,
- .devopts = &hmo_devopts,
- .num_devopts = ARRAY_SIZE(hmo_devopts),
+ .devopts = &devopts,
+ .num_devopts = ARRAY_SIZE(devopts),
+
+ .devopts_cg_analog = &devopts_cg_analog,
+ .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog),
- .analog_devopts = &hmo_analog_devopts,
- .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+ .coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+ .trigger_sources = &compact4_trigger_sources,
+ .num_trigger_sources = ARRAY_SIZE(compact4_trigger_sources),
- .coupling_options = &hmo_coupling_options,
- .trigger_sources = &hmo_compact4_trigger_sources,
.trigger_slopes = &scope_trigger_slopes,
+ .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes),
- .timebases = &hmo_timebases,
- .num_timebases = ARRAY_SIZE(hmo_timebases),
+ .timebases = &timebases,
+ .num_timebases = ARRAY_SIZE(timebases),
- .vdivs = &hmo_vdivs,
- .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+ .vdivs = &vdivs,
+ .num_vdivs = ARRAY_SIZE(vdivs),
.num_xdivs = 12,
.num_ydivs = 8,
.analog_names = &scope_analog_channel_names,
.digital_names = &scope_digital_channel_names,
- .devopts = &hmo_devopts,
- .num_devopts = ARRAY_SIZE(hmo_devopts),
+ .devopts = &devopts,
+ .num_devopts = ARRAY_SIZE(devopts),
+
+ .devopts_cg_analog = &devopts_cg_analog,
+ .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog),
+
+ .coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
- .analog_devopts = &hmo_analog_devopts,
- .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+ .trigger_sources = &compact4_dig16_trigger_sources,
+ .num_trigger_sources = ARRAY_SIZE(compact4_dig16_trigger_sources),
- .coupling_options = &hmo_coupling_options,
- .trigger_sources = &hmo_compact4_dig16_trigger_sources,
.trigger_slopes = &scope_trigger_slopes,
+ .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes),
- .timebases = &hmo_timebases,
- .num_timebases = ARRAY_SIZE(hmo_timebases),
+ .timebases = &timebases,
+ .num_timebases = ARRAY_SIZE(timebases),
- .vdivs = &hmo_vdivs,
- .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+ .vdivs = &vdivs,
+ .num_vdivs = ARRAY_SIZE(vdivs),
.num_xdivs = 12,
.num_ydivs = 8,
}
static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi,
- const char *command, const char *(*array)[], int *result)
+ const char *command, const char *(*array)[], unsigned int n, int *result)
{
char *tmp;
- unsigned int i;
+ int idx;
if (sr_scpi_get_string(scpi, command, &tmp) != SR_OK) {
g_free(tmp);
return SR_ERR;
}
- for (i = 0; (*array)[i]; i++) {
- if (!g_strcmp0(tmp, (*array)[i])) {
- *result = i;
- g_free(tmp);
- tmp = NULL;
- break;
- }
- }
-
- if (tmp) {
+ if ((idx = std_str_idx_s(tmp, *array, n)) < 0) {
g_free(tmp);
- return SR_ERR;
+ return SR_ERR_ARG;
}
+ *result = idx;
+
+ g_free(tmp);
+
return SR_OK;
}
return SR_ERR;
}
-static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
+static struct sr_channel *get_channel_by_index_and_type(GSList *channel_lhead,
+ int index, int type)
+{
+ while (channel_lhead) {
+ struct sr_channel *ch = channel_lhead->data;
+ if (ch->index == index && ch->type == type)
+ return ch;
+
+ channel_lhead = channel_lhead->next;
+ }
+
+ return 0;
+}
+
+static int analog_channel_state_get(struct sr_dev_inst *sdi,
const struct scope_config *config,
struct scope_state *state)
{
unsigned int i, j;
char command[MAX_COMMAND_SIZE];
char *tmp_str;
+ struct sr_channel *ch;
+ struct sr_scpi_dev_inst *scpi = sdi->conn;
for (i = 0; i < config->analog_channels; i++) {
g_snprintf(command, sizeof(command),
&state->analog_channels[i].state) != SR_OK)
return SR_ERR;
+ ch = get_channel_by_index_and_type(sdi->channels, i, SR_CHANNEL_ANALOG);
+ if (ch)
+ ch->enabled = state->analog_channels[i].state;
+
g_snprintf(command, sizeof(command),
(*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_DIV],
i + 1);
if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK)
return SR_ERR;
- if (array_float_get(tmp_str, hmo_vdivs, ARRAY_SIZE(hmo_vdivs),
- &j) != SR_OK) {
+ if (array_float_get(tmp_str, ARRAY_AND_SIZE(vdivs), &j) != SR_OK) {
g_free(tmp_str);
sr_err("Could not determine array index for vertical div scale.");
return SR_ERR;
i + 1);
if (scope_state_get_array_option(scpi, command, config->coupling_options,
+ config->num_coupling_options,
&state->analog_channels[i].coupling) != SR_OK)
return SR_ERR;
return SR_OK;
}
-static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi,
+static int digital_channel_state_get(struct sr_dev_inst *sdi,
const struct scope_config *config,
struct scope_state *state)
{
unsigned int i;
char command[MAX_COMMAND_SIZE];
+ struct sr_channel *ch;
+ struct sr_scpi_dev_inst *scpi = sdi->conn;
for (i = 0; i < config->digital_channels; i++) {
g_snprintf(command, sizeof(command),
if (sr_scpi_get_bool(scpi, command,
&state->digital_channels[i]) != SR_OK)
return SR_ERR;
+
+ ch = get_channel_by_index_and_type(sdi->channels, i, SR_CHANNEL_LOGIC);
+ if (ch)
+ ch->enabled = state->digital_channels[i];
}
for (i = 0; i < config->digital_pods; i++) {
struct dev_context *devc;
struct scope_state *state;
const struct scope_config *config;
-
int tmp;
unsigned int i;
float tmp_float;
channel_found = FALSE;
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);
+ if (!state->analog_channels[i].state)
+ continue;
+ g_snprintf(chan_name, sizeof(chan_name), "CHAN%d", i + 1);
+ g_snprintf(tmp_str, sizeof(tmp_str),
+ (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE],
+ chan_name);
+ channel_found = TRUE;
+ break;
+ }
+
+ if (!channel_found) {
+ for (i = 0; i < config->digital_pods; i++) {
+ if (!state->digital_pods[i])
+ continue;
+ g_snprintf(chan_name, sizeof(chan_name), "POD%d", i);
g_snprintf(tmp_str, sizeof(tmp_str),
(*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE],
chan_name);
}
}
- if (!channel_found) {
- for (i = 0; i < config->digital_pods; i++) {
- if (state->digital_pods[i]) {
- g_snprintf(chan_name, sizeof(chan_name), "POD%d", i);
- g_snprintf(tmp_str, sizeof(tmp_str),
- (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE],
- chan_name);
- channel_found = TRUE;
- break;
- }
- }
- }
-
/* No channel is active, ask the instrument for the sample rate
* in single shot mode */
if (!channel_found) {
sr_info("Fetching scope state");
- if (analog_channel_state_get(sdi->conn, config, state) != SR_OK)
+ if (analog_channel_state_get(sdi, config, state) != SR_OK)
return SR_ERR;
- if (digital_channel_state_get(sdi->conn, config, state) != SR_OK)
+ if (digital_channel_state_get(sdi, config, state) != SR_OK)
return SR_ERR;
if (sr_scpi_get_float(sdi->conn,
&tmp_str) != SR_OK)
return SR_ERR;
- if (array_float_get(tmp_str, hmo_timebases, ARRAY_SIZE(hmo_timebases),
- &i) != SR_OK) {
+ if (array_float_get(tmp_str, ARRAY_AND_SIZE(timebases), &i) != SR_OK) {
g_free(tmp_str);
sr_err("Could not determine array index for time base.");
return SR_ERR;
if (scope_state_get_array_option(sdi->conn,
(*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SOURCE],
- config->trigger_sources, &state->trigger_source) != SR_OK)
+ config->trigger_sources, config->num_trigger_sources,
+ &state->trigger_source) != SR_OK)
return SR_ERR;
if (scope_state_get_array_option(sdi->conn,
- (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE],
- config->trigger_slopes, &state->trigger_slope) != SR_OK)
+ (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE],
+ config->trigger_slopes, config->num_trigger_slopes,
+ &state->trigger_slope) != SR_OK)
return SR_ERR;
if (hmo_update_sample_rate(sdi) != SR_OK)
SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
{
- char tmp[25];
int model_index;
unsigned int i, j, group;
struct sr_channel *ch;
struct dev_context *devc;
+ int ret;
devc = sdi->priv;
model_index = -1;
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].digital_pods);
+ if (!devc->analog_groups || !devc->digital_groups) {
+ g_free(devc->analog_groups);
+ g_free(devc->digital_groups);
+ return SR_ERR_MALLOC;
+ }
/* Add analog channels. */
for (i = 0; i < scope_models[model_index].analog_channels; i++) {
}
/* Add digital channel groups. */
+ ret = SR_OK;
for (i = 0; i < scope_models[model_index].digital_pods; i++) {
- g_snprintf(tmp, 25, "POD%d", i);
-
devc->digital_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
-
- devc->digital_groups[i]->name = g_strdup(tmp);
+ if (!devc->digital_groups[i]) {
+ ret = SR_ERR_MALLOC;
+ break;
+ }
+ devc->digital_groups[i]->name = g_strdup_printf("POD%d", i);
sdi->channel_groups = g_slist_append(sdi->channel_groups,
devc->digital_groups[i]);
}
+ if (ret != SR_OK)
+ return ret;
/* Add digital channels. */
for (i = 0; i < scope_models[model_index].digital_channels; i++) {
* the first enabled channel.
*/
if (++devc->num_frames == devc->frame_limit) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
hmo_cleanup_logic_data(devc);
} else {
devc->current_channel = devc->enabled_channels;
const uint32_t (*devopts)[];
const uint8_t num_devopts;
- const uint32_t (*analog_devopts)[];
- const uint8_t num_analog_devopts;
+ const uint32_t (*devopts_cg_analog)[];
+ const uint8_t num_devopts_cg_analog;
const char *(*coupling_options)[];
const uint8_t num_coupling_options;
const uint8_t num_trigger_sources;
const char *(*trigger_slopes)[];
+ const uint8_t num_trigger_slopes;
const uint64_t (*timebases)[][2];
const uint8_t num_timebases;
uint64_t sample_rate;
};
-/** Private, per-device-instance driver context. */
struct dev_context {
const void *model_config;
void *model_state;
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Andreas Zschunke <andreas.zschunke@gmx.net>
+ * Copyright (C) 2017 Andrej Valek <andy@skyrain.eu>
+ * Copyright (C) 2017 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+
+#define USB_INTERFACE 0
+#define NUM_CHANNELS 32
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+ SR_CONF_CONN | SR_CONF_GET,
+ SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_EXTERNAL_CLOCK_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint32_t devopts_fpga_zero[] = {
+ SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+ SR_CONF_CONN | SR_CONF_GET,
+};
+
+static const uint32_t devopts_cg[] = {
+ SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const char *cg_names[] = {
+ "A", "B",
+};
+
+static const char *signal_edges[] = {
+ [H4032L_CLOCK_EDGE_TYPE_RISE] = "rising",
+ [H4032L_CLOCK_EDGE_TYPE_FALL] = "falling",
+ [H4032L_CLOCK_EDGE_TYPE_BOTH] = "both",
+};
+
+static const char *ext_clock_sources[] = {
+ [H4032L_EXT_CLOCK_SOURCE_CHANNEL_A] = "ACLK",
+ [H4032L_EXT_CLOCK_SOURCE_CHANNEL_B] = "BCLK"
+};
+
+static const uint8_t ext_clock_edges[2][3] = {
+ {
+ H4032L_CLOCK_EDGE_TYPE_RISE_A,
+ H4032L_CLOCK_EDGE_TYPE_FALL_A,
+ H4032L_CLOCK_EDGE_TYPE_BOTH_A
+ },
+ {
+ H4032L_CLOCK_EDGE_TYPE_RISE_B,
+ H4032L_CLOCK_EDGE_TYPE_FALL_B,
+ H4032L_CLOCK_EDGE_TYPE_BOTH_B
+ }
+};
+
+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_KHZ(1),
+ SR_KHZ(2),
+ SR_KHZ(4),
+ SR_KHZ(8),
+ SR_KHZ(16),
+ SR_HZ(31250),
+ SR_HZ(62500),
+ SR_KHZ(125),
+ SR_KHZ(250),
+ SR_KHZ(500),
+ SR_KHZ(625),
+ SR_HZ(781250),
+ SR_MHZ(1),
+ SR_KHZ(1250),
+ SR_HZ(1562500),
+ SR_MHZ(2),
+ SR_KHZ(2500),
+ SR_KHZ(3125),
+ SR_MHZ(4),
+ SR_MHZ(5),
+ SR_KHZ(6250),
+ SR_MHZ(10),
+ SR_KHZ(12500),
+ SR_MHZ(20),
+ SR_MHZ(25),
+ SR_MHZ(40),
+ SR_MHZ(50),
+ SR_MHZ(80),
+ SR_MHZ(100),
+ SR_MHZ(160),
+ SR_MHZ(200),
+ SR_MHZ(320),
+ SR_MHZ(400),
+};
+
+static const uint64_t samplerates_hw[] = {
+ SR_MHZ(100),
+ SR_MHZ(50),
+ SR_MHZ(25),
+ SR_KHZ(12500),
+ SR_KHZ(6250),
+ SR_KHZ(3125),
+ SR_HZ(1562500),
+ SR_HZ(781250),
+ SR_MHZ(80),
+ SR_MHZ(40),
+ SR_MHZ(20),
+ SR_MHZ(10),
+ SR_MHZ(5),
+ SR_KHZ(2500),
+ SR_KHZ(1250),
+ SR_KHZ(625),
+ SR_MHZ(4),
+ SR_MHZ(2),
+ SR_MHZ(1),
+ SR_KHZ(500),
+ SR_KHZ(250),
+ SR_KHZ(125),
+ SR_HZ(62500),
+ SR_HZ(31250),
+ SR_KHZ(16),
+ SR_KHZ(8),
+ SR_KHZ(4),
+ SR_KHZ(2),
+ SR_KHZ(1),
+ 0,
+ 0,
+ 0,
+ SR_MHZ(200),
+ SR_MHZ(160),
+ SR_MHZ(400),
+ SR_MHZ(320),
+};
+
+SR_PRIV struct sr_dev_driver hantek_4032l_driver_info;
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ struct drv_context *drvc = di->context;
+ GSList *l, *devices, *conn_devices;
+ libusb_device **devlist;
+ struct libusb_device_descriptor des;
+ const char *conn;
+ int i;
+ char connection_id[64];
+ struct sr_channel_group *cg;
+ struct sr_dev_inst *sdi;
+ struct sr_channel *ch;
+
+ devices = NULL;
+ conn_devices = NULL;
+ drvc->instances = NULL;
+ conn = NULL;
+
+ for (l = options; l; l = l->next) {
+ struct sr_config *src = l->data;
+ if (src->key == 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;
+
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (i = 0; devlist[i]; i++) {
+ if (conn) {
+ struct sr_usb_dev_inst *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 (des.idVendor != H4032L_USB_VENDOR ||
+ des.idProduct != H4032L_USB_PRODUCT)
+ continue;
+
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->driver = &hantek_4032l_driver_info;
+ sdi->vendor = g_strdup("Hantek");
+ sdi->model = g_strdup("4032L");
+ sdi->connection_id = g_strdup(connection_id);
+
+ struct sr_channel_group *channel_groups[2];
+ for (int j = 0; j < 2; j++) {
+ cg = g_malloc0(sizeof(struct sr_channel_group));
+ cg->name = g_strdup_printf("%c", 'A' + j);
+ channel_groups[j] = cg;
+ sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+ }
+
+ /* Assemble channel list and add channel to channel groups. */
+ for (int j = 0; j < NUM_CHANNELS; j++) {
+ char channel_name[4];
+ sprintf(channel_name, "%c%d", 'A' + (j & 1), j / 2);
+ ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE, channel_name);
+ cg = channel_groups[j & 1];
+ cg->channels = g_slist_append(cg->channels, ch);
+ }
+
+ struct dev_context *devc = g_malloc0(sizeof(struct dev_context));
+
+ /* Initialize command packet. */
+ devc->cmd_pkt.magic = H4032L_CMD_PKT_MAGIC;
+ devc->cmd_pkt.sample_size = 16 * 1024;
+ devc->sample_rate = 0;
+
+ devc->status = H4032L_STATUS_IDLE;
+
+ devc->capture_ratio = 5;
+ devc->external_clock = FALSE;
+ devc->clock_edge = H4032L_CLOCK_EDGE_TYPE_RISE;
+
+ /* Create array of thresholds from min to max. */
+ GVariant *thresholds = std_gvar_min_max_step_thresholds(
+ H4032L_THR_VOLTAGE_MIN, H4032L_THR_VOLTAGE_MAX,
+ H4032L_THR_VOLTAGE_STEP);
+ /* Take default threshold value from array (FP workaround). */
+ g_variant_get_child(thresholds, H4032L_THR_VOLTAGE_DEFAULT,
+ "(dd)", &devc->cur_threshold[0], &devc->cur_threshold[1]);
+
+ sdi->priv = devc;
+ devices = g_slist_append(devices, sdi);
+
+ sdi->status = SR_ST_INACTIVE;
+ 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);
+ }
+
+ g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+ libusb_free_device_list(devlist, 1);
+
+ return std_scan_complete(di, devices);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ int ret;
+
+ ret = h4032l_dev_open(sdi);
+ if (ret != SR_OK) {
+ sr_err("Unable to open device.");
+ return SR_ERR;
+ }
+
+ ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+ if (ret != 0) {
+ switch (ret) {
+ case LIBUSB_ERROR_BUSY:
+ sr_err("Unable to claim USB interface. Another "
+ "program or driver has already claimed it.");
+ break;
+ case LIBUSB_ERROR_NO_DEVICE:
+ sr_err("Device has been disconnected.");
+ break;
+ default:
+ sr_err("Unable to claim interface: %s.",
+ libusb_error_name(ret));
+ break;
+ }
+
+ return SR_ERR;
+ }
+
+ /* Get FPGA version. */
+ if ((ret = h4032l_get_fpga_version(sdi)) != SR_OK)
+ return ret;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+
+ usb = sdi->conn;
+
+ if (!usb->devhdl)
+ return SR_ERR_BUG;
+
+ sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
+ usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+
+ return SR_OK;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sr_usb_dev_inst *usb;
+ int idx;
+
+ switch (key) {
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if ((idx = std_str_idx_s(cg->name, ARRAY_AND_SIZE(cg_names))) < 0)
+ return SR_ERR_CHANNEL_GROUP;
+ *data = std_gvar_tuple_double(
+ devc->cur_threshold[idx], devc->cur_threshold[idx]);
+ break;
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(samplerates_hw[devc->sample_rate]);
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ *data = g_variant_new_uint64(devc->capture_ratio);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->cmd_pkt.sample_size);
+ break;
+ case SR_CONF_EXTERNAL_CLOCK:
+ *data = g_variant_new_boolean(devc->external_clock);
+ break;
+ case SR_CONF_EXTERNAL_CLOCK_SOURCE:
+ *data = g_variant_new_string(ext_clock_sources[devc->external_clock_source]);
+ break;
+ case SR_CONF_CONN:
+ if (!sdi || !(usb = sdi->conn))
+ return SR_ERR_ARG;
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
+ break;
+ case SR_CONF_CLOCK_EDGE:
+ *data = g_variant_new_string(signal_edges[devc->clock_edge]);
+ 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)
+{
+ int idx;
+ struct dev_context *devc = sdi->priv;
+ struct h4032l_cmd_pkt *cmd_pkt = &devc->cmd_pkt;
+ uint64_t sample_rate, num_samples;
+ double low, high;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ idx = 0;
+ sample_rate = g_variant_get_uint64(data);
+ while (idx < (int)ARRAY_SIZE(samplerates_hw) && samplerates_hw[idx] != sample_rate)
+ idx++;
+ if (idx == ARRAY_SIZE(samplerates_hw) || sample_rate == 0) {
+ sr_err("Invalid sample rate.");
+ return SR_ERR_SAMPLERATE;
+ }
+ devc->sample_rate = idx;
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ devc->capture_ratio = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ num_samples = g_variant_get_uint64(data);
+ num_samples += 511;
+ num_samples &= 0xfffffe00;
+ if (num_samples < H4043L_NUM_SAMPLES_MIN ||
+ num_samples > H4032L_NUM_SAMPLES_MAX) {
+ sr_err("Invalid sample range 2k...64M: %"
+ PRIu64 ".", num_samples);
+ return SR_ERR;
+ }
+ cmd_pkt->sample_size = num_samples;
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if ((idx = std_str_idx_s(cg->name, ARRAY_AND_SIZE(cg_names))) < 0)
+ return SR_ERR_CHANNEL_GROUP;
+ g_variant_get(data, "(dd)", &low, &high);
+ devc->cur_threshold[idx] = (low + high) / 2.0;
+ break;
+ case SR_CONF_EXTERNAL_CLOCK:
+ devc->external_clock = g_variant_get_boolean(data);
+ break;
+ case SR_CONF_EXTERNAL_CLOCK_SOURCE:
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(ext_clock_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->external_clock_source = idx;
+ break;
+ case SR_CONF_CLOCK_EDGE:
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edges))) < 0)
+ return SR_ERR_ARG;
+ devc->clock_edge = idx;
+ 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 = (sdi) ? sdi->priv : NULL;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ if (cg) {
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
+ break;
+ }
+ /* Disable external clock and edges for FPGA version 0. */
+ if (devc && (!devc->fpga_version))
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts_fpga_zero);
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_SAMPLERATE:
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
+ break;
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ *data = std_gvar_min_max_step_thresholds(H4032L_THR_VOLTAGE_MIN,
+ H4032L_THR_VOLTAGE_MAX, H4032L_THR_VOLTAGE_STEP);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = std_gvar_tuple_u64(H4043L_NUM_SAMPLES_MIN, H4032L_NUM_SAMPLES_MAX);
+ break;
+ case SR_CONF_CLOCK_EDGE:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(signal_edges));
+ break;
+ case SR_CONF_EXTERNAL_CLOCK_SOURCE:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(ext_clock_sources));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ struct sr_dev_driver *di = sdi->driver;
+ struct drv_context *drvc = di->context;
+ struct dev_context *devc = sdi->priv;
+ struct sr_trigger *trigger = sr_session_trigger_get(sdi->session);
+ struct h4032l_cmd_pkt *cmd_pkt = &devc->cmd_pkt;
+
+ /* Initialize variables. */
+ devc->acq_aborted = FALSE;
+ devc->submitted_transfers = 0;
+ devc->sent_samples = 0;
+
+ /* Calculate packet ratio. */
+ cmd_pkt->pre_trigger_size = (cmd_pkt->sample_size * devc->capture_ratio) / 100;
+ devc->trigger_pos = cmd_pkt->pre_trigger_size;
+
+ /* Set clock edge, when external clock is enabled. */
+ if (devc->external_clock)
+ cmd_pkt->sample_rate = ext_clock_edges[devc->external_clock_source][devc->clock_edge];
+ else
+ cmd_pkt->sample_rate = devc->sample_rate;
+
+ /* Set pwm channel values. */
+ devc->cmd_pkt.pwm_a = h4032l_voltage2pwm(devc->cur_threshold[0]);
+ devc->cmd_pkt.pwm_b = h4032l_voltage2pwm(devc->cur_threshold[1]);
+
+ cmd_pkt->trig_flags.enable_trigger1 = 0;
+ cmd_pkt->trig_flags.enable_trigger2 = 0;
+ cmd_pkt->trig_flags.trigger_and_logic = 0;
+
+ if (trigger && trigger->stages) {
+ GSList *stages = trigger->stages;
+ struct sr_trigger_stage *stage1 = stages->data;
+ if (stages->next) {
+ sr_err("Only one trigger stage supported for now.");
+ return SR_ERR;
+ }
+ cmd_pkt->trig_flags.enable_trigger1 = 1;
+ cmd_pkt->trigger[0].flags.edge_type = H4032L_TRIGGER_EDGE_TYPE_DISABLED;
+ cmd_pkt->trigger[0].flags.data_range_enabled = 0;
+ cmd_pkt->trigger[0].flags.time_range_enabled = 0;
+ cmd_pkt->trigger[0].flags.combined_enabled = 0;
+ cmd_pkt->trigger[0].flags.data_range_type = H4032L_TRIGGER_DATA_RANGE_TYPE_MAX;
+ cmd_pkt->trigger[0].data_range_mask = 0;
+ cmd_pkt->trigger[0].data_range_max = 0;
+
+ /* Initialize range mask values. */
+ uint32_t range_mask = 0;
+ uint32_t range_value = 0;
+
+ GSList *channel = stage1->matches;
+ while (channel) {
+ struct sr_trigger_match *match = channel->data;
+
+ switch (match->match) {
+ case SR_TRIGGER_ZERO:
+ range_mask |= (1 << match->channel->index);
+ break;
+ case SR_TRIGGER_ONE:
+ range_mask |= (1 << match->channel->index);
+ range_value |= (1 << match->channel->index);
+ break;
+ case SR_TRIGGER_RISING:
+ if (cmd_pkt->trigger[0].flags.edge_type != H4032L_TRIGGER_EDGE_TYPE_DISABLED) {
+ sr_err("Only one trigger signal with fall/rising/edge allowed.");
+ return SR_ERR;
+ }
+ cmd_pkt->trigger[0].flags.edge_type = H4032L_TRIGGER_EDGE_TYPE_RISE;
+ cmd_pkt->trigger[0].flags.edge_signal = match->channel->index;
+ break;
+ case SR_TRIGGER_FALLING:
+ if (cmd_pkt->trigger[0].flags.edge_type != H4032L_TRIGGER_EDGE_TYPE_DISABLED) {
+ sr_err("Only one trigger signal with fall/rising/edge allowed.");
+ return SR_ERR;
+ }
+ cmd_pkt->trigger[0].flags.edge_type = H4032L_TRIGGER_EDGE_TYPE_FALL;
+ cmd_pkt->trigger[0].flags.edge_signal = match->channel->index;
+ break;
+ case SR_TRIGGER_EDGE:
+ if (cmd_pkt->trigger[0].flags.edge_type != H4032L_TRIGGER_EDGE_TYPE_DISABLED) {
+ sr_err("Only one trigger signal with fall/rising/edge allowed.");
+ return SR_ERR;
+ }
+ cmd_pkt->trigger[0].flags.edge_type = H4032L_TRIGGER_EDGE_TYPE_TOGGLE;
+ cmd_pkt->trigger[0].flags.edge_signal = match->channel->index;
+ break;
+ default:
+ sr_err("Unknown trigger value.");
+ return SR_ERR;
+ }
+
+ channel = channel->next;
+ }
+
+ /* Compress range mask value and apply range settings. */
+ if (range_mask) {
+ cmd_pkt->trigger[0].flags.data_range_enabled = 1;
+ cmd_pkt->trigger[0].data_range_mask |= (range_mask);
+
+ uint32_t new_range_value = 0;
+ uint32_t bit_mask = 1;
+ while (range_mask) {
+ if ((range_mask & 1) != 0) {
+ new_range_value <<= 1;
+ if ((range_value & 1) != 0)
+ new_range_value |= bit_mask;
+ bit_mask <<= 1;
+ }
+ range_mask >>= 1;
+ range_value >>= 1;
+ }
+ cmd_pkt->trigger[0].data_range_max |= range_value;
+ }
+ }
+
+ usb_source_add(sdi->session, drvc->sr_ctx, 1000,
+ h4032l_receive_data, sdi->driver->context);
+
+ /* Start capturing. */
+ return h4032l_start(sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ /* Stop capturing. */
+ return h4032l_stop(sdi);
+}
+
+SR_PRIV struct sr_dev_driver hantek_4032l_driver_info = {
+ .name = "hantek-4032l",
+ .longname = "Hantek 4032L",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(hantek_4032l_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Andreas Zschunke <andreas.zschunke@gmx.net>
+ * Copyright (C) 2017 Andrej Valek <andy@skyrain.eu>
+ * Copyright (C) 2017 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "protocol.h"
+
+#define H4032L_USB_TIMEOUT 500
+
+enum h4032l_cmd {
+ CMD_RESET = 0x00b3, /* Also arms the logic analyzer. */
+ CMD_CONFIGURE = 0x2b1a,
+ CMD_STATUS = 0x4b3a,
+ CMD_GET = 0x6b5a
+};
+
+struct h4032l_status_packet {
+ uint32_t magic;
+ uint32_t values;
+ uint32_t status;
+ uint32_t usbxi_data;
+ uint32_t fpga_version;
+};
+
+static void abort_acquisition(struct dev_context *devc)
+{
+ int i;
+
+ devc->acq_aborted = TRUE;
+
+ for (i = devc->num_transfers - 1; i >= 0; i--) {
+ if (devc->transfers[i])
+ libusb_cancel_transfer(devc->transfers[i]);
+ }
+
+ devc->status = H4032L_STATUS_IDLE;
+}
+
+static void finish_acquisition(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct drv_context *drvc = sdi->driver->context;
+
+ std_session_send_df_end(sdi);
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ devc->num_transfers = 0;
+ g_free(devc->transfers);
+}
+
+static void free_transfer(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *sdi = transfer->user_data;
+ struct dev_context *devc = sdi->priv;
+ unsigned int i;
+
+ if ((transfer->buffer != (unsigned char *)&devc->cmd_pkt) &&
+ (transfer->buffer != devc->buf)) {
+ g_free(transfer->buffer);
+ }
+
+ transfer->buffer = NULL;
+ libusb_free_transfer(transfer);
+
+ for (i = 0; i < devc->num_transfers; i++) {
+ if (devc->transfers[i] == transfer) {
+ devc->transfers[i] = NULL;
+ break;
+ }
+ }
+
+ if (--devc->submitted_transfers == 0)
+ finish_acquisition(sdi);
+}
+
+static void resubmit_transfer(struct libusb_transfer *transfer)
+{
+ int ret;
+
+ if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS)
+ return;
+
+ sr_err("%s: %s", __func__, libusb_error_name(ret));
+ free_transfer(transfer);
+}
+
+static void send_data(struct sr_dev_inst *sdi,
+ uint32_t *data, size_t sample_count)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sr_datafeed_logic logic = {
+ .length = sample_count * sizeof(uint32_t),
+ .unitsize = sizeof(uint32_t),
+ .data = data
+ };
+ const struct sr_datafeed_packet packet = {
+ .type = SR_DF_LOGIC,
+ .payload = &logic
+ };
+ const struct sr_datafeed_packet trig = {
+ .type = SR_DF_TRIGGER,
+ .payload = NULL
+ };
+ size_t trigger_offset;
+
+ if (devc->trigger_pos >= devc->sent_samples &&
+ devc->trigger_pos < (devc->sent_samples + sample_count)) {
+ /* Get trigger position. */
+ trigger_offset = devc->trigger_pos - devc->sent_samples;
+ logic.length = trigger_offset * sizeof(uint32_t);
+ if (logic.length)
+ sr_session_send(sdi, &packet);
+
+ /* Send trigger position. */
+ sr_session_send(sdi, &trig);
+
+ /* Send rest of data. */
+ logic.length = (sample_count-trigger_offset) * sizeof(uint32_t);
+ logic.data = data + trigger_offset;
+ if (logic.length)
+ sr_session_send(sdi, &packet);
+ } else {
+ sr_session_send(sdi, &packet);
+ }
+
+ devc->sent_samples += sample_count;
+}
+
+SR_PRIV int h4032l_receive_data(int fd, int revents, void *cb_data)
+{
+ struct timeval tv;
+ struct drv_context *drvc;
+
+ (void)fd;
+ (void)revents;
+
+ drvc = (struct drv_context *)cb_data;
+
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+ return TRUE;
+}
+
+void LIBUSB_CALL h4032l_data_transfer_callback(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *const sdi = transfer->user_data;
+ struct dev_context *const devc = sdi->priv;
+ uint32_t max_samples = transfer->actual_length / sizeof(uint32_t);
+ uint32_t *buf;
+ uint32_t num_samples;
+
+ /*
+ * If acquisition has already ended, just free any queued up
+ * transfer that come in.
+ */
+ if (devc->acq_aborted) {
+ free_transfer(transfer);
+ return;
+ }
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
+ sr_dbg("%s error: %d.", __func__, transfer->status);
+
+ /* Cancel pending transfers. */
+ if (transfer->actual_length == 0) {
+ resubmit_transfer(transfer);
+ return;
+ }
+
+ buf = (uint32_t *)transfer->buffer;
+
+ num_samples = MIN(devc->remaining_samples, max_samples);
+ devc->remaining_samples -= num_samples;
+ send_data(sdi, buf, num_samples);
+ sr_dbg("Remaining: %d %08X %08X.", devc->remaining_samples,
+ buf[0], buf[1]);
+
+ /* Close data receiving. */
+ if (devc->remaining_samples == 0) {
+ if (buf[num_samples] != H4032L_END_PACKET_MAGIC)
+ sr_err("Mismatch magic number of end poll.");
+
+ abort_acquisition(devc);
+ free_transfer(transfer);
+ } else {
+ if (((devc->submitted_transfers - 1) * H4032L_DATA_BUFFER_SIZE) <
+ (int32_t)(devc->remaining_samples * sizeof(uint32_t)))
+ resubmit_transfer(transfer);
+ else
+ free_transfer(transfer);
+ }
+}
+
+void LIBUSB_CALL h4032l_usb_callback(struct libusb_transfer *transfer)
+{
+ struct sr_dev_inst *const sdi = transfer->user_data;
+ struct dev_context *const devc = sdi->priv;
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ gboolean cmd = FALSE;
+ uint32_t max_samples = transfer->actual_length / sizeof(uint32_t);
+ uint32_t *buf;
+ struct h4032l_status_packet *status;
+ uint32_t num_samples;
+ int ret;
+
+ /*
+ * If acquisition has already ended, just free any queued up
+ * transfers that come in.
+ */
+ if (devc->acq_aborted) {
+ free_transfer(transfer);
+ return;
+ }
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
+ sr_dbg("%s error: %d.", __func__, transfer->status);
+
+ buf = (uint32_t *)transfer->buffer;
+
+ switch (devc->status) {
+ case H4032L_STATUS_IDLE:
+ sr_err("USB callback called in idle.");
+ break;
+ case H4032L_STATUS_CMD_CONFIGURE:
+ /* Select status request as next. */
+ cmd = TRUE;
+ devc->cmd_pkt.cmd = CMD_STATUS;
+ devc->status = H4032L_STATUS_CMD_STATUS;
+ break;
+ case H4032L_STATUS_CMD_STATUS:
+ /* Select status request as next. */
+ devc->status = H4032L_STATUS_RESPONSE_STATUS;
+ break;
+ case H4032L_STATUS_RESPONSE_STATUS:
+ /*
+ * Check magic and if status is complete, then select
+ * First Transfer as next.
+ */
+ status = (struct h4032l_status_packet *)transfer->buffer;
+ if (status->magic != H4032L_STATUS_PACKET_MAGIC)
+ devc->status = H4032L_STATUS_RESPONSE_STATUS;
+ else if (status->status == 2)
+ devc->status = H4032L_STATUS_RESPONSE_STATUS_CONTINUE;
+ else
+ devc->status = H4032L_STATUS_RESPONSE_STATUS_RETRY;
+ break;
+ case H4032L_STATUS_RESPONSE_STATUS_RETRY:
+ devc->status = H4032L_STATUS_CMD_STATUS;
+ devc->cmd_pkt.cmd = CMD_STATUS;
+ cmd = TRUE;
+ break;
+ case H4032L_STATUS_RESPONSE_STATUS_CONTINUE:
+ devc->status = H4032L_STATUS_CMD_GET;
+ devc->cmd_pkt.cmd = CMD_GET;
+ cmd = TRUE;
+ break;
+ case H4032L_STATUS_CMD_GET:
+ devc->status = H4032L_STATUS_FIRST_TRANSFER;
+ /* Trigger has been captured. */
+ std_session_send_df_header(sdi);
+ break;
+ case H4032L_STATUS_FIRST_TRANSFER:
+ /* Drop packets until H4032L_START_PACKET_MAGIC. */
+ if (buf[0] != H4032L_START_PACKET_MAGIC) {
+ sr_dbg("Mismatch magic number of start poll.");
+ break;
+ }
+ devc->status = H4032L_STATUS_TRANSFER;
+ max_samples--;
+ buf++;
+ /* Fallthrough. */
+ case H4032L_STATUS_TRANSFER:
+ num_samples = MIN(devc->remaining_samples, max_samples);
+ devc->remaining_samples -= num_samples;
+ send_data(sdi, buf, num_samples);
+ sr_dbg("Remaining: %d %08X %08X.", devc->remaining_samples,
+ buf[0], buf[1]);
+ break;
+ }
+
+ /* Start data receiving. */
+ if (devc->status == H4032L_STATUS_TRANSFER) {
+ if ((ret = h4032l_start_data_transfers(sdi)) != SR_OK) {
+ sr_err("Can not start data transfers: %d", ret);
+ devc->status = H4032L_STATUS_IDLE;
+ }
+ } else if (devc->status != H4032L_STATUS_IDLE) {
+ if (cmd) {
+ /* Setup new USB cmd packet, reuse transfer object. */
+ sr_dbg("New command: %d.", devc->status);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 2 | LIBUSB_ENDPOINT_OUT,
+ (unsigned char *)&devc->cmd_pkt,
+ sizeof(struct h4032l_cmd_pkt),
+ h4032l_usb_callback,
+ (void *)sdi, H4032L_USB_TIMEOUT);
+ } else {
+ /* Setup new USB poll packet, reuse transfer object. */
+ sr_dbg("Poll: %d.", devc->status);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 6 | LIBUSB_ENDPOINT_IN,
+ devc->buf, ARRAY_SIZE(devc->buf),
+ h4032l_usb_callback,
+ (void *)sdi, H4032L_USB_TIMEOUT);
+ }
+ /* Send prepared USB packet. */
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ devc->status = H4032L_STATUS_IDLE;
+ }
+ } else {
+ sr_dbg("Now idle.");
+ }
+
+ if (devc->status == H4032L_STATUS_IDLE)
+ free_transfer(transfer);
+}
+
+uint16_t h4032l_voltage2pwm(double voltage)
+{
+ /*
+ * word PwmA - channel A Vref PWM value, pseudocode:
+ * -6V < ThresholdVoltage < +6V
+ * Vref = 1.8 - ThresholdVoltage
+ * if Vref > 10.0
+ * Vref = 10.0
+ * if Vref < -5.0
+ * Vref = -5.0
+ * pwm = ToInt((Vref + 5.0) / 15.0 * 4096.0)
+ * if pwm > 4095
+ * pwm = 4095
+ */
+ voltage = 1.8 - voltage;
+ if (voltage > 10.0)
+ voltage = 10.0;
+ else if (voltage < -5.0)
+ voltage = -5.0;
+
+ return (uint16_t) ((voltage + 5.0) * (4096.0 / 15.0));
+}
+
+SR_PRIV int h4032l_start_data_transfers(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ struct libusb_transfer *transfer;
+ uint8_t *buf;
+ unsigned int num_transfers;
+ unsigned int i;
+ int ret;
+
+ devc->submitted_transfers = 0;
+
+ /*
+ * Set number of data transfers regarding to size of buffer.
+ * FPGA version 0 can't transfer multiple transfers at once.
+ */
+ if ((num_transfers = MIN(devc->remaining_samples * sizeof(uint32_t) /
+ H4032L_DATA_BUFFER_SIZE, devc->fpga_version ?
+ H4032L_DATA_TRANSFER_MAX_NUM : 1)) == 0)
+ num_transfers = 1;
+
+ g_free(devc->transfers);
+ devc->transfers = g_malloc(sizeof(*devc->transfers) * num_transfers);
+ devc->num_transfers = num_transfers;
+
+ for (i = 0; i < num_transfers; i++) {
+ buf = g_malloc(H4032L_DATA_BUFFER_SIZE);
+ transfer = libusb_alloc_transfer(0);
+
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 6 | LIBUSB_ENDPOINT_IN,
+ buf, H4032L_DATA_BUFFER_SIZE,
+ h4032l_data_transfer_callback,
+ (void *)sdi, H4032L_USB_TIMEOUT);
+
+ /* Send prepared usb packet. */
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ abort_acquisition(devc);
+ return SR_ERR;
+ }
+ devc->transfers[i] = transfer;
+ devc->submitted_transfers++;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int h4032l_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ struct libusb_transfer *transfer;
+ unsigned char buf[] = {0x0f, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int ret;
+
+ /* Send reset command to arm the logic analyzer. */
+ if ((ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, CMD_RESET,
+ 0x00, 0x00, buf, ARRAY_SIZE(buf), H4032L_USB_TIMEOUT)) < 0) {
+ sr_err("Failed to send vendor request %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /* Wait for reset vendor request. */
+ g_usleep(20 * 1000);
+
+ /* Send configure command. */
+ devc->cmd_pkt.cmd = CMD_CONFIGURE;
+ devc->status = H4032L_STATUS_CMD_CONFIGURE;
+ devc->remaining_samples = devc->cmd_pkt.sample_size;
+
+ transfer = libusb_alloc_transfer(0);
+
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 2 | LIBUSB_ENDPOINT_OUT, (unsigned char *)&devc->cmd_pkt,
+ sizeof(struct h4032l_cmd_pkt), h4032l_usb_callback,
+ (void *)sdi, H4032L_USB_TIMEOUT);
+
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ return SR_ERR;
+ }
+
+ devc->transfers = g_malloc0(sizeof(*devc->transfers));
+ devc->submitted_transfers++;
+ devc->num_transfers = 1;
+ devc->transfers[0] = transfer;
+
+ return SR_OK;
+}
+
+SR_PRIV int h4032l_stop(struct sr_dev_inst *sdi)
+{
+ abort_acquisition(sdi->priv);
+
+ return SR_OK;
+}
+
+SR_PRIV int h4032l_dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc = sdi->driver->context;
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ int ret = SR_ERR, i, device_count;
+ char connection_id[64];
+
+ device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ if (device_count < 0) {
+ sr_err("Failed to get device list: %s.",
+ libusb_error_name(device_count));
+ return SR_ERR;
+ }
+
+ for (i = 0; i < device_count; i++) {
+ libusb_get_device_descriptor(devlist[i], &des);
+
+ if (des.idVendor != H4032L_USB_VENDOR ||
+ des.idProduct != H4032L_USB_PRODUCT)
+ continue;
+
+ if ((sdi->status == SR_ST_INITIALIZING) ||
+ (sdi->status == SR_ST_INACTIVE)) {
+ /* Check device by its physical USB bus/port address. */
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
+ if (strcmp(sdi->connection_id, connection_id))
+ /* This is not the one. */
+ continue;
+ }
+
+ if (!(ret = libusb_open(devlist[i], &usb->devhdl))) {
+ if (usb->address == 0xff)
+ /*
+ * First time we touch this device after FW
+ * upload, so we don't know the address yet.
+ */
+ usb->address =
+ libusb_get_device_address(devlist[i]);
+ } else {
+ sr_err("Failed to open device: %s.",
+ libusb_error_name(ret));
+ ret = SR_ERR;
+ break;
+ }
+
+ ret = SR_OK;
+ break;
+ }
+
+ libusb_free_device_list(devlist, 1);
+ return ret;
+}
+
+SR_PRIV int h4032l_get_fpga_version(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ struct h4032l_status_packet *status;
+ int transferred;
+ int i, ret;
+
+ /* Set command to status. */
+ devc->cmd_pkt.magic = H4032L_CMD_PKT_MAGIC;
+ devc->cmd_pkt.cmd = CMD_STATUS;
+
+ /* Send status request. */
+ if ((ret = libusb_bulk_transfer(usb->devhdl,
+ 2 | LIBUSB_ENDPOINT_OUT, (unsigned char *)&devc->cmd_pkt,
+ sizeof(struct h4032l_cmd_pkt), &transferred, H4032L_USB_TIMEOUT)) < 0) {
+ sr_err("Unable to send FPGA version request: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /* Attempt to get FGPA version. */
+ for (i = 0; i < 10; i++) {
+ if ((ret = libusb_bulk_transfer(usb->devhdl,
+ 6 | LIBUSB_ENDPOINT_IN, devc->buf,
+ ARRAY_SIZE(devc->buf), &transferred, H4032L_USB_TIMEOUT)) < 0) {
+ sr_err("Unable to receive FPGA version: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ status = (struct h4032l_status_packet *)devc->buf;
+ if (status->magic == H4032L_STATUS_PACKET_MAGIC) {
+ sr_dbg("FPGA version: 0x%x.", status->fpga_version);
+ devc->fpga_version = status->fpga_version;
+ return SR_OK;
+ }
+ }
+
+ sr_err("Unable to get FPGA version.");
+
+ return SR_ERR;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Andreas Zschunke <andreas.zschunke@gmx.net>
+ * Copyright (C) 2017 Andrej Valek <andy@skyrain.eu>
+ * Copyright (C) 2017 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_HANTEK_4032L_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_HANTEK_4032L_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <string.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "hantek-4032l"
+
+#define H4032L_USB_VENDOR 0x04b5
+#define H4032L_USB_PRODUCT 0x4032
+
+#define H4032L_DATA_BUFFER_SIZE (2 * 1024)
+#define H4032L_DATA_TRANSFER_MAX_NUM 32
+
+#define H4043L_NUM_SAMPLES_MIN (2 * 1024)
+#define H4032L_NUM_SAMPLES_MAX (64 * 1024 * 1024)
+
+#define H4032L_THR_VOLTAGE_MIN -6.0
+#define H4032L_THR_VOLTAGE_MAX 6.0
+#define H4032L_THR_VOLTAGE_STEP 0.1
+/*
+ * Array index of the default voltage threshold value (2.5V):
+ * (|min| / step) + (default / step) = (|-6.0| / 0.1) + (2.5 / 0.1) = 85
+ */
+#define H4032L_THR_VOLTAGE_DEFAULT 85
+
+#define H4032L_CMD_PKT_MAGIC 0x017f
+#define H4032L_STATUS_PACKET_MAGIC 0x2B1A037F
+#define H4032L_START_PACKET_MAGIC 0x2B1A027F
+#define H4032L_END_PACKET_MAGIC 0x4D3C037F
+
+enum h4032l_clock_edge_type {
+ H4032L_CLOCK_EDGE_TYPE_RISE,
+ H4032L_CLOCK_EDGE_TYPE_FALL,
+ H4032L_CLOCK_EDGE_TYPE_BOTH
+};
+
+enum h4032l_ext_clock_source {
+ H4032L_EXT_CLOCK_SOURCE_CHANNEL_A,
+ H4032L_EXT_CLOCK_SOURCE_CHANNEL_B
+};
+
+enum h4032l_clock_edge_type_channel {
+ H4032L_CLOCK_EDGE_TYPE_RISE_A = 0x24,
+ H4032L_CLOCK_EDGE_TYPE_RISE_B,
+ H4032L_CLOCK_EDGE_TYPE_BOTH_A,
+ H4032L_CLOCK_EDGE_TYPE_BOTH_B,
+ H4032L_CLOCK_EDGE_TYPE_FALL_A,
+ H4032L_CLOCK_EDGE_TYPE_FALL_B
+};
+
+enum h4032l_trigger_edge_type {
+ H4032L_TRIGGER_EDGE_TYPE_RISE,
+ H4032L_TRIGGER_EDGE_TYPE_FALL,
+ H4032L_TRIGGER_EDGE_TYPE_TOGGLE,
+ H4032L_TRIGGER_EDGE_TYPE_DISABLED
+};
+
+enum h4032l_trigger_data_range_type {
+ H4032L_TRIGGER_DATA_RANGE_TYPE_MAX,
+ H4032L_TRIGGER_DATA_RANGE_TYPE_MIN_OR_MAX,
+ H4032L_TRIGGER_DATA_RANGE_TYPE_OUT_OF_RANGE,
+ H4032L_TRIGGER_DATA_RANGE_TYPE_WITHIN_RANGE
+};
+
+enum h4032l_trigger_time_range_type {
+ H4032L_TRIGGER_TIME_RANGE_TYPE_MAX,
+ H4032L_TRIGGER_TIME_RANGE_TYPE_MIN_OR_MAX,
+ H4032L_TRIGGER_TIME_RANGE_TYPE_OUT_OF_RANGE,
+ H4032L_TRIGGER_TIME_RANGE_TYPE_WITHIN_RANGE
+};
+
+enum h4032l_trigger_data_selection {
+ H4032L_TRIGGER_DATA_SELECTION_NEXT,
+ H4032L_TRIGGER_DATA_SELECTION_CURRENT,
+ H4032L_TRIGGER_DATA_SELECTION_PREV
+};
+
+enum h4032l_status {
+ H4032L_STATUS_IDLE,
+ H4032L_STATUS_CMD_CONFIGURE,
+ H4032L_STATUS_CMD_STATUS,
+ H4032L_STATUS_RESPONSE_STATUS,
+ H4032L_STATUS_RESPONSE_STATUS_RETRY,
+ H4032L_STATUS_RESPONSE_STATUS_CONTINUE,
+ H4032L_STATUS_CMD_GET,
+ H4032L_STATUS_FIRST_TRANSFER,
+ H4032L_STATUS_TRANSFER
+};
+
+#pragma pack(push,2)
+struct h4032l_trigger {
+ struct {
+ uint32_t edge_signal:5;
+ uint32_t edge_type:2;
+ uint32_t :1;
+ uint32_t data_range_type:2;
+ uint32_t time_range_type:2;
+ uint32_t data_range_enabled:1;
+ uint32_t time_range_enabled:1;
+ uint32_t :2;
+ uint32_t data_sel:2;
+ uint32_t combined_enabled:1;
+ } flags;
+ uint32_t data_range_min;
+ uint32_t data_range_max;
+ uint32_t time_range_min;
+ uint32_t time_range_max;
+ uint32_t data_range_mask;
+ uint32_t combine_mask;
+ uint32_t combine_data;
+};
+
+struct h4032l_cmd_pkt {
+ uint16_t magic; /* 0x017f */
+ uint8_t sample_rate;
+ struct {
+ uint8_t enable_trigger1:1;
+ uint8_t enable_trigger2:1;
+ uint8_t trigger_and_logic:1;
+ } trig_flags;
+ uint16_t pwm_a;
+ uint16_t pwm_b;
+ uint16_t reserved;
+ uint32_t sample_size; /* Sample depth in bits per channel, 2k-64M, must be multiple of 512. */
+ uint32_t pre_trigger_size; /* Pretrigger buffer depth in bits, must be < sample_size. */
+ struct h4032l_trigger trigger[2];
+ uint16_t cmd;
+};
+#pragma pack(pop)
+
+struct dev_context {
+ enum h4032l_status status;
+ uint64_t sample_rate;
+ unsigned int sent_samples;
+ int submitted_transfers;
+ uint32_t remaining_samples;
+ gboolean acq_aborted;
+ struct h4032l_cmd_pkt cmd_pkt;
+ unsigned int num_transfers;
+ struct libusb_transfer **transfers;
+ uint8_t buf[512];
+ uint64_t capture_ratio;
+ uint32_t trigger_pos;
+ gboolean external_clock;
+ enum h4032l_ext_clock_source external_clock_source;
+ enum h4032l_clock_edge_type clock_edge;
+ double cur_threshold[2];
+ uint32_t fpga_version;
+};
+
+SR_PRIV int h4032l_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV uint16_t h4032l_voltage2pwm(double voltage);
+SR_PRIV void LIBUSB_CALL h4032l_usb_callback(struct libusb_transfer *transfer);
+SR_PRIV void LIBUSB_CALL h4032l_data_transfer_callback(struct libusb_transfer *transfer);
+SR_PRIV int h4032l_start_data_transfers(const struct sr_dev_inst *sdi);
+SR_PRIV int h4032l_start(const struct sr_dev_inst *sdi);
+SR_PRIV int h4032l_stop(struct sr_dev_inst *sdi);
+SR_PRIV int h4032l_dev_open(struct sr_dev_inst *sdi);
+SR_PRIV int h4032l_get_fpga_version(const struct sr_dev_inst *sdi);
+
+#endif
static const struct hantek_6xxx_profile dev_profiles[] = {
{
+ /* Windows: "Hantek6022BE DRIVER 1": 04b4:6022 */
0x04b4, 0x6022, 0x1d50, 0x608e, 0x0001,
"Hantek", "6022BE", "fx2lafw-hantek-6022be.fw",
- dc_coupling, ARRAY_SIZE(dc_coupling), FALSE,
+ ARRAY_AND_SIZE(dc_coupling), FALSE,
+ },
+ {
+ /* Windows: "Hantek6022BE DRIVER 2": 04b5:6022 */
+ 0x04b5, 0x6022, 0x1d50, 0x608e, 0x0001,
+ "Hantek", "6022BE", "fx2lafw-hantek-6022be.fw",
+ ARRAY_AND_SIZE(dc_coupling), FALSE,
},
{
0x8102, 0x8102, 0x1d50, 0x608e, 0x0002,
"Sainsmart", "DDS120", "fx2lafw-sainsmart-dds120.fw",
- acdc_coupling, ARRAY_SIZE(acdc_coupling), TRUE,
+ ARRAY_AND_SIZE(acdc_coupling), TRUE,
},
{
+ /* Windows: "Hantek6022BL DRIVER 1": 04b4:602a */
0x04b4, 0x602a, 0x1d50, 0x608e, 0x0003,
"Hantek", "6022BL", "fx2lafw-hantek-6022bl.fw",
- dc_coupling, ARRAY_SIZE(dc_coupling), FALSE,
+ ARRAY_AND_SIZE(dc_coupling), FALSE,
+ },
+ {
+ /* Windows: "Hantek6022BL DRIVER 2": 04b5:602a */
+ 0x04b5, 0x602a, 0x1d50, 0x608e, 0x0003,
+ "Hantek", "6022BL", "fx2lafw-hantek-6022bl.fw",
+ ARRAY_AND_SIZE(dc_coupling), FALSE,
},
ALL_ZERO
};
static int read_channel(const struct sr_dev_inst *sdi, uint32_t amount);
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
-
static struct sr_dev_inst *hantek_6xxx_dev_new(const struct hantek_6xxx_profile *prof)
{
struct sr_dev_inst *sdi;
devc->coupling_tab_size = prof->coupling_tab_size;
devc->has_coupling = prof->has_coupling;
- devc->sample_buf = NULL;
- devc->sample_buf_write = 0;
- devc->sample_buf_size = 0;
-
devc->profile = prof;
devc->dev_state = IDLE;
devc->samplerate = DEFAULT_SAMPLERATE;
return SR_OK;
}
-static void clear_dev_context(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
g_slist_free(devc->enabled_channels);
- g_free(devc);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_dev_context);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static GSList *scan(struct sr_dev_driver *di, GSList *options)
libusb_get_device_descriptor(devlist[i], &des);
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
prof = NULL;
for (j = 0; dev_profiles[j].orig_vid; j++) {
devices = g_slist_append(devices, sdi);
devc = sdi->priv;
if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
- USB_CONFIGURATION, prof->firmware) == SR_OK)
+ 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.");
+ } else {
+ sr_err("Firmware upload failed, name %s.", prof->firmware);
+ }
/* Dummy USB address of 0xff will get overwritten later. */
sdi->conn = sr_usb_dev_inst_new(
libusb_get_bus_number(devlist[i]), 0xff, 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)
+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 uint64_t *vdiv;
int ch_idx;
/* 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);
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
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)
+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 p, q;
- int tmp_int, ch_idx, ret;
- unsigned int i;
- const char *tmp_str;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
+ int ch_idx, idx;
- ret = SR_OK;
devc = sdi->priv;
if (!cg) {
switch (key) {
devc->limit_samples = g_variant_get_uint64(data);
break;
default:
- ret = SR_ERR_NA;
- break;
+ return SR_ERR_NA;
}
} else {
if (sdi->channel_groups->data == cg)
return SR_ERR_ARG;
switch (key) {
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[ch_idx] = tmp_int;
- hantek_6xxx_update_vdiv(sdi);
- } else
- ret = SR_ERR_ARG;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+ return SR_ERR_ARG;
+ devc->voltage[ch_idx] = idx;
+ hantek_6xxx_update_vdiv(sdi);
break;
case SR_CONF_COUPLING:
- tmp_str = g_variant_get_string(data, NULL);
- for (i = 0; i < devc->coupling_tab_size; i++) {
- if (!strcmp(tmp_str, devc->coupling_vals[i])) {
- devc->coupling[ch_idx] = i;
- break;
- }
- }
- if (i == devc->coupling_tab_size)
- ret = SR_ERR_ARG;
+ if ((idx = std_str_idx(data, devc->coupling_vals,
+ devc->coupling_tab_size)) < 0)
+ return SR_ERR_ARG;
+ devc->coupling[ch_idx] = idx;
break;
default:
- ret = SR_ERR_NA;
- break;
+ return SR_ERR_NA;
}
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *tuple, *rational[2];
- GVariantBuilder gvb;
- unsigned int i;
- GVariant *gvar;
- struct dev_context *devc = NULL;
-
- 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;
- }
+ struct dev_context *devc;
- if (sdi)
- devc = sdi->priv;
+ devc = (sdi) ? sdi->priv : NULL;
if (!cg) {
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
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));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
break;
case SR_CONF_COUPLING:
if (!devc)
- return SR_ERR_NA;
+ return SR_ERR_ARG;
*data = g_variant_new_strv(devc->coupling_vals, devc->coupling_tab_size);
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);
+ *data = std_gvar_tuple_array(ARRAY_AND_SIZE(vdivs));
break;
default:
return SR_ERR_NA;
return;
}
- for (int ch = 0; ch < 2; ch++) {
+ for (int ch = 0; ch < NUM_CHANNELS; ch++) {
if (!devc->ch_enabled[ch])
continue;
g_free(analog.data);
}
-static void send_data(struct sr_dev_inst *sdi, struct libusb_transfer *buf[], uint64_t samples)
-{
- int i = 0;
- uint64_t send = 0;
- uint32_t chunk;
-
- while (send < samples) {
- chunk = MIN(samples - send, (uint64_t)(buf[i]->actual_length / NUM_CHANNELS));
- send += chunk;
- send_chunk(sdi, buf[i]->buffer, chunk);
-
- /*
- * Everything in this transfer was either copied to the buffer
- * or sent to the session bus.
- */
- g_free(buf[i]->buffer);
- libusb_free_transfer(buf[i]);
- i++;
- }
-}
-
/*
* Called by libusb (as triggered by handle_event()) when a transfer comes in.
* Only channel data comes in asynchronously, and all transfers for this are
if (devc->dev_state != CAPTURE)
return;
- if (!devc->sample_buf) {
- devc->sample_buf_size = 10;
- devc->sample_buf = g_try_malloc(devc->sample_buf_size * sizeof(transfer));
- devc->sample_buf_write = 0;
- }
-
- if (devc->sample_buf_write >= devc->sample_buf_size) {
- devc->sample_buf_size += 10;
- devc->sample_buf = g_try_realloc(devc->sample_buf,
- devc->sample_buf_size * sizeof(transfer));
- if (!devc->sample_buf) {
- sr_err("Sample buffer malloc failed.");
- devc->dev_state = STOPPING;
- return;
- }
- }
-
- devc->sample_buf[devc->sample_buf_write++] = transfer;
- devc->samp_received += transfer->actual_length / NUM_CHANNELS;
-
sr_spew("receive_transfer(): calculated samplerate == %" PRIu64 "ks/s",
(uint64_t)(transfer->actual_length * 1000 /
(g_get_monotonic_time() - devc->read_start_ts + 1) /
/* Nothing to send to the bus. */
return;
+ unsigned samples_received = transfer->actual_length / NUM_CHANNELS;
+ send_chunk(sdi, transfer->buffer, samples_received);
+ devc->samp_received += samples_received;
+
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+
if (devc->limit_samples && devc->samp_received >= devc->limit_samples) {
sr_info("Requested number of samples reached, stopping. %"
PRIu64 " <= %" PRIu64, devc->limit_samples,
devc->samp_received);
- send_data(sdi, devc->sample_buf, devc->limit_samples);
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
} else if (devc->limit_msec && (g_get_monotonic_time() -
devc->aq_started) / 1000 >= devc->limit_msec) {
sr_info("Requested time limit reached, stopping. %d <= %d",
(uint32_t)devc->limit_msec,
(uint32_t)(g_get_monotonic_time() - devc->aq_started) / 1000);
- send_data(sdi, devc->sample_buf, devc->samp_received);
- g_free(devc->sample_buf);
- devc->sample_buf = NULL;
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
} else {
read_channel(sdi, data_amount(sdi));
}
amount = MIN(amount, MAX_PACKET_SIZE);
ret = hantek_6xxx_get_channeldata(sdi, receive_transfer, amount);
devc->read_start_ts = g_get_monotonic_time();
- devc->read_data_amount = amount;
return ret;
}
struct sr_dev_driver *di = sdi->driver;
struct drv_context *drvc = di->context;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
if (configure_channels(sdi) != SR_OK) {
{
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
-
devc = sdi->priv;
devc->dev_state = STOPPING;
- g_free(devc->sample_buf);
- devc->sample_buf = NULL;
-
return SR_OK;
}
struct sr_usb_dev_inst *usb;
struct libusb_device_descriptor des;
libusb_device **devlist;
- int err, i;
+ int err = SR_ERR, i;
char connection_id[64];
devc = sdi->priv;
usb = sdi->conn;
- if (sdi->status == SR_ST_ACTIVE)
- /* Already in use. */
- return SR_ERR;
-
libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
for (i = 0; devlist[i]; i++) {
libusb_get_device_descriptor(devlist[i], &des);
/*
* Check device by its physical USB bus/port address.
*/
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
if (strcmp(sdi->connection_id, connection_id))
/* This is not the one. */
continue;
usb->address = libusb_get_device_address(devlist[i]);
}
- sdi->status = SR_ST_ACTIVE;
sr_info("Opened device on %d.%d (logical) / "
"%s (physical) interface %d.",
usb->bus, usb->address,
sdi->connection_id, USB_INTERFACE);
+
+ err = SR_OK;
} else {
sr_err("Failed to open device: %s.",
libusb_error_name(err));
+ err = SR_ERR;
}
/* If we made it here, we handled the device (somehow). */
libusb_free_device_list(devlist, 1);
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
-
- return SR_OK;
+ return err;
}
SR_PRIV void hantek_6xxx_close(struct sr_dev_inst *sdi)
#define FLUSH_PACKET_SIZE 1024
#define MIN_PACKET_SIZE 512
+#ifdef _WIN32
+#define MAX_PACKET_SIZE (2 * 1024 * 1024)
+#else
#define MAX_PACKET_SIZE (12 * 1024 * 1024)
+#endif
#define HANTEK_EP_IN 0x86
#define USB_INTERFACE 0
uint64_t aq_started;
uint64_t read_start_ts;
- uint32_t read_data_amount;
-
- struct libusb_transfer **sample_buf;
- uint32_t sample_buf_write;
- uint32_t sample_buf_size;
gboolean ch_enabled[NUM_CHANNELS];
int voltage[NUM_CHANNELS];
SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
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_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
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_BUFFERSIZE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_NUM_VDIV | SR_CONF_GET,
+ SR_CONF_TRIGGER_LEVEL | SR_CONF_GET | SR_CONF_SET,
};
static const uint32_t devopts_cg[] = {
{ 400, 1000 },
};
+static const uint64_t samplerates[] = {
+ SR_KHZ(20),
+ SR_KHZ(25),
+ SR_KHZ(50),
+ SR_KHZ(100),
+ SR_KHZ(200),
+ SR_KHZ(250),
+ 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(125),
+ /* Fast mode not supported yet.
+ SR_MHZ(200),
+ SR_MHZ(250), */
+};
+
static const uint64_t vdivs[][2] = {
/* millivolts */
{ 10, 1000 },
};
static const char *trigger_sources[] = {
- "CH1",
- "CH2",
- "EXT",
+ "CH1", "CH2", "EXT",
/* TODO: forced */
};
static const char *trigger_slopes[] = {
- "r",
- "f",
+ "r", "f",
};
static const char *coupling[] = {
- "AC",
- "DC",
- "GND",
+ "AC", "DC", "GND",
};
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
-
static struct sr_dev_inst *dso_dev_new(const struct dso_profile *prof)
{
struct sr_dev_inst *sdi;
devc->profile = prof;
devc->dev_state = IDLE;
devc->timebase = DEFAULT_TIMEBASE;
+ devc->samplerate = DEFAULT_SAMPLERATE;
devc->ch_enabled[0] = TRUE;
devc->ch_enabled[1] = TRUE;
devc->voltage[0] = DEFAULT_VOLTAGE;
devc->framesize = DEFAULT_FRAMESIZE;
devc->triggerslope = SLOPE_POSITIVE;
devc->triggersource = g_strdup(DEFAULT_TRIGGER_SOURCE);
- devc->triggerposition = DEFAULT_HORIZ_TRIGGERPOS;
+ devc->capture_ratio = DEFAULT_CAPTURE_RATIO;
sdi->priv = devc;
return sdi;
devc = sdi->priv;
g_slist_free(devc->enabled_channels);
+ devc->enabled_channels = NULL;
devc->ch_enabled[0] = devc->ch_enabled[1] = FALSE;
for (l = sdi->channels, p = 0; l; l = l->next, p++) {
ch = l->data;
return SR_OK;
}
-static void clear_dev_context(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
g_free(devc->triggersource);
g_slist_free(devc->enabled_channels);
-
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_dev_context);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static GSList *scan(struct sr_dev_driver *di, GSList *options)
libusb_get_device_descriptor(devlist[i], &des);
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
prof = NULL;
for (j = 0; dev_profiles[j].orig_vid; j++) {
devices = g_slist_append(devices, sdi);
devc = sdi->priv;
if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
- USB_CONFIGURATION, prof->firmware) == SR_OK)
+ 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");
+ } else {
+ sr_err("Firmware upload failed, name %s", prof->firmware);
+ }
/* Dummy USB address of 0xff will get overwritten later. */
sdi->conn = sr_usb_dev_inst_new(
libusb_get_bus_number(devlist[i]), 0xff, 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)
+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;
devc = sdi->priv;
if (!cg) {
switch (key) {
+ case SR_CONF_TRIGGER_LEVEL:
+ *data = g_variant_new_double(devc->voffset_trigger);
+ break;
case SR_CONF_CONN:
if (!sdi->conn)
return SR_ERR_ARG;
/* 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);
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
break;
case SR_CONF_TIMEBASE:
*data = g_variant_new("(tt)", timebases[devc->timebase][0],
timebases[devc->timebase][1]);
break;
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(devc->samplerate);
+ break;
case SR_CONF_BUFFERSIZE:
*data = g_variant_new_uint64(devc->framesize);
break;
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);
+ 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)
+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, ch_idx, ret;
- unsigned int i;
- const char *tmp_str;
-
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
+ int ch_idx, idx;
+ float flt;
- ret = SR_OK;
devc = sdi->priv;
if (!cg) {
switch (key) {
case SR_CONF_LIMIT_FRAMES:
devc->limit_frames = g_variant_get_uint64(data);
break;
+ case SR_CONF_TRIGGER_LEVEL:
+ flt = g_variant_get_double(data);
+ if (flt < 0.0 || flt > 1.0) {
+ sr_err("Trigger level must be in [0.0,1.0].");
+ return SR_ERR_ARG;
+ }
+ devc->voffset_trigger = flt;
+ if (dso_set_voffsets(sdi) != SR_OK)
+ return SR_ERR;
+ 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 ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
return SR_ERR_ARG;
- devc->triggerslope = (tmp_str[0] == 'r')
- ? SLOPE_POSITIVE : SLOPE_NEGATIVE;
+ devc->triggerslope = idx;
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;
+ case SR_CONF_CAPTURE_RATIO:
+ devc->capture_ratio = g_variant_get_uint64(data);
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 (i == NUM_BUFFER_SIZES)
- ret = SR_ERR_ARG;
+ if ((idx = std_u64_idx(data, devc->profile->buffersizes, NUM_BUFFER_SIZES)) < 0)
+ return SR_ERR_ARG;
+ devc->framesize = devc->profile->buffersizes[idx];
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 (tmp_int >= 0)
- devc->timebase = tmp_int;
- else
- ret = SR_ERR_ARG;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(timebases))) < 0)
+ return SR_ERR_ARG;
+ devc->timebase = idx;
+ break;
+ case SR_CONF_SAMPLERATE:
+ if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(samplerates))) < 0)
+ return SR_ERR_ARG;
+ devc->samplerate = samplerates[idx];
+ if (dso_set_trigger_samplerate(sdi) != SR_OK)
+ return SR_ERR;
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;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->triggersource = g_strdup(trigger_sources[idx]);
break;
default:
- ret = SR_ERR_NA;
- break;
+ return SR_ERR_NA;
}
} else {
if (sdi->channel_groups->data == cg)
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[ch_idx] = tmp_int;
- } else
- ret = SR_ERR_ARG;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+ return SR_ERR_ARG;
+ devc->voltage[ch_idx] = idx;
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;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(coupling))) < 0)
+ return SR_ERR_ARG;
+ devc->coupling[ch_idx] = idx;
break;
default:
- ret = SR_ERR_NA;
- break;
+ return SR_ERR_NA;
}
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
struct dev_context *devc;
- GVariant *tuple, *rational[2];
- GVariantBuilder gvb;
- unsigned int i;
-
- 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;
if (!cg) {
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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));
+ *data = std_gvar_array_u64(devc->profile->buffersizes, NUM_BUFFER_SIZES);
+ break;
+ case SR_CONF_SAMPLERATE:
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
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);
+ *data = std_gvar_tuple_array(ARRAY_AND_SIZE(timebases));
break;
case SR_CONF_TRIGGER_SOURCE:
- *data = g_variant_new_strv(trigger_sources,
- ARRAY_SIZE(trigger_sources));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_sources));
break;
case SR_CONF_TRIGGER_SLOPE:
- *data = g_variant_new_strv(trigger_slopes,
- ARRAY_SIZE(trigger_slopes));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_slopes));
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));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
break;
case SR_CONF_COUPLING:
- *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+ *data = g_variant_new_strv(ARRAY_AND_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);
+ *data = std_gvar_tuple_array(ARRAY_AND_SIZE(vdivs));
break;
default:
return SR_ERR_NA;
/* TODO: Check malloc return value. */
analog.data = g_try_malloc(num_samples * sizeof(float));
- for (int ch = 0; ch < 2; ch++) {
+ for (int ch = 0; ch < NUM_CHANNELS; ch++) {
if (!devc->ch_enabled[ch])
continue;
sr_dbg("End of frame, sending %d pre-trigger buffered samples.",
devc->samp_buffered);
send_chunk(sdi, devc->framebuf, devc->samp_buffered);
+ g_free(devc->framebuf);
+ devc->framebuf = NULL;
/* Mark the end of this frame. */
packet.type = SR_DF_FRAME_END;
sr_session_send(sdi, &packet);
- if (devc->limit_frames && ++devc->num_frames == devc->limit_frames) {
+ if (devc->limit_frames && ++devc->num_frames >= devc->limit_frames) {
/* Terminate session */
devc->dev_state = STOPPING;
} else {
/* No data yet. */
break;
case CAPTURE_READY_8BIT:
+ case CAPTURE_READY_2250:
/* Remember where in the captured frame the trigger is. */
devc->trigger_offset = trigger_offset;
struct sr_dev_driver *di = sdi->driver;
struct drv_context *drvc = di->context;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
if (configure_channels(sdi) != SR_OK) {
{
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR;
-
devc = sdi->priv;
devc->dev_state = STOPPING;
+ devc->num_frames = 0;
return SR_OK;
}
#include "libsigrok-internal.h"
#include "protocol.h"
-#define NUM_CHANNELS 2
-
static int send_begin(const struct sr_dev_inst *sdi)
{
struct sr_usb_dev_inst *usb;
devc = sdi->priv;
usb = sdi->conn;
- if (sdi->status == SR_ST_ACTIVE)
- /* already in use */
- return SR_ERR;
-
libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
for (i = 0; devlist[i]; i++) {
libusb_get_device_descriptor(devlist[i], &des);
/*
* Check device by its physical USB bus/port address.
*/
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
if (strcmp(sdi->connection_id, connection_id))
/* This is not the one. */
continue;
libusb_close(usb->devhdl);
usb->devhdl = NULL;
sdi->status = SR_ST_INACTIVE;
-
}
static int get_channel_offsets(const struct sr_dev_inst *sdi)
return SR_OK;
}
-static int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi)
+static void dso2250_set_triggerpos(int value, int long_buffer, uint8_t dest[], int offset)
+{
+ uint32_t min, max;
+ uint32_t tmp, diff;
+
+ min = long_buffer ? 0 : 0x7d7ff;
+ max = 0x7ffff;
+
+ diff = max - min;
+ tmp = min + diff * value / 100;
+ sr_dbg("2250 trigger pos: %3d%% * [0x%x,0x%x] == 0x%x", value, min, max, tmp);
+
+ dest[offset + 0] = tmp & 0xff;
+ dest[offset + 1] = (tmp >> 8) & 0xff;
+ dest[offset + 2] = (tmp >> 16) & 0x7;
+}
+
+/* See http://openhantek.sourceforge.net/doc/namespaceHantek.html#ac1cd181814cf3da74771c29800b39028 */
+static int dso2250_set_trigger_samplerate(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ int ret, tmp;
+ uint64_t base;
+ uint8_t cmdstring[12];
+ int trig;
+
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ memset(cmdstring, 0, sizeof(cmdstring));
+ /* Command */
+ cmdstring[0] = CMD_2250_SET_TRIGGERSOURCE;
+ sr_dbg("Trigger source %s.", devc->triggersource);
+ if (!strcmp("CH2", devc->triggersource))
+ tmp = 3;
+ else if (!strcmp("CH1", devc->triggersource))
+ tmp = 2;
+ else if (!strcmp("EXT", devc->triggersource))
+ tmp = 0;
+ else {
+ sr_err("Invalid trigger source: '%s'.", devc->triggersource);
+ return SR_ERR_ARG;
+ }
+ cmdstring[2] = tmp;
+
+ sr_dbg("Trigger slope: %d.", devc->triggerslope);
+ cmdstring[2] |= (devc->triggerslope == SLOPE_NEGATIVE ? 1 : 0) << 3;
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, 8, &tmp, 100)) != 0) {
+ sr_err("Failed to set trigger/samplerate: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ /* Frame size */
+ sr_dbg("Frame size: %d.", devc->framesize);
+ cmdstring[0] = CMD_2250_SET_RECORD_LENGTH;
+ cmdstring[2] = (devc->framesize == FRAMESIZE_SMALL) ? 0x01 : 0x02;
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, 4, &tmp, 100)) != 0) {
+ sr_err("Failed to set record length: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ memset(cmdstring, 0, sizeof(cmdstring));
+ cmdstring[0] = CMD_2250_SET_SAMPLERATE;
+ base = 100e6;
+ if (devc->samplerate > base) {
+ /* Timebase fast */
+ sr_err("Sample rate > 100MHz not yet supported.");
+ return SR_ERR_ARG;
+ }
+
+ tmp = base / devc->samplerate;
+ if (tmp) {
+ /* Downsampling on */
+ cmdstring[2] |= 2;
+ /* Downsampler = 1comp((Base / Samplerate) - 2)
+ * Base == 100Msa resp. 200MSa
+ *
+ * Example for 500kSa/s:
+ * 100e6 / 500e3 => 200
+ * 200 - 2 => 198
+ * 1comp(198) => ff39 */
+ tmp -= 2;
+ tmp = ~tmp;
+ sr_dbg("Down sampler value: 0x%x.", tmp & 0xffff);
+ cmdstring[4] = (tmp >> 0) & 0xff;
+ cmdstring[5] = (tmp >> 8) & 0xff;
+ }
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, 8, &tmp, 100)) != 0) {
+ sr_err("Failed to set sample rate: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Sent CMD_2250_SET_SAMPLERATE.");
+
+ /* Enabled channels: 00=CH1, 01=CH2, 10=both. */
+ memset(cmdstring, 0, sizeof(cmdstring));
+ cmdstring[0] = CMD_2250_SET_CHANNELS;
+ sr_dbg("Channels: CH1=%d, CH2=%d.", devc->ch_enabled[0], devc->ch_enabled[1]);
+ cmdstring[2] = (devc->ch_enabled[0] ? 0 : 1) + (devc->ch_enabled[1] ? 2 : 0);
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, 4, &tmp, 100)) != 0) {
+ sr_err("Failed to set channels: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+ sr_dbg("Sent CMD_2250_SET_CHANNELS.");
+
+ /* Trigger slope: 0=positive, 1=negative. */
+ memset(cmdstring, 0, sizeof(cmdstring));
+ cmdstring[0] = CMD_2250_SET_TRIGGERPOS_AND_BUFFER;
+
+ sr_dbg("Capture ratio: %" PRIu64 ".", devc->capture_ratio);
+ trig = devc->capture_ratio;
+ dso2250_set_triggerpos(trig,
+ devc->framesize != FRAMESIZE_SMALL, cmdstring, 2);
+ dso2250_set_triggerpos(100 - trig,
+ devc->framesize != FRAMESIZE_SMALL, cmdstring, 6);
+
+ if (send_begin(sdi) != SR_OK)
+ return SR_ERR;
+
+ /* TODO: 12 bytes according to documentation? */
+ if ((ret = libusb_bulk_transfer(usb->devhdl, DSO_EP_OUT,
+ cmdstring, 10, &tmp, 100)) != 0) {
+ sr_err("Failed to set trigger position: %s.",
+ libusb_error_name(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
uint16_t timebase_large[] = { 0xffff, 0x0000, 0xfffc, 0xfff7, 0xffe8,
0xffce, 0xff9d, 0xff07, 0xfe0d, 0xfc19, 0xf63d, 0xec79 };
+ devc = sdi->priv;
+ if (devc->profile->fw_pid == 0x2250)
+ return dso2250_set_trigger_samplerate(sdi);
+
sr_dbg("Preparing CMD_SET_TRIGGER_SAMPLERATE.");
- devc = sdi->priv;
usb = sdi->conn;
memset(cmdstring, 0, sizeof(cmdstring));
cmdstring[5] = (tmp >> 8) & 0xff;
/* Horizontal trigger position */
- sr_dbg("Trigger position: %3.2f.", devc->triggerposition);
- tmp = 0x77fff + 0x8000 * devc->triggerposition;
+ sr_dbg("Capture ratio: %" PRIu64 ".", devc->capture_ratio);
+ tmp = 0x77fff + 0x8000 * devc->capture_ratio / 100;
cmdstring[6] = tmp & 0xff;
cmdstring[7] = (tmp >> 8) & 0xff;
cmdstring[10] = (tmp >> 16) & 0xff;
memset(cmdstring, 0, sizeof(cmdstring));
cmdstring[0] = CMD_SET_VOLTAGE;
- cmdstring[1] = 0x0f;
- cmdstring[2] = 0x30;
- /* CH1 volts/div is encoded in bits 0-1 */
+ if (devc->profile->fw_pid == 0x2250) {
+ cmdstring[1] = 0x00;
+ cmdstring[2] = 0x08;
+ } else {
+ cmdstring[1] = 0x0f;
+ cmdstring[2] = 0x30;
+ }
+
+ /* CH1 volts/div is encoded in bits 0-1. */
sr_dbg("CH1 vdiv index: %d.", devc->voltage[0]);
switch (devc->voltage[0]) {
case VDIV_1V:
break;
}
- /* CH2 volts/div is encoded in bits 2-3 */
+ /* CH2 volts/div is encoded in bits 2-3. */
sr_dbg("CH2 vdiv index: %d.", devc->voltage[1]);
switch (devc->voltage[1]) {
case VDIV_1V:
return SR_OK;
}
-static int dso_set_voffsets(const struct sr_dev_inst *sdi)
+SR_PRIV int dso_set_voffsets(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
SR_PRIV int dso_init(const struct sr_dev_inst *sdi)
{
-
sr_dbg("Initializing DSO.");
if (get_channel_offsets(sdi) != SR_OK)
#define DEFAULT_VOLTAGE VDIV_500MV
#define DEFAULT_FRAMESIZE FRAMESIZE_SMALL
#define DEFAULT_TIMEBASE TIME_100us
+#define DEFAULT_SAMPLERATE SR_KHZ(10)
#define DEFAULT_TRIGGER_SOURCE "CH1"
#define DEFAULT_COUPLING COUPLING_DC
-#define DEFAULT_HORIZ_TRIGGERPOS 0.5
+#define DEFAULT_CAPTURE_RATIO 100
#define DEFAULT_VERT_OFFSET 0.5
#define DEFAULT_VERT_TRIGGERPOS 0.5
/* All models have this for their "fast" mode. */
#define FRAMESIZE_SMALL (10 * 1024)
+#define NUM_CHANNELS 2
+
enum control_requests {
CTRL_READ_EEPROM = 0xa2,
CTRL_GETSPEED = 0xb2,
};
enum dso_commands {
- CMD_SET_FILTERS = 0,
- CMD_SET_TRIGGER_SAMPLERATE,
- CMD_FORCE_TRIGGER,
- CMD_CAPTURE_START,
- CMD_ENABLE_TRIGGER,
- CMD_GET_CHANNELDATA,
- CMD_GET_CAPTURESTATE,
- CMD_SET_VOLTAGE,
+ CMD_SET_FILTERS = 0x0,
+ CMD_SET_TRIGGER_SAMPLERATE = 0x1,
+ CMD_FORCE_TRIGGER = 0x2,
+ CMD_CAPTURE_START = 0x3,
+ CMD_ENABLE_TRIGGER = 0x4,
+ CMD_GET_CHANNELDATA = 0x5,
+ CMD_GET_CAPTURESTATE = 0x6,
+ CMD_SET_VOLTAGE = 0x7,
/* unused */
- CMD_SET_LOGICALDATA,
- CMD_GET_LOGICALDATA,
+ CMD_SET_LOGICALDATA = 0x8,
+ CMD_GET_LOGICALDATA = 0x9,
+ CMD__UNUSED1 = 0xa,
+ /*
+ * For the following and other specials please see
+ * http://openhantek.sourceforge.net/doc/namespaceHantek.html#ac1cd181814cf3da74771c29800b39028
+ */
+ CMD_2250_SET_CHANNELS = 0xb,
+ CMD_2250_SET_TRIGGERSOURCE = 0xc,
+ CMD_2250_SET_RECORD_LENGTH = 0xd,
+ CMD_2250_SET_SAMPLERATE = 0xe,
+ CMD_2250_SET_TRIGGERPOS_AND_BUFFER = 0xf,
};
/* Must match the coupling table. */
CAPTURE_EMPTY = 0,
CAPTURE_FILLING = 1,
CAPTURE_READY_8BIT = 2,
+ CAPTURE_READY_2250 = 3,
CAPTURE_READY_9BIT = 7,
CAPTURE_TIMEOUT = 127,
CAPTURE_UNKNOWN = 255,
int dev_state;
/* Oscilloscope settings. */
+ uint64_t samplerate;
int timebase;
gboolean ch_enabled[2];
int voltage[2];
gboolean filter[2];
int triggerslope;
char *triggersource;
- float triggerposition;
+ uint64_t capture_ratio;
int triggermode;
/* Frame transfer */
SR_PRIV int dso_capture_start(const struct sr_dev_inst *sdi);
SR_PRIV int dso_get_channeldata(const struct sr_dev_inst *sdi,
libusb_transfer_cb_fn cb);
+SR_PRIV int dso_set_trigger_samplerate(const struct sr_dev_inst *sdi);
+SR_PRIV int dso_set_voffsets(const struct sr_dev_inst *sdi);
#endif
sr_scpi_send(scpi, "TRIG HOLD");
sr_scpi_get_float(scpi, "NPLC?", &devc->nplc);
- sdi->status = SR_ST_ACTIVE;
-
return SR_OK;
}
{
struct sr_scpi_dev_inst *scpi = sdi->conn;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
/* Disable scan-advance (preserve relay life). */
sr_scpi_send(scpi, "SADV HOLD");
/* Switch back to auto-triggering. */
sr_scpi_close(scpi);
- sdi->status = SR_ST_INACTIVE;
-
return SR_OK;
}
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;
devc = sdi->priv;
- ret = SR_OK;
switch (key) {
case SR_CONF_ADC_POWERLINE_CYCLES:
*data = g_variant_new_double(devc->nplc);
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static int config_set(uint32_t key, GVariant *data,
- const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- int ret;
enum sr_mq mq;
enum sr_mqflag mq_flags;
struct dev_context *devc;
(void)cg;
- 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);
mq = g_variant_get_uint32(tuple_child);
tuple_child = g_variant_get_child_value(data, 1);
mq_flags = g_variant_get_uint64(tuple_child);
- ret = hp_3457a_set_mq(sdi, mq, mq_flags);
g_variant_unref(tuple_child);
- break;
+ return hp_3457a_set_mq(sdi, mq, mq_flags);
case SR_CONF_ADC_POWERLINE_CYCLES:
- ret = hp_3457a_set_nplc(sdi, g_variant_get_double(data));
- break;
+ return hp_3457a_set_nplc(sdi, g_variant_get_double(data));
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ 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;
-
- 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;
- } 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;
- }
-
- /* From here on, we're only concerned with channel group config. */
- if (!cg)
- return SR_ERR_NA;
-
/*
* TODO: Implement channel group configuration when adding support for
* plug-in cards.
*/
- ret = SR_OK;
- switch (key) {
- default:
- ret = SR_ERR_NA;
- }
-
- return ret;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static void create_channel_index_list(GSList *channels, GArray **arr)
GArray *ch_list;
GSList *channels;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
scpi = sdi->conn;
devc = sdi->priv;
return SR_ERR_ARG;
}
- devc->current_channel = devc->active_channels->data;
devc->num_active_channels = g_slist_length(devc->active_channels);
+ if (!devc->num_active_channels) {
+ sr_err("Need at least one active channel!");
+ g_slist_free(devc->active_channels);
+ return SR_ERR_ARG;
+ }
+ devc->current_channel = devc->active_channels->data;
hp_3457a_select_input(sdi, front_selected ? CONN_FRONT : CONN_REAR);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
static int set_mq_volt(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
static int set_mq_amp(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
static int set_mq_ohm(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+
/*
* The source for the frequency measurement can be either AC voltage, AC+DC
* voltage, AC current, or AC+DC current. Configuring this is not yet
ret = sr_scpi_get_double(scpi, NULL, &devc->last_channel_sync);
if (ret != SR_OK) {
sr_err("Cannot check channel synchronization.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return FALSE;
}
devc->acq_state = ACQ_GOT_CHANNEL_SYNC;
sr_err("Expected channel %u, but device says %u",
chanc->index,
(unsigned int)devc->last_channel_sync);
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return FALSE;
}
/* All is good. Back to business. */
}
if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return FALSE;
}
CONN_REAR,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
/* Information about rear card option, or NULL if unknown */
const struct rear_card_info *rear_card;
- /* Acquisition settings */
enum sr_mq measurement_mq;
enum sr_mqflag measurement_mq_flags;
enum sr_unit measurement_unit;
unsigned int num_active_channels;
struct sr_channel *current_channel;
- /* Operational state */
enum acquisition_state acq_state;
enum channel_conn input_loc;
uint64_t num_samples;
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "scpi.h"
+#include "protocol.h"
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const struct {
+ enum sr_mq mq;
+ enum sr_mqflag mqflag;
+} mqopts[] = {
+ {SR_MQ_VOLTAGE, SR_MQFLAG_DC},
+ {SR_MQ_VOLTAGE, SR_MQFLAG_DC | SR_MQFLAG_AUTORANGE},
+ {SR_MQ_VOLTAGE, SR_MQFLAG_AC | SR_MQFLAG_RMS},
+ {SR_MQ_VOLTAGE, SR_MQFLAG_AC | SR_MQFLAG_RMS | SR_MQFLAG_AUTORANGE},
+ {SR_MQ_CURRENT, SR_MQFLAG_DC},
+ {SR_MQ_CURRENT, SR_MQFLAG_DC | SR_MQFLAG_AUTORANGE},
+ {SR_MQ_CURRENT, SR_MQFLAG_AC | SR_MQFLAG_RMS},
+ {SR_MQ_CURRENT, SR_MQFLAG_AC | SR_MQFLAG_RMS | SR_MQFLAG_AUTORANGE},
+ {SR_MQ_RESISTANCE, 0},
+ {SR_MQ_RESISTANCE, 0 | SR_MQFLAG_AUTORANGE},
+ {SR_MQ_RESISTANCE, SR_MQFLAG_FOUR_WIRE},
+ {SR_MQ_RESISTANCE, SR_MQFLAG_FOUR_WIRE | SR_MQFLAG_AUTORANGE},
+};
+
+SR_PRIV struct sr_dev_driver hp_3478a_driver_info;
+
+static int create_front_channel(struct sr_dev_inst *sdi, int chan_idx)
+{
+ struct sr_channel *channel;
+ struct channel_context *chanc;
+
+ chanc = g_malloc(sizeof(*chanc));
+ chanc->location = TERMINAL_FRONT;
+
+ channel = sr_channel_new(sdi, chan_idx++, SR_CHANNEL_ANALOG, TRUE, "P1");
+ channel->priv = chanc;
+
+ return chan_idx;
+}
+
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
+{
+ int ret;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->vendor = g_strdup("Hewlett-Packard");
+ sdi->model = g_strdup("3478A");
+ sdi->conn = scpi;
+ sdi->driver = &hp_3478a_driver_info;
+ sdi->inst_type = SR_INST_SCPI;
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ sr_sw_limits_init(&devc->limits);
+ sdi->priv = devc;
+
+ /* Get actual status (function, digits, ...). */
+ ret = hp_3478a_get_status_bytes(sdi);
+ if (ret != SR_OK)
+ return NULL;
+
+ create_front_channel(sdi, 0);
+
+ return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ return sr_scpi_scan(di->context, options, probe_device);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ return sr_scpi_open(sdi->conn);
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ return sr_scpi_close(sdi->conn);
+}
+
+static int config_get(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+ int ret;
+ GVariant *arr[2];
+
+ (void)cg;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ return sr_sw_limits_config_get(&devc->limits, key, data);
+ case SR_CONF_MEASURED_QUANTITY:
+ ret = hp_3478a_get_status_bytes(sdi);
+ if (ret != SR_OK)
+ return ret;
+ arr[0] = g_variant_new_uint32(devc->measurement_mq);
+ arr[1] = g_variant_new_uint64(devc->measurement_mq_flags);
+ *data = g_variant_new_tuple(arr, 2);
+ 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;
+ enum sr_mq mq;
+ enum sr_mqflag mq_flags;
+ GVariant *tuple_child;
+
+ (void)cg;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ return sr_sw_limits_config_set(&devc->limits, key, data);
+ case SR_CONF_MEASURED_QUANTITY:
+ tuple_child = g_variant_get_child_value(data, 0);
+ mq = g_variant_get_uint32(tuple_child);
+ tuple_child = g_variant_get_child_value(data, 1);
+ mq_flags = g_variant_get_uint64(tuple_child);
+ g_variant_unref(tuple_child);
+ return hp_3478a_set_mq(sdi, mq, mq_flags);
+ 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)
+{
+ unsigned int i;
+ GVariant *gvar, *arr[2];
+ GVariantBuilder gvb;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_MEASURED_QUANTITY:
+ /*
+ * TODO: move to std.c as
+ * SR_PRIV GVariant *std_gvar_measured_quantities()
+ */
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < ARRAY_SIZE(mqopts); i++) {
+ arr[0] = g_variant_new_uint32(mqopts[i].mq);
+ arr[1] = g_variant_new_uint64(mqopts[i].mqflag);
+ gvar = g_variant_new_tuple(arr, 2);
+ 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)
+{
+ struct sr_scpi_dev_inst *scpi;
+ struct dev_context *devc;
+
+ scpi = sdi->conn;
+ devc = sdi->priv;
+
+ sr_sw_limits_acquisition_start(&devc->limits);
+ std_session_send_df_header(sdi);
+
+ /*
+ * NOTE: For faster readings, there are some things one can do:
+ * - Turn off the display: sr_scpi_send(scpi, "D3SIGROK").
+ * - Set the line frequency to 60Hz via switch (back of the unit).
+ * - Set to 3.5 digits measurement (add config key SR_CONF_DIGITS).
+ */
+
+ /* Set to internal trigger. */
+ sr_scpi_send(scpi, "T1");
+ /* Get device status. */
+ hp_3478a_get_status_bytes(sdi);
+
+ return sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 100,
+ hp_3478a_receive_data, (void *)sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi;
+
+ scpi = sdi->conn;
+
+ sr_scpi_source_remove(sdi->session, scpi);
+ std_session_send_df_end(sdi);
+
+ /* Set to internal trigger. */
+ sr_scpi_send(scpi, "T1");
+ /* Turn on display. */
+ sr_scpi_send(scpi, "D1");
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver hp_3478a_driver_info = {
+ .name = "hp-3478a",
+ .longname = "HP 3478A",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
+
+SR_REGISTER_DEV_DRIVER(hp_3478a_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017-2018 Frank Stettner <frank-stettner@gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+#include "scpi.h"
+#include "protocol.h"
+
+static int set_mq_volt(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+static int set_mq_amp(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+static int set_mq_ohm(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+
+static const struct {
+ enum sr_mq mq;
+ int (*set_mode)(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags);
+} sr_mq_to_cmd_map[] = {
+ { SR_MQ_VOLTAGE, set_mq_volt },
+ { SR_MQ_CURRENT, set_mq_amp },
+ { SR_MQ_RESISTANCE, set_mq_ohm },
+};
+
+static int set_mq_volt(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags)
+{
+ if ((flags & SR_MQFLAG_AC) != SR_MQFLAG_AC &&
+ (flags & SR_MQFLAG_DC) != SR_MQFLAG_DC)
+ return SR_ERR_NA;
+
+ return sr_scpi_send(scpi, "%s",
+ ((flags & SR_MQFLAG_AC) == SR_MQFLAG_AC) ? "F2" : "F1");
+}
+
+static int set_mq_amp(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags)
+{
+ if ((flags & SR_MQFLAG_AC) != SR_MQFLAG_AC &&
+ (flags & SR_MQFLAG_DC) != SR_MQFLAG_DC)
+ return SR_ERR_NA;
+
+ return sr_scpi_send(scpi, "%s", (flags & SR_MQFLAG_AC) ? "F6" : "F5");
+}
+
+static int set_mq_ohm(struct sr_scpi_dev_inst *scpi, enum sr_mqflag flags)
+{
+ return sr_scpi_send(scpi, "%s",
+ (flags & SR_MQFLAG_FOUR_WIRE) ? "F4" : "F3");
+}
+
+SR_PRIV int hp_3478a_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq,
+ enum sr_mqflag mq_flags)
+{
+ int ret;
+ size_t i;
+ struct sr_scpi_dev_inst *scpi = sdi->conn;
+ struct dev_context *devc = sdi->priv;
+
+ /* No need to send command if we're not changing measurement type. */
+ if (devc->measurement_mq == mq &&
+ ((devc->measurement_mq_flags & mq_flags) == mq_flags))
+ return SR_OK;
+
+ for (i = 0; i < ARRAY_SIZE(sr_mq_to_cmd_map); i++) {
+ if (sr_mq_to_cmd_map[i].mq != mq)
+ continue;
+
+ ret = sr_mq_to_cmd_map[i].set_mode(scpi, mq_flags);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = hp_3478a_get_status_bytes(sdi);
+ return ret;
+ }
+
+ return SR_ERR_NA;
+}
+
+static int parse_range_vdc(struct dev_context *devc, uint8_t range_byte)
+{
+ if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_30MV)
+ devc->enc_digits = devc->spec_digits - 2;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_300MV)
+ devc->enc_digits = devc->spec_digits - 3;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_3V)
+ devc->enc_digits = devc->spec_digits - 1;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_30V)
+ devc->enc_digits = devc->spec_digits - 2;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VDC_300V)
+ devc->enc_digits = devc->spec_digits - 3;
+ else
+ return SR_ERR_DATA;
+
+ return SR_OK;
+}
+
+static int parse_range_vac(struct dev_context *devc, uint8_t range_byte)
+{
+ if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VAC_300MV)
+ devc->enc_digits = devc->spec_digits - 3;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VAC_3V)
+ devc->enc_digits = devc->spec_digits - 1;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VAC_30V)
+ devc->enc_digits = devc->spec_digits - 2;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_VAC_300V)
+ devc->enc_digits = devc->spec_digits - 3;
+ else
+ return SR_ERR_DATA;
+
+ return SR_OK;
+}
+
+static int parse_range_a(struct dev_context *devc, uint8_t range_byte)
+{
+ if ((range_byte & SB1_RANGE_BLOCK) == RANGE_A_300MA)
+ devc->enc_digits = devc->spec_digits - 3;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_A_3A)
+ devc->enc_digits = devc->spec_digits - 1;
+ else
+ return SR_ERR_DATA;
+
+ return SR_OK;
+}
+
+static int parse_range_ohm(struct dev_context *devc, uint8_t range_byte)
+{
+ if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_30R)
+ devc->enc_digits = devc->spec_digits - 2;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_300R)
+ devc->enc_digits = devc->spec_digits - 3;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_3KR)
+ devc->enc_digits = devc->spec_digits - 1;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_30KR)
+ devc->enc_digits = devc->spec_digits - 2;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_300KR)
+ devc->enc_digits = devc->spec_digits - 3;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_3MR)
+ devc->enc_digits = devc->spec_digits - 1;
+ else if ((range_byte & SB1_RANGE_BLOCK) == RANGE_OHM_30MR)
+ devc->enc_digits = devc->spec_digits - 2;
+ else
+ return SR_ERR_DATA;
+
+ return SR_OK;
+}
+
+static int parse_function_byte(struct dev_context *devc, uint8_t function_byte)
+{
+ /* Digits / Resolution (spec_digits must be set before range parsing) */
+ if ((function_byte & SB1_DIGITS_BLOCK) == DIGITS_5_5)
+ devc->spec_digits = 6;
+ else if ((function_byte & SB1_DIGITS_BLOCK) == DIGITS_4_5)
+ devc->spec_digits = 5;
+ else if ((function_byte & SB1_DIGITS_BLOCK) == DIGITS_3_5)
+ devc->spec_digits = 4;
+ else
+ return SR_ERR_DATA;
+
+ /* Function + Range */
+ devc->measurement_mq_flags = 0;
+ if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_VDC) {
+ devc->measurement_mq = SR_MQ_VOLTAGE;
+ devc->measurement_mq_flags |= SR_MQFLAG_DC;
+ devc->measurement_unit = SR_UNIT_VOLT;
+ parse_range_vdc(devc, function_byte);
+ } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_VAC) {
+ devc->measurement_mq = SR_MQ_VOLTAGE;
+ devc->measurement_mq_flags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+ devc->measurement_unit = SR_UNIT_VOLT;
+ parse_range_vac(devc, function_byte);
+ } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_2WR) {
+ devc->measurement_mq = SR_MQ_RESISTANCE;
+ devc->measurement_unit = SR_UNIT_OHM;
+ parse_range_ohm(devc, function_byte);
+ } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_4WR) {
+ devc->measurement_mq = SR_MQ_RESISTANCE;
+ devc->measurement_mq_flags |= SR_MQFLAG_FOUR_WIRE;
+ devc->measurement_unit = SR_UNIT_OHM;
+ parse_range_ohm(devc, function_byte);
+ } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_ADC) {
+ devc->measurement_mq = SR_MQ_CURRENT;
+ devc->measurement_mq_flags |= SR_MQFLAG_DC;
+ devc->measurement_unit = SR_UNIT_AMPERE;
+ parse_range_a(devc, function_byte);
+ } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_AAC) {
+ devc->measurement_mq = SR_MQ_CURRENT;
+ devc->measurement_mq_flags |= SR_MQFLAG_AC | SR_MQFLAG_RMS;
+ devc->measurement_unit = SR_UNIT_AMPERE;
+ parse_range_a(devc, function_byte);
+ } else if ((function_byte & SB1_FUNCTION_BLOCK) == FUNCTION_EXR) {
+ devc->measurement_mq = SR_MQ_RESISTANCE;
+ devc->measurement_unit = SR_UNIT_OHM;
+ parse_range_ohm(devc, function_byte);
+ }
+
+ return SR_OK;
+}
+
+static int parse_status_byte(struct dev_context *devc, uint8_t status_byte)
+{
+ devc->trigger = TRIGGER_UNDEFINED;
+
+ /* External Trigger */
+ if ((status_byte & STATUS_EXT_TRIGGER) == STATUS_EXT_TRIGGER)
+ devc->trigger = TRIGGER_EXTERNAL;
+
+ /* Cal RAM */
+ if ((status_byte & STATUS_CAL_RAM) == STATUS_CAL_RAM)
+ devc->calibration = TRUE;
+ else
+ devc->calibration = FALSE;
+
+ /* Front/Rear terminals */
+ if ((status_byte & STATUS_FRONT_TERMINAL) == STATUS_FRONT_TERMINAL)
+ devc->terminal = TERMINAL_FRONT;
+ else
+ devc->terminal = TERMINAL_REAR;
+
+ /* 50Hz / 60Hz */
+ if ((status_byte & STATUS_50HZ) == STATUS_50HZ)
+ devc->line = LINE_50HZ;
+ else
+ devc->line = LINE_60HZ;
+
+ /* Auto-Zero */
+ if ((status_byte & STATUS_AUTO_ZERO) == STATUS_AUTO_ZERO)
+ devc->auto_zero = TRUE;
+ else
+ devc->auto_zero = FALSE;
+
+ /* Auto-Range */
+ if ((status_byte & STATUS_AUTO_RANGE) == STATUS_AUTO_RANGE)
+ devc->measurement_mq_flags |= SR_MQFLAG_AUTORANGE;
+ else
+ devc->measurement_mq_flags &= ~SR_MQFLAG_AUTORANGE;
+
+ /* Internal trigger */
+ if ((status_byte & STATUS_INT_TRIGGER) == STATUS_INT_TRIGGER)
+ devc->trigger = TRIGGER_INTERNAL;
+
+ return SR_OK;
+}
+
+static int parse_srq_byte(uint8_t sqr_byte)
+{
+ (void)sqr_byte;
+
+#if 0
+ /* The ServiceReQuest register isn't used at the moment. */
+
+ /* PON SRQ */
+ if ((sqr_byte & SRQ_POWER_ON) == SRQ_POWER_ON)
+ sr_spew("Power On SRQ or clear msg received");
+
+ /* Cal failed SRQ */
+ if ((sqr_byte & SRQ_CAL_FAILED) == SRQ_CAL_FAILED)
+ sr_spew("CAL failed SRQ");
+
+ /* Keyboard SRQ */
+ if ((sqr_byte & SRQ_KEYBORD) == SRQ_KEYBORD)
+ sr_spew("Keyboard SRQ");
+
+ /* Hardware error SRQ */
+ if ((sqr_byte & SRQ_HARDWARE_ERR) == SRQ_HARDWARE_ERR)
+ sr_spew("Hardware error SRQ");
+
+ /* Syntax error SRQ */
+ if ((sqr_byte & SRQ_SYNTAX_ERR) == SRQ_SYNTAX_ERR)
+ sr_spew("Syntax error SRQ");
+
+ /* Every reading is available to the bus SRQ */
+ if ((sqr_byte & SRQ_BUS_AVAIL) == SRQ_BUS_AVAIL)
+ sr_spew("Every reading is available to the bus SRQ");
+#endif
+
+ return SR_OK;
+}
+
+static int parse_error_byte(uint8_t error_byte)
+{
+ int ret;
+
+ ret = SR_OK;
+
+ /* A/D link */
+ if ((error_byte & ERROR_AD_LINK) == ERROR_AD_LINK) {
+ sr_err("Failure in the A/D link");
+ ret = SR_ERR;
+ }
+
+ /* A/D Self Test */
+ if ((error_byte & ERROR_AD_SELF_TEST) == ERROR_AD_SELF_TEST) {
+ sr_err("A/D has failed its internal Self Test");
+ ret = SR_ERR;
+ }
+
+ /* A/D slope error */
+ if ((error_byte & ERROR_AD_SLOPE) == ERROR_AD_SLOPE) {
+ sr_err("There has been an A/D slope error");
+ ret = SR_ERR;
+ }
+
+ /* ROM Selt Test */
+ if ((error_byte & ERROR_ROM_SELF_TEST) == ERROR_ROM_SELF_TEST) {
+ sr_err("The ROM Self Test has failed");
+ ret = SR_ERR;
+ }
+
+ /* RAM Selt Test */
+ if ((error_byte & ERROR_RAM_SELF_TEST) == ERROR_RAM_SELF_TEST) {
+ sr_err("The RAM Self Test has failed");
+ ret = SR_ERR;
+ }
+
+ /* Selt Test */
+ if ((error_byte & ERROR_SELF_TEST) == ERROR_SELF_TEST) {
+ sr_err("Self Test: Any of the CAL RAM locations have bad "
+ "checksums, or a range with a bad checksum is selected");
+ ret = SR_ERR;
+ }
+
+ return ret;
+}
+
+SR_PRIV int hp_3478a_get_status_bytes(const struct sr_dev_inst *sdi)
+{
+ int ret;
+ char *response;
+ uint8_t function_byte, status_byte, srq_byte, error_byte;
+ struct sr_scpi_dev_inst *scpi = sdi->conn;
+ struct dev_context *devc = sdi->priv;
+
+ ret = sr_scpi_get_string(scpi, "B", &response);
+ if (ret != SR_OK)
+ return ret;
+
+ if (!response)
+ return SR_ERR;
+
+ function_byte = (uint8_t)response[0];
+ status_byte = (uint8_t)response[1];
+ srq_byte = (uint8_t)response[2];
+ error_byte = (uint8_t)response[3];
+
+ g_free(response);
+
+ parse_function_byte(devc, function_byte);
+ parse_status_byte(devc, status_byte);
+ parse_srq_byte(srq_byte);
+ ret = parse_error_byte(error_byte);
+
+ return ret;
+}
+
+static void acq_send_measurement(struct sr_dev_inst *sdi)
+{
+ 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;
+ float f;
+
+ devc = sdi->priv;
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+
+ sr_analog_init(&analog, &encoding, &meaning, &spec, devc->enc_digits);
+
+ /* TODO: Implement NAN, depending on counts, range and value. */
+ f = devc->measurement;
+ analog.num_samples = 1;
+ analog.data = &f;
+
+ encoding.unitsize = sizeof(float);
+ encoding.is_float = TRUE;
+ encoding.digits = devc->enc_digits;
+
+ meaning.mq = devc->measurement_mq;
+ meaning.mqflags = devc->measurement_mq_flags;
+ meaning.unit = devc->measurement_unit;
+ meaning.channels = sdi->channels;
+
+ spec.spec_digits = devc->spec_digits;
+
+ sr_session_send(sdi, &packet);
+}
+
+SR_PRIV int hp_3478a_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_scpi_dev_inst *scpi;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+
+ (void)fd;
+ (void)revents;
+
+ if (!(sdi = cb_data) || !(devc = sdi->priv))
+ return TRUE;
+
+ scpi = sdi->conn;
+
+ /*
+ * This is necessary to get the actual range for the encoding digits.
+ * When SPoll is implemmented, this can be done via SPoll.
+ */
+ if (hp_3478a_get_status_bytes(sdi) != SR_OK)
+ return FALSE;
+
+ /*
+ * TODO: Implement GPIB-SPoll, to get notified by a SRQ when a new
+ * measurement is available. This is necessary, because when
+ * switching ranges, there could be a timeout.
+ */
+ if (sr_scpi_get_double(scpi, NULL, &devc->measurement) != SR_OK)
+ return FALSE;
+
+ acq_send_measurement(sdi);
+ sr_sw_limits_update_samples_read(&devc->limits, 1);
+
+ if (sr_sw_limits_check(&devc->limits))
+ sr_dev_acquisition_stop(sdi);
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_HP_3478A_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_HP_3478A_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "hp-3478a"
+
+#define SB1_FUNCTION_BLOCK 0b11100000
+#define SB1_RANGE_BLOCK 0b00011100
+#define SB1_DIGITS_BLOCK 0b00000011
+
+/* Status Byte 1 (Function) */
+enum sb1_function {
+ FUNCTION_VDC = 0b00100000,
+ FUNCTION_VAC = 0b01000000,
+ FUNCTION_2WR = 0b01100000,
+ FUNCTION_4WR = 0b10000000,
+ FUNCTION_ADC = 0b10100000,
+ FUNCTION_AAC = 0b11000000,
+ FUNCTION_EXR = 0b11100000,
+};
+
+/* Status Byte 1 (Range V DC) */
+enum sb1_range_vdc {
+ RANGE_VDC_30MV = 0b00000100,
+ RANGE_VDC_300MV = 0b00001000,
+ RANGE_VDC_3V = 0b00001100,
+ RANGE_VDC_30V = 0b00010000,
+ RANGE_VDC_300V = 0b00010100,
+};
+
+/* Status Byte 1 (Range V AC) */
+enum sb1_range_vac {
+ RANGE_VAC_300MV = 0b00000100,
+ RANGE_VAC_3V = 0b00001000,
+ RANGE_VAC_30V = 0b00001100,
+ RANGE_VAC_300V = 0b00010000,
+};
+
+/* Status Byte 1 (Range A) */
+enum sb1_range_a {
+ RANGE_A_300MA = 0b00000100,
+ RANGE_A_3A = 0b00001000,
+};
+
+/* Status Byte 1 (Range Ohm) */
+enum sb1_range_ohm {
+ RANGE_OHM_30R = 0b00000100,
+ RANGE_OHM_300R = 0b00001000,
+ RANGE_OHM_3KR = 0b00001100,
+ RANGE_OHM_30KR = 0b00010000,
+ RANGE_OHM_300KR = 0b00010100,
+ RANGE_OHM_3MR = 0b00011000,
+ RANGE_OHM_30MR = 0b00011100,
+};
+
+/* Status Byte 1 (Digits) */
+enum sb1_digits {
+ DIGITS_5_5 = 0b00000001,
+ DIGITS_4_5 = 0b00000010,
+ DIGITS_3_5 = 0b00000011,
+};
+
+/* Status Byte 2 */
+enum sb2_status {
+ STATUS_INT_TRIGGER = (1 << 0),
+ STATUS_AUTO_RANGE = (1 << 1),
+ STATUS_AUTO_ZERO = (1 << 2),
+ STATUS_50HZ = (1 << 3),
+ STATUS_FRONT_TERMINAL = (1 << 4),
+ STATUS_CAL_RAM = (1 << 5),
+ STATUS_EXT_TRIGGER = (1 << 6),
+};
+
+/* Status Byte 3 (Serial Poll Mask) */
+enum sb3_srq {
+ SRQ_BUS_AVAIL = (1 << 0),
+ SRQ_SYNTAX_ERR = (1 << 2),
+ SRQ_HARDWARE_ERR = (1 << 3),
+ SRQ_KEYBORD = (1 << 4),
+ SRQ_CAL_FAILED = (1 << 5),
+ SRQ_POWER_ON = (1 << 7),
+};
+
+/* Status Byte 4 (Error) */
+enum sb4_error {
+ ERROR_SELF_TEST = (1 << 0),
+ ERROR_RAM_SELF_TEST = (1 << 1),
+ ERROR_ROM_SELF_TEST = (1 << 2),
+ ERROR_AD_SLOPE = (1 << 3),
+ ERROR_AD_SELF_TEST = (1 << 4),
+ ERROR_AD_LINK = (1 << 5),
+};
+
+/* Channel connector (front terminals or rear terminals. */
+enum terminal_connector {
+ TERMINAL_FRONT,
+ TERMINAL_REAR,
+};
+
+/* Possible triggers */
+enum trigger_state {
+ TRIGGER_UNDEFINED,
+ TRIGGER_EXTERNAL,
+ TRIGGER_INTERNAL,
+};
+
+/* Possible line frequencies */
+enum line_freq {
+ LINE_50HZ,
+ LINE_60HZ,
+};
+
+struct dev_context {
+ struct sr_sw_limits limits;
+
+ double measurement;
+ enum sr_mq measurement_mq;
+ enum sr_mqflag measurement_mq_flags;
+ enum sr_unit measurement_unit;
+ uint8_t enc_digits;
+ uint8_t spec_digits;
+
+ enum terminal_connector terminal;
+ enum trigger_state trigger;
+ enum line_freq line;
+ gboolean auto_zero;
+ gboolean calibration;
+};
+
+struct channel_context {
+ int index;
+ enum terminal_connector location;
+};
+
+SR_PRIV int hp_3478a_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq,
+ enum sr_mqflag mq_flags);
+SR_PRIV int hp_3478a_get_status_bytes(const struct sr_dev_inst *sdi);
+SR_PRIV int hp_3478a_receive_data(int fd, int revents, void *cb_data);
+
+#endif
SR_CONF_BUFFERSIZE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
-static const uint32_t cgopts[] = {
+static const uint32_t devopts_cg[] = {
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,
return std_scan_complete(di, devices);
}
-static void clear_private(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc = priv;
-
g_slist_free(devc->enabled_channel);
}
}
}
- return std_dev_clear(di, clear_private);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
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 (!devc->samples)
goto fail3;
- sdi->status = SR_ST_ACTIVE;
-
return SR_OK;
fail3:
{
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 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)
+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;
+ int idx, 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_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]);
+ if ((idx = std_u8_idx_s(devc->cctl[0] & 0xC0, ARRAY_AND_SIZE(trigger_sources_map))) < 0)
+ return SR_ERR_BUG;
+ *data = g_variant_new_string(trigger_sources[idx]);
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]);
+ return SR_ERR;
+ *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]);
- }
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if ((idx = std_u8_idx_s(devc->cctl[ch] & 0x33, ARRAY_AND_SIZE(vdivs_map))) < 0)
+ return SR_ERR_BUG;
+ *data = g_variant_new("(tt)", vdivs[idx][0], vdivs[idx][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]);
- }
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if ((idx = std_u8_idx_s(devc->cctl[ch] & 0x0C, ARRAY_AND_SIZE(coupling_map))) < 0)
+ return SR_ERR_BUG;
+ *data = g_variant_new_string(coupling[idx]);
break;
case SR_CONF_PROBE_FACTOR:
- if (ch == -1)
- ret = SR_ERR_CHANNEL_GROUP;
- else
- *data = g_variant_new_uint64(devc->probe[ch]);
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ *data = g_variant_new_uint64(devc->probe[ch]);
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
+ int idx, ch = -1;
+ uint64_t u;
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;
+ if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(samplerates))) < 0)
+ return SR_ERR_ARG;
+ devc->rate = idx;
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];
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->cctl[0] = (devc->cctl[0] & 0x3F) | trigger_sources_map[idx];
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;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
+ return SR_ERR_ARG;
+ devc->edge = idx;
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;
+ if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(buffersizes))) < 0)
+ return SR_ERR_ARG;
+ devc->last_step = idx;
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];
- }
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if (!g_variant_is_of_type(data, G_VARIANT_TYPE("(tt)")))
+ return SR_ERR_ARG;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+ return SR_ERR_ARG;
+ devc->cctl[ch] = (devc->cctl[ch] & 0xCC) | vdivs_map[idx];
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];
- }
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(coupling))) < 0)
+ return SR_ERR_ARG;
+ devc->cctl[ch] = (devc->cctl[ch] & 0xF3) | coupling_map[idx];
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;
- }
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ u = g_variant_get_uint64(data);
+ if (!u)
+ return SR_ERR_ARG;
+ devc->probe[ch] = u;
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static int config_channel_set(const struct sr_dev_inst *sdi,
- struct sr_channel *ch,
- unsigned int changes)
+ 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);
- }
+ if (!(changes & SR_CHANNEL_SET_ENABLED))
+ return SR_OK;
+
+ 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;
}
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:
break;
case 0x00:
state = 0x01;
+ /* Fallthrough */
default:
ret = hung_chang_dso_2100_move_to(sdi, 1);
if (ret != SR_OK)
return ret;
+ /* Fallthrough */
case 0x01:
hung_chang_dso_2100_write_mbox(sdi->conn, 4);
}
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)
+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_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
break;
case SR_CONF_SAMPLERATE:
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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, NO_OPTS, NO_OPTS);
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));
+ if (!cg)
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
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);
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
break;
case SR_CONF_TRIGGER_SOURCE:
- *data = g_variant_new_strv(trigger_sources, ARRAY_SIZE(trigger_sources));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_sources));
break;
case SR_CONF_TRIGGER_SLOPE:
- *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes));
+ *data = g_variant_new_strv(ARRAY_AND_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));
+ *data = std_gvar_array_u64(ARRAY_AND_SIZE(buffersizes));
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);
+ *data = std_gvar_tuple_array(ARRAY_AND_SIZE(vdivs));
break;
case SR_CONF_COUPLING:
- *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(coupling));
break;
}
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};
return SR_OK;
}
-SR_PRIV int hung_chang_dso_2100_dev_acquisition_stop(const struct sr_dev_inst *sdi)
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
std_session_send_df_end(sdi);
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)
-{
- return hung_chang_dso_2100_dev_acquisition_stop(sdi);
-}
-
static struct sr_dev_driver hung_chang_dso_2100_driver_info = {
.name = "hung-chang-dso-2100",
.longname = "Hung-Chang DSO-2100",
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 sr_dev_inst *sdi;
struct dev_context *devc;
uint8_t state, buf[1000];
sr_session_send(sdi, &packet);
if (++devc->frame >= devc->frame_limit)
- hung_chang_dso_2100_dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
else
hung_chang_dso_2100_move_to(sdi, 0x21);
#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 offset[2];
uint8_t gain[2];
- /* Operational state */
uint64_t frame_limit;
uint64_t frame;
uint64_t probe[2];
uint8_t retries;
gboolean adc2;
- /* Temporary state across callbacks */
float *samples;
float factor;
gboolean state_known;
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);
#endif
#include <config.h>
#include "protocol.h"
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
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,
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->vendor = g_strdup("IKALOGIC");
+ sdi->model = g_strdup("Scanalogic-2");
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;
return std_scan_complete(di, devices);
}
-static void clear_dev_context(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
-
- sr_dbg("Device context cleared.");
-
libusb_free_transfer(devc->xfer_in);
libusb_free_transfer(devc->xfer_out);
- g_free(devc);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, &clear_dev_context);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static int dev_open(struct sr_dev_inst *sdi)
if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
return SR_ERR;
- /*
- * Determine if a kernel driver is active on this interface and, if so,
- * detach it.
- */
if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) {
ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE);
if (ret < 0) {
return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
-
return SR_OK;
}
usb = sdi->conn;
if (!usb->devhdl)
- return SR_OK;
+ return SR_ERR_BUG;
libusb_release_interface(usb->devhdl, USB_INTERFACE);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
- int ret;
(void)cg;
- ret = SR_OK;
devc = sdi->priv;
switch (key) {
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
- int ret;
+ struct dev_context *devc;
+ uint64_t samplerate, limit_samples;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
+ devc = sdi->priv;
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
limit_samples = g_variant_get_uint64(data);
- ret = sl2_set_limit_samples(sdi, limit_samples);
- break;
+ return sl2_set_limit_samples(sdi, limit_samples);
case SR_CONF_SAMPLERATE:
samplerate = g_variant_get_uint64(data);
- ret = sl2_set_samplerate(sdi, samplerate);
- break;
+ return sl2_set_samplerate(sdi, samplerate);
case SR_CONF_CAPTURE_RATIO:
- capture_ratio = g_variant_get_uint64(data);
- ret = sl2_set_capture_ratio(sdi, capture_ratio);
+ devc->capture_ratio = g_variant_get_uint64(data);
break;
default:
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
- int ret;
-
- (void)sdi;
- (void)cg;
-
- ret = SR_OK;
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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
case SR_CONF_SAMPLERATE:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- sl2_samplerates, ARRAY_SIZE(sl2_samplerates),
- sizeof(uint64_t));
- g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(sl2_samplerates));
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));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
case SR_CONF_LIMIT_SAMPLES:
- grange[0] = g_variant_new_uint64(0);
- grange[1] = g_variant_new_uint64(MAX_SAMPLES);
- *data = g_variant_new_tuple(grange, 2);
+ *data = std_gvar_tuple_u64(0, MAX_SAMPLES);
break;
default:
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
unsigned int i, j;
int ret;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
drvc = di->context;
usb_source_add(sdi->session, drvc->sr_ctx, 100,
ikalogic_scanalogic2_receive_data, (void *)sdi);
- sr_dbg("Acquisition started successfully.");
-
std_session_send_df_header(sdi);
devc->next_state = STATE_SAMPLE;
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- sr_dbg("Stopping acquisition.");
-
sdi->status = SR_ST_STOPPING;
return SR_OK;
std_session_send_df_end(sdi);
- sdi->driver->dev_close(sdi);
+ sr_dev_close(sdi);
}
static void buffer_sample_data(const struct sr_dev_inst *sdi)
return SR_OK;
}
-SR_PRIV int sl2_set_capture_ratio(const struct sr_dev_inst *sdi,
- uint64_t capture_ratio)
-{
- struct dev_context *devc;
-
- devc = sdi->priv;
-
- if (capture_ratio > 100) {
- sr_err("Invalid capture ratio: %" PRIu64 " %%.", capture_ratio);
- return SR_ERR_ARG;
- }
-
- sr_info("Capture ratio set to %" PRIu64 " %%.", capture_ratio);
-
- devc->capture_ratio = capture_ratio;
-
- return SR_OK;
-}
-
SR_PRIV int sl2_set_after_trigger_delay(const struct sr_dev_inst *sdi,
uint64_t after_trigger_delay)
{
if (sr_usb_open(drvc->sr_ctx->libusb_ctx, &usb) != SR_OK)
return SR_ERR;
- /*
- * Determine if a kernel driver is active on this interface and, if so,
- * detach it.
- */
if (libusb_kernel_driver_active(usb.devhdl, USB_INTERFACE) == 1) {
ret = libusb_detach_kernel_driver(usb.devhdl,
USB_INTERFACE);
#define LOG_PREFIX "ikalogic-scanalogic2"
-#define VENDOR_NAME "IKALOGIC"
-#define MODEL_NAME "Scanalogic-2"
-
#define USB_VID_PID "20a0.4123"
#define USB_INTERFACE 0
#define USB_TIMEOUT_MS (5 * 1000)
STATE_WAIT_DEVICE_READY
};
-/** Private, per-device-instance driver context. */
struct dev_context {
/* Current selected samplerate. */
uint64_t samplerate;
uint8_t trigger_channel;
uint8_t trigger_type;
- unsigned int capture_ratio;
+ uint64_t capture_ratio;
/* Time that the trigger will be delayed in milliseconds. */
uint16_t after_trigger_delay;
SR_PRIV int sl2_set_limit_samples(const struct sr_dev_inst *sdi,
uint64_t limit_samples);
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,
uint64_t after_trigger_delay);
SR_PRIV void sl2_calculate_trigger_samples(const struct sr_dev_inst *sdi);
#define USB_VENDOR_ID 0x0403
#define USB_DEVICE_ID 0x6014
-#define USB_VENDOR_NAME "IKALOGIC"
-#define USB_MODEL_NAME "ScanaPLUS"
#define USB_IPRODUCT "SCANAPLUS"
#define SAMPLE_BUF_SIZE (8 * 1024 * 1024)
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
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",
};
/* Note: The IKALOGIC ScanaPLUS always samples at 100MHz. */
static const uint64_t samplerates[1] = { SR_MHZ(100) };
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
-
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
-
ftdi_free(devc->ftdic);
g_free(devc->compressed_buf);
g_free(devc->sample_buf);
- g_free(devc);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static GSList *scan(struct sr_dev_driver *di, GSList *options)
(void)options;
- /* Allocate memory for our private device context. */
devc = g_malloc0(sizeof(struct dev_context));
/* Allocate memory for the incoming compressed samples. */
goto err_free_compressed_buf;
}
- /* Allocate memory for the FTDI context (ftdic) and initialize it. */
if (!(devc->ftdic = ftdi_new())) {
sr_err("Failed to initialize libftdi.");
goto err_free_sample_buf;
}
- /* 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) {
goto err_free_ftdic;
}
- /* Register the device with libsigrok. */
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(USB_VENDOR_NAME);
- sdi->model = g_strdup(USB_MODEL_NAME);
+ sdi->vendor = g_strdup("IKALOGIC");
+ sdi->model = g_strdup("ScanaPLUS");
sdi->priv = devc;
for (i = 0; i < ARRAY_SIZE(channel_names); i++)
sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_names[i]);
- /* Close device. We'll reopen it again when we need it. */
scanaplus_close(devc);
return std_scan_complete(di, g_slist_append(NULL, sdi));
scanaplus_close(devc);
err_free_ftdic:
- ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
+ ftdi_free(devc->ftdic);
err_free_sample_buf:
g_free(devc->sample_buf);
err_free_compressed_buf:
devc = sdi->priv;
- /* Select interface A, otherwise communication will fail. */
ret = ftdi_set_interface(devc->ftdic, INTERFACE_A);
if (ret < 0) {
sr_err("Failed to set FTDI interface A (%d): %s", ret,
ftdi_get_error_string(devc->ftdic));
return SR_ERR;
}
- sr_dbg("FTDI chip interface A set successfully.");
- /* Open the device. */
ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID, USB_DEVICE_ID,
USB_IPRODUCT, NULL);
if (ret < 0) {
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_dev_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_dev_open_close_ftdic;
}
- sr_dbg("FTDI chip bitmode reset successfully.");
- /* Set FTDI bitmode to "sync FIFO". */
ret = ftdi_set_bitmode(devc->ftdic, 0xff, BITMODE_SYNCFF);
if (ret < 0) {
sr_err("Failed to put FTDI chip into sync FIFO mode (%d): %s.",
ret, ftdi_get_error_string(devc->ftdic));
goto err_dev_open_close_ftdic;
}
- sr_dbg("FTDI chip sync FIFO mode entered successfully.");
- /* Set the FTDI latency timer to 2. */
ret = ftdi_set_latency_timer(devc->ftdic, 2);
if (ret < 0) {
sr_err("Failed to set FTDI latency timer (%d): %s.",
ret, ftdi_get_error_string(devc->ftdic));
goto err_dev_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_dev_open_close_ftdic;
}
- sr_dbg("FTDI chip read data chunk size set successfully.");
/* Get the ScanaPLUS device ID from the FTDI EEPROM. */
if ((ret = scanaplus_get_device_id(devc)) < 0) {
sr_dbg("Received ScanaPLUS device ID successfully: %02x %02x %02x.",
devc->devid[0], devc->devid[1], devc->devid[2]);
- sdi->status = SR_ST_ACTIVE;
-
return SR_OK;
err_dev_open_close_ftdic:
scanaplus_close(devc);
+
return SR_ERR;
}
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 = scanaplus_close(devc);
- } else {
- sr_spew("Status not ACTIVE, nothing to do.");
- }
-
- sdi->status = SR_ST_INACTIVE;
-
- return ret;
+ return scanaplus_close(devc);
}
-static int config_get(uint32_t 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)
{
(void)sdi;
(void)cg;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
switch (key) {
/* Nothing to do, the ScanaPLUS samplerate is always 100MHz. */
break;
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;
default:
return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
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);
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
break;
default:
return SR_ERR_NA;
int ret;
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
if (!devc->ftdic)
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- sr_dbg("Stopping acquisition.");
sr_session_source_remove(sdi->session, -1);
std_session_send_df_end(sdi);
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);
+ sr_dev_acquisition_stop(sdi);
return FALSE;
}
if (bytes_read == 0) {
if (devc->limit_samples && (n >= devc->limit_samples)) {
send_samples(sdi, devc->limit_samples - devc->samples_sent);
sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
} else if (devc->limit_msec && (n >= max)) {
send_samples(sdi, max - devc->samples_sent);
sr_info("Requested time limit reached.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
} else {
send_samples(sdi, devc->bytes_received / 2);
#define COMPRESSED_BUF_SIZE (64 * 1024)
-/* Private, per-device-instance driver context. */
struct dev_context {
- /** FTDI device context (used by libftdi). */
struct ftdi_context *ftdic;
-
- /** The current sampling limit (in ms). */
uint64_t limit_msec;
-
- /** The current sampling limit (in number of samples). */
uint64_t limit_samples;
uint8_t *compressed_buf;
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Eva Kissling <eva.kissling@bluewin.ch>
+ *
+ * This program is free software: you can 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[] = {
+ SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+ SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const int32_t trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+ SR_TRIGGER_EDGE,
+};
+
+SR_PRIV struct sr_dev_driver ipdbg_la_driver_info;
+
+static void ipdbg_la_split_addr_port(const char *conn, char **addr,
+ char **port)
+{
+ char **strs = g_strsplit(conn, "/", 3);
+
+ *addr = g_strdup(strs[1]);
+ *port = g_strdup(strs[2]);
+
+ g_strfreev(strs);
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ struct drv_context *drvc;
+ GSList *devices;
+
+ devices = NULL;
+ drvc = di->context;
+ drvc->instances = NULL;
+ const char *conn;
+ struct sr_config *src;
+ GSList *l;
+
+ 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;
+
+ struct ipdbg_la_tcp *tcp = ipdbg_la_tcp_new();
+
+ ipdbg_la_split_addr_port(conn, &tcp->address, &tcp->port);
+
+ if (!tcp->address)
+ return NULL;
+
+ if (ipdbg_la_tcp_open(tcp) != SR_OK)
+ return NULL;
+
+ ipdbg_la_send_reset(tcp);
+ ipdbg_la_send_reset(tcp);
+
+ if (ipdbg_la_request_id(tcp) != SR_OK)
+ return NULL;
+
+ struct sr_dev_inst *sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->status = SR_ST_INACTIVE;
+ sdi->vendor = g_strdup("ipdbg.org");
+ sdi->model = g_strdup("IPDBG LA");
+ sdi->version = g_strdup("v1.0");
+ sdi->driver = di;
+
+ struct dev_context *devc = ipdbg_la_dev_new();
+ sdi->priv = devc;
+
+ ipdbg_la_get_addrwidth_and_datawidth(tcp, devc);
+
+ sr_dbg("addr_width = %d, data_width = %d\n", devc->addr_width,
+ devc->data_width);
+ sr_dbg("limit samples = %" PRIu64 "\n", devc->limit_samples_max);
+
+ for (uint32_t i = 0; i < devc->data_width; i++) {
+ char *name = g_strdup_printf("CH%d", i);
+ sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
+ g_free(name);
+ }
+
+ sdi->inst_type = SR_INST_USER;
+ sdi->conn = tcp;
+
+ ipdbg_la_tcp_close(tcp);
+
+ devices = g_slist_append(devices, sdi);
+
+ return std_scan_complete(di, devices);
+}
+
+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;
+ struct ipdbg_la_tcp *tcp = sdi->conn;
+ if (tcp) {
+ ipdbg_la_tcp_close(tcp);
+ ipdbg_la_tcp_free(tcp);
+ g_free(tcp);
+ }
+ sdi->conn = NULL;
+ }
+ }
+
+ return std_dev_clear(di);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct ipdbg_la_tcp *tcp = sdi->conn;
+
+ if (!tcp)
+ return SR_ERR;
+
+ if (ipdbg_la_tcp_open(tcp) != SR_OK)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ /* Should be called before a new call to scan(). */
+ struct ipdbg_la_tcp *tcp = sdi->conn;
+
+ if (tcp)
+ ipdbg_la_tcp_close(tcp);
+
+ sdi->conn = 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_CAPTURE_RATIO:
+ *data = g_variant_new_uint64(devc->capture_ratio);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ *data = g_variant_new_uint64(devc->limit_samples);
+ 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;
+
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_CAPTURE_RATIO:
+ devc->capture_ratio = 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)
+{
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_TRIGGER_MATCH:
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ struct ipdbg_la_tcp *tcp = sdi->conn;
+ struct dev_context *devc = sdi->priv;
+
+ ipdbg_la_convert_trigger(sdi);
+ ipdbg_la_send_trigger(devc, tcp);
+ ipdbg_la_send_delay(devc, tcp);
+
+ /* 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.
+ */
+ sr_session_source_add(sdi->session, tcp->socket, G_IO_IN, 100,
+ ipdbg_la_receive_data, (struct sr_dev_inst *)sdi);
+
+ ipdbg_la_send_start(tcp);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ struct ipdbg_la_tcp *tcp = sdi->conn;
+ struct dev_context *devc = sdi->priv;
+
+ uint8_t byte;
+
+ if (devc->num_transfers > 0) {
+ while (devc->num_transfers <
+ (devc->limit_samples_max * devc->data_width_bytes)) {
+ ipdbg_la_tcp_receive(tcp, &byte);
+ devc->num_transfers++;
+ }
+ }
+
+ ipdbg_la_send_reset(tcp);
+ ipdbg_la_abort_acquisition(sdi);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver ipdbg_la_driver_info = {
+ .name = "ipdbg-la",
+ .longname = "IPDBG LA",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
+
+SR_REGISTER_DEV_DRIVER(ipdbg_la_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Eva Kissling <eva.kissling@bluewin.ch>
+ *
+ * This program is free software: you can 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>
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "protocol.h"
+
+#define BUFFER_SIZE 4
+
+/* Top-level command opcodes */
+#define CMD_SET_TRIGGER 0x00
+#define CMD_CFG_TRIGGER 0xF0
+#define CMD_CFG_LA 0x0F
+#define CMD_START 0xFE
+#define CMD_RESET 0xEE
+
+#define CMD_GET_BUS_WIDTHS 0xAA
+#define CMD_GET_LA_ID 0xBB
+#define CMD_ESCAPE 0x55
+
+/* Trigger subfunction command opcodes */
+#define CMD_TRIG_MASKS 0xF1
+#define CMD_TRIG_MASK 0xF3
+#define CMD_TRIG_VALUE 0xF7
+
+#define CMD_TRIG_MASKS_LAST 0xF9
+#define CMD_TRIG_MASK_LAST 0xFB
+#define CMD_TRIG_VALUE_LAST 0xFF
+
+#define CMD_TRIG_SELECT_EDGE_MASK 0xF5
+#define CMD_TRIG_SET_EDGE_MASK 0xF6
+
+/* LA subfunction command opcodes */
+#define CMD_LA_DELAY 0x1F
+
+static gboolean data_available(struct ipdbg_la_tcp *tcp)
+{
+#ifdef _WIN32
+ u_long bytes_available;
+ ioctlsocket(tcp->socket, FIONREAD, &bytes_available);
+ return (bytes_available > 0);
+#else
+ int status;
+
+ if (ioctl(tcp->socket, FIONREAD, &status) < 0) { // TIOCMGET
+ sr_err("FIONREAD failed: %s\n", g_strerror(errno));
+ return FALSE;
+ }
+
+ return (status < 1) ? FALSE : TRUE;
+#endif
+}
+
+SR_PRIV struct ipdbg_la_tcp *ipdbg_la_tcp_new(void)
+{
+ struct ipdbg_la_tcp *tcp;
+
+ tcp = g_malloc0(sizeof(struct ipdbg_la_tcp));
+ tcp->address = NULL;
+ tcp->port = NULL;
+ tcp->socket = -1;
+
+ return tcp;
+}
+
+SR_PRIV void ipdbg_la_tcp_free(struct ipdbg_la_tcp *tcp)
+{
+ g_free(tcp->address);
+ g_free(tcp->port);
+}
+
+SR_PRIV int ipdbg_la_tcp_open(struct ipdbg_la_tcp *tcp)
+{
+ struct addrinfo hints;
+ struct addrinfo *results, *res;
+ int err;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ err = getaddrinfo(tcp->address, tcp->port, &hints, &results);
+
+ if (err) {
+ sr_err("Address lookup failed: %s:%s: %s", tcp->address,
+ tcp->port, gai_strerror(err));
+ return SR_ERR;
+ }
+
+ for (res = results; res; res = res->ai_next) {
+ if ((tcp->socket = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ continue;
+ if (connect(tcp->socket, res->ai_addr, res->ai_addrlen) != 0) {
+ close(tcp->socket);
+ tcp->socket = -1;
+ continue;
+ }
+ break;
+ }
+
+ freeaddrinfo(results);
+
+ if (tcp->socket < 0) {
+ sr_err("Failed to connect to %s:%s: %s", tcp->address, tcp->port,
+ g_strerror(errno));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_tcp_close(struct ipdbg_la_tcp *tcp)
+{
+ int ret = SR_OK;
+
+ if (close(tcp->socket) < 0)
+ ret = SR_ERR;
+
+ tcp->socket = -1;
+
+ return ret;
+}
+
+static int tcp_send(struct ipdbg_la_tcp *tcp, const uint8_t *buf, size_t len)
+{
+ int out;
+ out = send(tcp->socket, (const char *)buf, len, 0);
+
+ if (out < 0) {
+ sr_err("Send error: %s", g_strerror(errno));
+ return SR_ERR;
+ }
+
+ if (out < (int)len)
+ sr_dbg("Only sent %d/%d bytes of data.", out, (int)len);
+
+ return SR_OK;
+}
+
+static int tcp_receive_blocking(struct ipdbg_la_tcp *tcp,
+ uint8_t *buf, int bufsize)
+{
+ int received = 0;
+ int error_count = 0;
+
+ /* Timeout after 500ms of not receiving data */
+ while ((received < bufsize) && (error_count < 500)) {
+ if (ipdbg_la_tcp_receive(tcp, buf) > 0) {
+ buf++;
+ received++;
+ } else {
+ error_count++;
+ g_usleep(1000); /* Sleep for 1ms */
+ }
+ }
+
+ return received;
+}
+
+SR_PRIV int ipdbg_la_tcp_receive(struct ipdbg_la_tcp *tcp,
+ uint8_t *buf)
+{
+ int received = 0;
+
+ if (data_available(tcp)) {
+ while (received < 1) {
+ int len = recv(tcp->socket, (char *)buf, 1, 0);
+
+ if (len < 0) {
+ sr_err("Receive error: %s", g_strerror(errno));
+ return SR_ERR;
+ } else
+ received += len;
+ }
+
+ return received;
+ } else
+ return -1;
+}
+
+SR_PRIV int ipdbg_la_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;
+
+ devc = sdi->priv;
+
+ devc->num_stages = 0;
+ devc->num_transfers = 0;
+ devc->raw_sample_buf = NULL;
+
+ for (uint64_t i = 0; i < devc->data_width_bytes; i++) {
+ devc->trigger_mask[i] = 0;
+ devc->trigger_value[i] = 0;
+ devc->trigger_mask_last[i] = 0;
+ devc->trigger_value_last[i] = 0;
+ devc->trigger_edge_mask[i] = 0;
+ }
+
+ 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;
+ int byte_idx = match->channel->index / 8;
+ uint8_t match_bit = 1 << (match->channel->index % 8);
+
+ if (!match->channel->enabled)
+ /* Ignore disabled channels with a trigger. */
+ continue;
+
+ if (match->match == SR_TRIGGER_ONE) {
+ devc->trigger_value[byte_idx] |= match_bit;
+ devc->trigger_mask[byte_idx] |= match_bit;
+ devc->trigger_mask_last[byte_idx] &= ~match_bit;
+ devc->trigger_edge_mask[byte_idx] &= ~match_bit;
+ } else if (match->match == SR_TRIGGER_ZERO) {
+ devc->trigger_value[byte_idx] &= ~match_bit;
+ devc->trigger_mask[byte_idx] |= match_bit;
+ devc->trigger_mask_last[byte_idx] &= ~match_bit;
+ devc->trigger_edge_mask[byte_idx] &= ~match_bit;
+ } else if (match->match == SR_TRIGGER_RISING) {
+ devc->trigger_value[byte_idx] |= match_bit;
+ devc->trigger_value_last[byte_idx] &=
+ ~match_bit;
+ devc->trigger_mask[byte_idx] |= match_bit;
+ devc->trigger_mask_last[byte_idx] |= match_bit;
+ devc->trigger_edge_mask[byte_idx] &= ~match_bit;
+ } else if (match->match == SR_TRIGGER_FALLING) {
+ devc->trigger_value[byte_idx] &= ~match_bit;
+ devc->trigger_value_last[byte_idx] |= match_bit;
+ devc->trigger_mask[byte_idx] |= match_bit;
+ devc->trigger_mask_last[byte_idx] |= match_bit;
+ devc->trigger_edge_mask[byte_idx] &= ~match_bit;
+ } else if (match->match == SR_TRIGGER_EDGE) {
+ devc->trigger_mask[byte_idx] &= ~match_bit;
+ devc->trigger_mask_last[byte_idx] &= ~match_bit;
+ devc->trigger_edge_mask[byte_idx] |= match_bit;
+ }
+ }
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_receive_data(int fd, int revents, void *cb_data)
+{
+ const struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ if (!sdi)
+ return FALSE;
+
+ if (!(devc = sdi->priv))
+ return FALSE;
+
+ struct ipdbg_la_tcp *tcp = sdi->conn;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+
+ if (!devc->raw_sample_buf) {
+ devc->raw_sample_buf =
+ g_try_malloc(devc->limit_samples * devc->data_width_bytes);
+ if (!devc->raw_sample_buf) {
+ sr_err("Sample buffer malloc failed.");
+ return FALSE;
+ }
+ }
+
+ if (devc->num_transfers <
+ (devc->limit_samples_max * devc->data_width_bytes)) {
+ uint8_t byte;
+
+ if (ipdbg_la_tcp_receive(tcp, &byte) == 1) {
+ if (devc->num_transfers <
+ (devc->limit_samples * devc->data_width_bytes))
+ devc->raw_sample_buf[devc->num_transfers] = byte;
+
+ devc->num_transfers++;
+ }
+ } else {
+ if (devc->delay_value > 0) {
+ /* There are pre-trigger samples, send those first. */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = devc->delay_value * devc->data_width_bytes;
+ logic.unitsize = devc->data_width_bytes;
+ logic.data = devc->raw_sample_buf;
+ 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->limit_samples - devc->delay_value) *
+ devc->data_width_bytes;
+ logic.unitsize = devc->data_width_bytes;
+ logic.data = devc->raw_sample_buf +
+ (devc->delay_value * devc->data_width_bytes);
+ sr_session_send(cb_data, &packet);
+
+ g_free(devc->raw_sample_buf);
+ devc->raw_sample_buf = NULL;
+
+ ipdbg_la_abort_acquisition(sdi);
+ }
+
+ return TRUE;
+}
+
+static int send_escaping(struct ipdbg_la_tcp *tcp, uint8_t *data_to_send,
+ uint32_t length)
+{
+ uint8_t escape = CMD_ESCAPE;
+
+ while (length--) {
+ uint8_t payload = *data_to_send++;
+
+ if (payload == CMD_RESET)
+ if (tcp_send(tcp, &escape, 1) != SR_OK)
+ sr_warn("Couldn't send escape");
+
+ if (payload == CMD_ESCAPE)
+ if (tcp_send(tcp, &escape, 1) != SR_OK)
+ sr_warn("Couldn't send escape");
+
+ if (tcp_send(tcp, &payload, 1) != SR_OK)
+ sr_warn("Couldn't send data");
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_send_delay(struct dev_context *devc,
+ struct ipdbg_la_tcp *tcp)
+{
+ devc->delay_value = (devc->limit_samples / 100.0) * devc->capture_ratio;
+
+ uint8_t buf;
+ buf = CMD_CFG_LA;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_LA_DELAY;
+ tcp_send(tcp, &buf, 1);
+
+ uint8_t delay_buf[4] = { devc->delay_value & 0x000000ff,
+ (devc->delay_value >> 8) & 0x000000ff,
+ (devc->delay_value >> 16) & 0x000000ff,
+ (devc->delay_value >> 24) & 0x000000ff
+ };
+
+ for (uint64_t i = 0; i < devc->addr_width_bytes; i++)
+ send_escaping(tcp, &(delay_buf[devc->addr_width_bytes - 1 - i]), 1);
+
+ return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_send_trigger(struct dev_context *devc,
+ struct ipdbg_la_tcp *tcp)
+{
+ uint8_t buf;
+
+ /* Mask */
+ buf = CMD_CFG_TRIGGER;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_MASKS;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_MASK;
+ tcp_send(tcp, &buf, 1);
+
+ for (size_t i = 0; i < devc->data_width_bytes; i++)
+ send_escaping(tcp,
+ devc->trigger_mask + devc->data_width_bytes - 1 - i, 1);
+
+ /* Value */
+ buf = CMD_CFG_TRIGGER;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_MASKS;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_VALUE;
+ tcp_send(tcp, &buf, 1);
+
+ for (size_t i = 0; i < devc->data_width_bytes; i++)
+ send_escaping(tcp,
+ devc->trigger_value + devc->data_width_bytes - 1 - i, 1);
+
+ /* Mask_last */
+ buf = CMD_CFG_TRIGGER;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_MASKS_LAST;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_MASK_LAST;
+ tcp_send(tcp, &buf, 1);
+
+ for (size_t i = 0; i < devc->data_width_bytes; i++)
+ send_escaping(tcp,
+ devc->trigger_mask_last + devc->data_width_bytes - 1 - i, 1);
+
+ /* Value_last */
+ buf = CMD_CFG_TRIGGER;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_MASKS_LAST;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_VALUE_LAST;
+ tcp_send(tcp, &buf, 1);
+
+ for (size_t i = 0; i < devc->data_width_bytes; i++)
+ send_escaping(tcp,
+ devc->trigger_value_last + devc->data_width_bytes - 1 - i, 1);
+
+ /* Edge_mask */
+ buf = CMD_CFG_TRIGGER;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_SELECT_EDGE_MASK;
+ tcp_send(tcp, &buf, 1);
+ buf = CMD_TRIG_SET_EDGE_MASK;
+ tcp_send(tcp, &buf, 1);
+
+ for (size_t i = 0; i < devc->data_width_bytes; i++)
+ send_escaping(tcp,
+ devc->trigger_edge_mask + devc->data_width_bytes - 1 - i, 1);
+
+ return SR_OK;
+}
+
+SR_PRIV void ipdbg_la_get_addrwidth_and_datawidth(
+ struct ipdbg_la_tcp *tcp, struct dev_context *devc)
+{
+ uint8_t buf[8];
+ uint8_t read_cmd = CMD_GET_BUS_WIDTHS;
+
+ if (tcp_send(tcp, &read_cmd, 1) != SR_OK)
+ sr_warn("Can't send read command");
+
+ if (tcp_receive_blocking(tcp, buf, 8) != 8)
+ sr_warn("Can't get address and data width from device");
+
+ devc->data_width = buf[0] & 0x000000FF;
+ devc->data_width |= (buf[1] << 8) & 0x0000FF00;
+ devc->data_width |= (buf[2] << 16) & 0x00FF0000;
+ devc->data_width |= (buf[3] << 24) & 0xFF000000;
+
+ devc->addr_width = buf[4] & 0x000000FF;
+ devc->addr_width |= (buf[5] << 8) & 0x0000FF00;
+ devc->addr_width |= (buf[6] << 16) & 0x00FF0000;
+ devc->addr_width |= (buf[7] << 24) & 0xFF000000;
+
+ const uint8_t host_word_size = 8;
+
+ devc->data_width_bytes =
+ (devc->data_width + host_word_size - 1) / host_word_size;
+ devc->addr_width_bytes =
+ (devc->addr_width + host_word_size - 1) / host_word_size;
+
+ devc->limit_samples_max = (0x01 << devc->addr_width);
+ devc->limit_samples = devc->limit_samples_max;
+
+ devc->trigger_mask = g_malloc0(devc->data_width_bytes);
+ devc->trigger_value = g_malloc0(devc->data_width_bytes);
+ devc->trigger_mask_last = g_malloc0(devc->data_width_bytes);
+ devc->trigger_value_last = g_malloc0(devc->data_width_bytes);
+ devc->trigger_edge_mask = g_malloc0(devc->data_width_bytes);
+}
+
+SR_PRIV struct dev_context *ipdbg_la_dev_new(void)
+{
+ struct dev_context *devc;
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ devc->capture_ratio = 50;
+
+ return devc;
+}
+
+SR_PRIV int ipdbg_la_send_reset(struct ipdbg_la_tcp *tcp)
+{
+ uint8_t buf = CMD_RESET;
+ if (tcp_send(tcp, &buf, 1) != SR_OK)
+ sr_warn("Couldn't send reset");
+
+ return SR_OK;
+}
+
+SR_PRIV int ipdbg_la_request_id(struct ipdbg_la_tcp *tcp)
+{
+ uint8_t buf = CMD_GET_LA_ID;
+ if (tcp_send(tcp, &buf, 1) != SR_OK)
+ sr_warn("Couldn't send ID request");
+
+ char id[4];
+ if (tcp_receive_blocking(tcp, (uint8_t *)id, 4) != 4) {
+ sr_err("Couldn't read device ID");
+ return SR_ERR;
+ }
+
+ if (strncmp(id, "IDBG", 4)) {
+ sr_err("Invalid device ID: expected 'IDBG', got '%c%c%c%c'.",
+ id[0], id[1], id[2], id[3]);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV void ipdbg_la_abort_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct ipdbg_la_tcp *tcp = sdi->conn;
+
+ sr_session_source_remove(sdi->session, tcp->socket);
+
+ std_session_send_df_end(sdi);
+}
+
+SR_PRIV int ipdbg_la_send_start(struct ipdbg_la_tcp *tcp)
+{
+ uint8_t buf = CMD_START;
+
+ if (tcp_send(tcp, &buf, 1) != SR_OK)
+ sr_warn("Couldn't send start");
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 Eva Kissling <eva.kissling@bluewin.ch>
+ *
+ * This program is free software: you can 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_IPDBG_LA_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_IPDBG_LA_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "ipdbg-la"
+
+struct ipdbg_la_tcp {
+ char *address;
+ char *port;
+ int socket;
+};
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ uint32_t data_width;
+ uint32_t data_width_bytes;
+ uint32_t addr_width;
+ uint32_t addr_width_bytes;
+
+ uint64_t limit_samples;
+ uint64_t limit_samples_max;
+ uint8_t capture_ratio;
+ uint8_t *trigger_mask;
+ uint8_t *trigger_value;
+ uint8_t *trigger_mask_last;
+ uint8_t *trigger_value_last;
+ uint8_t *trigger_edge_mask;
+ uint64_t delay_value;
+ int num_stages;
+ uint64_t num_transfers;
+ uint8_t *raw_sample_buf;
+};
+
+SR_PRIV struct ipdbg_la_tcp *ipdbg_la_tcp_new(void);
+SR_PRIV void ipdbg_la_tcp_free(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_tcp_open(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_tcp_close(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_tcp_receive(struct ipdbg_la_tcp *tcp, uint8_t *buf);
+
+SR_PRIV int ipdbg_la_convert_trigger(const struct sr_dev_inst *sdi);
+
+SR_PRIV struct dev_context *ipdbg_la_dev_new(void);
+SR_PRIV void ipdbg_la_get_addrwidth_and_datawidth(
+ struct ipdbg_la_tcp *tcp, struct dev_context *devc);
+SR_PRIV int ipdbg_la_send_reset(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_request_id(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_send_start(struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_send_trigger(struct dev_context *devc,
+ struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_send_delay(struct dev_context *devc,
+ struct ipdbg_la_tcp *tcp);
+SR_PRIV int ipdbg_la_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV void ipdbg_la_abort_acquisition(const struct sr_dev_inst *sdi);
+
+#endif
#include "protocol.h"
#define USB_CONN "1041.8101"
-#define VENDOR "Kecheng"
#define USB_INTERFACE 0
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_SOUNDLEVELMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_SAMPLE_INTERVAL | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
static const char *weight_freq[] = {
- "A",
- "C",
+ "A", "C",
};
static const char *weight_time[] = {
- "F",
- "S",
+ "F", "S",
};
static const char *data_sources[] = {
- "Live",
- "Memory",
+ "Live", "Memory",
};
static int scan_kecheng(struct sr_dev_driver *di,
continue;
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(VENDOR);
+ sdi->vendor = g_strdup("Kecheng");
sdi->model = model; /* Already g_strndup()'d. */
sdi->inst_type = SR_INST_USB;
sdi->conn = l->data;
sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
- return ret;
+ return SR_OK;
}
static int dev_close(struct sr_dev_inst *sdi)
usb = sdi->conn;
if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
+ return SR_ERR_BUG;
/* This allows a frontend to configure the device without ever
* doing an acquisition step. */
libusb_release_interface(usb->devhdl, USB_INTERFACE);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
- GVariant *rational[2];
const uint64_t *si;
(void)cg;
break;
case SR_CONF_SAMPLE_INTERVAL:
si = kecheng_kc_330b_sample_intervals[devc->sample_interval];
- rational[0] = g_variant_new_uint64(si[0]);
- rational[1] = g_variant_new_uint64(si[1]);
- *data = g_variant_new_tuple(rational, 2);
+ *data = std_gvar_tuple_u64(si[0], si[1]);
break;
case SR_CONF_DATALOG:
/* There really isn't a way to be sure the device is logging. */
return SR_ERR_NA;
- break;
case SR_CONF_SPL_WEIGHT_FREQ:
if (devc->mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A)
*data = g_variant_new_string("A");
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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 p, q;
- unsigned int i;
- int tmp, ret;
- const char *tmp_str;
+ int idx;
(void)cg;
- 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);
- sr_dbg("Setting sample limit to %" PRIu64 ".",
- devc->limit_samples);
break;
case SR_CONF_SAMPLE_INTERVAL:
- g_variant_get(data, "(tt)", &p, &q);
- for (i = 0; i < ARRAY_SIZE(kecheng_kc_330b_sample_intervals); i++) {
- if (kecheng_kc_330b_sample_intervals[i][0] != p || kecheng_kc_330b_sample_intervals[i][1] != q)
- continue;
- devc->sample_interval = i;
- devc->config_dirty = TRUE;
- break;
- }
- if (i == ARRAY_SIZE(kecheng_kc_330b_sample_intervals))
- ret = SR_ERR_ARG;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(kecheng_kc_330b_sample_intervals))) < 0)
+ return SR_ERR_ARG;
+ devc->sample_interval = idx;
+ devc->config_dirty = TRUE;
break;
case SR_CONF_SPL_WEIGHT_FREQ:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "A"))
- tmp = SR_MQFLAG_SPL_FREQ_WEIGHT_A;
- else if (!strcmp(tmp_str, "C"))
- tmp = SR_MQFLAG_SPL_FREQ_WEIGHT_C;
- else
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_freq))) < 0)
return SR_ERR_ARG;
devc->mqflags &= ~(SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
- devc->mqflags |= tmp;
+ devc->mqflags |= (weight_freq[idx][0] == 'A') ? SR_MQFLAG_SPL_FREQ_WEIGHT_A : SR_MQFLAG_SPL_FREQ_WEIGHT_C;
devc->config_dirty = TRUE;
break;
case SR_CONF_SPL_WEIGHT_TIME:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "F"))
- tmp = SR_MQFLAG_SPL_TIME_WEIGHT_F;
- else if (!strcmp(tmp_str, "S"))
- tmp = SR_MQFLAG_SPL_TIME_WEIGHT_S;
- else
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_time))) < 0)
return SR_ERR_ARG;
devc->mqflags &= ~(SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
- devc->mqflags |= tmp;
+ devc->mqflags |= (weight_time[idx][0] == 'F') ? SR_MQFLAG_SPL_TIME_WEIGHT_F : SR_MQFLAG_SPL_TIME_WEIGHT_S;
devc->config_dirty = TRUE;
break;
case SR_CONF_DATA_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "Live"))
- devc->data_source = DATA_SOURCE_LIVE;
- else if (!strcmp(tmp_str, "Memory"))
- devc->data_source = DATA_SOURCE_MEMORY;
- else
- return SR_ERR;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->data_source = idx;
devc->config_dirty = TRUE;
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *tuple, *rational[2];
- GVariantBuilder gvb;
- unsigned int i;
-
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
case SR_CONF_SAMPLE_INTERVAL:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < ARRAY_SIZE(kecheng_kc_330b_sample_intervals); i++) {
- rational[0] = g_variant_new_uint64(kecheng_kc_330b_sample_intervals[i][0]);
- rational[1] = g_variant_new_uint64(kecheng_kc_330b_sample_intervals[i][1]);
- tuple = g_variant_new_tuple(rational, 2);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_tuple_array(ARRAY_AND_SIZE(kecheng_kc_330b_sample_intervals));
break;
case SR_CONF_SPL_WEIGHT_FREQ:
- *data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_freq));
break;
case SR_CONF_SPL_WEIGHT_TIME:
- *data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_time));
break;
case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
break;
default:
return SR_ERR_NA;
struct sr_datafeed_meta meta;
struct sr_config *src;
struct sr_usb_dev_inst *usb;
- GVariant *gvar, *rational[2];
+ GVariant *gvar;
const uint64_t *si;
int req_len, buf_len, len, ret;
unsigned char buf[9];
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
drvc = di->context;
devc = sdi->priv;
usb = sdi->conn;
devc->stored_samples = devc->limit_samples;
si = kecheng_kc_330b_sample_intervals[buf[1]];
- rational[0] = g_variant_new_uint64(si[0]);
- rational[1] = g_variant_new_uint64(si[1]);
- gvar = g_variant_new_tuple(rational, 2);
+ gvar = std_gvar_tuple_u64(si[0], si[1]);
+
src = sr_config_new(SR_CONF_SAMPLE_INTERVAL, gvar);
packet.type = SR_DF_META;
packet.payload = &meta;
{
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
/* Signal USB transfer handler to clean up and stop. */
sdi->status = SR_ST_STOPPING;
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
#include <config.h>
#include <string.h>
#include "protocol.h"
+
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)
if (ret != 0 || len != 1) {
sr_dbg("Failed to request new acquisition: %s",
libusb_error_name(ret));
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
libusb_submit_transfer(devc->xfer);
if (ret != 0 || len != 4) {
sr_dbg("Failed to request next chunk: %s",
libusb_error_name(ret));
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
libusb_submit_transfer(devc->xfer);
switch (transfer->status) {
case LIBUSB_TRANSFER_NO_DEVICE:
/* USB device was unplugged. */
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return;
case LIBUSB_TRANSFER_COMPLETED:
case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
send_data(sdi, fvalue, 1);
devc->num_samples++;
if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
} else {
/* let USB event handler fire off another
* request when the time is right. */
send_data(sdi, fvalue, 1);
devc->num_samples += num_samples;
if (devc->num_samples >= devc->stored_samples) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
} else {
/* let USB event handler fire off another
* request when the time is right. */
DEVICE_INACTIVE,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Acquisition settings */
uint64_t limit_samples;
int sample_interval;
int alarm_low;
enum sr_mqflag mqflags;
int data_source;
- /* Operational state */
int state;
gboolean config_dirty;
uint64_t num_samples;
struct libusb_transfer *xfer;
unsigned char buf[128];
- /* Temporary state across callbacks */
gint64 last_live_request;
-
};
SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data);
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_SCALE,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
serial_flush(serial);
sr_spew("Set O1 mode (continuous values, stable and unstable ones).");
- if (serial_write_nonblocking(serial, "O1\r\n", 4) != 4)
+ if (serial_write_blocking(serial, "O1\r\n", 4, 0) < 0)
goto scan_cleanup;
/* Device replies with "A00\r\n" (OK) or "E01\r\n" (Error). Ignore. */
return std_scan_complete(di, devices);
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
serial = sdi->conn;
sr_spew("Set O1 mode (continuous values, stable and unstable ones).");
- if (serial_write_nonblocking(serial, "O1\r\n", 4) != 4)
+ if (serial_write_blocking(serial, "O1\r\n", 4, 0) < 0)
return SR_ERR;
/* Device replies with "A00\r\n" (OK) or "E01\r\n" (Error). Ignore. */
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* 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);
.cleanup = std_cleanup, \
.scan = scan, \
.dev_list = std_dev_list, \
+ .dev_clear = std_dev_clear, \
.config_get = NULL, \
.config_set = config_set, \
.config_list = config_list, \
{
struct scale_info *scale;
struct dev_context *devc;
- int len, i, offset = 0;
+ int len, offset;
struct sr_serial_dev_inst *serial;
scale = (struct scale_info *)sdi->driver;
devc->buflen += len;
/* Now look for packets in that data. */
+ offset = 0;
while ((devc->buflen - offset) >= scale->packet_size) {
if (scale->packet_valid(devc->buf + offset)) {
handle_packet(devc->buf + offset, sdi, info);
}
/* 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];
+ if (offset < devc->buflen)
+ memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
devc->buflen -= offset;
}
}
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#define SCALE_BUFSIZE 256
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits limits;
* This file is part of the libsigrok project.
*
* Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae@gmail.com>
+ * Copyright (C) 2018 Frank Stettner <frank-stettner@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#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 */
+static const uint32_t drvopts[] = {
SR_CONF_POWER_SUPPLY,
- /* Acquisition modes. */
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
- /* 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,
/* 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}},
+ {KORAD_KD3005P, "Korad", "KD3005P",
+ "KORAD KD3005P V2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+ {KORAD_KD3005P_V20_NOSP, "Korad", "KD3005P",
+ "KORADKD3005PV2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+ {RND_320K30PV, "RND", "KA3005P",
+ "RND 320-KA3005P V2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+ {TENMA_72_2540_V20, "Tenma", "72-2540",
+ "TENMA72-2540V2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+ {TENMA_72_2540_V21, "Tenma", "72-2540",
+ "TENMA 72-2540 V2.1", 1, {0, 31, 0.01}, {0, 5, 0.001}},
ALL_ZERO
};
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->inst_type = SR_INST_SERIAL;
sdi->conn = serial;
- sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
+ sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I");
devc = g_malloc0(sizeof(struct dev_context));
sr_sw_limits_init(&devc->limits);
+ g_mutex_init(&devc->rw_mutex);
devc->model = &models[model_id];
- devc->reply[5] = 0;
devc->req_sent_at = 0;
sdi->priv = devc;
case SR_CONF_LIMIT_MSEC:
return sr_sw_limits_config_get(&devc->limits, key, data);
case SR_CONF_VOLTAGE:
+ korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_VOLTAGE, devc);
*data = g_variant_new_double(devc->voltage);
break;
case SR_CONF_VOLTAGE_TARGET:
+ korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_VOLTAGE_MAX, devc);
*data = g_variant_new_double(devc->voltage_max);
break;
case SR_CONF_CURRENT:
+ korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_CURRENT, devc);
*data = g_variant_new_double(devc->current);
break;
case SR_CONF_CURRENT_LIMIT:
+ korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_CURRENT_MAX, devc);
*data = g_variant_new_double(devc->current_max);
break;
case SR_CONF_ENABLED:
+ korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_OUTPUT, devc);
*data = g_variant_new_boolean(devc->output_enabled);
break;
case SR_CONF_REGULATION:
/* Dual channel not supported. */
+ korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_STATUS, devc);
*data = g_variant_new_string((devc->cc_mode[0]) ? "CC" : "CV");
break;
case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+ korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_OCP, devc);
*data = g_variant_new_boolean(devc->ocp_enabled);
break;
case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+ korad_kaxxxxp_get_value(sdi->conn, KAXXXXP_OVP, devc);
*data = g_variant_new_boolean(devc->ovp_enabled);
break;
default:
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
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)
+ if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_VOLTAGE_MAX, devc) < 0)
return SR_ERR;
break;
case SR_CONF_CURRENT_LIMIT:
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)
+ if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_CURRENT_MAX, 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)
+ if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_OUTPUT, 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)
+ if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_OCP, 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)
+ if (korad_kaxxxxp_set_value(sdi->conn, KAXXXXP_OVP, devc) < 0)
return SR_ERR;
break;
default:
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;
+ devc = (sdi) ? sdi->priv : NULL;
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ *data = std_gvar_min_max_step_array(devc->model->voltage);
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);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ *data = std_gvar_min_max_step_array(devc->model->current);
break;
default:
return SR_ERR_NA;
return SR_OK;
}
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ devc = (sdi) ? sdi->priv : NULL;
+ if (devc)
+ g_mutex_clear(&devc->rw_mutex);
+
+ return std_serial_dev_close(sdi);
+}
+
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- devc->reply_pending = FALSE;
devc->req_sent_at = 0;
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN,
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
.dev_open = std_serial_dev_open,
- .dev_close = std_serial_dev_close,
+ .dev_close = dev_close,
.dev_acquisition_start = dev_acquisition_start,
.dev_acquisition_stop = std_serial_dev_acquisition_stop,
.context = NULL,
* This file is part of the libsigrok project.
*
* Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae@gmail.com>
+ * Copyright (C) 2018 Frank Stettner <frank-stettner@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
}
SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial,
- struct dev_context *devc)
+ int target, struct dev_context *devc)
{
- char msg[21];
+ char *msg;
const char *cmd;
float value;
int ret;
+ g_mutex_lock(&devc->rw_mutex);
give_device_time_to_process(devc);
- msg[20] = 0;
- switch (devc->target) {
+ switch (target) {
case KAXXXXP_CURRENT:
case KAXXXXP_VOLTAGE:
case KAXXXXP_STATUS:
- sr_err("Can't set measurable parameter.");
+ sr_err("Can't set measurable parameter %d.", target);
+ g_mutex_unlock(&devc->rw_mutex);
return SR_ERR;
case KAXXXXP_CURRENT_MAX:
cmd = "ISET1:%05.3f";
if (devc->program < 1 || devc->program > 5) {
sr_err("Only programs 1-5 supported and %d isn't "
"between them.", devc->program);
+ g_mutex_unlock(&devc->rw_mutex);
return SR_ERR;
}
value = devc->program;
if (devc->program < 1 || devc->program > 5) {
sr_err("Only programs 1-5 supported and %d isn't "
"between them.", devc->program);
+ g_mutex_unlock(&devc->rw_mutex);
return SR_ERR;
}
value = devc->program;
break;
default:
- sr_err("Don't know how to set %d.", devc->target);
+ sr_err("Don't know how to set %d.", target);
+ g_mutex_unlock(&devc->rw_mutex);
return SR_ERR;
}
+ msg = g_malloc0(20 + 1);
if (cmd)
- snprintf(msg, 20, cmd, value);
+ sr_snprintf_ascii(msg, 20, cmd, value);
ret = korad_kaxxxxp_send_cmd(serial, msg);
devc->req_sent_at = g_get_monotonic_time();
- devc->reply_pending = FALSE;
+ g_free(msg);
+
+ g_mutex_unlock(&devc->rw_mutex);
return ret;
}
-SR_PRIV int korad_kaxxxxp_query_value(struct sr_serial_dev_inst *serial,
- struct dev_context *devc)
+SR_PRIV int korad_kaxxxxp_get_value(struct sr_serial_dev_inst *serial,
+ int target, struct dev_context *devc)
{
- int ret;
+ int ret, count;
+ char reply[6];
+ float *value;
+ char status_byte;
+ g_mutex_lock(&devc->rw_mutex);
give_device_time_to_process(devc);
- switch (devc->target) {
+ value = NULL;
+ count = 5;
+
+ switch (target) {
case KAXXXXP_CURRENT:
/* Read current from device. */
ret = korad_kaxxxxp_send_cmd(serial, "IOUT1?");
+ value = &(devc->current);
break;
case KAXXXXP_CURRENT_MAX:
/* Read set current from device. */
ret = korad_kaxxxxp_send_cmd(serial, "ISET1?");
+ value = &(devc->current_max);
break;
case KAXXXXP_VOLTAGE:
/* Read voltage from device. */
ret = korad_kaxxxxp_send_cmd(serial, "VOUT1?");
+ value = &(devc->voltage);
break;
case KAXXXXP_VOLTAGE_MAX:
/* Read set voltage from device. */
ret = korad_kaxxxxp_send_cmd(serial, "VSET1?");
+ value = &(devc->voltage_max);
break;
case KAXXXXP_STATUS:
case KAXXXXP_OUTPUT:
+ case KAXXXXP_OCP:
+ case KAXXXXP_OVP:
/* Read status from device. */
ret = korad_kaxxxxp_send_cmd(serial, "STATUS?");
+ count = 1;
break;
default:
- sr_err("Don't know how to query %d.", devc->target);
+ sr_err("Don't know how to query %d.", target);
+ g_mutex_unlock(&devc->rw_mutex);
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)
+ if ((ret = korad_kaxxxxp_read_chars(serial, count, reply)) < 0) {
+ g_mutex_unlock(&devc->rw_mutex);
return ret;
+ }
- devc->reply[count] = 0;
+ reply[count] = 0;
- if (target) {
- value = g_ascii_strtod(devc->reply, NULL);
- *target = (float)value;
- sr_dbg("value: %f",value);
+ if (value) {
+ sr_atof_ascii((const char *)&reply, value);
+ sr_dbg("value: %f", *value);
} else {
/* We have status reply. */
- status_byte = devc->reply[0];
+ status_byte = reply[0];
/* Constant current */
devc->cc_mode[0] = !(status_byte & (1 << 0)); /* Channel one */
devc->cc_mode[1] = !(status_byte & (1 << 1)); /* Channel two */
(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)
+ if (target == KAXXXXP_CURRENT_MAX)
serial_read_blocking(serial, &status_byte, 1, 10);
- devc->reply_pending = FALSE;
+
+ g_mutex_unlock(&devc->rw_mutex);
+
+ return ret;
+}
+
+SR_PRIV int korad_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
+ struct dev_context *devc)
+{
+ int ret, target;
+
+ for (target = KAXXXXP_CURRENT;
+ target <= KAXXXXP_STATUS; target++) {
+ if ((ret = korad_kaxxxxp_get_value(serial, target, devc)) < 0)
+ return ret;
+ }
return ret;
}
static void next_measurement(struct dev_context *devc)
{
- switch (devc->target) {
+ switch (devc->acquisition_target) {
case KAXXXXP_CURRENT:
- devc->target = KAXXXXP_VOLTAGE;
- break;
- case KAXXXXP_CURRENT_MAX:
- devc->target = KAXXXXP_CURRENT;
+ devc->acquisition_target = KAXXXXP_VOLTAGE;
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;
+ devc->acquisition_target = KAXXXXP_STATUS;
break;
case KAXXXXP_STATUS:
- devc->target = KAXXXXP_CURRENT;
+ devc->acquisition_target = KAXXXXP_CURRENT;
break;
default:
- devc->target = KAXXXXP_CURRENT;
+ devc->acquisition_target = KAXXXXP_CURRENT;
+ sr_err("Invalid target for next acquisition.");
}
}
struct sr_analog_encoding encoding;
struct sr_analog_meaning meaning;
struct sr_analog_spec spec;
- uint64_t elapsed_us;
+ GSList *l;
(void)fd;
+ (void)revents;
if (!(sdi = cb_data))
return TRUE;
serial = sdi->conn;
- if (revents == G_IO_IN) {
- /* Get the value. */
- korad_kaxxxxp_get_reply(serial, devc);
-
- /* Note: digits/spec_digits will be overridden later. */
- sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
-
- /* Send the value forward. */
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- analog.meaning->channels = sdi->channels;
- analog.num_samples = 1;
- if (devc->target == KAXXXXP_CURRENT) {
- analog.meaning->mq = SR_MQ_CURRENT;
- analog.meaning->unit = SR_UNIT_AMPERE;
- analog.meaning->mqflags = 0;
- analog.encoding->digits = 3;
- analog.spec->spec_digits = 3;
- analog.data = &devc->current;
- sr_session_send(sdi, &packet);
- }
- if (devc->target == KAXXXXP_VOLTAGE) {
- analog.meaning->mq = SR_MQ_VOLTAGE;
- analog.meaning->unit = SR_UNIT_VOLT;
- analog.meaning->mqflags = SR_MQFLAG_DC;
- analog.encoding->digits = 2;
- analog.spec->spec_digits = 2;
- analog.data = &devc->voltage;
- sr_session_send(sdi, &packet);
- sr_sw_limits_update_samples_read(&devc->limits, 1);
- }
- 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 (sr_sw_limits_check(&devc->limits)) {
- sdi->driver->dev_acquisition_stop(sdi);
- return TRUE;
+ /* Get the value. */
+ korad_kaxxxxp_get_value(serial, devc->acquisition_target, devc);
+
+ /* Note: digits/spec_digits will be overridden later. */
+ sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+
+ /* Send the value forward. */
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.num_samples = 1;
+ l = g_slist_copy(sdi->channels);
+ if (devc->acquisition_target == KAXXXXP_CURRENT) {
+ l = g_slist_remove_link(l, g_slist_nth(l, 0));
+ analog.meaning->channels = l;
+ analog.meaning->mq = SR_MQ_CURRENT;
+ analog.meaning->unit = SR_UNIT_AMPERE;
+ analog.meaning->mqflags = 0;
+ analog.encoding->digits = 3;
+ analog.spec->spec_digits = 3;
+ analog.data = &devc->current;
+ sr_session_send(sdi, &packet);
+ } else if (devc->acquisition_target == KAXXXXP_VOLTAGE) {
+ l = g_slist_remove_link(l, g_slist_nth(l, 1));
+ analog.meaning->channels = l;
+ analog.meaning->mq = SR_MQ_VOLTAGE;
+ analog.meaning->unit = SR_UNIT_VOLT;
+ analog.meaning->mqflags = SR_MQFLAG_DC;
+ analog.encoding->digits = 2;
+ analog.spec->spec_digits = 2;
+ analog.data = &devc->voltage;
+ sr_session_send(sdi, &packet);
+ sr_sw_limits_update_samples_read(&devc->limits, 1);
}
+ next_measurement(devc);
- /* 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;
- }
-
- }
+ if (sr_sw_limits_check(&devc->limits))
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
* This file is part of the libsigrok project.
*
* Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae@gmail.com>
+ * Copyright (C) 2018 Frank Stettner <frank-stettner@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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
VELLEMAN_LABPS3005D,
KORAD_KA3005P,
KORAD_KA3005P_0X01,
+ KORAD_KD3005P,
+ KORAD_KD3005P_V20_NOSP,
+ RND_320K30PV,
+ TENMA_72_2540_V20,
+ TENMA_72_2540_V21,
/* Support for future devices with this protocol. */
};
KAXXXXP_RECALL,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
const struct korad_kaxxxxp_model *model; /**< Model information. */
- /* Acquisition settings */
struct sr_sw_limits limits;
int64_t req_sent_at;
- gboolean reply_pending;
+ GMutex rw_mutex;
- /* 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. */
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 acquisition_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,
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);
+ int target, struct dev_context *devc);
+SR_PRIV int korad_kaxxxxp_get_value(struct sr_serial_dev_inst *serial,
+ int target, 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);
SR_CONF_CONN,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_THERMOMETER,
SR_CONF_HYGROMETER,
+};
+
+static const uint32_t devopts[] = {
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,
sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
- return ret;
+ return SR_OK;
}
static int dev_close(struct sr_dev_inst *sdi)
usb = sdi->conn;
if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
+ return SR_ERR_BUG;
libusb_release_interface(usb->devhdl, LASCAR_INTERFACE);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
int ret;
- char str[128];
(void)cg;
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);
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
break;
case SR_CONF_DATALOG:
if (!sdi)
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
- ret = SR_OK;
+
switch (key) {
case SR_CONF_DATALOG:
if (g_variant_get_boolean(data))
- ret = lascar_start_logging(sdi);
+ return lascar_start_logging(sdi);
else
- ret = lascar_stop_logging(sdi);
+ return lascar_stop_logging(sdi);
break;
case SR_CONF_LIMIT_SAMPLES:
devc->limit_samples = g_variant_get_uint64(data);
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static void LIBUSB_CALL mark_xfer(struct libusb_transfer *xfer)
int ret;
unsigned char cmd[3], resp[4], *buf;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
drvc = di->context;
devc = sdi->priv;
usb = sdi->conn;
return SR_OK;
}
-SR_PRIV int dev_acquisition_stop(struct sr_dev_inst *sdi)
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("Device inactive, can't stop acquisition.");
- return SR_ERR;
- }
-
sdi->status = SR_ST_STOPPING;
/* TODO: free ongoing transfers? */
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
modelid = config[0];
sdi = NULL;
- if (modelid) {
- profile = NULL;
- for (i = 0; profiles[i].modelid; i++) {
- if (profiles[i].modelid == modelid) {
- profile = &profiles[i];
- break;
- }
- }
- if (!profile) {
- sr_dbg("unknown EL-USB modelid %d", modelid);
- return NULL;
- }
- i = config[52] | (config[53] << 8);
- memcpy(firmware, config + 0x30, 4);
- firmware[4] = '\0';
- sr_dbg("found %s with firmware version %s serial %d",
- profile->modelname, firmware, i);
+ if (!modelid)
+ return sdi;
- if (profile->logformat == LOG_UNSUPPORTED) {
- sr_dbg("unsupported EL-USB logformat for %s", profile->modelname);
- return NULL;
+ profile = NULL;
+ for (i = 0; profiles[i].modelid; i++) {
+ if (profiles[i].modelid == modelid) {
+ profile = &profiles[i];
+ break;
}
+ }
+ if (!profile) {
+ sr_dbg("unknown EL-USB modelid %d", modelid);
+ 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);
-
- if (profile->logformat == LOG_TEMP_RH) {
- /* Model this as two channels: temperature and humidity. */
- 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) {
- sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CO");
- } else {
- sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
- }
+ i = config[52] | (config[53] << 8);
+ memcpy(firmware, config + 0x30, 4);
+ firmware[4] = '\0';
+ sr_dbg("found %s with firmware version %s serial %d",
+ profile->modelname, firmware, i);
+
+ if (profile->logformat == LOG_UNSUPPORTED) {
+ sr_dbg("unsupported EL-USB logformat for %s", profile->modelname);
+ return NULL;
+ }
- devc = g_malloc0(sizeof(struct dev_context));
- sdi->priv = devc;
- devc->profile = profile;
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->status = SR_ST_INACTIVE;
+ sdi->vendor = g_strdup("Lascar");
+ sdi->model = g_strdup(profile->modelname);
+ sdi->version = g_strdup(firmware);
+
+ if (profile->logformat == LOG_TEMP_RH) {
+ /* Model this as two channels: temperature and humidity. */
+ 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) {
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CO");
+ } else {
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
}
+ devc = g_malloc0(sizeof(struct dev_context));
+ sdi->priv = devc;
+ devc->profile = profile;
+
return sdi;
}
packet.type = SR_DF_ANALOG;
packet.payload = &analog;
analog.meaning->mqflags = 0;
- if (!(temp = g_try_malloc(sizeof(float) * samples)))
- break;
- if (!(rh = g_try_malloc(sizeof(float) * samples)))
+ temp = g_try_malloc(sizeof(float) * samples);
+ rh = g_try_malloc(sizeof(float) * samples);
+ if (!temp || !rh) {
+ g_free(temp);
+ g_free(rh);
break;
+ }
for (i = 0, j = 0; i < samples; i++) {
/* Both Celsius and Fahrenheit stored at base -40. */
if (devc->temp_unit == 0)
switch (transfer->status) {
case LIBUSB_TRANSFER_NO_DEVICE:
/* USB device was unplugged. */
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return;
case LIBUSB_TRANSFER_COMPLETED:
case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
devc->rcvd_bytes, devc->log_size,
devc->rcvd_samples, devc->logged_samples);
if (devc->rcvd_bytes >= devc->log_size)
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
if (sdi->status == SR_ST_ACTIVE) {
libusb_error_name(ret));
g_free(transfer->buffer);
libusb_free_transfer(transfer);
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
} else {
/* This was the last transfer we're going to receive, so
#define LOG_PREFIX "lascar-el-usb"
-#define LASCAR_VENDOR "Lascar"
#define LASCAR_INTERFACE 0
#define LASCAR_EP_IN 0x82
#define LASCAR_EP_OUT 2
#define SLEEP_US_LONG (5 * 1000)
#define SLEEP_US_SHORT (1 * 1000)
-/** Private, per-device-instance driver context. */
struct dev_context {
const struct elusb_profile *profile;
+
/* Generic EL-USB */
unsigned char config[MAX_CONFIGBLOCK_SIZE];
unsigned int log_size;
unsigned int logged_samples;
unsigned int rcvd_samples;
uint64_t limit_samples;
+
/* Model-specific */
/* EL-USB-CO: these are something like scaling and calibration values
* fixed per device, used to convert the sample values to CO ppm. */
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);
-SR_PRIV int dev_acquisition_stop(struct sr_dev_inst *sdi);
#endif
#define UNKNOWN_ADDRESS 0xff
#define MAX_RENUM_DELAY_MS 3000
-static const uint32_t devopts[] = {
+#define NUM_CHANNELS 16
+
+static const uint32_t drvopts[] = {
SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
sdi->inst_type = SR_INST_USB;
sdi->conn = usb;
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < NUM_CHANNELS; i++) {
snprintf(channel_name, sizeof(channel_name), "D%i", i);
sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
}
if (des.idVendor != LOGICSTUDIO16_VID)
continue;
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
usb = NULL;
size_t i;
int r;
- if (sdi->status == SR_ST_ACTIVE)
- return SR_ERR;
-
drvc = sdi->driver->context;
usb = sdi->conn;
des.idProduct != LOGICSTUDIO16_PID_HAVE_FIRMWARE)
continue;
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
/*
* Check if this device is the same one that we associated
}
if (!usb->devhdl)
- return SR_ERR;
+ return SR_ERR_BUG;
libusb_release_interface(usb->devhdl, 0);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
-
return SR_OK;
}
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;
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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
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);
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
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));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
default:
return SR_ERR_NA;
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)
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
return lls_stop_acquisition(sdi);
}
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
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),
+ return write_registers_async(sdi, 0x12, 5444, ARRAY_AND_SIZE(cmd),
handle_fetch_samples_done);
}
prep_regw(®s[k++], REG_TRIGGER_CFG, value);
}
- if (write_registers_sync(sdi, 0x12, 5444, regs, ARRAY_SIZE(regs))) {
+ if (write_registers_sync(sdi, 0x12, 5444, ARRAY_AND_SIZE(regs))) {
sr_err("Failed to upload trigger config.");
return SR_ERR;
}
total_samples = devc->num_thousand_samples * 1000;
- pre_trigger_samples = total_samples * devc->capture_ratio / 100;
+ 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);
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));
+ return write_registers_sync(sdi, 0x12, 5444, ARRAY_AND_SIZE(cmd));
}
SR_PRIV int lls_stop_acquisition(const struct sr_dev_inst *sdi)
assert(i == ARRAY_SIZE(cmd));
- return write_registers_sync(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd));
+ return write_registers_sync(sdi, 0x12, 5444, ARRAY_AND_SIZE(cmd));
}
struct samplerate_info;
-/** Private, per-device-instance driver context. */
struct dev_context {
struct libusb_transfer *intr_xfer;
struct libusb_transfer *bulk_xfer;
SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
-static const uint32_t analog_devopts[] = {
+static const uint32_t devopts_cg_analog[] = {
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 int check_manufacturer(const char *manufacturer)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(manufacturers); i++)
- if (!strcmp(manufacturer, manufacturers[i]))
- return SR_OK;
-
- return SR_ERR;
-}
-
-static struct sr_dev_inst *probe_serial_device(struct sr_scpi_dev_inst *scpi)
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
{
struct sr_dev_inst *sdi;
struct dev_context *devc;
goto fail;
}
- if (check_manufacturer(hw_info->manufacturer) != SR_OK)
+ if (std_str_idx_s(hw_info->manufacturer, ARRAY_AND_SIZE(manufacturers)) < 0)
goto fail;
sdi = g_malloc0(sizeof(struct sr_dev_inst));
hw_info = NULL;
devc = g_malloc0(sizeof(struct dev_context));
-
sdi->priv = devc;
if (lecroy_xstream_init_device(sdi) != SR_OK)
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
- return sr_scpi_scan(di->context, options, probe_serial_device);
+ return sr_scpi_scan(di->context, options, probe_device);
}
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
-
lecroy_xstream_state_free(devc->model_state);
-
g_free(devc->analog_groups);
-
- g_free(devc);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static int dev_open(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
+ if (sr_scpi_open(sdi->conn) != SR_OK)
return SR_ERR;
if (lecroy_xstream_state_get(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;
+ return sr_scpi_close(sdi->conn);
}
static int config_get(uint32_t key, GVariant **data,
- const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- int ret;
- unsigned int i;
+ int idx;
struct dev_context *devc;
const struct scope_config *model;
struct scope_state *state;
devc = sdi->priv;
- ret = SR_ERR_NA;
model = devc->model_config;
state = devc->model_state;
*data = NULL;
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)",
- model->timebases[state->timebase].p,
- model->timebases[state->timebase].q);
- ret = SR_OK;
+ (*model->timebases)[state->timebase][0],
+ (*model->timebases)[state->timebase][1]);
break;
case SR_CONF_NUM_VDIV:
- 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;
- }
+ if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) < 0)
+ return SR_ERR_ARG;
+ *data = g_variant_new_int32(model->num_ydivs);
break;
case SR_CONF_VDIV:
- 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].p,
- model->vdivs[state->analog_channels[i].vdiv].q);
- ret = SR_OK;
- }
+ if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ *data = g_variant_new("(tt)",
+ (*model->vdivs)[state->analog_channels[idx].vdiv][0],
+ (*model->vdivs)[state->analog_channels[idx].vdiv][1]);
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((*model->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:
- 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;
- }
+ if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ *data = g_variant_new_string((*model->coupling_options)[state->analog_channels[idx].coupling]);
break;
case SR_CONF_SAMPLERATE:
*data = g_variant_new_uint64(state->sample_rate);
- ret = SR_OK;
break;
case SR_CONF_ENABLED:
*data = g_variant_new_boolean(FALSE);
- ret = SR_OK;
break;
default:
- ret = SR_ERR_NA;
- }
-
- return ret;
-}
-
-static GVariant *build_tuples(const struct sr_rational *array, 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].p);
- rational[1] = g_variant_new_uint64(array[i].q);
-
- /* FIXME: Valgrind reports a memory leak here. */
- g_variant_builder_add_value(&gvb, g_variant_new_tuple(rational, 2));
+ return SR_ERR_NA;
}
- return g_variant_builder_end(&gvb);
+ return SR_OK;
}
static int config_set(uint32_t key, GVariant *data,
- const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- int ret;
- unsigned int i, j;
+ int ret, idx, j;
char command[MAX_COMMAND_SIZE];
struct dev_context *devc;
const struct scope_config *model;
struct scope_state *state;
- const char *tmp;
- int64_t p;
- uint64_t q;
double tmp_d;
- gboolean update_sample_rate;
if (!sdi)
return SR_ERR_ARG;
model = devc->model_config;
state = devc->model_state;
- update_sample_rate = FALSE;
-
- ret = SR_ERR_NA;
switch (key) {
case SR_CONF_LIMIT_FRAMES:
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;
- g_snprintf(command, sizeof(command),
- "SET TRIGGER SOURCE %s",
- (*model->trigger_sources)[i]);
-
- ret = sr_scpi_send(sdi->conn, command);
- break;
- }
+ if ((idx = std_str_idx(data, *model->trigger_sources, model->num_trigger_sources)) < 0)
+ return SR_ERR_ARG;
+ state->trigger_source = idx;
+ g_snprintf(command, sizeof(command),
+ "TRIG_SELECT EDGE,SR,%s", (*model->trigger_sources)[idx]);
+ ret = sr_scpi_send(sdi->conn, command);
break;
case SR_CONF_VDIV:
- g_variant_get(data, "(tt)", &p, &q);
-
- for (i = 0; i < model->num_vdivs; i++) {
- if (p != model->vdivs[i].p || q != model->vdivs[i].q)
- continue;
- for (j = 1; j <= model->analog_channels; j++) {
- if (cg != devc->analog_groups[j - 1])
- continue;
- state->analog_channels[j - 1].vdiv = i;
- g_snprintf(command, sizeof(command),
- "C%d:VDIV %E", j, (float)p/q);
-
- if (sr_scpi_send(sdi->conn, command) != SR_OK ||
- sr_scpi_get_opc(sdi->conn) != SR_OK)
- return SR_ERR;
-
- break;
- }
-
- ret = SR_OK;
- break;
- }
+ if ((idx = std_u64_tuple_idx(data, *model->vdivs, model->num_vdivs)) < 0)
+ return SR_ERR_ARG;
+ if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ state->analog_channels[j].vdiv = idx;
+ g_snprintf(command, sizeof(command),
+ "C%d:VDIV %E", j + 1, (float) (*model->vdivs)[idx][0] / (*model->vdivs)[idx][1]);
+ if (sr_scpi_send(sdi->conn, command) != SR_OK || sr_scpi_get_opc(sdi->conn) != SR_OK)
+ return SR_ERR;
+ ret = SR_OK;
break;
case SR_CONF_TIMEBASE:
- g_variant_get(data, "(tt)", &p, &q);
-
- for (i = 0; i < model->num_timebases; i++) {
- if (p != model->timebases[i].p ||
- q != model->timebases[i].q)
- continue;
- state->timebase = i;
- g_snprintf(command, sizeof(command),
- "TIME_DIV %E", (float)p/q);
-
- ret = sr_scpi_send(sdi->conn, command);
- update_sample_rate = TRUE;
- break;
- }
+ if ((idx = std_u64_tuple_idx(data, *model->timebases, model->num_timebases)) < 0)
+ return SR_ERR_ARG;
+ state->timebase = idx;
+ g_snprintf(command, sizeof(command),
+ "TIME_DIV %E", (float) (*model->timebases)[idx][0] / (*model->timebases)[idx][1]);
+ ret = sr_scpi_send(sdi->conn, command);
break;
case SR_CONF_HORIZ_TRIGGERPOS:
tmp_d = g_variant_get_double(data);
state->horiz_triggerpos = tmp_d;
tmp_d = -(tmp_d - 0.5) *
- ((double)model->timebases[state->timebase].p /
- model->timebases[state->timebase].q)
- * model->num_xdivs;
+ ((double)(*model->timebases)[state->timebase][0] /
+ (*model->timebases)[state->timebase][1])
+ * model->num_xdivs;
g_snprintf(command, sizeof(command), "TRIG POS %e S", tmp_d);
ret = sr_scpi_send(sdi->conn, command);
break;
case SR_CONF_TRIGGER_SLOPE:
- tmp = g_variant_get_string(data, NULL);
- for (i = 0; (*model->trigger_slopes)[i]; i++) {
- if (g_strcmp0(tmp, (*model->trigger_slopes)[i]) != 0)
- continue;
- state->trigger_slope = i;
- g_snprintf(command, sizeof(command),
- "SET TRIGGER SLOPE %s",
- (*model->trigger_slopes)[i]);
-
- ret = sr_scpi_send(sdi->conn, command);
- break;
- }
+ if ((idx = std_str_idx(data, *model->trigger_slopes, model->num_trigger_slopes)) < 0)
+ return SR_ERR_ARG;
+ state->trigger_slope = idx;
+ g_snprintf(command, sizeof(command),
+ "%s:TRIG_SLOPE %s", (*model->trigger_sources)[state->trigger_source],
+ (*model->trigger_slopes)[idx]);
+ ret = sr_scpi_send(sdi->conn, command);
break;
case SR_CONF_COUPLING:
- 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_channels[j - 1].coupling = i;
-
- g_snprintf(command, sizeof(command),
- "C%d:COUPLING %s", j, tmp);
-
- if (sr_scpi_send(sdi->conn, command) != SR_OK ||
- sr_scpi_get_opc(sdi->conn) != SR_OK)
- return SR_ERR;
- break;
- }
-
- ret = SR_OK;
- break;
- }
+ if ((idx = std_str_idx(data, *model->coupling_options, model->num_coupling_options)) < 0)
+ return SR_ERR_ARG;
+ if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ state->analog_channels[j].coupling = idx;
+ g_snprintf(command, sizeof(command), "C%d:COUPLING %s",
+ j + 1, (*model->coupling_options)[idx]);
+ if (sr_scpi_send(sdi->conn, command) != SR_OK || sr_scpi_get_opc(sdi->conn) != SR_OK)
+ return SR_ERR;
+ ret = SR_OK;
break;
default:
ret = SR_ERR_NA;
if (ret == SR_OK)
ret = sr_scpi_get_opc(sdi->conn);
- if (ret == SR_OK && update_sample_rate)
- ret = lecroy_xstream_update_sample_rate(sdi);
-
return ret;
}
-static int config_list(uint32_t key, GVariant **data,
- const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+static int config_channel_set(const struct sr_dev_inst *sdi,
+ struct sr_channel *ch, unsigned int changes)
{
- struct dev_context *devc = NULL;
- const struct scope_config *model = NULL;
-
- (void)cg;
-
- /* SR_CONF_SCAN_OPTIONS is always valid, regardless of sdi or channel group. */
- 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;
- }
+ /* Currently we only handle SR_CHANNEL_SET_ENABLED. */
+ if (changes != SR_CHANNEL_SET_ENABLED)
+ return SR_ERR_NA;
- /* 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,
- drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
- return SR_OK;
- }
+ return lecroy_xstream_channel_state_set(sdi, ch->index, ch->enabled);
+}
- /* Every other option requires a valid device instance. */
- if (!sdi)
- return SR_ERR_ARG;
+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;
+ const struct scope_config *model;
- devc = sdi->priv;
- model = devc->model_config;
+ devc = (sdi) ? sdi->priv : NULL;
+ model = (devc) ? devc->model_config : NULL;
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, NO_OPTS, NO_OPTS);
case SR_CONF_DEVICE_OPTIONS:
- if (!cg) {
- /* If cg is NULL, only the SR_CONF_DEVICE_OPTIONS that are not
- * specific to a channel group must be returned. */
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- return SR_OK;
- }
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- analog_devopts, ARRAY_SIZE(analog_devopts),
- sizeof(uint32_t));
+ if (!cg)
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog));
break;
case SR_CONF_COUPLING:
- *data = g_variant_new_strv(*model->coupling_options,
- g_strv_length((char **)*model->coupling_options));
+ if (!model)
+ return SR_ERR_ARG;
+ *data = g_variant_new_strv(*model->coupling_options, model->num_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));
+ *data = g_variant_new_strv(*model->trigger_sources, model->num_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));
+ *data = g_variant_new_strv(*model->trigger_slopes, model->num_trigger_slopes);
break;
case SR_CONF_TIMEBASE:
if (!model)
return SR_ERR_ARG;
- *data = build_tuples(model->timebases, model->num_timebases);
+ *data = std_gvar_tuple_array(*model->timebases, model->num_timebases);
break;
case SR_CONF_VDIV:
if (!model)
return SR_ERR_ARG;
- *data = build_tuples(model->vdivs, model->num_vdivs);
+ *data = std_gvar_tuple_array(*model->vdivs, model->num_vdivs);
break;
default:
return SR_ERR_NA;
}
+
return SR_OK;
}
devc = sdi->priv;
+ /*
+ * We may be left with an invalid current_channel if acquisition was
+ * already stopped but we are processing the last pending events.
+ */
+ if (!devc->current_channel)
+ return SR_ERR_NA;
+
ch = devc->current_channel->data;
if (ch->type != SR_CHANNEL_ANALOG)
return SR_ERR;
- g_snprintf(command, sizeof(command),
- "COMM_FORMAT DEF9,WORD,BIN;C%d:WAVEFORM?", ch->index + 1);
+ g_snprintf(command, sizeof(command), "C%d:WAVEFORM?", ch->index + 1);
return sr_scpi_send(sdi->conn, command);
}
static int setup_channels(const struct sr_dev_inst *sdi)
{
GSList *l;
- gboolean setup_changed;
char command[MAX_COMMAND_SIZE];
struct scope_state *state;
struct sr_channel *ch;
devc = sdi->priv;
scpi = sdi->conn;
state = devc->model_state;
- setup_changed = FALSE;
for (l = sdi->channels; l; l = l->next) {
ch = l->data;
if (ch->enabled == state->analog_channels[ch->index].state)
break;
g_snprintf(command, sizeof(command), "C%d:TRACE %s",
- ch->index + 1, ch->enabled ? "ON" : "OFF");
+ ch->index + 1, ch->enabled ? "ON" : "OFF");
if (sr_scpi_send(scpi, command) != SR_OK)
return SR_ERR;
+
state->analog_channels[ch->index].state = ch->enabled;
- setup_changed = TRUE;
break;
default:
return SR_ERR;
}
}
- if (setup_changed && lecroy_xstream_update_sample_rate(sdi) != SR_OK)
- return SR_ERR;
-
return SR_OK;
}
GSList *l;
struct sr_channel *ch;
struct dev_context *devc;
+ struct scope_state *state;
int ret;
struct sr_scpi_dev_inst *scpi;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
scpi = sdi->conn;
+
/* Preset empty results. */
g_slist_free(devc->enabled_channels);
devc->enabled_channels = NULL;
+ state = devc->model_state;
+ state->sample_rate = 0;
- /*
- * Contruct the list of enabled channels. Determine the highest
- * number of digital pods involved in the acquisition.
- */
-
+ /* Contruct the list of enabled channels. */
for (l = sdi->channels; l; l = l->next) {
ch = l->data;
if (!ch->enabled)
continue;
- /* Only add a single digital channel per group (pod). */
- devc->enabled_channels = g_slist_append(
- devc->enabled_channels, ch);
+
+ devc->enabled_channels = g_slist_append(devc->enabled_channels, ch);
}
if (!devc->enabled_channels)
return SR_ERR;
- /*
- * Configure the analog channels and the
- * corresponding digital pods.
- */
+ /* Configure the analog channels. */
if (setup_channels(sdi) != SR_OK) {
sr_err("Failed to setup channel configuration!");
ret = SR_ERR;
std_session_send_df_end(sdi);
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
devc->num_frames = 0;
.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,
} __attribute__((packed));
static const char *coupling_options[] = {
- "A1M", // AC with 1 MOhm termination
- "D50", // DC with 50 Ohm termination
- "D1M", // DC with 1 MOhm termination
+ "A1M", ///< AC with 1 MOhm termination
+ "D50", ///< DC with 50 Ohm termination
+ "D1M", ///< DC with 1 MOhm termination
"GND",
"OVL",
- NULL,
};
static const char *scope_trigger_slopes[] = {
- "POS",
- "NEG",
- NULL,
+ "POS", "NEG",
};
static const char *trigger_sources[] = {
- "C1",
- "C2",
- "C3",
- "C4",
- "LINE",
- "EXT",
- NULL,
+ "C1", "C2", "C3", "C4", "LINE", "EXT",
};
-static const struct sr_rational timebases[] = {
+static const uint64_t timebases[][2] = {
/* picoseconds */
- { 20, 1000000000000 },
- { 50, 1000000000000 },
- { 100, 1000000000000 },
- { 200, 1000000000000 },
- { 500, 1000000000000 },
+ { 20, UINT64_C(1000000000000) },
+ { 50, UINT64_C(1000000000000) },
+ { 100, UINT64_C(1000000000000) },
+ { 200, UINT64_C(1000000000000) },
+ { 500, UINT64_C(1000000000000) },
/* nanoseconds */
{ 1, 1000000000 },
{ 2, 1000000000 },
{ 1000, 1 },
};
-static const struct sr_rational vdivs[] = {
+static const uint64_t vdivs[][2] = {
/* millivolts */
{ 1, 1000 },
{ 2, 1000 },
};
static const char *scope_analog_channel_names[] = {
- "CH1",
- "CH2",
- "CH3",
- "CH4",
+ "CH1", "CH2", "CH3", "CH4",
};
static const struct scope_config scope_models[] = {
{
- .name = { "WP7000", "WP7100", "WP7200", "WP7300" },
+ /* Default config */
+ .name = {NULL},
.analog_channels = 4,
.analog_names = &scope_analog_channel_names,
.coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
+
.trigger_sources = &trigger_sources,
+ .num_trigger_sources = ARRAY_SIZE(trigger_sources),
+
.trigger_slopes = &scope_trigger_slopes,
+ .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes),
- .timebases = timebases,
+ .timebases = &timebases,
.num_timebases = ARRAY_SIZE(timebases),
- .vdivs = vdivs,
+ .vdivs = &vdivs,
.num_vdivs = ARRAY_SIZE(vdivs),
.num_xdivs = 10,
};
static void scope_state_dump(const struct scope_config *config,
- struct scope_state *state)
+ struct scope_state *state)
{
unsigned int i;
char *tmp;
for (i = 0; i < config->analog_channels; i++) {
- tmp = sr_voltage_string(config->vdivs[state->analog_channels[i].vdiv].p,
- config->vdivs[state->analog_channels[i].vdiv].q);
+ 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)",
- i + 1, state->analog_channels[i].state ? "On" : "Off",
- (*config->coupling_options)[state->analog_channels[i].coupling],
- tmp, state->analog_channels[i].vertical_offset);
+ i + 1, state->analog_channels[i].state ? "On" : "Off",
+ (*config->coupling_options)[state->analog_channels[i].coupling],
+ tmp, state->analog_channels[i].vertical_offset);
}
- tmp = sr_period_string(config->timebases[state->timebase].p,
- config->timebases[state->timebase].q);
+ tmp = sr_period_string((*config->timebases)[state->timebase][0],
+ (*config->timebases)[state->timebase][1]);
sr_info("Current timebase: %s", tmp);
g_free(tmp);
g_free(tmp);
sr_info("Current trigger: %s (source), %s (slope) %.2f (offset)",
- (*config->trigger_sources)[state->trigger_source],
- (*config->trigger_slopes)[state->trigger_slope],
- state->horiz_triggerpos);
+ (*config->trigger_sources)[state->trigger_source],
+ (*config->trigger_slopes)[state->trigger_slope],
+ state->horiz_triggerpos);
}
static int scope_state_get_array_option(const char *resp,
- const char *(*array)[], int *result)
+ const char *(*array)[], unsigned int n, int *result)
{
unsigned int i;
- for (i = 0; (*array)[i]; i++) {
+ for (i = 0; i < n; i++) {
if (!g_strcmp0(resp, (*array)[i])) {
*result = i;
return SR_OK;
*
* @return SR_ERR on any parsing error, SR_OK otherwise.
*/
-static int array_float_get(gchar *value, const struct sr_rational *aval,
+static int array_float_get(gchar *value, const uint64_t array[][2],
int array_len, unsigned int *result)
{
struct sr_rational rval;
+ struct sr_rational aval;
if (sr_parse_rational(value, &rval) != SR_OK)
return SR_ERR;
for (int i = 0; i < array_len; i++) {
- if (sr_rational_eq(&rval, aval + i)) {
+ sr_rational_set(&aval, array[i][0], array[i][1]);
+ if (sr_rational_eq(&rval, &aval)) {
*result = i;
return SR_OK;
}
}
static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
- const struct scope_config *config,
- struct scope_state *state)
+ const struct scope_config *config, struct scope_state *state)
{
unsigned int i, j;
char command[MAX_COMMAND_SIZE];
if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK)
return SR_ERR;
- if (array_float_get(tmp_str, vdivs, ARRAY_SIZE(vdivs), &j) != SR_OK) {
+ if (array_float_get(tmp_str, ARRAY_AND_SIZE(vdivs), &j) != SR_OK) {
g_free(tmp_str);
sr_err("Could not determine array index for vertical div scale.");
return SR_ERR;
if (scope_state_get_array_option(tmp_str, config->coupling_options,
+ config->num_coupling_options,
&state->analog_channels[i].coupling) != SR_OK)
return SR_ERR;
return SR_OK;
}
-SR_PRIV int lecroy_xstream_update_sample_rate(const struct sr_dev_inst *sdi)
+SR_PRIV int lecroy_xstream_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;
+ char command[MAX_COMMAND_SIZE];
+ gboolean chan_found;
+ int result;
+
+ result = SR_OK;
+
+ devc = sdi->priv;
+ state = devc->model_state;
+ chan_found = FALSE;
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+
+ switch (ch->type) {
+ case SR_CHANNEL_ANALOG:
+ if (ch->index == ch_index) {
+ g_snprintf(command, sizeof(command), "C%d:TRACE %s", ch_index + 1,
+ (ch_state ? "ON" : "OFF"));
+ if ((sr_scpi_send(sdi->conn, command) != SR_OK ||
+ sr_scpi_get_opc(sdi->conn) != SR_OK)) {
+ result = SR_ERR;
+ break;
+ }
+
+ ch->enabled = ch_state;
+ state->analog_channels[ch->index].state = ch_state;
+ chan_found = TRUE;
+ break;
+ }
+ break;
+ default:
+ result = SR_ERR_NA;
+ }
+ }
+
+ if ((result == SR_OK) && !chan_found)
+ result = SR_ERR_BUG;
+
+ return result;
+}
+
+SR_PRIV int lecroy_xstream_update_sample_rate(const struct sr_dev_inst *sdi,
+ int num_of_samples)
{
struct dev_context *devc;
struct scope_state *state;
const struct scope_config *config;
- float memsize, timediv;
+ double time_div;
devc = sdi->priv;
- state = devc->model_state;
config = devc->model_config;
+ state = devc->model_state;
- if (sr_scpi_get_float(sdi->conn, "MEMORY_SIZE?", &memsize) != SR_OK)
- return SR_ERR;
-
- if (sr_scpi_get_float(sdi->conn, "TIME_DIV?", &timediv) != SR_OK)
+ if (sr_scpi_get_double(sdi->conn, "TIME_DIV?", &time_div) != SR_OK)
return SR_ERR;
- state->sample_rate = 1 / ((timediv * config->num_xdivs) / memsize);
+ state->sample_rate = num_of_samples / (time_div * config->num_xdivs);
return SR_OK;
}
if (sr_scpi_get_string(sdi->conn, "TIME_DIV?", &tmp_str) != SR_OK)
return SR_ERR;
- if (array_float_get(tmp_str, timebases, ARRAY_SIZE(timebases), &i) != SR_OK) {
+ if (array_float_get(tmp_str, ARRAY_AND_SIZE(timebases), &i) != SR_OK) {
g_free(tmp_str);
sr_err("Could not determine array index for timbase scale.");
return SR_ERR;
i++;
}
- if (!trig_source || scope_state_get_array_option(trig_source, config->trigger_sources, &state->trigger_source) != SR_OK)
+ if (!trig_source || scope_state_get_array_option(trig_source,
+ config->trigger_sources, config->num_trigger_sources,
+ &state->trigger_source) != SR_OK)
return SR_ERR;
g_snprintf(command, sizeof(command), "%s:TRIG_SLOPE?", trig_source);
if (sr_scpi_get_string(sdi->conn, command, &tmp_str) != SR_OK)
return SR_ERR;
- if (scope_state_get_array_option(tmp_str,
- config->trigger_slopes, &state->trigger_slope) != SR_OK)
+ if (scope_state_get_array_option(tmp_str, config->trigger_slopes,
+ config->num_trigger_slopes, &state->trigger_slope) != SR_OK)
return SR_ERR;
- if (sr_scpi_get_float(sdi->conn, "TRIG_DELAY?", &state->horiz_triggerpos) != SR_OK)
- return SR_ERR;
-
- if (lecroy_xstream_update_sample_rate(sdi) != SR_OK)
+ if (sr_scpi_get_float(sdi->conn, "TRIG_DELAY?", &state->horiz_triggerpos) != SR_OK)
return SR_ERR;
sr_info("Fetching finished.");
}
if (model_index == -1) {
- sr_dbg("Unsupported LeCroy device.");
- return SR_ERR_NA;
+ sr_dbg("Unknown LeCroy device, using default config.");
+ for (i = 0; i < ARRAY_SIZE(scope_models); i++)
+ if (scope_models[i].name[0] == NULL)
+ model_index = i;
}
+ /* Set the desired response and format modes. */
+ sr_scpi_send(sdi->conn, "COMM_HEADER OFF");
+ sr_scpi_send(sdi->conn, "COMM_FORMAT DEF9,WORD,BIN");
+
devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) *
scope_models[model_index].analog_channels);
g_snprintf(command, sizeof(command), "C%d:VDIV?", i + 1);
ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, channel_enabled,
- (*scope_models[model_index].analog_names)[i]);
+ (*scope_models[model_index].analog_names)[i]);
devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
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]);
}
devc->model_config = &scope_models[model_index];
devc->frame_limit = 0;
-
- if (!(devc->model_state = scope_state_new(devc->model_config)))
- return SR_ERR_MALLOC;
-
- /* Set the desired response mode. */
- sr_scpi_send(sdi->conn, "COMM_HEADER OFF,WORD,BIN");
+ devc->model_state = scope_state_new(devc->model_config);
return SR_OK;
}
static int lecroy_waveform_2_x_to_analog(GByteArray *data,
- struct lecroy_wavedesc *desc,
- struct sr_datafeed_analog *analog)
+ struct lecroy_wavedesc *desc, struct sr_datafeed_analog *analog)
{
struct sr_analog_encoding *encoding = analog->encoding;
struct sr_analog_meaning *meaning = analog->meaning;
data_float = g_malloc(desc->version_2_x.wave_array_count * sizeof(float));
num_samples = desc->version_2_x.wave_array_count;
- waveform_data = (int16_t *)(data->data +
- + desc->version_2_x.wave_descriptor_length
- + desc->version_2_x.user_text_len);
+ waveform_data = (int16_t*)(data->data +
+ + desc->version_2_x.wave_descriptor_length
+ + desc->version_2_x.user_text_len);
for (i = 0; i < num_samples; i++)
data_float[i] = (float)waveform_data[i]
}
static int lecroy_waveform_to_analog(GByteArray *data,
- struct sr_datafeed_analog *analog)
+ struct sr_datafeed_analog *analog)
{
struct lecroy_wavedesc *desc;
if (data->len < sizeof(struct lecroy_wavedesc))
return SR_ERR;
- desc = (struct lecroy_wavedesc *)data->data;
+ desc = (struct lecroy_wavedesc*)data->data;
if (!strncmp(desc->template_name, "LECROY_2_2", 16) ||
!strncmp(desc->template_name, "LECROY_2_3", 16)) {
return lecroy_waveform_2_x_to_analog(data, desc, analog);
}
- sr_err("Waveformat template '%.16s' not supported.",
- desc->template_name);
-
+ sr_err("Waveformat template '%.16s' not supported.", desc->template_name);
return SR_ERR;
}
SR_PRIV int lecroy_xstream_receive_data(int fd, int revents, void *cb_data)
{
+ char command[MAX_COMMAND_SIZE];
struct sr_channel *ch;
struct sr_dev_inst *sdi;
struct dev_context *devc;
+ struct scope_state *state;
struct sr_datafeed_packet packet;
GByteArray *data;
struct sr_datafeed_analog analog;
return TRUE;
ch = devc->current_channel->data;
-
- /*
- * Send "frame begin" packet upon reception of data for the
- * first enabled channel.
- */
- if (devc->current_channel == devc->enabled_channels) {
- packet.type = SR_DF_FRAME_BEGIN;
- sr_session_send(sdi, &packet);
- }
+ state = devc->model_state;
if (ch->type != SR_CHANNEL_ANALOG)
return SR_ERR;
/* Pass on the received data of the channel(s). */
if (sr_scpi_read_data(sdi->conn, buf, 4) != 4) {
- sr_err("Reading header failed.");
+ sr_err("Reading header failed, scope probably didn't send any data.");
return TRUE;
}
if (lecroy_waveform_to_analog(data, &analog) != SR_OK)
return SR_ERR;
+ if (analog.num_samples == 0) {
+ g_free(analog.data);
+
+ /* No data available, we have to acquire data first. */
+ g_snprintf(command, sizeof(command), "ARM;WAIT;*OPC;C%d:WAVEFORM?", ch->index + 1);
+ sr_scpi_send(sdi->conn, command);
+
+ state->sample_rate = 0;
+ return TRUE;
+ } else {
+ /* Update sample rate if needed. */
+ if (state->sample_rate == 0)
+ if (lecroy_xstream_update_sample_rate(sdi, analog.num_samples) != SR_OK) {
+ g_free(analog.data);
+ return SR_ERR;
+ }
+ }
+
+ /*
+ * Send "frame begin" packet upon reception of data for the
+ * first enabled channel.
+ */
+ if (devc->current_channel == devc->enabled_channels) {
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+ }
+
meaning.channels = g_slist_append(NULL, ch);
packet.payload = &analog;
packet.type = SR_DF_ANALOG;
* number of frames, or continue reception by starting over at
* the first enabled channel.
*/
- if (++devc->num_frames == devc->frame_limit) {
- sdi->driver->dev_acquisition_stop(sdi);
+ devc->num_frames++;
+ if (devc->frame_limit && (devc->num_frames == devc->frame_limit)) {
+ sr_dev_acquisition_stop(sdi);
} else {
devc->current_channel = devc->enabled_channels;
+
+ /* Wait for trigger, then begin fetching data. */
+ g_snprintf(command, sizeof(command), "ARM;WAIT;*OPC");
+ sr_scpi_send(sdi->conn, command);
+
lecroy_xstream_request_data(sdi);
}
const uint8_t num_trigger_sources;
const char *(*trigger_slopes)[];
+ const uint8_t num_trigger_slopes;
- const struct sr_rational *timebases;
+ const uint64_t (*timebases)[][2];
const uint8_t num_timebases;
- const struct sr_rational *vdivs;
+ const uint64_t (*vdivs)[][2];
const uint8_t num_vdivs;
const uint8_t num_xdivs;
uint64_t sample_rate;
};
-/** Private, per-device-instance driver context. */
struct dev_context {
const void *model_config;
void *model_state;
SR_PRIV void lecroy_xstream_state_free(struct scope_state *state);
SR_PRIV int lecroy_xstream_state_get(struct sr_dev_inst *sdi);
-SR_PRIV int lecroy_xstream_update_sample_rate(const struct sr_dev_inst *sdi);
+SR_PRIV int lecroy_xstream_channel_state_set(const struct sr_dev_inst *sdi,
+ const int ch_index, gboolean ch_state);
+SR_PRIV int lecroy_xstream_update_sample_rate(const struct sr_dev_inst *sdi,
+ int num_of_samples);
#endif
#include <config.h>
#include "protocol.h"
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_OSCILLOSCOPE,
SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_TRIGGER_TYPE | SR_CONF_LIST,
SR_HZ(100),
};
+static const char *trigger_slopes[2] = {
+ "r", "f",
+};
+
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
int i;
if (serial_open(devc->serial, SERIAL_RDWR) != SR_OK)
return SR_ERR;
- sdi->status = SR_ST_ACTIVE;
-
/* FIXME: discard serial buffer */
mso_check_trigger(devc->serial, &devc->trigger_state);
sr_dbg("Trigger state: 0x%x.", devc->trigger_state);
return SR_OK;
}
-static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+static int config_get(int key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
return SR_OK;
}
-static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+static int config_set(int key, GVariant *data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- int ret;
struct dev_context *devc;
uint64_t num_samples;
const char *slope;
devc = sdi->priv;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
switch (key) {
case SR_CONF_SAMPLERATE:
// FIXME
return mso_configure_rate(sdi, g_variant_get_uint64(data));
- ret = SR_OK;
- break;
case SR_CONF_LIMIT_SAMPLES:
num_samples = g_variant_get_uint64(data);
if (num_samples != 1024) {
sr_err("Only 1024 samples are supported.");
- ret = SR_ERR_ARG;
- } else {
- devc->limit_samples = num_samples;
- ret = SR_OK;
+ return SR_ERR_ARG;
}
+ devc->limit_samples = num_samples;
break;
case SR_CONF_CAPTURE_RATIO:
- ret = SR_OK;
break;
case SR_CONF_TRIGGER_SLOPE:
- slope = g_variant_get_string(data, NULL);
-
- if (!slope || !(slope[0] == 'f' || slope[0] == 'r'))
- sr_err("Invalid trigger slope");
- ret = SR_ERR_ARG;
- } else {
- devc->trigger_slope = (slope[0] == 'r')
- ? SLOPE_POSITIVE : SLOPE_NEGATIVE;
- ret = SR_OK;
- }
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
+ return SR_ERR_ARG;
+ devc->trigger_slope = idx;
break;
case SR_CONF_HORIZ_TRIGGERPOS:
pos = g_variant_get_double(data);
if (pos < 0 || pos > 255) {
sr_err("Trigger position (%f) should be between 0 and 255.", pos);
- ret = SR_ERR_ARG;
- } else {
- trigger_pos = (int)pos;
- devc->trigger_holdoff[0] = trigger_pos & 0xff;
- ret = SR_OK;
+ return SR_ERR_ARG;
}
+ trigger_pos = (int)pos;
+ devc->trigger_holdoff[0] = trigger_pos & 0xff;
break;
case SR_CONF_RLE:
- ret = SR_OK;
break;
default:
- ret = SR_ERR_NA;
- break;
+ return SR_ERR_NA;
}
- return ret;
+ 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(int key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- GVariant *gvar;
- GVariantBuilder gvb;
-
- (void)cg;
- (void)sdi;
-
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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
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);
+ *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
break;
case SR_CONF_TRIGGER_TYPE:
*data = g_variant_new_string(TRIGGER_TYPE);
struct dev_context *devc;
int ret = SR_ERR;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
if (mso_configure_channels(sdi) != SR_OK) {
return SR_OK;
}
-/* This stops acquisition on ALL devices, ignoring dev_index. */
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
stop_acquisition(sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
p += sizeof(mso_head);
for (i = 0; i < n; i++) {
- *(uint16_t *) p = g_htons(payload[i]);
- p += 2;
+ WB16(p, payload[i]);
+ p += sizeof(uint16_t);
}
memcpy(p, mso_foot, sizeof(mso_foot));
if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
sr_info("Requested number of samples reached.");
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
return TRUE;
uint8_t spimode;
};
-/* Private, per-device-instance driver context. */
struct dev_context {
/* info */
uint8_t hwmodel;
uint16_t offset_range;
uint64_t limit_samples;
uint64_t num_samples;
+
/* register cache */
uint8_t ctlbase1;
uint8_t ctlbase2;
- /* state */
+
uint8_t la_threshold;
uint64_t cur_rate;
uint8_t dso_probe_attn;
/* FIXME: Determine corresponding voltages */
static const uint16_t la_threshold_map[] = {
- 0x8600,
- 0x8770,
- 0x88ff,
- 0x8c70,
- 0x8eff,
- 0x8fff,
+ 0x8600, 0x8770, 0x88ff, 0x8c70, 0x8eff, 0x8fff,
};
#endif
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-/**
- * @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 */
+static const uint32_t drvopts[] = {
SR_CONF_POWER_SUPPLY,
- /* Acquisition modes. */
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
- /* 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,
{ 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_3304, "HCS-3304-USB", "HCS-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 } },
}
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");
return NULL;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
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))
+
+ if (hcs_send_cmd(sdi->conn, "SOUT%1d\r", !bval) < 0) {
+ sr_err("Could not send SR_CONF_ENABLED command.");
return SR_ERR;
+ }
+ if (hcs_read_reply(sdi->conn, 1, devc->buf, sizeof(devc->buf)) < 0) {
+ sr_err("Could not read SR_CONF_ENABLED reply.");
+ return SR_ERR;
+ }
devc->output_enabled = bval;
break;
default:
return SR_OK;
}
-static int config_list(uint32_t 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)
{
+ const double *a;
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;
+ devc = (sdi) ? sdi->priv : NULL;
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- break;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ a = devc->model->voltage;
+ *data = std_gvar_min_max_step(a[0], devc->voltage_max_device, a[2]);
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);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ a = devc->model->current;
+ *data = std_gvar_min_max_step(a[0], devc->current_max_device, a[2]);
break;
default:
return SR_ERR_NA;
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->limits);
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);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-/**
- * @file
- *
- * <em>Manson HCS-3xxx Series</em> power supply driver
- *
- * @internal
- */
-
#include <config.h>
#include "protocol.h"
}
if (sr_sw_limits_check(&devc->limits)) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-/**
- * @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
double current[3]; /**< Min, max, step */
};
-/** Private, per-device-instance driver context. */
struct dev_context {
const struct hcs_model *model; /**< Model information. */
static const uint32_t scanopts[] = {
SR_CONF_CONN,
SR_CONF_SERIALCOMM,
- SR_CONF_MODBUSADDR
+ SR_CONF_MODBUSADDR,
};
static const uint32_t drvopts[] = {
sdi->status = SR_ST_INACTIVE;
sdi->vendor = g_strdup("Maynuo");
sdi->model = g_strdup(model->name);
- sdi->version = g_strdup_printf("v%d.%d", version/10, version%10);
+ 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;
if (sr_modbus_open(modbus) < 0)
return SR_ERR;
- sdi->status = SR_ST_ACTIVE;
-
maynuo_m97_set_bit(modbus, PC1, 1);
return SR_OK;
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);
- }
+ if (!modbus)
+ return SR_ERR_BUG;
- maynuo_m97_set_bit(modbus, PC1, 0);
+ devc = sdi->priv;
- if (sr_modbus_close(modbus) < 0)
- return SR_ERR;
- sdi->status = SR_ST_INACTIVE;
+ 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);
}
- return SR_OK;
+ maynuo_m97_set_bit(modbus, PC1, 0);
+
+ return sr_modbus_close(modbus);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
struct sr_modbus_dev_inst *modbus;
return ret;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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)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:
case SR_CONF_LIMIT_MSEC:
- ret = sr_sw_limits_config_set(&devc->limits, key, data);
- break;
+ return sr_sw_limits_config_set(&devc->limits, key, data);
case SR_CONF_ENABLED:
- ret = maynuo_m97_set_input(modbus, g_variant_get_boolean(data));
- break;
+ return maynuo_m97_set_input(modbus, g_variant_get_boolean(data));
case SR_CONF_VOLTAGE_TARGET:
- ret = maynuo_m97_set_float(modbus, UFIX, g_variant_get_double(data));
- break;
+ return maynuo_m97_set_float(modbus, UFIX, g_variant_get_double(data));
case SR_CONF_CURRENT_LIMIT:
- ret = maynuo_m97_set_float(modbus, IFIX, g_variant_get_double(data));
- break;
+ return maynuo_m97_set_float(modbus, IFIX, g_variant_get_double(data));
case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
- ret = maynuo_m97_set_float(modbus, UMAX, g_variant_get_double(data));
- break;
+ return maynuo_m97_set_float(modbus, UMAX, g_variant_get_double(data));
case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
- ret = maynuo_m97_set_float(modbus, IMAX, g_variant_get_double(data));
- break;
+ return maynuo_m97_set_float(modbus, IMAX, g_variant_get_double(data));
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
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;
+ devc = (sdi) ? sdi->priv : NULL;
- 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;
- }
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
} 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));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
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);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ *data = std_gvar_min_max_step(0.0, devc->model->max_voltage, 0.001);
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);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ *data = std_gvar_min_max_step(0.0, devc->model->max_current, 0.0001);
break;
default:
return SR_ERR_NA;
}
}
- return ret;
+ return SR_OK;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct sr_modbus_dev_inst *modbus;
int ret;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
modbus = sdi->conn;
devc = sdi->priv;
{
struct sr_modbus_dev_inst *modbus;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
std_session_send_df_end(sdi);
modbus = sdi->conn;
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
}
if (sr_sw_limits_check(&devc->limits)) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
unsigned int max_power;
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
const struct maynuo_m97_model *model;
-
- /* Acquisition settings */
struct sr_sw_limits limits;
-
- /* Operational state */
int expecting_registers;
};
if (!conn)
return NULL;
- if (serialcomm) {
- /* Use the provided comm specs. */
+ if (serialcomm)
devices = mic_scan(conn, serialcomm, idx);
- } else {
- /* Try the default. */
+ else
devices = mic_scan(conn, mic_devs[idx].conn, idx);
- }
return devices;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg, int idx)
+static int config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg, int idx)
{
- (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:
- 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;
- }
-
- return SR_OK;
+ /*
+ * We can't use the ternary operator here! The result would contain
+ * sizeof((cond) ? A : B) where A/B are arrays of different type/size.
+ * The ternary operator always returns the "common" type of A and B,
+ * which would be a pointer instead of either the A or B arrays.
+ * Thus, sizeof() would yield the size of a pointer, not the size
+ * of either the A or B array, which is not what we want.
+ */
+ if (mic_devs[idx].has_humidity)
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts_temp_hum, devopts);
+ else
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts_temp, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi, int idx)
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* Poll every 100ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 100,
mic_devs[idx].receive_data, (void *)sdi);
.cleanup = std_cleanup, \
.scan = scan_##ID_UPPER, \
.dev_list = std_dev_list, \
+ .dev_clear = std_dev_clear, \
.config_get = NULL, \
.config_set = config_set, \
.config_list = config_list_##ID_UPPER, \
{
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- int len, i, offset = 0;
+ int len, offset;
devc = sdi->priv;
serial = sdi->conn;
devc->buflen += len;
/* Now look for packets in that data. */
+ offset = 0;
while ((devc->buflen - offset) >= mic_devs[idx].packet_size) {
if (mic_devs[idx].packet_valid(devc->buf + offset)) {
handle_packet(devc->buf + offset, sdi, idx);
}
/* 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];
+ if (offset < devc->buflen)
+ memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
devc->buflen -= offset;
}
}
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#define SERIAL_BUFSIZE 256
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits limits;
* 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"
+
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 */
+static const uint32_t drvopts[] = {
SR_CONF_POWER_SUPPLY,
- /* Acquisition modes. */
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
- /* 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[] = {
+static const uint32_t devopts_cg_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_ENABLED | SR_CONF_GET | SR_CONF_SET,
};
-/** Hardware capabilities channel 3. (LPS-304/305 only). */
-static const uint32_t devopts_ch3[] = {
+/** Hardware capabilities channel 3 (LPS-304/305 only). */
+static const uint32_t devopts_cg_ch3[] = {
SR_CONF_VOLTAGE | SR_CONF_GET,
SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
};
},
};
-/** Send command to device with va_list.
- */
+/** 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;
return SR_OK;
}
-/** Send command to device.
- */
+/** Send command to device. */
SR_PRIV int lps_send_req(struct sr_serial_dev_inst *serial, const char *fmt, ...)
{
int retc;
return SR_ERR; /* Timeout! */
}
-/** Scan for LPS-300 series device.
- */
+/** 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;
devc = NULL;
conn = serialcomm = NULL;
- sr_spew("scan() called!");
-
/* Process and check options. */
if (sr_serial_extract_options(options, &conn, &serialcomm) != SR_OK)
return NULL;
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(VENDOR_MOTECH);
+ sdi->vendor = g_strdup("Motech");
sdi->model = g_strdup(models[modelid].modelstr);
sdi->version = g_strdup(verstr);
sdi->inst_type = SR_INST_SERIAL;
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);
+ snprintf(channel, sizeof(channel), "CG%d", cnt + 1);
cg->name = g_strdup(channel);
cg->priv = NULL;
cg->channels = g_slist_append(NULL, ch);
return do_scan(LPS_301, di, options);
}
-static void dev_clear_private(struct dev_context *devc)
+static void clear_helper(struct dev_context *devc)
{
int ch_idx;
g_slist_free(devc->channel_status[ch_idx].info);
}
-static int dev_clear_lps301(const struct sr_dev_driver *di)
+static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, (std_dev_clear_callback)dev_clear_private);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
struct sr_channel *ch;
devc = sdi->priv;
if (!cg) {
- /* No channel group: global options. */
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
/* 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);
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
return SR_ERR_NA;
if (!cg) {
- /* No channel group: global options. */
switch (key) {
case SR_CONF_LIMIT_MSEC:
case SR_CONF_LIMIT_SAMPLES:
return sr_sw_limits_config_set(&devc->limits, key, data);
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)
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(channel_modes))) < 0)
return SR_ERR_ARG;
+ if (devc->model->modelid <= LPS_303 && idx != 0)
+ break; /* Only first setting possible for smaller models. */
+ 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);
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;
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. */
return SR_OK;
}
-static int config_list(uint32_t 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)
{
struct dev_context *devc;
struct sr_channel *ch;
- int ch_idx, i;
- GVariant *gvar;
- GVariantBuilder gvb;
+ int ch_idx;
- /* 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)
- 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;
- }
+ devc = (sdi) ? sdi->priv : NULL;
- /* Device options, independent from channel groups. */
if (!cg) {
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_CHANNEL_CONFIG:
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
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));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(channel_modes));
}
return SR_OK;
default:
}
}
- /* Device options, depending on channel groups. */
+ /* 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_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));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_ch12));
else /* Must be CH3 */
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts_ch3, ARRAY_SIZE(devopts_ch3), sizeof(uint32_t));
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_ch3));
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);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ *data = std_gvar_min_max_step_array(devc->model->channels[ch_idx].voltage);
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);
+ if (!devc || !devc->model)
+ return SR_ERR_ARG;
+ *data = std_gvar_min_max_step_array(devc->model->channels[ch_idx].current);
break;
default:
return SR_ERR_NA;
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;
.cleanup = std_cleanup,
.scan = scan_lps301,
.dev_list = std_dev_list,
- .dev_clear = dev_clear_lps301,
+ .dev_clear = dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
* 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>
int auxint;
devc = sdi->priv;
+ if (!devc)
+ return;
switch (devc->acq_req_pending) {
case 0: /* Should not happen... */
case AQ_U2:
case AQ_I1:
case AQ_I2:
- if (sr_atod(devc->buf, &dbl) != SR_OK) {
+ dbl = 0.0;
+ if (sr_atod_ascii(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:
+ auxint = 0;
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));
}
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
/* Only request the next packet if required. */
if (!((sdi->status == SR_ST_ACTIVE) && (devc->acq_running)))
* 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 <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
+#define LOG_PREFIX "motech-lps-30x"
+
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. */
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. */
struct sr_sw_limits limits;
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. */
gchar buf[LINELEN_MAX]; /**< Buffer for read callback */
int buflen; /**< Data len in buf */
* 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"
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
if (serial_write_blocking(serial, req, strlen(req),
serial_timeout(serial, strlen(req))) < 0) {
sr_err("Unable to send identification request.");
+ g_free(buf);
return NULL;
}
len = BUF_MAX;
return std_scan_complete(drv, devices);
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* Poll every 100ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 100,
norma_dmm_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = NULL,
.config_set = config_set,
.config_list = config_list,
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = NULL,
.config_set = config_set,
.config_list = config_list,
* 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"
}
if (sr_sw_limits_check(&devc->limits)) {
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
} else {
/* Request next package. */
if (devc->last_req_pending) {
#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
/** Strings for requests. */
extern const struct nmadmm_req nmadmm_requests[];
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
int type; /**< DM9x0, e.g. 5 = DM950 */
- /* Acquisition settings */
struct sr_sw_limits limits;
- /* 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. */
- /* Temporary state across callbacks */
uint8_t buf[NMADMM_BUFSIZE]; /**< Buffer for read callback */
int buflen; /**< Data len in buf */
};
#define SERIALCOMM "115200/8n1"
-static const uint32_t drvopts[] = {
- SR_CONF_LOGIC_ANALYZER,
-};
-
static const uint32_t scanopts[] = {
SR_CONF_CONN,
SR_CONF_SERIALCOMM,
};
+static const uint32_t drvopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
+};
+
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,
return std_scan_complete(di, g_slist_append(NULL, sdi));
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
return SR_ERR_ARG;
devc = sdi->priv;
+
switch (key) {
case SR_CONF_SAMPLERATE:
*data = g_variant_new_uint64(devc->cur_samplerate);
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
uint16_t flag;
uint64_t tmp_u64;
- int ret;
const char *stropt;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
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));
- break;
+ return ols_set_samplerate(sdi, g_variant_get_uint64(data));
case SR_CONF_LIMIT_SAMPLES:
tmp_u64 = g_variant_get_uint64(data);
if (tmp_u64 < MIN_NUM_SAMPLES)
return SR_ERR;
devc->limit_samples = tmp_u64;
- ret = SR_OK;
break;
case SR_CONF_CAPTURE_RATIO:
devc->capture_ratio = g_variant_get_uint64(data);
- if (devc->capture_ratio < 0 || devc->capture_ratio > 100)
- ret = SR_ERR;
- else
- ret = SR_OK;
break;
case SR_CONF_EXTERNAL_CLOCK:
if (g_variant_get_boolean(data)) {
sr_info("Disabled external clock.");
devc->flag_reg &= ~FLAG_CLOCK_EXTERNAL;
}
- ret = SR_OK;
break;
case SR_CONF_PATTERN_MODE:
stropt = g_variant_get_string(data, NULL);
- ret = SR_OK;
- flag = 0xffff;
if (!strcmp(stropt, STR_PATTERN_NONE)) {
sr_info("Disabling test modes.");
flag = 0x0000;
- }else if (!strcmp(stropt, STR_PATTERN_INTERNAL)) {
+ } else if (!strcmp(stropt, STR_PATTERN_INTERNAL)) {
sr_info("Enabling internal test mode.");
flag = FLAG_INTERNAL_TEST_MODE;
} else if (!strcmp(stropt, STR_PATTERN_EXTERNAL)) {
sr_info("Enabling external test mode.");
flag = FLAG_EXTERNAL_TEST_MODE;
} else {
- ret = SR_ERR;
- }
- if (flag != 0xffff) {
- devc->flag_reg &= ~(FLAG_INTERNAL_TEST_MODE | FLAG_EXTERNAL_TEST_MODE);
- devc->flag_reg |= flag;
+ return SR_ERR;
}
+ devc->flag_reg &= ~FLAG_INTERNAL_TEST_MODE;
+ devc->flag_reg &= ~FLAG_EXTERNAL_TEST_MODE;
+ devc->flag_reg |= flag;
break;
case SR_CONF_SWAP:
if (g_variant_get_boolean(data)) {
sr_info("Disabling channel swapping.");
devc->flag_reg &= ~FLAG_SWAP_CHANNELS;
}
- ret = SR_OK;
break;
-
case SR_CONF_RLE:
if (g_variant_get_boolean(data)) {
sr_info("Enabling RLE.");
sr_info("Disabling RLE.");
devc->flag_reg &= ~FLAG_RLE;
}
- ret = SR_OK;
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
struct dev_context *devc;
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
int num_ols_changrp, i;
- (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:
- 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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
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));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
case SR_CONF_PATTERN_MODE:
- *data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(patterns));
break;
case SR_CONF_LIMIT_SAMPLES:
if (!sdi)
if (devc->channel_mask & (0xff << (i * 8)))
num_ols_changrp++;
}
- grange[0] = g_variant_new_uint64(MIN_NUM_SAMPLES);
- 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);
+
+ *data = std_gvar_tuple_u64(MIN_NUM_SAMPLES,
+ (num_ols_changrp) ? devc->max_samples / num_ols_changrp : MIN_NUM_SAMPLES);
break;
default:
return SR_ERR_NA;
int num_ols_changrp;
int ret, i;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
serial = sdi->conn;
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
-#define LOG_PREFIX "ols"
+#define LOG_PREFIX "openbench-logic-sniffer"
#define NUM_CHANNELS 32
#define NUM_TRIGGER_STAGES 4
#define FLAG_FILTER (1 << 1)
#define FLAG_DEMUX (1 << 0)
-/* Private, per-device-instance driver context. */
struct dev_context {
- /* Fixed device settings */
int max_channels;
uint32_t max_samples;
uint32_t max_samplerate;
uint32_t protocol_version;
- /* Acquisition settings */
uint64_t cur_samplerate;
uint32_t cur_samplerate_divider;
uint64_t limit_samples;
- int capture_ratio;
+ uint64_t capture_ratio;
int trigger_at;
uint32_t channel_mask;
uint32_t trigger_mask[NUM_TRIGGER_STAGES];
int num_stages;
uint16_t flag_reg;
- /* Operational states */
unsigned int num_transfers;
unsigned int num_samples;
int num_bytes;
int cnt_samples;
int cnt_samples_rle;
- /* Temporary variables */
unsigned int rle_count;
unsigned char sample[4];
unsigned char tmp_sample[4];
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
#include <config.h>
+#include <string.h>
#include "protocol.h"
#define SERIALCOMM "115200/8n1"
};
static const char *weight_freq[] = {
- "A",
- "C",
+ "A", "C",
};
static const char *weight_time[] = {
- "F",
- "S",
+ "F", "S",
};
static const uint64_t meas_ranges[][2] = {
};
static const char *data_sources[] = {
- "Live",
- "Memory",
+ "Live", "Memory",
};
static GSList *scan(struct sr_dev_driver *di, GSList *options)
return std_scan_complete(di, g_slist_append(NULL, sdi));
}
-static int dev_clear(const struct sr_dev_driver *di)
-{
- return std_dev_clear(di, NULL);
-}
-
static int config_get(uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
- GVariant *range[2];
uint64_t low, high;
uint64_t tmp;
int ret;
return SR_ERR_ARG;
devc = sdi->priv;
- ret = SR_OK;
+
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
*data = g_variant_new_uint64(devc->limit_samples);
return SR_ERR;
break;
case SR_CONF_SPL_MEASUREMENT_RANGE:
- if ((ret = pce_322a_meas_range_get(sdi, &low, &high)) == SR_OK) {
- range[0] = g_variant_new_uint64(low);
- range[1] = g_variant_new_uint64(high);
- *data = g_variant_new_tuple(range, 2);
- }
+ if ((ret = pce_322a_meas_range_get(sdi, &low, &high)) == SR_OK)
+ *data = std_gvar_tuple_u64(low, high);
+ else
+ return ret;
break;
case SR_CONF_POWER_OFF:
*data = g_variant_new_boolean(FALSE);
return SR_ERR_NA;
}
- return ret;
+ 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_u64, low, high;
- unsigned int i;
- int ret;
- const char *tmp_str;
+ int idx;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
- ret = SR_OK;
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
- tmp_u64 = g_variant_get_uint64(data);
- devc->limit_samples = tmp_u64;
- ret = SR_OK;
+ devc->limit_samples = g_variant_get_uint64(data);
break;
case SR_CONF_SPL_WEIGHT_FREQ:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "A"))
- ret = pce_322a_weight_freq_set(sdi,
- SR_MQFLAG_SPL_FREQ_WEIGHT_A);
- else if (!strcmp(tmp_str, "C"))
- ret = pce_322a_weight_freq_set(sdi,
- SR_MQFLAG_SPL_FREQ_WEIGHT_C);
- else
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_freq))) < 0)
return SR_ERR_ARG;
- break;
+ return pce_322a_weight_freq_set(sdi, (weight_freq[idx][0] == 'A') ?
+ SR_MQFLAG_SPL_FREQ_WEIGHT_A : SR_MQFLAG_SPL_FREQ_WEIGHT_C);
case SR_CONF_SPL_WEIGHT_TIME:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "F"))
- ret = pce_322a_weight_time_set(sdi,
- SR_MQFLAG_SPL_TIME_WEIGHT_F);
- else if (!strcmp(tmp_str, "S"))
- ret = pce_322a_weight_time_set(sdi,
- SR_MQFLAG_SPL_TIME_WEIGHT_S);
- else
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(weight_time))) < 0)
return SR_ERR_ARG;
- break;
+ return pce_322a_weight_time_set(sdi, (weight_time[idx][0] == 'F') ?
+ SR_MQFLAG_SPL_TIME_WEIGHT_F : SR_MQFLAG_SPL_TIME_WEIGHT_S);
case SR_CONF_SPL_MEASUREMENT_RANGE:
- g_variant_get(data, "(tt)", &low, &high);
- ret = SR_ERR_ARG;
- for (i = 0; i < ARRAY_SIZE(meas_ranges); i++) {
- if (meas_ranges[i][0] == low && meas_ranges[i][1] == high) {
- ret = pce_322a_meas_range_set(sdi, low, high);
- break;
- }
- }
- break;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(meas_ranges))) < 0)
+ return SR_ERR_ARG;
+ return pce_322a_meas_range_set(sdi, meas_ranges[idx][0], meas_ranges[idx][1]);
case SR_CONF_POWER_OFF:
if (g_variant_get_boolean(data))
- ret = pce_322a_power_off(sdi);
+ return pce_322a_power_off(sdi);
break;
case SR_CONF_DATA_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "Live"))
- devc->cur_data_source = DATA_SOURCE_LIVE;
- else if (!strcmp(tmp_str, "Memory"))
- devc->cur_data_source = DATA_SOURCE_MEMORY;
- else
- return SR_ERR;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->cur_data_source = idx;
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
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];
- GVariantBuilder gvb;
- unsigned int i;
- int ret;
-
- (void)cg;
-
- ret = SR_OK;
- 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;
- }
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_SPL_WEIGHT_FREQ:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_freq));
+ break;
+ case SR_CONF_SPL_WEIGHT_TIME:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(weight_time));
+ break;
+ case SR_CONF_SPL_MEASUREMENT_RANGE:
+ *data = std_gvar_tuple_array(ARRAY_AND_SIZE(meas_ranges));
+ break;
+ case SR_CONF_DATA_SOURCE:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
+ break;
+ default:
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static int dev_open(struct sr_dev_inst *sdi)
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->buffer_len = 0;
devc->memory_state = MEM_STATE_REQUEST_MEMORY_USAGE;
std_session_send_df_header(sdi);
- /* Poll every 150ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 150,
pce_322a_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = dev_clear,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
*/
#include <config.h>
+#include <string.h>
#include "protocol.h"
static int send_command(const struct sr_dev_inst *sdi, uint16_t command)
if (!(serial = sdi->conn))
return SR_ERR;
- if (serial_write_nonblocking(serial, (const void *)buffer, 2) != 2)
- return SR_ERR;
-
- return SR_OK;
+ return serial_write_blocking(serial, (const void *)buffer, 2, 0);
}
static int send_long_command(const struct sr_dev_inst *sdi, uint32_t command)
if (!(serial = sdi->conn))
return SR_ERR;
- if (serial_write_nonblocking(serial, (const void *)buffer, 4) != 4)
- return SR_ERR;
-
- return SR_OK;
+ return serial_write_blocking(serial, (const void *)buffer, 4, 0);
}
static void send_data(const struct sr_dev_inst *sdi, float sample)
devc->num_samples++;
/* Limiting number of samples is only supported for live data. */
if (devc->cur_data_source == DATA_SOURCE_LIVE && devc->limit_samples && devc->num_samples >= devc->limit_samples)
- sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
+ sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
}
static void process_measurement(const struct sr_dev_inst *sdi)
static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c)
{
struct dev_context *devc;
- unsigned int i;
devc = sdi->priv;
if (devc->buffer_len < BUFFER_SIZE) {
devc->buffer[devc->buffer_len++] = c;
} else {
- for (i = 1; i < BUFFER_SIZE; i++)
- devc->buffer[i - 1] = devc->buffer[i];
+ memmove(devc->buffer, devc->buffer + 1, BUFFER_SIZE - 1);
devc->buffer[BUFFER_SIZE - 1] = c;
}
static void process_usage_byte(const struct sr_dev_inst *sdi, uint8_t c)
{
struct dev_context *devc;
- unsigned int i;
devc = sdi->priv;
if (devc->buffer_len < MEM_USAGE_BUFFER_SIZE) {
devc->buffer[devc->buffer_len++] = c;
} else {
- for (i = 1; i < MEM_USAGE_BUFFER_SIZE; i++)
- devc->buffer[i - 1] = devc->buffer[i];
+ memmove(devc->buffer, devc->buffer + 1, MEM_USAGE_BUFFER_SIZE - 1);
devc->buffer[MEM_USAGE_BUFFER_SIZE - 1] = c;
}
static void process_memory_byte(const struct sr_dev_inst *sdi, uint8_t c)
{
struct dev_context *devc;
- unsigned int i;
devc = sdi->priv;
if (devc->buffer_len < MEM_DATA_BUFFER_SIZE) {
devc->buffer[devc->buffer_len++] = c;
} else {
- for (i = 1; i < MEM_DATA_BUFFER_SIZE; i++)
- devc->buffer[i - 1] = devc->buffer[i];
+ memmove(devc->buffer, devc->buffer + 1, MEM_DATA_BUFFER_SIZE - 1);
devc->buffer[MEM_DATA_BUFFER_SIZE - 1] = c;
}
MEM_STATE_GET_MEMORY_BLOCK,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
uint64_t cur_mqflags;
uint8_t cur_meas_range;
- /* Acquisition settings */
uint8_t cur_data_source;
uint64_t limit_samples;
- /* Operational state */
uint64_t num_samples;
/* Memory reading state */
uint16_t memory_block_counter; /* Number of memory blocks retrieved so far. */
uint8_t memory_block_cursor; /* Number of bytes retrieved in current memory block. */
- /* Temporary state across callbacks. */
uint8_t buffer[BUFFER_SIZE];
int buffer_len;
int buffer_skip; /* Number of bytes to skip in memory mode. */
#include <config.h>
#include "protocol.h"
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_LOGIC_ANALYZER,
+};
+
+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,
devices = NULL;
- /* Allocate memory for our private device context. */
devc = g_malloc0(sizeof(struct dev_context));
- /* Device-specific settings */
devc->max_samplebytes = devc->max_samplerate = devc->protocol_version = 0;
- /* 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) {
+ 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
goto err_close_ftdic;
}
- /* Close device. We'll reopen it again when we need it. */
p_ols_close(devc);
/* Parse the metadata. */
err_close_ftdic:
p_ols_close(devc);
err_free_ftdic:
- ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
+ ftdi_free(devc->ftdic);
err_free_ftdi_buf:
g_free(devc->ftdi_buf);
g_free(devc);
return NULL;
}
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
-
ftdi_free(devc->ftdic);
g_free(devc->ftdi_buf);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
return SR_ERR_ARG;
devc = sdi->priv;
+
switch (key) {
case SR_CONF_SAMPLERATE:
*data = g_variant_new_uint64(devc->cur_samplerate);
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
uint16_t flag;
uint64_t tmp_u64;
- int ret;
const char *stropt;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
tmp_u64 = g_variant_get_uint64(data);
if (tmp_u64 < samplerates[0] || tmp_u64 > samplerates[1])
return SR_ERR_SAMPLERATE;
- ret = p_ols_set_samplerate(sdi, g_variant_get_uint64(data));
- break;
+ return p_ols_set_samplerate(sdi, g_variant_get_uint64(data));
case SR_CONF_LIMIT_SAMPLES:
tmp_u64 = g_variant_get_uint64(data);
if (tmp_u64 < MIN_NUM_SAMPLES)
return SR_ERR;
devc->limit_samples = tmp_u64;
- ret = SR_OK;
break;
case SR_CONF_CAPTURE_RATIO:
devc->capture_ratio = g_variant_get_uint64(data);
- if (devc->capture_ratio < 0 || devc->capture_ratio > 100)
- ret = SR_ERR;
- else
- ret = SR_OK;
break;
case SR_CONF_EXTERNAL_CLOCK:
if (g_variant_get_boolean(data)) {
sr_info("Disabled external clock.");
devc->flag_reg &= ~FLAG_CLOCK_EXTERNAL;
}
- ret = SR_OK;
break;
case SR_CONF_PATTERN_MODE:
stropt = g_variant_get_string(data, NULL);
- ret = SR_OK;
- flag = 0xffff;
if (!strcmp(stropt, STR_PATTERN_NONE)) {
sr_info("Disabling test modes.");
flag = 0x0000;
- }else if (!strcmp(stropt, STR_PATTERN_INTERNAL)) {
+ } else if (!strcmp(stropt, STR_PATTERN_INTERNAL)) {
sr_info("Enabling internal test mode.");
flag = FLAG_INTERNAL_TEST_MODE;
} else if (!strcmp(stropt, STR_PATTERN_EXTERNAL)) {
sr_info("Enabling external test mode.");
flag = FLAG_EXTERNAL_TEST_MODE;
} else {
- ret = SR_ERR;
- }
- if (flag != 0xffff) {
- devc->flag_reg &= ~(FLAG_INTERNAL_TEST_MODE | FLAG_EXTERNAL_TEST_MODE);
- devc->flag_reg |= flag;
+ return SR_ERR;
}
+ devc->flag_reg &= ~FLAG_INTERNAL_TEST_MODE;
+ devc->flag_reg &= ~FLAG_EXTERNAL_TEST_MODE;
+ devc->flag_reg |= flag;
break;
case SR_CONF_SWAP:
if (g_variant_get_boolean(data)) {
sr_info("Disabling channel swapping.");
devc->flag_reg &= ~FLAG_SWAP_CHANNELS;
}
- ret = SR_OK;
break;
-
case SR_CONF_RLE:
if (g_variant_get_boolean(data)) {
sr_info("Enabling RLE.");
sr_info("Disabling RLE.");
devc->flag_reg &= ~FLAG_RLE;
}
- ret = SR_OK;
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
struct dev_context *devc;
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
int num_pols_changrp, i;
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
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);
+ *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
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));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
case SR_CONF_PATTERN_MODE:
- *data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(patterns));
break;
case SR_CONF_LIMIT_SAMPLES:
if (!sdi)
/* 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);
- 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);
+
+ *data = std_gvar_tuple_u64(MIN_NUM_SAMPLES,
+ (num_pols_changrp) ? devc->max_samplebytes / num_pols_changrp : MIN_NUM_SAMPLES);
break;
default:
return SR_ERR_NA;
devc = sdi->priv;
- if (p_ols_open(devc) != SR_OK) {
- return SR_ERR;
- } else {
- sdi->status = SR_ST_ACTIVE;
- return SR_OK;
- }
+ return p_ols_open(devc);
}
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;
+ return p_ols_close(devc);
}
static int set_trigger(const struct sr_dev_inst *sdi, int stage)
int num_pols_changrp, samplespercount;
int ret, i;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
pols_channel_mask(sdi);
devc = sdi->priv;
- sr_dbg("Stopping acquisition.");
write_shortcommand(devc, CMD_RESET);
write_shortcommand(devc, CMD_RESET);
write_shortcommand(devc, CMD_RESET);
/* 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) {
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;
}
uint8_t key, type, token;
GString *tmp_str, *devname, *version;
guchar tmp_c;
- int index, i;
+ int index;
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
break;
case 1:
/* 32-bit unsigned integer */
- tmp_int = 0;
- for (i = 0; i < 4; i++) {
- tmp_int = (tmp_int << 8) | buf[index++];
- }
+ tmp_int = RB32(&buf[index]);
+ index += sizeof(uint32_t);
sr_dbg("Got metadata key 0x%.2x value 0x%.8x.",
key, tmp_int);
switch (token) {
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);
+ sr_dev_acquisition_stop(sdi);
return FALSE;
}
if (bytes_read == 0) {
}
g_free(devc->raw_sample_buf);
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
return TRUE;
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
-#define LOG_PREFIX "p-ols"
+#define LOG_PREFIX "pipistrello-ols"
#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 FLAG_FILTER (1 << 1)
#define FLAG_DEMUX (1 << 0)
-/* 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_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;
+ uint64_t capture_ratio;
int trigger_at;
uint32_t channel_mask;
uint32_t trigger_mask[NUM_TRIGGER_STAGES];
int num_stages;
uint16_t flag_reg;
- /* Operational states */
unsigned int num_transfers;
unsigned int num_samples;
int num_bytes;
unsigned int cnt_samples;
int cnt_samples_rle;
- /* Temporary variables */
unsigned int rle_count;
unsigned char sample[4];
unsigned char tmp_sample[4];
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 James Churchill <pelrun@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 scanopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+ SR_CONF_MODBUSADDR,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_POWER_SUPPLY,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_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_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
+ SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | 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,
+};
+
+/* Model ID, model name, max current, max voltage, max power */
+static const struct rdtech_dps_model supported_models[] = {
+ { 3005, "DPS3005", 3, 50, 160 },
+ { 5005, "DPS5005", 5, 50, 250 },
+ { 5205, "DPH5005", 5, 50, 250 },
+ { 5015, "DPS5015", 15, 50, 750 },
+ { 5020, "DPS5020", 20, 50, 1000 },
+ { 8005, "DPS8005", 5, 80, 408 },
+};
+
+static struct sr_dev_driver rdtech_dps_driver_info;
+
+static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus)
+{
+ const struct rdtech_dps_model *model = NULL;
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ uint16_t id, version;
+ unsigned int i;
+
+ int ret = rdtech_dps_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("Unknown model: %d.", id);
+ return NULL;
+ }
+
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->status = SR_ST_INACTIVE;
+ sdi->vendor = g_strdup("RDTech");
+ sdi->model = g_strdup(model->name);
+ sdi->version = g_strdup_printf("v%d", version);
+ sdi->conn = modbus;
+ sdi->driver = &rdtech_dps_driver_info;
+ sdi->inst_type = SR_INST_MODBUS;
+
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "I");
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P");
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ sr_sw_limits_init(&devc->limits);
+ devc->model = model;
+ devc->expecting_registers = 0;
+
+ 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 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;
+
+ rdtech_dps_set_reg(modbus, REG_LOCK, 1);
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_modbus_dev_inst *modbus;
+
+ modbus = sdi->conn;
+
+ if (!modbus)
+ return SR_ERR_BUG;
+
+ 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);
+ }
+
+ rdtech_dps_set_reg(modbus, REG_LOCK, 0);
+
+ return sr_modbus_close(modbus);
+}
+
+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;
+ int ret;
+ uint16_t ivalue;
+
+ (void)cg;
+
+ modbus = sdi->conn;
+ devc = sdi->priv;
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ ret = sr_sw_limits_config_get(&devc->limits, key, data);
+ break;
+ case SR_CONF_ENABLED:
+ if ((ret = rdtech_dps_get_reg(modbus, REG_ENABLE, &ivalue)) == SR_OK)
+ *data = g_variant_new_boolean(ivalue);
+ break;
+ case SR_CONF_REGULATION:
+ if ((ret = rdtech_dps_get_reg(modbus, REG_CV_CC, &ivalue)) != SR_OK)
+ break;
+ *data = g_variant_new_string((ivalue == MODE_CC) ? "CC" : "CV");
+ break;
+ case SR_CONF_VOLTAGE:
+ if ((ret = rdtech_dps_get_reg(modbus, REG_UOUT, &ivalue)) == SR_OK)
+ *data = g_variant_new_double((float)ivalue / 100.0f);
+ break;
+ case SR_CONF_VOLTAGE_TARGET:
+ if ((ret = rdtech_dps_get_reg(modbus, REG_USET, &ivalue)) == SR_OK)
+ *data = g_variant_new_double((float)ivalue / 100.0f);
+ break;
+ case SR_CONF_CURRENT:
+ if ((ret = rdtech_dps_get_reg(modbus, REG_IOUT, &ivalue)) == SR_OK)
+ *data = g_variant_new_double((float)ivalue / 100.0f);
+ break;
+ case SR_CONF_CURRENT_LIMIT:
+ if ((ret = rdtech_dps_get_reg(modbus, REG_ISET, &ivalue)) == SR_OK)
+ *data = g_variant_new_double((float)ivalue / 1000.0f);
+ break;
+ case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+ *data = g_variant_new_boolean(TRUE);
+ break;
+ case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE:
+ if ((ret = rdtech_dps_get_reg(modbus, REG_PROTECT, &ivalue)) == SR_OK)
+ *data = g_variant_new_boolean(ivalue == STATE_OVP);
+ break;
+ case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+ if ((ret = rdtech_dps_get_reg(modbus, PRE_OVPSET, &ivalue)) == SR_OK)
+ *data = g_variant_new_double((float)ivalue / 100.0f);
+ break;
+ case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+ *data = g_variant_new_boolean(TRUE);
+ break;
+ case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE:
+ if ((ret = rdtech_dps_get_reg(modbus, REG_PROTECT, &ivalue)) == SR_OK)
+ *data = g_variant_new_boolean(ivalue == STATE_OCP);
+ break;
+ case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+ if ((ret = rdtech_dps_get_reg(modbus, PRE_OCPSET, &ivalue)) == SR_OK)
+ *data = g_variant_new_double((float)ivalue / 1000.0f);
+ 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;
+
+ (void)cg;
+
+ modbus = sdi->conn;
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ return sr_sw_limits_config_set(&devc->limits, key, data);
+ case SR_CONF_ENABLED:
+ return rdtech_dps_set_reg(modbus, REG_ENABLE, g_variant_get_boolean(data));
+ case SR_CONF_VOLTAGE_TARGET:
+ return rdtech_dps_set_reg(modbus, REG_USET, g_variant_get_double(data) * 100);
+ case SR_CONF_CURRENT_LIMIT:
+ return rdtech_dps_set_reg(modbus, REG_ISET, g_variant_get_double(data) * 1000);
+ case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+ return rdtech_dps_set_reg(modbus, PRE_OVPSET, g_variant_get_double(data) * 100);
+ case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+ return rdtech_dps_set_reg(modbus, PRE_OCPSET, g_variant_get_double(data) * 1000);
+ 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;
+
+ devc = (sdi) ? sdi->priv : NULL;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_VOLTAGE_TARGET:
+ *data = std_gvar_min_max_step(0.0, devc->model->max_voltage, 0.001);
+ break;
+ case SR_CONF_CURRENT_LIMIT:
+ *data = std_gvar_min_max_step(0.0, devc->model->max_current, 0.0001);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_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_source_add(sdi->session, modbus, G_IO_IN, 10,
+ rdtech_dps_receive_data, (void *)sdi)) != SR_OK)
+ return ret;
+
+ sr_sw_limits_acquisition_start(&devc->limits);
+ std_session_send_df_header(sdi);
+
+ return rdtech_dps_capture_start(sdi);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ struct sr_modbus_dev_inst *modbus;
+
+ std_session_send_df_end(sdi);
+
+ modbus = sdi->conn;
+ sr_modbus_source_remove(sdi->session, modbus);
+
+ return SR_OK;
+}
+
+static struct sr_dev_driver rdtech_dps_driver_info = {
+ .name = "rdtech-dps",
+ .longname = "RDTech DPS/DPH series power supply",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(rdtech_dps_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 James Churchill <pelrun@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"
+
+SR_PRIV int rdtech_dps_get_reg(struct sr_modbus_dev_inst *modbus,
+ uint16_t address, uint16_t *value)
+{
+ uint16_t registers[1];
+ int ret = sr_modbus_read_holding_registers(modbus, address, 1, registers);
+ *value = RB16(registers + 0);
+ return ret;
+}
+
+SR_PRIV int rdtech_dps_set_reg(struct sr_modbus_dev_inst *modbus,
+ uint16_t address, uint16_t value)
+{
+ uint16_t registers[1];
+ WB16(registers, value);
+ return sr_modbus_write_multiple_registers(modbus, address, 1, registers);
+}
+
+SR_PRIV int rdtech_dps_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, REG_MODEL, 2, registers);
+ if (ret == SR_OK) {
+ *model = RB16(registers + 0);
+ *version = RB16(registers + 1);
+ sr_info("RDTech PSU model: %d version: %d", *model, *version);
+ }
+ return ret;
+}
+
+static void send_value(const struct sr_dev_inst *sdi, struct sr_channel *ch,
+ float value, enum sr_mq mq, enum sr_unit unit, int digits)
+{
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ struct sr_analog_encoding encoding;
+ struct sr_analog_meaning meaning;
+ struct sr_analog_spec spec;
+
+ sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+ analog.meaning->channels = g_slist_append(NULL, ch);
+ analog.num_samples = 1;
+ analog.data = &value;
+ analog.meaning->mq = mq;
+ analog.meaning->unit = unit;
+ analog.meaning->mqflags = SR_MQFLAG_DC;
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(sdi, &packet);
+ g_slist_free(analog.meaning->channels);
+}
+
+SR_PRIV int rdtech_dps_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, REG_UOUT, 3, NULL)) == SR_OK)
+ devc->expecting_registers = 2;
+ return ret;
+}
+
+SR_PRIV int rdtech_dps_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[3];
+
+ (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, 3, registers) == SR_OK) {
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+
+ send_value(sdi, sdi->channels->data,
+ RB16(registers + 0) / 100.0f,
+ SR_MQ_VOLTAGE, SR_UNIT_VOLT, 3);
+ send_value(sdi, sdi->channels->next->data,
+ RB16(registers + 1) / 1000.0f,
+ SR_MQ_CURRENT, SR_UNIT_AMPERE, 4);
+ send_value(sdi, sdi->channels->next->next->data,
+ RB16(registers + 2) / 100.0f,
+ SR_MQ_POWER, SR_UNIT_WATT, 3);
+
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+ sr_sw_limits_update_samples_read(&devc->limits, 1);
+ }
+
+ if (sr_sw_limits_check(&devc->limits)) {
+ sr_dev_acquisition_stop(sdi);
+ return TRUE;
+ }
+
+ rdtech_dps_capture_start(sdi);
+ return TRUE;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 James Churchill <pelrun@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_RDTECH_DPS_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_RDTECH_DPS_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "rdtech-dps"
+
+struct rdtech_dps_model {
+ unsigned int id;
+ const char *name;
+ unsigned int max_current;
+ unsigned int max_voltage;
+ unsigned int max_power;
+};
+
+struct dev_context {
+ const struct rdtech_dps_model *model;
+ struct sr_sw_limits limits;
+ int expecting_registers;
+};
+
+enum rdtech_dps_register {
+ REG_USET = 0x00, /* Mirror of 0x50 */
+ REG_ISET = 0x01, /* Mirror of 0x51 */
+ REG_UOUT = 0x02,
+ REG_IOUT = 0x03,
+ REG_POWER = 0x04,
+ REG_UIN = 0x05,
+ REG_LOCK = 0x06,
+ REG_PROTECT = 0x07,
+ REG_CV_CC = 0x08,
+ REG_ENABLE = 0x09,
+ REG_BACKLIGHT = 0x0A, /* Mirror of 0x55 */
+ REG_MODEL = 0x0B,
+ REG_VERSION = 0x0C,
+
+ REG_PRESET = 0x23, /* Loads a preset into preset 0. */
+
+/*
+ * Add (preset * 0x10) to each of the following, for preset 1-9.
+ * Preset 0 regs below are the active output settings.
+ */
+ PRE_USET = 0x50,
+ PRE_ISET = 0x51,
+ PRE_OVPSET = 0x52,
+ PRE_OCPSET = 0x53,
+ PRE_OPPSET = 0x54,
+ PRE_BACKLIGHT = 0x55,
+ PRE_DISABLE = 0x56, /* Disable output if 0 is copied here from a preset (1 is no change). */
+ PRE_BOOT = 0x57, /* Enable output at boot if 1. */
+};
+
+enum rdtech_dps_state {
+ STATE_NORMAL = 0,
+ STATE_OVP = 1,
+ STATE_OCP = 2,
+ STATE_OPP = 3,
+};
+
+enum rdtech_dps_mode {
+ MODE_CV = 0,
+ MODE_CC = 1,
+};
+
+SR_PRIV int rdtech_dps_get_reg(struct sr_modbus_dev_inst *modbus, uint16_t address, uint16_t *value);
+SR_PRIV int rdtech_dps_set_reg(struct sr_modbus_dev_inst *modbus, uint16_t address, uint16_t value);
+
+SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
+ uint16_t *model, uint16_t *version);
+
+SR_PRIV int rdtech_dps_capture_start(const struct sr_dev_inst *sdi);
+SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data);
+
+#endif
static const uint32_t scanopts[] = {
SR_CONF_CONN,
- SR_CONF_SERIALCOMM
+ SR_CONF_SERIALCOMM,
};
static const uint32_t drvopts[] = {
SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
-static const uint32_t analog_devopts[] = {
+static const uint32_t devopts_cg_analog[] = {
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,
{ 100, 1 },
};
-#define NUM_TIMEBASE ARRAY_SIZE(timebases)
-#define NUM_VDIV ARRAY_SIZE(vdivs)
-
-static const char *trigger_sources[] = {
- "CH1",
- "CH2",
- "CH3",
- "CH4",
- "EXT",
- "AC Line",
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
- "D8",
- "D9",
- "D10",
- "D11",
- "D12",
- "D13",
- "D14",
- "D15",
+static const char *trigger_sources_2_chans[] = {
+ "CH1", "CH2",
+ "EXT", "AC Line",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+ "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15",
+};
+
+static const char *trigger_sources_4_chans[] = {
+ "CH1", "CH2", "CH3", "CH4",
+ "EXT", "AC Line",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+ "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15",
};
static const char *trigger_slopes[] = {
- "r",
- "f",
+ "r", "f",
};
static const char *coupling[] = {
- "AC",
- "DC",
- "GND",
+ "AC", "DC", "GND",
};
static const uint64_t probe_factor[] = {
- 1,
- 2,
- 5,
- 10,
- 20,
- 50,
- 100,
- 200,
- 500,
- 1000,
+ 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000,
};
/* Do not change the order of entries */
"Segmented",
};
+static const struct rigol_ds_command std_cmd[] = {
+ { CMD_GET_HORIZ_TRIGGERPOS, ":TIM:OFFS?" },
+ { CMD_SET_HORIZ_TRIGGERPOS, ":TIM:OFFS %s" },
+};
+
+static const struct rigol_ds_command mso7000a_cmd[] = {
+ { CMD_GET_HORIZ_TRIGGERPOS, ":TIM:POS?" },
+ { CMD_SET_HORIZ_TRIGGERPOS, ":TIM:POS %s" },
+};
+
enum vendor {
RIGOL,
AGILENT,
DS2000A,
DSO1000,
DS1000Z,
+ DS4000,
+ MSO7000A,
};
/* short name, full name */
};
#define VENDOR(x) &supported_vendors[x]
-/* vendor, series, protocol, max timebase, min vdiv, number of horizontal divs,
- * live waveform samples, memory buffer samples */
+/* vendor, series/name, protocol, data format, max timebase, min vdiv,
+ * number of horizontal divs, live waveform samples, memory buffer samples */
static const struct rigol_ds_series supported_series[] = {
[VS5000] = {VENDOR(RIGOL), "VS5000", PROTOCOL_V1, FORMAT_RAW,
{50, 1}, {2, 1000}, 14, 2048, 0},
{50, 1}, {2, 1000}, 12, 600, 20480},
[DS1000Z] = {VENDOR(RIGOL), "DS1000Z", PROTOCOL_V4, FORMAT_IEEE488_2,
{50, 1}, {1, 1000}, 12, 1200, 12000000},
+ [DS4000] = {VENDOR(RIGOL), "DS4000", PROTOCOL_V4, FORMAT_IEEE488_2,
+ {1000, 1}, {1, 1000}, 14, 1400, 14000},
+ [MSO7000A] = {VENDOR(AGILENT), "MSO7000A", PROTOCOL_V4, FORMAT_IEEE488_2,
+ {50, 1}, {2, 1000}, 10, 1000, 8000000},
};
#define SERIES(x) &supported_series[x]
+/*
+ * Use a macro to select the correct list of trigger sources and its length
+ * based on the number of analog channels and presence of digital channels.
+ */
+#define CH_INFO(num, digital) \
+ num, digital, trigger_sources_##num##_chans, \
+ digital ? ARRAY_SIZE(trigger_sources_##num##_chans) : (num + 2)
/* series, model, min timebase, analog channels, digital */
static const struct rigol_ds_model supported_models[] = {
- {SERIES(VS5000), "VS5022", {20, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5042", {10, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5062", {5, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5102", {2, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5202", {2, 1000000000}, 2, false},
- {SERIES(VS5000), "VS5022D", {20, 1000000000}, 2, true},
- {SERIES(VS5000), "VS5042D", {10, 1000000000}, 2, true},
- {SERIES(VS5000), "VS5062D", {5, 1000000000}, 2, true},
- {SERIES(VS5000), "VS5102D", {2, 1000000000}, 2, true},
- {SERIES(VS5000), "VS5202D", {2, 1000000000}, 2, true},
- {SERIES(DS1000), "DS1052E", {5, 1000000000}, 2, false},
- {SERIES(DS1000), "DS1102E", {2, 1000000000}, 2, false},
- {SERIES(DS1000), "DS1152E", {2, 1000000000}, 2, false},
- {SERIES(DS1000), "DS1052D", {5, 1000000000}, 2, true},
- {SERIES(DS1000), "DS1102D", {2, 1000000000}, 2, true},
- {SERIES(DS1000), "DS1152D", {2, 1000000000}, 2, true},
- {SERIES(DS2000), "DS2072", {5, 1000000000}, 2, false},
- {SERIES(DS2000), "DS2102", {5, 1000000000}, 2, false},
- {SERIES(DS2000), "DS2202", {2, 1000000000}, 2, false},
- {SERIES(DS2000), "DS2302", {1, 1000000000}, 2, false},
- {SERIES(DS2000A), "DS2072A", {5, 1000000000}, 2, false},
- {SERIES(DS2000A), "DS2102A", {5, 1000000000}, 2, false},
- {SERIES(DS2000A), "DS2202A", {2, 1000000000}, 2, false},
- {SERIES(DS2000A), "DS2302A", {1, 1000000000}, 2, false},
- {SERIES(DS2000A), "MSO2072A", {5, 1000000000}, 2, true},
- {SERIES(DS2000A), "MSO2102A", {5, 1000000000}, 2, true},
- {SERIES(DS2000A), "MSO2202A", {2, 1000000000}, 2, true},
- {SERIES(DS2000A), "MSO2302A", {1, 1000000000}, 2, true},
- {SERIES(DSO1000), "DSO1002A", {5, 1000000000}, 2, false},
- {SERIES(DSO1000), "DSO1004A", {5, 1000000000}, 4, false},
- {SERIES(DSO1000), "DSO1012A", {2, 1000000000}, 2, false},
- {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), "DS1074Z Plus", {5, 1000000000}, 4, false},
- {SERIES(DS1000Z), "DS1104Z Plus", {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},
+ {SERIES(VS5000), "VS5022", {20, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(VS5000), "VS5042", {10, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(VS5000), "VS5062", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(VS5000), "VS5102", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(VS5000), "VS5202", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(VS5000), "VS5022D", {20, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(VS5000), "VS5042D", {10, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(VS5000), "VS5062D", {5, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(VS5000), "VS5102D", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(VS5000), "VS5202D", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(DS1000), "DS1052E", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS1000), "DS1102E", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS1000), "DS1152E", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS1000), "DS1052D", {5, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(DS1000), "DS1102D", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(DS1000), "DS1152D", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(DS2000), "DS2072", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS2000), "DS2102", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS2000), "DS2202", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS2000), "DS2302", {1, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS2000A), "DS2072A", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS2000A), "DS2102A", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS2000A), "DS2202A", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS2000A), "DS2302A", {1, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DS2000A), "MSO2072A", {5, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(DS2000A), "MSO2102A", {5, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(DS2000A), "MSO2202A", {2, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(DS2000A), "MSO2302A", {1, 1000000000}, CH_INFO(2, true), std_cmd},
+ {SERIES(DSO1000), "DSO1002A", {5, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DSO1000), "DSO1004A", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DSO1000), "DSO1012A", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DSO1000), "DSO1014A", {2, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DSO1000), "DSO1022A", {2, 1000000000}, CH_INFO(2, false), std_cmd},
+ {SERIES(DSO1000), "DSO1024A", {2, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DS1000Z), "DS1054Z", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DS1000Z), "DS1074Z", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DS1000Z), "DS1104Z", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DS1000Z), "DS1074Z-S", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DS1000Z), "DS1104Z-S", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DS1000Z), "DS1074Z Plus", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DS1000Z), "DS1104Z Plus", {5, 1000000000}, CH_INFO(4, false), std_cmd},
+ {SERIES(DS1000Z), "MSO1074Z", {5, 1000000000}, CH_INFO(4, true), std_cmd},
+ {SERIES(DS1000Z), "MSO1104Z", {5, 1000000000}, CH_INFO(4, true), std_cmd},
+ {SERIES(DS1000Z), "MSO1074Z-S", {5, 1000000000}, CH_INFO(4, true), std_cmd},
+ {SERIES(DS1000Z), "MSO1104Z-S", {5, 1000000000}, CH_INFO(4, true), std_cmd},
+ {SERIES(DS4000), "DS4024", {1, 1000000000}, CH_INFO(4, false), std_cmd},
+ /* TODO: Digital channels are not yet supported on MSO7000A. */
+ {SERIES(MSO7000A), "MSO7034A", {2, 1000000000}, CH_INFO(4, false), mso7000a_cmd},
};
static struct sr_dev_driver rigol_ds_driver_info;
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
unsigned int i;
- devc = priv;
g_free(devc->data);
g_free(devc->buffer);
for (i = 0; i < ARRAY_SIZE(devc->coupling); i++)
g_free(devc->trigger_source);
g_free(devc->trigger_slope);
g_free(devc->analog_groups);
- g_free(devc);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
devc->digital_group);
}
- for (i = 0; i < NUM_TIMEBASE; i++) {
+ for (i = 0; i < ARRAY_SIZE(timebases); i++) {
if (!memcmp(&devc->model->min_timebase, &timebases[i], sizeof(uint64_t[2])))
devc->timebases = &timebases[i];
if (!memcmp(&devc->model->series->max_timebase, &timebases[i], sizeof(uint64_t[2])))
devc->num_timebases = &timebases[i] - devc->timebases + 1;
}
- for (i = 0; i < NUM_VDIV; i++) {
+ for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
if (!memcmp(&devc->model->series->min_vdiv,
&vdivs[i], sizeof(uint64_t[2]))) {
devc->vdivs = &vdivs[i];
- devc->num_vdivs = NUM_VDIV - i;
+ devc->num_vdivs = ARRAY_SIZE(vdivs) - i;
}
}
return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
-
return SR_OK;
}
struct sr_scpi_dev_inst *scpi;
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
scpi = sdi->conn;
devc = sdi->priv;
+ if (!scpi)
+ return SR_ERR_BUG;
+
if (devc->model->series->protocol == PROTOCOL_V2)
rigol_ds_config_set(sdi, ":KEY:LOCK DISABLE");
- if (scpi) {
- if (sr_scpi_close(scpi) < 0)
- return SR_ERR;
- sdi->status = SR_ST_INACTIVE;
- }
-
- return SR_OK;
+ return sr_scpi_close(scpi);
}
static int analog_frame_size(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)
+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;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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 p, q;
+ uint64_t p;
double t_dbl;
- unsigned int i, j;
- int ret;
+ int ret, idx, i;
const char *tmp_str;
char buffer[16];
devc = sdi->priv;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
/* If a channel group is specified, it must be a valid one. */
if (cg && !g_slist_find(sdi->channel_groups, cg)) {
sr_err("Invalid channel group specified.");
return SR_ERR;
}
- ret = SR_OK;
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')) {
- sr_err("Unknown trigger slope: '%s'.",
- (tmp_str) ? tmp_str : "NULL");
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
return SR_ERR_ARG;
- }
-
g_free(devc->trigger_slope);
- devc->trigger_slope = g_strdup((tmp_str[0] == 'r') ? "POS" : "NEG");
- ret = rigol_ds_config_set(sdi, ":TRIG:EDGE:SLOP %s", devc->trigger_slope);
- break;
+ devc->trigger_slope = g_strdup((trigger_slopes[idx][0] == 'r') ? "POS" : "NEG");
+ return rigol_ds_config_set(sdi, ":TRIG:EDGE:SLOP %s", devc->trigger_slope);
case SR_CONF_HORIZ_TRIGGERPOS:
t_dbl = g_variant_get_double(data);
if (t_dbl < 0.0 || t_dbl > 1.0) {
* need to express this in seconds. */
t_dbl = -(devc->horiz_triggerpos - 0.5) * devc->timebase * devc->num_timebases;
g_ascii_formatd(buffer, sizeof(buffer), "%.6f", t_dbl);
- ret = rigol_ds_config_set(sdi, ":TIM:OFFS %s", buffer);
- break;
+ return rigol_ds_config_set(sdi,
+ devc->model->cmds[CMD_SET_HORIZ_TRIGGERPOS].str, buffer);
case SR_CONF_TRIGGER_LEVEL:
t_dbl = g_variant_get_double(data);
g_ascii_formatd(buffer, sizeof(buffer), "%.3f", t_dbl);
ret = rigol_ds_config_set(sdi, ":TRIG:EDGE:LEV %s", buffer);
if (ret == SR_OK)
devc->trigger_level = t_dbl;
- break;
+ return ret;
case SR_CONF_TIMEBASE:
- g_variant_get(data, "(tt)", &p, &q);
- for (i = 0; i < devc->num_timebases; i++) {
- if (devc->timebases[i][0] == p && devc->timebases[i][1] == q) {
- devc->timebase = (float)p / q;
- g_ascii_formatd(buffer, sizeof(buffer), "%.9f",
- devc->timebase);
- ret = rigol_ds_config_set(sdi, ":TIM:SCAL %s", buffer);
- break;
- }
- }
- if (i == devc->num_timebases) {
- sr_err("Invalid timebase index: %d.", i);
- ret = SR_ERR_ARG;
- }
- break;
+ if ((idx = std_u64_tuple_idx(data, devc->timebases, devc->num_timebases)) < 0)
+ return SR_ERR_ARG;
+ devc->timebase = (float)devc->timebases[idx][0] / devc->timebases[idx][1];
+ g_ascii_formatd(buffer, sizeof(buffer), "%.9f",
+ devc->timebase);
+ return rigol_ds_config_set(sdi, ":TIM:SCAL %s", buffer);
case SR_CONF_TRIGGER_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- for (i = 0; i < ARRAY_SIZE(trigger_sources); i++) {
- if (!strcmp(trigger_sources[i], tmp_str)) {
- g_free(devc->trigger_source);
- devc->trigger_source = g_strdup(trigger_sources[i]);
- if (!strcmp(devc->trigger_source, "AC Line"))
- tmp_str = "ACL";
- else if (!strcmp(devc->trigger_source, "CH1"))
- tmp_str = "CHAN1";
- else if (!strcmp(devc->trigger_source, "CH2"))
- tmp_str = "CHAN2";
- else if (!strcmp(devc->trigger_source, "CH3"))
- tmp_str = "CHAN3";
- else if (!strcmp(devc->trigger_source, "CH4"))
- tmp_str = "CHAN4";
- else
- tmp_str = (char *)devc->trigger_source;
- ret = rigol_ds_config_set(sdi, ":TRIG:EDGE:SOUR %s", tmp_str);
- break;
- }
- }
- if (i == ARRAY_SIZE(trigger_sources)) {
- sr_err("Invalid trigger source index: %d.", i);
- ret = SR_ERR_ARG;
- }
- break;
+ if ((idx = std_str_idx(data, devc->model->trigger_sources, devc->model->num_trigger_sources)) < 0)
+ return SR_ERR_ARG;
+ g_free(devc->trigger_source);
+ devc->trigger_source = g_strdup(devc->model->trigger_sources[idx]);
+ if (!strcmp(devc->trigger_source, "AC Line"))
+ tmp_str = "ACL";
+ else if (!strcmp(devc->trigger_source, "CH1"))
+ tmp_str = "CHAN1";
+ else if (!strcmp(devc->trigger_source, "CH2"))
+ tmp_str = "CHAN2";
+ else if (!strcmp(devc->trigger_source, "CH3"))
+ tmp_str = "CHAN3";
+ else if (!strcmp(devc->trigger_source, "CH4"))
+ tmp_str = "CHAN4";
+ else
+ tmp_str = (char *)devc->trigger_source;
+ return rigol_ds_config_set(sdi, ":TRIG:EDGE:SOUR %s", tmp_str);
case SR_CONF_VDIV:
- if (!cg) {
- sr_err("No channel group specified.");
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- }
- g_variant_get(data, "(tt)", &p, &q);
- 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;
- devc->vdiv[i] = (float)p / q;
- g_ascii_formatd(buffer, sizeof(buffer), "%.3f",
- devc->vdiv[i]);
- 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;
+ if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+ return SR_ERR_ARG;
+ devc->vdiv[i] = (float)vdivs[idx][0] / vdivs[idx][1];
+ g_ascii_formatd(buffer, sizeof(buffer), "%.3f", devc->vdiv[i]);
+ return rigol_ds_config_set(sdi, ":CHAN%d:SCAL %s", i + 1, buffer);
case SR_CONF_COUPLING:
- if (!cg) {
- sr_err("No channel group specified.");
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- }
- tmp_str = g_variant_get_string(data, NULL);
- 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]);
- devc->coupling[i] = g_strdup(coupling[j]);
- return rigol_ds_config_set(sdi, ":CHAN%d:COUP %s", i + 1,
- 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;
+ if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(coupling))) < 0)
+ return SR_ERR_ARG;
+ g_free(devc->coupling[i]);
+ devc->coupling[i] = g_strdup(coupling[idx]);
+ return rigol_ds_config_set(sdi, ":CHAN%d:COUP %s", i + 1, devc->coupling[i]);
case SR_CONF_PROBE_FACTOR:
- if (!cg) {
- sr_err("No channel group specified.");
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- }
+ if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(probe_factor))) < 0)
+ return SR_ERR_ARG;
p = g_variant_get_uint64(data);
- for (i = 0; i < devc->model->analog_channels; i++) {
- if (cg == devc->analog_groups[i]) {
- for (j = 0; j < ARRAY_SIZE(probe_factor); j++) {
- if (p == probe_factor[j]) {
- devc->attenuation[i] = p;
- ret = rigol_ds_config_set(sdi, ":CHAN%d:PROB %"PRIu64,
- i + 1, p);
- if (ret == SR_OK)
- rigol_ds_get_dev_cfg_vertical(sdi);
- return ret;
- }
- }
- sr_err("Invalid probe factor: %"PRIu64".", p);
- return SR_ERR_ARG;
- }
- }
- sr_dbg("Didn't set probe factor, unknown channel(group).");
- return SR_ERR_NA;
+ devc->attenuation[i] = probe_factor[idx];
+ ret = rigol_ds_config_set(sdi, ":CHAN%d:PROB %"PRIu64, i + 1, p);
+ if (ret == SR_OK)
+ rigol_ds_get_dev_cfg_vertical(sdi);
+ return ret;
case SR_CONF_DATA_SOURCE:
tmp_str = g_variant_get_string(data, NULL);
if (!strcmp(tmp_str, "Live"))
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *tuple, *rational[2];
- GVariantBuilder gvb;
- unsigned int i;
- struct dev_context *devc = NULL;
-
- /* SR_CONF_SCAN_OPTIONS is always valid, regardless of sdi or channel group. */
- 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;
- }
-
- /* 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,
- drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
- return SR_OK;
- }
-
- /* Every other option requires a valid device instance. */
- if (!sdi)
- return SR_ERR_ARG;
- devc = sdi->priv;
+ struct dev_context *devc;
- /* If a channel group is specified, it must be a valid one. */
- if (cg && !g_slist_find(sdi->channel_groups, cg)) {
- sr_err("Invalid channel group specified.");
- return SR_ERR;
- }
+ devc = (sdi) ? sdi->priv : NULL;
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- if (!cg) {
- /* If cg is NULL, only the SR_CONF_DEVICE_OPTIONS that are not
- * specific to a channel group must be returned. */
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
- return SR_OK;
- }
+ if (!cg)
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ if (!devc)
+ return SR_ERR_ARG;
if (cg == devc->digital_group) {
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
- NULL, 0, sizeof(uint32_t));
+ *data = std_gvar_array_u32(NULL, 0);
return SR_OK;
} else {
- 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;
- }
- }
- return SR_ERR_NA;
+ if (std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels) < 0)
+ return SR_ERR_ARG;
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog));
+ return SR_OK;
}
break;
case SR_CONF_COUPLING:
- if (!cg) {
- sr_err("No channel group specified.");
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- }
- *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(coupling));
break;
case SR_CONF_PROBE_FACTOR:
- if (!cg) {
- sr_err("No channel group specified.");
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- }
- *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
- probe_factor, ARRAY_SIZE(probe_factor), sizeof(uint64_t));
+ *data = std_gvar_array_u64(ARRAY_AND_SIZE(probe_factor));
break;
case SR_CONF_VDIV:
if (!devc)
/* Can't know this until we have the exact model. */
return SR_ERR_ARG;
- if (!cg) {
- sr_err("No channel group specified.");
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- }
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- 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);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_tuple_array(devc->vdivs, devc->num_vdivs);
break;
case SR_CONF_TIMEBASE:
if (!devc)
return SR_ERR_ARG;
if (devc->num_timebases <= 0)
return SR_ERR_NA;
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < devc->num_timebases; i++) {
- rational[0] = g_variant_new_uint64(devc->timebases[i][0]);
- rational[1] = g_variant_new_uint64(devc->timebases[i][1]);
- tuple = g_variant_new_tuple(rational, 2);
- g_variant_builder_add_value(&gvb, tuple);
- }
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_tuple_array(devc->timebases, devc->num_timebases);
break;
case SR_CONF_TRIGGER_SOURCE:
if (!devc)
/* Can't know this until we have the exact model. */
return SR_ERR_ARG;
- *data = g_variant_new_strv(trigger_sources,
- devc->model->has_digital ? ARRAY_SIZE(trigger_sources) : 4);
+ *data = g_variant_new_strv(devc->model->trigger_sources, devc->model->num_trigger_sources);
break;
case SR_CONF_TRIGGER_SLOPE:
- *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_slopes));
break;
case SR_CONF_DATA_SOURCE:
if (!devc)
*data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 1);
break;
default:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
break;
}
break;
gboolean some_digital;
GSList *l;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
scpi = sdi->conn;
devc = sdi->priv;
devc = sdi->priv;
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("Device inactive, can't stop acquisition.");
- return SR_ERR;
- }
-
std_session_send_df_end(sdi);
g_slist_free(devc->enabled_channels);
return SR_OK;
if (ch->type == SR_CHANNEL_LOGIC) {
- if (rigol_ds_config_set(sdi->conn, ":WAV:SOUR LA") != SR_OK)
+ if (rigol_ds_config_set(sdi, ":WAV:SOUR LA") != SR_OK)
return SR_ERR;
} else {
if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
if (!(devc = sdi->priv))
return SR_ERR;
- sr_dbg("Starting data capture for frameset %" PRIu64 " of %" PRIu64,
- devc->num_frames + 1, devc->limit_frames);
+ if (devc->limit_frames == 0)
+ sr_dbg("Starting data capture for frameset %" PRIu64,
+ devc->num_frames + 1);
+ else
+ sr_dbg("Starting data capture for frameset %" PRIu64 " of %"
+ PRIu64, devc->num_frames + 1, devc->limit_frames);
switch (devc->model->series->protocol) {
case PROTOCOL_V1:
break;
case PROTOCOL_V3:
if (ch->type == SR_CHANNEL_LOGIC) {
- if (rigol_ds_config_set(sdi->conn, ":WAV:SOUR LA") != SR_OK)
+ if (rigol_ds_config_set(sdi, ":WAV:SOUR LA") != SR_OK)
return SR_ERR;
} else {
if (rigol_ds_config_set(sdi, ":WAV:SOUR CHAN%d",
if (devc->model->series->protocol >= PROTOCOL_V3 &&
ch->type == SR_CHANNEL_ANALOG) {
+ /* Vertical increment. */
+ if (sr_scpi_get_float(sdi->conn, ":WAV:YINC?",
+ &devc->vert_inc[ch->index]) != SR_OK)
+ return SR_ERR;
+ /* Vertical origin. */
+ if (sr_scpi_get_float(sdi->conn, ":WAV:YOR?",
+ &devc->vert_origin[ch->index]) != SR_OK)
+ return SR_ERR;
/* Vertical reference. */
if (sr_scpi_get_int(sdi->conn, ":WAV:YREF?",
&devc->vert_reference[ch->index]) != SR_OK)
return SR_ERR;
+ } else if (ch->type == SR_CHANNEL_ANALOG) {
+ devc->vert_inc[ch->index] = devc->vdiv[ch->index] / 25.6;
}
rigol_ds_set_wait_event(devc, WAIT_BLOCK);
struct sr_analog_meaning meaning;
struct sr_analog_spec spec;
struct sr_datafeed_logic logic;
- double vdiv, offset;
+ double vdiv, offset, origin;
int len, i, vref;
struct sr_channel *ch;
gsize expected_data_bytes;
/* Still reading the header. */
return TRUE;
if (len == -1) {
- sr_err("Read error, aborting capture.");
+ sr_err("Error while reading block header, aborting capture.");
packet.type = SR_DF_FRAME_END;
sr_session_send(sdi, &packet);
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
/* At slow timebases in live capture the DS2072
len = sr_scpi_read_data(scpi, (char *)devc->buffer, len);
if (len == -1) {
- sr_err("Read error, aborting capture.");
+ sr_err("Error while reading block data, aborting capture.");
packet.type = SR_DF_FRAME_END;
sr_session_send(sdi, &packet);
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
if (ch->type == SR_CHANNEL_ANALOG) {
vref = devc->vert_reference[ch->index];
- vdiv = devc->vdiv[ch->index] / 25.6;
+ vdiv = devc->vert_inc[ch->index];
+ origin = devc->vert_origin[ch->index];
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;
+ devc->data[i] = ((int)devc->buffer[i] - vref - origin) * vdiv;
else
for (i = 0; i < len; i++)
devc->data[i] = (128 - devc->buffer[i]) * vdiv - offset;
if (devc->data_source != DATA_SOURCE_LIVE)
rigol_ds_set_wait_event(devc, WAIT_BLOCK);
}
- if (!sr_scpi_read_complete(scpi)) {
+ /* End acquisition when data for all channels is acquired. */
+ if (!sr_scpi_read_complete(scpi) && !devc->channel_entry->next) {
sr_err("Read should have been completed");
packet.type = SR_DF_FRAME_END;
sr_session_send(sdi, &packet);
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
devc->num_block_read = 0;
if (++devc->num_frames == devc->limit_frames) {
/* Last frame, stop capture. */
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
} else {
/* Get the next frame, starting with the first channel. */
devc->channel_entry = devc->enabled_channels;
sr_dbg("Current trigger source %s", devc->trigger_source);
/* Horizontal trigger position. */
- if (sr_scpi_get_float(sdi->conn, ":TIM:OFFS?", &devc->horiz_triggerpos) != SR_OK)
+ if (sr_scpi_get_float(sdi->conn, devc->model->cmds[CMD_GET_HORIZ_TRIGGERPOS].str,
+ &devc->horiz_triggerpos) != SR_OK)
return SR_ERR;
sr_dbg("Current horizontal trigger position %g", devc->horiz_triggerpos);
int buffer_samples;
};
+enum cmds {
+ CMD_GET_HORIZ_TRIGGERPOS,
+ CMD_SET_HORIZ_TRIGGERPOS,
+};
+
+struct rigol_ds_command {
+ int cmd;
+ const char *str;
+};
+
struct rigol_ds_model {
const struct rigol_ds_series *series;
const char *name;
uint64_t min_timebase[2];
unsigned int analog_channels;
bool has_digital;
+ const char **trigger_sources;
+ unsigned int num_trigger_sources;
+ const struct rigol_ds_command *cmds;
};
enum wait_events {
WAIT_STOP, /* Wait for scope stopping (only single shots) */
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Device model */
const struct rigol_ds_model *model;
enum data_format format;
float attenuation[MAX_ANALOG_CHANNELS];
float vdiv[MAX_ANALOG_CHANNELS];
int vert_reference[MAX_ANALOG_CHANNELS];
+ float vert_origin[MAX_ANALOG_CHANNELS];
float vert_offset[MAX_ANALOG_CHANNELS];
+ float vert_inc[MAX_ANALOG_CHANNELS];
char *trigger_source;
float horiz_triggerpos;
char *trigger_slope;
float trigger_level;
char *coupling[MAX_ANALOG_CHANNELS];
- /* Operational state */
-
/* Number of frames received in total. */
uint64_t num_frames;
/* GSList entry for the current channel. */
return SR_OK;
}
-static struct sr_dev_inst *rs_probe_serial_device(struct sr_scpi_dev_inst *scpi)
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
{
struct sr_dev_inst *sdi;
struct dev_context *devc;
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
- return sr_scpi_scan(di->context, options, rs_probe_serial_device);
-}
-
-static int dev_clear(const struct sr_dev_driver *di)
-{
- return std_dev_clear(di, NULL);
+ return sr_scpi_scan(di->context, options, probe_device);
}
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;
-
- sdi->status = SR_ST_ACTIVE;
-
- return SR_OK;
+ return sr_scpi_open(sdi->conn);
}
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;
+ return sr_scpi_close(sdi->conn);
}
static int config_get(uint32_t key, GVariant **data,
if (!sdi)
return SR_ERR_ARG;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
switch (key) {
case SR_CONF_OUTPUT_FREQUENCY:
value_f = g_variant_get_double(data);
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;
-
- /* 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;
- }
-
- 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)
-{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
- return SR_OK;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
SR_PRIV struct sr_dev_driver rohde_schwarz_sme_0x_driver_info = {
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = dev_clear,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
.dev_open = dev_open,
.dev_close = dev_close,
- .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_start = std_dummy_dev_acquisition_start,
.dev_acquisition_stop = std_serial_dev_acquisition_stop,
.context = NULL,
};
double power_min;
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
const struct rs_device_model *model_config;
};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Jan Luebbe <jluebbe@lasnet.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 <glib.h>
+#include <string.h>
+#include "protocol.h"
+
+#define BUF_COUNT 512
+#define BUF_SIZE (16 * 1024)
+#define BUF_TIMEOUT 1000
+
+SR_PRIV struct sr_dev_driver saleae_logic_pro_driver_info;
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_CONN | SR_CONF_GET,
+ SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const char *channel_names[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", "10", "11", "12", "13", "14", "15",
+};
+
+static const uint64_t samplerates[] = {
+ SR_MHZ(1),
+ SR_MHZ(2),
+ SR_KHZ(2500),
+ SR_MHZ(10),
+ SR_MHZ(25),
+ SR_MHZ(50),
+};
+
+#define FW_HEADER_SIZE 7
+#define FW_MAX_PART_SIZE (4 * 1024)
+
+static int upload_firmware(struct sr_context *ctx, libusb_device *dev, const char *name)
+{
+ struct libusb_device_handle *hdl = NULL;
+ unsigned char *firmware = NULL;
+ int ret = SR_ERR;
+ size_t fw_size, fw_offset = 0;
+ uint32_t part_address = 0;
+ uint16_t part_size = 0;
+ uint8_t part_final = 0;
+
+ firmware = sr_resource_load(ctx, SR_RESOURCE_FIRMWARE,
+ name, &fw_size, 256 * 1024);
+ if (!firmware)
+ goto out;
+
+ sr_info("Uploading firmware '%s'.", name);
+
+ if (libusb_open(dev, &hdl) != 0)
+ goto out;
+
+ while ((fw_offset + FW_HEADER_SIZE) <= fw_size) {
+ part_size = GUINT16_FROM_LE(*(uint16_t*)(firmware + fw_offset));
+ part_address = GUINT32_FROM_LE(*(uint32_t*)(firmware + fw_offset + 2));
+ part_final = *(uint8_t*)(firmware + fw_offset + 6);
+ if (part_size > FW_MAX_PART_SIZE) {
+ sr_err("Part too large (%d).", part_size);
+ goto out;
+ }
+ fw_offset += FW_HEADER_SIZE;
+ if ((fw_offset + part_size) > fw_size) {
+ sr_err("Truncated firmware file.");
+ goto out;
+ }
+ ret = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_OUT, 0xa0,
+ part_address & 0xffff, part_address >> 16,
+ firmware + fw_offset, part_size,
+ 100);
+ if (ret < 0) {
+ sr_err("Unable to send firmware to device: %s.",
+ libusb_error_name(ret));
+ ret = SR_ERR;
+ goto out;
+ }
+ if (part_size)
+ sr_spew("Uploaded %d bytes.", part_size);
+ else
+ sr_info("Started firmware at 0x%x.", part_address);
+ fw_offset += part_size;
+ }
+
+ if ((!part_final) || (part_size != 0)) {
+ sr_err("Missing final part.");
+ goto out;
+ }
+
+ ret = SR_OK;
+
+ sr_info("Firmware upload done.");
+
+ out:
+ if (hdl)
+ libusb_close(hdl);
+
+ g_free(firmware);
+
+ return ret;
+}
+
+static gboolean scan_firmware(libusb_device *dev)
+{
+ struct libusb_device_descriptor des;
+ struct libusb_device_handle *hdl;
+ gboolean ret;
+ unsigned char strdesc[64];
+
+ hdl = NULL;
+ ret = FALSE;
+
+ libusb_get_device_descriptor(dev, &des);
+
+ if (libusb_open(dev, &hdl) != 0)
+ goto out;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
+ goto out;
+ if (strcmp((const char *)strdesc, "Saleae"))
+ goto out;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iProduct, strdesc, sizeof(strdesc)) < 0)
+ goto out;
+ if (strcmp((const char *)strdesc, "Logic Pro"))
+ goto out;
+
+ ret = TRUE;
+
+out:
+ if (hdl)
+ libusb_close(hdl);
+
+ return ret;
+}
+
+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, *conn_devices;
+ libusb_device **devlist;
+ struct libusb_device_descriptor des;
+ const char *conn;
+ char connection_id[64];
+ gboolean fw_loaded = FALSE;
+
+ devices = NULL;
+ conn_devices = NULL;
+ drvc = di->context;
+ drvc->instances = NULL;
+
+ conn = NULL;
+ for (GSList *l = options; l; l = l->next) {
+ struct sr_config *src = l->data;
+
+ switch (src->key) {
+ case SR_CONF_CONN:
+ conn = g_variant_get_string(src->data, NULL);
+ break;
+ }
+ }
+
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ for (unsigned int i = 0; devlist[i]; i++) {
+ libusb_get_device_descriptor(devlist[i], &des);
+
+ if (des.idVendor != 0x21a9 || des.idProduct != 0x1006)
+ continue;
+
+ if (!scan_firmware(devlist[i])) {
+ const char *fwname;
+ sr_info("Found a Logic Pro 16 device (no firmware loaded).");
+ fwname = "saleae-logicpro16-fx3.fw";
+ if (upload_firmware(drvc->sr_ctx, devlist[i],
+ fwname) != SR_OK) {
+ sr_err("Firmware upload failed, name %s.", fwname);
+ continue;
+ };
+ fw_loaded = TRUE;
+ }
+
+ }
+ if (fw_loaded) {
+ /* Give the device some time to come back and scan again */
+ libusb_free_device_list(devlist, 1);
+ g_usleep(500 * 1000);
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+ }
+ if (conn)
+ conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+ for (unsigned int i = 0; devlist[i]; i++) {
+ if (conn_devices) {
+ struct sr_usb_dev_inst *usb = NULL;
+ GSList *l;
+
+ 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 (des.idVendor != 0x21a9 || des.idProduct != 0x1006)
+ continue;
+
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->status = SR_ST_INITIALIZING;
+ sdi->vendor = g_strdup("Saleae");
+ sdi->model = g_strdup("Logic Pro 16");
+ sdi->connection_id = g_strdup(connection_id);
+
+ for (unsigned int j = 0; j < ARRAY_SIZE(channel_names); j++)
+ sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE,
+ channel_names[j]);
+
+ sr_dbg("Found a Logic Pro 16 device.");
+ sdi->status = SR_ST_INACTIVE;
+ 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);
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ sdi->priv = devc;
+ devices = g_slist_append(devices, sdi);
+
+ }
+ g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+ libusb_free_device_list(devlist, 1);
+
+ return std_scan_complete(di, devices);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_dev_driver *di = sdi->driver;
+ struct drv_context *drvc = di->context;
+ struct dev_context *devc = sdi->priv;
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ int ret;
+
+ if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
+ 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;
+ }
+
+ /* Configure default samplerate. */
+ if (devc->dig_samplerate == 0)
+ devc->dig_samplerate = samplerates[3];
+
+ return saleae_logic_pro_init(sdi);
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ sr_usb_close(sdi->conn);
+
+ 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 sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_CONN:
+ if (!sdi || !sdi->conn)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
+ break;
+ case SR_CONF_SAMPLERATE:
+ if (!sdi)
+ return SR_ERR;
+ devc = sdi->priv;
+ *data = g_variant_new_uint64(devc->dig_samplerate);
+ 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;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ devc->dig_samplerate = 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)
+{
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_SAMPLERATE:
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static void dev_acquisition_abort(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ unsigned int i;
+
+ for (i = 0; i < devc->num_transfers; i++) {
+ if (devc->transfers[i])
+ libusb_cancel_transfer(devc->transfers[i]);
+ }
+}
+
+static int dev_acquisition_handle(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi = cb_data;
+ struct drv_context *drvc = sdi->driver->context;
+ struct timeval tv = ALL_ZERO;
+
+ (void)fd;
+
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+ /* Handle timeout */
+ if (!revents)
+ sr_dev_acquisition_stop(sdi);
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct drv_context *drvc = sdi->driver->context;
+ struct libusb_transfer *transfer;
+ struct sr_usb_dev_inst *usb;
+ uint8_t *buf;
+ unsigned int i, ret;
+
+ ret = saleae_logic_pro_prepare(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ usb = sdi->conn;
+
+ devc->conv_buffer = g_malloc(CONV_BUFFER_SIZE);
+
+ devc->num_transfers = BUF_COUNT;
+ devc->transfers = g_malloc0(sizeof(*devc->transfers) * BUF_COUNT);
+ for (i = 0; i < devc->num_transfers; i++) {
+ buf = g_malloc(BUF_SIZE);
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, usb->devhdl,
+ 2 | LIBUSB_ENDPOINT_IN, buf, BUF_SIZE,
+ saleae_logic_pro_receive_data, (void *)sdi, 0);
+ if ((ret = libusb_submit_transfer(transfer)) != 0) {
+ sr_err("Failed to submit transfer: %s.",
+ libusb_error_name(ret));
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ dev_acquisition_abort(sdi);
+ return SR_ERR;
+ }
+ devc->transfers[i] = transfer;
+ devc->submitted_transfers++;
+ }
+
+ usb_source_add(sdi->session, drvc->sr_ctx, BUF_TIMEOUT, dev_acquisition_handle, (void *)sdi);
+
+ std_session_send_df_header(sdi);
+
+ saleae_logic_pro_start(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ struct drv_context *drvc = sdi->driver->context;
+
+ saleae_logic_pro_stop(sdi);
+
+ std_session_send_df_end(sdi);
+
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ g_free(devc->conv_buffer);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver saleae_logic_pro_driver_info = {
+ .name = "saleae-logic-pro",
+ .longname = "Saleae Logic Pro",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
+
+SR_REGISTER_DEV_DRIVER(saleae_logic_pro_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Jan Luebbe <jluebbe@lasnet.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 "protocol.h"
+
+#define COMMAND_START_CAPTURE 0x01
+#define COMMAND_STOP_CAPTURE 0x02
+#define COMMAND_READ_EEPROM 0x07
+#define COMMAND_INIT_BITSTREAM 0x7e
+#define COMMAND_SEND_BITSTREAM 0x7f
+#define COMMAND_WRITE_REG 0x80
+#define COMMAND_READ_REG 0x81
+#define COMMAND_READ_TEMP 0x86
+#define COMMAND_WRITE_I2C 0x87
+#define COMMAND_READ_I2C 0x88
+#define COMMAND_WAKE_I2C 0x89
+#define COMMAND_READ_FW_VER 0x8b
+
+#define REG_ADC_IDX 0x03
+#define REG_ADC_VAL_LSB 0x04
+#define REG_ADC_VAL_MSB 0x05
+#define REG_LED_RED 0x0f
+#define REG_LED_GREEN 0x10
+#define REG_LED_BLUE 0x11
+#define REG_STATUS 0x40
+
+static void iterate_lfsr(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint32_t lfsr = devc->lfsr;
+ int i, max;
+
+ max = (lfsr & 0x1f) + 34;
+ for (i = 0; i <= max; i++) {
+ lfsr = (lfsr >> 1) | \
+ ((lfsr ^ \
+ (lfsr >> 1) ^ \
+ (lfsr >> 21) ^ \
+ (lfsr >> 31) \
+ ) << 31);
+ }
+ sr_spew("Iterate 0x%08x -> 0x%08x", devc->lfsr, lfsr);
+ devc->lfsr = lfsr;
+}
+
+static void encrypt(const struct sr_dev_inst *sdi, const uint8_t *in, uint8_t *out, uint16_t len)
+{
+ struct dev_context *devc = sdi->priv;
+ uint32_t lfsr = devc->lfsr;
+ uint8_t value, mask;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ value = in[i];
+ mask = lfsr >> (i % 4 * 8);
+ if (i == 0)
+ value = (value & 0x28) | ((value ^ mask) & ~0x28);
+ else
+ value = value ^ mask;
+ out[i] = value;
+ }
+ iterate_lfsr(sdi);
+}
+
+static void decrypt(const struct sr_dev_inst *sdi, uint8_t *data, uint16_t len)
+{
+ struct dev_context *devc = sdi->priv;
+ uint32_t lfsr = devc->lfsr;
+ int i;
+
+ for (i = 0; i < len; i++)
+ data[i] ^= (lfsr >> (i % 4 * 8));
+ iterate_lfsr(sdi);
+}
+
+static int transact(const struct sr_dev_inst *sdi,
+ const uint8_t *req, uint16_t req_len,
+ uint8_t *rsp, uint16_t rsp_len)
+{
+ struct sr_usb_dev_inst *usb = sdi->conn;
+ uint8_t *req_enc;
+ uint8_t rsp_dummy[1] = {};
+ int ret, xfer;
+
+ if (req_len < 2 || req_len > 1024 || rsp_len > 128 ||
+ !req || (rsp_len > 0 && !rsp))
+ return SR_ERR_ARG;
+
+ req_enc = g_malloc(req_len);
+ encrypt(sdi, req, req_enc, req_len);
+
+ ret = libusb_bulk_transfer(usb->devhdl, 1, req_enc, req_len, &xfer, 1000);
+ if (ret != 0) {
+ sr_dbg("Failed to send request 0x%02x: %s.",
+ req[1], libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (xfer != req_len) {
+ sr_dbg("Failed to send request 0x%02x: incorrect length "
+ "%d != %d.", req[1], xfer, req_len);
+ return SR_ERR;
+ }
+
+ if (req[0] == 0x20) { /* Reseed. */
+ return SR_OK;
+ } else if (rsp_len == 0) {
+ rsp = rsp_dummy;
+ rsp_len = sizeof(rsp_dummy);
+ }
+
+ ret = libusb_bulk_transfer(usb->devhdl, 0x80 | 1, rsp, rsp_len,
+ &xfer, 1000);
+ if (ret != 0) {
+ sr_dbg("Failed to receive response to request 0x%02x: %s.",
+ req[1], libusb_error_name(ret));
+ return SR_ERR;
+ }
+ if (xfer != rsp_len) {
+ sr_dbg("Failed to receive response to request 0x%02x: "
+ "incorrect length %d != %d.", req[1], xfer, rsp_len);
+ return SR_ERR;
+ }
+
+ decrypt(sdi, rsp, rsp_len);
+
+ return SR_OK;
+}
+
+static int reseed(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint8_t req[] = {0x20, 0x24, 0x4b, 0x35, 0x8e};
+
+ devc->lfsr = 0;
+ return transact(sdi, req, sizeof(req), NULL, 0);
+}
+
+static int write_regs(const struct sr_dev_inst *sdi, uint8_t (*regs)[2], uint8_t cnt)
+{
+ uint8_t req[64];
+ int i;
+
+ if (cnt < 1 || cnt > 30)
+ return SR_ERR_ARG;
+
+ req[0] = 0x00;
+ req[1] = COMMAND_WRITE_REG;
+ req[2] = cnt;
+
+ for (i = 0; i < cnt; i++) {
+ req[3 + 2 * i] = regs[i][0];
+ req[4 + 2 * i] = regs[i][1];
+ }
+
+ return transact(sdi, req, 3 + (2 * cnt), NULL, 0);
+}
+
+static int write_reg(const struct sr_dev_inst *sdi,
+ uint8_t address, uint8_t value)
+{
+ uint8_t regs[2] = {address, value};
+
+ return write_regs(sdi, ®s, 1);
+}
+
+static int read_regs(const struct sr_dev_inst *sdi,
+ const uint8_t *regs, uint8_t *values,
+ uint8_t cnt)
+{
+ uint8_t req[33];
+
+ if (cnt < 1 || cnt > 30)
+ return SR_ERR_ARG;
+
+ req[0] = 0x00;
+ req[1] = COMMAND_READ_REG;
+ req[2] = cnt;
+ if (cnt)
+ memcpy(&req[3], regs, cnt);
+
+ return transact(sdi, req, 3 + cnt, values, cnt);
+}
+
+static int read_reg(const struct sr_dev_inst *sdi,
+ uint8_t address, uint8_t *value)
+{
+ return read_regs(sdi, &address, value, 1);
+}
+
+static int write_adc(const struct sr_dev_inst *sdi,
+ uint8_t address, uint16_t value)
+{
+ uint8_t regs[][2] = {
+ {REG_ADC_IDX, address},
+ {REG_ADC_VAL_LSB, value},
+ {REG_ADC_VAL_MSB, value >> 8},
+ };
+
+ return write_regs(sdi, ARRAY_AND_SIZE(regs));
+}
+
+static int read_eeprom(const struct sr_dev_inst *sdi,
+ uint16_t address, uint8_t *data, uint16_t len)
+{
+ uint8_t req[8] = {
+ 0x00, COMMAND_READ_EEPROM,
+ 0x33, 0x81, /* Unknown values */
+ address, address >> 8,
+ len, len >> 8
+ };
+
+ return transact(sdi, req, sizeof(req), data, len);
+}
+
+static int read_eeprom_serial(const struct sr_dev_inst *sdi,
+ uint8_t data[8])
+{
+ return read_eeprom(sdi, 0x08, data, 0x8);
+}
+
+static int read_eeprom_magic(const struct sr_dev_inst *sdi,
+ uint8_t data[16])
+{
+ return read_eeprom(sdi, 0x10, data, 0x10);
+}
+
+static int read_temperature(const struct sr_dev_inst *sdi, int8_t *temp)
+{
+ uint8_t req[2] = {0x00, COMMAND_READ_TEMP};
+
+ return transact(sdi, req, sizeof(req), (uint8_t*)temp, 1);
+}
+
+static int get_firmware_version(const struct sr_dev_inst *sdi)
+{
+ uint8_t req[2] = {0x00, COMMAND_READ_FW_VER};
+ uint8_t rsp[128] = {};
+ int ret;
+
+ ret = transact(sdi, req, sizeof(req), rsp, sizeof(rsp));
+ if (ret == SR_OK) {
+ rsp[63] = 0;
+ sr_dbg("fw-version: %s", rsp);
+ }
+
+ return ret;
+}
+
+static int read_i2c(const struct sr_dev_inst *sdi, uint8_t *data, uint8_t len)
+{
+ uint8_t req[5];
+ uint8_t rsp[1 + 128];
+ int ret;
+
+ if (len < 1 || len > 128 || !data)
+ return SR_ERR_ARG;
+
+ req[0] = 0x00;
+ req[1] = COMMAND_READ_I2C;
+ req[2] = 0xc0; /* Fixed address */
+ req[3] = len;
+ req[4] = 0; /* Len MSB? */
+
+ ret = transact(sdi, req, sizeof(req), rsp, 1 + len);
+ if (ret != SR_OK)
+ return ret;
+ if (rsp[0] != 0x02) {
+ sr_dbg("Failed to do I2C read (0x%02x).", rsp[0]);
+ return SR_ERR;
+ }
+
+ memcpy(data, rsp + 1, len);
+ return SR_OK;
+}
+
+static int write_i2c(const struct sr_dev_inst *sdi, const uint8_t *data, uint8_t len)
+{
+ uint8_t req[5 + 128];
+ uint8_t rsp[1];
+ int ret;
+
+ if (len < 1 || len > 128 || !data)
+ return SR_ERR_ARG;
+
+ req[0] = 0x00;
+ req[1] = COMMAND_WRITE_I2C;
+ req[2] = 0xc0; /* Fixed address */
+ req[3] = len;
+ req[4] = 0; /* Len MSB? */
+ memcpy(req + 5, data, len);
+
+ ret = transact(sdi, req, 5 + len, rsp, sizeof(rsp));
+ if (ret != SR_OK)
+ return ret;
+ if (rsp[0] != 0x02) {
+ sr_dbg("Failed to do I2C write (0x%02x).", rsp[0]);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int wake_i2c(const struct sr_dev_inst *sdi)
+{
+ uint8_t req[] = {0x00, COMMAND_WAKE_I2C};
+ uint8_t rsp[1] = {};
+ uint8_t i2c_rsp[1 + 1 + 2] = {};
+ int ret;
+
+ ret = transact(sdi, req, sizeof(req), rsp, sizeof(rsp));
+ if (ret != SR_OK)
+ return ret;
+ if (rsp[0] != 0x00) {
+ sr_dbg("Failed to do I2C wake trigger (0x%02x).", rsp[0]);
+ return SR_ERR;
+ }
+
+ ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp));
+ if (ret != SR_OK) {
+ return ret;
+ }
+ if (i2c_rsp[1] != 0x11) {
+ sr_dbg("Failed to do I2C wake read (0x%02x).", i2c_rsp[0]);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int crypto_random(const struct sr_dev_inst *sdi, uint8_t *data)
+{
+ uint8_t i2c_req[8] = {0x03, 0x07, 0x1b, 0x00, 0x00, 0x00, 0x24, 0xcd};
+ uint8_t i2c_rsp[1 + 32 + 2] = {};
+ int ret;
+
+ ret = write_i2c(sdi, i2c_req, sizeof(i2c_req));
+ if (ret != SR_OK)
+ return ret;
+
+ g_usleep(100000); /* TODO: Poll instead. */
+
+ ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp));
+ if (ret != SR_OK)
+ return ret;
+
+ if (data)
+ memcpy(data, i2c_rsp + 1, 32);
+
+ return SR_OK;
+}
+
+static int crypto_nonce(const struct sr_dev_inst *sdi, uint8_t *data)
+{
+ uint8_t i2c_req[6 + 20 + 2] = {0x03, 0x1b, 0x16, 0x00, 0x00, 0x00};
+ uint8_t i2c_rsp[1 + 32 + 2] = {};
+ int ret;
+
+ /* CRC */
+ i2c_req[26] = 0x7d;
+ i2c_req[27] = 0xe0;
+
+ ret = write_i2c(sdi, i2c_req, sizeof(i2c_req));
+ if (ret != SR_OK)
+ return ret;
+
+ g_usleep(100000); /* TODO: Poll instead. */
+
+ ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp));
+ if (ret != SR_OK)
+ return ret;
+
+ if (data)
+ memcpy(data, i2c_rsp + 1, 32);
+
+ return SR_OK;
+}
+
+static int crypto_sign(const struct sr_dev_inst *sdi, uint8_t *data, uint8_t *crc)
+{
+ uint8_t i2c_req[8] = {0x03, 0x07, 0x41, 0x80, 0x00, 0x00, 0x28, 0x05};
+ uint8_t i2c_rsp[1 + 64 + 2] = {};
+ int ret;
+
+ ret = write_i2c(sdi, i2c_req, sizeof(i2c_req));
+ if (ret != SR_OK)
+ return ret;
+
+ g_usleep(100000); /* TODO: Poll instead. */
+
+ ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp));
+ if (ret != SR_OK)
+ return ret;
+
+ memcpy(data, i2c_rsp + 1, 64);
+ memcpy(crc, i2c_rsp + 1 + 64, 2);
+
+ return SR_OK;
+}
+
+static int authenticate(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint8_t random[32] = {};
+ uint8_t nonce[32] = {};
+ uint8_t sig[64] = {};
+ uint8_t sig_crc[64] = {};
+ uint32_t lfsr;
+ int i, ret;
+
+ ret = wake_i2c(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = crypto_random(sdi, random);
+ if (ret != SR_OK)
+ return ret;
+ sr_dbg("random: 0x%02x 0x%02x 0x%02x 0x%02x", random[0], random[1], random[2], random[3]);
+
+ ret = crypto_nonce(sdi, nonce);
+ if (ret != SR_OK)
+ return ret;
+ sr_dbg("nonce: 0x%02x 0x%02x 0x%02x 0x%02x", nonce[0], nonce[1], nonce[2], nonce[3]);
+
+ ret = crypto_nonce(sdi, nonce);
+ if (ret != SR_OK)
+ return ret;
+ sr_dbg("nonce: 0x%02x 0x%02x 0x%02x 0x%02x", nonce[0], nonce[1], nonce[2], nonce[3]);
+
+ ret = crypto_sign(sdi, sig, sig_crc);
+ if (ret != SR_OK)
+ return ret;
+ sr_dbg("sig: 0x%02x 0x%02x 0x%02x 0x%02x", sig[0], sig[1], sig[2], sig[3]);
+ sr_dbg("sig crc: 0x%02x 0x%02x", sig_crc[0], sig_crc[1]);
+
+ lfsr = 0;
+ for (i = 0; i < 28; i++)
+ lfsr ^= nonce[i] << (8 * (i % 4));
+ lfsr ^= sig_crc[0] | sig_crc[1] << 8;
+
+ sr_dbg("Authenticate 0x%08x -> 0x%08x", devc->lfsr, lfsr);
+ devc->lfsr = lfsr;
+
+ return SR_OK;
+}
+
+static int upload_bitstream_part(const struct sr_dev_inst *sdi,
+ const uint8_t *data, uint16_t len)
+{
+ uint8_t req[4 + 1020];
+ uint8_t rsp[1];
+ int ret;
+
+ if (len < 1 || len > 1020 || !data)
+ return SR_ERR_ARG;
+
+ req[0] = 0x00;
+ req[1] = COMMAND_SEND_BITSTREAM;
+ req[2] = len;
+ req[3] = len >> 8;
+ memcpy(req + 4, data, len);
+
+ ret = transact(sdi, req, 4 + len, rsp, sizeof(rsp));
+ if (ret != SR_OK)
+ return ret;
+ if (rsp[0] != 0x00) {
+ sr_dbg("Failed to do bitstream upload (0x%02x).", rsp[0]);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int upload_bitstream(const struct sr_dev_inst *sdi,
+ const char *name)
+{
+ struct drv_context *drvc = sdi->driver->context;
+ unsigned char *bitstream = NULL;
+ uint8_t req[2];
+ uint8_t rsp[1];
+ uint8_t reg_val;
+ int ret = SR_ERR;
+ size_t bs_size, bs_offset = 0, bs_part_size;
+
+ bitstream = sr_resource_load(drvc->sr_ctx, SR_RESOURCE_FIRMWARE,
+ name, &bs_size, 512 * 1024);
+ if (!bitstream)
+ goto out;
+
+ sr_info("Uploading bitstream '%s'.", name);
+
+ req[0] = 0x00;
+ req[1] = COMMAND_INIT_BITSTREAM;
+
+ ret = transact(sdi, req, sizeof(req), rsp, sizeof(rsp));
+ if (ret != SR_OK)
+ return ret;
+ if (rsp[0] != 0x00) {
+ sr_err("Failed to start bitstream upload (0x%02x).", rsp[0]);
+ ret = SR_ERR;
+ goto out;
+ }
+
+ while (bs_offset < bs_size) {
+ bs_part_size = MIN(bs_size - bs_offset, 1020);
+ sr_spew("Uploading %zd bytes.", bs_part_size);
+ ret = upload_bitstream_part(sdi, bitstream + bs_offset, bs_part_size);
+ if (ret != SR_OK)
+ goto out;
+ bs_offset += bs_part_size;
+ }
+
+ sr_info("Bitstream upload done.");
+
+ /* Check a scratch register? */
+ ret = write_reg(sdi, 0x7f, 0xaa);
+ if (ret != SR_OK)
+ goto out;
+ ret = read_reg(sdi, 0x7f, ®_val);
+ if (ret != SR_OK)
+ goto out;
+ if (reg_val != 0xaa) {
+ sr_err("Failed FPGA register read-back (0x%02x != 0xaa).", rsp[0]);
+ ret = SR_ERR;
+ goto out;
+ }
+
+ out:
+ g_free(bitstream);
+
+ return ret;
+}
+
+#if 0
+static int set_led(const struct sr_dev_inst *sdi, uint8_t r, uint8_t g, uint8_t b)
+{
+ uint8_t regs[][2] = {
+ {REG_LED_RED, r},
+ {REG_LED_GREEN, g},
+ {REG_LED_BLUE, b},
+ };
+
+ authenticate(sdi);
+
+ return write_regs(sdi, ARRAY_AND_SIZE(regs));
+}
+#endif
+
+static int configure_channels(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ const struct sr_channel *c;
+ const GSList *l;
+ uint16_t mask;
+
+ devc->dig_channel_cnt = 0;
+ devc->dig_channel_mask = 0;
+ for (l = sdi->channels; l; l = l->next) {
+ c = l->data;
+ if (!c->enabled)
+ continue;
+
+ mask = 1 << c->index;
+ devc->dig_channel_masks[devc->dig_channel_cnt++] = mask;
+ devc->dig_channel_mask |= mask;
+
+ }
+ sr_dbg("%d channels enabled (0x%04x)",
+ devc->dig_channel_cnt, devc->dig_channel_mask);
+
+ return SR_OK;
+}
+
+SR_PRIV int saleae_logic_pro_init(const struct sr_dev_inst *sdi)
+{
+ uint8_t reg_val;
+ uint8_t dummy[8];
+ uint8_t serial[8];
+ uint8_t magic[16];
+ int8_t temperature;
+ int ret, i;
+
+ ret = reseed(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = get_firmware_version(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ sr_dbg("read serial");
+ ret = read_eeprom_serial(sdi, serial);
+ if (ret != SR_OK)
+ return ret;
+
+ /* Check if we need to upload the bitstream. */
+ ret = read_reg(sdi, 0x7f, ®_val);
+ if (ret != SR_OK)
+ return ret;
+ if (reg_val == 0xaa) {
+ sr_info("Skipping bitstream upload.");
+ } else {
+ ret = upload_bitstream(sdi, "saleae-logicpro16-fpga.bitstream");
+ if (ret != SR_OK)
+ return ret;
+ }
+
+ /* Reset the ADC? */
+ sr_dbg("reset ADC");
+ ret = write_reg(sdi, 0x00, 0x00);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_reg(sdi, 0x00, 0x80);
+ if (ret != SR_OK)
+ return ret;
+
+ sr_dbg("init ADC");
+ ret = write_adc(sdi, 0x11, 0x0444);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x12, 0x0777);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x25, 0x0000);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x45, 0x0000);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x2a, 0x1111);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x2b, 0x1111);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x46, 0x0004);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x50, 0x0000);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x55, 0x0020);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_adc(sdi, 0x56, 0x0000);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = write_reg(sdi, 0x15, 0x00);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = write_adc(sdi, 0x0f, 0x0100);
+ if (ret != SR_OK)
+ return ret;
+
+ /* Resets? */
+ sr_dbg("resets");
+ ret = write_reg(sdi, 0x00, 0x02); /* bit 1 */
+ if (ret != SR_OK)
+ return ret;
+ ret = write_reg(sdi, 0x00, 0x00);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_reg(sdi, 0x00, 0x04); /* bit 2 */
+ if (ret != SR_OK)
+ return ret;
+ ret = write_reg(sdi, 0x00, 0x00);
+ if (ret != SR_OK)
+ return ret;
+ ret = write_reg(sdi, 0x00, 0x08); /* bit 3 */
+ if (ret != SR_OK)
+ return ret;
+ ret = write_reg(sdi, 0x00, 0x00);
+ if (ret != SR_OK)
+ return ret;
+
+ sr_dbg("read dummy");
+ for (i = 0; i < 8; i++) {
+ ret = read_reg(sdi, 0x41 + i, &dummy[i]);
+ if (ret != SR_OK)
+ return ret;
+ }
+
+ /* Read and write back magic EEPROM value. */
+ sr_dbg("read/write magic");
+ ret = read_eeprom_magic(sdi, magic);
+ if (ret != SR_OK)
+ return ret;
+ for (i = 0; i < 16; i++) {
+ ret = write_reg(sdi, 0x17, magic[i]);
+ if (ret != SR_OK)
+ return ret;
+ }
+
+ ret = read_temperature(sdi, &temperature);
+ if (ret != SR_OK)
+ return ret;
+ sr_dbg("temperature = %d", temperature);
+
+ /* Setting the LED doesn't work yet. */
+ /* set_led(sdi, 0x00, 0x00, 0xff); */
+
+ return SR_OK;
+}
+
+SR_PRIV int saleae_logic_pro_prepare(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+ uint8_t regs_unknown[][2] = {
+ {0x03, 0x0f},
+ {0x04, 0x00},
+ {0x05, 0x00},
+ };
+ uint8_t regs_config[][2] = {
+ {0x00, 0x00},
+ {0x08, 0x00}, /* Analog channel mask (LSB) */
+ {0x09, 0x00}, /* Analog channel mask (MSB) */
+ {0x06, 0x01}, /* Digital channel mask (LSB) */
+ {0x07, 0x00}, /* Digital channel mask (MSB) */
+ {0x0a, 0x00}, /* Analog sample rate? */
+ {0x0b, 0x64}, /* Digital sample rate? */
+ {0x0c, 0x00},
+ {0x0d, 0x00}, /* Analog mux rate? */
+ {0x0e, 0x01}, /* Digital mux rate? */
+ {0x12, 0x04},
+ {0x13, 0x00},
+ {0x14, 0xff}, /* Pre-divider? */
+ };
+ uint8_t start_req[] = {0x00, 0x01};
+ uint8_t start_rsp[2] = {};
+
+ configure_channels(sdi);
+
+ /* Digital channel mask and muxing */
+ regs_config[3][1] = devc->dig_channel_mask;
+ regs_config[4][1] = devc->dig_channel_mask >> 8;
+ regs_config[9][1] = devc->dig_channel_cnt;
+
+ /* Samplerate */
+ switch (devc->dig_samplerate) {
+ case SR_MHZ(1):
+ regs_config[6][1] = 0x64;
+ break;
+ case SR_MHZ(2):
+ regs_config[6][1] = 0x32;
+ break;
+ case SR_KHZ(2500):
+ regs_config[6][1] = 0x28;
+ break;
+ case SR_MHZ(10):
+ regs_config[6][1] = 0x0a;
+ break;
+ case SR_MHZ(25):
+ regs_config[6][1] = 0x04;
+ regs_config[12][1] = 0x80;
+ break;
+ case SR_MHZ(50):
+ regs_config[6][1] = 0x02;
+ regs_config[12][1] = 0x40;
+ break;
+ default:
+ return SR_ERR_ARG;
+ }
+
+ authenticate(sdi);
+
+ write_reg(sdi, 0x15, 0x03);
+ write_regs(sdi, ARRAY_AND_SIZE(regs_unknown));
+ write_regs(sdi, ARRAY_AND_SIZE(regs_config));
+
+ transact(sdi, start_req, sizeof(start_req), start_rsp, sizeof(start_rsp));
+
+ return SR_OK;
+}
+
+SR_PRIV int saleae_logic_pro_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc = sdi->priv;
+
+ devc->conv_size = 0;
+ devc->batch_index = 0;
+
+ write_reg(sdi, 0x00, 0x01);
+
+ return SR_OK;
+}
+
+SR_PRIV int saleae_logic_pro_stop(const struct sr_dev_inst *sdi)
+{
+ uint8_t stop_req[] = {0x00, 0x02};
+ uint8_t stop_rsp[2] = {};
+ uint8_t status;
+ int ret;
+
+ write_reg(sdi, 0x00, 0x00);
+ transact(sdi, stop_req, sizeof(stop_req), stop_rsp, sizeof(stop_rsp));
+
+ ret = read_reg(sdi, 0x40, &status);
+ if (ret != SR_OK)
+ return ret;
+ if (status != 0x20) {
+ sr_err("Capture error (status reg = 0x%02x).", status);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static void saleae_logic_pro_send_data(const struct sr_dev_inst *sdi,
+ void *data, size_t length, size_t unitsize)
+{
+ const struct sr_datafeed_logic logic = {
+ .length = length,
+ .unitsize = unitsize,
+ .data = data
+ };
+
+ const struct sr_datafeed_packet packet = {
+ .type = SR_DF_LOGIC,
+ .payload = &logic
+ };
+
+ sr_session_send(sdi, &packet);
+}
+
+/*
+ * One batch from the device consists of 32 samples per active digital channel.
+ * This stream of batches is packed into USB packets with 16384 bytes each.
+ */
+static void saleae_logic_pro_convert_data(const struct sr_dev_inst *sdi,
+ const uint32_t *src, size_t srccnt)
+{
+ struct dev_context *devc = sdi->priv;
+ uint8_t *dst = devc->conv_buffer;
+ uint32_t samples;
+ uint16_t channel_mask;
+ unsigned int sample_index, batch_index;
+ uint16_t *dst_batch;
+
+ /* Copy partial batch to the beginning. */
+ memcpy(dst, dst + devc->conv_size, CONV_BATCH_SIZE);
+ /* Reset converted size. */
+ devc->conv_size = 0;
+
+ batch_index = devc->batch_index;
+ while (srccnt--) {
+ samples = *src++;
+ dst_batch = (uint16_t*)dst;
+
+ /* First index of the batch. */
+ if (batch_index == 0)
+ memset(dst, 0, CONV_BATCH_SIZE);
+
+ /* Convert one channel. */
+ channel_mask = devc->dig_channel_masks[batch_index];
+ for (sample_index = 0; sample_index <= 31; sample_index++)
+ if ((samples >> (31 - sample_index)) & 1)
+ dst_batch[sample_index] |= channel_mask;
+
+ /* Last index of the batch. */
+ if (++batch_index == devc->dig_channel_cnt) {
+ devc->conv_size += CONV_BATCH_SIZE;
+ batch_index = 0;
+ dst += CONV_BATCH_SIZE;
+ }
+ }
+ devc->batch_index = batch_index;
+}
+
+SR_PRIV void LIBUSB_CALL saleae_logic_pro_receive_data(struct libusb_transfer *transfer)
+{
+ const struct sr_dev_inst *sdi = transfer->user_data;
+ struct dev_context *devc = sdi->priv;
+ int ret;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ sr_dbg("FIXME no device");
+ return;
+ case LIBUSB_TRANSFER_COMPLETED:
+ case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */
+ break;
+ default:
+ /* FIXME */
+ return;
+ }
+
+ saleae_logic_pro_convert_data(sdi, (uint32_t*)transfer->buffer, 16 * 1024 / 4);
+ saleae_logic_pro_send_data(sdi, devc->conv_buffer, devc->conv_size, 2);
+
+ if ((ret = libusb_submit_transfer(transfer)) != LIBUSB_SUCCESS)
+ sr_dbg("FIXME resubmit failed");
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2017 Jan Luebbe <jluebbe@lasnet.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_SALEAE_LOGIC_PRO_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SALEAE_LOGIC_PRO_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "saleae-logic-pro"
+
+/* 16 channels * 32 samples */
+#define CONV_BATCH_SIZE (2 * 32)
+
+/*
+ * One packet + one partial conversion: Worst case is only one active
+ * channel converted to 2 bytes per sample, with 8 * 16384 samples per packet.
+ */
+#define CONV_BUFFER_SIZE (2 * 8 * 16384 + CONV_BATCH_SIZE)
+
+struct dev_context {
+ unsigned int dig_channel_cnt;
+ uint16_t dig_channel_mask;
+ uint16_t dig_channel_masks[16];
+ uint64_t dig_samplerate;
+
+ uint32_t lfsr;
+
+ unsigned int num_transfers;
+ unsigned int submitted_transfers;
+ struct libusb_transfer **transfers;
+
+ uint8_t *conv_buffer;
+ unsigned int conv_size;
+ unsigned int batch_index;
+};
+
+SR_PRIV int saleae_logic_pro_init(const struct sr_dev_inst *sdi);
+SR_PRIV int saleae_logic_pro_prepare(const struct sr_dev_inst *sdi);
+SR_PRIV int saleae_logic_pro_start(const struct sr_dev_inst *sdi);
+SR_PRIV int saleae_logic_pro_stop(const struct sr_dev_inst *sdi);
+SR_PRIV void LIBUSB_CALL saleae_logic_pro_receive_data(struct libusb_transfer *transfer);
+
+#endif
SR_CONF_CONN,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_CONN | SR_CONF_GET,
SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
};
-static const int32_t soft_trigger_matches[] = {
+static const int32_t trigger_matches[] = {
SR_TRIGGER_ZERO,
SR_TRIGGER_ONE,
SR_TRIGGER_RISING,
static const struct {
enum voltage_range range;
- gdouble low;
- gdouble high;
-} volt_thresholds[] = {
- { VOLTAGE_RANGE_18_33_V, 0.7, 1.4 },
- { VOLTAGE_RANGE_5_V, 1.4, 3.6 },
+} thresholds_ranges[] = {
+ { VOLTAGE_RANGE_18_33_V, },
+ { VOLTAGE_RANGE_5_V, },
+};
+
+static const double thresholds[][2] = {
+ { 0.7, 1.4 },
+ { 1.4, 3.6 },
};
static const uint64_t samplerates[] = {
libusb_get_device_descriptor(devlist[i], &des);
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
if (des.idVendor != LOGIC16_VID || des.idProduct != LOGIC16_PID)
continue;
libusb_get_device_address(devlist[i]), NULL);
} else {
if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
- USB_CONFIGURATION, FX2_FIRMWARE) == SR_OK)
+ 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.");
+ } else {
+ sr_err("Firmware upload failed, name %s.", FX2_FIRMWARE);
+ }
sdi->inst_type = SR_INST_USB;
sdi->conn = sr_usb_dev_inst_new(
libusb_get_bus_number(devlist[i]), 0xff, NULL);
struct sr_usb_dev_inst *usb;
struct libusb_device_descriptor des;
struct drv_context *drvc;
- int ret, i, device_count;
+ int ret = SR_ERR, i, device_count;
char connection_id[64];
di = sdi->driver;
drvc = di->context;
usb = sdi->conn;
- if (sdi->status == SR_ST_ACTIVE)
- /* Device is already in use. */
- return SR_ERR;
-
device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
if (device_count < 0) {
sr_err("Failed to get device list: %s.",
/*
* Check device by its physical USB bus/port address.
*/
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
+
if (strcmp(sdi->connection_id, connection_id))
/* This is not the one. */
continue;
} else {
sr_err("Failed to open device: %s.",
libusb_error_name(ret));
+ ret = SR_ERR;
break;
}
if (ret == LIBUSB_ERROR_BUSY) {
sr_err("Unable to claim USB interface. Another "
"program or driver has already claimed it.");
+ ret = SR_ERR;
break;
} else if (ret == LIBUSB_ERROR_NO_DEVICE) {
sr_err("Device has been disconnected.");
+ ret = SR_ERR;
break;
} else if (ret != 0) {
sr_err("Unable to claim interface: %s.",
libusb_error_name(ret));
+ ret = SR_ERR;
break;
}
break;
}
- sdi->status = SR_ST_ACTIVE;
sr_info("Opened device on %d.%d (logical) / %s (physical), interface %d.",
usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
+ ret = SR_OK;
+
break;
}
+
libusb_free_device_list(devlist, 1);
- if (sdi->status != SR_ST_ACTIVE) {
+ if (ret != SR_OK) {
if (usb->devhdl) {
libusb_release_interface(usb->devhdl, USB_INTERFACE);
libusb_close(usb->devhdl);
struct sr_usb_dev_inst *usb;
usb = sdi->conn;
+
if (!usb->devhdl)
- return SR_ERR;
+ return SR_ERR_BUG;
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;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
- GVariant *range[2];
- char str[128];
- int ret;
unsigned int i;
(void)cg;
- ret = SR_OK;
switch (key) {
case SR_CONF_CONN:
if (!sdi || !sdi->conn)
/* 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);
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
break;
case SR_CONF_SAMPLERATE:
if (!sdi)
if (!sdi)
return SR_ERR;
devc = sdi->priv;
- ret = SR_ERR;
- for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
- if (devc->selected_voltage_range !=
- volt_thresholds[i].range)
+ for (i = 0; i < ARRAY_SIZE(thresholds); i++) {
+ if (devc->selected_voltage_range != thresholds_ranges[i].range)
continue;
- range[0] = g_variant_new_double(volt_thresholds[i].low);
- range[1] = g_variant_new_double(volt_thresholds[i].high);
- *data = g_variant_new_tuple(range, 2);
- ret = SR_OK;
- break;
+ *data = std_gvar_tuple_double(thresholds[i][0], thresholds[i][1]);
+ return SR_OK;
}
- break;
+ return SR_ERR;
default:
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
- gdouble low, high;
- int ret;
- unsigned int i;
+ int idx;
(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:
devc->cur_samplerate = 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;
- for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
- if (fabs(volt_thresholds[i].low - low) < 0.1 &&
- fabs(volt_thresholds[i].high - high) < 0.1) {
- devc->selected_voltage_range =
- volt_thresholds[i].range;
- ret = SR_OK;
- break;
- }
- }
+ if ((idx = std_double_tuple_idx(data, ARRAY_AND_SIZE(thresholds))) < 0)
+ return SR_ERR_ARG;
+ devc->selected_voltage_range = thresholds_ranges[idx].range;
break;
default:
- ret = SR_ERR_NA;
+ return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
-static int config_list(uint32_t 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)
{
- GVariant *gvar, *range[2];
- GVariantBuilder gvb;
- int ret;
- unsigned int i;
-
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
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);
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
break;
case SR_CONF_VOLTAGE_THRESHOLD:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (i = 0; i < ARRAY_SIZE(volt_thresholds); i++) {
- range[0] = g_variant_new_double(volt_thresholds[i].low);
- range[1] = g_variant_new_double(volt_thresholds[i].high);
- gvar = g_variant_new_tuple(range, 2);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_thresholds(ARRAY_AND_SIZE(thresholds));
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));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
default:
return SR_ERR_NA;
}
- return ret;
+ return SR_OK;
}
static void abort_acquisition(struct dev_context *devc)
unsigned char *buf;
size_t size, convsize;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
drvc = di->context;
devc = sdi->priv;
usb = sdi->conn;
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;
+ 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;
{
int ret;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
ret = logic16_abort_acquisition(sdi);
abort_acquisition(sdi->priv);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
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. */
+ if (new_samples <= 0) {
+ resubmit_transfer(transfer);
+ return;
+ }
+
+ /* At least one new sample. */
+ 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(sdi, &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 &&
- new_samples > devc->limit_samples - devc->sent_samples)
- new_samples = devc->limit_samples - devc->sent_samples;
- logic.length = new_samples * 2;
+ 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;
+ logic.data = devc->convbuffer + trigger_offset * 2;
sr_session_send(sdi, &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(sdi, &packet);
- devc->sent_samples += num_samples;
-
- devc->trigger_fired = TRUE;
- }
- }
+ devc->sent_samples += num_samples;
- if (devc->limit_samples &&
- (uint64_t)devc->sent_samples >= devc->limit_samples) {
- devc->sent_samples = -2;
- free_transfer(transfer);
- return;
+ devc->trigger_fired = TRUE;
}
}
+ if (devc->limit_samples &&
+ (uint64_t)devc->sent_samples >= devc->limit_samples) {
+ devc->sent_samples = -2;
+ free_transfer(transfer);
+ return;
+ }
+
resubmit_transfer(transfer);
}
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;
* This file is part of the libsigrok project.
*
* Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
devc = g_malloc0(sizeof(struct dev_context));
devc->device = device;
+ sr_sw_limits_init(&devc->limits);
sdi->priv = devc;
if (device->num_channels) {
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))
+ if (!sr_scpi_cmd_get(devc->device->commands, pci[i].command))
continue;
g_snprintf(ch_name, 16, "%s%s", pci[i].prefix,
channels[ch_num].name);
sr_scpi_hw_info_free(hw_info);
hw_info = NULL;
- scpi_cmd(sdi, devc->device->commands, SCPI_CMD_LOCAL);
+ sr_scpi_cmd(sdi, devc->device->commands, 0, NULL, SCPI_CMD_LOCAL);
return sdi;
}
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);
+ sr_scpi_cmd(sdi, devc->device->commands, 0, NULL, SCPI_CMD_REMOTE);
devc->beeper_was_set = FALSE;
- if (scpi_cmd_resp(sdi, devc->device->commands, &beeper,
- G_VARIANT_TYPE_BOOLEAN, SCPI_CMD_BEEPER) == SR_OK) {
+ if (sr_scpi_cmd_resp(sdi, devc->device->commands, 0, NULL,
+ &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);
+ sr_scpi_cmd(sdi, devc->device->commands,
+ 0, NULL, SCPI_CMD_BEEPER_DISABLE);
}
g_variant_unref(beeper);
}
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;
+ if (!scpi)
+ return SR_ERR_BUG;
+
+ if (devc->beeper_was_set)
+ sr_scpi_cmd(sdi, devc->device->commands,
+ 0, NULL, SCPI_CMD_BEEPER_ENABLE);
+ sr_scpi_cmd(sdi, devc->device->commands, 0, NULL, SCPI_CMD_LOCAL);
+
+ return sr_scpi_close(scpi);
}
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- struct dev_context *devc;
-
- devc = priv;
g_free(devc->channels);
g_free(devc->channel_groups);
- g_free(devc);
}
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, clear_helper);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
const GVariantType *gvtype;
unsigned int i;
+ int channel_group_cmd;
+ char *channel_group_name;
int cmd, ret;
const char *s;
case SR_CONF_REGULATION:
gvtype = G_VARIANT_TYPE_STRING;
cmd = SCPI_CMD_GET_OUTPUT_REGULATION;
+ break;
+ default:
+ return sr_sw_limits_config_get(&devc->limits, key, data);
}
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);
+ channel_group_cmd = 0;
+ channel_group_name = NULL;
+ if (cg) {
+ channel_group_cmd = SCPI_CMD_SELECT_CHANNEL;
+ channel_group_name = g_strdup(cg->name);
+ }
+
+ ret = sr_scpi_cmd_resp(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name, data, gvtype, cmd);
+ g_free(channel_group_name);
if (cmd == SCPI_CMD_GET_OUTPUT_REGULATION) {
/*
return ret;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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 channel_group_cmd;
+ char *channel_group_name;
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);
+ channel_group_cmd = 0;
+ channel_group_name = NULL;
+ if (cg) {
+ channel_group_cmd = SCPI_CMD_SELECT_CHANNEL;
+ channel_group_name = g_strdup(cg->name);
+ }
devc = sdi->priv;
switch (key) {
case SR_CONF_ENABLED:
if (g_variant_get_boolean(data))
- ret = scpi_cmd(sdi, devc->device->commands,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
SCPI_CMD_SET_OUTPUT_ENABLE);
else
- ret = scpi_cmd(sdi, devc->device->commands,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
SCPI_CMD_SET_OUTPUT_DISABLE);
break;
case SR_CONF_VOLTAGE_TARGET:
d = g_variant_get_double(data);
- ret = scpi_cmd(sdi, devc->device->commands,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
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,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
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,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
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,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_ENABLE);
else
- ret = scpi_cmd(sdi, devc->device->commands,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
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,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
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,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE);
else
- ret = scpi_cmd(sdi, devc->device->commands,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
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,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
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,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_ENABLE);
else
- ret = scpi_cmd(sdi, devc->device->commands,
+ ret = sr_scpi_cmd(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name,
SCPI_CMD_SET_OVER_TEMPERATURE_PROTECTION_DISABLE);
break;
default:
- ret = SR_ERR_NA;
+ ret = sr_sw_limits_config_set(&devc->limits, key, data);
}
+ g_free(channel_group_name);
+
return ret;
}
-static int config_list(uint32_t 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)
{
struct dev_context *devc;
struct sr_channel *ch;
const struct channel_spec *ch_spec;
- GVariant *gvar;
- GVariantBuilder gvb;
- int ret, i;
+ int 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;
+ devc = (sdi) ? sdi->priv : NULL;
- ret = SR_OK;
if (!cg) {
- /* No channel group: global options. */
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
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));
+ return std_opts_config_list(key, data, sdi, cg,
+ ARRAY_AND_SIZE(scanopts),
+ ARRAY_AND_SIZE(drvopts),
+ (devc && devc->device) ? devc->device->devopts : NULL,
+ (devc && devc->device) ? devc->device->num_devopts : 0);
break;
case SR_CONF_CHANNEL_CONFIG:
+ if (!devc || !devc->device)
+ return SR_ERR_ARG;
/* Not used. */
i = 0;
if (devc->device->features & PPS_INDEPENDENT)
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
* specification for use in series or parallel mode.
*/
ch = cg->channels->data;
+ if (!devc || !devc->device)
+ return SR_ERR_ARG;
+ ch_spec = &(devc->device->channels[ch->index]);
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));
+ *data = std_gvar_array_u32(devc->device->devopts_cg, devc->device->num_devopts_cg);
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);
+ *data = std_gvar_min_max_step_array(ch_spec->voltage);
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);
+ *data = std_gvar_min_max_step_array(ch_spec->frequency);
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);
+ *data = std_gvar_min_max_step_array(ch_spec->current);
+ break;
+ case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+ *data = std_gvar_min_max_step_array(ch_spec->ovp);
+ break;
+ case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+ *data = std_gvar_min_max_step_array(ch_spec->ocp);
break;
default:
return SR_ERR_NA;
}
}
- return ret;
+ return SR_OK;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
{
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;
+ int ret;
devc = sdi->priv;
scpi = sdi->conn;
+ /* Prime the pipe with the first channel. */
+ devc->cur_acquisition_channel = sr_next_enabled_channel(sdi, NULL);
+
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);
-
- /* 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);
+ sr_sw_limits_acquisition_start(&devc->limits);
return SR_OK;
}
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
struct sr_scpi_dev_inst *scpi;
- float f;
-
- 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);
std_session_send_df_end(sdi);
* Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
* Copyright (C) 2015 Google, Inc.
* (Written by Alexandru Gagniuc <mrnuke@google.com> for Google, Inc.)
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#define CH_IDX(x) (1 << x)
#define FREQ_DC_ONLY {0, 0, 0, 0, 0}
-
-static const uint32_t devopts_none[] = { };
+#define NO_OVP_LIMITS {0, 0, 0, 0, 0}
+#define NO_OCP_LIMITS {0, 0, 0, 0, 0}
/* Agilent/Keysight N5700A series */
static const uint32_t agilent_n5700a_devopts[] = {
SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
};
static const uint32_t agilent_n5700a_devopts_cg[] = {
};
static const struct channel_spec agilent_n5767a_ch[] = {
- { "1", { 0, 60, 0.0072, 3, 4 }, { 0, 25, 0.003, 3, 4 }, { 0, 1500 }, FREQ_DC_ONLY },
+ { "1", { 0, 60, 0.0072, 3, 4 }, { 0, 25, 0.003, 3, 4 }, { 0, 1500 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
};
static const struct channel_spec agilent_n5763a_ch[] = {
- { "1", { 0, 12.5, 0.0015, 3, 4 }, { 0, 120, 0.0144, 3, 4 }, { 0, 1500 }, FREQ_DC_ONLY },
+ { "1", { 0, 12.5, 0.0015, 3, 4 }, { 0, 120, 0.0144, 3, 4 }, { 0, 1500 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
};
/*
/* Chroma 61600 series AC source */
static const uint32_t chroma_61604_devopts[] = {
SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
};
static const uint32_t chroma_61604_devopts_cg[] = {
};
static const struct channel_spec chroma_61604_ch[] = {
- { "1", { 0, 300, 0.1, 1, 1 }, { 0, 16, 0.1, 2, 2 }, { 0, 2000, 0, 1, 1 }, { 1.0, 1000.0, 0.01 } },
+ { "1", { 0, 300, 0.1, 1, 1 }, { 0, 16, 0.1, 2, 2 }, { 0, 2000, 0, 1, 1 }, { 1.0, 1000.0, 0.01 }, NO_OVP_LIMITS, NO_OCP_LIMITS },
};
static const struct channel_group_spec chroma_61604_cg[] = {
};
/* Chroma 62000 series DC source */
-
static const uint32_t chroma_62000_devopts[] = {
SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
};
static const uint32_t chroma_62000_devopts_cg[] = {
channel = g_malloc0(sizeof(struct channel_spec));
channel->name = "1";
channel->voltage[0] = channel->current[0] = channel->power[0] = 0.0;
- channel->voltage[1] = (float)volts;
- channel->current[1] = (float)amps;
- channel->power[1] = (float)watts;
+ channel->voltage[1] = volts;
+ channel->current[1] = amps;
+ channel->power[1] = watts;
channel->voltage[2] = channel->current[2] = 0.01;
channel->voltage[3] = channel->voltage[4] = 3;
channel->current[3] = channel->current[4] = 4;
return SR_OK;
}
+/* Rigol DP700 series */
+static const uint32_t rigol_dp700_devopts[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t rigol_dp700_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_LIST,
+ 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_LIST,
+ SR_CONF_VOLTAGE | SR_CONF_GET,
+ SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_CURRENT | SR_CONF_GET,
+ SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const struct channel_spec rigol_dp711_ch[] = {
+ { "1", { 0, 30, 0.01, 3, 3 }, { 0, 5, 0.01, 3, 3 }, { 0, 150, 0, 3, 3 }, FREQ_DC_ONLY, { 0.01, 33, 0.01}, { 0.01, 5.5, 0.01 } },
+};
+
+static const struct channel_spec rigol_dp712_ch[] = {
+ { "1", { 0, 50, 0.01, 3, 3 }, { 0, 3, 0.01, 3, 3 }, { 0, 150, 0, 3, 3 }, FREQ_DC_ONLY, { 0.01, 55, 0.01}, { 0.01, 3.3, 0.01 } },
+};
+
+static const struct channel_group_spec rigol_dp700_cg[] = {
+ { "1", CH_IDX(0), PPS_OVP | PPS_OCP },
+};
+
+/* Same as the DP800 series, except for the missing :SYST:OTP* commands. */
+static const struct scpi_command rigol_dp700_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_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
+};
+
/* Rigol DP800 series */
static const uint32_t rigol_dp800_devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET | 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 rigol_dp800_devopts_cg[] = {
};
static const struct channel_spec rigol_dp821a_ch[] = {
- { "1", { 0, 60, 0.001, 3, 3 }, { 0, 1, 0.0001, 4, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY },
- { "2", { 0, 8, 0.001, 3, 3 }, { 0, 10, 0.001, 3, 3 }, { 0, 80, 0, 3, 3 }, FREQ_DC_ONLY },
+ { "1", { 0, 60, 0.001, 3, 3 }, { 0, 1, 0.0001, 4, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+ { "2", { 0, 8, 0.001, 3, 3 }, { 0, 10, 0.001, 3, 3 }, { 0, 80, 0, 3, 3 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
};
static const struct channel_spec rigol_dp831_ch[] = {
- { "1", { 0, 8, 0.001, 3, 4 }, { 0, 5, 0.0003, 3, 4 }, { 0, 40, 0, 3, 4 }, FREQ_DC_ONLY },
- { "2", { 0, 30, 0.001, 3, 4 }, { 0, 2, 0.0001, 3, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY },
- { "3", { 0, -30, 0.001, 3, 4 }, { 0, 2, 0.0001, 3, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY },
+ { "1", { 0, 8, 0.001, 3, 4 }, { 0, 5, 0.0003, 3, 4 }, { 0, 40, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+ { "2", { 0, 30, 0.001, 3, 4 }, { 0, 2, 0.0001, 3, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+ { "3", { 0, -30, 0.001, 3, 4 }, { 0, 2, 0.0001, 3, 4 }, { 0, 60, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
};
static const struct channel_spec rigol_dp832_ch[] = {
- { "1", { 0, 30, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY },
- { "2", { 0, 30, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY },
- { "3", { 0, 5, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY },
+ { "1", { 0, 30, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+ { "2", { 0, 30, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+ { "3", { 0, 5, 0.001, 3, 4 }, { 0, 3, 0.001, 3, 4 }, { 0, 90, 0, 3, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
};
static const struct channel_group_spec rigol_dp820_cg[] = {
};
/* HP 663xx series */
-
static const uint32_t hp_6630a_devopts[] = {
SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t hp_6630a_devopts_cg[] = {
SR_CONF_ENABLED | SR_CONF_SET,
SR_CONF_VOLTAGE | SR_CONF_GET,
SR_CONF_CURRENT | SR_CONF_GET,
SR_CONF_VOLTAGE_TARGET | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_CURRENT_LIMIT | SR_CONF_SET | SR_CONF_LIST,
- SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_SET,
+ SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_SET,
};
-static const uint32_t hp_6632b_devopts[] = {
+static const uint32_t hp_6630b_devopts[] = {
SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t hp_6630b_devopts_cg[] = {
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,
+ SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET,
};
static const struct channel_spec hp_6633a_ch[] = {
- { "1", { 0, 51.188, 0.0125, 3, 4 }, { 0, 2.0475, 0.0005, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY },
+ { "1", { 0, 51.188, 0.0125, 3, 4 }, { 0, 2.0475, 0.0005, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY, { 0, 55, 0.25 }, NO_OCP_LIMITS },
+};
+
+static const struct channel_spec hp_6631b_ch[] = {
+ { "1", { 0, 8.19, 0.002, 3, 4 }, { 0, 10.237, 0.00263, 4, 5 }, { 0, 83.84103 }, FREQ_DC_ONLY, { 0, 12, 0.06 }, NO_OCP_LIMITS },
};
static const struct channel_spec hp_6632b_ch[] = {
- { "1", { 0, 20.475, 0.005, 3, 4 }, { 0, 5.1188, 0.00132, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY },
+ { "1", { 0, 20.475, 0.005, 3, 4 }, { 0, 5.1188, 0.00132, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY, { 0, 22, 0.1 }, NO_OCP_LIMITS },
+};
+
+static const struct channel_spec hp_66332a_ch[] = {
+ { "1", { 0, 20.475, 0.005, 3, 4 }, { 0, 5.1188, 0.00132, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY, { 0, 22, 0.1 }, NO_OCP_LIMITS },
+};
+
+static const struct channel_spec hp_6633b_ch[] = {
+ { "1", { 0, 51.188, 0.0125, 3, 4 }, { 0, 2.0475, 0.000526, 4, 5 }, { 0, 104.80743 }, FREQ_DC_ONLY, { 0, 55, 0.25 }, NO_OCP_LIMITS },
+};
+
+static const struct channel_spec hp_6634b_ch[] = {
+ { "1", { 0, 102.38, 0.025, 3, 4 }, { 0, 1.0238, 0.000263, 4, 5 }, { 0, 104.81664 }, FREQ_DC_ONLY, { 0, 110, 0.5 }, NO_OCP_LIMITS },
};
static const struct channel_group_spec hp_663xx_cg[] = {
ALL_ZERO
};
-static const struct scpi_command hp_6632b_cmd[] = {
+static const struct scpi_command hp_6630b_cmd[] = {
+ { SCPI_CMD_REMOTE, "SYST:REM" },
+ { SCPI_CMD_LOCAL, "SYST:LOC" },
{ 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_SET_VOLTAGE_TARGET, ":SOUR:VOLT %.6f" },
{ SCPI_CMD_GET_CURRENT_LIMIT, ":SOUR:CURR?" },
{ SCPI_CMD_SET_CURRENT_LIMIT, ":SOUR:CURR %.6f" },
+ { SCPI_CMD_GET_OVER_CURRENT_PROTECTION_ENABLED, ":CURR:PROT:STAT?" },
+ { SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLE, ":CURR:PROT:STAT 1" },
+ { SCPI_CMD_SET_OVER_CURRENT_PROTECTION_DISABLE, ":CURR:PROT:STAT 0" },
+ { SCPI_CMD_GET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":VOLT:PROT?" },
+ { SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ":VOLT:PROT %.6f" },
ALL_ZERO
};
/* Philips/Fluke PM2800 series */
static const uint32_t philips_pm2800_devopts[] = {
SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
};
static const uint32_t philips_pm2800_devopts_cg[] = {
static const struct philips_pm2800_module_spec {
/* Min, max, programming resolution. */
- float voltage[5];
- float current[5];
- float power[5];
+ double voltage[5];
+ double current[5];
+ double power[5];
} philips_pm2800_module_specs[] = {
/* Autoranging modules. */
[PM2800_MOD_30V_10A] = { { 0, 30, 0.0075, 2, 4 }, { 0, 10, 0.0025, 2, 4 }, { 0, 60 } },
spec->current[0], spec->current[1],
spec->power[0], spec->power[1]);
(*channels)[i].name = (char *)philips_pm2800_names[i];
- memcpy(&((*channels)[i].voltage), spec, sizeof(float) * 15);
+ memcpy(&((*channels)[i].voltage), spec, sizeof(double) * 15);
(*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;
static const uint32_t rs_hmc8043_devopts[] = {
SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
};
static const uint32_t rs_hmc8043_devopts_cg[] = {
};
static const struct channel_spec rs_hmc8043_ch[] = {
- { "1", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY },
- { "2", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY },
- { "3", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY },
+ { "1", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+ { "2", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
+ { "3", { 0, 32.050, 0.001, 3, 4 }, { 0.001, 3, 0.001, 3, 4 }, { 0, 0, 0, 0, 4 }, FREQ_DC_ONLY, NO_OVP_LIMITS, NO_OCP_LIMITS },
};
static const struct channel_group_spec rs_hmc8043_cg[] = {
agilent_n5700a_cmd,
.probe_channels = NULL,
},
+
/* Agilent N5767A */
{ "Agilent", "N5767A", 0,
ARRAY_AND_SIZE(agilent_n5700a_devopts),
agilent_n5700a_cmd,
.probe_channels = NULL,
},
+
/* Chroma 61604 */
{ "Chroma", "61604", 0,
ARRAY_AND_SIZE(chroma_61604_devopts),
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),
chroma_62000_cmd,
.probe_channels = chroma_62000p_probe_channels,
},
+
/* HP 6633A */
{ "HP", "6633A", 0,
ARRAY_AND_SIZE(hp_6630a_devopts),
- ARRAY_AND_SIZE(devopts_none),
+ ARRAY_AND_SIZE(hp_6630a_devopts_cg),
ARRAY_AND_SIZE(hp_6633a_ch),
ARRAY_AND_SIZE(hp_663xx_cg),
hp_6630a_cmd,
.probe_channels = NULL,
},
+ /* HP 6631B */
+ { "HP", "6631B", PPS_OVP | PPS_OCP | PPS_OTP,
+ ARRAY_AND_SIZE(hp_6630b_devopts),
+ ARRAY_AND_SIZE(hp_6630b_devopts_cg),
+ ARRAY_AND_SIZE(hp_6631b_ch),
+ ARRAY_AND_SIZE(hp_663xx_cg),
+ hp_6630b_cmd,
+ .probe_channels = NULL,
+ },
+
/* HP 6632B */
- { "HP", "6632B", 0,
- ARRAY_AND_SIZE(hp_6632b_devopts),
- ARRAY_AND_SIZE(devopts_none),
+ { "HP", "6632B", PPS_OVP | PPS_OCP | PPS_OTP,
+ ARRAY_AND_SIZE(hp_6630b_devopts),
+ ARRAY_AND_SIZE(hp_6630b_devopts_cg),
ARRAY_AND_SIZE(hp_6632b_ch),
ARRAY_AND_SIZE(hp_663xx_cg),
- hp_6632b_cmd,
+ hp_6630b_cmd,
+ .probe_channels = NULL,
+ },
+
+ /* HP 66332A */
+ { "HP", "66332A", PPS_OVP | PPS_OCP | PPS_OTP,
+ ARRAY_AND_SIZE(hp_6630b_devopts),
+ ARRAY_AND_SIZE(hp_6630b_devopts_cg),
+ ARRAY_AND_SIZE(hp_66332a_ch),
+ ARRAY_AND_SIZE(hp_663xx_cg),
+ hp_6630b_cmd,
+ .probe_channels = NULL,
+ },
+
+ /* HP 6633B */
+ { "HP", "6633B", PPS_OVP | PPS_OCP | PPS_OTP,
+ ARRAY_AND_SIZE(hp_6630b_devopts),
+ ARRAY_AND_SIZE(hp_6630b_devopts_cg),
+ ARRAY_AND_SIZE(hp_6633b_ch),
+ ARRAY_AND_SIZE(hp_663xx_cg),
+ hp_6630b_cmd,
+ .probe_channels = NULL,
+ },
+
+ /* HP 6634B */
+ { "HP", "6634B", PPS_OVP | PPS_OCP | PPS_OTP,
+ ARRAY_AND_SIZE(hp_6630b_devopts),
+ ARRAY_AND_SIZE(hp_6630b_devopts_cg),
+ ARRAY_AND_SIZE(hp_6634b_ch),
+ ARRAY_AND_SIZE(hp_663xx_cg),
+ hp_6630b_cmd,
+ .probe_channels = NULL,
+ },
+
+ /* Rigol DP700 series */
+ { "Rigol", "^DP711$", 0,
+ ARRAY_AND_SIZE(rigol_dp700_devopts),
+ ARRAY_AND_SIZE(rigol_dp700_devopts_cg),
+ ARRAY_AND_SIZE(rigol_dp711_ch),
+ ARRAY_AND_SIZE(rigol_dp700_cg),
+ rigol_dp700_cmd,
+ .probe_channels = NULL,
+ },
+ { "Rigol", "^DP712$", 0,
+ ARRAY_AND_SIZE(rigol_dp700_devopts),
+ ARRAY_AND_SIZE(rigol_dp700_devopts_cg),
+ ARRAY_AND_SIZE(rigol_dp712_ch),
+ ARRAY_AND_SIZE(rigol_dp700_cg),
+ rigol_dp700_cmd,
.probe_channels = NULL,
},
#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_analog_encoding encoding;
struct sr_analog_meaning meaning;
struct sr_analog_spec spec;
- const struct sr_dev_inst *sdi;
- struct sr_channel *next_channel;
- struct sr_scpi_dev_inst *scpi;
+ struct sr_dev_inst *sdi;
+ int channel_group_cmd;
+ const char *channel_group_name;
struct pps_channel *pch;
const struct channel_spec *ch_spec;
+ int ret;
float f;
+ GVariant *gvdata;
+ const GVariantType *gvtype;
int cmd;
(void)fd;
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;
- ch_spec = &devc->device->channels[pch->hw_output_idx];
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- /* Note: digits/spec_digits will be overridden later. */
- sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
- analog.meaning->channels = g_slist_append(NULL, devc->cur_channel);
- analog.num_samples = 1;
- analog.meaning->mq = pch->mq;
- if (pch->mq == SR_MQ_VOLTAGE) {
- analog.meaning->unit = SR_UNIT_VOLT;
- analog.encoding->digits = ch_spec->voltage[4];
- analog.spec->spec_digits = ch_spec->voltage[3];
- } else if (pch->mq == SR_MQ_CURRENT) {
- analog.meaning->unit = SR_UNIT_AMPERE;
- analog.encoding->digits = ch_spec->current[4];
- analog.spec->spec_digits = ch_spec->current[3];
- } else if (pch->mq == SR_MQ_POWER) {
- analog.meaning->unit = SR_UNIT_WATT;
- analog.encoding->digits = ch_spec->power[4];
- analog.spec->spec_digits = ch_spec->power[3];
- }
- analog.meaning->mqflags = SR_MQFLAG_DC;
- analog.data = &f;
- sr_session_send(sdi, &packet);
- g_slist_free(analog.meaning->channels);
- }
+ pch = devc->cur_acquisition_channel->priv;
- 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;
- }
+ channel_group_cmd = 0;
+ channel_group_name = NULL;
+ if (g_slist_length(sdi->channel_groups) > 1) {
+ channel_group_cmd = SCPI_CMD_SELECT_CHANNEL;
+ channel_group_name = pch->hwname;
}
- pch = devc->cur_channel->priv;
- if (pch->mq == SR_MQ_VOLTAGE)
+ if (pch->mq == SR_MQ_VOLTAGE) {
+ gvtype = G_VARIANT_TYPE_DOUBLE;
cmd = SCPI_CMD_GET_MEAS_VOLTAGE;
- else if (pch->mq == SR_MQ_FREQUENCY)
+ } else if (pch->mq == SR_MQ_FREQUENCY) {
+ gvtype = G_VARIANT_TYPE_DOUBLE;
cmd = SCPI_CMD_GET_MEAS_FREQUENCY;
- else if (pch->mq == SR_MQ_CURRENT)
+ } else if (pch->mq == SR_MQ_CURRENT) {
+ gvtype = G_VARIANT_TYPE_DOUBLE;
cmd = SCPI_CMD_GET_MEAS_CURRENT;
- else if (pch->mq == SR_MQ_POWER)
+ } else if (pch->mq == SR_MQ_POWER) {
+ gvtype = G_VARIANT_TYPE_DOUBLE;
cmd = SCPI_CMD_GET_MEAS_POWER;
- else
+ } else {
return SR_ERR;
- scpi_cmd(sdi, devc->device->commands, cmd);
+ }
+
+ ret = sr_scpi_cmd_resp(sdi, devc->device->commands,
+ channel_group_cmd, channel_group_name, &gvdata, gvtype, cmd);
+
+ if (ret != SR_OK)
+ return ret;
+
+ ch_spec = &devc->device->channels[pch->hw_output_idx];
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ /* Note: digits/spec_digits will be overridden later. */
+ sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+ analog.meaning->channels = g_slist_append(NULL, devc->cur_acquisition_channel);
+ analog.num_samples = 1;
+ analog.meaning->mq = pch->mq;
+ if (pch->mq == SR_MQ_VOLTAGE) {
+ analog.meaning->unit = SR_UNIT_VOLT;
+ analog.encoding->digits = ch_spec->voltage[4];
+ analog.spec->spec_digits = ch_spec->voltage[3];
+ } else if (pch->mq == SR_MQ_CURRENT) {
+ analog.meaning->unit = SR_UNIT_AMPERE;
+ analog.encoding->digits = ch_spec->current[4];
+ analog.spec->spec_digits = ch_spec->current[3];
+ } else if (pch->mq == SR_MQ_POWER) {
+ analog.meaning->unit = SR_UNIT_WATT;
+ analog.encoding->digits = ch_spec->power[4];
+ analog.spec->spec_digits = ch_spec->power[3];
+ }
+ analog.meaning->mqflags = SR_MQFLAG_DC;
+ f = (float)g_variant_get_double(gvdata);
+ g_variant_unref(gvdata);
+ analog.data = &f;
+ sr_session_send(sdi, &packet);
+ g_slist_free(analog.meaning->channels);
+
+ /* Next channel. */
+ if (g_slist_length(sdi->channels) > 1) {
+ devc->cur_acquisition_channel =
+ sr_next_enabled_channel(sdi, devc->cur_acquisition_channel);
+ }
+
+ if (devc->cur_acquisition_channel == sr_next_enabled_channel(sdi, NULL))
+ /* First enabled channel, so each channel has been sampled */
+ sr_sw_limits_update_samples_read(&devc->limits, 1);
+
+ /* Stop if limits have been hit. */
+ if (sr_sw_limits_check(&devc->limits))
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
* This file is part of the libsigrok project.
*
* Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#define LOG_PREFIX "scpi-pps"
enum pps_scpi_cmds {
- SCPI_CMD_REMOTE,
+ SCPI_CMD_REMOTE = 1,
SCPI_CMD_LOCAL,
SCPI_CMD_BEEPER,
SCPI_CMD_BEEPER_ENABLE,
struct channel_spec {
const char *name;
/* Min, max, programming resolution, spec digits, encoding digits. */
- float voltage[5];
- float current[5];
- float power[5];
- float frequency[5];
+ double voltage[5];
+ double current[5];
+ double power[5];
+ double frequency[5];
+ double ovp[5];
+ double ocp[5];
};
struct channel_group_spec {
STATE_STOP,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
const struct scpi_pps *device;
- /* 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;
+ struct sr_channel *cur_acquisition_channel;
+ struct sr_sw_limits limits;
};
SR_PRIV extern unsigned int num_pps_profiles;
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
int dropped, ret;
size_t len;
uint8_t buf[128];
+ size_t ch_idx;
+ char ch_name[12];
dmm = (struct dmm_info *)di;
sdi->inst_type = SR_INST_SERIAL;
sdi->conn = serial;
sdi->priv = devc;
- sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
+ dmm->channel_count = 1;
+ if (dmm->packet_parse == sr_metex14_4packets_parse)
+ dmm->channel_count = 4;
+ if (dmm->packet_parse == sr_eev121gw_3displays_parse) {
+ dmm->channel_count = EEV121GW_DISPLAY_COUNT;
+ dmm->channel_formats = eev121gw_channel_formats;
+ }
+ for (ch_idx = 0; ch_idx < dmm->channel_count; ch_idx++) {
+ size_t ch_num;
+ const char *fmt;
+ fmt = "P%zu";
+ if (dmm->channel_formats && dmm->channel_formats[ch_idx])
+ fmt = dmm->channel_formats[ch_idx];
+ ch_num = ch_idx + 1;
+ snprintf(ch_name, sizeof(ch_name), fmt, ch_num);
+ sr_channel_new(sdi, ch_idx, SR_CHANNEL_ANALOG, TRUE, ch_name);
+ }
devices = g_slist_append(devices, sdi);
scan_cleanup:
return std_scan_complete(di, devices);
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->limits);
std_session_send_df_header(sdi);
- /* 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);
.cleanup = std_cleanup, \
.scan = scan, \
.dev_list = std_dev_list, \
+ .dev_clear = std_dev_clear, \
.config_get = NULL, \
.config_set = config_set, \
.config_list = config_list, \
.context = NULL, \
}, \
VENDOR, MODEL, CONN, BAUDRATE, PACKETSIZE, TIMEOUT, DELAY, \
- REQUEST, VALID, PARSE, DETAILS, sizeof(struct CHIPSET##_info) \
+ REQUEST, 1, NULL, VALID, PARSE, DETAILS, sizeof(struct CHIPSET##_info) \
}).di
SR_REGISTER_DEV_DRIVER_LIST(serial_dmm_drivers,
sr_dtm0660_packet_valid, sr_dtm0660_parse, NULL
),
/* }}} */
+ /* eev121gw based meters {{{ */
+ DMM(
+ "eevblog-121gw", eev121gw, "EEVblog", "121GW",
+ "115200/8n1", 115200, EEV121GW_PACKET_SIZE, 0, 0, NULL,
+ sr_eev121gw_packet_valid, sr_eev121gw_3displays_parse, NULL
+ ),
+ /* }}} */
/* es519xx based meters {{{ */
DMM(
"iso-tech-idm103n", es519xx,
),
/* }}} */
/* fs9922 based meters {{{ */
+ DMM(
+ "sparkfun-70c", fs9922,
+ "SparkFun", "70C", "2400/8n1/rts=0/dtr=1",
+ 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9922_packet_valid, sr_fs9922_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(
+ "victor-dmm-ser", fs9922,
+ "Victor", "Victor DMMs (Mini-USB cable)", "2400/8n1",
+ 2400, FS9922_PACKET_SIZE, 0, 0, NULL,
+ sr_fs9922_packet_valid, sr_fs9922_parse, NULL
+ ),
DMM(
/*
* Note: The VC830 doesn't set the 'volt' and 'diode' bits of
NULL
),
/* }}} */
+ /* ms8250d based meters {{{ */
+ DMM(
+ "mastech-ms8250d", ms8250d,
+ "MASTECH", "MS8250D", "2400/8n1/rts=0/dtr=1",
+ 2400, MS8250D_PACKET_SIZE, 0, 0, NULL,
+ sr_ms8250d_packet_valid, sr_ms8250d_parse,
+ NULL
+ ),
+ /* }}} */
/* metex14 based meters {{{ */
DMM(
"mastech-mas345", metex14,
sr_metex14_packet_valid, sr_metex14_parse,
NULL
),
+ DMM(
+ "metex-m3860m", metex14,
+ "Metex", "M-3860M", "9600/7n2/rts=0/dtr=1", 9600,
+ 4 * METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_4packets_valid, sr_metex14_4packets_parse,
+ NULL
+ ),
DMM(
"metex-m4650cr", metex14,
"Metex", "M-4650CR", "1200/7n2/rts=0/dtr=1", 1200,
sr_metex14_packet_valid, sr_metex14_parse,
NULL
),
+ DMM(
+ "peaktech-4390a", metex14,
+ "PeakTech", "4390A", "9600/7n2/rts=0/dtr=1", 9600,
+ 4 * METEX14_PACKET_SIZE, 0, 0, sr_metex14_packet_request,
+ sr_metex14_4packets_valid, sr_metex14_4packets_parse,
+ NULL
+ ),
DMM(
"radioshack-22-168", metex14,
"RadioShack", "22-168", "1200/7n2/rts=0/dtr=1", 1200,
sr_vc870_packet_valid, sr_vc870_parse, NULL
),
/* }}} */
+ /* vc96 based meters {{{ */
+ DMM(
+ "voltcraft-vc96", vc96,
+ "Voltcraft", "VC-96", "1200/8n2", 1200,
+ VC96_PACKET_SIZE, 0, 0, NULL,
+ sr_vc96_packet_valid, sr_vc96_parse,
+ NULL
+ ),
+ /* }}} */
/*
* The list is sorted. Add new items in the respective chip's group.
*/
#include "libsigrok-internal.h"
#include "protocol.h"
-static void log_dmm_packet(const uint8_t *buf)
+static void log_dmm_packet(const uint8_t *buf, size_t len)
{
- 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[14], buf[15], buf[16], buf[17], buf[18], buf[19], buf[20],
- buf[21], buf[22]);
+ GString *text;
+
+ text = sr_hexdump_new(buf, len);
+ sr_dbg("DMM packet: %s", text->str);
+ sr_hexdump_free(text);
}
static void handle_packet(const uint8_t *buf, struct sr_dev_inst *sdi,
struct sr_analog_meaning meaning;
struct sr_analog_spec spec;
struct dev_context *devc;
+ gboolean sent_sample;
+ struct sr_channel *channel;
+ size_t ch_idx;
dmm = (struct dmm_info *)sdi->driver;
- log_dmm_packet(buf);
+ log_dmm_packet(buf, dmm->packet_size);
devc = sdi->priv;
- /* Note: digits/spec_digits will be overridden by the DMM parsers. */
- sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
-
- analog.meaning->channels = sdi->channels;
- analog.num_samples = 1;
- analog.meaning->mq = 0;
-
- dmm->packet_parse(buf, &floatval, &analog, info);
- analog.data = &floatval;
-
- /* If this DMM needs additional handling, call the resp. function. */
- if (dmm->dmm_details)
- dmm->dmm_details(&analog, info);
+ sent_sample = FALSE;
+ memset(info, 0, dmm->info_size);
+ for (ch_idx = 0; ch_idx < dmm->channel_count; ch_idx++) {
+ /* Note: digits/spec_digits will be overridden by the DMM parsers. */
+ sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
+
+ channel = g_slist_nth_data(sdi->channels, ch_idx);
+ analog.meaning->channels = g_slist_append(NULL, channel);
+ analog.num_samples = 1;
+ analog.meaning->mq = 0;
+
+ dmm->packet_parse(buf, &floatval, &analog, info);
+ analog.data = &floatval;
+
+ /* If this DMM needs additional handling, call the resp. function. */
+ if (dmm->dmm_details)
+ dmm->dmm_details(&analog, info);
+
+ if (analog.meaning->mq != 0 && channel->enabled) {
+ /* Got a measurement. */
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(sdi, &packet);
+ sent_sample = TRUE;
+ }
+ }
- if (analog.meaning->mq != 0) {
- /* Got a measurement. */
- packet.type = SR_DF_ANALOG;
- packet.payload = &analog;
- sr_session_send(sdi, &packet);
+ if (sent_sample) {
sr_sw_limits_update_samples_read(&devc->limits, 1);
}
}
{
struct dmm_info *dmm;
struct dev_context *devc;
- int len, i, offset = 0;
+ int len, offset;
struct sr_serial_dev_inst *serial;
dmm = (struct dmm_info *)sdi->driver;
devc->buflen += len;
/* Now look for packets in that data. */
+ offset = 0;
while ((devc->buflen - offset) >= dmm->packet_size) {
if (dmm->packet_valid(devc->buf + offset)) {
handle_packet(devc->buf + offset, sdi, info);
}
/* 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];
+ if (devc->buflen > offset)
+ memmove(devc->buf, devc->buf + offset, devc->buflen - offset);
devc->buflen -= offset;
}
}
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
int64_t req_delay_ms;
/** Packet request function. */
int (*packet_request)(struct sr_serial_dev_inst *);
+ /** Number of channels / displays. */
+ size_t channel_count;
+ /** (Optional) printf formats for channel names. */
+ const char **channel_formats;
/** Packet validation function. */
gboolean (*packet_valid)(const uint8_t *);
/** Packet parsing function. */
#define DMM_BUFSIZE 256
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits limits;
static int dev_clear(const struct sr_dev_driver *di)
{
- return std_dev_clear(di, es51919_serial_clean);
+ return std_dev_clear_with_callback(di, es51919_serial_clean);
}
static GSList *scan(struct sr_dev_driver *di, GSList *options)
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 mhooijboer <marchelh@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 <fcntl.h>
+#include <glib.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "protocol.h"
+#include "scpi.h"
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_OSCILLOSCOPE,
+ SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+ 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_TRIGGER_LEVEL | 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_LIST,
+ SR_CONF_SAMPLERATE | SR_CONF_GET,
+ SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_DATA_SOURCE | 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 | SR_CONF_LIST,
+};
+
+static const uint32_t devopts_cg_analog[] = {
+ SR_CONF_NUM_VDIV | SR_CONF_GET,
+ SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_TIMEBASE | 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 | SR_CONF_LIST,
+ SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint64_t timebases[][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 },
+};
+
+static const uint64_t vdivs[][2] = {
+ /* microvolts */
+ { 500, 100000 },
+ /* millivolts */
+ { 1, 1000 },
+ { 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 },
+};
+
+static const char *trigger_sources[] = {
+ "CH1", "CH2", "Ext", "Ext /5", "AC Line",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+ "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15",
+};
+
+static const char *trigger_slopes[] = {
+ "r", "f",
+};
+
+static const char *coupling[] = {
+ "A1M AC 1 Meg",
+ "A50 AC 50 Ohm",
+ "D1M DC 1 Meg",
+ "D50 DC 50 Ohm",
+ "GND",
+};
+
+static const uint64_t probe_factor[] = {
+ 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000,
+};
+
+static const uint64_t averages[] = {
+ 4, 16, 32, 64, 128, 256, 512, 1024,
+};
+
+/* Do not change the order of entries. */
+static const char *data_sources[] = {
+ "Display",
+ "History",
+};
+
+enum vendor {
+ SIGLENT,
+};
+
+enum series {
+ SDS1000CML,
+ SDS1000CNL,
+ SDS1000DL,
+ SDS1000X,
+ SDS1000XP,
+ SDS1000XE,
+ SDS2000X,
+};
+
+/* short name, full name */
+static const struct siglent_sds_vendor supported_vendors[] = {
+ [SIGLENT] = {"Siglent", "Siglent Technologies"},
+};
+
+#define VENDOR(x) &supported_vendors[x]
+/* vendor, series, protocol, max timebase, min vdiv, number of horizontal divs,
+ * number of vertical divs, live waveform samples, memory buffer samples */
+static const struct siglent_sds_series supported_series[] = {
+ [SDS1000CML] = {VENDOR(SIGLENT), "SDS1000CML", NON_SPO_MODEL,
+ { 50, 1 }, { 2, 1000 }, 18, 8, 1400363},
+ [SDS1000CNL] = {VENDOR(SIGLENT), "SDS1000CNL", NON_SPO_MODEL,
+ { 50, 1 }, { 2, 1000 }, 18, 8, 1400363},
+ [SDS1000DL] = {VENDOR(SIGLENT), "SDS1000DL", NON_SPO_MODEL,
+ { 50, 1 }, { 2, 1000 }, 18, 8, 1400363},
+ [SDS1000X] = {VENDOR(SIGLENT), "SDS1000X", SPO_MODEL,
+ { 50, 1 }, { 500, 100000 }, 14, 8, 14000363},
+ [SDS1000XP] = {VENDOR(SIGLENT), "SDS1000X+", SPO_MODEL,
+ { 50, 1 }, { 500, 100000 }, 14, 8, 14000363},
+ [SDS1000XE] = {VENDOR(SIGLENT), "SDS1000XE", ESERIES,
+ { 50, 1 }, { 500, 100000 }, 14, 8, 14000363},
+ [SDS2000X] = {VENDOR(SIGLENT), "SDS2000X", SPO_MODEL,
+ { 50, 1 }, { 500, 100000 }, 14, 8, 14000363},
+};
+
+#define SERIES(x) &supported_series[x]
+/* series, model, min timebase, analog channels, digital */
+static const struct siglent_sds_model supported_models[] = {
+ { SERIES(SDS1000CML), "SDS1152CML", { 20, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000CML), "SDS1102CML", { 10, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000CML), "SDS1072CML", { 5, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000CNL), "SDS1202CNL", { 20, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000CNL), "SDS1102CNL", { 10, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000CNL), "SDS1072CNL", { 5, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000DL), "SDS1202DL", { 20, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000DL), "SDS1102DL", { 10, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000DL), "SDS1022DL", { 5, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000DL), "SDS1052DL", { 5, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000DL), "SDS1052DL+", { 5, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000X), "SDS1102X", { 2, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000XP), "SDS1102X+", { 2, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000X), "SDS1202X", { 2, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000XP), "SDS1202X+", { 2, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000XE), "SDS1202X-E", { 1, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS1000XE), "SDS1104X-E", { 1, 1000000000 }, 4, true, 16 },
+ { SERIES(SDS1000XE), "SDS1204X-E", { 1, 1000000000 }, 4, true, 16 },
+ { SERIES(SDS2000X), "SDS2072X", { 2, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS2000X), "SDS2074X", { 2, 1000000000 }, 4, false, 0 },
+ { SERIES(SDS2000X), "SDS2102X", { 2, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS2000X), "SDS2104X", { 2, 1000000000 }, 4, false, 0 },
+ { SERIES(SDS2000X), "SDS2202X", { 2, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS2000X), "SDS2204X", { 2, 1000000000 }, 4, false, 0 },
+ { SERIES(SDS2000X), "SDS2302X", { 2, 1000000000 }, 2, false, 0 },
+ { SERIES(SDS2000X), "SDS2304X", { 2, 1000000000 }, 4, false, 0 },
+};
+
+SR_PRIV struct sr_dev_driver siglent_sds_driver_info;
+
+static void clear_helper(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+ if (!devc)
+ return;
+ g_free(devc->analog_groups);
+ g_free(devc->enabled_channels);
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+ return std_dev_clear_with_callback(di, clear_helper);
+}
+
+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 *ch;
+ unsigned int i;
+ const struct siglent_sds_model *model;
+ gchar *channel_name;
+
+ sr_dbg("Setting Communication Headers to off.");
+ if (sr_scpi_send(scpi, "CHDR OFF") != SR_OK)
+ return NULL;
+
+ if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+ sr_info("Couldn't get IDN response, retrying.");
+ sr_scpi_close(scpi);
+ sr_scpi_open(scpi);
+ if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+ sr_info("Couldn't get IDN response.");
+ return NULL;
+ }
+ }
+
+ model = NULL;
+ for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
+ if (!strcmp(hw_info->model, supported_models[i].name)) {
+ model = &supported_models[i];
+ break;
+ }
+ }
+
+ 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 = &siglent_sds_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->limit_frames = 1;
+ devc->model = model;
+
+ 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++) {
+ 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]);
+ }
+
+ if (devc->model->has_digital) {
+ 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);
+ devc->digital_group->channels = g_slist_append(
+ devc->digital_group->channels, ch);
+ }
+ devc->digital_group->name = g_strdup("LA");
+ sdi->channel_groups = g_slist_append(sdi->channel_groups,
+ devc->digital_group);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(timebases); i++) {
+ if (!memcmp(&devc->model->min_timebase, &timebases[i], sizeof(uint64_t[2])))
+ devc->timebases = &timebases[i];
+
+ if (!memcmp(&devc->model->series->max_timebase, &timebases[i], sizeof(uint64_t[2])))
+ devc->num_timebases = &timebases[i] - devc->timebases + 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+ devc->vdivs = &vdivs[i];
+ if (!memcmp(&devc->model->series->min_vdiv,
+ &vdivs[i], sizeof(uint64_t[2]))) {
+ devc->vdivs = &vdivs[i];
+ devc->num_vdivs = ARRAY_SIZE(vdivs) - i;
+ break;
+ }
+ }
+
+ devc->buffer = g_malloc(devc->model->series->buffer_samples);
+ sr_dbg("Setting device context buffer size: %i.", devc->model->series->buffer_samples);
+ devc->data = g_malloc(devc->model->series->buffer_samples * sizeof(float));
+
+ devc->data_source = DATA_SOURCE_SCREEN;
+
+ sdi->priv = devc;
+
+ return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ /* TODO: Implement RPC call for LXI device discovery. */
+ return sr_scpi_scan(di->context, options, probe_device);
+}
+
+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;
+ }
+
+ if ((ret = siglent_sds_get_dev_cfg(sdi)) < 0) {
+ sr_err("Failed to get device config: %s.", sr_strerror(ret));
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ return sr_scpi_close(sdi->conn);
+}
+
+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;
+ const char *tmp_str;
+ int analog_channel = -1;
+ float smallest_diff = INFINITY;
+ int idx = -1;
+ unsigned i;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ /* If a channel group is specified, it must be a valid one. */
+ if (cg && !g_slist_find(sdi->channel_groups, cg)) {
+ sr_err("Invalid channel group specified.");
+ return SR_ERR;
+ }
+
+ if (cg) {
+ ch = g_slist_nth_data(cg->channels, 0);
+ if (!ch)
+ return SR_ERR;
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ if (ch->name[2] < '1' || ch->name[2] > '4')
+ return SR_ERR;
+ analog_channel = ch->name[2] - '1';
+ }
+ }
+
+ 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(devc->num_vdivs);
+ break;
+ case SR_CONF_LIMIT_FRAMES:
+ *data = g_variant_new_uint64(devc->limit_frames);
+ break;
+ case SR_CONF_DATA_SOURCE:
+ if (devc->data_source == DATA_SOURCE_SCREEN)
+ *data = g_variant_new_string("Screen");
+ else if (devc->data_source == DATA_SOURCE_HISTORY)
+ *data = g_variant_new_string("History");
+ break;
+ case SR_CONF_SAMPLERATE:
+ siglent_sds_get_dev_cfg_horizontal(sdi);
+ *data = g_variant_new_uint64(devc->samplerate);
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ if (!strcmp(devc->trigger_source, "ACL"))
+ tmp_str = "AC Line";
+ else if (!strcmp(devc->trigger_source, "CHAN1"))
+ tmp_str = "CH1";
+ else if (!strcmp(devc->trigger_source, "CHAN2"))
+ tmp_str = "CH2";
+ else
+ tmp_str = devc->trigger_source;
+ *data = g_variant_new_string(tmp_str);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ if (!strncmp(devc->trigger_slope, "POS", 3)) {
+ tmp_str = "r";
+ } else if (!strncmp(devc->trigger_slope, "NEG", 3)) {
+ tmp_str = "f";
+ } 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_TRIGGER_LEVEL:
+ *data = g_variant_new_double(devc->trigger_level);
+ break;
+ case SR_CONF_HORIZ_TRIGGERPOS:
+ *data = g_variant_new_double(devc->horiz_triggerpos);
+ break;
+ case SR_CONF_TIMEBASE:
+ for (i = 0; i < devc->num_timebases; i++) {
+ float tb, diff;
+
+ tb = (float)devc->timebases[i][0] / devc->timebases[i][1];
+ diff = fabs(devc->timebase - tb);
+ if (diff < smallest_diff) {
+ smallest_diff = diff;
+ idx = i;
+ }
+ }
+ 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) {
+ 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 = fabsf(devc->vdiv[analog_channel] - vdiv);
+ if (diff < smallest_diff) {
+ smallest_diff = diff;
+ idx = i;
+ }
+ }
+ 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) {
+ sr_dbg("Negative analog channel: %d.", analog_channel);
+ return SR_ERR_NA;
+ }
+ *data = g_variant_new_string(devc->coupling[analog_channel]);
+ break;
+ case SR_CONF_PROBE_FACTOR:
+ if (analog_channel < 0) {
+ sr_dbg("Negative analog channel: %d.", analog_channel);
+ return SR_ERR_NA;
+ }
+ *data = g_variant_new_uint64(devc->attenuation[analog_channel]);
+ break;
+ case SR_CONF_AVERAGING:
+ *data = g_variant_new_boolean(devc->average_enabled);
+ break;
+ case SR_CONF_AVG_SAMPLES:
+ *data = g_variant_new_uint64(devc->average_samples);
+ 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 p;
+ double t_dbl;
+ int i;
+ int ret, idx;
+ const char *tmp_str;
+ char buffer[16];
+ char *cmd = "";
+ char cmd4[4];
+
+ devc = sdi->priv;
+
+ /* If a channel group is specified, it must be a valid one. */
+ if (cg && !g_slist_find(sdi->channel_groups, cg)) {
+ sr_err("Invalid channel group specified.");
+ return SR_ERR;
+ }
+
+ ret = SR_OK;
+ switch (key) {
+ case SR_CONF_LIMIT_FRAMES:
+ devc->limit_frames = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_slopes))) < 0)
+ return SR_ERR_ARG;
+ g_free(devc->trigger_slope);
+ devc->trigger_slope = g_strdup((trigger_slopes[idx][0] == 'r') ? "POS" : "NEG");
+ return siglent_sds_config_set(sdi, "%s:TRSL %s",
+ devc->trigger_source, devc->trigger_slope);
+ case SR_CONF_HORIZ_TRIGGERPOS:
+ t_dbl = g_variant_get_double(data);
+ 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. */
+ t_dbl = -(devc->horiz_triggerpos - 0.5) * devc->timebase * devc->num_timebases;
+ g_ascii_formatd(buffer, sizeof(buffer), "%.6f", t_dbl);
+ return siglent_sds_config_set(sdi, ":TIM:OFFS %s", buffer);
+ case SR_CONF_TRIGGER_LEVEL:
+ t_dbl = g_variant_get_double(data);
+ g_ascii_formatd(buffer, sizeof(buffer), "%.3f", t_dbl);
+ ret = siglent_sds_config_set(sdi, ":TRIG:EDGE:LEV %s", buffer);
+ if (ret == SR_OK)
+ devc->trigger_level = t_dbl;
+ break;
+ case SR_CONF_TIMEBASE:
+ if ((idx = std_u64_tuple_idx(data, devc->timebases, devc->num_timebases)) < 0)
+ return SR_ERR_ARG;
+ devc->timebase = (float)devc->timebases[idx][0] / devc->timebases[idx][1];
+ p = devc->timebases[idx][0];
+ switch (devc->timebases[idx][1]) {
+ case 1:
+ cmd = g_strdup_printf("%" PRIu64 "S", p);
+ break;
+ case 1000:
+ cmd = g_strdup_printf("%" PRIu64 "MS", p);
+ break;
+ case 1000000:
+ cmd = g_strdup_printf("%" PRIu64 "US", p);
+ break;
+ case 1000000000:
+ cmd = g_strdup_printf("%" PRIu64 "NS", p);
+ break;
+ }
+ ret = siglent_sds_config_set(sdi, "TDIV %s", cmd);
+ g_free(cmd);
+ return ret;
+ case SR_CONF_TRIGGER_SOURCE:
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 0)
+ return SR_ERR_ARG;
+ g_free(devc->trigger_source);
+ devc->trigger_source = g_strdup(trigger_sources[idx]);
+ if (!strcmp(devc->trigger_source, "AC Line"))
+ tmp_str = "LINE";
+ else if (!strcmp(devc->trigger_source, "CH1"))
+ tmp_str = "C1";
+ else if (!strcmp(devc->trigger_source, "CH2"))
+ tmp_str = "C2";
+ else if (!strcmp(devc->trigger_source, "CH3"))
+ tmp_str = "C3";
+ else if (!strcmp(devc->trigger_source, "CH4"))
+ tmp_str = "C4";
+ else if (!strcmp(devc->trigger_source, "Ext"))
+ tmp_str = "EX";
+ else if (!strcmp(devc->trigger_source, "Ext /5"))
+ tmp_str = "EX5";
+ else
+ tmp_str = (char *)devc->trigger_source;
+ return siglent_sds_config_set(sdi, "TRSE EDGE,SR,%s,OFF", tmp_str);
+ case SR_CONF_VDIV:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(vdivs))) < 0)
+ return SR_ERR_ARG;
+ devc->vdiv[i] = (float)vdivs[idx][0] / vdivs[idx][1];
+ p = vdivs[idx][0];
+ switch (vdivs[idx][1]) {
+ case 1:
+ cmd = g_strdup_printf("%" PRIu64 "V", p);
+ break;
+ case 1000:
+ cmd = g_strdup_printf("%" PRIu64 "MV", p);
+ break;
+ case 100000:
+ cmd = g_strdup_printf("%" PRIu64 "UV", p);
+ break;
+ }
+ ret = siglent_sds_config_set(sdi, "C%d:VDIV %s", i + 1, cmd);
+ g_free(cmd);
+ return ret;
+ case SR_CONF_COUPLING:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(coupling))) < 0)
+ return SR_ERR_ARG;
+ g_free(devc->coupling[i]);
+ devc->coupling[i] = g_strdup(coupling[idx]);
+ strncpy(cmd4, devc->coupling[i], 3);
+ cmd4[3] = 0;
+ return siglent_sds_config_set(sdi, "C%d:CPL %s", i + 1, cmd4);
+ case SR_CONF_PROBE_FACTOR:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ if ((i = std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ if ((idx = std_u64_idx(data, ARRAY_AND_SIZE(probe_factor))) < 0)
+ return SR_ERR_ARG;
+ p = g_variant_get_uint64(data);
+ devc->attenuation[i] = probe_factor[idx];
+ ret = siglent_sds_config_set(sdi, "C%d:ATTN %" PRIu64, i + 1, p);
+ if (ret == SR_OK)
+ siglent_sds_get_dev_cfg_vertical(sdi);
+ return ret;
+ case SR_CONF_DATA_SOURCE:
+ tmp_str = g_variant_get_string(data, NULL);
+ if (!strcmp(tmp_str, "Display"))
+ devc->data_source = DATA_SOURCE_SCREEN;
+ else if (devc->model->series->protocol >= SPO_MODEL
+ && !strcmp(tmp_str, "History"))
+ devc->data_source = DATA_SOURCE_HISTORY;
+ else {
+ sr_err("Unknown data source: '%s'.", tmp_str);
+ return SR_ERR;
+ }
+ break;
+ case SR_CONF_SAMPLERATE:
+ siglent_sds_get_dev_cfg_horizontal(sdi);
+ data = g_variant_new_uint64(devc->samplerate);
+ break;
+ case SR_CONF_AVERAGING:
+ devc->average_enabled = g_variant_get_boolean(data);
+ sr_dbg("%s averaging", devc->average_enabled ? "Enabling" : "Disabling");
+ break;
+ case SR_CONF_AVG_SAMPLES:
+ devc->average_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting averaging rate to %" PRIu64, devc->average_samples);
+ break;
+ default:
+ return 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;
+
+ devc = (sdi) ? sdi->priv : NULL;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ if (!cg)
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ if (!devc)
+ return SR_ERR_ARG;
+ if (cg == devc->digital_group) {
+ *data = std_gvar_array_u32(NULL, 0);
+ return SR_OK;
+ } else {
+ if (std_cg_idx(cg, devc->analog_groups, devc->model->analog_channels) < 0)
+ return SR_ERR_ARG;
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog));
+ return SR_OK;
+ }
+ break;
+ case SR_CONF_COUPLING:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(coupling));
+ break;
+ case SR_CONF_PROBE_FACTOR:
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ *data = std_gvar_array_u64(ARRAY_AND_SIZE(probe_factor));
+ break;
+ case SR_CONF_VDIV:
+ if (!devc)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ if (!cg)
+ return SR_ERR_CHANNEL_GROUP;
+ *data = std_gvar_tuple_array(devc->vdivs, devc->num_vdivs);
+ break;
+ case SR_CONF_TIMEBASE:
+ if (!devc)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ if (devc->num_timebases <= 0)
+ return SR_ERR_NA;
+ *data = std_gvar_tuple_array(devc->timebases, devc->num_timebases);
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ if (!devc)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ *data = g_variant_new_strv(trigger_sources,
+ devc->model->has_digital ? ARRAY_SIZE(trigger_sources) : 5);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_slopes));
+ break;
+ case SR_CONF_DATA_SOURCE:
+ if (!devc)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ switch (devc->model->series->protocol) {
+ /* TODO: Check what must be done here for the data source buffer sizes. */
+ case NON_SPO_MODEL:
+ *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 1);
+ break;
+ case SPO_MODEL:
+ case ESERIES:
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
+ break;
+ }
+ break;
+ case SR_CONF_NUM_HDIV:
+ *data = g_variant_new_int32(devc->model->series->num_horizontal_divs);
+ break;
+ case SR_CONF_AVG_SAMPLES:
+ *data = std_gvar_array_u64(ARRAY_AND_SIZE(averages));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi;
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ struct sr_datafeed_packet packet;
+ gboolean some_digital;
+ GSList *l, *d;
+
+ scpi = sdi->conn;
+ devc = sdi->priv;
+
+ devc->num_frames = 0;
+ some_digital = FALSE;
+
+ /*
+ * Check if there are any logic channels enabled, if so then enable
+ * the MSO, otherwise skip the digital channel setup. Enable and
+ * disable channels on the device is very slow and it is faster when
+ * checked in a small loop without the actual actions.
+ */
+ for (d = sdi->channels; d; d = d->next) {
+ ch = d->data;
+ if (ch->type == SR_CHANNEL_LOGIC && ch->enabled)
+ some_digital = TRUE;
+ }
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ if (ch->enabled)
+ 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 (siglent_sds_config_set(sdi, "C%d:TRA %s", ch->index + 1,
+ ch->enabled ? "ON" : "OFF") != SR_OK)
+ return SR_ERR;
+ devc->analog_channels[ch->index] = ch->enabled;
+ }
+ } else if (ch->type == SR_CHANNEL_LOGIC && some_digital) {
+ if (ch->enabled) {
+ /* Turn on LA module if currently off and digital channels are enabled. */
+ if (!devc->la_enabled) {
+ if (siglent_sds_config_set(sdi, "DI:SW?") != SR_OK)
+ return SR_ERR;
+ devc->la_enabled = TRUE;
+ }
+ devc->enabled_channels = g_slist_append(
+ devc->enabled_channels, ch);
+ }
+ /* Enabled channel is currently disabled, or vice versa. */
+ if (siglent_sds_config_set(sdi, "D%d:TRA %s", ch->index,
+ ch->enabled ? "ON" : "OFF") != SR_OK)
+ return SR_ERR;
+ devc->digital_channels[ch->index] = ch->enabled;
+ }
+ }
+ if (!devc->enabled_channels)
+ return SR_ERR;
+ /* Turn off LA module if on and no digital channels selected. */
+ if (devc->la_enabled && !some_digital)
+ if (siglent_sds_config_set(sdi, "DGST OFF") != SR_OK) {
+ devc->la_enabled = FALSE;
+ g_usleep(500000);
+ return SR_ERR;
+ }
+
+ // devc->analog_frame_size = devc->model->series->buffer_samples;
+ // devc->digital_frame_size = devc->model->series->buffer_samples;
+
+ siglent_sds_get_dev_cfg_horizontal(sdi);
+ switch (devc->model->series->protocol) {
+ case SPO_MODEL:
+ if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK)
+ return SR_ERR;
+ if (devc->average_enabled) {
+ if (siglent_sds_config_set(sdi, "ACQW AVERAGE,%i", devc->average_samples) != SR_OK)
+ return SR_ERR;
+ } else {
+ if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK)
+ return SR_ERR;
+ }
+ break;
+ case NON_SPO_MODEL:
+ /* TODO: Implement CML/CNL/DL models. */
+ if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK)
+ return SR_ERR;
+ if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK)
+ return SR_ERR;
+ break;
+ default:
+ break;
+ }
+
+ sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 7000,
+ siglent_sds_receive, (void *) sdi);
+
+ std_session_send_df_header(sdi);
+
+ devc->channel_entry = devc->enabled_channels;
+
+ if (siglent_sds_capture_start(sdi) != SR_OK)
+ return SR_ERR;
+
+ /* Start of first frame. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_scpi_dev_inst *scpi;
+
+ devc = sdi->priv;
+
+ std_session_send_df_end(sdi);
+
+ g_slist_free(devc->enabled_channels);
+ devc->enabled_channels = NULL;
+ scpi = sdi->conn;
+ sr_scpi_source_remove(sdi->session, scpi);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver siglent_sds_driver_info = {
+ .name = "siglent-sds",
+ .longname = "Siglent SDS1000/SDS2000",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
+
+SR_REGISTER_DEV_DRIVER(siglent_sds_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2016 mhooijboer <marchelh@gmail.com>
+ * Copyright (C) 2012 Martin Ling <martin-git@earth.li>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2013 Mathias Grimmberger <mgri@zaphod.sax.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/>.
+ */
+
+#define _GNU_SOURCE
+
+#include <config.h>
+#include <errno.h>
+#include <glib.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
+#include "protocol.h"
+
+/* Set the next event to wait for in siglent_sds_receive(). */
+static void siglent_sds_set_wait_event(struct dev_context *devc, enum wait_events event)
+{
+ if (event == WAIT_STOP) {
+ devc->wait_status = 2;
+ } else {
+ devc->wait_status = 1;
+ devc->wait_event = event;
+ }
+}
+
+/*
+ * Waiting for a event will return a timeout after 2 to 3 seconds in order
+ * to not block the application.
+ */
+static int siglent_sds_event_wait(const struct sr_dev_inst *sdi)
+{
+ char *buf;
+ long s;
+ int out;
+ struct dev_context *devc;
+ time_t start;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ start = time(NULL);
+
+ s = 10000; /* Sleep time for status refresh. */
+ if (devc->wait_status == 1) {
+ do {
+ if (time(NULL) - start >= 3) {
+ sr_dbg("Timeout waiting for trigger.");
+ return SR_ERR_TIMEOUT;
+ }
+
+ if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+ return SR_ERR;
+ sr_atoi(buf, &out);
+ g_usleep(s);
+ } while (out == 0);
+
+ sr_dbg("Device triggered.");
+
+ if ((devc->timebase < 0.51) && (devc->timebase > 0.99e-6)) {
+ /*
+ * Timebase * num hor. divs * 85(%) * 1e6(usecs) / 100
+ * -> 85 percent of sweep time
+ */
+ s = (devc->timebase * devc->model->series->num_horizontal_divs * 1000);
+ sr_spew("Sleeping for %ld usecs after trigger, "
+ "to let the acq buffer in the device fill", s);
+ g_usleep(s);
+ }
+ }
+ if (devc->wait_status == 2) {
+ do {
+ if (time(NULL) - start >= 3) {
+ sr_dbg("Timeout waiting for trigger.");
+ return SR_ERR_TIMEOUT;
+ }
+ if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+ return SR_ERR;
+ sr_atoi(buf, &out);
+ g_usleep(s);
+ /* XXX
+ * Now this loop condition looks suspicious! A bitwise
+ * OR of a variable and a non-zero literal should be
+ * non-zero. Logical AND of several non-zero values
+ * should be non-zero. Are many parts of the condition
+ * not taking effect? Was some different condition meant
+ * to get encoded? This needs review, and adjustment.
+ */
+ } while (out != DEVICE_STATE_TRIG_RDY || out != DEVICE_STATE_DATA_TRIG_RDY || out != DEVICE_STATE_STOPPED);
+
+ sr_dbg("Device triggered.");
+
+ siglent_sds_set_wait_event(devc, WAIT_NONE);
+ }
+
+ return SR_OK;
+}
+
+static int siglent_sds_trigger_wait(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+ return siglent_sds_event_wait(sdi);
+}
+
+/* Wait for scope to got to "Stop" in single shot mode. */
+static int siglent_sds_stop_wait(const struct sr_dev_inst *sdi)
+{
+ return siglent_sds_event_wait(sdi);
+}
+
+/* Send a configuration setting. */
+SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi, const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, format);
+ ret = sr_scpi_send_variadic(sdi->conn, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/* Start capturing a new frameset. */
+SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ switch (devc->model->series->protocol) {
+ case SPO_MODEL:
+ if (devc->data_source == DATA_SOURCE_SCREEN) {
+ char *buf;
+ int out;
+
+ sr_dbg("Starting data capture for active frameset %" PRIu64 " of %" PRIu64,
+ devc->num_frames + 1, devc->limit_frames);
+ if (siglent_sds_config_set(sdi, "ARM") != SR_OK)
+ return SR_ERR;
+ if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+ return SR_ERR;
+ sr_atoi(buf, &out);
+ if (out == DEVICE_STATE_TRIG_RDY) {
+ siglent_sds_set_wait_event(devc, WAIT_TRIGGER);
+ } else if (out == DEVICE_STATE_DATA_TRIG_RDY) {
+ sr_spew("Device triggered.");
+ siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+ return SR_OK;
+ } else {
+ sr_spew("Device did not enter ARM mode.");
+ return SR_ERR;
+ }
+ } else { /* TODO: Implement history retrieval. */
+ unsigned int framecount;
+ char buf[200];
+ int ret;
+
+ sr_dbg("Starting data capture for history frameset.");
+ if (siglent_sds_config_set(sdi, "FPAR?") != SR_OK)
+ return SR_ERR;
+ ret = sr_scpi_read_data(sdi->conn, buf, 200);
+ if (ret < 0) {
+ sr_err("Read error while reading data header.");
+ return SR_ERR;
+ }
+ memcpy(&framecount, buf + 40, 4);
+ if (devc->limit_frames > framecount)
+ sr_err("Frame limit higher than frames in buffer of device!");
+ else if (devc->limit_frames == 0)
+ devc->limit_frames = framecount;
+ sr_dbg("Starting data capture for history frameset %" PRIu64 " of %" PRIu64,
+ devc->num_frames + 1, devc->limit_frames);
+ if (siglent_sds_config_set(sdi, "FRAM %i", devc->num_frames + 1) != SR_OK)
+ return SR_ERR;
+ if (siglent_sds_channel_start(sdi) != SR_OK)
+ return SR_ERR;
+ siglent_sds_set_wait_event(devc, WAIT_STOP);
+ }
+ break;
+ case ESERIES:
+ if (devc->data_source == DATA_SOURCE_SCREEN) {
+ char *buf;
+ int out;
+
+ sr_dbg("Starting data capture for active frameset %" PRIu64 " of %" PRIu64,
+ devc->num_frames + 1, devc->limit_frames);
+ if (siglent_sds_config_set(sdi, "ARM") != SR_OK)
+ return SR_ERR;
+ if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+ return SR_ERR;
+ sr_atoi(buf, &out);
+ if (out == DEVICE_STATE_TRIG_RDY) {
+ siglent_sds_set_wait_event(devc, WAIT_TRIGGER);
+ } else if (out == DEVICE_STATE_DATA_TRIG_RDY) {
+ sr_spew("Device triggered.");
+ siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+ return SR_OK;
+ } else {
+ sr_spew("Device did not enter ARM mode.");
+ return SR_ERR;
+ }
+ } else { /* TODO: Implement history retrieval. */
+ unsigned int framecount;
+ char buf[200];
+ int ret;
+
+ sr_dbg("Starting data capture for history frameset.");
+ if (siglent_sds_config_set(sdi, "FPAR?") != SR_OK)
+ return SR_ERR;
+ ret = sr_scpi_read_data(sdi->conn, buf, 200);
+ if (ret < 0) {
+ sr_err("Read error while reading data header.");
+ return SR_ERR;
+ }
+ memcpy(&framecount, buf + 40, 4);
+ if (devc->limit_frames > framecount)
+ sr_err("Frame limit higher than frames in buffer of device!");
+ else if (devc->limit_frames == 0)
+ devc->limit_frames = framecount;
+ sr_dbg("Starting data capture for history frameset %" PRIu64 " of %" PRIu64,
+ devc->num_frames + 1, devc->limit_frames);
+ if (siglent_sds_config_set(sdi, "FRAM %i", devc->num_frames + 1) != SR_OK)
+ return SR_ERR;
+ if (siglent_sds_channel_start(sdi) != SR_OK)
+ return SR_ERR;
+ siglent_sds_set_wait_event(devc, WAIT_STOP);
+ }
+ break;
+ case NON_SPO_MODEL:
+ siglent_sds_set_wait_event(devc, WAIT_TRIGGER);
+ break;
+ }
+
+ return SR_OK;
+}
+
+/* Start reading data from the current channel. */
+SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ const char *s;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ ch = devc->channel_entry->data;
+
+ sr_dbg("Start reading data from channel %s.", ch->name);
+
+ switch (devc->model->series->protocol) {
+ case NON_SPO_MODEL:
+ case SPO_MODEL:
+ s = (ch->type == SR_CHANNEL_LOGIC) ? "D%d:WF?" : "C%d:WF? ALL";
+ if (sr_scpi_send(sdi->conn, s, ch->index + 1) != SR_OK)
+ return SR_ERR;
+ siglent_sds_set_wait_event(devc, WAIT_NONE);
+ break;
+ case ESERIES:
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ if (sr_scpi_send(sdi->conn, "C%d:WF? ALL",
+ ch->index + 1) != SR_OK)
+ return SR_ERR;
+ }
+ siglent_sds_set_wait_event(devc, WAIT_NONE);
+ if (sr_scpi_read_begin(sdi->conn) != SR_OK)
+ return TRUE;
+ siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+ break;
+ }
+
+ devc->num_channel_bytes = 0;
+ devc->num_header_bytes = 0;
+ devc->num_block_bytes = 0;
+
+ return SR_OK;
+}
+
+/* Read the header of a data block. */
+static int siglent_sds_read_header(struct sr_dev_inst *sdi)
+{
+ struct sr_scpi_dev_inst *scpi = sdi->conn;
+ struct dev_context *devc = sdi->priv;
+ char *buf = (char *)devc->buffer;
+ int ret, desc_length;
+ int block_offset = 15; /* Offset for descriptor block. */
+ long data_length = 0;
+
+ /* Read header from device. */
+ ret = sr_scpi_read_data(scpi, buf, SIGLENT_HEADER_SIZE);
+ if (ret < SIGLENT_HEADER_SIZE) {
+ sr_err("Read error while reading data header.");
+ return SR_ERR;
+ }
+ sr_dbg("Device returned %i bytes.", ret);
+ devc->num_header_bytes += ret;
+ buf += block_offset; /* Skip to start descriptor block. */
+
+ /* Parse WaveDescriptor header. */
+ memcpy(&desc_length, buf + 36, 4); /* Descriptor block length */
+ memcpy(&data_length, buf + 60, 4); /* Data block length */
+
+ devc->block_header_size = desc_length + 15;
+ devc->num_samples = data_length;
+
+ sr_dbg("Received data block header: '%s' -> block length %d.", buf, ret);
+
+ return ret;
+}
+
+static int siglent_sds_get_digital(const struct sr_dev_inst *sdi, struct sr_channel *ch)
+{
+ struct sr_scpi_dev_inst *scpi = sdi->conn;
+ struct dev_context *devc = sdi->priv;
+ GArray *tmp_samplebuf; /* Temp buffer while iterating over the scope samples */
+ char *buf = (char *)devc->buffer; /* Buffer from scope */
+ uint8_t tmp_value; /* Holding temp value from data */
+ GArray *data_low_channels, *data_high_channels, *buffdata;
+ GSList *l;
+ gboolean low_channels; /* Lower channels enabled */
+ gboolean high_channels; /* Higher channels enabled */
+ int len, channel_index;
+ long samples_index;
+
+ len = 0;
+ channel_index = 0;
+ low_channels = FALSE;
+ high_channels = FALSE;
+ data_low_channels = g_array_new(FALSE, TRUE, sizeof(uint8_t));
+ data_high_channels = g_array_new(FALSE, TRUE, sizeof(uint8_t));
+
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ samples_index = 0;
+ if (ch->type == SR_CHANNEL_LOGIC) {
+ if (ch->enabled) {
+ if (sr_scpi_send(sdi->conn, "D%d:WF? DAT2", ch->index) != SR_OK)
+ return SR_ERR;
+ if (sr_scpi_read_begin(scpi) != SR_OK)
+ return TRUE;
+ len = sr_scpi_read_data(scpi, buf, -1);
+ if (len < 0)
+ return TRUE;
+ len -= 15;
+ buffdata = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len);
+ buf += 15; /* Skipping the data header. */
+ g_array_append_vals(buffdata, buf, len);
+ tmp_samplebuf = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len); /* New temp buffer. */
+ for (uint64_t cur_sample_index = 0; cur_sample_index < devc->memory_depth_digital; cur_sample_index++) {
+ char sample = (char)g_array_index(buffdata, uint8_t, cur_sample_index);
+ for (int ii = 0; ii < 8; ii++, sample >>= 1) {
+ if (ch->index < 8) {
+ channel_index = ch->index;
+ if (data_low_channels->len <= samples_index) {
+ tmp_value = 0; /* New sample. */
+ low_channels = TRUE; /* We have at least one enabled low channel. */
+ } else {
+ /* Get previous stored sample from low channel buffer. */
+ tmp_value = g_array_index(data_low_channels, uint8_t, samples_index);
+ }
+ } else {
+ channel_index = ch->index - 8;
+ if (data_high_channels->len <= samples_index) {
+ tmp_value = 0; /* New sample. */
+ high_channels = TRUE; /* We have at least one enabled high channel. */
+ } else {
+ /* Get previous stored sample from high channel buffer. */
+ tmp_value = g_array_index(data_high_channels, uint8_t, samples_index);
+ }
+ }
+ /* Check if the current scope sample bit is set. */
+ if (sample & 0x1)
+ tmp_value |= 1UL << channel_index; /* Set current scope sample bit based on channel index. */
+ g_array_append_val(tmp_samplebuf, tmp_value);
+ samples_index++;
+ }
+ }
+
+ /* Clear the buffers to prepare for the new samples */
+ if (ch->index < 8) {
+ g_array_free(data_low_channels, FALSE);
+ data_low_channels = g_array_new(FALSE, FALSE, sizeof(uint8_t));
+ } else {
+ g_array_free(data_high_channels, FALSE);
+ data_high_channels = g_array_new(FALSE, FALSE, sizeof(uint8_t));
+ }
+
+ /* Storing the converted temp values from the the scope into the buffers. */
+ for (long index = 0; index < tmp_samplebuf->len; index++) {
+ uint8_t value = g_array_index(tmp_samplebuf, uint8_t, index);
+ if (ch->index < 8)
+ g_array_append_val(data_low_channels, value);
+ else
+ g_array_append_val(data_high_channels, value);
+ }
+ g_array_free(tmp_samplebuf, TRUE);
+ g_array_free(buffdata, TRUE);
+ }
+ }
+ }
+
+ /* Combining the lower and higher channel buffers into one buffer for sigrok. */
+ devc->dig_buffer = g_array_new(FALSE, FALSE, sizeof(uint8_t));
+ for (uint64_t index = 0; index < devc->memory_depth_digital; index++) {
+ uint8_t value;
+ if (low_channels) {
+ value = g_array_index(data_low_channels, uint8_t, index);
+ g_array_append_val(devc->dig_buffer, value);
+ } else {
+ value = 0;
+ g_array_append_val(devc->dig_buffer, value);
+ }
+ if (high_channels) {
+ value = g_array_index(data_high_channels, uint8_t, index);
+ g_array_append_val(devc->dig_buffer, value);
+ } else {
+ value = 0;
+ g_array_append_val(devc->dig_buffer, value);
+ }
+ }
+
+ g_array_free(data_low_channels, TRUE);
+ g_array_free(data_high_channels, TRUE);
+
+ return len;
+}
+
+SR_PRIV int siglent_sds_receive(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 analog;
+ struct sr_analog_encoding encoding;
+ struct sr_analog_meaning meaning;
+ struct sr_analog_spec spec;
+ struct sr_datafeed_logic logic;
+ struct sr_channel *ch;
+ int len, i;
+ float wait;
+ gboolean read_complete = false;
+
+ (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->wait_event) {
+ case WAIT_NONE:
+ break;
+ case WAIT_TRIGGER:
+ if (siglent_sds_trigger_wait(sdi) != SR_OK)
+ return TRUE;
+ if (siglent_sds_channel_start(sdi) != SR_OK)
+ return TRUE;
+ return TRUE;
+ case WAIT_BLOCK:
+ if (siglent_sds_channel_start(sdi) != SR_OK)
+ return TRUE;
+ break;
+ case WAIT_STOP:
+ if (siglent_sds_stop_wait(sdi) != SR_OK)
+ return TRUE;
+ if (siglent_sds_channel_start(sdi) != SR_OK)
+ return TRUE;
+ return TRUE;
+ default:
+ sr_err("BUG: Unknown event target encountered.");
+ break;
+ }
+
+ ch = devc->channel_entry->data;
+ len = 0;
+
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ if (devc->num_block_bytes == 0) {
+ /* Wait for the device to fill its output buffers. */
+ switch (devc->model->series->protocol) {
+ case NON_SPO_MODEL:
+ case SPO_MODEL:
+ /* The older models need more time to prepare the the output buffers due to CPU speed. */
+ wait = (devc->memory_depth_analog * 2.5);
+ sr_dbg("Waiting %.f0 ms for device to prepare the output buffers", wait / 1000);
+ g_usleep(wait);
+ if (sr_scpi_read_begin(scpi) != SR_OK)
+ return TRUE;
+ break;
+ case ESERIES:
+ /* The newer models (ending with the E) have faster CPUs but still need time when a slow timebase is selected. */
+ if (sr_scpi_read_begin(scpi) != SR_OK)
+ return TRUE;
+ wait = ((devc->timebase * devc->model->series->num_horizontal_divs) * 100000);
+ sr_dbg("Waiting %.f0 ms for device to prepare the output buffers", wait / 1000);
+ g_usleep(wait);
+ break;
+ }
+
+ sr_dbg("New block with header expected.");
+ len = siglent_sds_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(sdi, &packet);
+ sdi->driver->dev_acquisition_stop(sdi);
+ return TRUE;
+ }
+ devc->num_block_bytes = len;
+ devc->num_block_read = 0;
+
+ if (len == -1) {
+ sr_err("Read error, aborting capture.");
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+ sdi->driver->dev_acquisition_stop(sdi);
+ return TRUE;
+ }
+
+ do {
+ read_complete = false;
+ if (devc->num_block_bytes > devc->num_samples) {
+ /* We received all data as one block. */
+ /* Offset the data block buffer past the IEEE header and description header. */
+ devc->buffer += devc->block_header_size;
+ len = devc->num_samples;
+ } else {
+ sr_dbg("Requesting: %li bytes.", devc->num_samples - devc->num_block_bytes);
+ len = sr_scpi_read_data(scpi, (char *)devc->buffer, devc->num_samples-devc->num_block_bytes);
+ if (len == -1) {
+ sr_err("Read error, aborting capture.");
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+ sdi->driver->dev_acquisition_stop(sdi);
+ return TRUE;
+ }
+ devc->num_block_read++;
+ devc->num_block_bytes += len;
+ }
+ sr_dbg("Received block: %i, %d bytes.", devc->num_block_read, len);
+ if (ch->type == SR_CHANNEL_ANALOG) {
+ float vdiv = devc->vdiv[ch->index];
+ float offset = devc->vert_offset[ch->index];
+ GArray *float_data;
+ static GArray *data;
+ float voltage, vdivlog;
+ int digits;
+
+ data = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len);
+ g_array_append_vals(data, devc->buffer, len);
+ float_data = g_array_new(FALSE, FALSE, sizeof(float));
+ for (i = 0; i < len; i++) {
+ voltage = (float)g_array_index(data, int8_t, i) / 25;
+ voltage = ((vdiv * voltage) - offset);
+ g_array_append_val(float_data, voltage);
+ }
+ vdivlog = log10f(vdiv);
+ digits = -(int) vdivlog + (vdivlog < 0.0);
+ sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+ analog.meaning->channels = g_slist_append(NULL, ch);
+ analog.num_samples = float_data->len;
+ analog.data = (float *)float_data->data;
+ analog.meaning->mq = SR_MQ_VOLTAGE;
+ analog.meaning->unit = SR_UNIT_VOLT;
+ analog.meaning->mqflags = 0;
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(sdi, &packet);
+ g_slist_free(analog.meaning->channels);
+ g_array_free(data, TRUE);
+ }
+ len = 0;
+ if (devc->num_samples == (devc->num_block_bytes - SIGLENT_HEADER_SIZE)) {
+ sr_dbg("Transfer has been completed.");
+ devc->num_header_bytes = 0;
+ devc->num_block_bytes = 0;
+ read_complete = true;
+ if (!sr_scpi_read_complete(scpi)) {
+ sr_err("Read should have been completed.");
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+ sdi->driver->dev_acquisition_stop(sdi);
+ return TRUE;
+ }
+ devc->num_block_read = 0;
+ } else {
+ sr_dbg("%" PRIu64 " of %" PRIu64 " block bytes read.",
+ devc->num_block_bytes, devc->num_samples);
+ }
+ } while (!read_complete);
+
+ if (devc->channel_entry->next) {
+ /* We got the frame for this channel, now get the next channel. */
+ devc->channel_entry = devc->channel_entry->next;
+ siglent_sds_channel_start(sdi);
+ } else {
+ /* Done with this frame. */
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+ if (++devc->num_frames == devc->limit_frames) {
+ /* Last frame, stop capture. */
+ sdi->driver->dev_acquisition_stop(sdi);
+ } else {
+ /* Get the next frame, starting with the first channel. */
+ devc->channel_entry = devc->enabled_channels;
+ siglent_sds_capture_start(sdi);
+
+ /* Start of next frame. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+ }
+ }
+ }
+ } else {
+ if (!siglent_sds_get_digital(sdi, ch))
+ return TRUE;
+ logic.length = devc->dig_buffer->len;
+ logic.unitsize = 2;
+ logic.data = devc->dig_buffer->data;
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ sr_session_send(sdi, &packet);
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+ sdi->driver->dev_acquisition_stop(sdi);
+
+ if (++devc->num_frames == devc->limit_frames) {
+ /* Last frame, stop capture. */
+ sdi->driver->dev_acquisition_stop(sdi);
+ } else {
+ /* Get the next frame, starting with the first channel. */
+ devc->channel_entry = devc->enabled_channels;
+ siglent_sds_capture_start(sdi);
+
+ /* Start of next frame. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+ }
+ }
+
+ // sr_session_send(sdi, &packet);
+ // packet.type = SR_DF_FRAME_END;
+ // sr_session_send(sdi, &packet);
+ // sdi->driver->dev_acquisition_stop(sdi);
+
+ return TRUE;
+}
+
+SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *ch;
+ char *cmd, *response;
+ unsigned int i;
+ int res, num_tokens;
+ gchar **tokens;
+ int len;
+ float trigger_pos;
+
+ devc = sdi->priv;
+
+ /* Analog channel state. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf("C%i:TRA?", i + 1);
+ res = sr_scpi_get_bool(sdi->conn, cmd, &devc->analog_channels[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ ch = g_slist_nth_data(sdi->channels, i);
+ ch->enabled = devc->analog_channels[i];
+ }
+ sr_dbg("Current analog channel state:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %s", i + 1, devc->analog_channels[i] ? "On" : "Off");
+
+ /* Digital channel state. */
+ if (devc->model->has_digital) {
+ gboolean status;
+
+ sr_dbg("Check logic analyzer channel state.");
+ devc->la_enabled = FALSE;
+ cmd = g_strdup_printf("DI:SW?");
+ res = sr_scpi_get_bool(sdi->conn, cmd, &status);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ sr_dbg("Logic analyzer status: %s", status ? "On" : "Off");
+ if (status) {
+ devc->la_enabled = TRUE;
+ for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+ cmd = g_strdup_printf("D%i:TRA?", i);
+ res = sr_scpi_get_bool(sdi->conn, cmd, &devc->digital_channels[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ ch = g_slist_nth_data(sdi->channels, i + devc->model->analog_channels);
+ ch->enabled = devc->digital_channels[i];
+ sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "On" : "Off");
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+ ch = g_slist_nth_data(sdi->channels, i + devc->model->analog_channels);
+ devc->digital_channels[i] = FALSE;
+ ch->enabled = devc->digital_channels[i];
+ sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "On" : "Off");
+ }
+ }
+ }
+
+ /* Timebase. */
+ if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current timebase: %g.", devc->timebase);
+
+ /* Probe attenuation. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf("C%d:ATTN?", i + 1);
+ res = sr_scpi_get_float(sdi->conn, cmd, &devc->attenuation[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ }
+ sr_dbg("Current probe attenuation:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %g", i + 1, devc->attenuation[i]);
+
+ /* Vertical gain and offset. */
+ if (siglent_sds_get_dev_cfg_vertical(sdi) != SR_OK)
+ return SR_ERR;
+
+ /* Coupling. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf("C%d:CPL?", i + 1);
+ res = sr_scpi_get_string(sdi->conn, cmd, &devc->coupling[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ }
+
+ sr_dbg("Current coupling:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %s", i + 1, devc->coupling[i]);
+
+ /* Trigger source. */
+ response = NULL;
+ tokens = NULL;
+ if (sr_scpi_get_string(sdi->conn, "TRSE?", &response) != SR_OK)
+ return SR_ERR;
+ tokens = g_strsplit(response, ",", 0);
+ for (num_tokens = 0; tokens[num_tokens] != NULL; num_tokens++);
+ if (num_tokens < 4) {
+ sr_dbg("IDN response not according to spec: %80.s.", response);
+ g_strfreev(tokens);
+ g_free(response);
+ return SR_ERR_DATA;
+ }
+ g_free(response);
+ devc->trigger_source = g_strstrip(g_strdup(tokens[2]));
+ sr_dbg("Current trigger source: %s.", devc->trigger_source);
+
+ /* TODO: Horizontal trigger position. */
+ response = "";
+ trigger_pos = 0;
+ // if (sr_scpi_get_string(sdi->conn, g_strdup_printf("%s:TRDL?", devc->trigger_source), &response) != SR_OK)
+ // return SR_ERR;
+ // len = strlen(response);
+ len = strlen(tokens[4]);
+ if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "us")) {
+ trigger_pos = atof(tokens[4]) / SR_GHZ(1);
+ sr_dbg("Current trigger position us %s.", tokens[4] );
+ } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "ns")) {
+ trigger_pos = atof(tokens[4]) / SR_MHZ(1);
+ sr_dbg("Current trigger position ms %s.", tokens[4] );
+ } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "ms")) {
+ trigger_pos = atof(tokens[4]) / SR_KHZ(1);
+ sr_dbg("Current trigger position ns %s.", tokens[4] );
+ } else if (!g_ascii_strcasecmp(tokens[4] + (len - 2), "s")) {
+ trigger_pos = atof(tokens[4]);
+ sr_dbg("Current trigger position s %s.", tokens[4] );
+ };
+ devc->horiz_triggerpos = trigger_pos;
+
+ sr_dbg("Current horizontal trigger position %.10f.", devc->horiz_triggerpos);
+
+ /* Trigger slope. */
+ cmd = g_strdup_printf("%s:TRSL?", devc->trigger_source);
+ res = sr_scpi_get_string(sdi->conn, cmd, &devc->trigger_slope);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current trigger slope: %s.", devc->trigger_slope);
+
+ /* Trigger level, only when analog channel. */
+ if (g_str_has_prefix(tokens[2], "C")) {
+ cmd = g_strdup_printf("%s:TRLV?", devc->trigger_source);
+ res = sr_scpi_get_float(sdi->conn, cmd, &devc->trigger_level);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current trigger level: %g.", devc->trigger_level);
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ char *cmd;
+ unsigned int i;
+ int res;
+
+ devc = sdi->priv;
+
+ /* Vertical gain. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf("C%d:VDIV?", i + 1);
+ res = sr_scpi_get_float(sdi->conn, cmd, &devc->vdiv[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ }
+ sr_dbg("Current vertical gain:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %g", i + 1, devc->vdiv[i]);
+
+ /* Vertical offset. */
+ for (i = 0; i < devc->model->analog_channels; i++) {
+ cmd = g_strdup_printf("C%d:OFST?", i + 1);
+ res = sr_scpi_get_float(sdi->conn, cmd, &devc->vert_offset[i]);
+ g_free(cmd);
+ if (res != SR_OK)
+ return SR_ERR;
+ }
+ sr_dbg("Current vertical offset:");
+ for (i = 0; i < devc->model->analog_channels; i++)
+ sr_dbg("CH%d %g", i + 1, devc->vert_offset[i]);
+
+ return SR_OK;
+}
+
+SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ char *cmd;
+ int res;
+ char *sample_points_string;
+ float samplerate_scope, fvalue;
+
+ devc = sdi->priv;
+
+ switch (devc->model->series->protocol) {
+ case SPO_MODEL:
+ case NON_SPO_MODEL:
+ cmd = g_strdup_printf("SANU? C1");
+ res = sr_scpi_get_string(sdi->conn, cmd, &sample_points_string);
+ g_free(cmd);
+ samplerate_scope = 0;
+ fvalue = 0;
+ if (res != SR_OK)
+ return SR_ERR;
+ if (g_strstr_len(sample_points_string, -1, "Mpts") != NULL) {
+ sample_points_string[strlen(sample_points_string) - 4] = '\0';
+ if (sr_atof_ascii(sample_points_string, &fvalue) != SR_OK) {
+ sr_dbg("Invalid float converted from scope response.");
+ return SR_ERR;
+ }
+ samplerate_scope = fvalue * 1000000;
+ } else if (g_strstr_len(sample_points_string, -1, "Kpts") != NULL) {
+ sample_points_string[strlen(sample_points_string) - 4] = '\0';
+ if (sr_atof_ascii(sample_points_string, &fvalue) != SR_OK) {
+ sr_dbg("Invalid float converted from scope response.");
+ return SR_ERR;
+ }
+ samplerate_scope = fvalue * 10000;
+ } else {
+ samplerate_scope = fvalue;
+ }
+ g_free(sample_points_string);
+ devc->memory_depth_analog = samplerate_scope;
+ break;
+ case ESERIES:
+ cmd = g_strdup_printf("SANU? C1");
+ if (sr_scpi_get_float(sdi->conn, cmd, &fvalue) != SR_OK)
+ return SR_ERR;
+ devc->memory_depth_analog = (long)fvalue;
+ if (devc->la_enabled) {
+ cmd = g_strdup_printf("SANU? D0");
+ if (sr_scpi_get_float(sdi->conn, cmd, &fvalue) != SR_OK)
+ return SR_ERR;
+ devc->memory_depth_digital = (long)fvalue;
+ }
+ g_free(cmd);
+ break;
+ };
+
+ /* Get the timebase. */
+ if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK)
+ return SR_ERR;
+
+ sr_dbg("Current timebase: %g.", devc->timebase);
+ devc->samplerate = devc->memory_depth_analog / (devc->timebase * devc->model->series->num_horizontal_divs);
+ sr_dbg("Current samplerate: %0f.", devc->samplerate);
+ sr_dbg("Current memory depth: %lu.", devc->memory_depth_analog);
+
+ return SR_OK;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 mhooijboer <marchelh@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_SIGLENT_SDS_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SIGLENT_SDS_PROTOCOL_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "siglent-sds"
+
+/* Size of acquisition buffers */
+//#define ACQ_BUFFER_SIZE (6000000)
+#define ACQ_BUFFER_SIZE (18000000)
+
+#define SIGLENT_HEADER_SIZE 363
+#define SIGLENT_DIG_HEADER_SIZE 346
+
+/* Maximum number of samples to retrieve at once. */
+#define ACQ_BLOCK_SIZE (30 * 1000)
+
+#define MAX_ANALOG_CHANNELS 4
+#define MAX_DIGITAL_CHANNELS 16
+
+#define DEVICE_STATE_STOPPED 0 /* Scope is in stopped state bit */
+#define DEVICE_STATE_DATA_ACQ 1 /* A new signal has been acquired bit */
+#define DEVICE_STATE_TRIG_RDY 8192 /* Trigger is ready bit */
+#define DEVICE_STATE_DATA_TRIG_RDY 8193 /* Trigger is ready bit */
+
+enum protocol_version {
+ SPO_MODEL,
+ NON_SPO_MODEL,
+ ESERIES,
+};
+
+enum data_source {
+ DATA_SOURCE_SCREEN,
+ DATA_SOURCE_HISTORY,
+};
+
+struct siglent_sds_vendor {
+ const char *name;
+ const char *full_name;
+};
+
+struct siglent_sds_series {
+ const struct siglent_sds_vendor *vendor;
+ const char *name;
+ enum protocol_version protocol;
+ uint64_t max_timebase[2];
+ uint64_t min_vdiv[2];
+ int num_horizontal_divs;
+ int num_vertical_divs;
+ int buffer_samples;
+};
+
+struct siglent_sds_model {
+ const struct siglent_sds_series *series;
+ const char *name;
+ uint64_t min_timebase[2];
+ unsigned int analog_channels;
+ bool has_digital;
+ unsigned int digital_channels;
+};
+
+enum wait_events {
+ WAIT_NONE, /* Don't wait */
+ WAIT_TRIGGER, /* Wait for trigger */
+ WAIT_BLOCK, /* Wait for block data (only when reading sample mem) */
+ WAIT_STOP, /* Wait for scope stopping (only single shots) */
+};
+
+struct dev_context {
+ /* Device model */
+ const struct siglent_sds_model *model;
+
+ /* Device properties */
+ const uint64_t (*timebases)[2];
+ uint64_t num_timebases;
+ const uint64_t (*vdivs)[2];
+ uint64_t num_vdivs;
+
+ /* Channel groups */
+ struct sr_channel_group **analog_groups;
+ struct sr_channel_group *digital_group;
+
+ /* Acquisition settings */
+ GSList *enabled_channels;
+ uint64_t limit_frames;
+ uint64_t average_samples;
+ gboolean average_enabled;
+ enum data_source data_source;
+ uint64_t analog_frame_size;
+ uint64_t digital_frame_size;
+ uint64_t num_samples;
+ uint64_t memory_depth_analog;
+ uint64_t memory_depth_digital;
+ long block_header_size;
+ float samplerate;
+
+ /* Device settings */
+ gboolean analog_channels[MAX_ANALOG_CHANNELS];
+ gboolean digital_channels[MAX_DIGITAL_CHANNELS];
+ gboolean la_enabled;
+ float timebase;
+ float attenuation[MAX_ANALOG_CHANNELS];
+ float vdiv[MAX_ANALOG_CHANNELS];
+ int vert_reference[MAX_ANALOG_CHANNELS];
+ float vert_offset[MAX_ANALOG_CHANNELS];
+ char *trigger_source;
+ float horiz_triggerpos;
+ char *trigger_slope;
+ float trigger_level;
+ char *coupling[MAX_ANALOG_CHANNELS];
+
+ /* Operational state */
+
+ /* Number of frames received in total. */
+ uint64_t num_frames;
+ /* GSList entry for the current channel. */
+ GSList *channel_entry;
+ /* Number of bytes received for current channel. */
+ uint64_t num_channel_bytes;
+ /* Number of bytes of block header read. */
+ uint64_t num_header_bytes;
+ /* Number of data blocks bytes already read. */
+ uint64_t num_block_bytes;
+ /* Number of data blocks read. */
+ int num_block_read;
+ /* What to wait for in *_receive. */
+ enum wait_events wait_event;
+ /* Trigger/block copying/stop waiting status. */
+ int wait_status;
+ /* Acq buffers used for reading from the scope and sending data to app. */
+ unsigned char *buffer;
+ float *data;
+ GArray *dig_buffer;
+};
+
+SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi,
+ const char *format, ...);
+SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data);
+SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi);
+
+#endif
#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_FALLING,
};
-/* Names assigned to available trigger sources.
- */
-static const char *const trigger_source_names[] = {
+static const char *trigger_sources[] = {
[TRIGGER_CHANNELS] = "CH",
[TRIGGER_EXT_TRG] = "TRG",
};
-/* Names assigned to available edge slope choices.
- */
-static const char *const signal_edge_names[] = {
+static const char *signal_edges[] = {
[EDGE_POSITIVE] = "r",
[EDGE_NEGATIVE] = "f",
};
-/* 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;
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->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->vendor = g_strdup("SysClk");
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;
}
-/* 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;
return std_scan_complete(di, devices);
}
-/* 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.
return SR_OK;
}
-/* Open and initialize device.
- */
static int dev_open(struct sr_dev_inst *sdi)
{
struct drv_context *drvc;
devc = sdi->priv;
usb = sdi->conn;
- 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);
return ret;
}
-/* Shutdown and close device.
- */
static int dev_close(struct sr_dev_inst *sdi)
{
struct dev_context *devc;
devc = sdi->priv;
usb = sdi->conn;
- 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. */
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);
+ if (usb->devhdl)
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+
sr_usb_close(usb);
return ret;
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)
+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;
break;
case SR_CONF_CLOCK_EDGE:
idx = devc->cfg_clock_edge;
- if (idx >= ARRAY_SIZE(signal_edge_names))
+ if (idx >= ARRAY_SIZE(signal_edges))
return SR_ERR_BUG;
- *data = g_variant_new_string(signal_edge_names[idx]);
+ *data = g_variant_new_string(signal_edges[idx]);
break;
case SR_CONF_TRIGGER_SOURCE:
idx = devc->cfg_trigger_source;
- if (idx >= ARRAY_SIZE(trigger_source_names))
+ if (idx >= ARRAY_SIZE(trigger_sources))
return SR_ERR_BUG;
- *data = g_variant_new_string(trigger_source_names[idx]);
+ *data = g_variant_new_string(trigger_sources[idx]);
break;
case SR_CONF_TRIGGER_SLOPE:
idx = devc->cfg_trigger_slope;
- if (idx >= ARRAY_SIZE(signal_edge_names))
+ if (idx >= ARRAY_SIZE(signal_edges))
return SR_ERR_BUG;
- *data = g_variant_new_string(signal_edge_names[idx]);
+ *data = g_variant_new_string(signal_edges[idx]);
break;
default:
/* Must not happen for a key listed in devopts. */
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)
+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;
? 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)
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edges))) < 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)
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_sources))) < 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)
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edges))) < 0)
return SR_ERR_ARG;
devc->cfg_trigger_slope = idx;
break;
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)
+ struct sr_channel *ch, unsigned int changes)
{
uint64_t channel_bit;
struct dev_context *devc;
return SR_OK;
}
-/* Derive trigger masks from the session's trigger configuration.
- */
+/* 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;
return SR_OK;
}
-/* Apply current device configuration to the hardware.
- */
static int config_commit(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
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;
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)
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
- GVariant *gvar;
- GVariantBuilder gvb;
- (void)cg;
+ devc = (sdi) ? sdi->priv : NULL;
- 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;
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return std_opts_config_list(key, data, sdi, cg,
+ ARRAY_AND_SIZE(scanopts), ARRAY_AND_SIZE(drvopts),
+ (devc) ? devc->model->devopts : NULL,
+ (devc) ? devc->model->num_devopts : 0);
}
+ if (!devc)
+ return SR_ERR_ARG;
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);
+ *data = std_gvar_samplerates(devc->model->samplerates, devc->model->num_samplerates);
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]));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
case SR_CONF_TRIGGER_SOURCE:
- *data = g_variant_new_strv(trigger_source_names,
- ARRAY_SIZE(trigger_source_names));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_sources));
break;
case SR_CONF_TRIGGER_SLOPE:
case SR_CONF_CLOCK_EDGE:
- *data = g_variant_new_strv(signal_edge_names,
- ARRAY_SIZE(signal_edge_names));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(signal_edges));
break;
default:
/* Must not happen for a key listed in devopts. */
*/
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
{
- 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)
{
struct dev_context *devc;
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.");
+ sr_dbg("Requesting cancel.");
}
return SR_OK;
}
-/* SysClk LWLA driver descriptor.
- */
static struct sr_dev_driver sysclk_lwla_driver_info = {
.name = "sysclk-lwla",
.longname = "SysClk LWLA series",
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = dev_clear,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_channel_set = config_channel_set,
command[0] = LWLA_WORD(CMD_READ_REG);
command[1] = LWLA_WORD(reg);
- ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+ ret = lwla_send_command(usb, ARRAY_AND_SIZE(command));
if (ret != SR_OK)
return ret;
command[2] = LWLA_WORD_0(value);
command[3] = LWLA_WORD_1(value);
- return lwla_send_command(usb, command, ARRAY_SIZE(command));
+ return lwla_send_command(usb, ARRAY_AND_SIZE(command));
}
SR_PRIV int lwla_write_regs(const struct sr_usb_dev_inst *usb,
*/
#define PACKET_SIZE (5000 * 4 * 5)
-/** LWLA protocol command ID codes.
- */
+/** LWLA protocol command ID codes. */
enum command_id {
CMD_READ_REG = 1,
CMD_WRITE_REG = 2,
STATUS_MEM_AVAIL = 1 << 6,
};
-/** LWLA1034 run-length encoding states.
- */
+/** LWLA1034 run-length encoding states. */
enum rle_state {
RLE_STATE_DATA,
RLE_STATE_LEN
};
-/** Register address/value pair.
- */
+/** Register address/value pair. */
struct regval {
unsigned int reg;
uint32_t val;
};
-/** LWLA sample acquisition and decompression state.
- */
+/** 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 */
#include "lwla.h"
#include "protocol.h"
-/* Number of logic channels.
- */
+/* Number of logic channels. */
#define NUM_CHANNELS 16
-/* Unit size for the sigrok logic datafeed.
- */
+/* Unit size for the sigrok logic datafeed. */
#define UNIT_SIZE ((NUM_CHANNELS + 7) / 8)
-/* Size of the acquisition buffer in device memory units.
- */
+/* Size of the acquisition buffer in device memory units. */
#define MEMORY_DEPTH (256 * 1024) /* 256k x 32 bit */
-/* Capture memory read start address.
- */
+/* Capture memory read start address. */
#define READ_START_ADDR 2
-/* Number of device memory units (32 bit) to read at a time.
- */
+/* Number of device memory units (32 bit) to read at a time. */
#define READ_CHUNK_LEN 250
-/** LWLA1016 register addresses.
- */
+/** LWLA1016 register addresses. */
enum reg_addr {
REG_CHAN_MASK = 0x1000, /* bit mask of enabled channels */
REG_DIV_COUNT = 0x10BC, /* write */
};
-/** Flag bits for REG_MEM_CTRL.
- */
+/** 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.
- */
+/** 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_CNTR_NOT_ENDR = 1 << 6, /* "cntr_not_endr" bit */
};
-/* Available FPGA configurations.
- */
+/* 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.
- */
+/* 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.
- */
+/* Demangle incoming sample data from the transfer buffer. */
static void read_response(struct acquisition_state *acq)
{
uint32_t *in_p, *out_p;
acq->samples_done += run_samples;
}
-/* Demangle and decompress incoming sample data from the transfer buffer.
- */
+/* Demangle and decompress incoming sample data from the transfer buffer. */
static void read_response_rle(struct acquisition_state *acq)
{
uint32_t *in_p;
command[3] = LWLA_WORD_0(count);
command[4] = LWLA_WORD_1(count);
- ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+ ret = lwla_send_command(usb, ARRAY_AND_SIZE(command));
if (ret != SR_OK)
return ret;
return SR_OK;
}
-/* Select and transfer FPGA bitstream for the current configuration.
- */
+/* Select and transfer FPGA bitstream for the current configuration. */
static int apply_fpga_config(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
return ret;
}
-/* Perform initialization self test.
- */
+/* Perform initialization self test. */
static int device_init_check(const struct sr_dev_inst *sdi)
{
static const struct regval mem_reset[] = {
return SR_ERR;
}
- ret = lwla_write_regs(sdi->conn, mem_reset, ARRAY_SIZE(mem_reset));
+ ret = lwla_write_regs(sdi->conn, ARRAY_AND_SIZE(mem_reset));
if (ret != SR_OK)
return ret;
if (ret != SR_OK)
return ret;
- ret = lwla_write_regs(usb, capture_init, ARRAY_SIZE(capture_init));
+ ret = lwla_write_regs(usb, ARRAY_AND_SIZE(capture_init));
if (ret != SR_OK)
return ret;
return SR_OK;
}
-/* Model descriptor for the LWLA1016.
- */
+/* Model descriptor for the LWLA1016. */
SR_PRIV const struct model_info lwla1016_info = {
.name = "LWLA1016",
.num_channels = NUM_CHANNELS,
#include "lwla.h"
#include "protocol.h"
-/* Number of logic channels.
- */
+/* Number of logic channels. */
#define NUM_CHANNELS 34
-/* Bit mask covering all logic channels.
- */
+/* Bit mask covering all logic channels. */
#define ALL_CHANNELS_MASK ((UINT64_C(1) << NUM_CHANNELS) - 1)
-/* Unit size for the sigrok logic datafeed.
- */
+/* Unit size for the sigrok logic datafeed. */
#define UNIT_SIZE ((NUM_CHANNELS + 7) / 8)
-/* Size of the acquisition buffer in device memory units.
- */
+/* Size of the acquisition buffer in device memory units. */
#define MEMORY_DEPTH (256 * 1024) /* 256k x 36 bit */
-/* Capture memory read start address.
- */
+/* 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
*/
#define READ_CHUNK_LEN (28 * 8)
-/* Bit mask for the RLE repeat-count-follows flag.
- */
+/* 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.
#define READ_LREGS_START LREG_MEM_FILL
#define READ_LREGS_COUNT (LREG_STATUS + 1 - READ_LREGS_START)
-/** LWLA1034 register addresses.
- */
+/** LWLA1034 register addresses. */
enum reg_addr {
REG_MEM_CTRL = 0x1074, /* capture buffer control */
REG_MEM_FILL = 0x1078, /* capture buffer fill level */
REG_LONG_HIGH = 0x10BC, /* long register high word */
};
-/** Flag bits for REG_MEM_CTRL.
- */
+/** 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.
- */
+/* LWLA1034 long register addresses. */
enum long_reg_addr {
LREG_CHAN_MASK = 0, /* channel enable mask */
LREG_DIV_COUNT = 1, /* clock divider max count */
LREG_TEST_ID = 100, /* constant test ID */
};
-/** Flag bits for LREG_CAP_CTRL.
- */
+/** 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_CLR_COUNTER = 1 << 6, /* "clr_cntr0" bit */
};
-/* Available FPGA configurations.
- */
+/* Available FPGA configurations. */
enum fpga_config {
FPGA_OFF = 0, /* FPGA shutdown config */
FPGA_INT, /* internal clock config */
FPGA_EXTNEG, /* external clock, falling edge config */
};
-/* FPGA bitstream resource filenames.
- */
+/* FPGA bitstream resource filenames. */
static const char bitstream_map[][32] = {
[FPGA_OFF] = "sysclk-lwla1034-off.rbf",
[FPGA_INT] = "sysclk-lwla1034-int.rbf",
[FPGA_EXTNEG] = "sysclk-lwla1034-extneg.rbf",
};
-/* Read 64-bit long register.
- */
+/* Read 64-bit long register. */
static int read_long_reg(const struct sr_usb_dev_inst *usb,
uint32_t addr, uint64_t *value)
{
return SR_OK;
}
-/* Queue access sequence for a long register write.
- */
+/* 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_STROBE, 0);
}
-/* Helper to fill in the long register bulk write command.
- */
+/* 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 + 6] = LWLA_WORD_3(value);
}
-/* Helper for dissecting the response to a long register bulk read.
- */
+/* 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)
{
command[1] = LWLA_WORD(0);
command[2] = LWLA_WORD(lreg_count);
- ret = lwla_send_command(usb, command, ARRAY_SIZE(command));
+ ret = lwla_send_command(usb, ARRAY_AND_SIZE(command));
if (ret != SR_OK)
return ret;
return SR_ERR;
}
-/* Select and transfer FPGA bitstream for the current configuration.
- */
+/* Select and transfer FPGA bitstream for the current configuration. */
static int apply_fpga_config(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
return ret;
}
-/* Perform initialization self test.
- */
+/* Perform initialization self test. */
static int device_init_check(const struct sr_dev_inst *sdi)
{
uint64_t value;
return detect_short_transfer_quirk(sdi);
}
-/* Set up the device in preparation for an acquisition session.
- */
+/* 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[] = {
usb = sdi->conn;
acq = devc->acquisition;
- ret = lwla_write_regs(usb, capture_init, ARRAY_SIZE(capture_init));
+ ret = lwla_write_regs(usb, ARRAY_AND_SIZE(capture_init));
if (ret != SR_OK)
return ret;
return SR_OK;
}
-/** Model descriptor for the LWLA1034.
- */
+/** Model descriptor for the LWLA1034. */
SR_PRIV const struct model_info lwla1034_info = {
.name = "LWLA1034",
.num_channels = NUM_CHANNELS,
#include "protocol.h"
#include "lwla.h"
-/* Submit an already filled-in USB transfer.
- */
+/* Submit an already filled-in USB transfer. */
static int submit_transfer(struct dev_context *devc,
struct libusb_transfer *xfer)
{
return SR_OK;
}
-/* Set up transfer for the next register in a write sequence.
- */
+/* Set up transfer for the next register in a write sequence. */
static void next_reg_write(struct acquisition_state *acq)
{
struct regval *regval;
acq->xfer_out->length = 4 * sizeof(acq->xfer_buf_out[0]);
}
-/* Set up transfer for the next register in a read sequence.
- */
+/* Set up transfer for the next register in a read sequence. */
static void next_reg_read(struct acquisition_state *acq)
{
unsigned int addr;
acq->xfer_out->length = 2 * sizeof(acq->xfer_buf_out[0]);
}
-/* Decode the response to a register read request.
- */
+/* Decode the response to a register read request. */
static int read_reg_response(struct acquisition_state *acq)
{
uint32_t value;
return SR_OK;
}
-/* Enter a new state and submit the corresponding request to the device.
- */
+/* 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)
{
return submit_transfer(devc, acq->xfer_out);
}
-/* Evaluate and act on the response to a capture status request.
- */
+/* 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;
}
}
-/* Evaluate and act on the response to a capture length request.
- */
+/* 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;
submit_request(sdi, STATE_READ_PREPARE);
}
-/* Evaluate and act on the response to a capture memory read request.
- */
+/* 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;
submit_request(sdi, STATE_READ_FINISH);
}
-/* Destroy and unset the acquisition state record.
- */
+/* Destroy and unset the acquisition state record. */
static void clear_acquisition_state(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
}
}
-/* USB I/O source callback.
- */
+/* USB I/O source callback. */
static int transfer_event(int fd, int revents, void *cb_data)
{
const struct sr_dev_inst *sdi;
return G_SOURCE_REMOVE;
}
-/* USB output transfer completion callback.
- */
+/* USB output transfer completion callback. */
static void LIBUSB_CALL transfer_out_completed(struct libusb_transfer *transfer)
{
const struct sr_dev_inst *sdi;
}
}
-/* USB input transfer completion callback.
- */
+/* USB input transfer completion callback. */
static void LIBUSB_CALL transfer_in_completed(struct libusb_transfer *transfer)
{
const struct sr_dev_inst *sdi;
}
}
-/* Set up the acquisition state record.
- */
+/* Set up the acquisition state record. */
static int init_acquisition_state(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
#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"
+#define LOG_PREFIX "sysclk-lwla"
/* Maximum configurable sample count limit.
* Due to compression, there is no meaningful hardware limit the driver
struct acquisition_state;
-/* USB vendor and product IDs.
- */
+/* USB vendor and product IDs. */
enum {
USB_VID_SYSCLK = 0x2961,
USB_PID_LWLA1016 = 0x6688,
USB_PID_LWLA1034 = 0x6689,
};
-/* USB device characteristics.
- */
+/* USB device characteristics. */
enum {
USB_CONFIG = 1,
USB_INTERFACE = 0,
USB_TIMEOUT_MS = 1000,
};
-/** USB device end points.
- */
+/** USB device end points. */
enum usb_endpoint {
EP_COMMAND = 2,
EP_CONFIG = 4,
EP_REPLY = 6 | LIBUSB_ENDPOINT_IN
};
-/** LWLA1034 clock sources.
- */
+/** LWLA1034 clock sources. */
enum clock_source {
CLOCK_INTERNAL = 0,
CLOCK_EXT_CLK,
};
-/** LWLA1034 trigger sources.
- */
+/** LWLA1034 trigger sources. */
enum trigger_source {
TRIGGER_CHANNELS = 0,
TRIGGER_EXT_TRG,
};
-/** Edge choices for the LWLA1034 external clock and trigger inputs.
- */
+/** Edge choices for the LWLA1034 external clock and trigger inputs. */
enum signal_edge {
EDGE_POSITIVE = 0,
EDGE_NEGATIVE,
FPGA_NOCONF = -1,
};
-/** Acquisition protocol states.
- */
+/** Acquisition protocol states. */
enum protocol_state {
/* idle states */
STATE_IDLE = 0,
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 */
enum signal_edge cfg_trigger_slope; /* ext trigger slope setting */
};
-/** LWLA model descriptor.
- */
+/** LWLA model descriptor. */
struct model_info {
char name[12];
int num_channels;
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_ENERGYMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
return std_scan_complete(di, devices);
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->sw_limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct sr_serial_dev_inst *serial = sdi->conn;
struct dev_context *devc;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
sr_sw_limits_acquisition_start(&devc->sw_limits);
std_session_send_df_header(sdi);
- /* Poll every 50ms, or whenever some data comes in. */
serial_source_add(sdi->session, serial, G_IO_IN, 50,
teleinfo_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = NULL,
.config_set = config_set,
.config_list = config_list,
return;
}
- if (!strcmp(label, "ADCO")) {
+ if (!strcmp(label, "ADCO"))
sr_sw_limits_update_samples_read(&devc->sw_limits, 1);
- } else if (!strcmp(label, "BASE")) {
+ else if (!strcmp(label, "BASE"))
teleinfo_send_value(sdi, "BASE", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "HCHP")) {
+ else if (!strcmp(label, "HCHP"))
teleinfo_send_value(sdi, "HP" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "HCHC")) {
+ else if (!strcmp(label, "HCHC"))
teleinfo_send_value(sdi, "HC" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "EJPHN")) {
+ else if (!strcmp(label, "EJPHN"))
teleinfo_send_value(sdi, "HN" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "EJPHPM")) {
+ else if (!strcmp(label, "EJPHPM"))
teleinfo_send_value(sdi, "HPM" , v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHPJB")) {
+ else if (!strcmp(label, "BBRHPJB"))
teleinfo_send_value(sdi, "HPJB", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHPJW")) {
+ else if (!strcmp(label, "BBRHPJW"))
teleinfo_send_value(sdi, "HPJW", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHPJR")) {
+ else if (!strcmp(label, "BBRHPJR"))
teleinfo_send_value(sdi, "HPJR", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHCJB")) {
+ else if (!strcmp(label, "BBRHCJB"))
teleinfo_send_value(sdi, "HCJB", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHCJW")) {
+ else if (!strcmp(label, "BBRHCJW"))
teleinfo_send_value(sdi, "HCJW", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "BBRHCJR")) {
+ else if (!strcmp(label, "BBRHCJR"))
teleinfo_send_value(sdi, "HCJR", v, SR_MQ_POWER, SR_UNIT_WATT_HOUR);
- } else if (!strcmp(label, "IINST")) {
+ else if (!strcmp(label, "IINST"))
teleinfo_send_value(sdi, "IINST", v, SR_MQ_CURRENT, SR_UNIT_AMPERE);
- } else if (!strcmp(label, "PAPP")) {
+ else if (!strcmp(label, "PAPP"))
teleinfo_send_value(sdi, "PAPP", v, SR_MQ_POWER, SR_UNIT_VOLT_AMPERE);
- }
}
static gboolean teleinfo_parse_group(struct sr_dev_inst *sdi,
}
if (sr_sw_limits_check(&devc->sw_limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#define TELEINFO_BUF_SIZE 256
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Acquisition settings */
struct sr_sw_limits sw_limits;
-
- /* Operational state */
enum optarif optarif; /**< The device mode (which measures are reported) */
-
- /* Temporary state across callbacks */
uint8_t buf[TELEINFO_BUF_SIZE];
int buf_len;
};
#include "protocol.h"
#define SERIALCOMM "115200/8n1"
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
static const uint32_t scanopts[] = {
SR_CONF_CONN,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_GET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET | SR_CONF_GET,
if (strncmp(manufacturer, "testo", 5))
continue;
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
- /* Hardcode the 435 for now.*/
+ /* Hardcode the 435 for now. */
if (strcmp(product, "testo 435/635/735"))
continue;
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);
sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
return SR_OK;
}
struct sr_usb_dev_inst *usb;
usb = sdi->conn;
+
if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
+ return SR_ERR_BUG;
libusb_release_interface(usb->devhdl, 0);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc = sdi->priv;
struct sr_usb_dev_inst *usb;
- char str[128];
(void)cg;
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);
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
break;
case SR_CONF_LIMIT_MSEC:
case SR_CONF_LIMIT_SAMPLES:
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
return sr_sw_limits_config_set(&devc->sw_limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static void receive_data(struct sr_dev_inst *sdi, unsigned char *data, int len)
devc->reply_size = 0;
if (sr_sw_limits_check(&devc->sw_limits))
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
else
testo_request_packet(sdi);
if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
/* USB device was unplugged. */
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
} else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
/* First two bytes in any transfer are FTDI status bytes. */
if (transfer->actual_length > 2)
libusb_error_name(ret));
g_free(transfer->buffer);
libusb_free_transfer(transfer);
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
} else {
/* This was the last transfer we're going to receive, so
drvc = di->context;
if (sr_sw_limits_check(&devc->sw_limits))
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
if (sdi->status == SR_ST_STOPPING) {
usb_source_remove(sdi->session, drvc->sr_ctx);
unsigned char *buf;
drvc = di->context;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
-
devc = sdi->priv;
usb = sdi->conn;
devc->reply_size = 0;
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
sdi->status = SR_ST_STOPPING;
return SR_OK;
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
usb = sdi->conn;
sr_dbg("Probing for channels.");
- if (sdi->driver->dev_open(sdi) != SR_OK)
+ if (sr_dev_open(sdi) != SR_OK)
return SR_ERR;
if (testo_set_serial_params(usb) != SR_OK)
return SR_ERR;
/* Got a complete packet. */
break;
}
- sdi->driver->dev_close(sdi);
+ sr_dev_close(sdi);
if (packet[6] > MAX_CHANNELS) {
sr_err("Device says it has %d channels!", packet[6]);
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);
+ sr_dev_acquisition_stop((struct sr_dev_inst *)sdi);
return SR_ERR;
}
sr_dbg("Requested new packet.");
* 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)
{
+ static const unsigned char check[] = { 0x21, 0, 0, 0, 1 };
int i;
- unsigned char check[] = { 0x21, 0, 0, 0, 1 };
if (len < 5)
return FALSE;
const uint8_t *request;
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Model-specific information */
const struct testo_model *model;
-
- /* Acquisition settings */
struct sr_sw_limits sw_limits;
- /* Operational state */
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_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_SOUNDLEVELMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
serial = sr_serial_dev_inst_new(conn, serialcomm);
- if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+ if (serial_open(serial, SERIAL_RDWR) != SR_OK) {
+ g_free(sdi);
return NULL;
+ }
sdi->inst_type = SR_INST_SERIAL;
sdi->conn = serial;
return std_scan_complete(di, g_slist_append(NULL, sdi));
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
struct dev_context *devc = sdi->priv;
struct sr_serial_dev_inst *serial;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
std_session_send_df_header(sdi);
sr_sw_limits_acquisition_start(&devc->limits);
- /* Poll every 500ms, or whenever some data comes in. */
serial = sdi->conn;
serial_source_add(sdi->session, serial, G_IO_IN, 500,
tondaj_sl_814_receive_data, (void *)sdi);
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = NULL,
.config_set = config_set,
.config_list = config_list,
}
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#define LOG_PREFIX "tondaj-sl-814"
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits limits;
int state;
SR_CONF_CONN,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_GET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET | SR_CONF_GET,
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;
+ return sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
std_session_send_df_header(sdi);
- sr_session_source_add(sdi->session, -1, 0, 10 /* poll_timeout */,
+ sr_session_source_add(sdi->session, -1, 0, 10,
uni_t_dmm_receive_data, (void *)sdi);
return SR_OK;
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- sr_dbg("Stopping acquisition.");
std_session_send_df_end(sdi);
sr_session_source_remove(sdi->session, -1);
.cleanup = std_cleanup, \
.scan = scan, \
.dev_list = std_dev_list, \
+ .dev_clear = std_dev_clear, \
.config_get = NULL, \
.config_set = config_set, \
.config_list = config_list, \
.dev_open = dev_open, \
- .dev_close = dev_close, \
+ .dev_close = std_dummy_dev_close /* TODO */, \
.dev_acquisition_start = dev_acquisition_start, \
.dev_acquisition_stop = dev_acquisition_stop, \
.context = NULL, \
usb = sdi->conn;
- /* Detach kernel drivers which grabbed this device (if any). */
if (libusb_kernel_driver_active(usb->devhdl, 0) == 1) {
ret = libusb_detach_kernel_driver(usb->devhdl, 0);
if (ret < 0) {
libusb_error_name(ret));
return SR_ERR;
}
- sr_dbg("Successfully detached kernel driver.");
- } else {
- sr_dbg("No need to detach a kernel driver.");
}
- /* Claim interface 0. */
if ((ret = libusb_claim_interface(usb->devhdl, 0)) < 0) {
sr_err("Failed to claim interface 0: %s.",
libusb_error_name(ret));
return SR_ERR;
}
- sr_dbg("Successfully claimed interface 0.");
/* Set data for the HID feature report (e.g. baudrate). */
buf[0] = baudrate & 0xff; /* Baudrate, LSB */
return SR_ERR;
}
- sr_dbg("Successfully sent initial HID feature report.");
-
return SR_OK;
}
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",
- 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]);
+ GString *text;
+
+ text = sr_hexdump_new(buf, 14);
+ sr_dbg("DMM packet: %s", text->str);
+ sr_hexdump_free(text);
}
static int get_and_handle_data(struct sr_dev_inst *sdi)
}
/* Move remaining bytes to beginning of buffer. */
- for (i = 0; i < devc->buflen - devc->bufoffset; i++)
- pbuf[i] = pbuf[devc->bufoffset + i];
+ if (devc->bufoffset < devc->buflen)
+ memmove(pbuf, pbuf + devc->bufoffset, devc->buflen - devc->bufoffset);
devc->buflen -= devc->bufoffset;
return SR_OK;
/* Abort acquisition if we acquired enough samples. */
if (sr_sw_limits_check(&devc->limits))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
#define DMM_BUFSIZE 256
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits limits;
SR_CONF_CONN,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_THERMOMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
static const char *data_sources[] = {
- "Live",
- "Memory",
+ "Live", "Memory",
};
static GSList *scan(struct sr_dev_driver *di, GSList *options)
for (l = usb_devices; l; l = l->next) {
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(VENDOR);
- sdi->model = g_strdup(MODEL);
+ sdi->vendor = g_strdup("UNI-T");
+ sdi->model = g_strdup("UT32x");
sdi->inst_type = SR_INST_USB;
sdi->conn = l->data;
for (i = 0; i < ARRAY_SIZE(channel_names); i++)
sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
- return ret;
+ return SR_OK;
}
static int dev_close(struct sr_dev_inst *sdi)
struct sr_usb_dev_inst *usb;
usb = sdi->conn;
+
if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
+ return SR_ERR_BUG;
libusb_release_interface(usb->devhdl, USB_INTERFACE);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
- const char *tmp_str;
+ int idx;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
devc->limit_samples = g_variant_get_uint64(data);
break;
case SR_CONF_DATA_SOURCE:
- tmp_str = g_variant_get_string(data, NULL);
- if (!strcmp(tmp_str, "Live"))
- devc->data_source = DATA_SOURCE_LIVE;
- else if (!strcmp(tmp_str, "Memory"))
- devc->data_source = DATA_SOURCE_MEMORY;
- else
- return SR_ERR;
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources))) < 0)
+ return SR_ERR_ARG;
+ devc->data_source = idx;
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)
+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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_DATA_SOURCE:
- *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
break;
default:
return SR_ERR_NA;
int len, ret;
unsigned char cmd[2];
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
drvc = di->context;
devc = sdi->priv;
usb = sdi->conn;
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
/* Signal USB transfer handler to clean up and stop. */
sdi->status = SR_ST_STOPPING;
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
* a sample limit on "Memory" data source still works: unused
* memory slots come through as "----" measurements. */
devc->num_samples++;
- if (devc->limit_samples && devc->num_samples >= devc->limit_samples) {
- sdi->driver->dev_acquisition_stop(sdi);
- }
-
+ if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
+ sr_dev_acquisition_stop(sdi);
}
SR_PRIV void LIBUSB_CALL uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer)
#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
#define USB_CONN "1a86.e008"
-#define VENDOR "UNI-T"
-#define MODEL "UT32x"
#define USB_INTERFACE 0
#define USB_CONFIGURATION 1
CMD_GET_STORED = 7,
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /* Acquisition settings */
uint64_t limit_samples;
gboolean data_source;
- /* Operational state */
uint64_t num_samples;
unsigned char buf[8];
struct libusb_transfer *xfer;
- /* Temporary state across callbacks */
unsigned char packet[32];
int packet_len;
};
#define VICTOR_VID 0x1244
#define VICTOR_PID 0xd237
-#define VICTOR_VENDOR "Victor"
#define VICTOR_INTERFACE 0
#define VICTOR_ENDPOINT (LIBUSB_ENDPOINT_IN | 1)
-static int dev_acquisition_stop(struct sr_dev_inst *sdi);
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+};
static const uint32_t drvopts[] = {
SR_CONF_MULTIMETER,
};
-static const uint32_t scanopts[] = {
- SR_CONF_CONN,
-};
-
static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
if (des.idVendor != VICTOR_VID || des.idProduct != VICTOR_PID)
continue;
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(VICTOR_VENDOR);
+ sdi->vendor = g_strdup("Victor");
sdi->connection_id = g_strdup(connection_id);
devc = g_malloc0(sizeof(struct dev_context));
sr_sw_limits_init(&devc->limits);
if (ret != SR_OK)
return ret;
- /* The device reports as HID class, so the kernel would have
- * claimed it. */
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.",
sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
return SR_ERR;
}
- sdi->status = SR_ST_ACTIVE;
return SR_OK;
}
usb = sdi->conn;
if (!usb->devhdl)
- /* Nothing to do. */
- return SR_OK;
+ return SR_ERR_BUG;
libusb_release_interface(usb->devhdl, VICTOR_INTERFACE);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc = sdi->priv;
struct sr_usb_dev_inst *usb;
- char str[128];
(void)cg;
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);
+ *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
break;
case SR_CONF_LIMIT_SAMPLES:
case SR_CONF_LIMIT_MSEC:
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
-
devc = sdi->priv;
return sr_sw_limits_config_set(&devc->limits, key, data);
}
-static int config_list(uint32_t 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)
{
- (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:
- 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;
- }
-
- return SR_OK;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
}
static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
devc = sdi->priv;
if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
/* USB device was unplugged. */
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
} else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
sr_dbg("Got %d-byte packet.", transfer->actual_length);
if (transfer->actual_length == DMM_DATA_SIZE) {
victor_dmm_receive_data(sdi, transfer->buffer);
if (sr_sw_limits_check(&devc->limits))
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
}
/* Anything else is either an error or a timeout, which is fine:
libusb_error_name(ret));
g_free(transfer->buffer);
libusb_free_transfer(transfer);
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
} else {
/* This was the last transfer we're going to receive, so
drvc = di->context;
if (sr_sw_limits_check(&devc->limits))
- dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
if (sdi->status == SR_ST_STOPPING) {
usb_source_remove(sdi->session, drvc->sr_ctx);
int ret;
unsigned char *buf;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
usb = sdi->conn;
std_session_send_df_header(sdi);
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("Device not active, can't stop acquisition.");
- return SR_ERR;
- }
-
sdi->status = SR_ST_STOPPING;
return SR_OK;
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
if (is_diode) {
meaning.mq = SR_MQ_VOLTAGE;
meaning.unit = SR_UNIT_VOLT;
- meaning.mqflags |= SR_MQFLAG_DIODE;
+ meaning.mqflags |= SR_MQFLAG_DIODE | SR_MQFLAG_DC;
if (ivalue < 0)
fvalue = NAN;
} else {
SR_PRIV int victor_dmm_receive_data(struct sr_dev_inst *sdi, unsigned char *buf)
{
+ static const unsigned char obfuscation[DMM_DATA_SIZE] = "jodenxunickxia";
+ static const unsigned char shuffle[DMM_DATA_SIZE] = {
+ 6, 13, 5, 11, 2, 7, 9, 8, 3, 10, 12, 0, 4, 1
+ };
GString *dbg;
int i;
unsigned char data[DMM_DATA_SIZE];
- unsigned char obfuscation[DMM_DATA_SIZE] = "jodenxunickxia";
- unsigned char shuffle[DMM_DATA_SIZE] = {
- 6, 13, 5, 11, 2, 7, 9, 8, 3, 10, 12, 0, 4, 1
- };
for (i = 0; i < DMM_DATA_SIZE && buf[i] == 0; i++);
if (i == DMM_DATA_SIZE) {
#define DMM_DATA_SIZE 14
-/** Private, per-device-instance driver context. */
struct dev_context {
struct sr_sw_limits limits;
};
static 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[] = {
+static const uint32_t scanopts[] = {
SR_CONF_CONN,
};
-static const uint32_t dlm_drvopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_LOGIC_ANALYZER,
SR_CONF_OSCILLOSCOPE,
};
-static const uint32_t dlm_devopts[] = {
+static const uint32_t 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_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
-static const uint32_t dlm_analog_devopts[] = {
+static const uint32_t devopts_cg_analog[] = {
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 uint32_t dlm_digital_devopts[] = {
+static const uint32_t devopts_cg_digital[] = {
};
enum {
CG_DIGITAL,
};
-static struct sr_dev_inst *probe_usbtmc_device(struct sr_scpi_dev_inst *scpi)
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
{
struct sr_dev_inst *sdi;
struct dev_context *devc;
goto fail;
sdi = g_malloc0(sizeof(struct sr_dev_inst));
- sdi->vendor = g_strdup(MANUFACTURER_NAME);
+ sdi->vendor = g_strdup("Yokogawa");
sdi->model = g_strdup(model_name);
sdi->version = g_strdup(hw_info->firmware_version);
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
- return sr_scpi_scan(di->context, options, probe_usbtmc_device);
+ return sr_scpi_scan(di->context, options, probe_device);
}
-static void clear_helper(void *priv)
+static void clear_helper(struct dev_context *devc)
{
- 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);
+ return std_dev_clear_with_callback(di, (std_dev_clear_callback)clear_helper);
}
static int dev_open(struct sr_dev_inst *sdi)
{
- if (sdi->status != SR_ST_ACTIVE && sr_scpi_open(sdi->conn) != SR_OK)
+ if (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;
+ return sr_scpi_close(sdi->conn);
}
/**
static int check_channel_group(struct dev_context *devc,
const struct sr_channel_group *cg)
{
- unsigned int i;
const struct scope_config *model;
+ if (!devc)
+ return CG_INVALID;
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;
+ if (std_cg_idx(cg, devc->analog_groups, model->analog_channels) >= 0)
+ return CG_ANALOG;
- for (i = 0; i < model->pods; i++)
- if (cg == devc->digital_groups[i])
- return CG_DIGITAL;
+ if (std_cg_idx(cg, devc->digital_groups, model->pods) >= 0)
+ 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)
+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;
+ int ret, cg_type, idx;
struct dev_context *devc;
const struct scope_config *model;
struct scope_state *state;
ret = SR_OK;
break;
case SR_CONF_NUM_VDIV:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
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;
- }
+ if (cg_type != CG_ANALOG)
+ return SR_ERR_NA;
+ *data = g_variant_new_int32(model->num_ydivs);
+ ret = SR_OK;
break;
case SR_CONF_VDIV:
- ret = SR_ERR_NA;
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
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;
- }
+ if (cg_type != CG_ANALOG)
+ return SR_ERR_NA;
+ if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ *data = g_variant_new("(tt)",
+ dlm_vdivs[state->analog_states[idx].vdiv][0],
+ dlm_vdivs[state->analog_states[idx].vdiv][1]);
+ ret = SR_OK;
break;
case SR_CONF_TRIGGER_SOURCE:
*data = g_variant_new_string((*model->trigger_sources)[state->trigger_source]);
ret = SR_OK;
break;
case SR_CONF_COUPLING:
- ret = SR_ERR_NA;
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
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;
- }
+ if (cg_type != CG_ANALOG)
+ return SR_ERR_NA;
+ if ((idx = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ *data = g_variant_new_string((*model->coupling_options)[state->analog_states[idx].coupling]);
+ ret = SR_OK;
break;
case SR_CONF_SAMPLERATE:
*data = g_variant_new_uint64(state->sample_rate);
return ret;
}
-static GVariant *build_tuples(const uint64_t (*array)[][2], unsigned int n)
+static int config_set(uint32_t key, GVariant *data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
- 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;
+ int ret, cg_type, idx, 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;
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;
- }
+ if ((idx = std_str_idx(data, *model->trigger_sources, model->num_trigger_sources)) < 0)
+ return SR_ERR_ARG;
+ state->trigger_source = idx;
+ /* TODO: A and B trigger support possible? */
+ ret = dlm_trigger_source_set(sdi->conn, (*model->trigger_sources)[idx]);
break;
case SR_CONF_VDIV:
- if (cg_type == CG_NONE) {
- sr_err("No channel group specified.");
+ if (!cg)
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;
- }
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(dlm_vdivs))) < 0)
+ return SR_ERR_ARG;
+ if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ state->analog_states[j].vdiv = idx;
+ g_ascii_formatd(float_str, sizeof(float_str),
+ "%E", (float) dlm_vdivs[idx][0] / dlm_vdivs[idx][1]);
+ if (dlm_analog_chan_vdiv_set(sdi->conn, j + 1, float_str) != SR_OK ||
+ sr_scpi_get_opc(sdi->conn) != SR_OK)
+ return SR_ERR;
+ ret = SR_OK;
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;
- }
+ if ((idx = std_u64_tuple_idx(data, ARRAY_AND_SIZE(dlm_timebases))) < 0)
+ return SR_ERR_ARG;
+ state->timebase = idx;
+ g_ascii_formatd(float_str, sizeof(float_str),
+ "%E", (float) dlm_timebases[idx][0] / dlm_timebases[idx][1]);
+ ret = dlm_timebase_set(sdi->conn, float_str);
+ update_sample_rate = TRUE;
break;
case SR_CONF_HORIZ_TRIGGERPOS:
tmp_d = g_variant_get_double(data);
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'))
+ if ((idx = std_str_idx(data, ARRAY_AND_SIZE(dlm_trigger_slopes))) < 0)
return SR_ERR_ARG;
-
/* Note: See dlm_trigger_slopes[] in protocol.c. */
- state->trigger_slope = (tmp[0] == 'r') ?
- SLOPE_POSITIVE : SLOPE_NEGATIVE;
-
+ state->trigger_slope = idx;
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.");
+ if (!cg)
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;
- }
+ if ((idx = std_str_idx(data, *model->coupling_options, model->num_coupling_options)) < 0)
+ return SR_ERR_ARG;
+ if ((j = std_cg_idx(cg, devc->analog_groups, model->analog_channels)) < 0)
+ return SR_ERR_ARG;
+ state->analog_states[j].coupling = idx;
+ if (dlm_analog_chan_coupl_set(sdi->conn, j + 1, (*model->coupling_options)[idx]) != SR_OK ||
+ sr_scpi_get_opc(sdi->conn) != SR_OK)
+ return SR_ERR;
+ ret = SR_OK;
break;
default:
ret = SR_ERR_NA;
}
static int config_channel_set(const struct sr_dev_inst *sdi,
- struct sr_channel *ch, unsigned int changes)
+ struct sr_channel *ch, unsigned int changes)
{
/* Currently we only handle SR_CHANNEL_SET_ENABLED. */
if (changes != SR_CHANNEL_SET_ENABLED)
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)
+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 channel 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;
+ struct dev_context *devc;
+ const struct scope_config *model;
- devc = sdi->priv;
- model = devc->model_config;
+ devc = (sdi) ? sdi->priv : NULL;
+ model = (devc) ? devc->model_config : NULL;
- /*
- * If cg is NULL, only the SR_CONF_DEVICE_OPTIONS that are not
- * specific to a channel group must be returned.
- */
if (!cg) {
switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_TIMEBASE:
- *data = build_tuples(&dlm_timebases, ARRAY_SIZE(dlm_timebases));
+ *data = std_gvar_tuple_array(ARRAY_AND_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));
+ *data = g_variant_new_strv(*model->trigger_sources, model->num_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));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(dlm_trigger_slopes));
return SR_OK;
case SR_CONF_NUM_HDIV:
+ if (!model)
+ return SR_ERR_ARG;
*data = g_variant_new_uint32(model->num_xdivs);
return SR_OK;
default:
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));
- }
+ if (cg_type == CG_ANALOG)
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_analog));
+ else if (cg_type == CG_DIGITAL)
+ *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_digital));
+ else
+ *data = std_gvar_array_u32(NULL, 0);
break;
case SR_CONF_COUPLING:
- if (cg_type == CG_NONE)
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- *data = g_variant_new_strv(*model->coupling_options,
- g_strv_length((char **)*model->coupling_options));
+ *data = g_variant_new_strv(*model->coupling_options, model->num_coupling_options);
break;
case SR_CONF_VDIV:
- if (cg_type == CG_NONE)
+ if (!cg)
return SR_ERR_CHANNEL_GROUP;
- *data = build_tuples(&dlm_vdivs, ARRAY_SIZE(dlm_vdivs));
+ *data = std_gvar_tuple_array(ARRAY_AND_SIZE(dlm_vdivs));
break;
default:
return SR_ERR_NA;
struct dev_context *devc;
struct sr_scpi_dev_inst *scpi;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
scpi = sdi->conn;
devc = sdi->priv;
digital_added = FALSE;
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);
std_session_send_df_end(sdi);
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
devc->num_frames = 0;
* 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 *coupling_options[] = {
+ "AC", "DC", "DC50", "GND",
};
-static const char *dlm_2ch_trigger_sources[] = {
- "1",
- "2",
- "LINE",
- "EXT",
- NULL,
+static const char *trigger_sources_2ch[] = {
+ "1", "2", "LINE", "EXT",
};
/* 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,
+static const char *trigger_sources_4ch[] = {
+ "1", "2", "3", "4",
+ "LINE", "EXT", "BIT1",
+ "BIT2", "BIT3", "BIT4", "BIT5", "BIT6", "BIT7", "BIT8",
};
/* Note: Values must correlate to the trigger_slopes values. */
-const char *dlm_trigger_slopes[3] = {
- "r",
- "f",
- NULL,
+const char *dlm_trigger_slopes[2] = {
+ "r", "f",
};
const uint64_t dlm_timebases[36][2] = {
};
static const char *scope_analog_channel_names[] = {
- "1",
- "2",
- "3",
- "4",
+ "1", "2", "3", "4",
};
static const char *scope_digital_channel_names_8[] = {
- "D0",
- "D1",
- "D2",
- "D3",
- "D4",
- "D5",
- "D6",
- "D7",
+ "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",
+ "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[] = {
.analog_names = &scope_analog_channel_names,
.digital_names = &scope_digital_channel_names_8,
- .coupling_options = &dlm_coupling_options,
- .trigger_sources = &dlm_2ch_trigger_sources,
+ .coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+ .trigger_sources = &trigger_sources_2ch,
+ .num_trigger_sources = ARRAY_SIZE(trigger_sources_2ch),
.num_xdivs = 10,
.num_ydivs = 8,
.analog_names = &scope_analog_channel_names,
.digital_names = &scope_digital_channel_names_8,
- .coupling_options = &dlm_coupling_options,
- .trigger_sources = &dlm_4ch_trigger_sources,
+ .coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+ .trigger_sources = &trigger_sources_4ch,
+ .num_trigger_sources = ARRAY_SIZE(trigger_sources_4ch),
.num_xdivs = 10,
.num_ydivs = 8,
.analog_names = &scope_analog_channel_names,
.digital_names = NULL,
- .coupling_options = &dlm_coupling_options,
- .trigger_sources = &dlm_4ch_trigger_sources,
+ .coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+ .trigger_sources = &trigger_sources_4ch,
+ .num_trigger_sources = ARRAY_SIZE(trigger_sources_4ch),
.num_xdivs = 10,
.num_ydivs = 8,
.analog_names = &scope_analog_channel_names,
.digital_names = &scope_digital_channel_names_32,
- .coupling_options = &dlm_coupling_options,
- .trigger_sources = &dlm_4ch_trigger_sources,
+ .coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+ .trigger_sources = &trigger_sources_4ch,
+ .num_trigger_sources = ARRAY_SIZE(trigger_sources_4ch),
.num_xdivs = 10,
.num_ydivs = 8,
.analog_names = &scope_analog_channel_names,
.digital_names = &scope_digital_channel_names_32,
- .coupling_options = &dlm_coupling_options,
- .trigger_sources = &dlm_4ch_trigger_sources,
+ .coupling_options = &coupling_options,
+ .num_coupling_options = ARRAY_SIZE(coupling_options),
+
+ .trigger_sources = &trigger_sources_4ch,
+ .num_trigger_sources = ARRAY_SIZE(trigger_sources_4ch),
.num_xdivs = 10,
.num_ydivs = 8,
* @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 n, int *result)
{
unsigned int i;
*result = -1;
- for (i = 0; (*array)[i]; i++)
+ for (i = 0; i < n; i++)
if (!g_strcmp0(value, (*array)[i])) {
*result = i;
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),
+ if (array_float_get(response, ARRAY_AND_SIZE(dlm_vdivs),
&j) != SR_OK) {
g_free(response);
return SR_ERR;
}
if (array_option_get(response, config->coupling_options,
+ config->num_coupling_options,
&state->analog_states[i].coupling) != SR_OK) {
g_free(response);
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) {
+ if (array_float_get(response, ARRAY_AND_SIZE(dlm_timebases), &i) != SR_OK) {
g_free(response);
return SR_ERR;
}
}
if (array_option_get(response, config->trigger_sources,
- &state->trigger_source) != SR_OK) {
+ config->num_trigger_sources, &state->trigger_source) != SR_OK) {
g_free(response);
return SR_ERR;
}
*/
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->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);
+ if (!devc->analog_groups || !devc->digital_groups) {
+ g_free(devc->analog_groups);
+ g_free(devc->digital_groups);
+ return SR_ERR_MALLOC;
+ }
/* Add analog channels, each in its own group. */
for (i = 0; i < scope_models[model_index].analog_channels; 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);
+ devc->digital_groups[i]->name = g_strdup_printf("POD%d", i);
sdi->channel_groups = g_slist_append(sdi->channel_groups,
devc->digital_groups[i]);
}
* 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);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
} else
devc->current_channel = devc->current_channel->next;
#include "protocol_wrappers.h"
#define LOG_PREFIX "yokogawa-dlm"
+
#define MAX_INSTRUMENT_VERSIONS 8
#define RECEIVE_BUFFER_SIZE 4096
SLOPE_NEGATIVE
};
-extern const char *dlm_trigger_slopes[3];
+extern const char *dlm_trigger_slopes[2];
extern const uint64_t dlm_timebases[36][2];
extern const uint64_t dlm_vdivs[17][2];
uint32_t samples_per_frame;
};
-/** Private, per-device-instance driver context. */
struct dev_context {
const void *model_config;
void *model_state;
#include <config.h>
#include "protocol.h"
-#define VENDOR_NAME "ZEROPLUS"
#define USB_INTERFACE 0
#define USB_CONFIGURATION 1
#define NUM_TRIGGER_STAGES 4
*/
static const struct zp_model zeroplus_models[] = {
{0x0c12, 0x7002, "LAP-16128U", 16, 128, 200},
+ {0x0c12, 0x7007, "LAP-16032U", 16, 32, 200},
{0x0c12, 0x7009, "LAP-C(16064)", 16, 64, 100},
{0x0c12, 0x700a, "LAP-C(16128)", 16, 128, 200},
{0x0c12, 0x700b, "LAP-C(32128)", 32, 128, 200},
{0x0c12, 0x700d, "LAP-C(322000)", 32, 2048, 200},
{0x0c12, 0x700e, "LAP-C(16032)", 16, 32, 100},
{0x0c12, 0x7016, "LAP-C(162000)", 16, 2048, 200},
- {0x0c12, 0x7100, "AKIP-9101", 16, 256, 200},
+ {0x0c12, 0x7025, "LAP-C(16128+)", 16, 128, 200},
+ {0x0c12, 0x7064, "Logian-16L", 16, 128, 200},
+ {0x0c12, 0x7100, "AKIP-9101", 16, 256, 200},
ALL_ZERO
};
SR_MHZ(200),
};
-static int dev_close(struct sr_dev_inst *sdi);
-
SR_PRIV int zp_set_samplerate(struct dev_context *devc, uint64_t samplerate)
{
int i;
libusb_close(hdl);
- usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+ if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+ continue;
prof = NULL;
for (j = 0; j < zeroplus_models[j].vid; j++) {
prof = &zeroplus_models[j];
}
}
- /* Skip if the device was not found. */
+
if (!prof)
continue;
sr_info("Found ZEROPLUS %s.", prof->model_name);
- /* Register the device with libsigrok. */
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
- sdi->vendor = g_strdup(VENDOR_NAME);
+ sdi->vendor = g_strdup("ZEROPLUS");
sdi->model = g_strdup(prof->model_name);
sdi->serial_num = g_strdup(serial_num);
sdi->connection_id = g_strdup(connection_id);
- /* Allocate memory for our private driver context. */
devc = g_malloc0(sizeof(struct dev_context));
sdi->priv = devc;
devc->prof = prof;
devc->memory_size = MEMORY_SIZE_8K;
// 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++)
sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE,
channel_names[j]);
if (ret != SR_OK)
return ret;
- sdi->status = SR_ST_ACTIVE;
-
ret = libusb_set_configuration(usb->devhdl, USB_CONFIGURATION);
if (ret < 0) {
sr_err("Unable to set USB configuration %d: %s.",
usb = sdi->conn;
if (!usb->devhdl)
- return SR_ERR;
+ return SR_ERR_BUG;
sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.",
usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
libusb_reset_device(usb->devhdl);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
- sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct dev_context *devc;
- GVariant *range[2];
(void)cg;
*data = g_variant_new_uint64(devc->capture_ratio);
break;
case SR_CONF_VOLTAGE_THRESHOLD:
- 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);
+ *data = std_gvar_tuple_double(devc->cur_threshold, devc->cur_threshold);
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)
+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;
gdouble low, high;
(void)cg;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
return set_limit_samples(devc, g_variant_get_uint64(data));
case SR_CONF_CAPTURE_RATIO:
- return set_capture_ratio(devc, g_variant_get_uint64(data));
+ devc->capture_ratio = g_variant_get_uint64(data);
+ break;
case SR_CONF_VOLTAGE_THRESHOLD:
g_variant_get(data, "(dd)", &low, &high);
return set_voltage_threshold(devc, (low + high) / 2.0);
return SR_OK;
}
-static int config_list(uint32_t 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)
{
struct dev_context *devc;
- GVariant *gvar, *grange[2];
- GVariantBuilder gvb;
- double v;
- GVariant *range[2];
-
- (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;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
case SR_CONF_SAMPLERATE:
devc = sdi->priv;
- g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
- if (devc->prof->max_sampling_freq == 100) {
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- samplerates_100, ARRAY_SIZE(samplerates_100),
- sizeof(uint64_t));
- } else if (devc->prof->max_sampling_freq == 200) {
- gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
- samplerates_200, ARRAY_SIZE(samplerates_200),
- sizeof(uint64_t));
- } else {
+ if (devc->prof->max_sampling_freq == 100)
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates_100));
+ else if (devc->prof->max_sampling_freq == 200)
+ *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates_200));
+ else {
sr_err("Internal error: Unknown max. samplerate: %d.",
devc->prof->max_sampling_freq);
return SR_ERR_ARG;
}
- 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));
+ *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
break;
case SR_CONF_VOLTAGE_THRESHOLD:
- g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
- for (v = -6.0; v <= 6.0; v += 0.1) {
- range[0] = g_variant_new_double(v);
- range[1] = g_variant_new_double(v);
- gvar = g_variant_new_tuple(range, 2);
- g_variant_builder_add_value(&gvb, gvar);
- }
- *data = g_variant_builder_end(&gvb);
+ *data = std_gvar_min_max_step_thresholds(-6.0, 6.0, 0.1);
break;
case SR_CONF_LIMIT_SAMPLES:
if (!sdi)
return SR_ERR_ARG;
devc = sdi->priv;
- grange[0] = g_variant_new_uint64(0);
- grange[1] = g_variant_new_uint64(devc->max_sample_depth);
- *data = g_variant_new_tuple(grange, 2);
+ *data = std_gvar_tuple_u64(0, devc->max_sample_depth);
break;
default:
return SR_ERR_NA;
unsigned int discard;
int trigger_now;
- if (sdi->status != SR_ST_ACTIVE)
- return SR_ERR_DEV_CLOSED;
-
devc = sdi->priv;
if (analyzer_add_triggers(sdi) != SR_OK) {
unsigned int buf_offset;
res = analyzer_read_data(usb->devhdl, buf, PACKET_SIZE);
- sr_info("Tried to read %d bytes, actually read %d bytes.",
- PACKET_SIZE, res);
+ if (res != PACKET_SIZE)
+ sr_warn("Tried to read %d bytes, actually read %d.",
+ PACKET_SIZE, res);
if (discard >= PACKET_SIZE / 4) {
discard -= PACKET_SIZE / 4;
return SR_OK;
}
-/* TODO: This stops acquisition on ALL devices, ignoring dev_index. */
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
struct sr_usb_dev_inst *usb;
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = NULL,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
return SR_OK;
}
-SR_PRIV int set_capture_ratio(struct dev_context *devc, uint64_t ratio)
-{
- if (ratio > 100) {
- sr_err("Invalid capture ratio: %" PRIu64 ".", ratio);
- return SR_ERR_ARG;
- }
-
- devc->capture_ratio = ratio;
-
- sr_info("Setting capture ratio to %d%%.", devc->capture_ratio);
-
- return SR_OK;
-}
-
SR_PRIV int set_voltage_threshold(struct dev_context *devc, double thresh)
{
if (thresh > 6.0)
trigger_depth = devc->limit_samples;
if (devc->trigger)
- triggerbar = trigger_depth * devc->capture_ratio / 100;
+ triggerbar = (trigger_depth * devc->capture_ratio) / 100;
else
triggerbar = 0;
#include "libsigrok-internal.h"
#include "analyzer.h"
-#define LOG_PREFIX "zeroplus"
+#define LOG_PREFIX "zeroplus-logic-cube"
-/* Private, per-device-instance driver context. */
struct dev_context {
uint64_t cur_samplerate;
uint64_t max_samplerate;
//uint8_t trigger_value[NUM_TRIGGER_STAGES];
// uint8_t trigger_buffer[NUM_TRIGGER_STAGES];
int trigger;
- unsigned int capture_ratio;
+ uint64_t capture_ratio;
double cur_threshold;
const struct zp_model *prof;
};
SR_PRIV unsigned int get_memory_size(int type);
SR_PRIV int zp_set_samplerate(struct dev_context *devc, uint64_t samplerate);
SR_PRIV int set_limit_samples(struct dev_context *devc, uint64_t samples);
-SR_PRIV int set_capture_ratio(struct dev_context *devc, uint64_t ratio);
SR_PRIV int set_voltage_threshold(struct dev_context *devc, double thresh);
SR_PRIV void set_triggerbar(struct dev_context *devc);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Sven Bursch-Osewold <sb_git@bursch.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 scanopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_ELECTRONIC_LOAD,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+};
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ struct dev_context *devc;
+ GSList *l;
+ struct sr_dev_inst *sdi;
+ const char *conn, *serialcomm;
+ struct sr_config *src;
+ struct sr_serial_dev_inst *serial;
+ uint8_t reply[MSG_LEN];
+
+ conn = NULL;
+ 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 = "9600/8e1";
+
+ serial = sr_serial_dev_inst_new(conn, serialcomm);
+ if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+ return NULL;
+
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->status = SR_ST_INACTIVE;
+ sdi->vendor = g_strdup("ZKETECH");
+ sdi->model = g_strdup("EBD-USB");
+ sdi->inst_type = SR_INST_SERIAL;
+ sdi->conn = serial;
+
+ sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
+ sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I");
+
+ devc = g_malloc0(sizeof(struct dev_context));
+ g_mutex_init(&devc->rw_mutex);
+ devc->current_limit = 0;
+ devc->running = FALSE;
+ devc->load_activated = FALSE;
+ sr_sw_limits_init(&devc->limits);
+ sdi->priv = devc;
+
+ /* Starting device. */
+ ebd_init(serial, devc);
+ int ret = ebd_read_chars(serial, MSG_LEN, reply);
+ if (ret != MSG_LEN || reply[MSG_FRAME_BEGIN_POS] != MSG_FRAME_BEGIN \
+ || reply[MSG_FRAME_END_POS] != MSG_FRAME_END) {
+ sr_warn("Invalid message received!");
+ ret = SR_ERR;
+ }
+ ebd_stop(serial, devc);
+
+ serial_close(serial);
+
+ if (ret < 0)
+ return NULL;
+
+ return std_scan_complete(di, g_slist_append(NULL, sdi));
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ devc = (sdi) ? sdi->priv : NULL;
+ if (devc)
+ g_mutex_clear(&devc->rw_mutex);
+
+ return std_serial_dev_close(sdi);
+}
+
+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;
+ float fvalue;
+
+ (void)cg;
+
+ if (!sdi || !data)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ return sr_sw_limits_config_get(&devc->limits, key, data);
+ case SR_CONF_CURRENT_LIMIT:
+ ret = ebd_get_current_limit(sdi, &fvalue);
+ if (ret == SR_OK)
+ *data = g_variant_new_double(fvalue);
+ return ret;
+ default:
+ return SR_ERR_NA;
+ }
+}
+
+static int config_set(uint32_t key, GVariant *data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ double value;
+ struct dev_context *devc;
+
+ (void)data;
+ (void)cg;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_LIMIT_MSEC:
+ case SR_CONF_LIMIT_SAMPLES:
+ return sr_sw_limits_config_set(&devc->limits, key, data);
+ case SR_CONF_CURRENT_LIMIT:
+ value = g_variant_get_double(data);
+ if (value < 0.0 || value > 4.0)
+ return SR_ERR_ARG;
+ return ebd_set_current_limit(sdi, value);
+ default:
+ return SR_ERR_NA;
+ }
+}
+
+static int config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ case SR_CONF_CURRENT_LIMIT:
+ *data = std_gvar_min_max_step(0.0, 4.0, 0.01);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ devc = sdi->priv;
+ serial = sdi->conn;
+
+ sr_sw_limits_acquisition_start(&devc->limits);
+ std_session_send_df_header(sdi);
+
+ ebd_init(serial, devc);
+ if (!ebd_current_is0(devc))
+ ebd_loadstart(serial, devc);
+
+ serial_source_add(sdi->session, serial, G_IO_IN, 100,
+ ebd_receive_data, (void *)sdi);
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ ebd_loadstop(sdi->conn, sdi->priv);
+
+ return std_serial_dev_acquisition_stop(sdi);
+}
+
+SR_PRIV struct sr_dev_driver zketech_ebd_usb_driver_info = {
+ .name = "zketech-ebd-usb",
+ .longname = "ZKETECH EBD-USB",
+ .api_version = 1,
+ .init = std_init,
+ .cleanup = std_cleanup,
+ .scan = scan,
+ .dev_list = std_dev_list,
+ .dev_clear = std_dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = std_serial_dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(zketech_ebd_usb_driver_info);
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Sven Bursch-Osewold <sb_git@bursch.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"
+
+/* Log a byte-array as hex values. */
+static void log_buf(const char *message, uint8_t buf[], size_t count)
+{
+ char buffer[count * 2 + 1];
+
+ for (size_t j = 0; j < count; j++)
+ sprintf(&buffer[2 * j], "%02X", buf[j]);
+
+ buffer[count * 2] = 0;
+
+ sr_dbg("%s: %s [%zu bytes]", message, buffer, count);
+}
+
+/* Send a command to the device. */
+static int send_cmd(struct sr_serial_dev_inst *serial, uint8_t buf[], size_t count)
+{
+ int ret;
+
+ log_buf("Sending", buf, count);
+ ret = serial_write_blocking(serial, buf, count, 0);
+ if (ret < 0) {
+ sr_err("Error sending command: %d.", ret);
+ return ret;
+ }
+
+ return (ret == (int)count) ? SR_OK : SR_ERR;
+}
+
+/* Decode high byte and low byte into a float. */
+static float decode_value(uint8_t hi, uint8_t lo, float divisor)
+{
+ return ((float)hi * 240.0 + (float)lo) / divisor;
+}
+
+/* Encode a float into high byte and low byte. */
+static void encode_value(float current, uint8_t *hi, uint8_t *lo, float divisor)
+{
+ int value;
+
+ value = (int)(current * divisor);
+ sr_dbg("Value %d %d %d", value, value / 240, value % 240);
+ *hi = value / 240;
+ *lo = value % 240;
+}
+
+/* Send updated configuration values to the load. */
+static int send_cfg(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+ uint8_t send[] = { 0xfa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8 };
+
+ encode_value(devc->current_limit, &send[2], &send[3], 1000.0);
+
+ send[8] = send[1] ^ send[2] ^ send[3] ^ send[4] ^ send[5] ^ \
+ send[6] ^ send[7];
+
+ return send_cmd(serial, send, 10);
+}
+
+/* Send the init/connect sequence; drive starts sending voltage and current. */
+SR_PRIV int ebd_init(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+ uint8_t init[] = { 0xfa, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf8 };
+
+ (void)devc;
+
+ int ret = send_cmd(serial, init, 10);
+ if (ret == SR_OK)
+ devc->running = TRUE;
+
+ return ret;
+}
+
+/* Start the load functionality. */
+SR_PRIV int ebd_loadstart(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+ uint8_t start[] = { 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8 };
+ int ret;
+
+ ret = send_cmd(serial, start, 10);
+ sr_dbg("Current limit: %f.", devc->current_limit);
+ if (ebd_current_is0(devc))
+ return SR_OK;
+
+ ret = send_cfg(serial, devc);
+ if (ret == SR_OK) {
+ sr_dbg("Load activated.");
+ devc->load_activated = TRUE;
+ }
+
+ return ret;
+}
+
+/* Stop the load functionality. */
+SR_PRIV int ebd_loadstop(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+ int ret;
+ uint8_t stop[] = { 0xfa, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xF8 };
+
+ ret = send_cmd(serial, stop, 10);
+ if (ret == SR_OK)
+ devc->load_activated = FALSE;
+
+ return ret;
+}
+
+/* Stop the drive. */
+SR_PRIV int ebd_stop(struct sr_serial_dev_inst *serial, struct dev_context *devc)
+{
+ uint8_t stop[] = { 0xfa, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xF8 };
+ int ret;
+
+ (void) devc;
+
+ ret = send_cmd(serial, stop, 10);
+ if (ret == SR_OK) {
+ devc->load_activated = FALSE;
+ devc->running= FALSE;
+ }
+
+ return ret;
+}
+
+/** Read count bytes from the serial connection. */
+SR_PRIV int ebd_read_chars(struct sr_serial_dev_inst *serial, int count, uint8_t *buf)
+{
+ int ret, received, turns;
+
+ received = 0;
+ turns = 0;
+
+ do {
+ ret = serial_read_blocking(serial, buf + received,
+ count - received, serial_timeout(serial, count));
+ if (ret < 0) {
+ sr_err("Error %d reading %d bytes.", ret, count);
+ return ret;
+ }
+ received += ret;
+ turns++;
+ } while ((received < count) && (turns < 100));
+
+ log_buf("Received", buf, received);
+
+ return received;
+}
+
+SR_PRIV int ebd_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 analog;
+ struct sr_analog_encoding encoding;
+ struct sr_analog_meaning meaning;
+ struct sr_analog_spec spec;
+ float voltage, current, current_limit;
+ GSList *l;
+
+ (void)revents;
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return FALSE;
+
+ if (!(devc = sdi->priv))
+ return FALSE;
+
+ serial = sdi->conn;
+
+ uint8_t reply[MSG_LEN];
+ int ret = ebd_read_chars(serial, MSG_LEN, reply);
+
+ /* Tests for correct message. */
+ if (ret != MSG_LEN) {
+ sr_err("Message invalid [Len].");
+ return (ret < 0) ? ret : SR_ERR;
+ }
+
+ uint8_t xor = reply[1] ^ reply[2] ^ reply[3] ^ reply[4] ^ \
+ reply[5] ^ reply[6] ^ reply[7] ^ reply[8] ^ \
+ reply[9] ^ reply[10] ^ reply[11] ^ reply[12] ^ \
+ reply[13] ^ reply[14] ^ reply[15] ^ reply[16];
+
+ if (reply[MSG_FRAME_BEGIN_POS] != MSG_FRAME_BEGIN || \
+ reply[MSG_FRAME_END_POS] != MSG_FRAME_END || \
+ xor != reply[MSG_CHECKSUM_POS]) {
+ sr_err("Message invalid [XOR, BEGIN/END].");
+ return SR_ERR;
+ }
+
+ /* Calculate values. */
+ sr_dbg("V: %02X %02X A: %02X %02X -- Limit %02X %02X", reply[4],
+ reply[5], reply[2], reply[3], reply[10], reply[11]);
+
+ voltage = decode_value(reply[4], reply[5], 1000.0);
+ current = decode_value(reply[2], reply[3], 10000.0);
+ current_limit = decode_value(reply[10], reply[11], 1000.0);
+
+ sr_dbg("Voltage %f", voltage);
+ sr_dbg("Current %f", current);
+ sr_dbg("Current limit %f", current_limit);
+
+ /* Begin frame. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ packet.payload = NULL;
+ sr_session_send(sdi, &packet);
+
+ sr_analog_init(&analog, &encoding, &meaning, &spec, 4);
+
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ analog.num_samples = 1;
+
+ /* Voltage */
+ l = g_slist_copy(sdi->channels);
+ l = g_slist_remove_link(l, g_slist_nth(l, 1));
+ meaning.channels = l;
+ meaning.mq = SR_MQ_VOLTAGE;
+ meaning.mqflags = SR_MQFLAG_DC;
+ meaning.unit = SR_UNIT_VOLT;
+ analog.data = &voltage;
+ sr_session_send(sdi, &packet);
+ g_slist_free(l);
+
+ /* Current */
+ l = g_slist_copy(sdi->channels);
+ l = g_slist_remove_link(l, g_slist_nth(l, 0));
+ meaning.channels = l;
+ meaning.mq = SR_MQ_CURRENT;
+ meaning.mqflags = SR_MQFLAG_DC;
+ meaning.unit = SR_UNIT_AMPERE;
+ analog.data = ¤t;
+ sr_session_send(sdi, &packet);
+ g_slist_free(l);
+
+ /* End frame. */
+ packet.type = SR_DF_FRAME_END;
+ packet.payload = NULL;
+ sr_session_send(sdi, &packet);
+
+ sr_sw_limits_update_samples_read(&devc->limits, 1);
+ if (sr_sw_limits_check(&devc->limits))
+ sr_dev_acquisition_stop(sdi);
+
+ return TRUE;
+}
+
+SR_PRIV int ebd_get_current_limit(const struct sr_dev_inst *sdi, float *current)
+{
+ struct dev_context *devc;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ g_mutex_lock(&devc->rw_mutex);
+ *current = devc->current_limit;
+ g_mutex_unlock(&devc->rw_mutex);
+
+ return SR_OK;
+}
+
+SR_PRIV int ebd_set_current_limit(const struct sr_dev_inst *sdi, float current)
+{
+ struct dev_context *devc;
+ int ret;
+
+ if (!(devc = sdi->priv))
+ return SR_ERR;
+
+ g_mutex_lock(&devc->rw_mutex);
+ devc->current_limit = current;
+
+ if (!devc->running) {
+ sr_dbg("Setting current limit later.");
+ g_mutex_unlock(&devc->rw_mutex);
+ return SR_OK;
+ }
+
+ sr_dbg("Setting current limit to %fV.", current);
+
+ if (devc->load_activated) {
+ if (ebd_current_is0(devc)) {
+ /* Stop load. */
+ ret = ebd_loadstop(sdi->conn, devc);
+ } else {
+ /* Send new current. */
+ ret = send_cfg(sdi->conn, devc);
+ }
+ } else {
+ if (ebd_current_is0(devc)) {
+ /* Nothing to do. */
+ ret = SR_OK;
+ } else {
+ /* Start load. */
+ ret = ebd_loadstart(sdi->conn, devc);
+ }
+ }
+
+ g_mutex_unlock(&devc->rw_mutex);
+
+ return ret;
+}
+
+SR_PRIV gboolean ebd_current_is0(struct dev_context *devc)
+{
+ return devc->current_limit < 0.001;
+}
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Sven Bursch-Osewold <sb_git@bursch.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_ZKETECH_EBD_USB_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_ZKETECH_EBD_USB_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "zketech-ebd-usb"
+
+#define MSG_LEN 19
+#define MSG_CHECKSUM_POS 17
+#define MSG_FRAME_BEGIN 0xfa
+#define MSG_FRAME_BEGIN_POS 0
+#define MSG_FRAME_END 0xf8
+#define MSG_FRAME_END_POS 18
+
+struct dev_context {
+ struct sr_sw_limits limits;
+ GMutex rw_mutex;
+ float current_limit;
+ gboolean running;
+ gboolean load_activated;
+};
+
+/* Communication via serial. */
+SR_PRIV int ebd_read_chars(struct sr_serial_dev_inst *serial, int count, uint8_t *buf);
+
+/* Commands. */
+SR_PRIV int ebd_init(struct sr_serial_dev_inst *serial, struct dev_context *devc);
+SR_PRIV int ebd_loadstart(struct sr_serial_dev_inst *serial, struct dev_context *devc);
+SR_PRIV int ebd_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int ebd_stop(struct sr_serial_dev_inst *serial, struct dev_context *devc);
+SR_PRIV int ebd_loadstop(struct sr_serial_dev_inst *serial, struct dev_context *devc);
+
+/* Configuration. */
+SR_PRIV int ebd_get_current_limit(const struct sr_dev_inst *sdi, float *current);
+SR_PRIV int ebd_set_current_limit(const struct sr_dev_inst *sdi, float current);
+SR_PRIV gboolean ebd_current_is0(struct dev_context *devc);
+
+#endif
{SR_CONF_ELECTRONIC_LOAD, SR_T_STRING, NULL, "Electronic load", NULL},
{SR_CONF_SCALE, SR_T_STRING, NULL, "Scale", NULL},
{SR_CONF_SIGNAL_GENERATOR, SR_T_STRING, NULL, "Signal generator", NULL},
+ {SR_CONF_POWERMETER, SR_T_STRING, NULL, "Power meter", NULL},
/* Driver scan options */
{SR_CONF_CONN, SR_T_STRING, "conn",
"Under-voltage condition", NULL},
{SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE, SR_T_BOOL, "uvc_active",
"Under-voltage condition active", NULL},
+ {SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD, SR_T_FLOAT, "uvc_threshold",
+ "Under-voltage condition threshold", NULL},
{SR_CONF_TRIGGER_LEVEL, SR_T_FLOAT, "triggerlevel",
"Trigger level", NULL},
+ {SR_CONF_EXTERNAL_CLOCK_SOURCE, SR_T_STRING, "external_clock_source",
+ "External clock source", NULL},
/* Special stuff */
{SR_CONF_SESSIONFILE, SR_T_STRING, "sessionfile",
return SR_ERR_ARG;
}
+ /* No log message here, too verbose and not very useful. */
+
if ((ret = driver->init(driver, ctx)) < 0)
sr_err("Failed to initialize the driver: %d.", ret);
l = driver->scan(driver, options);
- sr_spew("Scan of '%s' found %d devices.", driver->name,
- g_slist_length(l));
+ sr_spew("Scan found %d devices (%s).", g_slist_length(l), driver->name);
return l;
}
if (!ctx)
return;
+ sr_dbg("Cleaning up all drivers.");
+
drivers = sr_driver_list(ctx);
for (i = 0; drivers[i]; i++) {
if (drivers[i]->cleanup)
*/
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);
+}
+
+/** @private */
+SR_PRIV int sr_dev_acquisition_start(struct sr_dev_inst *sdi)
+{
+ if (!sdi || !sdi->driver) {
+ sr_err("%s: Invalid arguments.", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("%s: Device instance not active, can't start.",
+ sdi->driver->name);
+ return SR_ERR_DEV_CLOSED;
+ }
+
+ sr_dbg("%s: Starting acquisition.", sdi->driver->name);
+
+ return sdi->driver->dev_acquisition_start(sdi);
+}
+/** @private */
+SR_PRIV int sr_dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ if (!sdi || !sdi->driver) {
+ sr_err("%s: Invalid arguments.", __func__);
+ return SR_ERR_ARG;
+ }
+
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("%s: Device instance not active, can't stop.",
+ sdi->driver->name);
+ return SR_ERR_DEV_CLOSED;
+ }
+
+ sr_dbg("%s: Stopping acquisition.", sdi->driver->name);
+
+ return sdi->driver->dev_acquisition_stop(sdi);
}
static void log_key(const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg, uint32_t key, int op, GVariant *data)
+ const struct sr_channel_group *cg, uint32_t key, unsigned int op,
+ GVariant *data)
{
const char *opstr;
const struct sr_key_info *srci;
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)
+ uint32_t key, unsigned int op, GVariant *data)
{
const struct sr_key_info *srci;
gsize num_opts, i;
const char *opstr;
if (sdi && cg)
- suffix = " for this device and channel group";
+ suffix = " for this device instance and channel group";
else if (sdi)
- suffix = " for this device";
+ suffix = " for this device instance";
else
suffix = "";
return SR_ERR_ARG;
}
break;
+ case SR_CONF_CAPTURE_RATIO:
+ /* Capture ratio must always be between 0 and 100. */
+ if (op != SR_CONF_SET || !data)
+ break;
+ if (g_variant_get_uint64(data) > 100) {
+ sr_err("Capture ratio must be 0..100.");
+ return SR_ERR_ARG;
+ }
+ break;
}
if (sr_config_list(driver, sdi, cg, SR_CONF_DEVICE_OPTIONS, &gvar_opts) != SR_OK) {
g_variant_ref_sink(*data);
}
+ if (ret == SR_ERR_CHANNEL_GROUP)
+ sr_err("%s: No channel group specified.",
+ (sdi) ? sdi->driver->name : "unknown");
+
return ret;
}
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)
+ else if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("%s: Device instance not active, can't set config.",
+ sdi->driver->name);
+ ret = SR_ERR_DEV_CLOSED;
+ } 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);
g_variant_unref(data);
+ if (ret == SR_ERR_CHANNEL_GROUP)
+ sr_err("%s: No channel group specified.",
+ (sdi) ? sdi->driver->name : "unknown");
+
return ret;
}
ret = SR_ERR;
else if (!sdi->driver->config_commit)
ret = SR_OK;
- else
+ else if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("%s: Device instance not active, can't commit config.",
+ sdi->driver->name);
+ ret = SR_ERR_DEV_CLOSED;
+ } 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. Must not be NULL.
- * @param[in] sdi (optional) If the key is specific to a device, this must
- * contain a pointer to the struct sr_dev_inst to be checked.
+ * @param[in] sdi (optional) If the key is specific to a device instance, this
+ * must contain a pointer to the struct sr_dev_inst to be checked.
* Otherwise it must be NULL. If sdi is != NULL, sdi->priv must
* also be != NULL.
- * @param[in] cg The channel group on the device for which to list the
- * values, or NULL.
+ * @param[in] cg The channel group on the device instance for which to list
+ * the values, or NULL. If this device instance doesn't
+ * have channel groups, this must not be != NULL.
+ * If cg is NULL, this function will return the "common" device
+ * instance options that are channel-group independent. Otherwise
+ * it will return the channel-group specific options.
* @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.
+ * 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.
if (!driver || !data)
return SR_ERR;
- else if (!driver->config_list)
+
+ if (!driver->config_list)
return SR_ERR_ARG;
- else if (key != SR_CONF_SCAN_OPTIONS && key != SR_CONF_DEVICE_OPTIONS) {
+
+ 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 (sdi && !sdi->priv) {
sr_err("Can't list config (sdi != NULL, sdi->priv == NULL).");
return SR_ERR;
}
+
+ if (key != SR_CONF_SCAN_OPTIONS && key != SR_CONF_DEVICE_OPTIONS && !sdi) {
+ sr_err("Config keys other than SR_CONF_SCAN_OPTIONS and "
+ "SR_CONF_DEVICE_OPTIONS always need an sdi.");
+ return SR_ERR_ARG;
+ }
+
+ if (cg && sdi && !sdi->channel_groups) {
+ sr_err("Can't list config for channel group, there are none.");
+ return SR_ERR_ARG;
+ }
+
+ if (cg && sdi && !g_slist_find(sdi->channel_groups, cg)) {
+ sr_err("If a channel group is specified, it must be a valid one.");
+ return SR_ERR_ARG;
+ }
+
+ if (cg && !sdi) {
+ sr_err("Need sdi when a channel group is specified.");
+ 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);
}
+ if (ret == SR_ERR_CHANNEL_GROUP)
+ sr_err("%s: No channel group specified.",
+ (sdi) ? sdi->driver->name : "unknown");
+
return ret;
}
#define LOG_PREFIX "input/binary"
-#define MAX_CHUNK_SIZE 4096
+#define CHUNK_SIZE (4 * 1024 * 1024)
#define DEFAULT_NUM_CHANNELS 8
#define DEFAULT_SAMPLERATE 0
struct context {
gboolean started;
uint64_t samplerate;
+ uint16_t unitsize;
};
static int init(struct sr_input *in, GHashTable *options)
inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
for (i = 0; i < num_channels; i++) {
- snprintf(name, 16, "%d", i);
+ snprintf(name, sizeof(name), "%d", i);
sr_channel_new(in->sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
}
+ inc->unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+
return SR_OK;
}
packet.type = SR_DF_LOGIC;
packet.payload = &logic;
- logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+ logic.unitsize = inc->unitsize;
/* 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);
+ chunk = MIN(CHUNK_SIZE, chunk_size - i);
logic.length = chunk;
sr_session_send(in->sdi, &packet);
}
}
static struct sr_option options[] = {
- { "numchannels", "Number of channels", "Number of channels", NULL, NULL },
- { "samplerate", "Sample rate", "Sample rate", NULL, NULL },
+ { "numchannels", "Number of logic channels", "The number of (logic) channels in the data", NULL, NULL },
+ { "samplerate", "Sample rate (Hz)", "The sample rate of the (logic) data in Hz", NULL, NULL },
ALL_ZERO
};
SR_PRIV struct sr_input_module input_binary = {
.id = "binary",
.name = "Binary",
- .desc = "Raw binary",
+ .desc = "Raw binary logic data",
.exts = NULL,
.options = get_options,
.init = init,
#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)
+#define CHUNK_SIZE (4 * 1024 * 1024)
+
+/*
+ * File layout:
+ * - Fixed size 8MiB data part at offset 0.
+ * - Either one byte per sample for LA8.
+ * - Or two bytes per sample for LA16, in little endian format.
+ * - Five byte "header" at offset 8MiB.
+ * - One "clock divider" byte. The byte value is the divider factor
+ * minus 1. Value 0xff is invalid. Base clock is 100MHz for LA8, or
+ * 200MHz for LA16.
+ * - Four bytes for the trigger position. This 32bit value is the
+ * sample number in little endian format, or 0 when unused.
+ */
+#define CHRONOVU_LA8_DATASIZE (8 * 1024 * 1024)
+#define CHRONOVU_LA8_HDRSIZE (sizeof(uint8_t) + sizeof(uint32_t))
+#define CHRONOVU_LA8_FILESIZE (CHRONOVU_LA8_DATASIZE + CHRONOVU_LA8_HDRSIZE)
+
+/*
+ * Implementation note:
+ *
+ * The .format_match() routine only checks the file size, but none of
+ * the header fields. Only little would be gained (only clock divider
+ * 0xff could get tested), but complexity would increase dramatically.
+ * Also the .format_match() routine is unlikely to receive large enough
+ * a buffer to include the header. Neither is the filename available to
+ * the .format_match() routine.
+ *
+ * There is no way to programmatically tell whether the file was created
+ * by LA8 or LA16 software, i.e. with 8 or 16 logic channels. If the
+ * filename was available, one might guess based on the file extension,
+ * but still would require user specs if neither of the known extensions
+ * were used or the input is fed from a pipe.
+ *
+ * The current input module implementation assumes that users specify
+ * the (channel count and) sample rate. Input data gets processed and
+ * passed along to the session bus, before the file "header" is seen.
+ * A future implementation could move channel creation from init() to
+ * receive() or end() (actually: a common routine called from those two
+ * routines), and could defer sample processing and feeding the session
+ * until the header was seen, including deferred samplerate calculation
+ * after having seen the header. But again this improvement depends on
+ * the availability of either the filename or the device type. Also note
+ * that applications then had to keep sending data to the input module's
+ * receive() routine until sufficient amounts of input data were seen
+ * including the header (see bug #1017).
+ */
struct context {
gboolean started;
uint64_t samplerate;
+ uint64_t samples_remain;
};
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
{
- int size;
-
- size = GPOINTER_TO_INT(g_hash_table_lookup(metadata,
+ uint64_t size;
+
+ /*
+ * In the absence of a reliable condition like magic strings,
+ * we can only guess based on the file size. Since this is
+ * rather weak a condition, signal "little confidence" and
+ * optionally give precedence to better matches.
+ */
+ size = GPOINTER_TO_SIZE(g_hash_table_lookup(metadata,
GINT_TO_POINTER(SR_INPUT_META_FILESIZE)));
- if (size == CHRONOVU_LA8_FILESIZE)
- return SR_OK;
+ if (size != CHRONOVU_LA8_FILESIZE)
+ return SR_ERR;
+ *confidence = 100;
- return SR_ERR;
+ return SR_OK;
}
static int init(struct sr_input *in, GHashTable *options)
inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
for (i = 0; i < num_channels; i++) {
- snprintf(name, 16, "%d", i);
+ snprintf(name, sizeof(name), "%d", i);
sr_channel_new(in->sdi, i, SR_CHANNEL_LOGIC, TRUE, name);
}
struct sr_config *src;
struct context *inc;
gsize chunk_size, i;
- int chunk;
+ gsize chunk;
+ uint16_t unitsize;
inc = in->priv;
+ unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+
if (!inc->started) {
std_session_send_df_header(in->sdi);
sr_config_free(src);
}
+ inc->samples_remain = CHRONOVU_LA8_DATASIZE;
+ inc->samples_remain /= unitsize;
+
inc->started = TRUE;
}
packet.type = SR_DF_LOGIC;
packet.payload = &logic;
- logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
+ logic.unitsize = unitsize;
- /* Cut off at multiple of unitsize. */
+ /* Cut off at multiple of unitsize. Avoid sending the "header". */
chunk_size = in->buf->len / logic.unitsize * logic.unitsize;
+ chunk_size = MIN(chunk_size, inc->samples_remain * 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);
+ chunk = MIN(CHUNK_SIZE, chunk_size - i);
+ if (chunk) {
+ logic.length = chunk;
+ sr_session_send(in->sdi, &packet);
+ inc->samples_remain -= chunk / unitsize;
+ }
}
g_string_erase(in->buf, 0, chunk_size);
}
static struct sr_option options[] = {
- { "numchannels", "Number of channels", "Number of channels", NULL, NULL },
- { "samplerate", "Sample rate", "Sample rate", NULL, NULL },
+ { "numchannels", "Number of logic channels", "The number of (logic) channels in the data", NULL, NULL },
+ { "samplerate", "Sample rate (Hz)", "The sample rate of the (logic) data in Hz", NULL, NULL },
ALL_ZERO
};
SR_PRIV struct sr_input_module input_chronovu_la8 = {
.id = "chronovu-la8",
- .name = "Chronovu-LA8",
- .desc = "ChronoVu LA8",
- .exts = (const char*[]){"kdt", NULL},
+ .name = "ChronoVu LA8/LA16",
+ .desc = "ChronoVu LA8/LA16 native file format data",
+ .exts = (const char*[]){"kdt", "kd1", NULL},
.metadata = { SR_INPUT_META_FILESIZE | SR_INPUT_META_REQUIRED },
.options = get_options,
.format_match = format_match,
#define LOG_PREFIX "input/csv"
-#define DATAFEED_MAX_SAMPLES (128 * 1024)
+#define CHUNK_SIZE (4 * 1024 * 1024)
/*
* The CSV input module has the following options:
* to a location within that large buffer.
*/
inc->sample_unit_size = (inc->num_channels + 7) / 8;
- inc->datafeed_buf_size = DATAFEED_MAX_SAMPLES;
+ inc->datafeed_buf_size = CHUNK_SIZE;
inc->datafeed_buf_size *= inc->sample_unit_size;
inc->datafeed_buffer = g_malloc(inc->datafeed_buf_size);
inc->datafeed_buf_fill = 0;
}
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 },
+ { "single-column", "Single column", "Enable single-column mode, using the specified column (>= 1); 0: multi-col. mode", NULL, NULL },
+ { "numchannels", "Number of logic channels", "The number of (logic) channels (single-col. mode: number of bits beginning at 'first channel', LSB-first)", NULL, NULL },
+ { "delimiter", "Column delimiter", "The column delimiter (>= 1 characters)", NULL, NULL },
+ { "format", "Data format (single-col. mode)", "The numeric format of the data (single-col. mode): bin, hex, oct", NULL, NULL },
+ { "comment", "Comment character(s)", "The comment prefix character(s)", NULL, NULL },
+ { "samplerate", "Samplerate (Hz)", "The sample rate (used during capture) in Hz", NULL, NULL },
+ { "first-channel", "First channel", "The column number of the first channel (multi-col. mode); bit position for the first channel (single-col. mode)", NULL, NULL },
+ { "header", "Interpret first line as header (multi-col. mode)", "Treat the first line as header with channel names (multi-col. mode)", NULL, NULL },
+ { "startline", "Start line", "The line number at which to start processing samples (>= 1)", NULL, NULL },
ALL_ZERO
};
static const struct sr_option *get_options(void)
{
+ GSList *l;
+
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"));
+ l = NULL;
+ l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("bin")));
+ l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("hex")));
+ l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("oct")));
+ options[3].values = l;
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));
#define LOG_PREFIX "input"
/** @endcond */
+#define CHUNK_SIZE (4 * 1024 * 1024)
+
/**
* @file
*
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;
+extern SR_PRIV struct sr_input_module input_logicport;
+extern SR_PRIV struct sr_input_module input_null;
/* @endcond */
static const struct sr_input_module *input_module_list[] = {
&input_vcd,
&input_wav,
&input_raw_analog,
+ &input_logicport,
+ &input_null,
NULL,
};
* 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.
+ * the input modules to find a match. This is format-dependent. When
+ * magic strings get checked, 128 bytes normally could be enough. Note
+ * that some formats try to parse larger header sections, and benefit
+ * from seeing a larger scope.
*
* If an input module is found, an instance is created into *in.
- * Otherwise, *in contains NULL.
+ * Otherwise, *in contains NULL. When multiple input moduless claim
+ * support for the format, the one with highest confidence takes
+ * precedence. Applications will see at most one input module spec.
*
* 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.
*/
SR_API int sr_input_scan_buffer(GString *buf, const struct sr_input **in)
{
- const struct sr_input_module *imod;
+ const struct sr_input_module *imod, *best_imod;
GHashTable *meta;
unsigned int m, i;
+ unsigned int conf, best_conf;
int ret;
uint8_t mitem, avail_metadata[8];
avail_metadata[1] = 0;
*in = NULL;
- ret = SR_ERR;
+ best_imod = NULL;
+ best_conf = ~0;
for (i = 0; input_module_list[i]; i++) {
imod = input_module_list[i];
if (!imod->metadata[0]) {
continue;
}
sr_spew("Trying module %s.", imod->id);
- ret = imod->format_match(meta);
+ ret = imod->format_match(meta, &conf);
g_hash_table_destroy(meta);
if (ret == SR_ERR_DATA) {
/* Module recognized this buffer, but cannot handle it. */
- break;
+ continue;
} else if (ret == SR_ERR) {
/* Module didn't recognize this buffer. */
continue;
} else if (ret != SR_OK) {
/* Can be SR_ERR_NA. */
- return ret;
+ continue;
}
/* Found a matching module. */
- sr_spew("Module %s matched.", imod->id);
- *in = sr_input_new(imod, NULL);
+ sr_spew("Module %s matched, confidence %u.", imod->id, conf);
+ if (conf >= best_conf)
+ continue;
+ best_imod = imod;
+ best_conf = conf;
+ }
+
+ if (best_imod) {
+ *in = sr_input_new(best_imod, NULL);
g_string_insert_len((*in)->buf, 0, buf->str, buf->len);
- break;
+ return SR_OK;
}
- return ret;
+ return SR_ERR;
}
/**
* 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.
+ * Otherwise, *in contains NULL. When multiple input moduless claim
+ * support for the format, the one with highest confidence takes
+ * precedence. Applications will see at most one input module spec.
*
*/
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;
+ const struct sr_input_module *imod, *best_imod;
GHashTable *meta;
GString *header;
size_t count;
unsigned int midx, i;
+ unsigned int conf, best_conf;
int ret;
uint8_t avail_metadata[8];
fclose(stream);
return SR_ERR;
}
- /* This actually allocates 256 bytes to allow for NUL termination. */
- header = g_string_sized_new(255);
+ header = g_string_sized_new(CHUNK_SIZE);
count = fread(header->str, 1, header->allocated_len - 1, stream);
-
- if (count != header->allocated_len - 1 && ferror(stream)) {
+ if (count < 1 || ferror(stream)) {
sr_err("Failed to read %s: %s", filename, g_strerror(errno));
fclose(stream);
g_string_free(header, TRUE);
avail_metadata[midx] = 0;
/* TODO: MIME type */
- ret = SR_ERR;
-
+ best_imod = NULL;
+ best_conf = ~0;
for (i = 0; input_module_list[i]; i++) {
imod = input_module_list[i];
if (!imod->metadata[0]) {
sr_dbg("Trying module %s.", imod->id);
- ret = imod->format_match(meta);
+ ret = imod->format_match(meta, &conf);
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;
+ continue;
}
/* Found a matching module. */
- sr_dbg("Module %s matched.", imod->id);
-
- *in = sr_input_new(imod, NULL);
- break;
+ sr_dbg("Module %s matched, confidence %u.", imod->id, conf);
+ if (conf >= best_conf)
+ continue;
+ best_imod = imod;
+ best_conf = conf;
}
g_hash_table_destroy(meta);
g_string_free(header, TRUE);
- return ret;
+ if (best_imod) {
+ *in = sr_input_new(best_imod, NULL);
+ return SR_OK;
+ }
+
+ return SR_ERR;
+}
+
+/**
+ * Return the input instance's module "class". This can be used to find out
+ * which input module handles a specific input file. This is especially
+ * useful when an application did not create the input stream by specifying
+ * an input module, but instead some shortcut or convenience wrapper did.
+ *
+ * @since 0.6.0
+ */
+SR_API const struct sr_input_module *sr_input_module_get(const struct sr_input *in)
+{
+ if (!in)
+ return NULL;
+
+ return in->module;
}
/**
*/
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);
+ size_t len;
+
+ len = buf ? buf->len : 0;
+ sr_spew("Sending %zu bytes to %s module.", len, in->module->id);
return in->module->receive((struct sr_input *)in, buf);
}
*
* @since 0.5.0
*/
-SR_API int sr_input_reset(const struct sr_input *in)
+SR_API int sr_input_reset(const struct sr_input *in_ro)
{
- if (!in->module->reset) {
+ struct sr_input *in;
+ int rc;
+
+ in = (struct sr_input *)in_ro; /* "un-const" */
+ if (!in || !in->module)
+ return SR_ERR_ARG;
+
+ /*
+ * Run the optional input module's .reset() method. This shall
+ * take care of the context (kept in the 'inc' variable).
+ */
+ if (in->module->reset) {
+ sr_spew("Resetting %s module.", in->module->id);
+ rc = in->module->reset(in);
+ } else {
sr_spew("Tried to reset %s module but no reset handler found.",
in->module->id);
- return SR_OK;
+ rc = SR_OK;
}
- sr_spew("Resetting %s module.", in->module->id);
- return in->module->reset((struct sr_input *)in);
+ /*
+ * Handle input module status (kept in the 'in' variable) here
+ * in common logic. This agrees with how input module's receive()
+ * and end() routines "amend but never seed" the 'in' information.
+ *
+ * Void potentially accumulated receive() buffer content, and
+ * clear the sdi_ready flag. This makes sure that subsequent
+ * processing will scan the header again before sample data gets
+ * interpreted, and stale content from previous calls won't affect
+ * the result.
+ *
+ * This common logic does not harm when the input module implements
+ * .reset() and contains identical assignments. In the absence of
+ * an individual .reset() method, simple input modules can completely
+ * rely on common code and keep working across resets.
+ */
+ if (in->buf)
+ g_string_truncate(in->buf, 0);
+ in->sdi_ready = FALSE;
+
+ return rc;
}
/**
if (!in)
return;
+ /*
+ * Run the input module's optional .cleanup() routine. This
+ * takes care of the context (kept in the 'inc' variable).
+ */
if (in->module->cleanup)
in->module->cleanup((struct sr_input *)in);
+
+ /*
+ * Common code releases the input module's state (kept in the
+ * 'in' variable). Release the device instance, the receive()
+ * buffer, the shallow 'in->priv' block which is 'inc' (after
+ * .cleanup() released potentially nested resources under 'inc').
+ */
sr_dev_inst_free(in->sdi);
if (in->buf->len > 64) {
/* That seems more than just some sub-unitsize leftover... */
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/>.
+ */
+
+/*
+ * See the LA1034 vendor's http://www.pctestinstruments.com/ website.
+ *
+ * The hardware comes with (Windows only) software which uses the .lpf
+ * ("LogicPort File") filename extension for project files, which hold
+ * both the configuration as well as sample data (up to 2K samples). In
+ * the absence of an attached logic analyzer, the software provides a
+ * demo mode which generates random input signals. The software installs
+ * example project files (with samples), too.
+ *
+ * The file format is "mostly text", is line oriented, though it uses
+ * funny DC1 separator characters as well as line continuation by means
+ * of a combination of DC1 and slashes. Fortunately the last text line
+ * is terminated by means of CRLF.
+ *
+ * The software is rather complex and has features which don't easily
+ * translate to sigrok semantics (like one signal being a member of
+ * multiple groups, display format specs for groups' values).
+ *
+ * This input module implementation supports the following features:
+ * - input format auto detection
+ * - sample period to sample rate conversion
+ * - wire names, acquisition filters ("enabled") and inversion flags
+ * - decompression (repetition counters for sample data)
+ * - strict '0' and '1' levels (as well as ignoring 'U' values)
+ * - signal names (user assigned names, "aliases" for "wires")
+ * - signal groups (no support for multiple assignments, no support for
+ * display format specs)
+ * - "logic" channels (mere bits, no support for analog channels, also
+ * nothing analog "gets derived from" any signal groups) -- libsigrok
+ * using applications might provide such a feature if they want to
+ */
+
+#include <config.h>
+#include <ctype.h>
+#include <glib.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+/* TODO: Move these helpers to some library API routine group. */
+struct sr_channel_group *sr_channel_group_new(const char *name, void *priv);
+void sr_channel_group_free(struct sr_channel_group *cg);
+
+#define LOG_PREFIX "input/logicport"
+
+#define MAX_CHANNELS 34
+#define CHUNK_SIZE (4 * 1024 * 1024)
+
+#define CRLF "\r\n"
+#define DC1_CHR '\x11'
+#define DC1_STR "\x11"
+#define CONT_OPEN "/" DC1_STR
+#define CONT_CLOSE DC1_STR "/"
+
+/*
+ * This is some heuristics (read: a HACK). The current implementation
+ * neither processes nor displays the user's notes, but takes their
+ * presence as a hint that all relevant input was seen, and sample data
+ * can get forwarded to the session bus.
+ */
+#define LAST_KEYWORD "NotesString"
+
+/*
+ * The vendor software supports signal groups, and a single signal can
+ * be a member in multiple groups at the same time. The sigrok project
+ * does not support that configuration. Let's ignore the "All Signals"
+ * group by default, thus reducing the probability of a conflict.
+ */
+#define SKIP_SIGNAL_GROUP "All Signals"
+
+struct signal_group_desc {
+ char *name;
+ uint64_t mask;
+};
+
+struct context {
+ gboolean got_header;
+ gboolean ch_feed_prep;
+ gboolean header_sent;
+ gboolean rate_sent;
+ char *sw_version;
+ size_t sw_build;
+ GString *cont_buff;
+ size_t channel_count;
+ size_t sample_lines_total;
+ size_t sample_lines_read;
+ size_t sample_lines_fed;
+ uint64_t samples_got_uncomp;
+ enum {
+ SAMPLEDATA_NONE,
+ SAMPLEDATA_OPEN_BRACE,
+ SAMPLEDATA_WIRES_COUNT,
+ SAMPLEDATA_DATA_LINES,
+ SAMPLEDATA_CLOSE_BRACE,
+ } in_sample_data;
+ struct sample_data_entry {
+ uint64_t bits;
+ size_t repeat;
+ } *sample_data_queue;
+ uint64_t sample_rate;
+ uint64_t wires_all_mask;
+ uint64_t wires_enabled;
+ uint64_t wires_inverted;
+ uint64_t wires_undefined;
+ char *wire_names[MAX_CHANNELS];
+ char *signal_names[MAX_CHANNELS];
+ uint64_t wires_grouped;
+ GSList *signal_groups;
+ GSList *channels;
+ size_t unitsize;
+ size_t samples_per_chunk;
+ size_t samples_in_buffer;
+ uint8_t *feed_buffer;
+};
+
+static struct signal_group_desc *alloc_signal_group(const char *name)
+{
+ struct signal_group_desc *desc;
+
+ desc = g_malloc0(sizeof(*desc));
+ if (name)
+ desc->name = g_strdup(name);
+
+ return desc;
+}
+
+static void free_signal_group(struct signal_group_desc *desc)
+{
+ if (!desc)
+ return;
+ g_free(desc->name);
+ g_free(desc);
+}
+
+struct sr_channel_group *sr_channel_group_new(const char *name, void *priv)
+{
+ struct sr_channel_group *cg;
+
+ cg = g_malloc0(sizeof(*cg));
+ if (name && *name)
+ cg->name = g_strdup(name);
+ cg->priv = priv;
+
+ return cg;
+}
+
+void sr_channel_group_free(struct sr_channel_group *cg)
+{
+ if (!cg)
+ return;
+ g_free(cg->name);
+ g_slist_free(cg->channels);
+}
+
+/* Wrapper for GDestroyNotify compatibility. */
+static void sg_free(void *p)
+{
+ return free_signal_group(p);
+}
+
+static int check_vers_line(char *line, int need_key,
+ gchar **version, gchar **build)
+{
+ static const char *keyword = "Version";
+ static const char *caution = " CAUTION: Do not change the contents of this file.";
+ char *read_ptr;
+ const char *prev_ptr;
+
+ read_ptr = line;
+ if (version)
+ *version = NULL;
+ if (build)
+ *build = NULL;
+
+ /* Expect the 'Version' literal, followed by a DC1 separator. */
+ if (need_key) {
+ if (strncmp(read_ptr, keyword, strlen(keyword)) != 0)
+ return SR_ERR_DATA;
+ read_ptr += strlen(keyword);
+ if (*read_ptr != DC1_CHR)
+ return SR_ERR_DATA;
+ read_ptr++;
+ }
+
+ /* Expect some "\d+\.\d+" style version string and DC1. */
+ if (!*read_ptr)
+ return SR_ERR_DATA;
+ if (version)
+ *version = read_ptr;
+ prev_ptr = read_ptr;
+ read_ptr += strspn(read_ptr, "0123456789.");
+ if (read_ptr == prev_ptr)
+ return SR_ERR_DATA;
+ if (*read_ptr != DC1_CHR)
+ return SR_ERR_DATA;
+ *read_ptr++ = '\0';
+
+ /* Expect some "\d+" style build number and DC1. */
+ if (!*read_ptr)
+ return SR_ERR_DATA;
+ if (build)
+ *build = read_ptr;
+ prev_ptr = read_ptr;
+ read_ptr += strspn(read_ptr, "0123456789");
+ if (read_ptr == prev_ptr)
+ return SR_ERR_DATA;
+ if (*read_ptr != DC1_CHR)
+ return SR_ERR_DATA;
+ *read_ptr++ = '\0';
+
+ /* Expect the 'CAUTION...' text (weak test, only part of the text). */
+ if (strncmp(read_ptr, caution, strlen(caution)) != 0)
+ return SR_ERR_DATA;
+ read_ptr += strlen(caution);
+
+ /* No check for CRLF, due to the weak CAUTION test. */
+ return SR_OK;
+}
+
+static int process_wire_names(struct context *inc, char **names)
+{
+ size_t count, idx;
+
+ /*
+ * The 'names' array contains the *wire* names, plus a 'Count'
+ * label for the last column.
+ */
+ count = g_strv_length(names);
+ if (count != inc->channel_count + 1)
+ return SR_ERR_DATA;
+ if (strcmp(names[inc->channel_count], "Count") != 0)
+ return SR_ERR_DATA;
+
+ for (idx = 0; idx < inc->channel_count; idx++)
+ inc->wire_names[idx] = g_strdup(names[idx]);
+
+ return SR_OK;
+}
+
+static int process_signal_names(struct context *inc, char **names)
+{
+ size_t count, idx;
+
+ /*
+ * The 'names' array contains the *signal* names (and no other
+ * entries, unlike the *wire* names).
+ */
+ count = g_strv_length(names);
+ if (count != inc->channel_count)
+ return SR_ERR_DATA;
+
+ for (idx = 0; idx < inc->channel_count; idx++)
+ inc->signal_names[idx] = g_strdup(names[idx]);
+
+ return SR_OK;
+}
+
+static int process_signal_group(struct context *inc, char **args)
+{
+ char *name, *wires;
+ struct signal_group_desc *desc;
+ uint64_t bit_mask;
+ char *p, *endp;
+ size_t idx;
+
+ /*
+ * List of arguments that we receive:
+ * - [0] group name
+ * - [1] - [5] uncertain meaning, four integers and one boolean
+ * - [6] comma separated list of wire indices (zero based)
+ * - [7] - [9] uncertain meaning, a boolean, two integers
+ * - [10] - [35] uncertain meaning, 26 empty columns
+ */
+
+ /* Check for the minimum amount of input data. */
+ if (!args)
+ return SR_ERR_DATA;
+ if (g_strv_length(args) < 7)
+ return SR_ERR_DATA;
+ name = args[0];
+ wires = args[6];
+
+ /* Accept empty names and empty signal lists. Silently ignore. */
+ if (!name || !*name)
+ return SR_OK;
+ if (!wires || !*wires)
+ return SR_OK;
+ /*
+ * TODO: Introduce a user configurable "ignore" option? Skip the
+ * "All Signals" group by default, and in addition whatever
+ * the user specified?
+ */
+ if (strcmp(name, SKIP_SIGNAL_GROUP) == 0) {
+ sr_info("Skipping signal group '%s'", name);
+ return SR_OK;
+ }
+
+ /*
+ * Create the descriptor here to store the member list to. We
+ * cannot access signal names and sigrok channels yet, they
+ * only become avilable at a later point in time.
+ */
+ desc = alloc_signal_group(name);
+ if (!desc)
+ return SR_ERR_MALLOC;
+ inc->signal_groups = g_slist_append(inc->signal_groups, desc);
+
+ /* Determine the bit mask of the group's signals' indices. */
+ bit_mask = 0;
+ p = wires;
+ while (p && *p) {
+ endp = NULL;
+ idx = strtoul(p, &endp, 0);
+ if (!endp || endp == p)
+ return SR_ERR_DATA;
+ if (*endp && *endp != ',')
+ return SR_ERR_DATA;
+ p = endp;
+ if (*p == ',')
+ p++;
+ if (idx >= MAX_CHANNELS)
+ return SR_ERR_DATA;
+ bit_mask = UINT64_C(1) << idx;
+ if (inc->wires_grouped & bit_mask) {
+ sr_warn("Not adding signal at index %zu to group %s (multiple assignments)",
+ idx, name);
+ } else {
+ desc->mask |= bit_mask;
+ inc->wires_grouped |= bit_mask;
+ }
+ }
+ sr_dbg("'Group' done, name '%s', mask 0x%" PRIx64 ".",
+ desc->name, desc->mask);
+
+ return SR_OK;
+}
+
+static int process_ungrouped_signals(struct context *inc)
+{
+ uint64_t bit_mask;
+ struct signal_group_desc *desc;
+
+ /*
+ * Only create the "ungrouped" channel group if there are any
+ * groups of other signals already.
+ */
+ if (!inc->signal_groups)
+ return SR_OK;
+
+ /*
+ * Determine the bit mask of signals that are part of the
+ * acquisition and are not a member of any other group.
+ */
+ bit_mask = inc->wires_all_mask;
+ bit_mask &= inc->wires_enabled;
+ bit_mask &= ~inc->wires_grouped;
+ sr_dbg("'ungrouped' check: all 0x%" PRIx64 ", en 0x%" PRIx64 ", grp 0x%" PRIx64 " -> un 0x%" PRIx64 ".",
+ inc->wires_all_mask, inc->wires_enabled,
+ inc->wires_grouped, bit_mask);
+ if (!bit_mask)
+ return SR_OK;
+
+ /* Create a sigrok channel group without a name. */
+ desc = alloc_signal_group(NULL);
+ if (!desc)
+ return SR_ERR_MALLOC;
+ inc->signal_groups = g_slist_append(inc->signal_groups, desc);
+ desc->mask = bit_mask;
+
+ return SR_OK;
+}
+
+static int process_enabled_channels(struct context *inc, char **flags)
+{
+ size_t count, idx;
+ uint64_t bits, mask;
+
+ /*
+ * The 'flags' array contains (the textual representation of)
+ * the "enabled" state of the acquisition device's channels.
+ */
+ count = g_strv_length(flags);
+ if (count != inc->channel_count)
+ return SR_ERR_DATA;
+ bits = 0;
+ mask = UINT64_C(1);
+ for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+ if (strcmp(flags[idx], "True") == 0)
+ bits |= mask;
+ }
+ inc->wires_enabled = bits;
+
+ return SR_OK;
+}
+
+static int process_inverted_channels(struct context *inc, char **flags)
+{
+ size_t count, idx;
+ uint64_t bits, mask;
+
+ /*
+ * The 'flags' array contains (the textual representation of)
+ * the "inverted" state of the acquisition device's channels.
+ */
+ count = g_strv_length(flags);
+ if (count != inc->channel_count)
+ return SR_ERR_DATA;
+ bits = 0;
+ mask = UINT64_C(1);
+ for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+ if (strcmp(flags[idx], "True") == 0)
+ bits |= mask;
+ }
+ inc->wires_inverted = bits;
+
+ return SR_OK;
+}
+
+static int process_sample_line(struct context *inc, char **values)
+{
+ size_t count, idx;
+ struct sample_data_entry *entry;
+ uint64_t mask;
+ long conv_ret;
+ int rc;
+
+ /*
+ * The 'values' array contains '0'/'1' text representation of
+ * wire's values, as well as a (a textual representation of a)
+ * repeat counter for that set of samples.
+ */
+ count = g_strv_length(values);
+ if (count != inc->channel_count + 1)
+ return SR_ERR_DATA;
+ entry = &inc->sample_data_queue[inc->sample_lines_read];
+ entry->bits = 0;
+ mask = UINT64_C(1);
+ for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+ if (strcmp(values[idx], "1") == 0)
+ entry->bits |= mask;
+ if (strcmp(values[idx], "U") == 0)
+ inc->wires_undefined |= mask;
+ }
+ rc = sr_atol(values[inc->channel_count], &conv_ret);
+ if (rc != SR_OK)
+ return rc;
+ entry->repeat = conv_ret;
+ inc->samples_got_uncomp += entry->repeat;
+
+ return SR_OK;
+}
+
+static int process_keyvalue_line(struct context *inc, char *line)
+{
+ char *sep, *key, *arg;
+ char **args;
+ int rc;
+ char *version, *build;
+ long build_num;
+ int wires, samples;
+ size_t alloc_size;
+ double period, dbl_rate;
+ uint64_t int_rate;
+
+ /*
+ * Process lines of the 'SampleData' block. Inspection of the
+ * block got started below in the "regular keyword line" section.
+ * The code here handles the remaining number of lines: Opening
+ * and closing braces, wire names, and sample data sets. Note
+ * that the wire names and sample values are separated by comma,
+ * not by DC1 like other key/value pairs and argument lists.
+ */
+ switch (inc->in_sample_data) {
+ case SAMPLEDATA_OPEN_BRACE:
+ if (strcmp(line, "{") != 0)
+ return SR_ERR_DATA;
+ inc->in_sample_data++;
+ return SR_OK;
+ case SAMPLEDATA_WIRES_COUNT:
+ while (isspace(*line))
+ line++;
+ args = g_strsplit(line, ",", 0);
+ rc = process_wire_names(inc, args);
+ g_strfreev(args);
+ if (rc)
+ return rc;
+ inc->in_sample_data++;
+ inc->sample_lines_read = 0;
+ return SR_OK;
+ case SAMPLEDATA_DATA_LINES:
+ while (isspace(*line))
+ line++;
+ args = g_strsplit(line, ",", 0);
+ rc = process_sample_line(inc, args);
+ g_strfreev(args);
+ if (rc)
+ return rc;
+ inc->sample_lines_read++;
+ if (inc->sample_lines_read == inc->sample_lines_total)
+ inc->in_sample_data++;
+ return SR_OK;
+ case SAMPLEDATA_CLOSE_BRACE:
+ if (strcmp(line, "}") != 0)
+ return SR_ERR_DATA;
+ sr_dbg("'SampleData' done: samples count %" PRIu64 ".",
+ inc->samples_got_uncomp);
+ inc->sample_lines_fed = 0;
+ inc->in_sample_data = SAMPLEDATA_NONE;
+ return SR_OK;
+ case SAMPLEDATA_NONE:
+ /* EMPTY */ /* Fall through to regular keyword-line logic. */
+ break;
+ }
+
+ /* Process regular key/value lines separated by DC1. */
+ key = line;
+ sep = strchr(line, DC1_CHR);
+ if (!sep)
+ return SR_ERR_DATA;
+ *sep++ = '\0';
+ arg = sep;
+ if (strcmp(key, "Version") == 0) {
+ rc = check_vers_line(arg, 0, &version, &build);
+ if (rc == SR_OK) {
+ inc->sw_version = g_strdup(version ? version : "?");
+ rc = sr_atol(build, &build_num);
+ inc->sw_build = build_num;
+ }
+ sr_dbg("'Version' line: version %s, build %zu.",
+ inc->sw_version, inc->sw_build);
+ return rc;
+ }
+ if (strcmp(key, "AcquiredSamplePeriod") == 0) {
+ rc = sr_atod(arg, &period);
+ if (rc != SR_OK)
+ return rc;
+ /*
+ * Implementation detail: The vendor's software provides
+ * 1/2/5 choices in the 1kHz - 500MHz range. Unfortunately
+ * the choice of saving the sample _period_ as a floating
+ * point number in the text file yields inaccurate results
+ * for naive implementations of the conversion (0.1 is an
+ * "odd number" in the computer's internal representation).
+ * The below logic of rounding to integer and then rounding
+ * to full kHz works for the samplerate value's range.
+ * "Simplifying" the implementation will introduce errors.
+ */
+ dbl_rate = 1.0 / period;
+ int_rate = (uint64_t)(dbl_rate + 0.5);
+ int_rate += 500;
+ int_rate /= 1000;
+ int_rate *= 1000;
+ inc->sample_rate = int_rate;
+ if (!inc->sample_rate)
+ return SR_ERR_DATA;
+ sr_dbg("Sample rate: %" PRIu64 ".", inc->sample_rate);
+ return SR_OK;
+ }
+ if (strcmp(key, "AcquiredChannelList") == 0) {
+ args = g_strsplit(arg, DC1_STR, 0);
+ rc = process_enabled_channels(inc, args);
+ g_strfreev(args);
+ if (rc)
+ return rc;
+ sr_dbg("Enabled channels: 0x%" PRIx64 ".",
+ inc->wires_enabled);
+ return SR_OK;
+ }
+ if (strcmp(key, "InvertedChannelList") == 0) {
+ args = g_strsplit(arg, DC1_STR, 0);
+ rc = process_inverted_channels(inc, args);
+ g_strfreev(args);
+ sr_dbg("Inverted channels: 0x%" PRIx64 ".",
+ inc->wires_inverted);
+ return SR_OK;
+ }
+ if (strcmp(key, "Signals") == 0) {
+ args = g_strsplit(arg, DC1_STR, 0);
+ rc = process_signal_names(inc, args);
+ g_strfreev(args);
+ if (rc)
+ return rc;
+ sr_dbg("Got signal names.");
+ return SR_OK;
+ }
+ if (strcmp(key, "SampleData") == 0) {
+ args = g_strsplit(arg, DC1_STR, 3);
+ if (!args || !args[0] || !args[1]) {
+ g_strfreev(args);
+ return SR_ERR_DATA;
+ }
+ rc = sr_atoi(args[0], &wires);
+ if (rc) {
+ g_strfreev(args);
+ return SR_ERR_DATA;
+ }
+ rc = sr_atoi(args[1], &samples);
+ if (rc) {
+ g_strfreev(args);
+ return SR_ERR_DATA;
+ }
+ g_strfreev(args);
+ if (!wires || !samples)
+ return SR_ERR_DATA;
+ inc->channel_count = wires;
+ inc->sample_lines_total = samples;
+ sr_dbg("'SampleData' start: wires %zu, sample lines %zu.",
+ inc->channel_count, inc->sample_lines_total);
+ if (inc->channel_count > MAX_CHANNELS)
+ return SR_ERR_DATA;
+ inc->in_sample_data = SAMPLEDATA_OPEN_BRACE;
+ alloc_size = sizeof(inc->sample_data_queue[0]);
+ alloc_size *= inc->sample_lines_total;
+ inc->sample_data_queue = g_malloc0(alloc_size);
+ if (!inc->sample_data_queue)
+ return SR_ERR_DATA;
+ inc->sample_lines_fed = 0;
+ return SR_OK;
+ }
+ if (strcmp(key, "Group") == 0) {
+ args = g_strsplit(arg, DC1_STR, 0);
+ rc = process_signal_group(inc, args);
+ g_strfreev(args);
+ if (rc)
+ return rc;
+ return SR_OK;
+ }
+ if (strcmp(key, LAST_KEYWORD) == 0) {
+ sr_dbg("'" LAST_KEYWORD "' seen, assuming \"header done\".");
+ inc->got_header = TRUE;
+ return SR_OK;
+ }
+
+ /* Unsupported keyword, silently ignore the line. */
+ return SR_OK;
+}
+
+/* Check for, and isolate another line of text input. */
+static int have_text_line(struct sr_input *in, char **line, char **next)
+{
+ char *sol_ptr, *eol_ptr;
+
+ if (!in || !in->buf || !in->buf->str)
+ return 0;
+ sol_ptr = in->buf->str;
+ eol_ptr = strstr(sol_ptr, CRLF);
+ if (!eol_ptr)
+ return 0;
+ if (line)
+ *line = sol_ptr;
+ *eol_ptr = '\0';
+ eol_ptr += strlen(CRLF);
+ if (next)
+ *next = eol_ptr;
+
+ return 1;
+}
+
+/* Handle line continuation. Have logical lines processed. */
+static int process_text_line(struct context *inc, char *line)
+{
+ char *p;
+ int is_cont_end;
+ int rc;
+
+ /*
+ * Handle line continuation in the input stream. Notice that
+ * continued lines can start and end on the same input line.
+ * The text between the markers can be empty, too.
+ *
+ * Make the result look like a regular line. Put a DC1 delimiter
+ * between the keyword and the right hand side. Strip the /<DC1>
+ * and <DC1>/ "braces". Put CRLF between all continued parts,
+ * this makes the data appear "most intuitive and natural"
+ * should we e.g. pass on user's notes in a future version.
+ */
+ is_cont_end = 0;
+ if (!inc->cont_buff) {
+ p = strstr(line, CONT_OPEN);
+ if (p) {
+ /* Start of continuation. */
+ inc->cont_buff = g_string_new_len(line, p - line + 1);
+ inc->cont_buff->str[inc->cont_buff->len - 1] = DC1_CHR;
+ line = p + strlen(CONT_OPEN);
+ }
+ /* Regular line, fall through to below regular logic. */
+ }
+ if (inc->cont_buff) {
+ p = strstr(line, CONT_CLOSE);
+ is_cont_end = p != NULL;
+ if (is_cont_end)
+ *p = '\0';
+ g_string_append_len(inc->cont_buff, line, strlen(line));
+ if (!is_cont_end) {
+ /* Keep accumulating. */
+ g_string_append_len(inc->cont_buff, CRLF, strlen(CRLF));
+ return SR_OK;
+ }
+ /* End of continuation. */
+ line = inc->cont_buff->str;
+ }
+
+ /*
+ * Process a logical line of input. It either was received from
+ * the caller, or is the result of accumulating continued lines.
+ */
+ rc = process_keyvalue_line(inc, line);
+
+ /* Release the accumulation buffer when a continuation ended. */
+ if (is_cont_end) {
+ g_string_free(inc->cont_buff, TRUE);
+ inc->cont_buff = NULL;
+ }
+
+ return rc;
+}
+
+/* Tell whether received data is sufficient for session feed preparation. */
+static int have_header(GString *buf)
+{
+ const char *assumed_last_key = CRLF LAST_KEYWORD CONT_OPEN;
+
+ if (strstr(buf->str, assumed_last_key))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Process/inspect previously received input data. Get header parameters. */
+static int parse_header(struct sr_input *in)
+{
+ struct context *inc;
+ char *line, *next;
+ int rc;
+
+ inc = in->priv;
+ while (have_text_line(in, &line, &next)) {
+ rc = process_text_line(inc, line);
+ g_string_erase(in->buf, 0, next - line);
+ if (rc)
+ return rc;
+ }
+
+ return SR_OK;
+}
+
+/* Create sigrok channels and groups. */
+static int create_channels_groups(struct sr_input *in)
+{
+ struct context *inc;
+ uint64_t mask;
+ size_t idx;
+ const char *name;
+ gboolean enabled;
+ struct sr_channel *ch;
+ struct sr_dev_inst *sdi;
+ GSList *l;
+ struct signal_group_desc *desc;
+ struct sr_channel_group *cg;
+
+ inc = in->priv;
+
+ if (inc->channels)
+ return SR_OK;
+
+ mask = UINT64_C(1);
+ for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+ name = inc->signal_names[idx];
+ if (!name || !*name)
+ name = inc->wire_names[idx];
+ enabled = (inc->wires_enabled & mask) ? TRUE : FALSE;
+ ch = sr_channel_new(in->sdi, idx,
+ SR_CHANNEL_LOGIC, enabled, name);
+ if (!ch)
+ return SR_ERR_MALLOC;
+ inc->channels = g_slist_append(inc->channels, ch);
+ }
+
+ sdi = in->sdi;
+ for (l = inc->signal_groups; l; l = l->next) {
+ desc = l->data;
+ cg = sr_channel_group_new(desc->name, NULL);
+ if (!cg)
+ return SR_ERR_MALLOC;
+ sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+ mask = UINT64_C(1);
+ for (idx = 0; idx < inc->channel_count; idx++, mask <<= 1) {
+ if (!(desc->mask & mask))
+ continue;
+ ch = g_slist_nth_data(inc->channels, idx);
+ if (!ch)
+ return SR_ERR_DATA;
+ cg->channels = g_slist_append(cg->channels, ch);
+ }
+ }
+
+ return SR_OK;
+}
+
+/* Allocate the session feed buffer. */
+static int create_feed_buffer(struct sr_input *in)
+{
+ struct context *inc;
+
+ inc = in->priv;
+
+ inc->unitsize = (inc->channel_count + 7) / 8;
+ inc->samples_per_chunk = CHUNK_SIZE / inc->unitsize;
+ inc->samples_in_buffer = 0;
+ inc->feed_buffer = g_malloc0(inc->samples_per_chunk * inc->unitsize);
+ if (!inc->feed_buffer)
+ return SR_ERR_MALLOC;
+
+ return SR_OK;
+}
+
+/* Send all accumulated sample data values to the session. */
+static int send_buffer(struct sr_input *in)
+{
+ struct context *inc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_meta meta;
+ struct sr_config *src;
+ struct sr_datafeed_logic logic;
+ int rc;
+
+ inc = in->priv;
+ if (!inc->samples_in_buffer)
+ return SR_OK;
+
+ if (!inc->header_sent) {
+ rc = std_session_send_df_header(in->sdi);
+ if (rc)
+ return rc;
+ inc->header_sent = TRUE;
+ }
+
+ if (inc->sample_rate && !inc->rate_sent) {
+ packet.type = SR_DF_META;
+ packet.payload = &meta;
+ src = sr_config_new(SR_CONF_SAMPLERATE,
+ g_variant_new_uint64(inc->sample_rate));
+ meta.config = g_slist_append(NULL, src);
+ rc = sr_session_send(in->sdi, &packet);
+ g_slist_free(meta.config);
+ sr_config_free(src);
+ if (rc)
+ return rc;
+ inc->rate_sent = TRUE;
+ }
+
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.unitsize = inc->unitsize;
+ logic.data = inc->feed_buffer;
+ logic.length = inc->unitsize * inc->samples_in_buffer;
+ rc = sr_session_send(in->sdi, &packet);
+
+ inc->samples_in_buffer = 0;
+
+ if (rc)
+ return rc;
+
+ return SR_OK;
+}
+
+/*
+ * Add N copies of the current sample to the buffer. Send the buffer to
+ * the session feed when a maximum amount of data was collected.
+ */
+static int add_samples(struct sr_input *in, uint64_t samples, size_t count)
+{
+ struct context *inc;
+ uint8_t sample_buffer[sizeof(uint64_t)];
+ size_t idx;
+ size_t copy_count;
+ uint8_t *p;
+ int rc;
+
+ inc = in->priv;
+ for (idx = 0; idx < inc->unitsize; idx++) {
+ sample_buffer[idx] = samples & 0xff;
+ samples >>= 8;
+ }
+ while (count) {
+ copy_count = inc->samples_per_chunk - inc->samples_in_buffer;
+ if (copy_count > count)
+ copy_count = count;
+ count -= copy_count;
+
+ p = inc->feed_buffer + inc->samples_in_buffer * inc->unitsize;
+ while (copy_count-- > 0) {
+ memcpy(p, sample_buffer, inc->unitsize);
+ p += inc->unitsize;
+ inc->samples_in_buffer++;
+ }
+
+ if (inc->samples_in_buffer == inc->samples_per_chunk) {
+ rc = send_buffer(in);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return SR_OK;
+}
+
+/* Pass on previously received samples to the session. */
+static int process_queued_samples(struct sr_input *in)
+{
+ struct context *inc;
+ struct sample_data_entry *entry;
+ uint64_t sample_bits;
+ int rc;
+
+ inc = in->priv;
+ while (inc->sample_lines_fed < inc->sample_lines_total) {
+ entry = &inc->sample_data_queue[inc->sample_lines_fed++];
+ sample_bits = entry->bits;
+ sample_bits ^= inc->wires_inverted;
+ sample_bits &= inc->wires_enabled;
+ rc = add_samples(in, sample_bits, entry->repeat);
+ if (rc)
+ return rc;
+ }
+
+ return SR_OK;
+}
+
+/*
+ * Create required resources between having read the input file and
+ * sending sample data to the session. Send initial packets before
+ * sample data follows.
+ */
+static int prepare_session_feed(struct sr_input *in)
+{
+ struct context *inc;
+ int rc;
+
+ inc = in->priv;
+ if (inc->ch_feed_prep)
+ return SR_OK;
+
+ /* Got channel names? At least fallbacks? */
+ if (!inc->wire_names[0] || !inc->wire_names[0][0])
+ return SR_ERR_DATA;
+ /* Samples seen? Seen them all? */
+ if (!inc->channel_count)
+ return SR_ERR_DATA;
+ if (!inc->sample_lines_total)
+ return SR_ERR_DATA;
+ if (inc->in_sample_data)
+ return SR_ERR_DATA;
+ if (!inc->sample_data_queue)
+ return SR_ERR_DATA;
+ inc->sample_lines_fed = 0;
+
+ /*
+ * Normalize some variants of input data.
+ * - Let's create a mask for the maximum possible
+ * bit positions, it will be useful to avoid garbage
+ * in other code paths, too.
+ * - Input files _might_ specify which channels were
+ * enabled during acquisition. _Or_ not specify the
+ * enabled channels, but provide 'U' values in some
+ * columns. When neither was seen, assume that all
+ * channels are enabled.
+ * - If there are any signal groups, put all signals into
+ * an anonymous group that are not part of another group.
+ */
+ inc->wires_all_mask = UINT64_C(1);
+ inc->wires_all_mask <<= inc->channel_count;
+ inc->wires_all_mask--;
+ sr_dbg("all wires mask: 0x%" PRIx64 ".", inc->wires_all_mask);
+ if (!inc->wires_enabled) {
+ inc->wires_enabled = ~inc->wires_undefined;
+ inc->wires_enabled &= ~inc->wires_all_mask;
+ sr_dbg("enabled from undefined: 0x%" PRIx64 ".",
+ inc->wires_enabled);
+ }
+ if (!inc->wires_enabled) {
+ inc->wires_enabled = inc->wires_all_mask;
+ sr_dbg("enabled from total mask: 0x%" PRIx64 ".",
+ inc->wires_enabled);
+ }
+ sr_dbg("enabled mask: 0x%" PRIx64 ".",
+ inc->wires_enabled);
+ rc = process_ungrouped_signals(inc);
+ if (rc)
+ return rc;
+
+ /*
+ * "Start" the session: Create channels, send the DF
+ * header to the session. Optionally send the sample
+ * rate before sample data will be sent.
+ */
+ rc = create_channels_groups(in);
+ if (rc)
+ return rc;
+ rc = create_feed_buffer(in);
+ if (rc)
+ return rc;
+
+ inc->ch_feed_prep = TRUE;
+
+ return SR_OK;
+}
+
+static int format_match(GHashTable *metadata, unsigned int *confidence)
+{
+ GString *buf, *tmpbuf;
+ int rc;
+ gchar *version, *build;
+
+ /* Get a copy of the start of the file's content. */
+ buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+ if (!buf || !buf->str)
+ return SR_ERR_ARG;
+ tmpbuf = g_string_new_len(buf->str, buf->len);
+ if (!tmpbuf || !tmpbuf->str)
+ return SR_ERR_MALLOC;
+
+ /* See if we can spot a typical first LPF line. */
+ rc = check_vers_line(tmpbuf->str, 1, &version, &build);
+ if (rc == SR_OK && version && build) {
+ sr_dbg("Looks like a LogicProbe project, version %s, build %s.",
+ version, build);
+ *confidence = 1;
+ }
+ g_string_free(tmpbuf, TRUE);
+
+ return rc;
+}
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+ struct context *inc;
+
+ (void)options;
+
+ in->sdi = g_malloc0(sizeof(*in->sdi));
+ inc = g_malloc0(sizeof(*inc));
+ in->priv = inc;
+
+ return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+ struct context *inc;
+ int rc;
+
+ /* Accumulate another chunk of input data. */
+ g_string_append_len(in->buf, buf->str, buf->len);
+
+ /*
+ * Wait for the full header's availability, then process it in a
+ * single call, and set the "ready" flag. Make sure sample data
+ * and the header get processed in disjoint calls to receive(),
+ * the backend requires those separate phases.
+ */
+ inc = in->priv;
+ if (!inc->got_header) {
+ if (!have_header(in->buf))
+ return SR_OK;
+ rc = parse_header(in);
+ if (rc)
+ return rc;
+ rc = prepare_session_feed(in);
+ if (rc)
+ return rc;
+ in->sdi_ready = TRUE;
+ return SR_OK;
+ }
+
+ /* Process sample data, after the header got processed. */
+ rc = process_queued_samples(in);
+
+ return rc;
+}
+
+static int end(struct sr_input *in)
+{
+ struct context *inc;
+ int rc;
+
+ /* Nothing to do here if we never started feeding the session. */
+ if (!in->sdi_ready)
+ return SR_OK;
+
+ /*
+ * Process sample data that may not have been forwarded before.
+ * Flush any potentially queued samples.
+ */
+ rc = process_queued_samples(in);
+ if (rc)
+ return rc;
+ rc = send_buffer(in);
+ if (rc)
+ return rc;
+
+ /* End the session feed if one was started. */
+ inc = in->priv;
+ if (inc->header_sent) {
+ rc = std_session_send_df_end(in->sdi);
+ inc->header_sent = FALSE;
+ }
+
+ return rc;
+}
+
+static void cleanup(struct sr_input *in)
+{
+ struct context *inc;
+ size_t idx;
+
+ if (!in)
+ return;
+
+ inc = in->priv;
+ if (!inc)
+ return;
+
+ /*
+ * Release potentially allocated resources. Void all references
+ * and scalars, so that re-runs start out fresh again.
+ */
+ g_free(inc->sw_version);
+ if (inc->cont_buff)
+ g_string_free(inc->cont_buff, TRUE);
+ g_free(inc->sample_data_queue);
+ for (idx = 0; idx < inc->channel_count; idx++)
+ g_free(inc->wire_names[idx]);
+ for (idx = 0; idx < inc->channel_count; idx++)
+ g_free(inc->signal_names[idx]);
+ g_slist_free_full(inc->signal_groups, sg_free);
+ g_slist_free_full(inc->channels, g_free);
+ g_free(inc->feed_buffer);
+ memset(inc, 0, sizeof(*inc));
+}
+
+static int reset(struct sr_input *in)
+{
+ struct context *inc;
+ GSList *channels;
+
+ inc = in->priv;
+
+ /*
+ * The input module's .reset() routine clears the 'inc' context,
+ * but 'in' is kept which contains channel groups which reference
+ * channels. Since we cannot re-create the channels (applications
+ * don't expect us to, see bug #1215), make sure to keep the
+ * channels across the reset operation.
+ */
+ channels = inc->channels;
+ inc->channels = NULL;
+ cleanup(in);
+ inc->channels = channels;
+
+ return SR_OK;
+}
+
+static struct sr_option options[] = {
+ ALL_ZERO,
+};
+
+static const struct sr_option *get_options(void)
+{
+ return options;
+}
+
+SR_PRIV struct sr_input_module input_logicport = {
+ .id = "logicport",
+ .name = "LogicPort File",
+ .desc = "Intronix LA1034 LogicPort project",
+ .exts = (const char *[]){ "lpf", 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,
+ .reset = reset,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "input/null"
+
+static int init(struct sr_input *in, GHashTable *options)
+{
+ (void)in;
+ (void)options;
+
+ return SR_OK;
+}
+
+static int receive(struct sr_input *in, GString *buf)
+{
+ (void)in;
+ (void)buf;
+
+ return SR_OK;
+}
+
+static int end(struct sr_input *in)
+{
+ (void)in;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_input_module input_null = {
+ .id = "null",
+ .name = "Null",
+ .desc = "Null input (discards all input)",
+ .exts = NULL,
+ .options = NULL,
+ .init = init,
+ .receive = receive,
+ .end = end,
+ .reset = NULL,
+};
#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 CHUNK_SIZE (4 * 1024 * 1024)
#define DEFAULT_NUM_CHANNELS 1
#define DEFAULT_SAMPLERATE 0
static const struct sample_format 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}}},
+ // bytes, signed, floating, bigendian, digits, digits decimal, scale, offset
+ { "S8", { 1, TRUE, FALSE, FALSE, 7, FALSE, { 1, 128}, { 0, 1}}},
+ { "U8", { 1, FALSE, FALSE, FALSE, 8, FALSE, { 1, 255}, {-1, 2}}},
+ { "S16_LE", { 2, TRUE, FALSE, FALSE, 15, FALSE, { 1, INT16_MAX + 1}, { 0, 1}}},
+ { "U16_LE", { 2, FALSE, FALSE, FALSE, 16, FALSE, { 1, UINT16_MAX}, {-1, 2}}},
+ { "S16_BE", { 2, TRUE, FALSE, TRUE, 15, FALSE, { 1, INT16_MAX + 1}, { 0, 1}}},
+ { "U16_BE", { 2, FALSE, FALSE, TRUE, 16, FALSE, { 1, UINT16_MAX}, {-1, 2}}},
+ { "S32_LE", { 4, TRUE, FALSE, FALSE, 31, FALSE, { 1, (uint64_t)INT32_MAX + 1}, { 0, 1}}},
+ { "U32_LE", { 4, FALSE, FALSE, FALSE, 32, FALSE, { 1, UINT32_MAX}, {-1, 2}}},
+ { "S32_BE", { 4, TRUE, FALSE, TRUE, 31, FALSE, { 1, (uint64_t)INT32_MAX + 1}, { 0, 1}}},
+ { "U32_BE", { 4, FALSE, FALSE, TRUE, 32, FALSE, { 1, UINT32_MAX}, {-1, 2}}},
+ { "FLOAT_LE", { 4, TRUE, TRUE, FALSE, 6, TRUE, { 1, 1}, { 0, 1}}},
+ { "FLOAT_BE", { 4, TRUE, TRUE, TRUE, 6, TRUE, { 1, 1}, { 0, 1}}},
+ { "FLOAT64_LE", { 8, TRUE, TRUE, FALSE, 15, TRUE, { 1, 1}, { 0, 1}}},
+ { "FLOAT64_BE", { 8, TRUE, TRUE, TRUE, 15, TRUE, { 1, 1}, { 0, 1}}},
};
static int parse_format_string(const char *format)
{
struct context *inc;
int num_channels;
- char channelname[8];
+ char channelname[16];
const char *format;
int fmt_index;
in->priv = inc = g_malloc0(sizeof(struct context));
for (int i = 0; i < num_channels; i++) {
- snprintf(channelname, 8, "CH%d", i + 1);
+ snprintf(channelname, sizeof(channelname) - 1, "CH%d", i + 1);
sr_channel_new(in->sdi, i, SR_CHANNEL_ANALOG, TRUE, channelname);
}
}
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 },
+ { "numchannels", "Number of analog channels", "The number of (analog) channels in the data", NULL, NULL },
+ { "samplerate", "Sample rate (Hz)", "The sample rate of the (analog) data in Hz", NULL, NULL },
+ { "format", "Data format", "The format of the data (data type, signedness, endianness)", NULL, NULL },
ALL_ZERO
};
static void cleanup(struct sr_input *in)
{
- struct context *inc;
+ g_free(in->priv);
+ in->priv = NULL;
- 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;
}
static int reset(struct sr_input *in)
{
struct context *inc = in->priv;
- cleanup(in);
inc->started = FALSE;
+
g_string_truncate(in->buf, 0);
return SR_OK;
SR_PRIV struct sr_input_module input_raw_analog = {
.id = "raw_analog",
.name = "RAW analog",
- .desc = "analog signals without header",
+ .desc = "Raw analog data without header",
.exts = (const char*[]){"raw", "bin", NULL},
.options = get_options,
.init = init,
#define LOG_PREFIX "input/trace32_ad"
-#define MAX_CHUNK_SIZE 4096
-#define OUTBUF_FLUSH_SIZE 10240
+#define CHUNK_SIZE (4 * 1024 * 1024)
#define MAX_POD_COUNT 12
#define HEADER_SIZE 80
+#define SPACE ' '
+#define CTRLZ '\x1a'
+#define TRACE32 "trace32"
+
#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
/*
*/
#define DEFAULT_SAMPLERATE 200
-enum {
- AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
- AD_FORMAT_TXTHDR /* Textual header, binary data */
+enum ad_format {
+ AD_FORMAT_UNKNOWN,
+ AD_FORMAT_BINHDR, /* Binary header, binary data, textual setup info */
+ AD_FORMAT_TXTHDR, /* Textual header, binary data */
};
-enum {
+enum ad_device {
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-4530 uTrace analog probe */
};
-enum {
+enum ad_mode {
AD_MODE_250MHZ = 0,
AD_MODE_500MHZ = 1
};
-enum {
+enum ad_compr {
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;
+ enum ad_format format;
+ enum ad_device device;
+ enum ad_mode record_mode;
+ enum ad_compr compression;
char pod_status[MAX_POD_COUNT];
struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
uint64_t trigger_timestamp;
static int process_header(GString *buf, struct context *inc);
static void create_channels(struct sr_input *in);
+/* Transform non-printable chars to '\xNN' presentation. */
+static char *printable_name(const char *name)
+{
+ size_t l, i;
+ char *s, *p;
+
+ if (!name)
+ return NULL;
+ l = strlen(name);
+ s = g_malloc0(l * strlen("\\x00") + 1);
+ for (p = s, i = 0; i < l; i++) {
+ if (g_ascii_isprint(name[i])) {
+ *p++ = name[i];
+ } else {
+ snprintf(p, 5, "\\x%02x", name[i]);
+ p += strlen("\\x00");
+ }
+ }
+ *p = '\0';
+
+ return s;
+}
+
static char get_pod_name_from_id(int id)
{
switch (id) {
return SR_ERR;
}
- inc->out_buf = g_string_sized_new(OUTBUF_FLUSH_SIZE);
+ inc->out_buf = g_string_sized_new(CHUNK_SIZE);
return SR_OK;
}
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
{
GString *buf;
+ int rc;
buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+ rc = process_header(buf, NULL);
+
+ if (rc != SR_OK)
+ return rc;
+ *confidence = 10;
- return process_header(buf, NULL);
+ return SR_OK;
}
static int process_header(GString *buf, struct context *inc)
{
char *format_name, *format_name_sig;
- int i, record_size, device_id;
+ char *p;
+ int has_trace32;
+ size_t record_size;
+ enum ad_device device_id;
+ enum ad_format format;
/*
* 00-31 (0x00-1F) file format name
* 78-79 (0x4E-4F) ??
*/
- /* Note: inc is off-limits until we check whether it's a valid pointer. */
+ /*
+ * Note: The routine is called from different contexts. Either
+ * to auto-detect the file format (format_match(), 'inc' is NULL),
+ * or to process the data during acquisition (receive(), 'inc'
+ * is a valid pointer). This header parse routine shall gracefully
+ * deal with unexpected or incorrect input data.
+ */
+ /*
+ * Get up to the first 32 bytes of the file content. File format
+ * names end on SPACE or CTRL-Z (or NUL). Trim trailing SPACE
+ * before further processing.
+ */
format_name = g_strndup(buf->str, 32);
+ p = strchr(format_name, CTRLZ);
+ if (p)
+ *p = '\0';
+ g_strchomp(format_name);
- /* 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);
+ /*
+ * File format names either start with the "trace32" literal,
+ * or with a digit and SPACE.
+ */
+ format_name_sig = g_strndup(format_name, strlen(TRACE32));
+ has_trace32 = g_strcmp0(format_name_sig, TRACE32) == 0;
+ g_free(format_name_sig);
- /* 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);
+ format = AD_FORMAT_UNKNOWN;
+ if (has_trace32) {
+ /* Literal "trace32" leader, binary header follows. */
+ format = AD_FORMAT_BINHDR;
+ } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == SPACE)) {
+ /* Digit and SPACE leader, currently unsupported text header. */
+ format = AD_FORMAT_TXTHDR;
g_free(format_name);
- sr_err("This format isn't implemented yet, aborting.");
+ if (inc)
+ sr_err("This format isn't implemented yet, aborting.");
return SR_ERR;
} else {
- g_free(format_name_sig);
+ /* Unknown kind of format name. Unsupported. */
g_free(format_name);
- sr_err("Don't know this file format, aborting.");
+ if (inc)
+ sr_err("Don't know this file format, aborting.");
return SR_ERR;
}
+ if (!format)
+ return SR_ERR;
- sr_dbg("File says it's \"%s\"", format_name);
+ p = printable_name(format_name);
+ if (inc)
+ sr_dbg("File says it's \"%s\" -> format type %u.", p, format);
+ g_free(p);
record_size = R8(buf->str + 56);
device_id = 0;
}
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);
+ if (inc)
+ sr_err("Cannot handle file with record size %zu.",
+ 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->format = format;
inc->device = device_id;
inc->trigger_timestamp = RL64(buf->str + 32);
inc->compression = R8(buf->str + 48); /* Maps to the enum. */
inc->last_record);
/* Check if we can work with this compression. */
- if (inc->compression != AD_COMPR_NONE) {
+ if (inc->compression) {
sr_err("File uses unsupported compression (0x%02X), can't continue.",
inc->compression);
return SR_ERR;
continue;
for (channel = 0; channel < 16; channel++) {
- snprintf(name, 8, "%c%d", get_pod_name_from_id(pod), channel);
+ snprintf(name, sizeof(name), "%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));
+ snprintf(name, sizeof(name), "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++;
g_string_append_len(inc->out_buf, single_payload, payload_len);
}
- if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+ if (inc->out_buf->len >= CHUNK_SIZE)
flush_output_buffer(in);
}
g_string_append_len(inc->out_buf, single_payload, payload_len);
}
- if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+ if (inc->out_buf->len >= CHUNK_SIZE)
flush_output_buffer(in);
}
{ "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 },
- { "samplerate", "Reduced sample rate in MHz", "Reduced sample rate in MHz", NULL, NULL },
+ { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
ALL_ZERO
};
#define LOG_PREFIX "input/vcd"
-#define CHUNKSIZE (1024 * 1024)
+#define CHUNK_SIZE (4 * 1024 * 1024)
struct context {
gboolean started;
gboolean got_header;
+ uint64_t prev_timestamp;
uint64_t samplerate;
unsigned int maxchannels;
unsigned int channelcount;
size_t samples_in_buffer;
uint8_t *buffer;
uint8_t *current_levels;
+ GSList *prev_sr_channels;
};
struct vcd_channel {
static void free_channel(void *data)
{
- struct vcd_channel *vcd_ch = data;
+ struct vcd_channel *vcd_ch;
+
+ vcd_ch = data;
+ if (!vcd_ch)
+ return;
g_free(vcd_ch->name);
g_free(vcd_ch->identifier);
g_free(vcd_ch);
*dest = NULL;
}
+/*
+ * Keep track of a previously created channel list, in preparation of
+ * re-reading the input file. Gets called from reset()/cleanup() paths.
+ */
+static void keep_header_for_reread(const struct sr_input *in)
+{
+ struct context *inc;
+
+ inc = in->priv;
+ g_slist_free_full(inc->prev_sr_channels, sr_channel_free_cb);
+ inc->prev_sr_channels = in->sdi->channels;
+ in->sdi->channels = NULL;
+}
+
+/*
+ * Check whether the input file is being re-read, and refuse operation
+ * when essential parameters of the acquisition have changed in ways
+ * that are unexpected to calling applications. Gets called after the
+ * file header got parsed (again).
+ *
+ * Changing the channel list across re-imports of the same file is not
+ * supported, by design and for valid reasons, see bug #1215 for details.
+ * Users are expected to start new sessions when they change these
+ * essential parameters in the acquisition's setup. When we accept the
+ * re-read file, then make sure to keep using the previous channel list,
+ * applications may still reference them.
+ */
+static int check_header_in_reread(const struct sr_input *in)
+{
+ struct context *inc;
+
+ if (!in)
+ return FALSE;
+ inc = in->priv;
+ if (!inc)
+ return FALSE;
+ if (!inc->prev_sr_channels)
+ return TRUE;
+
+ if (sr_channel_lists_differ(inc->prev_sr_channels, in->sdi->channels)) {
+ sr_err("Channel list change not supported for file re-read.");
+ return FALSE;
+ }
+ g_slist_free_full(in->sdi->channels, sr_channel_free_cb);
+ in->sdi->channels = inc->prev_sr_channels;
+ inc->prev_sr_channels = NULL;
+
+ return TRUE;
+}
+
/*
* Parse VCD header to get values for context structure.
* The context structure should be zeroed before calling this.
inc->current_levels = g_malloc0(inc->bytes_per_sample);
inc->got_header = status;
+ if (status)
+ status = check_header_in_reread(in);
return status;
}
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
{
GString *buf, *tmpbuf;
gboolean status;
g_free(name);
g_free(contents);
- return status ? SR_OK : SR_ERR;
+ if (!status)
+ return SR_ERR;
+ *confidence = 1;
+
+ return SR_OK;
}
/* Send all accumulated bytes from inc->buffer. */
uint8_t *p;
inc = in->priv;
- samples_per_chunk = CHUNKSIZE / inc->bytes_per_sample;
+ samples_per_chunk = CHUNK_SIZE / inc->bytes_per_sample;
while (count) {
space_left = samples_per_chunk - inc->samples_in_buffer;
static void parse_contents(const struct sr_input *in, char *data)
{
struct context *inc;
- uint64_t timestamp, prev_timestamp;
+ uint64_t 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);
*/
if (inc->skip < 0) {
inc->skip = timestamp;
- prev_timestamp = timestamp;
+ inc->prev_timestamp = timestamp;
} else if (inc->skip > 0 && timestamp < (uint64_t)inc->skip) {
- prev_timestamp = inc->skip;
- } else if (timestamp == prev_timestamp) {
+ inc->prev_timestamp = inc->skip;
+ } else if (timestamp == inc->prev_timestamp) {
/* Ignore repeated timestamps (e.g. sigrok outputs these) */
+ } else if (timestamp < inc->prev_timestamp) {
+ sr_err("Invalid timestamp: %" PRIu64 " (smaller than previous timestamp).", timestamp);
+ inc->skip_until_end = TRUE;
+ break;
} else {
- if (inc->compress != 0 && timestamp - prev_timestamp > inc->compress) {
+ if (inc->compress != 0 && timestamp - inc->prev_timestamp > inc->compress) {
/* Compress long idle periods */
- prev_timestamp = timestamp - inc->compress;
+ inc->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;
+ add_samples(in, timestamp - inc->prev_timestamp);
+ inc->prev_timestamp = timestamp;
}
} else if (tokens[i][0] == '$' && tokens[i][1] != '\0') {
/*
in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
in->priv = inc;
- inc->buffer = g_malloc(CHUNKSIZE);
+ inc->buffer = g_malloc(CHUNK_SIZE);
return SR_OK;
}
struct context *inc;
inc = in->priv;
+ keep_header_for_reread(in);
g_slist_free_full(inc->channels, free_channel);
+ inc->channels = NULL;
+
g_free(inc->buffer);
inc->buffer = NULL;
g_free(inc->current_levels);
struct context *inc = in->priv;
cleanup(in);
- inc->started = FALSE;
g_string_truncate(in->buf, 0);
+ inc->started = FALSE;
+ inc->got_header = FALSE;
+ inc->prev_timestamp = 0;
+ inc->skip_until_end = FALSE;
+ inc->channelcount = 0;
+ /* The inc->channels list was released in cleanup() above. */
+ inc->buffer = g_malloc(CHUNK_SIZE);
+
return SR_OK;
}
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 },
+ { "numchannels", "Number of logic channels", "The number of (logic) channels in the data", NULL, NULL },
+ { "skip", "Skip samples until timestamp", "Skip samples until the specified timestamp; "
+ "< 0: Skip until first timestamp listed; 0: Don't skip", NULL, NULL },
+ { "downsample", "Downsampling factor", "Downsample, i.e. divide the samplerate by the specified factor", NULL, NULL },
+ { "compress", "Compress idle periods", "Compress idle periods longer than the specified value", NULL, NULL },
ALL_ZERO
};
SR_PRIV struct sr_input_module input_vcd = {
.id = "vcd",
.name = "VCD",
- .desc = "Value Change Dump",
+ .desc = "Value Change Dump data",
.exts = (const char*[]){"vcd", NULL},
.metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
.options = get_options,
#define LOG_PREFIX "input/wav"
/* How many bytes at a time to process and send to the session bus. */
-#define CHUNK_SIZE 4096
+#define CHUNK_SIZE (1 * 1024 * 1024 * sizeof(float))
/* Minimum size of header + 1 8-bit mono PCM sample. */
#define MIN_DATA_CHUNK_OFFSET 45
int num_channels;
int unitsize;
gboolean found_data;
+ gboolean create_channels;
};
static int parse_wav_header(GString *buf, struct context *inc)
return SR_OK;
}
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
{
GString *buf;
int ret;
if ((ret = parse_wav_header(buf, NULL)) != SR_OK)
return ret;
+ *confidence = 1;
+
return SR_OK;
}
static int init(struct sr_input *in, GHashTable *options)
{
+ struct context *inc;
+
(void)options;
in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
in->priv = g_malloc0(sizeof(struct context));
+ inc = in->priv;
+
+ inc->create_channels = TRUE;
return SR_OK;
}
struct sr_analog_meaning meaning;
struct sr_analog_spec spec;
struct context *inc;
- float fdata[CHUNK_SIZE];
+ float *fdata;
int total_samples, samplenum;
char *s, *d;
inc = in->priv;
+ total_samples = num_samples * inc->num_channels;
+ fdata = g_malloc0(total_samples * sizeof(float));
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) {
analog.meaning->mqflags = 0;
analog.meaning->unit = 0;
sr_session_send(in->sdi, &packet);
+ g_free(fdata);
}
static int process_buffer(struct sr_input *in)
{
struct context *inc;
int ret;
- char channelname[8];
+ char channelname[16];
g_string_append_len(in->buf, buf->str, buf->len);
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);
+ if (inc->create_channels) {
+ for (int i = 0; i < inc->num_channels; i++) {
+ snprintf(channelname, sizeof(channelname), "CH%d", i + 1);
+ sr_channel_new(in->sdi, i, SR_CHANNEL_ANALOG, TRUE, channelname);
+ }
}
+ inc->create_channels = FALSE;
+
/* sdi is ready, notify frontend. */
in->sdi_ready = TRUE;
return SR_OK;
static int reset(struct sr_input *in)
{
- struct context *inc = in->priv;
+ memset(in->priv, 0, sizeof(struct context));
+
+ /*
+ * We only want to create the sigrok channels once, so
+ * inc->create_channels won't be set to TRUE this time around.
+ */
- inc->started = FALSE;
g_string_truncate(in->buf, 0);
return SR_OK;
SR_PRIV struct sr_input_module input_wav = {
.id = "wav",
.name = "WAV",
- .desc = "WAV file",
+ .desc = "Microsoft WAV file format data",
.exts = (const char*[]){"wav", NULL},
.metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
.format_match = format_match,
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;
"NONE", "PARALLEL", "SERIES", "AUTO",
};
-/** Private, per-device-instance driver context. */
struct dev_context {
- /** 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 val;
float floatval;
gboolean frame;
+ struct sr_channel *channel;
devc = sdi->priv;
analog.num_samples = 1;
analog.data = &floatval;
- analog.meaning->channels = g_slist_append(NULL, sdi->channels->data);
+ channel = sdi->channels->data;
+ analog.meaning->channels = g_slist_append(NULL, channel);
parse_measurement(pkt, &floatval, &analog, 0);
- if (analog.meaning->mq != 0) {
+ if (analog.meaning->mq != 0 && channel->enabled) {
if (!frame) {
packet.type = SR_DF_FRAME_BEGIN;
sr_session_send(sdi, &packet);
}
g_slist_free(analog.meaning->channels);
- analog.meaning->channels = g_slist_append(NULL, sdi->channels->next->data);
+
+ channel = sdi->channels->next->data;
+ analog.meaning->channels = g_slist_append(NULL, channel);
parse_measurement(pkt, &floatval, &analog, 1);
- if (analog.meaning->mq != 0) {
+ if (analog.meaning->mq != 0 && channel->enabled) {
if (!frame) {
packet.type = SR_DF_FRAME_BEGIN;
sr_session_send(sdi, &packet);
if (dev_limit_counter_limit_reached(&devc->frame_count) ||
dev_time_limit_reached(&devc->time_count))
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
return TRUE;
}
return;
dev_buffer_destroy(devc->buf);
- g_free(devc);
}
SR_PRIV struct sr_dev_inst *es51919_serial_scan(GSList *options,
SR_CONF_SERIALCOMM,
};
-static const uint32_t devopts[] = {
+static const uint32_t drvopts[] = {
SR_CONF_LCRMETER,
+};
+
+static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS,
SR_CONF_LIMIT_FRAMES | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_SET,
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_SCAN_OPTIONS:
+ case SR_CONF_DEVICE_OPTIONS:
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
case SR_CONF_OUTPUT_FREQUENCY:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE,
- frequencies, ARRAY_SIZE(frequencies), sizeof(double));
+ ARRAY_AND_SIZE(frequencies), sizeof(double));
break;
case SR_CONF_EQUIV_CIRCUIT_MODEL:
- *data = g_variant_new_strv(models, ARRAY_SIZE(models));
+ *data = g_variant_new_strv(ARRAY_AND_SIZE(models));
break;
default:
return SR_ERR_NA;
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;
std_session_send_df_header(sdi);
- /* 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);
* Check if this input module can load and parse the specified stream.
*
* @param[in] metadata Metadata the module can use to identify the stream.
+ * @param[out] confidence "Strength" of the detection.
+ * Specialized handlers can take precedence over generic/basic support.
*
* @retval SR_OK This module knows the format.
* @retval SR_ERR_NA There wasn't enough data for this module to
* 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.
+ *
+ * Lower numeric values of 'confidence' mean that the input module
+ * stronger believes in its capability to handle this specific format.
+ * This way, multiple input modules can claim support for a format,
+ * and the application can pick the best match, or try fallbacks
+ * in case of errors. This approach also copes with formats that
+ * are unreliable to detect in the absence of magic signatures.
*/
- int (*format_match) (GHashTable *metadata);
+ int (*format_match) (GHashTable *metadata, unsigned int *confidence);
/**
* Initialize the input module.
/*--- log.c -----------------------------------------------------------------*/
-#if defined(G_OS_WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
+#if defined(_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 struct sr_channel *sr_channel_new(struct sr_dev_inst *sdi,
int index, int type, gboolean enabled, const char *name);
+SR_PRIV void sr_channel_free(struct sr_channel *ch);
+SR_PRIV void sr_channel_free_cb(void *p);
SR_PRIV struct sr_channel *sr_next_enabled_channel(const struct sr_dev_inst *sdi,
struct sr_channel *cur_channel);
+SR_PRIV gboolean sr_channels_differ(struct sr_channel *ch1, struct sr_channel *ch2);
+SR_PRIV gboolean sr_channel_lists_differ(GSList *l1, GSList *l2);
/** Device instance data */
struct sr_dev_inst {
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);
+SR_PRIV int sr_dev_acquisition_start(struct sr_dev_inst *sdi);
+SR_PRIV int sr_dev_acquisition_stop(struct sr_dev_inst *sdi);
/*--- session.c -------------------------------------------------------------*/
SR_PRIV int sr_sessionfile_check(const char *filename);
SR_PRIV struct sr_dev_inst *sr_session_prepare_sdi(const char *filename,
struct sr_session **session);
-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 --------------------------------------------------------*/
SR_PRIV int std_init(struct sr_dev_driver *di, struct sr_context *sr_ctx);
SR_PRIV int std_cleanup(const struct sr_dev_driver *di);
+SR_PRIV int std_dummy_dev_open(struct sr_dev_inst *sdi);
+SR_PRIV int std_dummy_dev_close(struct sr_dev_inst *sdi);
+SR_PRIV int std_dummy_dev_acquisition_start(const struct sr_dev_inst *sdi);
+SR_PRIV int std_dummy_dev_acquisition_stop(struct sr_dev_inst *sdi);
#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);
#endif
SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi);
SR_PRIV int std_session_send_df_end(const struct sr_dev_inst *sdi);
-SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
+SR_PRIV int std_session_send_frame_begin(const struct sr_dev_inst *sdi);
+SR_PRIV int std_session_send_frame_end(const struct sr_dev_inst *sdi);
+SR_PRIV int std_dev_clear_with_callback(const struct sr_dev_driver *driver,
std_dev_clear_callback clear_private);
+SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver);
SR_PRIV GSList *std_dev_list(const struct sr_dev_driver *di);
SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi);
SR_PRIV GSList *std_scan_complete(struct sr_dev_driver *di, GSList *devices);
+SR_PRIV int std_opts_config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg,
+ const uint32_t scanopts[], size_t scansize, const uint32_t drvopts[],
+ size_t drvsize, const uint32_t devopts[], size_t devsize);
+
+extern SR_PRIV const uint32_t NO_OPTS[1];
+
+#define STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts) \
+ std_opts_config_list(key, data, sdi, cg, ARRAY_AND_SIZE(scanopts), \
+ ARRAY_AND_SIZE(drvopts), ARRAY_AND_SIZE(devopts))
+
+SR_PRIV GVariant *std_gvar_tuple_array(const uint64_t a[][2], unsigned int n);
+SR_PRIV GVariant *std_gvar_tuple_rational(const struct sr_rational *r, unsigned int n);
+SR_PRIV GVariant *std_gvar_samplerates(const uint64_t samplerates[], unsigned int n);
+SR_PRIV GVariant *std_gvar_samplerates_steps(const uint64_t samplerates[], unsigned int n);
+SR_PRIV GVariant *std_gvar_min_max_step(double min, double max, double step);
+SR_PRIV GVariant *std_gvar_min_max_step_array(const double a[3]);
+SR_PRIV GVariant *std_gvar_min_max_step_thresholds(const double dmin, const double dmax, const double dstep);
+
+SR_PRIV GVariant *std_gvar_tuple_u64(uint64_t low, uint64_t high);
+SR_PRIV GVariant *std_gvar_tuple_double(double low, double high);
+
+SR_PRIV GVariant *std_gvar_array_i32(const int32_t a[], unsigned int n);
+SR_PRIV GVariant *std_gvar_array_u32(const uint32_t a[], unsigned int n);
+SR_PRIV GVariant *std_gvar_array_u64(const uint64_t a[], unsigned int n);
+SR_PRIV GVariant *std_gvar_array_str(const char *a[], unsigned int n);
+
+SR_PRIV GVariant *std_gvar_thresholds(const double a[][2], unsigned int n);
+
+SR_PRIV int std_str_idx(GVariant *data, const char *a[], unsigned int n);
+SR_PRIV int std_u64_idx(GVariant *data, const uint64_t a[], unsigned int n);
+SR_PRIV int std_u8_idx(GVariant *data, const uint8_t a[], unsigned int n);
+
+SR_PRIV int std_str_idx_s(const char *s, const char *a[], unsigned int n);
+SR_PRIV int std_u8_idx_s(uint8_t b, const uint8_t a[], unsigned int n);
+
+SR_PRIV int std_u64_tuple_idx(GVariant *data, const uint64_t a[][2], unsigned int n);
+SR_PRIV int std_double_tuple_idx(GVariant *data, const double a[][2], unsigned int n);
+SR_PRIV int std_double_tuple_idx_d0(const double d, const double a[][2], unsigned int n);
+
+SR_PRIV int std_cg_idx(const struct sr_channel_group *cg, struct sr_channel_group *a[], unsigned int n);
+
/*--- resource.c ------------------------------------------------------------*/
SR_PRIV int64_t sr_file_get_size(FILE *file);
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_atod_ascii(const char *str, double *ret);
SR_PRIV int sr_atof_ascii(const char *str, float *ret);
+SR_PRIV GString *sr_hexdump_new(const uint8_t *data, const size_t len);
+SR_PRIV void sr_hexdump_free(GString *s);
+
/*--- soft-trigger.c --------------------------------------------------------*/
struct soft_trigger_logic {
int pre_trigger_fill;
};
+SR_PRIV int logic_channel_unitsize(GSList *channels);
SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
const struct sr_dev_inst *sdi, struct sr_trigger *trigger,
int pre_trigger_samples);
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);
+SR_PRIV gboolean usb_match_manuf_prod(libusb_device *dev,
+ const char *manufacturer, const char *product);
#endif
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/dmm/ms8250d.c ------------------------------------------------*/
+
+#define MS8250D_PACKET_SIZE 18
+
+struct ms8250d_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_ncv, is_min, is_max, is_sign, is_autotimer;
+};
+
+SR_PRIV gboolean sr_ms8250d_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_ms8250d_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+
/*--- hardware/dmm/dtm0660.c ------------------------------------------------*/
#define DTM0660_PACKET_SIZE 15
#define METEX14_PACKET_SIZE 14
struct metex14_info {
+ size_t ch_idx;
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;
+ gboolean is_hertz, is_ohm, is_celsius, is_fahrenheit, is_watt;
+ gboolean is_pico, is_nano, is_micro, is_milli, is_kilo, is_mega;
+ gboolean is_gain, is_decibel, is_power, is_decibel_mw, is_power_factor;
+ gboolean is_hfe, is_unitless, is_logic, is_min, is_max, is_avg;
};
#ifdef HAVE_LIBSERIALPORT
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);
+SR_PRIV gboolean sr_metex14_4packets_valid(const uint8_t *buf);
+SR_PRIV int sr_metex14_4packets_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
/*--- hardware/dmm/rs9lcd.c -------------------------------------------------*/
SR_PRIV int sr_vc870_parse(const uint8_t *buf, float *floatval,
struct sr_datafeed_analog *analog, void *info);
+/*--- hardware/dmm/vc96.c ---------------------------------------------------*/
+
+#define VC96_PACKET_SIZE 13
+
+struct vc96_info {
+ size_t ch_idx;
+ gboolean is_ac, is_dc, is_resistance, is_diode, is_ampere, is_volt;
+ gboolean is_ohm, is_micro, is_milli, is_kilo, is_mega, is_hfe;
+ gboolean is_unitless;
+};
+
+SR_PRIV gboolean sr_vc96_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_vc96_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+
/*--- hardware/lcr/es51919.c ------------------------------------------------*/
SR_PRIV void es51919_serial_clean(void *priv);
SR_PRIV int sr_asycii_parse(const uint8_t *buf, float *floatval,
struct sr_datafeed_analog *analog, void *info);
+/*--- src/dmm/eev121gw.c ----------------------------------------------------*/
+
+#define EEV121GW_PACKET_SIZE 19
+
+enum eev121gw_display {
+ EEV121GW_DISPLAY_MAIN,
+ EEV121GW_DISPLAY_SUB,
+ EEV121GW_DISPLAY_BAR,
+ EEV121GW_DISPLAY_COUNT,
+};
+
+struct eev121gw_info {
+ /* Selected channel. */
+ size_t ch_idx;
+ /*
+ * Measured value, number and sign/overflow flags, scale factor
+ * and significant digits.
+ */
+ uint32_t uint_value;
+ gboolean is_ofl, is_neg;
+ int factor, digits;
+ /* Currently active mode (meter's function). */
+ gboolean is_ac, is_dc, is_voltage, is_current, is_power, is_gain;
+ gboolean is_resistance, is_capacitance, is_diode, is_temperature;
+ gboolean is_continuity, is_frequency, is_period, is_duty_cycle;
+ /* Quantities associated with mode/function. */
+ gboolean is_ampere, is_volt, is_volt_ampere, is_dbm;
+ gboolean is_ohm, is_farad, is_celsius, is_fahrenheit;
+ gboolean is_hertz, is_seconds, is_percent, is_loop_current;
+ gboolean is_unitless, is_logic;
+ /* Other indicators. */
+ gboolean is_min, is_max, is_avg, is_1ms_peak, is_rel, is_hold;
+ gboolean is_low_pass, is_mem, is_bt, is_auto_range, is_test;
+ gboolean is_auto_poweroff, is_low_batt;
+};
+
+extern SR_PRIV const char *eev121gw_channel_formats[];
+SR_PRIV gboolean sr_eev121gw_packet_valid(const uint8_t *buf);
+SR_PRIV int sr_eev121gw_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+SR_PRIV int sr_eev121gw_3displays_parse(const uint8_t *buf, float *floatval,
+ struct sr_datafeed_analog *analog, void *info);
+
/*--- hardware/scale/kern.c -------------------------------------------------*/
struct kern_info {
return SR_OK;
}
+/**
+ * Get the libsigrok log callback routine and callback data.
+ *
+ * @param[out] cb Pointer to a function pointer to receive the log callback
+ * function. Optional, can be NULL.
+ * @param[out] cb_data Pointer to a void pointer to receive the log callback's
+ * additional arguments. Optional, can be NULL.
+ *
+ * @return SR_OK upon success.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_log_callback_get(sr_log_callback *cb, void **cb_data)
+{
+ if (cb)
+ *cb = sr_log_cb;
+ if (cb_data)
+ *cb_data = sr_log_cb_data;
+
+ return SR_OK;
+}
+
static int sr_logv(void *cb_data, int loglevel, const char *format, va_list args)
{
uint64_t elapsed_us, minutes;
/* This specific log callback doesn't need the void pointer data. */
(void)cb_data;
- /* Only output messages of at least the selected loglevel(s). */
- if (loglevel > cur_loglevel)
- return SR_OK;
+ (void)loglevel;
if (cur_loglevel >= LOGLEVEL_TIMESTAMP) {
elapsed_us = g_get_monotonic_time() - sr_log_start_time;
}
g_fprintf(stderr, "%s\n", output);
+ fflush(stderr);
g_free(raw_output);
g_free(output);
int ret;
va_list args;
+ /* Only output messages of at least the selected loglevel(s). */
+ if (loglevel > cur_loglevel)
+ return SR_OK;
+
va_start(args, format);
ret = sr_log_cb(sr_log_cb_data, loglevel, format, args);
va_end(args);
uint8_t slave_addr = modbus->slave_addr;
uint16_t crc;
- result = serial_write_nonblocking(serial, &slave_addr, sizeof(slave_addr));
+ result = serial_write_blocking(serial, &slave_addr, sizeof(slave_addr), 0);
if (result < 0)
- return result;
+ return SR_ERR;
- result = serial_write_nonblocking(serial, buffer, buffer_size);
+ result = serial_write_blocking(serial, buffer, buffer_size, 0);
if (result < 0)
- return result;
+ return SR_ERR;
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));
+ result = serial_write_blocking(serial, &crc, sizeof(crc), 0);
if (result < 0)
- return result;
+ return SR_ERR;
return SR_OK;
}
uint8_t slave_addr;
int ret;
- ret = serial_read_blocking(modbus->serial, &slave_addr, 1, 100);
+ ret = serial_read_blocking(modbus->serial, &slave_addr, 1, 500);
if (ret != 1 || slave_addr != modbus->slave_addr)
- return ret;
+ return SR_ERR;
ret = serial_read_blocking(modbus->serial, function_code, 1, 100);
if (ret != 1)
- return ret;
+ return SR_ERR;
modbus->crc = modbus_serial_rtu_crc(0xFFFF, &slave_addr, sizeof(slave_addr));
modbus->crc = modbus_serial_rtu_crc(modbus->crc, function_code, 1);
ret = serial_read_blocking(modbus->serial, &crc, sizeof(crc), 100);
if (ret != 2)
- return ret;
+ return SR_ERR;
if (crc != modbus->crc) {
sr_err("CRC error (0x%04X vs 0x%04X).", crc, modbus->crc);
#define LOG_PREFIX "output/analog"
+#define BIN_TO_DEC_DIGITS (log(2) / log(10))
+
struct context {
int num_enabled_channels;
GPtrArray *channellist;
{
struct context *ctx;
const struct sr_datafeed_analog *analog;
+ const struct sr_datafeed_meta *meta;
+ const struct sr_config *src;
+ const struct sr_key_info *srci;
struct sr_channel *ch;
GSList *l;
float *fdata;
case SR_DF_FRAME_END:
*out = g_string_new("FRAME-END\n");
break;
+ case SR_DF_META:
+ meta = packet->payload;
+ for (l = meta->config; l; l = l->next) {
+ src = l->data;
+ if (!(srci = sr_key_info_get(SR_KEY_CONFIG, src->key)))
+ return SR_ERR;
+ *out = g_string_sized_new(512);
+ g_string_append(*out, "META ");
+ g_string_append_printf(*out, "%s: ", srci->id);
+ if (srci->datatype == SR_T_BOOL) {
+ g_string_append_printf(*out, "%u",
+ g_variant_get_boolean(src->data));
+ } else if (srci->datatype == SR_T_FLOAT) {
+ g_string_append_printf(*out, "%f",
+ g_variant_get_double(src->data));
+ } else if (srci->datatype == SR_T_UINT64) {
+ g_string_append_printf(*out, "%"
+ G_GUINT64_FORMAT,
+ g_variant_get_uint64(src->data));
+ }
+ g_string_append(*out, "\n");
+ }
+ break;
case SR_DF_ANALOG:
analog = packet->payload;
num_channels = g_slist_length(analog->meaning->channels);
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;
- }
+ if (ctx->digits == DIGITS_ALL)
+ digits = analog->encoding->digits;
+ else
+ digits = analog->spec->spec_digits;
+ if (!analog->encoding->is_digits_decimal)
+ digits = copysign(ceil(abs(digits) * BIN_TO_DEC_DIGITS), digits);
gboolean si_friendly = sr_analog_si_prefix_friendly(analog->meaning->unit);
sr_analog_unit_to_string(analog, &suffix);
for (i = 0; i < analog->num_samples; i++) {
SR_PRIV struct sr_output_module output_analog = {
.id = "analog",
.name = "Analog",
- .desc = "Analog data and types",
+ .desc = "ASCII analog data values and units",
.exts = NULL,
.flags = 0,
.options = get_options,
g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
g_string_append_c(*out, '\n');
if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
- offset = ctx->trigger + ctx->trigger / 8;
+ /*
+ * Sample data lines have one character per bit and
+ * no separator between bytes. Align trigger marker
+ * to this layout.
+ */
+ offset = ctx->trigger;
g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
ctx->trigger = -1;
}
SR_PRIV struct sr_output_module output_ascii = {
.id = "ascii",
.name = "ASCII",
- .desc = "ASCII art",
+ .desc = "ASCII art logic data",
.exts = (const char*[]){"txt", NULL},
.flags = 0,
.options = get_options,
SR_PRIV struct sr_output_module output_binary = {
.id = "binary",
.name = "Binary",
- .desc = "Raw binary",
+ .desc = "Raw binary logic data",
.exts = NULL,
.flags = 0,
.options = NULL,
g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
g_string_append_c(*out, '\n');
if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
+ /*
+ * Sample data lines have one character per bit,
+ * plus one separator per byte. Align trigger marker
+ * to this layout.
+ */
offset = ctx->trigger + ctx->trigger / 8;
g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
ctx->trigger = -1;
SR_PRIV struct sr_output_module output_bits = {
.id = "bits",
.name = "Bits",
- .desc = "0/1 digits",
+ .desc = "0/1 digits logic data",
.exts = (const char*[]){"txt", NULL},
.flags = 0,
.options = get_options,
SR_PRIV struct sr_output_module output_chronovu_la8 = {
.id = "chronovu-la8",
.name = "ChronoVu LA8",
- .desc = "ChronoVu LA8 native file format",
+ .desc = "ChronoVu LA8 native file format data",
.exts = (const char*[]){"kdt", NULL},
.flags = 0,
.options = NULL,
* trigger: Whether or not to add a "trigger" column as the last column.
* Defaults to FALSE.
*
- * dedup: Don't output duplicate rows. Defaults to TRUE. If time is off, then
+ * dedup: Don't output duplicate rows. Defaults to FALSE. If time is off, then
* this is forced to be off.
*/
-#include <math.h>
#include <config.h>
+#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
struct sr_channel *ch;
GVariant *gvar;
GString *header;
- GSList *l;
+ GSList *channels, *l;
unsigned int num_channels, i;
uint64_t samplerate = 0, sr;
char *samplerate_s;
ctx->title, ctime(&hdr->starttime.tv_sec));
/* Columns / channels */
- num_channels = g_slist_length(o->sdi->channels);
+ channels = o->sdi ? o->sdi->channels : NULL;
+ num_channels = g_slist_length(channels);
g_string_append_printf(header, "%s Channels (%d/%d):",
ctx->comment, ctx->num_analog_channels +
ctx->num_logic_channels, num_channels);
- for (l = o->sdi->channels; l; l = l->next) {
+ for (l = channels; l; l = l->next) {
ch = l->data;
if (ch->enabled)
g_string_append_printf(header, " %s,", ch->name);
}
- if (o->sdi->channels)
+ if (channels) {
/* Drop last separator. */
g_string_truncate(header, header->len - 1);
+ }
g_string_append_printf(header, "\n");
if (samplerate != 0) {
samplerate_s = sr_samplerate_string(samplerate);
const struct sr_datafeed_analog *analog)
{
int ret;
- unsigned int i, j, c, num_channels;
+ size_t num_rcvd_ch, num_have_ch;
+ size_t idx_have, idx_smpl, idx_rcvd;
+ size_t idx_send;
struct sr_analog_meaning *meaning;
GSList *l;
float *fdata = NULL;
+ struct sr_channel *ch;
if (!ctx->analog_samples) {
ctx->analog_samples = g_malloc(analog->num_samples
ctx->num_samples, analog->num_samples);
meaning = analog->meaning;
- num_channels = g_slist_length(meaning->channels);
- ctx->channels_seen += num_channels;
- sr_dbg("Processing packet of %u analog channels", num_channels);
- fdata = g_malloc(analog->num_samples * num_channels * sizeof(float));
+ num_rcvd_ch = g_slist_length(meaning->channels);
+ ctx->channels_seen += num_rcvd_ch;
+ sr_dbg("Processing packet of %zu analog channels", num_rcvd_ch);
+ fdata = g_malloc(analog->num_samples * num_rcvd_ch * sizeof(float));
if ((ret = sr_analog_to_float(analog, fdata)) != SR_OK)
sr_warn("Problems converting data to floating point values.");
- for (i = 0; i < ctx->num_analog_channels + ctx->num_logic_channels; i++) {
- if (ctx->channels[i].ch->type == SR_CHANNEL_ANALOG) {
- sr_dbg("Looking for channel %s",
- ctx->channels[i].ch->name);
- for (l = meaning->channels, c = 0; l; l = l->next, c++) {
- struct sr_channel *ch = l->data;
- sr_dbg("Checking %s", ch->name);
- if (ctx->channels[i].ch == l->data) {
- if (ctx->label_do && !ctx->label_names) {
- sr_analog_unit_to_string(analog,
- &ctx->channels[i].label);
- }
- for (j = 0; j < analog->num_samples; j++)
- ctx->analog_samples[j * ctx->num_analog_channels + i] = fdata[j * num_channels + c];
- break;
- }
+ num_have_ch = ctx->num_analog_channels + ctx->num_logic_channels;
+ idx_send = 0;
+ for (idx_have = 0; idx_have < num_have_ch; idx_have++) {
+ if (ctx->channels[idx_have].ch->type != SR_CHANNEL_ANALOG)
+ continue;
+ sr_dbg("Looking for channel %s",
+ ctx->channels[idx_have].ch->name);
+ for (l = meaning->channels, idx_rcvd = 0; l; l = l->next, idx_rcvd++) {
+ ch = l->data;
+ sr_dbg("Checking %s", ch->name);
+ if (ctx->channels[idx_have].ch != ch)
+ continue;
+ if (ctx->label_do && !ctx->label_names) {
+ sr_analog_unit_to_string(analog,
+ &ctx->channels[idx_have].label);
}
+ for (idx_smpl = 0; idx_smpl < analog->num_samples; idx_smpl++)
+ ctx->analog_samples[idx_smpl * ctx->num_analog_channels + idx_send] = fdata[idx_smpl * num_rcvd_ch + idx_rcvd];
+ break;
}
+ idx_send++;
}
g_free(fdata);
}
break;
case SR_DF_FRAME_BEGIN:
*out = g_string_new(ctx->frame);
- /* And then fall through to... */
+ /* Fallthrough */
case SR_DF_END:
/* Got to end of frame/session with part of the data. */
if (ctx->channels_seen)
{"scale", "scale", "Scale gnuplot graphs", NULL, NULL},
{"value", "Value separator", "Character to print between values", NULL, NULL},
{"record", "Record separator", "String to print between records", NULL, NULL},
- {"frame", "Frame seperator", "String to print between frames", NULL, NULL},
+ {"frame", "Frame separator", "String to print between frames", NULL, NULL},
{"comment", "Comment start string", "String used at start of comment lines", NULL, NULL},
{"header", "Output header", "Output header comment with capture metdata", NULL, NULL},
{"label", "Label values", "Type of column labels", NULL, NULL},
static const struct sr_option *get_options(void)
{
+ GSList *l = NULL;
+
if (!options[0].def) {
options[0].def = g_variant_ref_sink(g_variant_new_string(""));
options[1].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
options[5].def = g_variant_ref_sink(g_variant_new_string(";"));
options[6].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
options[7].def = g_variant_ref_sink(g_variant_new_string("units"));
+ l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("units")));
+ l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("channel")));
+ l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("off")));
+ options[7].values = l;
options[8].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
- options[10].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
+ options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
}
return options;
/* Flush line buffers. */
g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
g_string_append_c(*out, '\n');
- if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
- offset = ctx->trigger + ctx->trigger / 8;
+ if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
+ /*
+ * Sample data lines have one character per nibble,
+ * plus one separator per byte. Align trigger marker
+ * to this layout.
+ */
+ offset = ctx->trigger / 4 + ctx->trigger / 8;
g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
ctx->trigger = -1;
}
SR_PRIV struct sr_output_module output_hex = {
.id = "hex",
.name = "Hexadecimal",
- .desc = "Hexadecimal digits",
+ .desc = "Hexadecimal digits logic data",
.exts = (const char*[]){"txt", NULL},
.flags = 0,
.options = get_options,
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2018 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "output/null"
+
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
+ GString **out)
+{
+ (void)o;
+ (void)packet;
+
+ *out = NULL;
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_output_module output_null = {
+ .id = "null",
+ .name = "Null output",
+ .desc = "Null output (discards all data)",
+ .exts = NULL,
+ .flags = 0,
+ .options = NULL,
+ .receive = receive,
+};
SR_PRIV struct sr_output_module output_ols = {
.id = "ols",
.name = "OLS",
- .desc = "OpenBench Logic Sniffer",
+ .desc = "OpenBench Logic Sniffer data",
.exts = (const char*[]){"ols", NULL},
.flags = 0,
.options = NULL,
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;
+extern SR_PRIV struct sr_output_module output_null;
/* @endcond */
static const struct sr_output_module *output_module_list[] = {
&output_analog,
&output_srzip,
&output_wav,
+ &output_null,
NULL,
};
if (!ch->enabled)
continue;
+ s = NULL;
switch (ch->type) {
case SR_CHANNEL_LOGIC:
s = g_strdup_printf("probe%d", ch->index + 1);
index++;
break;
}
- g_key_file_set_string(meta, devgroup, s, ch->name);
- g_free(s);
+ if (s) {
+ g_key_file_set_string(meta, devgroup, s, ch->name);
+ g_free(s);
+ }
}
metabuf = g_key_file_to_data(meta, &metalen, NULL);
analogsrc = zip_source_buffer(archive, chunkbuf, chunksize, FALSE);
chunkname = g_strdup_printf("%s-%u", basename, next_chunk_num);
i = zip_add(archive, chunkname, analogsrc);
- g_free(chunkname);
if (i < 0) {
sr_err("Failed to add chunk '%s': %s", chunkname, zip_strerror(archive));
+ g_free(chunkname);
zip_source_free(analogsrc);
goto err_free_chunkbuf;
}
+ g_free(chunkname);
if (zip_close(archive) < 0) {
sr_err("Error saving session file: %s", zip_strerror(archive));
goto err_free_chunkbuf;
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;
}
struct out_context *outc;
outc = o->priv;
- g_variant_unref(options[0].def);
g_free(outc->analog_index_map);
g_free(outc->filename);
g_free(outc);
SR_PRIV struct sr_output_module output_srzip = {
.id = "srzip",
.name = "srzip",
- .desc = "srzip session file",
+ .desc = "srzip session file format data",
.exts = (const char*[]){"sr", NULL},
.flags = SR_OUTPUT_INTERNAL_IO_HANDLING,
.options = get_options,
/* timestamp */
t = time(NULL);
timestamp = g_strdup(ctime(&t));
- timestamp[strlen(timestamp)-1] = 0;
+ timestamp[strlen(timestamp) - 1] = 0;
g_string_printf(header, "$date %s $end\n", timestamp);
g_free(timestamp);
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++) {
+ for (i = 0, l = o->sdi->channels; l; l = l->next) {
ch = l->data;
if (ch->type != SR_CHANNEL_LOGIC)
continue;
continue;
g_string_append_printf(header, "$var wire 1 %c %s $end\n",
(char)('!' + i), ch->name);
+ i++;
}
g_string_append(header, "$upscope $end\n$enddefinitions $end\n");
timestamp_written = FALSE;
for (p = 0; p < ctx->num_enabled_channels; p++) {
- index = ctx->channel_index[p];
+ /*
+ * TODO Check whether the mapping from
+ * data image positions to channel numbers
+ * is required. Experiments suggest that
+ * the data image "is dense", and packs
+ * bits of enabled channels, and leaves no
+ * room for positions of disabled channels.
+ */
+ /* index = ctx->channel_index[p]; */
+ index = p;
curbit = ((unsigned)sample[index / 8]
>> (index % 8)) & 1;
struct sr_output_module output_vcd = {
.id = "vcd",
.name = "VCD",
- .desc = "Value Change Dump",
+ .desc = "Value Change Dump data",
.exts = (const char*[]){"vcd", NULL},
.flags = 0,
.options = NULL,
*/
static void float_to_le(uint8_t *buf, float value)
{
- char *old;
+ uint8_t *old;
- old = (char *)&value;
+ old = (uint8_t *)&value;
#ifdef WORDS_BIGENDIAN
buf[0] = old[3];
buf[1] = old[2];
/* Nothing in all the buffers yet. */
size = -1;
break;
- } else
+ } 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;
if (!outc->header_done) {
*out = gen_header(o);
outc->header_done = TRUE;
- } else
+ } else {
*out = g_string_sized_new(512);
+ }
analog = packet->payload;
num_samples = analog->num_samples;
idx = chan_idx[j];
buf = outc->chanbuf[idx] + outc->chanbuf_used[idx]++ * 4;
f = data[i * num_channels + j];
- if (outc->scale != 0.0)
+ if (outc->scale != 1.0)
f /= outc->scale;
float_to_le(buf, f);
}
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));
+ options[0].def = g_variant_ref_sink(g_variant_new_double(1.0));
return options;
}
SR_PRIV struct sr_output_module output_wav = {
.id = "wav",
.name = "WAV",
- .desc = "Microsoft WAV file format",
+ .desc = "Microsoft WAV file format data",
.exts = (const char*[]){"wav", NULL},
.flags = 0,
.options = get_options,
* Access to resource files.
*/
-/** Retrieve the size of the open stream @a file.
+/**
+ * Get a list of paths where we look for resource (e.g. firmware) files.
+ *
+ * @param res_type The type of resource to get the search paths for.
+ *
+ * @return List of strings that must be freed after use, including the strings.
+ *
+ * @since 0.6.0
+ */
+SR_API GSList *sr_resourcepaths_get(int res_type)
+{
+ const char *subdir = NULL;
+ GSList *l = NULL;
+ const char *env;
+ const char *const *datadirs;
+
+ if (res_type == SR_RESOURCE_FIRMWARE) {
+ subdir = "sigrok-firmware";
+
+ env = g_getenv("SIGROK_FIRMWARE_DIR");
+ if (env)
+ l = g_slist_append(l, g_strdup(env));
+ }
+
+ l = g_slist_append(l, g_build_filename(g_get_user_data_dir(), subdir, NULL));
+
+#ifdef FIRMWARE_DIR
+ if (res_type == SR_RESOURCE_FIRMWARE) {
+ /*
+ * Scan the hard-coded directory before the system directories to
+ * avoid picking up possibly outdated files from a system install.
+ */
+ l = g_slist_append(l, g_strdup(FIRMWARE_DIR));
+ }
+#endif
+
+ datadirs = g_get_system_data_dirs();
+ while (*datadirs)
+ l = g_slist_append(l, g_build_filename(*datadirs++, subdir, NULL));
+
+ return l;
+}
+
+/**
+ * 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.
char *filename;
FILE *file;
- filename = g_build_filename(datadir, subdir, name, NULL);
+ if (subdir)
+ filename = g_build_filename(datadir, subdir, name, NULL);
+ else
+ filename = g_build_filename(datadir, name, NULL);
+
file = g_fopen(filename, "rb");
if (file)
static int resource_open_default(struct sr_resource *res,
const char *name, void *cb_data)
{
+ GSList *paths, *p = NULL;
int64_t filesize;
-#ifdef FIRMWARE_DIR
- const char *builtindir;
-#endif
- const char *subdir;
- const char *const *datadirs;
- FILE *file;
+ FILE *file = NULL;
(void)cb_data;
- switch (res->type) {
- case SR_RESOURCE_FIRMWARE:
-#ifdef FIRMWARE_DIR
- builtindir = FIRMWARE_DIR;
-#endif
- subdir = "sigrok-firmware";
- break;
- default:
+ paths = sr_resourcepaths_get(res->type);
+
+ /* Currently, the enum only defines SR_RESOURCE_FIRMWARE. */
+ if (res->type != SR_RESOURCE_FIRMWARE) {
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);
+ p = paths;
+ while (p && !file) {
+ file = try_open_file((const char *)(p->data), NULL, name);
+ p = p->next;
}
+ g_slist_free_full(paths, g_free);
+
if (!file) {
sr_dbg("Failed to locate '%s'.", name);
return SR_ERR;
void *priv;
/* Only used for quirk workarounds, notably the Rigol DS1000 series. */
uint64_t firmware_version;
+ GMutex scpi_mutex;
+ char *actual_channel_name;
};
SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options,
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,
+SR_PRIV const char *sr_scpi_cmd_get(const struct scpi_command *cmdtable,
+ int command);
+SR_PRIV int sr_scpi_cmd(const struct sr_dev_inst *sdi,
const struct scpi_command *cmdtable,
+ int channel_command, const char *channel_name,
+ int command, ...);
+SR_PRIV int sr_scpi_cmd_resp(const struct sr_dev_inst *sdi,
+ const struct scpi_command *cmdtable,
+ int channel_command, const char *channel_name,
GVariant **gvar, const GVariantType *gvtype, int command, ...);
#endif
+++ /dev/null
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <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;
-}
* This file is part of the libsigrok project.
*
* Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
+ * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#define SCPI_READ_RETRIES 100
#define SCPI_READ_RETRY_TIMEOUT_US (10 * 1000)
+static const char *scpi_vendors[][2] = {
+ { "HEWLETT-PACKARD", "HP" },
+ { "Agilent Technologies", "Agilent" },
+ { "RIGOL TECHNOLOGIES", "Rigol" },
+ { "PHILIPS", "Philips" },
+ { "CHROMA", "Chroma" },
+ { "Chroma ATE", "Chroma" },
+};
+
/**
* Parse a string representation of a boolean-like value into a gboolean.
* Similar to sr_parse_boolstring but rejects strings which do not represent
return sdi;
}
+/**
+ * Send a SCPI command with a variadic argument list without mutex.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ * @param format Format string.
+ * @param args Argument list.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+static int scpi_send_variadic(struct sr_scpi_dev_inst *scpi,
+ const char *format, va_list args)
+{
+ va_list args_copy;
+ char *buf;
+ int len, ret;
+
+ /* Get length of buffer required. */
+ va_copy(args_copy, args);
+ len = sr_vsnprintf_ascii(NULL, 0, format, args_copy);
+ va_end(args_copy);
+
+ /* Allocate buffer and write out command. */
+ buf = g_malloc0(len + 2);
+ sr_vsprintf_ascii(buf, format, args);
+ if (buf[len - 1] != '\n')
+ buf[len] = '\n';
+
+ /* Send command. */
+ ret = scpi->send(scpi->priv, buf);
+
+ /* Free command buffer. */
+ g_free(buf);
+
+ return ret;
+}
+
+/**
+ * Send a SCPI command without mutex.
+ *
+ * @param scpi Previously initialized SCPI device structure.
+ * @param format Format string, to be followed by any necessary arguments.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+static int scpi_send(struct sr_scpi_dev_inst *scpi, const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, format);
+ ret = scpi_send_variadic(scpi, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/**
+ * Send data to SCPI device without mutex.
+ *
+ * TODO: This is only implemented in TcpRaw, but never used.
+ * TODO: Use Mutex at all?
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param buf Buffer with data to send.
+ * @param len Number of bytes to send.
+ *
+ * @return Number of bytes read, or SR_ERR upon failure.
+ */
+static int scpi_write_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen)
+{
+ return scpi->write_data(scpi->priv, buf, maxlen);
+}
+
+/**
+ * Read part of a response from SCPI device without mutex.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param buf Buffer to store result.
+ * @param maxlen Maximum number of bytes to read.
+ *
+ * @return Number of bytes read, or SR_ERR upon failure.
+ */
+static int scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen)
+{
+ return scpi->read_data(scpi->priv, buf, maxlen);
+}
+
+/**
+ * Do a non-blocking read of up to the allocated length, and
+ * check if a timeout has occured, without mutex.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param response Buffer to which the response is appended.
+ * @param abs_timeout_us Absolute timeout in microseconds
+ *
+ * @return read length on success, SR_ERR* on failure.
+ */
+static int scpi_read_response(struct sr_scpi_dev_inst *scpi,
+ GString *response, gint64 abs_timeout_us)
+{
+ int len, space;
+
+ space = response->allocated_len - response->len;
+ len = scpi->read_data(scpi->priv, &response->str[response->len], space);
+
+ if (len < 0) {
+ sr_err("Incompletely read SCPI response.");
+ return SR_ERR;
+ }
+
+ if (len > 0) {
+ g_string_set_size(response, response->len + len);
+ return len;
+ }
+
+ if (g_get_monotonic_time() > abs_timeout_us) {
+ sr_err("Timed out waiting for SCPI response.");
+ return SR_ERR_TIMEOUT;
+ }
+
+ return 0;
+}
+
+/**
+ * Send a SCPI command, receive the reply and store the reply in
+ * scpi_response, without mutex.
+ *
+ * @param scpi Previously initialised SCPI device structure.
+ * @param command The SCPI command to send to the device.
+ * @param scpi_response Pointer where to store the SCPI response.
+ *
+ * @return SR_OK on success, SR_ERR on failure.
+ */
+static int scpi_get_data(struct sr_scpi_dev_inst *scpi,
+ const char *command, GString **scpi_response)
+{
+ int ret;
+ GString *response;
+ int space;
+ gint64 timeout;
+
+ /* Optionally send caller provided command. */
+ if (command) {
+ if (scpi_send(scpi, command) != SR_OK)
+ return SR_ERR;
+ }
+
+ /* Initiate SCPI read operation. */
+ if (sr_scpi_read_begin(scpi) != SR_OK)
+ return SR_ERR;
+
+ /* Keep reading until completion or until timeout. */
+ timeout = g_get_monotonic_time() + scpi->read_timeout_us;
+
+ response = *scpi_response;
+
+ while (!sr_scpi_read_complete(scpi)) {
+ /* Resize the buffer when free space drops below a threshold. */
+ space = response->allocated_len - response->len;
+ if (space < 128) {
+ int oldlen = response->len;
+ g_string_set_size(response, oldlen + 1024);
+ g_string_set_size(response, oldlen);
+ }
+
+ /* Read another chunk of the response. */
+ ret = scpi_read_response(scpi, response, timeout);
+
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ timeout = g_get_monotonic_time() + scpi->read_timeout_us;
+ }
+
+ return SR_OK;
+}
+
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 int sr_scpi_open(struct sr_scpi_dev_inst *scpi)
{
+ g_mutex_init(&scpi->scpi_mutex);
+
return scpi->open(scpi);
}
int ret;
va_start(args, format);
- ret = sr_scpi_send_variadic(scpi, format, args);
+ g_mutex_lock(&scpi->scpi_mutex);
+ ret = scpi_send_variadic(scpi, format, args);
+ g_mutex_unlock(&scpi->scpi_mutex);
va_end(args);
return ret;
SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi,
const char *format, va_list args)
{
- va_list args_copy;
- char *buf;
- int len, ret;
-
- /* Get length of buffer required. */
- va_copy(args_copy, args);
- len = vsnprintf(NULL, 0, format, args_copy);
- va_end(args_copy);
-
- /* Allocate buffer and write out command. */
- buf = g_malloc0(len + 2);
- vsprintf(buf, format, args);
- if (buf[len - 1] != '\n')
- buf[len] = '\n';
-
- /* Send command. */
- ret = scpi->send(scpi->priv, buf);
+ int ret;
- /* Free command buffer. */
- g_free(buf);
+ g_mutex_lock(&scpi->scpi_mutex);
+ ret = scpi_send_variadic(scpi, format, args);
+ g_mutex_unlock(&scpi->scpi_mutex);
return ret;
}
SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi,
char *buf, int maxlen)
{
- return scpi->read_data(scpi->priv, buf, maxlen);
+ int ret;
+
+ g_mutex_lock(&scpi->scpi_mutex);
+ ret = scpi_read_data(scpi, buf, maxlen);
+ g_mutex_unlock(&scpi->scpi_mutex);
+
+ return ret;
}
/**
* Send data to SCPI device.
*
+ * TODO: This is only implemented in TcpRaw, but never used.
+ * TODO: Use Mutex at all?
+ *
* @param scpi Previously initialised SCPI device structure.
* @param buf Buffer with data to send.
* @param len Number of bytes to send.
SR_PRIV int sr_scpi_write_data(struct sr_scpi_dev_inst *scpi,
char *buf, int maxlen)
{
- return scpi->write_data(scpi->priv, buf, maxlen);
+ int ret;
+
+ g_mutex_lock(&scpi->scpi_mutex);
+ ret = scpi_write_data(scpi, buf, maxlen);
+ g_mutex_unlock(&scpi->scpi_mutex);
+
+ return ret;
}
/**
*/
SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi)
{
- return scpi->close(scpi);
+ int ret;
+
+ g_mutex_lock(&scpi->scpi_mutex);
+ ret = scpi->close(scpi);
+ g_mutex_unlock(&scpi->scpi_mutex);
+ g_mutex_clear(&scpi->scpi_mutex);
+
+ return ret;
}
/**
scpi->free(scpi->priv);
g_free(scpi->priv);
+ g_free(scpi->actual_channel_name);
g_free(scpi);
}
SR_PRIV int sr_scpi_read_response(struct sr_scpi_dev_inst *scpi,
GString *response, gint64 abs_timeout_us)
{
- int len, space;
-
- space = response->allocated_len - response->len;
- len = sr_scpi_read_data(scpi, &response->str[response->len], space);
-
- if (len < 0) {
- sr_err("Incompletely read SCPI response.");
- return SR_ERR;
- }
-
- if (len > 0) {
- g_string_set_size(response, response->len + len);
- return len;
- }
+ int ret;
- if (g_get_monotonic_time() > abs_timeout_us) {
- sr_err("Timed out waiting for SCPI response.");
- return SR_ERR_TIMEOUT;
- }
+ g_mutex_lock(&scpi->scpi_mutex);
+ ret = scpi_read_response(scpi, response, abs_timeout_us);
+ g_mutex_unlock(&scpi->scpi_mutex);
- return 0;
+ return ret;
}
SR_PRIV int sr_scpi_get_data(struct sr_scpi_dev_inst *scpi,
const char *command, GString **scpi_response)
{
int ret;
- GString *response;
- int space;
- gint64 timeout;
- /* Optionally send caller provided command. */
- if (command) {
- if (sr_scpi_send(scpi, command) != SR_OK)
- return SR_ERR;
- }
-
- /* Initiate SCPI read operation. */
- if (sr_scpi_read_begin(scpi) != SR_OK)
- return SR_ERR;
-
- /* Keep reading until completion or until timeout. */
- timeout = g_get_monotonic_time() + scpi->read_timeout_us;
-
- response = *scpi_response;
+ g_mutex_lock(&scpi->scpi_mutex);
+ ret = scpi_get_data(scpi, command, scpi_response);
+ g_mutex_unlock(&scpi->scpi_mutex);
- while (!sr_scpi_read_complete(scpi)) {
- /* Resize the buffer when free space drops below a threshold. */
- space = response->allocated_len - response->len;
- if (space < 128) {
- int oldlen = response->len;
- g_string_set_size(response, oldlen + 1024);
- g_string_set_size(response, oldlen);
- }
-
- /* Read another chunk of the response. */
- ret = sr_scpi_read_response(scpi, response, timeout);
-
- if (ret < 0)
- return ret;
- if (ret > 0)
- timeout = g_get_monotonic_time() + scpi->read_timeout_us;
- }
-
- return SR_OK;
+ return ret;
}
/**
if (ret != SR_OK && !response)
return ret;
- if (sr_atod(response, scpi_response) == SR_OK)
+ if (sr_atod_ascii(response, scpi_response) == SR_OK)
ret = SR_OK;
else
ret = SR_ERR_DATA;
gboolean opc;
for (i = 0; i < SCPI_READ_RETRIES; i++) {
+ opc = FALSE;
sr_scpi_get_bool(scpi, SCPI_CMD_OPC, &opc);
if (opc)
return SR_OK;
long datalen;
gint64 timeout;
+ g_mutex_lock(&scpi->scpi_mutex);
+
if (command)
- if (sr_scpi_send(scpi, command) != SR_OK)
+ if (scpi_send(scpi, command) != SR_OK) {
+ g_mutex_unlock(&scpi->scpi_mutex);
return SR_ERR;
+ }
- if (sr_scpi_read_begin(scpi) != SR_OK)
+ if (sr_scpi_read_begin(scpi) != SR_OK) {
+ g_mutex_unlock(&scpi->scpi_mutex);
return SR_ERR;
+ }
/*
* Assume an initial maximum length, optionally gets adjusted below.
/* Get (the first chunk of) the response. */
while (response->len < 2) {
- ret = sr_scpi_read_response(scpi, response, timeout);
+ ret = scpi_read_response(scpi, response, timeout);
if (ret < 0) {
+ g_mutex_unlock(&scpi->scpi_mutex);
g_string_free(response, TRUE);
return ret;
}
* the input buffer, leaving just the data bytes.
*/
if (response->str[0] != '#') {
+ g_mutex_unlock(&scpi->scpi_mutex);
g_string_free(response, TRUE);
return SR_ERR_DATA;
}
buf[1] = '\0';
ret = sr_atol(buf, &llen);
if ((ret != SR_OK) || (llen == 0)) {
+ g_mutex_unlock(&scpi->scpi_mutex);
g_string_free(response, TRUE);
return ret;
}
while (response->len < (unsigned long)(2 + llen)) {
- ret = sr_scpi_read_response(scpi, response, timeout);
+ ret = scpi_read_response(scpi, response, timeout);
if (ret < 0) {
+ g_mutex_unlock(&scpi->scpi_mutex);
g_string_free(response, TRUE);
return ret;
}
buf[llen] = '\0';
ret = sr_atol(buf, &datalen);
if ((ret != SR_OK) || (datalen == 0)) {
+ g_mutex_unlock(&scpi->scpi_mutex);
g_string_free(response, TRUE);
return ret;
}
}
while (response->len < (unsigned long)(datalen)) {
- ret = sr_scpi_read_response(scpi, response, timeout);
+ ret = scpi_read_response(scpi, response, timeout);
if (ret < 0) {
+ g_mutex_unlock(&scpi->scpi_mutex);
g_string_free(response, TRUE);
return ret;
}
timeout = g_get_monotonic_time() + scpi->read_timeout_us;
}
+ g_mutex_unlock(&scpi->scpi_mutex);
+
/* Convert received data to byte array. */
*scpi_response = g_byte_array_new_take(
(guint8*)g_string_free(response, FALSE), datalen);
char *response;
gchar **tokens;
struct sr_scpi_hw_info *hw_info;
+ gchar *idn_substr;
response = NULL;
tokens = NULL;
if (ret != SR_OK && !response)
return ret;
- sr_info("Got IDN string: '%s'", response);
-
/*
* The response to a '*IDN?' is specified by the SCPI spec. It contains
* a comma-separated list containing the manufacturer name, instrument
g_free(response);
hw_info = g_malloc0(sizeof(struct sr_scpi_hw_info));
- hw_info->manufacturer = g_strstrip(g_strdup(tokens[0]));
+
+ idn_substr = g_strstr_len(tokens[0], -1, "IDN ");
+ if (idn_substr == NULL)
+ hw_info->manufacturer = g_strstrip(g_strdup(tokens[0]));
+ else
+ hw_info->manufacturer = g_strstrip(g_strdup(idn_substr + 4));
+
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_free(hw_info->firmware_version);
g_free(hw_info);
}
+
+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 *sr_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 sr_scpi_cmd(const struct sr_dev_inst *sdi,
+ const struct scpi_command *cmdtable,
+ int channel_command, const char *channel_name,
+ int command, ...)
+{
+ struct sr_scpi_dev_inst *scpi;
+ va_list args;
+ int ret;
+ const char *channel_cmd;
+ const char *cmd;
+
+ scpi = sdi->conn;
+
+ if (!(cmd = sr_scpi_cmd_get(cmdtable, command))) {
+ /* Device does not implement this command, that's OK. */
+ return SR_OK;
+ }
+
+ g_mutex_lock(&scpi->scpi_mutex);
+
+ /* Select channel. */
+ channel_cmd = sr_scpi_cmd_get(cmdtable, channel_command);
+ if (channel_cmd && channel_name &&
+ g_strcmp0(channel_name, scpi->actual_channel_name)) {
+ sr_spew("sr_scpi_cmd(): new channel = %s", channel_name);
+ g_free(scpi->actual_channel_name);
+ scpi->actual_channel_name = g_strdup(channel_name);
+ ret = scpi_send(scpi, channel_cmd, channel_name);
+ if (ret != SR_OK)
+ return ret;
+ }
+
+ va_start(args, command);
+ ret = scpi_send_variadic(scpi, cmd, args);
+ va_end(args);
+
+ g_mutex_unlock(&scpi->scpi_mutex);
+
+ return ret;
+}
+
+SR_PRIV int sr_scpi_cmd_resp(const struct sr_dev_inst *sdi,
+ const struct scpi_command *cmdtable,
+ int channel_command, const char *channel_name,
+ GVariant **gvar, const GVariantType *gvtype, int command, ...)
+{
+ struct sr_scpi_dev_inst *scpi;
+ va_list args;
+ const char *channel_cmd;
+ const char *cmd;
+ GString *response;
+ char *s;
+ gboolean b;
+ double d;
+ int ret;
+
+ scpi = sdi->conn;
+
+ if (!(cmd = sr_scpi_cmd_get(cmdtable, command))) {
+ /* Device does not implement this command. */
+ return SR_ERR_NA;
+ }
+
+ g_mutex_lock(&scpi->scpi_mutex);
+
+ /* Select channel. */
+ channel_cmd = sr_scpi_cmd_get(cmdtable, channel_command);
+ if (channel_cmd && channel_name &&
+ g_strcmp0(channel_name, scpi->actual_channel_name)) {
+ sr_spew("sr_scpi_cmd_get(): new channel = %s", channel_name);
+ g_free(scpi->actual_channel_name);
+ scpi->actual_channel_name = g_strdup(channel_name);
+ ret = scpi_send(scpi, channel_cmd, channel_name);
+ if (ret != SR_OK)
+ return ret;
+ }
+
+ va_start(args, command);
+ ret = scpi_send_variadic(scpi, cmd, args);
+ va_end(args);
+ if (ret != SR_OK) {
+ g_mutex_unlock(&scpi->scpi_mutex);
+ return ret;
+ }
+
+ response = g_string_sized_new(1024);
+ ret = scpi_get_data(scpi, NULL, &response);
+ if (ret != SR_OK) {
+ g_mutex_unlock(&scpi->scpi_mutex);
+ if (response)
+ g_string_free(response, TRUE);
+ return ret;
+ }
+
+ g_mutex_unlock(&scpi->scpi_mutex);
+
+ /* Get rid of trailing linefeed if present */
+ if (response->len >= 1 && response->str[response->len - 1] == '\n')
+ g_string_truncate(response, response->len - 1);
+
+ /* 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);
+
+ s = g_string_free(response, FALSE);
+
+ ret = SR_OK;
+ if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_BOOLEAN)) {
+ if ((ret = parse_strict_bool(s, &b)) == SR_OK)
+ *gvar = g_variant_new_boolean(b);
+ } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_DOUBLE)) {
+ if ((ret = sr_atod_ascii(s, &d)) == SR_OK)
+ *gvar = g_variant_new_double(d);
+ } else if (g_variant_type_equal(gvtype, G_VARIANT_TYPE_STRING)) {
+ *gvar = g_variant_new_string(s);
+ } else {
+ sr_err("Unable to convert to desired GVariant type.");
+ ret = SR_ERR_NA;
+ }
+
+ g_free(s);
+
+ return ret;
+}
if (ibsta & ERR)
{
- sr_err("Error while reading SCPI response: iberr = %s.",
- gpib_error_string(iberr));
+ sr_err("Error while reading SCPI response: "
+ "iberr = %s, ibsta = %d.",
+ gpib_error_string(iberr), ibsta);
return SR_ERR;
}
static int scpi_serial_send(void *priv, const char *command)
{
- int len, result, written;
+ int result;
struct scpi_serial *sscpi = priv;
struct sr_serial_dev_inst *serial = sscpi->serial;
- len = strlen(command);
- written = 0;
- while (written < len) {
- result = serial_write_nonblocking(serial,
- command + written, len - written);
- if (result < 0) {
- sr_err("Error while sending SCPI command: '%s'.", command);
- return SR_ERR;
- }
- written += result;
+ result = serial_write_blocking(serial, command, strlen(command), 0);
+ if (result < 0) {
+ sr_err("Error while sending SCPI command '%s': %d.",
+ command, result);
+ return SR_ERR;
}
sr_spew("Successfully sent SCPI command: '%s'.", command);
return ret;
if (ret > 0) {
- sr_spew("Read %d bytes into buffer.", ret);
-
if (buf[ret - 1] == '\n') {
sscpi->got_newline = TRUE;
sr_spew("Received terminator");
static struct usbtmc_blacklist blacklist_remote[] = {
{ 0x1ab1, 0x0588 }, /* Rigol DS1000 series */
{ 0x1ab1, 0x04b0 }, /* Rigol DS2000 series */
+ { 0x1ab1, 0x04b1 }, /* Rigol DS4000 series */
{ 0x0957, 0x0588 }, /* Agilent DSO1000 series (rebadged Rigol DS1000) */
{ 0x0b21, 0xffff }, /* All Yokogawa devices */
+ { 0xf4ec, 0xffff }, /* All Siglent SDS devices */
+ ALL_ZERO
+};
+
+/* Devices that shall get reset during open(). */
+static struct usbtmc_blacklist whitelist_usb_reset[] = {
+ { 0xf4ec, 0xffff }, /* All Siglent SDS devices */
ALL_ZERO
};
for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
if ((ret = libusb_get_config_descriptor(devlist[i], confidx, &confdes)) < 0) {
- sr_dbg("Failed to get configuration descriptor: %s, "
- "ignoring device.", libusb_error_name(ret));
+ if (ret != LIBUSB_ERROR_NOT_FOUND)
+ sr_dbg("Failed to get configuration descriptor: %s, "
+ "ignoring device.", libusb_error_name(ret));
break;
}
for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
}
libusb_free_device_list(devlist, 1);
- sr_dbg("Found %d device(s).", g_slist_length(resources));
+ /* No log message for #devices found (caller will log that). */
return resources;
}
int confidx, intfidx, epidx, config = 0, current_config;
uint8_t capabilities[24];
int ret, found = 0;
+ int do_reset;
if (usb->devhdl)
return SR_OK;
for (confidx = 0; confidx < des.bNumConfigurations; confidx++) {
if ((ret = libusb_get_config_descriptor(dev, confidx, &confdes)) < 0) {
- sr_dbg("Failed to get configuration descriptor: %s, "
- "ignoring device.", libusb_error_name(ret));
+ if (ret != LIBUSB_ERROR_NOT_FOUND)
+ sr_dbg("Failed to get configuration descriptor: %s, "
+ "ignoring device.", libusb_error_name(ret));
continue;
}
for (intfidx = 0; intfidx < confdes->bNumInterfaces; intfidx++) {
return SR_ERR;
}
+ /* Optionally reset the USB device. */
+ do_reset = check_usbtmc_blacklist(whitelist_usb_reset,
+ des.idVendor, des.idProduct);
+ if (do_reset)
+ libusb_reset_device(usb->devhdl);
+
/* Get capabilities. */
ret = libusb_control_transfer(usb->devhdl, LIBUSB_ENDPOINT_IN |
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
#include <libserialport.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
-#ifdef G_OS_WIN32
+#ifdef _WIN32
#include <windows.h> /* for HANDLE */
#endif
time /= 1000;
if ((ibuf - i) >= packet_size) {
+ GString *text;
/* We have at least a packet's worth of data. */
+ text = sr_hexdump_new(&buf[i], packet_size);
+ sr_spew("Trying packet: %s", text->str);
+ sr_hexdump_free(text);
if (is_valid(&buf[i])) {
sr_spew("Found valid %zu-byte packet after "
"%" PRIu64 "ms.", (ibuf - i), time);
}
/** @cond PRIVATE */
-#ifdef G_OS_WIN32
+#ifdef _WIN32
typedef HANDLE event_handle;
#else
typedef int event_handle;
sr_strerror(ret));
return ret;
}
- if ((ret = sdi->driver->dev_acquisition_start(sdi)) != SR_OK) {
+ if ((ret = sr_dev_acquisition_start(sdi)) != SR_OK) {
sr_err("Failed to start acquisition of device in "
"running session (%s)", sr_strerror(ret));
return ret;
ret = SR_ERR;
break;
}
- ret = sdi->driver->dev_acquisition_start(sdi);
+ ret = sr_dev_acquisition_start(sdi);
if (ret != SR_OK) {
sr_err("Could not start %s device %s acquisition.",
sdi->driver->name, sdi->connection_id);
lend = l->next;
for (l = session->devs; l != lend; l = l->next) {
sdi = l->data;
- sdi->driver->dev_acquisition_stop(sdi);
+ sr_dev_acquisition_stop(sdi);
}
/* TODO: Handle delayed stops. Need to iterate the event
* sources... */
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);
+ sr_dev_acquisition_stop(sdi);
}
return G_SOURCE_REMOVE;
/* 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
+#ifdef _WIN32
g_io_channel_win32_make_pollfd(channel, events, &pollfd);
#else
pollfd.fd = g_io_channel_unix_get_fd(channel);
g_memdup(src, sizeof(struct sr_config)));
}
-/** @private */
-SR_PRIV int sr_packet_copy(const struct sr_datafeed_packet *packet,
+SR_API int sr_packet_copy(const struct sr_datafeed_packet *packet,
struct sr_datafeed_packet **copy)
{
const struct sr_datafeed_meta *meta;
case SR_DF_LOGIC:
logic = packet->payload;
logic_copy = g_malloc(sizeof(*logic_copy));
+ if (!logic_copy)
+ return SR_ERR;
logic_copy->length = logic->length;
logic_copy->unitsize = logic->unitsize;
+ logic_copy->data = g_malloc(logic->length * logic->unitsize);
+ if (!logic_copy->data) {
+ g_free(logic_copy);
+ return SR_ERR;
+ }
memcpy(logic_copy->data, logic->data, logic->length * logic->unitsize);
(*copy)->payload = logic_copy;
break;
return SR_OK;
}
-void sr_packet_free(struct sr_datafeed_packet *packet)
+SR_API void sr_packet_free(struct sr_datafeed_packet *packet)
{
const struct sr_datafeed_meta *meta;
const struct sr_datafeed_logic *logic;
sr_err("Unknown packet type %d", packet->type);
}
g_free(packet);
-
}
/** @} */
struct sr_analog_spec spec;
struct zip_stat zs;
int ret, got_data;
- char capturefile[16];
+ char capturefile[128];
void *buf;
got_data = FALSE;
sr_dbg("Opened %s.", vdev->capturefile);
} else {
/* Try as first chunk filename. */
- snprintf(capturefile, 15, "%s-1", vdev->capturefile);
+ snprintf(capturefile, sizeof(capturefile) - 1, "%s-1", vdev->capturefile);
if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
vdev->cur_chunk = 1;
if (!(vdev->capfile = zip_fopen(vdev->archive,
} else {
/* Capture data is chunked, advance to the next chunk. */
vdev->cur_chunk++;
- snprintf(capturefile, 15, "%s-%d", vdev->capturefile,
+ snprintf(capturefile, sizeof(capturefile) - 1, "%s-%d", vdev->capturefile,
vdev->cur_chunk);
if (zip_stat(vdev->archive, capturefile, 0, &zs) != -1) {
if (!(vdev->capfile = zip_fopen(vdev->archive,
ret = zip_fread(vdev->capfile, buf, CHUNKSIZE);
if (ret > 0) {
- got_data = TRUE;
if (vdev->cur_analog_channel != 0) {
+ got_data = TRUE;
packet.type = SR_DF_ANALOG;
packet.payload = &analog;
/* TODO: Use proper 'digits' value for this device (and its modes). */
analog.meaning->unit = SR_UNIT_VOLT;
analog.meaning->mqflags = SR_MQFLAG_DC;
analog.data = (float *) buf;
- } else {
+ } else if (vdev->unitsize) {
+ got_data = TRUE;
if (ret % vdev->unitsize != 0)
sr_warn("Read size %d not a multiple of the"
" unit size %d.", ret, vdev->unitsize);
logic.length = ret;
logic.unitsize = vdev->unitsize;
logic.data = buf;
+ } else {
+ /*
+ * Neither analog data, nor logic which has
+ * unitsize, must be an unexpected API use.
+ */
+ sr_warn("Neither analog nor logic data. Ignoring.");
+ }
+ if (got_data) {
+ vdev->bytes_read += ret;
+ sr_session_send(sdi, &packet);
}
- vdev->bytes_read += ret;
- sr_session_send(sdi, &packet);
} else {
/* done with this capture file */
zip_fclose(vdev->capfile);
/* driver callbacks */
-static int dev_clear(const struct sr_dev_driver *di)
-{
- struct drv_context *drvc;
- GSList *l;
-
- drvc = di->context;
- for (l = drvc->instances; l; l = l->next)
- sr_dev_inst_free(l->data);
- 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;
return SR_OK;
}
-static int config_get(uint32_t 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)
{
struct session_vdev *vdev;
return SR_OK;
}
-static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi,
- const struct sr_channel_group *cg)
+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;
return SR_OK;
}
-static int config_list(uint32_t 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)
{
- (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;
- default:
- return SR_ERR_NA;
- }
-
- return SR_OK;
+ return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, NO_OPTS, devopts);
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
.longname = "Session-emulating driver",
.api_version = 1,
.init = std_init,
- .cleanup = dev_clear,
+ .cleanup = std_cleanup,
.scan = NULL,
.dev_list = NULL,
- .dev_clear = dev_clear,
+ .dev_clear = std_dev_clear,
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
return SR_OK;
}
-
+
/** @private */
SR_PRIV struct sr_dev_inst *sr_session_prepare_sdi(const char *filename, struct sr_session **session)
{
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->driver = &session_driver;
- sdi->status = SR_ST_ACTIVE;
+ sdi->status = SR_ST_INACTIVE;
if (!session_driver_initialized) {
/* first device, init the driver */
session_driver_initialized = 1;
#define LOG_PREFIX "soft-trigger"
/* @endcond */
+SR_PRIV int logic_channel_unitsize(GSList *channels)
+{
+ int number = 0;
+ struct sr_channel *channel;
+ GSList *l;
+
+ for (l = channels; l; l = l->next) {
+ channel = l->data;
+ if (channel->type == SR_CHANNEL_LOGIC)
+ number++;
+ }
+
+ return (number + 7) / 8;
+}
+
SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
const struct sr_dev_inst *sdi, struct sr_trigger *trigger,
int pre_trigger_samples)
stl = g_malloc0(sizeof(struct soft_trigger_logic));
stl->sdi = sdi;
stl->trigger = trigger;
- stl->unitsize = (g_slist_length(sdi->channels) + 7) / 8;
+ stl->unitsize = logic_channel_unitsize(sdi->channels);
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_buffer = g_try_malloc(stl->pre_trigger_size);
+ if (pre_trigger_samples > 0 && !stl->pre_trigger_buffer) {
+ /*
+ * Error out if g_try_malloc() failed (or was invoked as
+ * g_try_malloc(0)) *and* more than 0 pretrigger samples
+ * were requested.
+ */
+ soft_trigger_logic_free(stl);
+ return NULL;
+ }
stl->pre_trigger_head = stl->pre_trigger_buffer;
if (stl->pre_trigger_size > 0 && !stl->pre_trigger_buffer) {
* @internal
*/
+/* Needed for gettimeofday(), at least on FreeBSD. */
+#define _XOPEN_SOURCE 700
+
#include <config.h>
+#include <string.h>
+#include <math.h>
+#include <sys/time.h>
#include <glib.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
#define LOG_PREFIX "std"
+SR_PRIV const uint32_t NO_OPTS[1] = {};
+
/**
- * Standard sr_driver_init() API helper.
+ * Standard driver init() callback API helper.
*
* This function can be used to simplify most driver's init() API callback.
*
- * It creates a new 'struct drv_context' (drvc), assigns sr_ctx to it, and
- * then 'drvc' is assigned to the 'struct sr_dev_driver' (di) that is passed.
+ * Create a new 'struct drv_context' (drvc), assign sr_ctx to it, and
+ * then assign 'drvc' to the 'struct sr_dev_driver' (di) that is passed.
*
- * @param di The driver instance to use.
- * @param sr_ctx The libsigrok context to assign.
+ * @param[in] di The driver instance to use. Must not be NULL.
+ * @param[in] sr_ctx The libsigrok context to assign. May be NULL.
*
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
*/
SR_PRIV int std_init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
{
struct drv_context *drvc;
+ if (!di) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
+ }
+
drvc = g_malloc0(sizeof(struct drv_context));
drvc->sr_ctx = sr_ctx;
drvc->instances = NULL;
}
/**
- * Standard driver cleanup() callback API helper
+ * Standard driver cleanup() callback API helper.
*
- * @param di The driver instance to use.
+ * This function can be used to simplify most driver's cleanup() API callback.
*
- * Frees all device instances by calling sr_dev_clear() and then releases any
+ * Free all device instances by calling sr_dev_clear() and then release any
* resources allocated by std_init().
*
- * @retval SR_OK Success
- * @retval SR_ERR_ARG Invalid driver
+ * @param[in] di The driver instance to use. Must not be NULL.
*
-*/
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
+ */
SR_PRIV int std_cleanup(const struct sr_dev_driver *di)
{
int ret;
+ if (!di) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
+ }
+
ret = sr_dev_clear(di);
g_free(di->context);
return ret;
}
+/**
+ * Dummmy driver dev_open() callback API helper.
+ *
+ * @param[in] sdi The device instance to use. May be NULL (unused).
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_dummy_dev_open(struct sr_dev_inst *sdi)
+{
+ (void)sdi;
+
+ return SR_OK;
+}
+
+/**
+ * Dummmy driver dev_close() callback API helper.
+ *
+ * @param[in] sdi The device instance to use. May be NULL (unused).
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_dummy_dev_close(struct sr_dev_inst *sdi)
+{
+ (void)sdi;
+
+ return SR_OK;
+}
+
+/**
+ * Dummmy driver dev_acquisition_start() callback API helper.
+ *
+ * @param[in] sdi The device instance to use. May be NULL (unused).
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_dummy_dev_acquisition_start(const struct sr_dev_inst *sdi)
+{
+ (void)sdi;
+
+ return SR_OK;
+}
+
+/**
+ * Dummmy driver dev_acquisition_stop() callback API helper.
+ *
+ * @param[in] sdi The device instance to use. May be NULL (unused).
+ *
+ * @retval SR_OK Success.
+ */
+SR_PRIV int std_dummy_dev_acquisition_stop(struct sr_dev_inst *sdi)
+{
+ (void)sdi;
+
+ return SR_OK;
+}
+
/**
* Standard API helper for sending an SR_DF_HEADER packet.
*
- * This function can be used to simplify most driver's
+ * This function can be used to simplify most drivers'
* dev_acquisition_start() API callback.
*
- * @param sdi The device instance to use.
+ * @param[in] sdi The device instance to use. Must not be NULL.
*
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- * SR_ERR upon other errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
*/
SR_PRIV int std_session_send_df_header(const struct sr_dev_inst *sdi)
{
- const char *prefix = (sdi->driver) ? sdi->driver->name : "unknown";
+ const char *prefix;
int ret;
struct sr_datafeed_packet packet;
struct sr_datafeed_header header;
- sr_dbg("%s: Starting acquisition.", prefix);
+ if (!sdi) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
+ }
+
+ prefix = (sdi->driver) ? sdi->driver->name : "unknown";
/* Send header packet to the session bus. */
- sr_dbg("%s: Sending SR_DF_HEADER packet.", prefix);
packet.type = SR_DF_HEADER;
packet.payload = (uint8_t *)&header;
header.feed_version = 1;
gettimeofday(&header.starttime, NULL);
if ((ret = sr_session_send(sdi, &packet)) < 0) {
- sr_err("%s: Failed to send header packet: %d.", prefix, ret);
+ sr_err("%s: Failed to send SR_DF_HEADER packet: %d.", prefix, ret);
return ret;
}
/**
* Standard API helper for sending an SR_DF_END packet.
*
- * @param sdi The device instance to use. Must not be NULL.
+ * This function can be used to simplify most drivers'
+ * dev_acquisition_stop() API callback.
+ *
+ * @param[in] sdi The device instance to use. Must not be NULL.
*
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- * SR_ERR upon other errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
*/
SR_PRIV int std_session_send_df_end(const struct sr_dev_inst *sdi)
{
- const char *prefix = (sdi->driver) ? sdi->driver->name : "unknown";
+ const char *prefix;
int ret;
struct sr_datafeed_packet packet;
- if (!sdi)
+ if (!sdi) {
+ sr_err("%s: Invalid argument.", __func__);
return SR_ERR_ARG;
+ }
- sr_dbg("%s: Sending SR_DF_END packet.", prefix);
+ prefix = (sdi->driver) ? sdi->driver->name : "unknown";
packet.type = SR_DF_END;
packet.payload = NULL;
return SR_OK;
}
+/**
+ * Standard API helper for sending an SR_DF_FRAME_BEGIN packet.
+ *
+ * This function can be used to simplify most drivers'
+ * frame handling.
+ *
+ * @param[in] sdi The device instance to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
+ */
+SR_PRIV int std_session_send_frame_begin(const struct sr_dev_inst *sdi)
+{
+ const char *prefix;
+ int ret;
+ struct sr_datafeed_packet packet;
+
+ if (!sdi) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
+ }
+
+ prefix = (sdi->driver) ? sdi->driver->name : "unknown";
+
+ packet.type = SR_DF_FRAME_BEGIN;
+ packet.payload = NULL;
+
+ if ((ret = sr_session_send(sdi, &packet)) < 0) {
+ sr_err("%s: Failed to send SR_DF_FRAME_BEGIN packet: %d.", prefix, ret);
+ return ret;
+ }
+
+ return SR_OK;
+}
+
+/**
+ * Standard API helper for sending an SR_DF_FRAME_END packet.
+ *
+ * This function can be used to simplify most drivers'
+ * frame handling.
+ *
+ * @param[in] sdi The device instance to use. Must not be NULL.
+ *
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
+ */
+SR_PRIV int std_session_send_frame_end(const struct sr_dev_inst *sdi)
+{
+ const char *prefix;
+ int ret;
+ struct sr_datafeed_packet packet;
+
+ if (!sdi) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
+ }
+
+ prefix = (sdi->driver) ? sdi->driver->name : "unknown";
+
+ packet.type = SR_DF_FRAME_END;
+ packet.payload = NULL;
+
+ if ((ret = sr_session_send(sdi, &packet)) < 0) {
+ sr_err("%s: Failed to send SR_DF_FRAME_END packet: %d.", prefix, ret);
+ return ret;
+ }
+
+ return SR_OK;
+}
+
#ifdef HAVE_LIBSERIALPORT
/**
- * Standard serial driver dev_open() helper.
+ * Standard serial driver dev_open() callback API helper.
*
* 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 flag.
*
- * If the open succeeded, the status field of the given sdi is set
- * to SR_ST_ACTIVE.
+ * @param[in] sdi The device instance to use. Must not be NULL.
*
* @retval SR_OK Success.
- * @retval SR_ERR Serial port open failed.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Serial port open failed.
*/
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) != SR_OK)
- return SR_ERR;
+ if (!sdi) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
+ }
- sdi->status = SR_ST_ACTIVE;
+ serial = sdi->conn;
- return SR_OK;
+ return serial_open(serial, SERIAL_RDWR);
}
/**
- * Standard serial driver dev_close() helper.
+ * Standard serial driver dev_close() callback API helper.
*
* This function can be used to implement the dev_close() driver API
* callback in drivers that use a serial port.
*
- * After closing the port, the status field of the given sdi is set
- * to SR_ST_INACTIVE.
+ * @param[in] sdi The device instance to use. Must not be NULL.
*
* @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Serial port close failed.
*/
SR_PRIV int std_serial_dev_close(struct sr_dev_inst *sdi)
{
struct sr_serial_dev_inst *serial;
- serial = sdi->conn;
- if (serial && sdi->status == SR_ST_ACTIVE) {
- serial_close(serial);
- sdi->status = SR_ST_INACTIVE;
+ if (!sdi) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
}
- return SR_OK;
+ serial = sdi->conn;
+
+ return serial_close(serial);
}
/**
- * Standard sr_session_stop() API helper.
+ * Standard serial driver dev_acquisition_stop() callback API helper.
*
- * This function can be used to simplify most (serial port based) driver's
+ * This function can be used to simplify most (serial port based) drivers'
* dev_acquisition_stop() API callback.
*
- * @param sdi The device instance for which acquisition should stop.
- * Must not be NULL.
+ * @param[in] sdi The device instance for which acquisition should stop.
+ * Must not be NULL.
*
* @retval SR_OK Success.
- * @retval SR_ERR_ARG Invalid arguments.
- * @retval SR_ERR_DEV_CLOSED Device is closed.
- * @retval SR_ERR Other errors.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval other Other error.
*/
SR_PRIV int std_serial_dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- struct sr_serial_dev_inst *serial = sdi->conn;
- const char *prefix = sdi->driver->name;
+ struct sr_serial_dev_inst *serial;
+ const char *prefix;
int ret;
- if (sdi->status != SR_ST_ACTIVE) {
- sr_err("%s: Device inactive, can't stop acquisition.", prefix);
- return SR_ERR_DEV_CLOSED;
+ if (!sdi) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
}
- sr_dbg("%s: Stopping acquisition.", prefix);
+ serial = sdi->conn;
+ prefix = sdi->driver->name;
if ((ret = serial_source_remove(sdi->session, serial)) < 0) {
sr_err("%s: Failed to remove source: %d.", prefix, ret);
return ret;
}
- if ((ret = sdi->driver->dev_close(sdi)) < 0) {
+ if ((ret = sr_dev_close(sdi)) < 0) {
sr_err("%s: Failed to close device: %d.", prefix, ret);
return ret;
}
- std_session_send_df_end(sdi);
-
- return SR_OK;
+ return std_session_send_df_end(sdi);
}
#endif
/**
- * Standard driver dev_clear() helper.
+ * Standard driver dev_clear() callback API helper.
*
* Clear driver, this means, close all instances.
*
* This function can be used to implement the dev_clear() driver API
* callback. dev_close() is called before every sr_dev_inst is cleared.
*
- * The only limitation is driver-specific device contexts (sdi->priv).
+ * The only limitation is driver-specific device contexts (sdi->priv / devc).
* These are freed, but any dynamic allocation within structs stored
* there cannot be freed.
*
- * @param driver The driver which will have its instances released.
- * @param clear_private If not NULL, this points to a function called
- * with sdi->priv as argument. The function can then clear any device
- * instance-specific resources kept there. It must also clear the struct
- * pointed to by sdi->priv.
+ * @param[in] driver The driver which will have its instances released.
+ * Must not be NULL.
+ * @param[in] clear_private If not NULL, this points to a function called
+ * with sdi->priv (devc) as argument. The function can then clear
+ * any device instance-specific resources kept there.
+ * It must NOT clear the struct pointed to by sdi->priv (devc),
+ * since this function will always free it after clear_private()
+ * has run.
*
- * @return SR_OK on success.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid argument.
+ * @retval SR_ERR_BUG Implementation bug.
+ * @retval other Other error.
*/
-SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver,
+SR_PRIV int std_dev_clear_with_callback(const struct sr_dev_driver *driver,
std_dev_clear_callback clear_private)
{
struct drv_context *drvc;
GSList *l;
int ret;
- if (!(drvc = driver->context))
- /* Driver was never initialized, nothing to do. */
- return SR_OK;
+ if (!driver) {
+ sr_err("%s: Invalid argument.", __func__);
+ return SR_ERR_ARG;
+ }
+
+ drvc = driver->context; /* Caller checked for context != NULL. */
ret = SR_OK;
for (l = drvc->instances; l; l = l->next) {
if (!(sdi = l->data)) {
+ sr_err("%s: Invalid device instance.", __func__);
ret = SR_ERR_BUG;
continue;
}
if (sdi->inst_type == SR_INST_MODBUS)
sr_modbus_free(sdi->conn);
}
+
+ /* Clear driver-specific stuff, if any. */
if (clear_private)
- /* The helper function is responsible for freeing
- * its own sdi->priv! */
clear_private(sdi->priv);
- else
- g_free(sdi->priv);
+
+ /* Clear sdi->priv (devc). */
+ g_free(sdi->priv);
sr_dev_inst_free(sdi);
}
return ret;
}
+SR_PRIV int std_dev_clear(const struct sr_dev_driver *driver)
+{
+ return std_dev_clear_with_callback(driver, NULL);
+}
+
/**
- * Standard implementation for the driver dev_list() callback
+ * Standard driver dev_list() callback API helper.
*
- * This function can be used as the dev_list callback by most drivers that use
- * the standard helper functions. It returns the devices contained in the driver
- * context instances list.
+ * This function can be used as the dev_list() callback by most drivers.
*
- * @param di The driver instance to use.
+ * Return the devices contained in the driver context instances list.
*
- * @return The list of devices/instances of this driver, or NULL upon errors
- * or if the list is empty.
+ * @param[in] di The driver instance to use. Must not be NULL.
+ *
+ * @retval NULL Error, or the list is empty.
+ * @retval other The list of device instances of this driver.
*/
SR_PRIV GSList *std_dev_list(const struct sr_dev_driver *di)
{
- struct drv_context *drvc = di->context;
+ struct drv_context *drvc;
+
+ if (!di) {
+ sr_err("%s: Invalid argument.", __func__);
+ return NULL;
+ }
+
+ drvc = di->context;
return drvc->instances;
}
/**
- * Standard scan() callback API helper.
+ * Standard driver scan() callback API helper.
*
* This function can be used to perform common tasks required by a driver's
* scan() callback. It will initialize the driver for each device on the list
* }
* @endcode
*
- * @param di The driver instance to use. Must not be NULL.
- * @param devices List of newly discovered devices (struct sr_dev_inst).
+ * @param[in] di The driver instance to use. Must not be NULL.
+ * @param[in] devices List of newly discovered devices (struct sr_dev_inst).
+ * May be NULL.
*
* @return The @p devices list.
*/
for (l = devices; l; l = l->next) {
struct sr_dev_inst *sdi = l->data;
if (!sdi) {
- sr_err("Invalid driver instance, cannot complete scan.");
+ sr_err("Invalid device instance, cannot complete scan.");
return NULL;
}
sdi->driver = di;
return devices;
}
+
+SR_PRIV int std_opts_config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg,
+ const uint32_t scanopts[], size_t scansize, const uint32_t drvopts[],
+ size_t drvsize, const uint32_t devopts[], size_t devsize)
+{
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ /* Always return scanopts, regardless of sdi or cg. */
+ if (!scanopts || scanopts == NO_OPTS)
+ return SR_ERR_ARG;
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+ scanopts, scansize, sizeof(uint32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ if (!sdi) {
+ /* sdi == NULL: return drvopts. */
+ if (!drvopts || drvopts == NO_OPTS)
+ return SR_ERR_ARG;
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+ drvopts, drvsize, sizeof(uint32_t));
+ } else if (sdi && !cg) {
+ /* sdi != NULL, cg == NULL: return devopts. */
+ if (!devopts || devopts == NO_OPTS)
+ return SR_ERR_ARG;
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+ devopts, devsize, sizeof(uint32_t));
+ } else {
+ /*
+ * Note: sdi != NULL, cg != NULL is not handled by
+ * this function since it's very driver-specific.
+ */
+ sr_err("%s: %s: sdi/cg != NULL: not handling.",
+ sdi->driver->name, __func__);
+ return SR_ERR_ARG;
+ }
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV GVariant *std_gvar_tuple_array(const uint64_t a[][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(a[i][0]);
+ rational[1] = g_variant_new_uint64(a[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);
+}
+
+SR_PRIV GVariant *std_gvar_tuple_rational(const struct sr_rational *r, 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(r[i].p);
+ rational[1] = g_variant_new_uint64(r[i].q);
+
+ /* 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 GVariant *samplerate_helper(const uint64_t samplerates[], unsigned int n, const char *str)
+{
+ GVariant *gvar;
+ GVariantBuilder gvb;
+
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
+ gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates,
+ n, sizeof(uint64_t));
+ g_variant_builder_add(&gvb, "{sv}", str, gvar);
+
+ return g_variant_builder_end(&gvb);
+}
+
+SR_PRIV GVariant *std_gvar_samplerates(const uint64_t samplerates[], unsigned int n)
+{
+ return samplerate_helper(samplerates, n, "samplerates");
+}
+
+SR_PRIV GVariant *std_gvar_samplerates_steps(const uint64_t samplerates[], unsigned int n)
+{
+ return samplerate_helper(samplerates, n, "samplerate-steps");
+}
+
+SR_PRIV GVariant *std_gvar_min_max_step(double min, double max, double step)
+{
+ GVariantBuilder gvb;
+
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+ g_variant_builder_add_value(&gvb, g_variant_new_double(min));
+ g_variant_builder_add_value(&gvb, g_variant_new_double(max));
+ g_variant_builder_add_value(&gvb, g_variant_new_double(step));
+
+ return g_variant_builder_end(&gvb);
+}
+
+SR_PRIV GVariant *std_gvar_min_max_step_array(const double a[3])
+{
+ unsigned int i;
+ GVariantBuilder gvb;
+
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+ for (i = 0; i < 3; i++)
+ g_variant_builder_add_value(&gvb, g_variant_new_double(a[i]));
+
+ return g_variant_builder_end(&gvb);
+}
+
+SR_PRIV GVariant *std_gvar_min_max_step_thresholds(const double min, const double max, const double step)
+{
+ double d, v;
+ GVariant *gvar, *range[2];
+ GVariantBuilder gvb;
+
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+ for (d = min; d <= max; d += step) {
+ /*
+ * We will never see exactly 0.0 because of the error we're
+ * accumulating, so catch the "zero" value and force it to be 0.
+ */
+ v = ((d > (-step / 2)) && (d < (step / 2))) ? 0 : d;
+
+ range[0] = g_variant_new_double(v);
+ range[1] = g_variant_new_double(v);
+
+ gvar = g_variant_new_tuple(range, 2);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+
+ return g_variant_builder_end(&gvb);
+}
+
+SR_PRIV GVariant *std_gvar_tuple_u64(uint64_t low, uint64_t high)
+{
+ GVariant *range[2];
+
+ range[0] = g_variant_new_uint64(low);
+ range[1] = g_variant_new_uint64(high);
+
+ return g_variant_new_tuple(range, 2);
+}
+
+SR_PRIV GVariant *std_gvar_tuple_double(double low, double high)
+{
+ GVariant *range[2];
+
+ range[0] = g_variant_new_double(low);
+ range[1] = g_variant_new_double(high);
+
+ return g_variant_new_tuple(range, 2);
+}
+
+SR_PRIV GVariant *std_gvar_array_i32(const int32_t a[], unsigned int n)
+{
+ return g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ a, n, sizeof(int32_t));
+}
+
+SR_PRIV GVariant *std_gvar_array_u32(const uint32_t a[], unsigned int n)
+{
+ return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+ a, n, sizeof(uint32_t));
+}
+
+SR_PRIV GVariant *std_gvar_array_u64(const uint64_t a[], unsigned int n)
+{
+ return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
+ a, n, sizeof(uint64_t));
+}
+
+SR_PRIV GVariant *std_gvar_array_str(const char *a[], unsigned int n)
+{
+ GVariant *gvar;
+ GVariantBuilder *builder;
+ unsigned int i;
+
+ builder = g_variant_builder_new(G_VARIANT_TYPE ("as"));
+
+ for (i = 0; i < n; i++)
+ g_variant_builder_add(builder, "s", a[i]);
+
+ gvar = g_variant_new("as", builder);
+ g_variant_builder_unref(builder);
+
+ return gvar;
+}
+
+SR_PRIV GVariant *std_gvar_thresholds(const double a[][2], unsigned int n)
+{
+ unsigned int i;
+ GVariant *gvar, *range[2];
+ GVariantBuilder gvb;
+
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+ for (i = 0; i < n; i++) {
+ range[0] = g_variant_new_double(a[i][0]);
+ range[1] = g_variant_new_double(a[i][1]);
+ gvar = g_variant_new_tuple(range, 2);
+ g_variant_builder_add_value(&gvb, gvar);
+ }
+
+ return g_variant_builder_end(&gvb);
+}
+
+/* Return the index of 'data' in the array 'arr' (or -1). */
+static int find_in_array(GVariant *data, const GVariantType *type,
+ const void *arr, unsigned int n)
+{
+ const char * const *sarr;
+ const char *s;
+ const uint64_t *u64arr;
+ const uint8_t *u8arr;
+ uint64_t u64;
+ uint8_t u8;
+ unsigned 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;
+}
+
+SR_PRIV int std_str_idx(GVariant *data, const char *a[], unsigned int n)
+{
+ return find_in_array(data, G_VARIANT_TYPE_STRING, a, n);
+}
+
+SR_PRIV int std_u64_idx(GVariant *data, const uint64_t a[], unsigned int n)
+{
+ return find_in_array(data, G_VARIANT_TYPE_UINT64, a, n);
+}
+
+SR_PRIV int std_u8_idx(GVariant *data, const uint8_t a[], unsigned int n)
+{
+ return find_in_array(data, G_VARIANT_TYPE_BYTE, a, n);
+}
+
+SR_PRIV int std_str_idx_s(const char *s, const char *a[], unsigned int n)
+{
+ int idx;
+ GVariant *data;
+
+ data = g_variant_new_string(s);
+ idx = find_in_array(data, G_VARIANT_TYPE_STRING, a, n);
+ g_variant_unref(data);
+
+ return idx;
+}
+
+SR_PRIV int std_u8_idx_s(uint8_t b, const uint8_t a[], unsigned int n)
+{
+ int idx;
+ GVariant *data;
+
+ data = g_variant_new_byte(b);
+ idx = find_in_array(data, G_VARIANT_TYPE_BYTE, a, n);
+ g_variant_unref(data);
+
+ return idx;
+}
+
+SR_PRIV int std_u64_tuple_idx(GVariant *data, const uint64_t a[][2], unsigned int n)
+{
+ unsigned int i;
+ uint64_t low, high;
+
+ g_variant_get(data, "(tt)", &low, &high);
+
+ for (i = 0; i < n; i++)
+ if (a[i][0] == low && a[i][1] == high)
+ return i;
+
+ return -1;
+}
+
+SR_PRIV int std_double_tuple_idx(GVariant *data, const double a[][2], unsigned int n)
+{
+ unsigned int i;
+ double low, high;
+
+ g_variant_get(data, "(dd)", &low, &high);
+
+ for (i = 0; i < n; i++)
+ if ((fabs(a[i][0] - low) < 0.1) && ((fabs(a[i][1] - high) < 0.1)))
+ return i;
+
+ return -1;
+}
+
+SR_PRIV int std_double_tuple_idx_d0(const double d, const double a[][2], unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < n; i++)
+ if (d == a[i][0])
+ return i;
+
+ return -1;
+}
+
+SR_PRIV int std_cg_idx(const struct sr_channel_group *cg, struct sr_channel_group *a[], unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < n; i++)
+ if (cg == a[i])
+ return i;
+
+ return -1;
+}
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+/* Needed for POSIX.1-2008 locale functions */
+#define _XOPEN_SOURCE 700
#include <config.h>
+#include <ctype.h>
+#include <locale.h>
+#if defined(__FreeBSD__) || defined(__APPLE__)
+#include <xlocale.h>
+#endif
+#if defined(__FreeBSD__)
+#include <sys/param.h>
+#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
errno = 0;
tmp = strtol(str, &endptr, 10);
+ while (endptr && isspace(*endptr))
+ endptr++;
+
if (!endptr || *endptr || errno) {
if (!errno)
errno = EINVAL;
errno = 0;
tmp = strtof(str, &endptr);
+ while (endptr && isspace(*endptr))
+ endptr++;
+
if (!endptr || *endptr || errno) {
if (!errno)
errno = EINVAL;
return SR_OK;
}
+/**
+ * @private
+ *
+ * Convert a string representation of a numeric value to a double. The
+ * conversion is strict and will fail if the complete string does not represent
+ * a valid double. The function sets errno according to the details of the
+ * failure. This version ignores the locale.
+ *
+ * @param str The string representation to convert.
+ * @param ret Pointer to double where the result of the conversion will be stored.
+ *
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
+ */
+SR_PRIV int sr_atod_ascii(const char *str, double *ret)
+{
+ double tmp;
+ char *endptr = NULL;
+
+ errno = 0;
+ tmp = g_ascii_strtod(str, &endptr);
+
+ if (!endptr || *endptr || errno) {
+ if (!errno)
+ errno = EINVAL;
+ return SR_ERR;
+ }
+
+ *ret = tmp;
+ return SR_OK;
+}
+
/**
* @private
*
return SR_OK;
}
+/**
+ * Compose a string with a format string in the buffer pointed to by buf.
+ *
+ * It is up to the caller to ensure that the allocated buffer is large enough
+ * to hold the formatted result.
+ *
+ * A terminating NUL character is automatically appended after the content
+ * written.
+ *
+ * After the format parameter, the function expects at least as many additional
+ * arguments as needed for format.
+ *
+ * This version ignores the current locale and uses the locale "C" for Linux,
+ * FreeBSD, OSX and Android.
+ *
+ * @param buf Pointer to a buffer where the resulting C string is stored.
+ * @param format C string that contains a format string (see printf).
+ * @param ... A sequence of additional arguments, each containing a value to be
+ * used to replace a format specifier in the format string.
+ *
+ * @return On success, the number of characters that would have been written,
+ * not counting the terminating NUL character.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_sprintf_ascii(char *buf, const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = sr_vsprintf_ascii(buf, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/**
+ * Compose a string with a format string in the buffer pointed to by buf.
+ *
+ * It is up to the caller to ensure that the allocated buffer is large enough
+ * to hold the formatted result.
+ *
+ * Internally, the function retrieves arguments from the list identified by
+ * args as if va_arg was used on it, and thus the state of args is likely to
+ * be altered by the call.
+ *
+ * In any case, args should have been initialized by va_start at some point
+ * before the call, and it is expected to be released by va_end at some point
+ * after the call.
+ *
+ * This version ignores the current locale and uses the locale "C" for Linux,
+ * FreeBSD, OSX and Android.
+ *
+ * @param buf Pointer to a buffer where the resulting C string is stored.
+ * @param format C string that contains a format string (see printf).
+ * @param args A value identifying a variable arguments list initialized with
+ * va_start.
+ *
+ * @return On success, the number of characters that would have been written,
+ * not counting the terminating NUL character.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_vsprintf_ascii(char *buf, const char *format, va_list args)
+{
+#if defined(_WIN32)
+ int ret;
+
+#if 0
+ /*
+ * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
+ * Doesn't start because of "Procedure entry point _create_locale
+ * not found in msvcrt.dll".
+ * mingw-w64 should link to msvcr100.dll not msvcrt.dll!
+ * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx
+ */
+ _locale_t locale;
+
+ locale = _create_locale(LC_NUMERIC, "C");
+ ret = _vsprintf_l(buf, format, locale, args);
+ _free_locale(locale);
+#endif
+
+ /* vsprintf() uses the current locale, may not work correctly for floats. */
+ ret = vsprintf(buf, format, args);
+
+ return ret;
+#elif defined(__APPLE__)
+ /*
+ * See:
+ * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
+ * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
+ */
+ int ret;
+ locale_t locale;
+
+ locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+ ret = vsprintf_l(buf, locale, format, args);
+ freelocale(locale);
+
+ return ret;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
+ /*
+ * See:
+ * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
+ * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
+ */
+ int ret;
+ locale_t locale;
+
+ locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+ ret = vsprintf_l(buf, locale, format, args);
+ freelocale(locale);
+
+ return ret;
+#elif defined(__ANDROID__)
+ /*
+ * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
+ * aka "en_US.UTF-8"). The decimal point is hard coded as "."
+ * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
+ */
+ int ret;
+
+ ret = vsprintf(buf, format, args);
+
+ return ret;
+#elif defined(__linux__)
+ int ret;
+ locale_t old_locale, temp_locale;
+
+ /* Switch to C locale for proper float/double conversion. */
+ temp_locale = newlocale(LC_NUMERIC, "C", NULL);
+ old_locale = uselocale(temp_locale);
+
+ ret = vsprintf(buf, format, args);
+
+ /* Switch back to original locale. */
+ uselocale(old_locale);
+ freelocale(temp_locale);
+
+ return ret;
+#elif defined(__unix__) || defined(__unix)
+ /*
+ * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
+ * using the current locale for snprintf(). This may not work correctly
+ * for floats!
+ */
+ int ret;
+
+ ret = vsprintf(buf, format, args);
+
+ return ret;
+#else
+ /* No implementation for unknown systems! */
+ return -1;
+#endif
+}
+
+/**
+ * Composes a string with a format string (like printf) in the buffer pointed
+ * by buf (taking buf_size as the maximum buffer capacity to fill).
+ * If the resulting string would be longer than n - 1 characters, the remaining
+ * characters are discarded and not stored, but counted for the value returned
+ * by the function.
+ * A terminating NUL character is automatically appended after the content
+ * written.
+ * After the format parameter, the function expects at least as many additional
+ * arguments as needed for format.
+ *
+ * This version ignores the current locale and uses the locale "C" for Linux,
+ * FreeBSD, OSX and Android.
+ *
+ * @param buf Pointer to a buffer where the resulting C string is stored.
+ * @param buf_size Maximum number of bytes to be used in the buffer. The
+ * generated string has a length of at most buf_size - 1, leaving space
+ * for the additional terminating NUL character.
+ * @param format C string that contains a format string (see printf).
+ * @param ... A sequence of additional arguments, each containing a value to be
+ * used to replace a format specifier in the format string.
+ *
+ * @return On success, the number of characters that would have been written if
+ * buf_size had been sufficiently large, not counting the terminating
+ * NUL character. On failure, a negative number is returned.
+ * Notice that only when this returned value is non-negative and less
+ * than buf_size, the string has been completely written.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_snprintf_ascii(char *buf, size_t buf_size,
+ const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = sr_vsnprintf_ascii(buf, buf_size, format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/**
+ * Composes a string with a format string (like printf) in the buffer pointed
+ * by buf (taking buf_size as the maximum buffer capacity to fill).
+ * If the resulting string would be longer than n - 1 characters, the remaining
+ * characters are discarded and not stored, but counted for the value returned
+ * by the function.
+ * A terminating NUL character is automatically appended after the content
+ * written.
+ * Internally, the function retrieves arguments from the list identified by
+ * args as if va_arg was used on it, and thus the state of args is likely to
+ * be altered by the call.
+ * In any case, arg should have been initialized by va_start at some point
+ * before the call, and it is expected to be released by va_end at some point
+ * after the call.
+ *
+ * This version ignores the current locale and uses the locale "C" for Linux,
+ * FreeBSD, OSX and Android.
+ *
+ * @param buf Pointer to a buffer where the resulting C string is stored.
+ * @param buf_size Maximum number of bytes to be used in the buffer. The
+ * generated string has a length of at most buf_size - 1, leaving space
+ * for the additional terminating NUL character.
+ * @param format C string that contains a format string (see printf).
+ * @param args A value identifying a variable arguments list initialized with
+ * va_start.
+ *
+ * @return On success, the number of characters that would have been written if
+ * buf_size had been sufficiently large, not counting the terminating
+ * NUL character. On failure, a negative number is returned.
+ * Notice that only when this returned value is non-negative and less
+ * than buf_size, the string has been completely written.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size,
+ const char *format, va_list args)
+{
+#if defined(_WIN32)
+ int ret;
+
+#if 0
+ /*
+ * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
+ * Doesn't start because of "Procedure entry point _create_locale
+ * not found in msvcrt.dll".
+ * mingw-w64 should link to msvcr100.dll not msvcrt.dll!.
+ * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx
+ */
+ _locale_t locale;
+
+ locale = _create_locale(LC_NUMERIC, "C");
+ ret = _vsnprintf_l(buf, buf_size, format, locale, args);
+ _free_locale(locale);
+#endif
+
+ /* vsprintf uses the current locale, may cause issues for floats. */
+ ret = vsnprintf(buf, buf_size, format, args);
+
+ return ret;
+#elif defined(__APPLE__)
+ /*
+ * See:
+ * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
+ * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
+ */
+ int ret;
+ locale_t locale;
+
+ locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+ ret = vsnprintf_l(buf, buf_size, locale, format, args);
+ freelocale(locale);
+
+ return ret;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
+ /*
+ * See:
+ * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
+ * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
+ */
+ int ret;
+ locale_t locale;
+
+ locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+ ret = vsnprintf_l(buf, buf_size, locale, format, args);
+ freelocale(locale);
+
+ return ret;
+#elif defined(__ANDROID__)
+ /*
+ * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
+ * aka "en_US.UTF-8"). The decimal point is hard coded as ".".
+ * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
+ */
+ int ret;
+
+ ret = vsnprintf(buf, buf_size, format, args);
+
+ return ret;
+#elif defined(__linux__)
+ int ret;
+ locale_t old_locale, temp_locale;
+
+ /* Switch to C locale for proper float/double conversion. */
+ temp_locale = newlocale(LC_NUMERIC, "C", NULL);
+ old_locale = uselocale(temp_locale);
+
+ ret = vsnprintf(buf, buf_size, format, args);
+
+ /* Switch back to original locale. */
+ uselocale(old_locale);
+ freelocale(temp_locale);
+
+ return ret;
+#elif defined(__unix__) || defined(__unix)
+ /*
+ * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
+ * using the current locale for snprintf(). This may not work correctly
+ * for floats!
+ */
+ int ret;
+
+ ret = vsnprintf(buf, buf_size, format, args);
+
+ return ret;
+#else
+ /* No implementation for unknown systems! */
+ return -1;
+#endif
+}
+
+/**
+ * Convert a sequence of bytes to its textual representation ("hex dump").
+ *
+ * Callers should free the allocated GString. See @ref sr_hexdump_free().
+ *
+ * @param[in] data Pointer to the byte sequence to print.
+ * @param[in] len Number of bytes to print.
+ *
+ * @return #NULL upon error, newly allocated GString pointer otherwise.
+ */
+SR_PRIV GString *sr_hexdump_new(const uint8_t *data, const size_t len)
+{
+ GString *s;
+ size_t i;
+
+ s = g_string_sized_new(3 * len);
+ for (i = 0; i < len; i++) {
+ if (i)
+ g_string_append_c(s, ' ');
+ g_string_append_printf(s, "%02x", data[i]);
+ }
+
+ return s;
+}
+
+/**
+ * Free a hex dump text that was created by @ref sr_hexdump_new().
+ *
+ * @param[in] s Pointer to the GString to release.
+ */
+SR_PRIV void sr_hexdump_free(GString *s)
+{
+ if (s)
+ g_string_free(s, TRUE);
+}
+
/**
* Convert a string representation of a numeric value to a sr_rational.
*
int64_t denominator = 1;
int32_t fractional_len = 0;
int32_t exponent = 0;
+ gboolean is_negative = FALSE;
+ gboolean no_integer, no_fractional;
+
+ while (isspace(*str))
+ str++;
errno = 0;
integral = g_ascii_strtoll(str, &endptr, 10);
- if (errno)
+ if (str == endptr && (str[0] == '-' || str[0] == '+') && str[1] == '.') {
+ endptr += 1;
+ no_integer = TRUE;
+ } else if (str == endptr && str[0] == '.') {
+ no_integer = TRUE;
+ } else if (errno) {
return SR_ERR;
+ } else {
+ no_integer = FALSE;
+ }
+ if (integral < 0 || str[0] == '-')
+ is_negative = TRUE;
+
+ errno = 0;
if (*endptr == '.') {
- const char* start = endptr + 1;
+ gboolean is_exp, is_eos;
+ const char *start = endptr + 1;
fractional = g_ascii_strtoll(start, &endptr, 10);
+ is_exp = *endptr == 'E' || *endptr == 'e';
+ is_eos = *endptr == '\0';
+ if (endptr == start && (is_exp || is_eos)) {
+ fractional = 0;
+ errno = 0;
+ }
if (errno)
return SR_ERR;
+ no_fractional = endptr == start;
+ if (no_integer && no_fractional)
+ return SR_ERR;
fractional_len = endptr - start;
}
+ errno = 0;
if ((*endptr == 'E') || (*endptr == 'e')) {
exponent = g_ascii_strtoll(endptr + 1, &endptr, 10);
if (errno)
integral *= 10;
exponent -= fractional_len;
- if (integral >= 0)
+ if (!is_negative)
integral += fractional;
else
integral -= fractional;
SR_API char *sr_period_string(uint64_t v_p, uint64_t v_q)
{
double freq, v;
- char *o;
- int prec, r;
+ int prec;
freq = 1 / ((double)v_p / v_q);
- o = g_malloc0(30 + 1);
-
if (freq > SR_GHZ(1)) {
v = (double)v_p / v_q * 1000000000000.0;
prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
- r = snprintf(o, 30, "%.*f ps", prec, v);
+ return g_strdup_printf("%.*f ps", prec, v);
} else if (freq > SR_MHZ(1)) {
v = (double)v_p / v_q * 1000000000.0;
prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
- r = snprintf(o, 30, "%.*f ns", prec, v);
+ return g_strdup_printf("%.*f ns", prec, v);
} else if (freq > SR_KHZ(1)) {
v = (double)v_p / v_q * 1000000.0;
prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
- r = snprintf(o, 30, "%.*f us", prec, v);
+ return g_strdup_printf("%.*f us", prec, v);
} else if (freq > 1) {
v = (double)v_p / v_q * 1000.0;
prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
- r = snprintf(o, 30, "%.*f ms", prec, v);
+ return g_strdup_printf("%.*f ms", prec, v);
} else {
v = (double)v_p / v_q;
prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
- r = snprintf(o, 30, "%.*f s", prec, v);
+ return g_strdup_printf("%.*f s", prec, v);
}
-
- if (r < 0) {
- /* Something went wrong... */
- g_free(o);
- return NULL;
- }
-
- return o;
}
/**
*/
SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
{
- int r;
- char *o;
-
- o = g_malloc0(30 + 1);
-
if (v_q == 1000)
- r = snprintf(o, 30, "%" PRIu64 "mV", v_p);
+ return g_strdup_printf("%" PRIu64 " mV", v_p);
else if (v_q == 1)
- r = snprintf(o, 30, "%" PRIu64 "V", v_p);
+ return g_strdup_printf("%" PRIu64 " V", v_p);
else
- r = snprintf(o, 30, "%gV", (float)v_p / (float)v_q);
-
- if (r < 0) {
- /* Something went wrong... */
- g_free(o);
- return NULL;
- }
-
- return o;
+ return g_strdup_printf("%g V", (float)v_p / (float)v_q);
}
/**
*/
SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
{
- int multiplier, done;
+ uint64_t multiplier;
+ int done;
double frac_part;
char *s;
case 'G':
multiplier = SR_GHZ(1);
break;
+ case 't':
+ case 'T':
+ multiplier = SR_GHZ(1000);
+ break;
+ case 'p':
+ case 'P':
+ multiplier = SR_GHZ(1000 * 1000);
+ break;
+ case 'e':
+ case 'E':
+ multiplier = SR_GHZ(1000 * 1000 * 1000);
+ break;
default:
done = TRUE;
s--;
if (multiplier > 0) {
*size *= multiplier;
*size += frac_part * multiplier;
- } else
+ } else {
*size += frac_part;
+ }
if (s && *s && g_ascii_strcasecmp(s, "Hz"))
return SR_ERR;
/** @since 0.1.0 */
SR_API gboolean sr_parse_boolstring(const char *boolstr)
{
- if (!boolstr)
- return FALSE;
+ /*
+ * Complete absence of an input spec is assumed to mean TRUE,
+ * as in command line option strings like this:
+ * ...:samplerate=100k:header:numchannels=4:...
+ */
+ if (!boolstr || !*boolstr)
+ return TRUE;
if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
!g_ascii_strncasecmp(boolstr, "yes", 3) ||
while (*s == ' ')
s++;
if (!strcmp(s, "fs"))
- *q = 1000000000000000ULL;
+ *q = UINT64_C(1000000000000000);
else if (!strcmp(s, "ps"))
- *q = 1000000000000ULL;
+ *q = UINT64_C(1000000000000);
else if (!strcmp(s, "ns"))
- *q = 1000000000ULL;
+ *q = UINT64_C(1000000000);
else if (!strcmp(s, "us"))
*q = 1000000;
else if (!strcmp(s, "ms"))
return;
pollfd = g_slice_new(GPollFD);
-#ifdef G_OS_WIN32
+#ifdef _WIN32
events = G_IO_IN;
#endif
pollfd->fd = (gintptr)fd;
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);
+ /* Trying to find USB device via VID:PID. */
} else {
g_match_info_unref(match);
g_regex_unref(reg);
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);
+ /* Trying to find USB device via bus.address. */
}
}
g_match_info_unref(match);
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);
+ usb = sr_usb_dev_inst_new(b, a, NULL);
devices = g_slist_append(devices, usb);
}
libusb_free_device_list(devlist, 1);
- sr_dbg("Found %d device(s).", g_slist_length(devices));
+ /* No log message for #devices found (caller will log that). */
return devices;
}
return SR_OK;
}
+
+/**
+ * Check the USB configuration to determine if this device has a given
+ * manufacturer and product string.
+ *
+ * @return TRUE if the device's configuration profile strings
+ * configuration, FALSE otherwise.
+ */
+SR_PRIV gboolean usb_match_manuf_prod(libusb_device *dev,
+ const char *manufacturer, const char *product)
+{
+ struct libusb_device_descriptor des;
+ struct libusb_device_handle *hdl;
+ gboolean ret;
+ unsigned char strdesc[64];
+
+ hdl = NULL;
+ ret = FALSE;
+ while (!ret) {
+ /* Assume the FW has not been loaded, unless proven wrong. */
+ 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)
+ break;
+ if (strcmp((const char *)strdesc, manufacturer))
+ break;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iProduct, strdesc, sizeof(strdesc)) < 0)
+ break;
+ if (strcmp((const char *)strdesc, product))
+ break;
+
+ ret = TRUE;
+ }
+ if (hdl)
+ libusb_close(hdl);
+
+ return ret;
+}
struct sr_analog_encoding encoding;
struct sr_analog_meaning meaning;
struct sr_analog_spec spec;
- const char *r[] = {" V RMS"};
+ const int u[] = {SR_UNIT_VOLT, SR_UNIT_AMPERE, SR_UNIT_CELSIUS};
+ const int f[] = {SR_MQFLAG_RMS, 0, 0};
+ const char *r[] = {"V RMS", "A", "°C"};
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;
+ for (i = 0; i < ARRAY_SIZE(r); i++) {
+ meaning.unit = u[i];
+ meaning.mqflags = f[i];
ret = sr_analog_unit_to_string(&analog, &result);
fail_unless(ret == SR_OK);
fail_unless(result != NULL);
#include <config.h>
#include <check.h>
+#include <errno.h>
+#include <locale.h>
#include <libsigrok/libsigrok.h>
#include "lib.h"
+#if 0
+static void test_vsnprintf(const char *expected, char *format, ...)
+{
+ va_list args;
+ char *s;
+ int len;
+
+ len = 16;
+ s = g_malloc0(len + 1);
+
+ va_start(args, format);
+ len = vsnprintf(s, len, format, args);
+ va_end(args);
+
+ fail_unless(s != NULL,
+ "Invalid result for '%s': len = %i.", expected, len);
+ fail_unless(!strcmp(s, expected),
+ "Invalid result for '%s': %s.", expected, s);
+ g_free(s);
+}
+#endif
+
+static void test_sr_vsnprintf_ascii(const char *expected, char *format, ...)
+{
+ va_list args;
+ char *s;
+ int len;
+
+ len = 16;
+ s = g_malloc0(len + 1);
+
+ va_start(args, format);
+ len = sr_vsnprintf_ascii(s, len, format, args);
+ va_end(args);
+
+ fail_unless(s != NULL,
+ "Invalid result for '%s': len = %i.", expected, len);
+ fail_unless(!strcmp(s, expected),
+ "Invalid result for '%s': %s.", expected, s);
+ g_free(s);
+}
+
+static void test_sr_vsprintf_ascii(const char *expected, char *format, ...)
+{
+ va_list args, args_copy;
+ char *s;
+ int len;
+
+ /* Get length of buffer required. */
+ va_start(args, format);
+ va_copy(args_copy, args);
+ len = sr_vsnprintf_ascii(NULL, 0, format, args);
+ va_end(args);
+
+ /* Allocate buffer and write out command. */
+ s = g_malloc0(len + 1);
+ len = sr_vsprintf_ascii(s, format, args_copy);
+ va_end(args_copy);
+
+ fail_unless(s != NULL,
+ "Invalid result for '%s': len = %i.", expected, len);
+ fail_unless(!strcmp(s, expected),
+ "Invalid result for '%s': %s.", expected, s);
+ g_free(s);
+}
+
static void test_samplerate(uint64_t samplerate, const char *expected)
{
char *s;
struct sr_rational rational;
ret = sr_parse_rational(input, &rational);
- fail_unless(ret == SR_OK);
+ fail_unless(ret == SR_OK, "Unexpected rc for '%s': %d, errno %d.",
+ input, ret, errno);
fail_unless((expected.p == rational.p) && (expected.q == rational.q),
"Invalid result for '%s': %ld/%ld'.",
input, rational.p, rational.q);
}
+static void test_rational_fail(const char *input)
+{
+ int ret;
+ struct sr_rational rational;
+
+ ret = sr_parse_rational(input, &rational);
+ fail_unless(ret != SR_OK, "Unexpected success for '%s'.", input);
+}
+
+static void test_voltage(uint64_t v_p, uint64_t v_q, const char *expected)
+{
+ char *s;
+
+ s = sr_voltage_string(v_p, v_q);
+ fail_unless(s != NULL);
+ fail_unless(!strcmp(s, expected),
+ "Invalid result for '%s': %s.", expected, s);
+ g_free(s);
+}
+
+START_TEST(test_locale)
+{
+ char *old_locale, *saved_locale;
+
+ /* Get the the current locale. */
+ old_locale = setlocale(LC_NUMERIC, NULL);
+ fprintf(stderr, "Old locale = %s\n", old_locale);
+ /* Copy the name so it won’t be clobbered by setlocale. */
+ saved_locale = g_strdup(old_locale);
+ ck_assert_msg(saved_locale != NULL);
+
+#ifdef _WIN32
+ /*
+ * See: https://msdn.microsoft.com/en-us/library/cc233982.aspx
+ * Doesn't work! Locale is not set!
+ */
+ setlocale(LC_NUMERIC, "de-DE");
+#else
+ /*
+ * For all *nix and OSX systems, change the locale for all threads to
+ * one that is known for not working correctly with printf(), e.g.
+ * "de_DE.UTF-8".
+ *
+ * Find all your available system locales with "locale -a".
+ */
+ setlocale(LC_NUMERIC, "de_DE.UTF-8");
+#endif
+ fprintf(stderr, "New locale = %s\n", setlocale(LC_NUMERIC, NULL));
+
+ test_sr_vsnprintf_ascii("0.1", "%.1f", (double)0.1);
+ test_sr_vsnprintf_ascii("0.12", "%.2f", (double)0.12);
+ test_sr_vsnprintf_ascii("0.123", "%.3f", (double)0.123);
+ test_sr_vsnprintf_ascii("0.1234", "%.4f", (double)0.1234);
+ test_sr_vsnprintf_ascii("0.12345", "%.5f", (double)0.12345);
+ test_sr_vsnprintf_ascii("0.123456", "%.6f", (double)0.123456);
+
+ test_sr_vsprintf_ascii("0.1", "%.1f", (double)0.1);
+ test_sr_vsprintf_ascii("0.12", "%.2f", (double)0.12);
+ test_sr_vsprintf_ascii("0.123", "%.3f", (double)0.123);
+ test_sr_vsprintf_ascii("0.1234", "%.4f", (double)0.1234);
+ test_sr_vsprintf_ascii("0.12345", "%.5f", (double)0.12345);
+ test_sr_vsprintf_ascii("0.123456", "%.6f", (double)0.123456);
+
+#if 0
+ /*
+ * These tests can be used to tell on which platforms the printf()
+ * functions are locale-dependent (i.e. these tests will fail).
+ */
+ test_vsnprintf("0.1", "%.1f", (double)0.1);
+ test_vsnprintf("0.12", "%.2f", (double)0.12);
+ test_vsnprintf("0.123", "%.3f", (double)0.123);
+ test_vsnprintf("0.1234", "%.4f", (double)0.1234);
+ test_vsnprintf("0.12345", "%.5f", (double)0.12345);
+ test_vsnprintf("0.123456", "%.6f", (double)0.123456);
+#endif
+
+ /* Restore the original locale. */
+ setlocale(LC_NUMERIC, saved_locale);
+ g_free(saved_locale);
+}
+END_TEST
+
/*
* Check various inputs for sr_samplerate_string():
*
START_TEST(test_ghz)
{
- /* Note: Numbers > 2^32 need a ULL suffix. */
-
- test_samplerate(1000000000, "1 GHz");
- test_samplerate(5000000000ULL, "5 GHz");
- test_samplerate(72000000000ULL, "72 GHz");
- test_samplerate(388000000000ULL, "388 GHz");
- test_samplerate(4417594444ULL, "4.417594444 GHz");
- test_samplerate(44175944444ULL, "44.175944444 GHz");
- test_samplerate(441759444441ULL, "441.759444441 GHz");
- test_samplerate(441759000001ULL, "441.759000001 GHz");
- test_samplerate(441050000000ULL, "441.05 GHz");
- test_samplerate(441000000005ULL, "441.000000005 GHz");
- test_samplerate(441500000000ULL, "441.5 GHz");
+ test_samplerate(UINT64_C(1000000000), "1 GHz");
+ test_samplerate(UINT64_C(5000000000), "5 GHz");
+ test_samplerate(UINT64_C(72000000000), "72 GHz");
+ test_samplerate(UINT64_C(388000000000), "388 GHz");
+ test_samplerate(UINT64_C(4417594444), "4.417594444 GHz");
+ test_samplerate(UINT64_C(44175944444), "44.175944444 GHz");
+ test_samplerate(UINT64_C(441759444441), "441.759444441 GHz");
+ test_samplerate(UINT64_C(441759000001), "441.759000001 GHz");
+ test_samplerate(UINT64_C(441050000000), "441.05 GHz");
+ test_samplerate(UINT64_C(441000000005), "441.000000005 GHz");
+ test_samplerate(UINT64_C(441500000000), "441.5 GHz");
/* Again, but now using SR_GHZ(). */
test_samplerate(SR_GHZ(1), "1 GHz");
test_samplerate(SR_GHZ(441.500000000), "441.5 GHz");
/* Now check the biggest-possible samplerate (2^64 Hz). */
- // test_samplerate(18446744073709551615ULL, "18446744073.709551615 GHz");
- // test_samplerate(SR_GHZ(18446744073ULL), "18446744073 GHz");
+ // test_samplerate(UINT64_C(18446744073709551615), "18446744073.709551615 GHz");
+ // test_samplerate(SR_GHZ(UINT64_C(18446744073)), "18446744073 GHz");
}
END_TEST
START_TEST(test_ghz_period)
{
- /* Note: Numbers > 2^32 need a ULL suffix. */
- test_period(1, 1000000000, "1 ns");
- test_period(1, 5000000000ULL, "200 ps");
- test_period(1, 72000000000ULL, "13.889 ps");
- test_period(1, 388000000000ULL, "2.577 ps");
- test_period(10, 1000000000000, "10 ps");
- test_period(200, 1000000000000ULL, "200 ps");
+ test_period(1, UINT64_C(1000000000), "1 ns");
+ test_period(1, UINT64_C(5000000000), "200 ps");
+ test_period(1, UINT64_C(72000000000), "13.889 ps");
+ test_period(1, UINT64_C(388000000000), "2.577 ps");
+ test_period(10, UINT64_C(1000000000000), "10 ps");
+ test_period(200, UINT64_C(1000000000000), "200 ps");
/* Again, but now using SR_GHZ(). */
test_period(1, SR_GHZ(1), "1 ns");
}
END_TEST
+START_TEST(test_volt)
+{
+ test_voltage(34, 1, "34 V");
+ test_voltage(34, 2, "17 V");
+ test_voltage(1, 1, "1 V");
+ test_voltage(1, 5, "0.2 V");
+ test_voltage(200, 1000, "200 mV");
+ test_voltage(1, 72, "0.0138889 V");
+ test_voltage(1, 388, "0.00257732 V");
+ test_voltage(10, 1000, "10 mV");
+}
+END_TEST
+
START_TEST(test_integral)
{
test_rational("1", (struct sr_rational){1, 1});
test_rational("12.34", (struct sr_rational){1234, 100});
test_rational("-12.34", (struct sr_rational){-1234, 100});
test_rational("10.00", (struct sr_rational){1000, 100});
+ test_rational(".1", (struct sr_rational){1, 10});
+ test_rational("+0.1", (struct sr_rational){1, 10});
+ test_rational("+.1", (struct sr_rational){1, 10});
+ test_rational("-0.1", (struct sr_rational){-1, 10});
+ test_rational("-.1", (struct sr_rational){-1, 10});
+ test_rational(".1", (struct sr_rational){1, 10});
+ test_rational(".123", (struct sr_rational){123, 1000});
+ test_rational("1.", (struct sr_rational){1, 1});
+ test_rational("123.", (struct sr_rational){123, 1});
+ test_rational("-.1", (struct sr_rational){-1, 10});
+ test_rational(" .1", (struct sr_rational){1, 10});
+ test_rational("+.1", (struct sr_rational){1, 10});
+ test_rational_fail(".");
+ test_rational_fail(".e");
+ test_rational_fail(".e1");
}
END_TEST
test_rational("0.001e3", (struct sr_rational){1, 1});
test_rational("0.001e0", (struct sr_rational){1, 1000});
test_rational("0.001e-3", (struct sr_rational){1, 1000000});
+ test_rational("43.737E-3", (struct sr_rational){43737, 1000000});
+ test_rational("-0.1e-2", (struct sr_rational){-1, 1000});
+ test_rational("-.1e-2", (struct sr_rational){-1, 1000});
+ test_rational("-.0e-2", (struct sr_rational){0, 1000});
+ test_rational("+.0e-2", (struct sr_rational){0, 1000});
}
END_TEST
tc = tcase_create("sr_samplerate_string");
tcase_add_checked_fixture(tc, srtest_setup, srtest_teardown);
+ tcase_add_test(tc, test_locale);
tcase_add_test(tc, test_hz);
tcase_add_test(tc, test_khz);
tcase_add_test(tc, test_mhz);
tcase_add_test(tc, test_ghz);
tcase_add_test(tc, test_hz_period);
tcase_add_test(tc, test_ghz_period);
+ tcase_add_test(tc, test_volt);
tcase_add_test(tc, test_integral);
tcase_add_test(tc, test_fractional);
tcase_add_test(tc, test_exponent);