From: magnuskarlsson Date: Sat, 10 May 2014 22:44:13 +0000 (-0700) Subject: added pipistrello-ols X-Git-Tag: libsigrok-0.4.0~1127 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=4bd80e12287dbc056f1431e42a17a0cb60010abc;p=libsigrok.git added pipistrello-ols Conflicts: configure.ac src/hwdriver.c --- diff --git a/Makefile.am b/Makefile.am index 78ae7c7d..e7b63efb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -264,6 +264,12 @@ libsigrok_la_SOURCES += \ src/hardware/openbench-logic-sniffer/protocol.c \ src/hardware/openbench-logic-sniffer/api.c endif +if HW_PIPISTRELLO_OLS +libsigrok_la_SOURCES += \ + src/hardware/pipistrello-ols/protocol.h \ + src/hardware/pipistrello-ols/protocol.c \ + src/hardware/pipistrello-ols/api.c +endif if HW_RIGOL_DS libsigrok_la_SOURCES += \ src/hardware/rigol-ds/protocol.h \ diff --git a/README.devices b/README.devices index db651faa..0f089f10 100644 --- a/README.devices +++ b/README.devices @@ -75,6 +75,7 @@ The following drivers/devices do not need any firmware upload: - mic-985xx (including all subdrivers) - norma-dmm - openbench-logic-sniffer + - pipistrello-ols - rigol-ds - serial-dmm (including all subdrivers) - teleinfo @@ -133,6 +134,7 @@ The following drivers/devices do not require a serial port specification: - ikalogic-scanaplus - kecheng-kc-330b - lascar-el-usb + - pipistrello-ols - rigol-ds (USBTMC or TCP) - saleae-logic16 - sysclk-lwla diff --git a/configure.ac b/configure.ac index cc51d324..d5a9e896 100644 --- a/configure.ac +++ b/configure.ac @@ -129,6 +129,7 @@ DRIVER([MIC 985xx], [mic-985xx]) DRIVER([Motech LPS 30x], [motech-lps-30x]) DRIVER([Norma DMM], [norma-dmm]) DRIVER([OpenBench Logic Sniffer], [openbench-logic-sniffer]) +DRIVER([Pipistrello-OLS], [pipistrello-ols]) DRIVER([Rigol DS], [rigol-ds]) DRIVER([Saleae Logic16], [saleae-logic16]) DRIVER([serial DMM], [serial-dmm]) @@ -375,6 +376,7 @@ fi if test "x$have_libftdi" = "xno"; then HW_ASIX_SIGMA="no"; HW_CHRONOVU_LA="no"; HW_IKALOGIC_SCANAPLUS="no"; + HW_PIPISTRELLO_OLS="no"; fi # glibmm-2.4 is needed for the C++ bindings. @@ -581,6 +583,11 @@ if test "x$HW_OPENBENCH_LOGIC_SNIFFER" = "xyes"; then AC_DEFINE(HAVE_HW_OPENBENCH_LOGIC_SNIFFER, 1, [OpenBench Logic Sniffer (OLS) support]) fi +AM_CONDITIONAL(HW_PIPISTRELLO_OLS, test x$HW_PIPISTRELLO_OLS = xyes) +if test "x$HW_PIPISTRELLO_OLS" = "xyes"; then + AC_DEFINE(HAVE_HW_PIPISTRELLO_OLS, 1, [Pipistrello OLS support]) +fi + AM_CONDITIONAL(HW_RIGOL_DS, test x$HW_RIGOL_DS = xyes) if test "x$HW_RIGOL_DS" = "xyes"; then AC_DEFINE(HAVE_HW_RIGOL_DS, 1, [Rigol DS support]) diff --git a/src/drivers.c b/src/drivers.c index 8aa9acf5..14936789 100644 --- a/src/drivers.c +++ b/src/drivers.c @@ -109,6 +109,9 @@ extern SR_PRIV struct sr_dev_driver siemens_b102x_driver_info; #ifdef HAVE_HW_OPENBENCH_LOGIC_SNIFFER extern SR_PRIV struct sr_dev_driver ols_driver_info; #endif +#ifdef HAVE_HW_PIPISTRELLO_OLS +extern SR_PRIV struct sr_dev_driver p_ols_driver_info; +#endif #ifdef HAVE_HW_RIGOL_DS extern SR_PRIV struct sr_dev_driver rigol_ds_driver_info; #endif @@ -277,6 +280,9 @@ SR_PRIV struct sr_dev_driver *drivers_list[] = { #ifdef HAVE_HW_OPENBENCH_LOGIC_SNIFFER &ols_driver_info, #endif +#ifdef HAVE_HW_PIPISTRELLO_OLS + &p_ols_driver_info, +#endif #ifdef HAVE_HW_RIGOL_DS &rigol_ds_driver_info, #endif diff --git a/src/hardware/pipistrello-ols/api.c b/src/hardware/pipistrello-ols/api.c new file mode 100644 index 00000000..7840b58a --- /dev/null +++ b/src/hardware/pipistrello-ols/api.c @@ -0,0 +1,678 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * + * This program is free software: you can 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 "protocol.h" + +#define USB_VENDOR_ID 0x0403 +#define USB_DEVICE_ID 0x6010 +#define USB_VENDOR_NAME "Saanlima" +#define USB_IPRODUCT "Pipistrello LX45" + +static const int32_t hwcaps[] = { + SR_CONF_LOGIC_ANALYZER, + SR_CONF_SAMPLERATE, + SR_CONF_TRIGGER_TYPE, + SR_CONF_CAPTURE_RATIO, + SR_CONF_LIMIT_SAMPLES, + SR_CONF_PATTERN_MODE, + SR_CONF_EXTERNAL_CLOCK, + SR_CONF_SWAP, + SR_CONF_RLE, +}; + +#define STR_PATTERN_NONE "None" +#define STR_PATTERN_EXTERNAL "External" +#define STR_PATTERN_INTERNAL "Internal" + +/* Supported methods of test pattern outputs */ +enum { + /** + * Capture pins 31:16 (unbuffered wing) output a test pattern + * that can captured on pins 0:15. + */ + PATTERN_EXTERNAL, + + /** Route test pattern internally to capture buffer. */ + PATTERN_INTERNAL, +}; + +static const char *patterns[] = { + STR_PATTERN_NONE, + STR_PATTERN_EXTERNAL, + STR_PATTERN_INTERNAL, +}; + +/* Channels are numbered 0-31 (on the PCB silkscreen). */ +SR_PRIV const char *p_ols_channel_names[NUM_CHANNELS + 1] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", + "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", + "24", "25", "26", "27", "28", "29", "30", "31", + NULL, +}; + +/* Default supported samplerates, can be overridden by device metadata. */ +static const uint64_t samplerates[] = { + SR_HZ(10), + SR_MHZ(200), + SR_HZ(1), +}; + +SR_PRIV struct sr_dev_driver p_ols_driver_info; +static struct sr_dev_driver *di = &p_ols_driver_info; + +static int init(struct sr_context *sr_ctx) +{ + return std_init(sr_ctx, di, LOG_PREFIX); +} + +static GSList *scan(GSList *options) +{ + struct sr_dev_inst *sdi; + struct drv_context *drvc; + struct dev_context *devc; + GSList *devices; + int ret, i; + char buf[70]; + int bytes_read; + + (void)options; + + drvc = di->priv; + + devices = NULL; + + /* Allocate memory for our private device context. */ + if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) { + sr_err("Device context malloc failed."); + goto err_free_nothing; + } + + /* Device-specific settings */ + devc->max_samples = 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. */ + if (!(devc->ftdi_buf = g_try_malloc0(FTDI_BUF_SIZE))) { + sr_err("ftdi_buf malloc failed."); + goto err_free_devc; + } + + /* Allocate memory for the FTDI context (ftdic) and initialize it. */ + if (!(devc->ftdic = ftdi_new())) { + sr_err("Failed to initialize libftdi."); + goto err_free_ftdi_buf;; + } + + /* Try to open the FTDI device */ + if (p_ols_open(devc) != SR_OK) { + goto err_free_ftdic; + } + + /* The discovery procedure is like this: first send the Reset + * command (0x00) 5 times, since the device could be anywhere + * in a 5-byte command. Then send the ID command (0x02). + * If the device responds with 4 bytes ("OLS1" or "SLA1"), we + * have a match. + */ + + ret = SR_OK; + for (i = 0; i < 5; i++) { + if ((ret = write_shortcommand(devc, CMD_RESET)) != SR_OK) { + break; + } + } + if (ret != SR_OK) { + sr_err("Could not reset device. Quitting."); + goto err_close_ftdic; + } + write_shortcommand(devc, CMD_ID); + + /* Read the response data. */ + bytes_read = ftdi_read_data(devc->ftdic, (uint8_t *)buf, 4); + if (bytes_read < 0) { + sr_err("Failed to read FTDI data (%d): %s.", + bytes_read, ftdi_get_error_string(devc->ftdic)); + goto err_close_ftdic; + } + if (bytes_read == 0) { + goto err_close_ftdic; + } + + if (strncmp(buf, "1SLO", 4) && strncmp(buf, "1ALS", 4)) + goto err_close_ftdic; + + /* Definitely using the OLS protocol, check if it supports + * the metadata command. + */ + write_shortcommand(devc, CMD_METADATA); + + /* Read the metadata. */ + bytes_read = ftdi_read_data(devc->ftdic, (uint8_t *)buf, 64); + if (bytes_read < 0) { + sr_err("Failed to read FTDI data (%d): %s.", + bytes_read, ftdi_get_error_string(devc->ftdic)); + goto err_close_ftdic; + } + if (bytes_read == 0) { + goto err_close_ftdic; + } + + /* Close device. We'll reopen it again when we need it. */ + p_ols_close(devc); + + /* Parse the metadata. */ + sdi = p_ols_get_metadata((uint8_t *)buf, bytes_read, devc); + sdi->index = 0; + + /* Configure samplerate and divider. */ + if (p_ols_set_samplerate(sdi, DEFAULT_SAMPLERATE) != SR_OK) + sr_dbg("Failed to set default samplerate (%"PRIu64").", + DEFAULT_SAMPLERATE); + /* Clear trigger masks, values and stages. */ + p_ols_configure_channels(sdi); + + drvc->instances = g_slist_append(drvc->instances, sdi); + devices = g_slist_append(devices, sdi); + + return devices; + +err_close_ftdic: + p_ols_close(devc); +err_free_ftdic: + ftdi_free(devc->ftdic); /* NOT free() or g_free()! */ +err_free_ftdi_buf: + g_free(devc->ftdi_buf); +err_free_devc: + g_free(devc); +err_free_nothing: + + return NULL; +} + +static GSList *dev_list(void) +{ + return ((struct drv_context *)(di->priv))->instances; +} + +static void clear_helper(void *priv) +{ + struct dev_context *devc; + + devc = priv; + + ftdi_free(devc->ftdic); + g_free(devc->ftdi_buf); +} + +static int dev_clear(void) +{ + return std_dev_clear(di, clear_helper); +} + +static int cleanup(void) +{ + return dev_clear(); +} + + +static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + struct dev_context *devc; + + (void)cg; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + switch (id) { + 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_LIMIT_SAMPLES: + *data = g_variant_new_uint64(devc->limit_samples); + break; + case SR_CONF_PATTERN_MODE: + if (devc->flag_reg & FLAG_EXTERNAL_TEST_MODE) + *data = g_variant_new_string(STR_PATTERN_EXTERNAL); + else if (devc->flag_reg & FLAG_INTERNAL_TEST_MODE) + *data = g_variant_new_string(STR_PATTERN_INTERNAL); + else + *data = g_variant_new_string(STR_PATTERN_NONE); + break; + case SR_CONF_RLE: + *data = g_variant_new_boolean(devc->flag_reg & FLAG_RLE ? TRUE : FALSE); + break; + case SR_CONF_EXTERNAL_CLOCK: + *data = g_variant_new_boolean(devc->flag_reg & FLAG_CLOCK_EXTERNAL ? TRUE : FALSE); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + struct dev_context *devc; + 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 (id) { + case SR_CONF_SAMPLERATE: + tmp_u64 = g_variant_get_uint64(data); + if (tmp_u64 < samplerates[0] || tmp_u64 > samplerates[1]) + return SR_ERR_SAMPLERATE; + ret = p_ols_set_samplerate(sdi, g_variant_get_uint64(data)); + break; + 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) { + devc->capture_ratio = 0; + ret = SR_ERR; + } else + ret = SR_OK; + break; + case SR_CONF_EXTERNAL_CLOCK: + if (g_variant_get_boolean(data)) { + sr_info("Enabling external clock."); + devc->flag_reg |= FLAG_CLOCK_EXTERNAL; + } else { + 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)) { + 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; + } + break; + case SR_CONF_SWAP: + if (g_variant_get_boolean(data)) { + sr_info("Enabling channel swapping."); + devc->flag_reg |= FLAG_SWAP_CHANNELS; + } else { + 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."); + devc->flag_reg |= FLAG_RLE; + } else { + sr_info("Disabling RLE."); + devc->flag_reg &= ~FLAG_RLE; + } + ret = SR_OK; + break; + default: + ret = SR_ERR_NA; + } + + return ret; +} + +static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + struct dev_context *devc; + GVariant *gvar, *grange[2]; + GVariantBuilder gvb; + int num_channels, i; + + (void)cg; + + switch (key) { + case SR_CONF_DEVICE_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, + hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t)); + break; + case SR_CONF_SAMPLERATE: + g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); + gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates, + ARRAY_SIZE(samplerates), sizeof(uint64_t)); + g_variant_builder_add(&gvb, "{sv}", "samplerate-steps", gvar); + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_TRIGGER_TYPE: + *data = g_variant_new_string(TRIGGER_TYPE); + break; + case SR_CONF_PATTERN_MODE: + *data = g_variant_new_strv(patterns, ARRAY_SIZE(patterns)); + break; + case SR_CONF_LIMIT_SAMPLES: + if (!sdi) + return SR_ERR_ARG; + devc = sdi->priv; + if (devc->flag_reg & FLAG_RLE) + return SR_ERR_NA; + if (devc->max_samples == 0) + /* Device didn't specify sample memory size in metadata. */ + return SR_ERR_NA; + /* + * Channel groups are turned off if no channels in that group are + * enabled, making more room for samples for the enabled group. + */ + p_ols_configure_channels(sdi); + num_channels = 0; + for (i = 0; i < 4; i++) { + if (devc->channel_mask & (0xff << (i * 8))) + num_channels++; + } + if (num_channels == 0) { + /* This can happen, but shouldn't cause too much drama. + * However we can't continue because the code below would + * divide by zero. */ + break; + } + grange[0] = g_variant_new_uint64(MIN_NUM_SAMPLES); + grange[1] = g_variant_new_uint64(devc->max_samples / num_channels); + *data = g_variant_new_tuple(grange, 2); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int dev_open(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + int ret; + + devc = sdi->priv; + + if (p_ols_open(devc) != SR_OK) { + return SR_ERR; + } else { + sdi->status = SR_ST_ACTIVE; + return SR_OK; + } +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + int ret; + struct dev_context *devc; + + ret = SR_OK; + devc = sdi->priv; + + if (sdi->status == SR_ST_ACTIVE) { + sr_dbg("Status ACTIVE, closing device."); + ret = p_ols_close(devc); + } else { + sr_spew("Status not ACTIVE, nothing to do."); + } + + sdi->status = SR_ST_INACTIVE; + + return ret; +} + + +static int set_trigger(const struct sr_dev_inst *sdi, int stage) +{ + struct dev_context *devc; + uint8_t cmd, arg[4]; + + devc = sdi->priv; + + cmd = CMD_SET_TRIGGER_MASK + stage * 4; + arg[0] = devc->trigger_mask[stage] & 0xff; + arg[1] = (devc->trigger_mask[stage] >> 8) & 0xff; + arg[2] = (devc->trigger_mask[stage] >> 16) & 0xff; + arg[3] = (devc->trigger_mask[stage] >> 24) & 0xff; + if (write_longcommand(devc, cmd, arg) != SR_OK) + return SR_ERR; + + cmd = CMD_SET_TRIGGER_VALUE + stage * 4; + arg[0] = devc->trigger_value[stage] & 0xff; + arg[1] = (devc->trigger_value[stage] >> 8) & 0xff; + arg[2] = (devc->trigger_value[stage] >> 16) & 0xff; + arg[3] = (devc->trigger_value[stage] >> 24) & 0xff; + if (write_longcommand(devc, cmd, arg) != SR_OK) + return SR_ERR; + + cmd = CMD_SET_TRIGGER_CONFIG + stage * 4; + arg[0] = arg[1] = arg[3] = 0x00; + arg[2] = stage; + if (stage == devc->num_stages) + /* Last stage, fire when this one matches. */ + arg[3] |= TRIGGER_START; + if (write_longcommand(devc, cmd, arg) != SR_OK) + return SR_ERR; + + return SR_OK; +} + +static int dev_acquisition_start(const struct sr_dev_inst *sdi, + void *cb_data) +{ + struct dev_context *devc; + uint32_t samplecount, readcount, delaycount; + uint8_t changrp_mask, arg[4]; + int num_channels; + int ret, i; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + devc = sdi->priv; + + if (p_ols_configure_channels(sdi) != SR_OK) { + sr_err("Failed to configure channels."); + return SR_ERR; + } + + /* + * Enable/disable channel groups in the flag register according to the + * channel mask. Calculate this here, because num_channels is needed + * to limit readcount. + */ + changrp_mask = 0; + num_channels = 0; + for (i = 0; i < 4; i++) { + if (devc->channel_mask & (0xff << (i * 8))) { + changrp_mask |= (1 << i); + num_channels++; + } + } + + /* + * Limit readcount to prevent reading past the end of the hardware + * buffer. + */ + sr_dbg("max_samples = %d", devc->max_samples); + sr_dbg("limit_samples = %d", devc->limit_samples); + samplecount = MIN(devc->max_samples / num_channels, devc->limit_samples); + readcount = samplecount / 4; + sr_dbg("Samplecount = %d", samplecount); + + /* Rather read too many samples than too few. */ + if (samplecount % 4 != 0) + readcount++; + + /* Basic triggers. */ + if (devc->trigger_mask[0] != 0x00000000) { + /* At least one channel has a trigger on it. */ + delaycount = readcount * (1 - devc->capture_ratio / 100.0); + devc->trigger_at = (readcount - delaycount) * 4 - devc->num_stages; + for (i = 0; i <= devc->num_stages; i++) { + sr_dbg("Setting stage %d trigger.", i); + if ((ret = set_trigger(sdi, i)) != SR_OK) + return ret; + } + } else { + /* No triggers configured, force trigger on first stage. */ + sr_dbg("Forcing trigger at stage 0."); + if ((ret = set_trigger(sdi, 0)) != SR_OK) + return ret; + delaycount = readcount; + } + + /* Samplerate. */ + sr_dbg("Setting samplerate to %" PRIu64 "Hz (divider %u)", + devc->cur_samplerate, devc->cur_samplerate_divider); + arg[0] = devc->cur_samplerate_divider & 0xff; + arg[1] = (devc->cur_samplerate_divider & 0xff00) >> 8; + arg[2] = (devc->cur_samplerate_divider & 0xff0000) >> 16; + arg[3] = 0x00; + if (write_longcommand(devc, CMD_SET_DIVIDER, arg) != SR_OK) + return SR_ERR; + /* Send extended sample limit and pre/post-trigger capture ratio. */ + arg[0] = ((readcount - 1) & 0xff); + arg[1] = ((readcount - 1) & 0xff00) >> 8; + arg[2] = ((readcount - 1) & 0xff0000) >> 16; + arg[3] = ((readcount - 1) & 0xff000000) >> 24; + if (write_longcommand(devc, CMD_CAPTURE_COUNT, arg) != SR_OK) + return SR_ERR; + arg[0] = ((delaycount - 1) & 0xff); + arg[1] = ((delaycount - 1) & 0xff00) >> 8; + arg[2] = ((delaycount - 1) & 0xff0000) >> 16; + arg[3] = ((delaycount - 1) & 0xff000000) >> 24; + if (write_longcommand(devc, CMD_CAPTURE_DELAY, arg) != SR_OK) + return SR_ERR; + /* Flag register. */ + sr_dbg("Setting intpat %s, extpat %s, RLE %s, noise_filter %s, demux %s", + devc->flag_reg & FLAG_INTERNAL_TEST_MODE ? "on": "off", + devc->flag_reg & FLAG_EXTERNAL_TEST_MODE ? "on": "off", + devc->flag_reg & FLAG_RLE ? "on" : "off", + devc->flag_reg & FLAG_FILTER ? "on": "off", + devc->flag_reg & FLAG_DEMUX ? "on" : "off"); + + /* 1 means "disable channel". */ + devc->flag_reg |= ~(changrp_mask << 2) & 0x3c; + arg[0] = devc->flag_reg & 0xff; + arg[1] = devc->flag_reg >> 8; + arg[2] = arg[3] = 0x00; + if (write_longcommand(devc, CMD_SET_FLAGS, arg) != SR_OK) + return SR_ERR; + + /* Start acquisition on the device. */ + if (write_shortcommand(devc, CMD_RUN) != SR_OK) + return SR_ERR; + + /* Reset all operational states. */ + devc->rle_count = devc->num_transfers = 0; + devc->num_samples = devc->num_bytes = 0; + devc->cnt_bytes = devc->cnt_samples = devc->cnt_samples_rle = 0; + memset(devc->sample, 0, 4); + + /* Send header packet to the session bus. */ + std_session_send_df_header(cb_data, LOG_PREFIX); + + /* Hook up a dummy handler to receive data from the device. */ + sr_source_add(-1, G_IO_IN, 0, p_ols_receive_data, (void *)sdi); + + return SR_OK; +} + + + +static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) +{ + struct dev_context *devc; + struct sr_datafeed_packet packet; + + devc = sdi->priv; + + sr_dbg("Stopping acquisition."); + write_shortcommand(devc, CMD_RESET); + write_shortcommand(devc, CMD_RESET); + write_shortcommand(devc, CMD_RESET); + write_shortcommand(devc, CMD_RESET); + write_shortcommand(devc, CMD_RESET); + + sr_source_remove(-1); + + /* Send end packet to the session bus. */ + sr_dbg("Sending SR_DF_END."); + packet.type = SR_DF_END; + sr_session_send(cb_data, &packet); + + return SR_OK; +} + +SR_PRIV struct sr_dev_driver p_ols_driver_info = { + .name = "p_ols", + .longname = "Pipistrello OLS", + .api_version = 1, + .init = init, + .cleanup = cleanup, + .scan = scan, + .dev_list = dev_list, + .dev_clear = dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .dev_open = dev_open, + .dev_close = dev_close, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .priv = NULL, +}; diff --git a/src/hardware/pipistrello-ols/protocol.c b/src/hardware/pipistrello-ols/protocol.c new file mode 100644 index 00000000..9f4ad7d7 --- /dev/null +++ b/src/hardware/pipistrello-ols/protocol.c @@ -0,0 +1,576 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * + * This program is free software: you can 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 "protocol.h" + +extern SR_PRIV struct sr_dev_driver p_ols_driver_info; +static struct sr_dev_driver *di = &p_ols_driver_info; + +SR_PRIV int write_shortcommand(struct dev_context *devc, uint8_t command) +{ + uint8_t buf[1]; + int bytes_written; + + sr_dbg("Sending cmd 0x%.2x.", command); + buf[0] = command; + bytes_written = ftdi_write_data(devc->ftdic, buf, 1); + if (bytes_written < 0) { + sr_err("Failed to write FTDI data (%d): %s.", + bytes_written, ftdi_get_error_string(devc->ftdic)); + return SR_ERR; + } else if (bytes_written != 1) { + sr_err("FTDI write error, only %d/%d bytes written: %s.", + bytes_written, 1, ftdi_get_error_string(devc->ftdic)); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int write_longcommand(struct dev_context *devc, uint8_t command, uint8_t *data) +{ + uint8_t buf[5]; + int bytes_written; + + sr_dbg("Sending cmd 0x%.2x data 0x%.2x%.2x%.2x%.2x.", command, + data[0], data[1], data[2], data[3]); + buf[0] = command; + buf[1] = data[0]; + buf[2] = data[1]; + buf[3] = data[2]; + buf[4] = data[3]; + bytes_written = ftdi_write_data(devc->ftdic, buf, 5); + if (bytes_written < 0) { + sr_err("Failed to write FTDI data (%d): %s.", + bytes_written, ftdi_get_error_string(devc->ftdic)); + return SR_ERR; + } else if (bytes_written != 5) { + sr_err("FTDI write error, only %d/%d bytes written: %s.", + bytes_written, 1, ftdi_get_error_string(devc->ftdic)); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int p_ols_open(struct dev_context *devc) +{ + int ret; + + /* Note: Caller checks devc and devc->ftdic. */ + + /* Select interface B, otherwise communication will fail. */ + ret = ftdi_set_interface(devc->ftdic, INTERFACE_B); + if (ret < 0) { + sr_err("Failed to set FTDI interface B (%d): %s", ret, + ftdi_get_error_string(devc->ftdic)); + return SR_ERR; + } + sr_dbg("FTDI chip interface B set successfully."); + + /* Check for the device and temporarily open it. */ + ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID, USB_DEVICE_ID, + USB_IPRODUCT, NULL); + if (ret < 0) { + /* Log errors, except for -3 ("device not found"). */ + if (ret != -3) + sr_err("Failed to open device (%d): %s", ret, + ftdi_get_error_string(devc->ftdic)); + return SR_ERR; + } + sr_dbg("FTDI device opened successfully."); + + /* Purge RX/TX buffers in the FTDI chip. */ + if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) { + sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.", + ret, ftdi_get_error_string(devc->ftdic)); + goto err_open_close_ftdic; + } + sr_dbg("FTDI chip buffers purged successfully."); + + /* Reset the FTDI bitmode. */ + ret = ftdi_set_bitmode(devc->ftdic, 0xff, BITMODE_RESET); + if (ret < 0) { + sr_err("Failed to reset the FTDI chip bitmode (%d): %s.", + ret, ftdi_get_error_string(devc->ftdic)); + goto err_open_close_ftdic; + } + sr_dbg("FTDI chip bitmode reset successfully."); + + /* Set the FTDI latency timer to 16. */ + ret = ftdi_set_latency_timer(devc->ftdic, 16); + if (ret < 0) { + sr_err("Failed to set FTDI latency timer (%d): %s.", + ret, ftdi_get_error_string(devc->ftdic)); + goto err_open_close_ftdic; + } + sr_dbg("FTDI chip latency timer set successfully."); + + /* Set the FTDI read data chunk size to 64kB. */ + ret = ftdi_read_data_set_chunksize(devc->ftdic, 64 * 1024); + if (ret < 0) { + sr_err("Failed to set FTDI read data chunk size (%d): %s.", + ret, ftdi_get_error_string(devc->ftdic)); + goto err_open_close_ftdic; + } + sr_dbg("FTDI chip read data chunk size set successfully."); + + return SR_OK; + +err_open_close_ftdic: + ftdi_usb_close(devc->ftdic)) + return SR_ERR; +} + +SR_PRIV int p_ols_close(struct dev_context *devc) +{ + int ret; + + /* Note: Caller checks devc and devc->ftdic. */ + + if ((ret = ftdi_usb_close(devc->ftdic)) < 0) { + sr_err("Failed to close FTDI device (%d): %s.", + ret, ftdi_get_error_string(devc->ftdic)); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int p_ols_configure_channels(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + const struct sr_channel *ch; + const GSList *l; + int channel_bit, stage, i; + char *tc; + + devc = sdi->priv; + + devc->channel_mask = 0; + for (i = 0; i < NUM_TRIGGER_STAGES; i++) { + devc->trigger_mask[i] = 0; + devc->trigger_value[i] = 0; + } + + devc->num_stages = 0; + for (l = sdi->channels; l; l = l->next) { + ch = (const struct sr_channel *)l->data; + if (!ch->enabled) + continue; + + if (ch->index >= devc->max_channels) { + sr_err("Channels over the limit of %d\n", devc->max_channels); + return SR_ERR; + } + + /* + * Set up the channel mask for later configuration into the + * flag register. + */ + channel_bit = 1 << (ch->index); + devc->channel_mask |= channel_bit; + + if (!ch->trigger) + continue; + + /* Configure trigger mask and value. */ + stage = 0; + for (tc = ch->trigger; tc && *tc; tc++) { + devc->trigger_mask[stage] |= channel_bit; + if (*tc == '1') + devc->trigger_value[stage] |= channel_bit; + stage++; + /* Only supporting parallel mode, with up to 4 stages. */ + if (stage > 3) + return SR_ERR; + } + if (stage > devc->num_stages) + devc->num_stages = stage - 1; + } + + return SR_OK; +} + +SR_PRIV struct sr_dev_inst *p_ols_get_metadata(uint8_t *buf, int bytes_read, struct dev_context *devc) +{ + struct sr_dev_inst *sdi; + struct sr_channel *ch; + uint32_t tmp_int, ui; + uint8_t key, type, token; + GString *tmp_str, *devname, *version; + guchar tmp_c; + int index, i; + + sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, NULL, NULL, NULL); + sdi->driver = di; + sdi->priv = devc; + + devname = g_string_new(""); + version = g_string_new(""); + + index = 0; + while (index < bytes_read) { + key = buf[index++]; + if (key == 0x00) { + sr_dbg("Got metadata key 0x00, metadata ends."); + break; + } + type = key >> 5; + token = key & 0x1f; + switch (type) { + case 0: + /* NULL-terminated string */ + tmp_str = g_string_new(""); + while ((index < bytes_read) && ((tmp_c = buf[index++]) != '\0')) + g_string_append_c(tmp_str, tmp_c); + sr_dbg("Got metadata key 0x%.2x value '%s'.", + key, tmp_str->str); + switch (token) { + case 0x01: + /* Device name */ + devname = g_string_append(devname, tmp_str->str); + break; + case 0x02: + /* FPGA firmware version */ + if (version->len) + g_string_append(version, ", "); + g_string_append(version, "FPGA version "); + g_string_append(version, tmp_str->str); + break; + case 0x03: + /* Ancillary version */ + if (version->len) + g_string_append(version, ", "); + g_string_append(version, "Ancillary version "); + g_string_append(version, tmp_str->str); + break; + default: + sr_info("Unknown token 0x%.2x: '%s'", + token, tmp_str->str); + break; + } + g_string_free(tmp_str, TRUE); + break; + case 1: + /* 32-bit unsigned integer */ + tmp_int = 0; + for (i = 0; i < 4; i++) { + tmp_int = (tmp_int << 8) | buf[index++]; + } + sr_dbg("Got metadata key 0x%.2x value 0x%.8x.", + key, tmp_int); + switch (token) { + case 0x00: + /* Number of usable channels */ + for (ui = 0; ui < tmp_int; ui++) { + if (!(ch = sr_channel_new(ui, SR_CHANNEL_LOGIC, TRUE, + p_ols_channel_names[ui]))) + return 0; + sdi->channels = g_slist_append(sdi->channels, ch); + } + break; + case 0x01: + /* Amount of sample memory available (bytes) */ + devc->max_samples = tmp_int; + break; + case 0x02: + /* Amount of dynamic memory available (bytes) */ + /* what is this for? */ + break; + case 0x03: + /* Maximum sample rate (hz) */ + devc->max_samplerate = tmp_int; + break; + case 0x04: + /* protocol version */ + devc->protocol_version = tmp_int; + break; + default: + sr_info("Unknown token 0x%.2x: 0x%.8x.", + token, tmp_int); + break; + } + break; + case 2: + /* 8-bit unsigned integer */ + tmp_c = buf[index++]; + sr_dbg("Got metadata key 0x%.2x value 0x%.2x.", + key, tmp_c); + switch (token) { + case 0x00: + /* Number of usable channels */ + for (ui = 0; ui < tmp_c; ui++) { + if (!(ch = sr_channel_new(ui, SR_CHANNEL_LOGIC, TRUE, + p_ols_channel_names[ui]))) + return 0; + sdi->channels = g_slist_append(sdi->channels, ch); + } + break; + case 0x01: + /* protocol version */ + devc->protocol_version = tmp_c; + break; + default: + sr_info("Unknown token 0x%.2x: 0x%.2x.", + token, tmp_c); + break; + } + break; + default: + /* unknown type */ + break; + } + } + + sdi->model = devname->str; + sdi->version = version->str; + g_string_free(devname, FALSE); + g_string_free(version, FALSE); + + return sdi; +} + +SR_PRIV int p_ols_set_samplerate(const struct sr_dev_inst *sdi, + const uint64_t samplerate) +{ + struct dev_context *devc; + + devc = sdi->priv; + if (devc->max_samplerate && samplerate > devc->max_samplerate) + return SR_ERR_SAMPLERATE; + + if (samplerate > CLOCK_RATE) { + sr_info("Enabling demux mode."); + devc->flag_reg |= FLAG_DEMUX; + devc->flag_reg &= ~FLAG_FILTER; + devc->max_channels = NUM_CHANNELS / 2; + devc->cur_samplerate_divider = (CLOCK_RATE * 2 / samplerate) - 1; + } else { + sr_info("Disabling demux mode."); + devc->flag_reg &= ~FLAG_DEMUX; + devc->flag_reg |= FLAG_FILTER; + devc->max_channels = NUM_CHANNELS; + devc->cur_samplerate_divider = (CLOCK_RATE / samplerate) - 1; + } + + /* Calculate actual samplerate used and complain if it is different + * from the requested. + */ + devc->cur_samplerate = CLOCK_RATE / (devc->cur_samplerate_divider + 1); + if (devc->flag_reg & FLAG_DEMUX) + devc->cur_samplerate *= 2; + if (devc->cur_samplerate != samplerate) + sr_info("Can't match samplerate %" PRIu64 ", using %" + PRIu64 ".", samplerate, devc->cur_samplerate); + + return SR_OK; +} + + +SR_PRIV int p_ols_receive_data(int fd, int revents, void *cb_data) +{ + struct dev_context *devc; + struct sr_dev_inst *sdi; + struct sr_datafeed_packet packet; + struct sr_datafeed_logic logic; + uint32_t sample; + int num_channels, offset, j; + int bytes_read, index; + unsigned int i; + unsigned char byte; + + (void)fd; + (void)revents; + + sdi = cb_data; + devc = sdi->priv; + + if (devc->num_transfers++ == 0) { + devc->raw_sample_buf = g_try_malloc(devc->limit_samples * 4); + if (!devc->raw_sample_buf) { + sr_err("Sample buffer malloc failed."); + return FALSE; + } + /* fill with 1010... for debugging */ + memset(devc->raw_sample_buf, 0x82, devc->limit_samples * 4); + } + + if (devc->num_samples < devc->limit_samples) { + + num_channels = 0; + for (i = NUM_CHANNELS; i > 0x02; i /= 2) { + if ((devc->flag_reg & i) == 0) { + num_channels++; + } + } + + /* Get a block of data. */ + bytes_read = ftdi_read_data(devc->ftdic, devc->ftdi_buf, FTDI_BUF_SIZE); + if (bytes_read < 0) { + sr_err("Failed to read FTDI data (%d): %s.", + bytes_read, ftdi_get_error_string(devc->ftdic)); + sdi->driver->dev_acquisition_stop(sdi, sdi); + return FALSE; + } + if (bytes_read == 0) { + sr_spew("Received 0 bytes, nothing to do."); + return TRUE; + } + + sr_dbg("Received %d bytes", bytes_read); + + index = 0; + while (index < bytes_read) { + byte = devc->ftdi_buf[index++]; + devc->cnt_bytes++; + + devc->sample[devc->num_bytes++] = byte; + sr_spew("Received byte 0x%.2x.", byte); + if (devc->num_bytes == num_channels) { + devc->cnt_samples++; + devc->cnt_samples_rle++; + /* + * Got a full sample. Convert from the OLS's little-endian + * sample to the local format. + */ + sample = devc->sample[0] | (devc->sample[1] << 8) \ + | (devc->sample[2] << 16) | (devc->sample[3] << 24); + sr_spew("Received sample 0x%.*x.", devc->num_bytes * 2, sample); + if (devc->flag_reg & FLAG_RLE) { + /* + * In RLE mode the high bit of the sample is the + * "count" flag, meaning this sample is the number + * of times the previous sample occurred. + */ + if (devc->sample[devc->num_bytes - 1] & 0x80) { + /* Clear the high bit. */ + sample &= ~(0x80 << (devc->num_bytes - 1) * 8); + devc->rle_count = sample; + devc->cnt_samples_rle += devc->rle_count; + sr_dbg("RLE count: %u.", devc->rle_count); + devc->num_bytes = 0; + continue; + } + } + devc->num_samples += devc->rle_count + 1; + if (devc->num_samples > devc->limit_samples) { + /* Save us from overrunning the buffer. */ + devc->rle_count -= devc->num_samples - devc->limit_samples; + devc->num_samples = devc->limit_samples; + } + + if (num_channels < 4) { + /* + * Some channel groups may have been turned + * off, to speed up transfer between the + * hardware and the PC. Expand that here before + * submitting it over the session bus -- + * whatever is listening on the bus will be + * expecting a full 32-bit sample, based on + * the number of channels. + */ + j = 0; + memset(devc->tmp_sample, 0, 4); + for (i = 0; i < 4; i++) { + if (((devc->flag_reg >> 2) & (1 << i)) == 0) { + /* + * This channel group was + * enabled, copy from received + * sample. + */ + devc->tmp_sample[i] = devc->sample[j++]; + } else if (devc->flag_reg & FLAG_DEMUX && (i > 2)) { + /* group 2 & 3 get added to 0 & 1 */ + devc->tmp_sample[i - 2] = devc->sample[j++]; + } + } + memcpy(devc->sample, devc->tmp_sample, 4); + sr_spew("Expanded sample: 0x%.8x.", sample); + } + + /* + * Pipistrello OLS sends its sample buffer backwards. + * store it in reverse order here, so we can dump + * this on the session bus later. + */ + offset = (devc->limit_samples - devc->num_samples) * 4; + for (i = 0; i <= devc->rle_count; i++) { + memcpy(devc->raw_sample_buf + offset + (i * 4), + devc->sample, 4); + } + memset(devc->sample, 0, 4); + devc->num_bytes = 0; + devc->rle_count = 0; + } + } + return TRUE; + } else { + /* + * We've acquired all the samples we asked for -- we're done. + * Send the (properly-ordered) buffer to the frontend. + */ + sr_dbg("Received %d bytes, %d samples, %d decompressed samples.", + devc->cnt_bytes, devc->cnt_samples, + devc->cnt_samples_rle); + if (devc->trigger_at != -1) { + /* + * A trigger was set up, so we need to tell the frontend + * about it. + */ + if (devc->trigger_at > 0) { + /* There are pre-trigger samples, send those first. */ + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + logic.length = devc->trigger_at * 4; + logic.unitsize = 4; + logic.data = devc->raw_sample_buf + + (devc->limit_samples - devc->num_samples) * 4; + sr_session_send(cb_data, &packet); + } + + /* Send the trigger. */ + packet.type = SR_DF_TRIGGER; + sr_session_send(cb_data, &packet); + + /* Send post-trigger samples. */ + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + logic.length = (devc->num_samples * 4) - (devc->trigger_at * 4); + logic.unitsize = 4; + logic.data = devc->raw_sample_buf + devc->trigger_at * 4 + + (devc->limit_samples - devc->num_samples) * 4; + sr_session_send(cb_data, &packet); + } else { + /* no trigger was used */ + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + logic.length = devc->num_samples * 4; + logic.unitsize = 4; + logic.data = devc->raw_sample_buf + + (devc->limit_samples - devc->num_samples) * 4; + sr_session_send(cb_data, &packet); + } + g_free(devc->raw_sample_buf); + + sdi->driver->dev_acquisition_stop(sdi, cb_data); + } + + return TRUE; +} diff --git a/src/hardware/pipistrello-ols/protocol.h b/src/hardware/pipistrello-ols/protocol.h new file mode 100644 index 00000000..64db0acb --- /dev/null +++ b/src/hardware/pipistrello-ols/protocol.h @@ -0,0 +1,124 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * + * This program is free software: you can 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_PIPISTRELLO_OLS_PROTOCOL_H +#define LIBSIGROK_HARDWARE_PIPISTRELLO_OLS_PROTOCOL_H + +#include +#include +#include +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#define LOG_PREFIX "pipistrello_ols" + +#define FTDI_BUF_SIZE (16 * 1024) + + +#define NUM_CHANNELS 32 +#define NUM_TRIGGER_STAGES 4 +#define TRIGGER_TYPE "01" +#define CLOCK_RATE SR_MHZ(100) +#define MIN_NUM_SAMPLES 4 +#define DEFAULT_SAMPLERATE SR_MHZ(100) + +/* Command opcodes */ +#define CMD_RESET 0x00 +#define CMD_RUN 0x01 +#define CMD_TESTMODE 0x03 +#define CMD_ID 0x02 +#define CMD_METADATA 0x04 +#define CMD_SET_DIVIDER 0x80 +#define CMD_SET_FLAGS 0x82 +#define CMD_CAPTURE_COUNT 0x83 +#define CMD_CAPTURE_DELAY 0x84 +#define CMD_SET_TRIGGER_MASK 0xc0 +#define CMD_SET_TRIGGER_VALUE 0xc1 +#define CMD_SET_TRIGGER_CONFIG 0xc2 + +/* Trigger config */ +#define TRIGGER_START (1 << 3) + +/* Bitmasks for CMD_FLAGS */ +/* 12-13 unused, 14-15 RLE mode (we hardcode mode 0). */ +#define FLAG_INTERNAL_TEST_MODE (1 << 11) +#define FLAG_EXTERNAL_TEST_MODE (1 << 10) +#define FLAG_SWAP_CHANNELS (1 << 9) +#define FLAG_RLE (1 << 8) +#define FLAG_SLOPE_FALLING (1 << 7) +#define FLAG_CLOCK_EXTERNAL (1 << 6) +#define FLAG_CHANNELGROUP_4 (1 << 5) +#define FLAG_CHANNELGROUP_3 (1 << 4) +#define FLAG_CHANNELGROUP_2 (1 << 3) +#define FLAG_CHANNELGROUP_1 (1 << 2) +#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_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; + int trigger_at; + uint32_t channel_mask; + uint32_t trigger_mask[4]; + uint32_t trigger_value[4]; + int num_stages; + uint16_t flag_reg; + + /* Operational states */ + unsigned int num_transfers; + unsigned int num_samples; + int num_bytes; + int cnt_bytes; + int cnt_samples; + int cnt_samples_rle; + + /* Temporary variables */ + unsigned int rle_count; + unsigned char sample[4]; + unsigned char tmp_sample[4]; + unsigned char *raw_sample_buf; +}; + + +SR_PRIV extern const char *p_ols_channel_names[NUM_CHANNELS + 1]; +SR_PRIV int write_shortcommand(struct dev_context *devc, uint8_t command); +SR_PRIV int write_longcommand(struct dev_context *devc, uint8_t command, uint8_t *data); +SR_PRIV int p_ols_open(struct dev_context *devc); +SR_PRIV int p_ols_close(struct dev_context *devc); +SR_PRIV int p_ols_configure_channels(const struct sr_dev_inst *sdi); +SR_PRIV struct sr_dev_inst *p_ols_get_metadata(uint8_t *buf, int bytes_read, struct dev_context *devc); +SR_PRIV int p_ols_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate); +SR_PRIV int p_ols_receive_data(int fd, int revents, void *cb_data); + +#endif