diff --git a/Makefile.am b/Makefile.am index 71d560d2a..6a9ef7340 100644 --- a/Makefile.am +++ b/Makefile.am @@ -213,6 +213,14 @@ src_libdrivers_tail_la_SOURCES = src/driver_list_stop.c src_libdrivers_la_SOURCES = src/drivers.c +if HW_ADALM_2000 +src_libdrivers_la_SOURCES += \ + src/hardware/adalm-2000/m2k_wrapper.h \ + src/hardware/adalm-2000/m2k_wrapper.cpp \ + src/hardware/adalm-2000/protocol.h \ + src/hardware/adalm-2000/protocol.c \ + src/hardware/adalm-2000/api.c +endif if HW_AGILENT_DMM src_libdrivers_la_SOURCES += \ src/hardware/agilent-dmm/protocol.h \ diff --git a/configure.ac b/configure.ac index 26621fd9c..0fabf9071 100644 --- a/configure.ac +++ b/configure.ac @@ -101,6 +101,8 @@ SR_ARG_OPT_PKG([libserialport], [LIBSERIALPORT], , SR_ARG_OPT_PKG([libftdi], [LIBFTDI], , [libftdi1 >= 1.0]) +SR_ARG_OPT_PKG([libm2k], [LIBM2K], , [libm2k >= 1.0]) + # pkg-config file names: MinGW/MacOSX: hidapi; Linux: hidapi-hidraw/-libusb SR_ARG_OPT_PKG([libhidapi], [LIBHIDAPI], , [hidapi >= 0.8.0], [hidapi-hidraw >= 0.8.0], [hidapi-libusb >= 0.8.0]) @@ -257,6 +259,7 @@ m4_define([_SR_DRIVER], [ m4_define([SR_DRIVER], [_SR_DRIVER([$1], [$2], m4_expand([AS_TR_CPP([HW_$2])]), [$3])]) +SR_DRIVER([ADALM 2000], [adalm-2000], [libm2k]) SR_DRIVER([Agilent DMM], [agilent-dmm], [serial_comm]) SR_DRIVER([Appa 55II], [appa-55ii], [serial_comm]) SR_DRIVER([Arachnid Labs Re:load Pro], [arachnid-labs-re-load-pro], [serial_comm]) diff --git a/src/hardware/adalm-2000/api.c b/src/hardware/adalm-2000/api.c new file mode 100644 index 000000000..b02ab043c --- /dev/null +++ b/src/hardware/adalm-2000/api.c @@ -0,0 +1,364 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * This program is free software: you can 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 . + */ + +#include +#include "protocol.h" +#include "m2k_wrapper.h" + +static const uint32_t scanopts[] = { + SR_CONF_CONN, + SR_CONF_NUM_LOGIC_CHANNELS, +}; + +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, + SR_CONF_NUM_LOGIC_CHANNELS | SR_CONF_GET, + SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, +}; + +static const int32_t trigger_matches[] = { + SR_TRIGGER_ZERO, + SR_TRIGGER_ONE, + SR_TRIGGER_RISING, + SR_TRIGGER_FALLING, + SR_TRIGGER_EDGE, +}; + +SR_PRIV const char *adalmm2k_channel_names[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", + "13", "14", "15", +}; + +/* Possible sample rates : 10 Hz to 100 MHz */ +static const uint64_t samplerates[] = { + SR_HZ(10), + SR_MHZ(100), + SR_HZ(1), +}; + +/** + * retrieve list of devices connected + * @param[in] di + * @param[in] options + * @return list of devices + */ +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + struct sr_config *src; + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct m2k_infos *info; + GSList *infos = NULL; + GSList *l; + GSList *devices = NULL; + const char *conn; + guint i; + char tmp[32]; + + conn = NULL; + for (l = options; l; l = l->next) { + src = l->data; + if (src->key == SR_CONF_CONN) + conn = g_variant_get_string(src->data, NULL); + } + + if (!conn) + m2k_list_all(&infos); + else { + sprintf(tmp, "usb:%s", conn); + m2k_get_specific_info(tmp, &infos); + } + + for (i = 0; i < g_slist_length(infos); i++) { + info = (struct m2k_infos *)g_slist_nth_data(infos, i); + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + devc = g_malloc0(sizeof(struct dev_context)); + sdi->priv = devc; + sdi->status = SR_ST_INACTIVE; + sdi->vendor = info->vendor; + sdi->model = info->name; + sdi->serial_num = info->serial_number; + sdi->connection_id = info->uri; + + for (int chan = 0; chan < 16; chan++) { + sprintf(tmp, "%d", chan); + sr_channel_new(sdi, chan, + SR_CHANNEL_LOGIC, TRUE, tmp); + } + + devices = g_slist_append(devices, sdi); + g_free(info); + } + + return std_scan_complete(di, devices); +} + +/** open a specific device and apply default configuration + * @param[in] sdi + * return SR_ERR if something wrong, SR_OK otherwise + */ +static int dev_open(struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + + devc->m2k = m2k_open(sdi->connection_id); + if (devc->m2k == NULL) { + sr_err("No ADALM2000 device available/connected to your PC.\n"); + return SR_ERR; + } + devc->sample_buf = NULL; + devc->cur_samplerate = (uint64_t)m2k_get_rate(devc->m2k); + devc->limit_samples = 1000000; + /* set buffer to the maximum allowed size */ + devc->sample_buf = (uint16_t *)g_malloc(sizeof(short) * MAX_SAMPLES); + if (devc->sample_buf == NULL) { + m2k_close(devc->m2k); + return SR_ERR; + } + if (m2k_set_rate(devc->m2k, devc->cur_samplerate) < 0) { + sr_err("Fail to configure samplerate\n"); + return SR_ERR; + } + if (m2k_disable_trigg(devc->m2k) < 0) { + sr_err("Fail to disable trigger\n"); + return SR_ERR; + } + + return SR_OK; +} + +/** + * close and cleanup device + */ +static int dev_close(struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + + if (devc->sample_buf != NULL) { + g_free(devc->sample_buf); + devc->sample_buf = NULL; + } + + if (m2k_close(devc->m2k) < 0) { + sr_err("Fail to close\n"); + return SR_ERR; + } + + return SR_OK; +} + +/** + * return options value + * @return SR_ERR_NA if option not supported + */ +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_LIMIT_SAMPLES: + *data = g_variant_new_uint64(devc->limit_samples); + break; + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(m2k_get_rate(devc->m2k)); + break; + case SR_CONF_CAPTURE_RATIO: + *data = g_variant_new_uint64(devc->capture_ratio); + break; + case SR_CONF_NUM_LOGIC_CHANNELS: + *data = g_variant_new_uint32(g_slist_length(sdi->channels)); + break; + default: + return SR_ERR_NA; + } + + return ret; +} + +/** + * apply provided configuration value + * return SR_ERR_NA if option not supported, SR_OK otherwise + */ +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; + int ret; + + (void)cg; + + devc = sdi->priv; + + ret = SR_OK; + switch (key) { + case SR_CONF_SAMPLERATE: + devc->cur_samplerate = g_variant_get_uint64(data); + m2k_set_rate(devc->m2k, devc->cur_samplerate); + return SR_OK; + case SR_CONF_LIMIT_SAMPLES: + tmp_u64 = g_variant_get_uint64(data); + devc->limit_samples = tmp_u64; + break; + case SR_CONF_CAPTURE_RATIO: + devc->capture_ratio = g_variant_get_uint64(data); + break; + default: + ret = SR_ERR_NA; + } + + return ret; +} + +static int config_list(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + int ret; + + ret = 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_SAMPLERATE: + *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; + case SR_CONF_LIMIT_SAMPLES: + *data = std_gvar_tuple_u64(MIN_SAMPLES, MAX_SAMPLES); + break; + default: + return SR_ERR_NA; + } + + return ret; +} + +/** + * start acquisition: configure channels, pre trigger + * @return SR_ERR if something wrong, SR_OK otherwise + */ +static int dev_acquisition_start(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + GSList *l; + struct sr_channel *channel; + + devc = sdi->priv; + + /* Configure channels */ + devc->chan_en = 0; + + /* Basic triggers. */ + if (adalm_2000_convert_trigger(sdi) != TRUE) + return SR_ERR; + + for (l = sdi->channels; l; l = l->next) { + channel = l->data; + if (channel->enabled) + devc->chan_en |= (1 << channel->index); + } + + if (m2k_enable_channel(devc->m2k, devc->chan_en) < 0) { + sr_err("Fail to enable channel\n"); + return SR_ERR; + } + + /* configure pre-trigger */ + int delay = -((devc->capture_ratio * devc->limit_samples) / 100); + /* min delay is - 8192 */ + if (delay < -8192) { + sr_warn("pre trigger delay outside allowed value, set to maximum value\n"); + delay = -8192; + } + if (m2k_pre_trigger_delay(devc->m2k, delay) != 0) { + sr_err("Fail to configure pre-trigger\n"); + return SR_ERR; + } + + std_session_send_df_header(sdi); + + if (m2k_start_acquisition(devc->m2k, devc->limit_samples) < 0) { + sr_err("Fail to start acquisition\n"); + return SR_ERR; + } + + sr_session_source_add(sdi->session, -1, G_IO_IN, 0, + adalm_2000_receive_data, (void *)sdi); + + return SR_OK; +} + +/** + * stop acquisition + * return SR_ERR if something wrong, SR_OK otherwise + */ +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + devc = sdi->priv; + + sr_session_source_remove(sdi->session, -1); + + std_session_send_df_end(sdi); + + if (m2k_stop_acquisition(devc->m2k) < 0) { + sr_err("Fail to stop acquisition\n"); + return SR_ERR; + } + + return SR_OK; +} + +static struct sr_dev_driver adalm_2000_driver_info = { + .name = "adalm-2000", + .longname = "ADALM 2000", + .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(adalm_2000_driver_info); diff --git a/src/hardware/adalm-2000/m2k_wrapper.cpp b/src/hardware/adalm-2000/m2k_wrapper.cpp new file mode 100644 index 000000000..c06a15339 --- /dev/null +++ b/src/hardware/adalm-2000/m2k_wrapper.cpp @@ -0,0 +1,366 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * This program is free software: you can 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 . + */ + +#include +#include +#include + +#include +#include +#include +//#include +//#include "libsigrok-internal.h" + +#include "m2k_wrapper.h" + +using namespace std; +using namespace libm2k; +using namespace libm2k::digital; +using namespace libm2k::context; + +/** + * Open device. + * @param[in] uri: uri to the selected device. If null the first device is used + * @return a libm2k wrapper or NULL + */ +m2k_wrapper_t *m2k_open(char *uri) +{ + m2k_wrapper_t *wrap; + M2k *ctx; + M2kDigital *dig; + + wrap = (m2k_wrapper_t *)g_malloc(sizeof(m2k_wrapper_t)); + + if (uri == NULL) + ctx = m2kOpen(); + else + ctx = m2kOpen(uri); + wrap->ctx = ctx; + + if (!wrap->ctx) { + g_free(wrap); + return NULL; + } + + dig = ctx->getDigital(); + dig->setCyclic(false); + + wrap->dig = dig; + wrap->trig = dig->getTrigger(); + + return wrap; +} + +/** + * Close libm2k context and free structure + * @param[in] m2k: struct to close + * @return < 0 if device not initialized, 0 otherwise + */ +int m2k_close(m2k_wrapper_t *m2k) +{ + M2k *ctx; + + if (m2k == NULL) + return -1; + + ctx = static_cast(m2k->ctx); + if (ctx == NULL) + return -2; + + contextClose(ctx); + + g_free(m2k); + return 0; +} + +/** + * Fill m2k_infos with all required information for a specified device + * @param[in] infos: GSList contained all information + * @param[in] serial: serial number + * @param[in] product: device name + * @param[in] vendor: manufacturer name + * @param[in] id_product: VID + * @param[in] id_vendor: PID + * @param[in] uri: uri to access + */ +void m2k_fill_infos(GSList **infos, string serial, string product, + string vendor, string id_product, string id_vendor, string uri) +{ + struct m2k_infos *m2k_info; + + m2k_info = (struct m2k_infos *)g_malloc(sizeof(struct m2k_infos)); + m2k_info->uri = (char *)g_malloc(uri.size()+1); + m2k_info->serial_number = (char *)g_malloc(serial.size()+1); + m2k_info->name = (char *)g_malloc(product.size()+1); + m2k_info->vendor = (char *)g_malloc(vendor.size()+1); + m2k_info->id_product = (char *)g_malloc(id_product.size()+1); + m2k_info->id_vendor = (char *)g_malloc(id_vendor.size()+1); + + strcpy(m2k_info->uri, uri.c_str()); + strcpy(m2k_info->serial_number, serial.c_str()); + strcpy(m2k_info->name, product.c_str()); + strcpy(m2k_info->vendor, vendor.c_str()); + strcpy(m2k_info->id_product, id_product.c_str()); + strcpy(m2k_info->id_vendor, id_vendor.c_str()); + + *infos = g_slist_append(*infos, m2k_info); +} + +/** + * retrieve all required information for a device specified by uri + * @param[in] uri: device uri + * @param[in] infos: list of m2k_infos + * @return 1 when context can't be opened, otherwise 0 + */ +int m2k_get_specific_info(char *uri, GSList **infos) +{ + string sn, product, vendor, id_product, id_vendor; + M2k *ctx; + + ctx = m2kOpen(uri); + if (!ctx) + return 1; + + sn = ctx->getContextAttributeValue("usb,serial"); + product = ctx->getContextAttributeValue("usb,product"); + vendor = ctx->getContextAttributeValue("usb,vendor"); + id_product = ctx->getContextAttributeValue("usb,idProduct"); + id_vendor = ctx->getContextAttributeValue("usb,idVendor"); + + m2k_fill_infos(infos, sn, product, vendor, id_product, id_vendor, string(uri)); + + contextClose(ctx); + + return 0; +} + +/** + * retrieve information for all devices connected + * @param[in] infos: list of m2k_infos + */ +void m2k_list_all(GSList **infos) +{ + auto ctx_info = getContextsInfo(); + + for (auto i = ctx_info.begin(); i != ctx_info.end(); i++) { + struct libm2k::CONTEXT_INFO* c = (*i); + m2k_fill_infos(infos, c->serial, c->product, c->manufacturer, + c->id_product, c->id_vendor, c->uri); + } +} + +/** + * set samplerate for the device + * @param[in] m2k: libm2k wrapper + * @param[in] rate: new samplerate + * @return < 0 if device not configured, otherwise real samplerate + */ +double m2k_set_rate(m2k_wrapper_t *m2k, double rate) +{ + M2kDigital *dig; + + if (m2k == NULL) + return -1; + + dig = static_cast(m2k->dig); + if (dig == NULL) + return -2; + + return dig->setSampleRateIn(rate); +} + +/** + * get current samplerate for the device + * @param[in] m2k: libm2k wrapper + * @return < 0 if device not configured, otherwise samplerate + */ +double m2k_get_rate(m2k_wrapper_t *m2k) +{ + M2kDigital *dig; + + if (m2k == NULL) + return -1; + + dig = static_cast(m2k->dig); + if (dig == NULL) + return -2; + + return dig->getSampleRateIn(); +} + +/** + * fetch samples for device + * @param[in] m2k: libm2k wrapper + * @param[in] samples: array to store samples + * @param[in] nb_sample: number of samples to fetch + * @return < 0 if device not configured, number of samples fetch otherwise + */ +int m2k_get_sample(m2k_wrapper_t *m2k, unsigned short *samples, int nb_sample) +{ + vector buff_in; + M2kDigital *dig; + + if (m2k == NULL) + return -1; + + dig = static_cast(m2k->dig); + if (dig == NULL) + return -2; + + buff_in = dig->getSamples(nb_sample); + memcpy(samples, buff_in.data(), buff_in.size() * sizeof(unsigned short)); + + return buff_in.size(); +} + +/** + * configure specified channels as input + * @param[in] m2k: libm2k wrapper + * @param[in] channels: list of channels to configure + * @return < 0 if device not configured, 0 otherwise + */ +int m2k_enable_channel(m2k_wrapper_t *m2k, unsigned short channels) +{ + M2kDigital *dig; + + if (m2k == NULL) + return -1; + + dig = static_cast(m2k->dig); + if (dig == NULL) + return -2; + + /* mask to set all direction enabled as input */ + for (int i = 0; i < 16; i++) + if (((channels >> i) & 0x01) != 0) + dig->setDirection(i, DIO_INPUT); + + return 0; +} + +/** + * configure trigger for specified channel + * @param[in] m2k: libm2k wrapper + * @param[in] channel: specified chanel + * @param[in] cond: trigger mode + * @return < 0 if device not configured, 0 otherwise + */ +int m2k_configure_trigg(m2k_wrapper_t *m2k, uint16_t channel, uint8_t cond) +{ + M2kHardwareTrigger *trig; + + if (m2k == NULL) + return -1; + + trig = static_cast(m2k->trig); + if (trig == NULL) + return -2; + + trig->setDigitalCondition(channel, static_cast(cond)); + + return 0; +} + +/** + * disable trigger for all channels + * @param[in] m2k: libm2k wrapper + * @return < 0 if device not configured, 0 otherwise + */ +int m2k_disable_trigg(m2k_wrapper_t *m2k) +{ + M2kHardwareTrigger *trig; + + if (m2k == NULL) + return -1; + + trig = static_cast(m2k->trig); + if (trig == NULL) + return -2; + + for (int channel = 0; channel < 16; channel++) + trig->setDigitalCondition(channel, NO_TRIGGER_DIGITAL); + + return 0; +} + +/** + * configure pre trigger + * @param[in] m2k: libm2k wrapper + * @param[in] delay: number of samples before trig (max -8192) + * @return < 0 if device not configured, -3 if delay not applied, 0 otherwise + */ +int m2k_pre_trigger_delay(m2k_wrapper_t *m2k, int delay) +{ + M2kHardwareTrigger *trig; + + if (m2k == NULL) + return -1; + + trig = static_cast(m2k->trig); + if (trig == NULL) + return -2; + + trig->setDigitalDelay(delay); + if (delay != trig->getDigitalDelay()) + return -3; + + return 0; +} + +/** + * disable acquisition + * @param[in] m2k: libm2k wrapper + * @return < 0 if device not configured, 0 otherwise + */ +int m2k_stop_acquisition(m2k_wrapper_t *m2k) +{ + M2kDigital *dig; + + if (m2k == NULL) + return -1; + + dig = static_cast(m2k->dig); + if (dig == NULL) + return -2; + + dig->stopAcquisition(); + + return 0; +} + +/** + * start acquisition + * @param[in] m2k: libm2k wrapper + * @return < 0 if device not configured, 0 otherwise + */ +int m2k_start_acquisition(m2k_wrapper_t *m2k, int nb_sample) +{ + M2kDigital *dig; + + if (m2k == NULL) + return -1; + + dig = static_cast(m2k->dig); + if (dig == NULL) + return -2; + + dig->startAcquisition(nb_sample); + + return 0; +} diff --git a/src/hardware/adalm-2000/m2k_wrapper.h b/src/hardware/adalm-2000/m2k_wrapper.h new file mode 100644 index 000000000..324c490d7 --- /dev/null +++ b/src/hardware/adalm-2000/m2k_wrapper.h @@ -0,0 +1,78 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * This program is free software: you can 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 . + */ + +#ifndef LIBSIGROK_HARDWARE_ADALM_2000_M2K_WRAPPER_H +#define LIBSIGROK_HARDWARE_ADALM_2000_M2K_WRAPPER_H + +#ifdef __cplusplus /* if C++, specify external linkage to C functions */ +extern "C" { +#endif + +/* wrapper between C and C++ + */ +struct m2k_wrapper { + void *ctx; /* m2k context */ + void *dig; /* digital context */ + void *trig; /* hardware trigger */ +}; + +typedef struct m2k_wrapper m2k_wrapper_t; + +/** + * struct used to store device information + */ +struct m2k_infos { + char *name; + char *vendor; + char *id_product; + char *id_vendor; + char *serial_number; + char *uri; +}; + +/* m2k_wrapper handling */ +m2k_wrapper_t *m2k_open(char *uri); +int m2k_close(m2k_wrapper_t *m2k); + +/* devices information */ +int m2k_get_specific_info(char *uri, GSList **infos); +void m2k_list_all(GSList **infos); + +/* samplerate configuration */ +double m2k_set_rate(m2k_wrapper_t *m2k, double rate); +double m2k_get_rate(m2k_wrapper_t *m2k); + +/* channel configuration */ +int m2k_enable_channel(m2k_wrapper_t *m2k, unsigned short channels); + +/* trigger configuration */ +int m2k_configure_trigg(m2k_wrapper_t *m2k, uint16_t channel, uint8_t cond); +int m2k_disable_trigg(m2k_wrapper_t *m2k); +int m2k_pre_trigger_delay(m2k_wrapper_t *m2k, int delay); + +/* acquisition */ +int m2k_start_acquisition(m2k_wrapper_t *m2k, int nb_sample); +int m2k_stop_acquisition(m2k_wrapper_t *m2k); +int m2k_get_sample(m2k_wrapper_t *m2k, unsigned short *samples, int nb_sample); + +#ifdef __cplusplus +} +#endif + +#endif // LIBSIGROK_HARDWARE_ADALM_2000_M2K_WRAPPER_H diff --git a/src/hardware/adalm-2000/protocol.c b/src/hardware/adalm-2000/protocol.c new file mode 100644 index 000000000..0bc892b94 --- /dev/null +++ b/src/hardware/adalm-2000/protocol.c @@ -0,0 +1,141 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * This program is free software: you can 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 . + */ + +#include +#include "protocol.h" +#include "m2k_wrapper.h" + +#define SAMPLEUNIT (sizeof(unsigned short)) + +/** + * configure trigger. In all case disable trigger, if needed reconfigure them + * @return FALSE if not allowed configured, TRUE otherwise + */ +SR_PRIV int adalm_2000_convert_trigger(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_trigger *trigger; + struct sr_trigger_match *match; + struct sr_trigger_stage *stage; + const GSList *l, *m; + + devc = sdi->priv; + + /* security: disable trigger */ + if (m2k_disable_trigg(devc->m2k) < 0) { + sr_err("fail to disable trigger\n"); + return FALSE; + } + + trigger = sr_session_trigger_get(sdi->session); + if (!trigger) + return TRUE; + + /* currently one stage trigger supported */ + if (g_slist_length(trigger->stages) > 1) { + sr_err("This device only supports 1 trigger stages."); + return FALSE; + } + + for (l = trigger->stages; l; l = l->next) { + stage = l->data; + /* single trigger only */ + if (g_slist_length(stage->matches) > 1) { + sr_err("Error only one channel supported for trigger\n"); + return FALSE; + } + + for (m = stage->matches; m; m = m->next) { + match = m->data; + switch (match->match) { + case SR_TRIGGER_ZERO: + devc->triggerflags = LOW_LEVEL_DIGITAL; + break; + case SR_TRIGGER_ONE: + devc->triggerflags = HIGH_LEVEL_DIGITAL; + break; + case SR_TRIGGER_RISING: + devc->triggerflags = RISING_EDGE_DIGITAL; + break; + case SR_TRIGGER_FALLING: + devc->triggerflags = FALLING_EDGE_DIGITAL; + break; + case SR_TRIGGER_EDGE: + devc->triggerflags = ANY_EDGE_DIGITAL; + break; + default: + devc->triggerflags = NO_TRIGGER_DIGITAL; + } + if (m2k_configure_trigg(devc->m2k, + match->channel->index, + devc->triggerflags) < 0) { + sr_err("fail to configure trigger source\n"); + return FALSE; + } + } + } + + return TRUE; +} + +/** + * do acquisition and push received data + * @return FALSE if something wrong, TRUE otherwise + */ +SR_PRIV int adalm_2000_receive_data(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_datafeed_logic logic; + struct sr_datafeed_packet packet; + int bytes_read; + int nb_samples; + + (void)fd; + + sdi = cb_data; + if (!sdi) + return TRUE; + devc = sdi->priv; + if (!devc) + return TRUE; + if (!(revents == G_IO_IN || revents == 0)) + return TRUE; + if (!devc->m2k) + return TRUE; + + nb_samples = devc->limit_samples; + + bytes_read = m2k_get_sample(devc->m2k, devc->sample_buf, nb_samples); + if (bytes_read < 0) { + sr_err("Fail to fetch samples\n"); + return FALSE; + } + + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + logic.length = SAMPLEUNIT * bytes_read; + logic.unitsize = SAMPLEUNIT; + logic.data = (unsigned char *)devc->sample_buf; + + sr_session_send(sdi, &packet); + sr_dev_acquisition_stop(sdi); + + return TRUE; +} diff --git a/src/hardware/adalm-2000/protocol.h b/src/hardware/adalm-2000/protocol.h new file mode 100644 index 000000000..45abe6f5b --- /dev/null +++ b/src/hardware/adalm-2000/protocol.h @@ -0,0 +1,68 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * This program is free software: you can 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 . + */ + +#ifndef LIBSIGROK_HARDWARE_ADALM_2000_PROTOCOL_H +#define LIBSIGROK_HARDWARE_ADALM_2000_PROTOCOL_H + +#include +#include +#include +#include "libsigrok-internal.h" +#include "m2k_wrapper.h" + +#define LOG_PREFIX "adalm-2000" + +/* Maximum possible input channels */ +#define NUM_CHANNELS 16 +/* Samples limit */ +#define MIN_SAMPLES 16 +#define MAX_SAMPLES 5000000 + +enum m2k_trigger_digital { + RISING_EDGE_DIGITAL = 0, + FALLING_EDGE_DIGITAL = 1, + LOW_LEVEL_DIGITAL = 2, + HIGH_LEVEL_DIGITAL = 3, + ANY_EDGE_DIGITAL = 4, + NO_TRIGGER_DIGITAL = 5, +}; + +/** Private, per-device-instance driver context. */ +struct dev_context { + /* m2k wrapper to deal with libm2k */ + m2k_wrapper_t *m2k; + + /* Acquisition settings */ + uint64_t cur_samplerate; + uint64_t limit_samples; + uint32_t triggerflags; + uint64_t capture_ratio; + + /* channels */ + uint16_t chan_en; + + uint64_t bytes_read; + uint64_t sent_samples; + uint16_t *sample_buf; /* mmap'd kernel buffer here */ +}; + +SR_PRIV int adalm_2000_convert_trigger(const struct sr_dev_inst *sdi); +SR_PRIV int adalm_2000_receive_data(int fd, int revents, void *cb_data); + +#endif