/*
* This file is part of the libsigrok project.
*
+ * Copyright (C) 2019 Katherine J. Temkin <k@ktemkin.com>
+ * Copyright (C) 2019 Mikaela Szekely <qyriad@gmail.com>
* Copyright (C) 2023 Gerhard Sittig <gerhard.sittig@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <config.h>
+#include "config.h"
+
#include "protocol.h"
-static struct sr_dev_driver greatfet_driver_info;
+#define DEFAULT_CONN "1d50.60e6"
+#define CONTROL_INTERFACE 0
+#define SAMPLES_INTERFACE 1
+
+#define VENDOR_TEXT "Great Scott Gadgets"
+#define MODEL_TEXT "GreatFET"
+
+#define BUFFER_SIZE (4 * 1024 * 1024)
+
+#define DEFAULT_SAMPLERATE SR_KHZ(34000)
+#define BANDWIDTH_THRESHOLD (SR_MHZ(42) * 8)
+
+#define WITH_16CHAN_SUPPORT 0
+
+static const uint32_t scanopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_PROBE_NAMES,
+};
+
+static const uint32_t drvopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+ SR_CONF_CONTINUOUS | SR_CONF_GET,
+ SR_CONF_CONN | SR_CONF_GET,
+ SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t devopts_cg[] = {
+ /* EMPTY */
+};
+
+static const char *channel_names[] = {
+ "SGPIO0", "SGPIO1", "SGPIO2", "SGPIO3",
+ "SGPIO4", "SGPIO5", "SGPIO6", "SGPIO7",
+#if WITH_16CHAN_SUPPORT
+ "SGPIO8", "SGPIO9", "SGPIO10", "SGPIO11",
+ "SGPIO12", "SGPIO13", "SGPIO14", "SGPIO15",
+#endif
+};
+
+/*
+ * The seemingly odd samplerates result from the 204MHz base clock and
+ * a 12bit integer divider. Theoretical minimum could be 50kHz but we
+ * don't bother to provide so low a selection item here.
+ *
+ * When users specify different samplerates, device firmware will pick
+ * the minimum rate which satisfies the user's request.
+ */
+static const uint64_t samplerates[] = {
+ SR_KHZ(1000), /* 1.0MHz */
+ SR_KHZ(2000), /* 2.0MHz */
+ SR_KHZ(4000), /* 4.0MHz */
+ SR_KHZ(8500), /* 8.5MHz */
+ SR_KHZ(10200), /* 10.2MHz */
+ SR_KHZ(12000), /* 12.0MHz */
+ SR_KHZ(17000), /* 17.0MHz */
+ SR_KHZ(20400), /* 20.4MHz, the maximum for 16 channels */
+ SR_KHZ(25500), /* 25.5MHz */
+ SR_KHZ(34000), /* 34.0MHz */
+ SR_KHZ(40800), /* 40.8MHz, the maximum for 8 channels */
+ SR_KHZ(51000), /* 51.0MHz */
+ SR_KHZ(68000), /* 68.0MHz, the maximum for 4 channels */
+ SR_KHZ(102000), /* 102.0MHz, the maximum for 2 channels */
+ SR_KHZ(204000), /* 204.0MHz, the maximum for 1 channel */
+};
+
+static void greatfet_free_devc(struct dev_context *devc)
+{
+
+ if (!devc)
+ return;
+
+ if (devc->sdi)
+ devc->sdi->priv = NULL;
+
+ g_string_free(devc->usb_comm_buffer, TRUE);
+ g_free(devc->firmware_version);
+ g_free(devc->serial_number);
+ sr_free_probe_names(devc->channel_names);
+ feed_queue_logic_free(devc->acquisition.feed_queue);
+ g_free(devc->transfers.transfers);
+ g_free(devc->transfers.transfer_buffer);
+ /*
+ * USB transfers should not have been allocated when we get here
+ * during device probe/scan, or during shutdown after acquisition
+ * has terminated.
+ */
+
+ g_free(devc);
+}
+
+static void greatfet_free_sdi(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+
+ if (!sdi)
+ return;
+
+ usb = sdi->conn;
+ sdi->conn = NULL;
+ if (usb && usb->devhdl)
+ sr_usb_close(usb);
+ sr_usb_dev_inst_free(usb);
+
+ devc = sdi->priv;
+ sdi->priv = NULL;
+ greatfet_free_devc(devc);
+
+ sr_dev_inst_free(sdi);
+}
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
struct drv_context *drvc;
+ struct sr_context *ctx;
GSList *devices;
+ const char *conn, *probe_names;
+ const char *want_snr;
+ struct sr_config *src;
+ GSList *conn_devices, *l;
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ gboolean skip_device;
+ struct libusb_device *dev;
+ struct libusb_device_descriptor des;
+ char *match;
+ char serno_txt[64], conn_id[64];
+ int ret;
+ size_t ch_off, ch_max, ch_idx;
+ gboolean enabled;
+ struct sr_channel *ch;
+ struct sr_channel_group *cg;
- (void)options;
+ drvc = di->context;
+ ctx = drvc->sr_ctx;
devices = NULL;
- drvc = di->context;
- drvc->instances = NULL;
- /* TODO: scan for devices, either based on a SR_CONF_CONN option
- * or on a USB scan. */
+ /* Accept user specs for conn= and probe names. */
+ conn = DEFAULT_CONN;
+ probe_names = 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_PROBE_NAMES:
+ probe_names = g_variant_get_string(src->data, NULL);
+ break;
+ }
+ }
+
+ /*
+ * By default search for all devices with the expected VID/PID.
+ * Accept external specs in either "bus.addr" or "vid.pid" form.
+ * As an alternative accept "sn=..." specs and keep using the
+ * default VID/PID in that case. This should result in maximum
+ * usability while still using a maximum amount of common code.
+ */
+ want_snr = NULL;
+ if (g_str_has_prefix(conn, "sn=")) {
+ want_snr = conn + strlen("sn=");
+ conn = DEFAULT_CONN;
+ sr_info("Searching default %s and serial number %s.",
+ conn, want_snr);
+ }
+ conn_devices = sr_usb_find(ctx->libusb_ctx, conn);
+ if (!conn_devices)
+ return devices;
+
+ /*
+ * Iterate over all devices that have the matching VID/PID.
+ * Skip those which we cannot open. Skip those which don't
+ * match additional serial number conditions. Allocate the
+ * structs for found devices "early", to re-use common code
+ * for communication to the firmware. Release these structs
+ * when identification fails or the device does not match.
+ *
+ * Notice that the scan for devices uses the USB string for
+ * the serial number, and does a weak check (partial match).
+ * This allows users to either use lsusb(8) or gf(1) output
+ * as well as match lazily when only part of the serial nr is
+ * known and becomes unique. Matching against serial nr and
+ * finding multiple devices is as acceptable, just might be a
+ * rare use case. Failure in this stage is silent, there are
+ * legal reasons why we cannot access a device during scan.
+ *
+ * Once a device was found usable, we get its serial number
+ * and version details by means of firmware communication.
+ * To verify that the firmware is operational and that the
+ * protocol works to a minimum degree. And to present data
+ * in --scan output which matches the vendor's gf(1) utility.
+ * This version detail is _not_ checked against conn= specs
+ * because users may specify the longer text string with
+ * more leading digits from lsusb(8) output. That test would
+ * fail when executed against the shorter firmware output.
+ */
+ for (l = conn_devices; l; l = l->next) {
+ usb = l->data;
+
+ ret = sr_usb_open(ctx->libusb_ctx, usb);
+ if (ret != SR_OK)
+ continue;
+
+ skip_device = FALSE;
+ if (want_snr) do {
+ dev = libusb_get_device(usb->devhdl);
+ ret = libusb_get_device_descriptor(dev, &des);
+ if (ret != 0 || !des.iSerialNumber) {
+ skip_device = TRUE;
+ break;
+ }
+ ret = libusb_get_string_descriptor_ascii(usb->devhdl,
+ des.iSerialNumber,
+ (uint8_t *)serno_txt, sizeof(serno_txt));
+ if (ret < 0) {
+ skip_device = TRUE;
+ break;
+ }
+ match = strstr(serno_txt, want_snr);
+ skip_device = !match;
+ sr_dbg("got serno %s, checking %s, match %d",
+ serno_txt, want_snr, !!match);
+ } while (0);
+ if (skip_device) {
+ sr_usb_close(usb);
+ continue;
+ }
+
+ sdi = g_malloc0(sizeof(*sdi));
+ sdi->conn = usb;
+ sdi->inst_type = SR_INST_USB;
+ sdi->status = SR_ST_INACTIVE;
+ devc = g_malloc0(sizeof(*devc));
+ sdi->priv = devc;
+ devc->sdi = sdi;
+ devc->usb_comm_buffer = NULL;
+
+ /*
+ * Get the serial number by way of device communication.
+ * Get the firmware version. Failure is fatal.
+ */
+ ret = greatfet_get_serial_number(sdi);
+ if (ret != SR_OK || !devc->serial_number) {
+ sr_err("Cannot get serial number.");
+ greatfet_free_sdi(sdi);
+ continue;
+ }
+ ret = greatfet_get_version_number(sdi);
+ if (ret != SR_OK || !devc->firmware_version) {
+ sr_err("Cannot get firmware version.");
+ greatfet_free_sdi(sdi);
+ continue;
+ }
+
+ /* Continue filling in sdi and devc. */
+ snprintf(conn_id, sizeof(conn_id), "%u.%u",
+ usb->bus, usb->address);
+ sdi->connection_id = g_strdup(conn_id);
+ sr_usb_close(usb);
+
+ sdi->vendor = g_strdup(VENDOR_TEXT);
+ sdi->model = g_strdup(MODEL_TEXT);
+ sdi->version = g_strdup(devc->firmware_version);
+ sdi->serial_num = g_strdup(devc->serial_number);
+
+ /* Create the "Logic" channel group. */
+ ch_off = 0;
+ ch_max = ARRAY_SIZE(channel_names);
+ devc->channel_names = sr_parse_probe_names(probe_names,
+ channel_names, ch_max, ch_max, &ch_max);
+ devc->channel_count = ch_max;
+ cg = sr_channel_group_new(sdi, "Logic", NULL);
+ for (ch_idx = 0; ch_idx < ch_max; ch_idx++) {
+ enabled = ch_idx < 8;
+ ch = sr_channel_new(sdi, ch_off,
+ SR_CHANNEL_LOGIC, enabled,
+ devc->channel_names[ch_idx]);
+ ch_off++;
+ cg->channels = g_slist_append(cg->channels, ch);
+ }
+
+ sr_sw_limits_init(&devc->sw_limits);
+ devc->samplerate = DEFAULT_SAMPLERATE;
+ devc->acquisition.bandwidth_threshold = BANDWIDTH_THRESHOLD;
+ devc->acquisition.control_interface = CONTROL_INTERFACE;
+ devc->acquisition.samples_interface = SAMPLES_INTERFACE;
+ devc->acquisition.acquisition_state = ACQ_IDLE;
+
+ devices = g_slist_append(devices, sdi);
+ }
+ g_slist_free(conn_devices);
- return devices;
+ return std_scan_complete(di, devices);
}
static int dev_open(struct sr_dev_inst *sdi)
{
- (void)sdi;
+ struct sr_dev_driver *di;
+ struct drv_context *drvc;
+ struct sr_context *ctx;
+ struct sr_usb_dev_inst *usb;
- /* TODO: get handle from sdi->conn and open it. */
+ di = sdi->driver;
+ drvc = di->context;
+ ctx = drvc->sr_ctx;
+ usb = sdi->conn;
- return SR_OK;
+ return sr_usb_open(ctx->libusb_ctx, usb);
}
static int dev_close(struct sr_dev_inst *sdi)
{
- (void)sdi;
-
- /* TODO: get handle from sdi->conn and close it. */
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ acq = &devc->acquisition;
+
+ greatfet_release_resources(sdi);
+
+ if (!usb->devhdl)
+ return SR_ERR_BUG;
+
+ sr_info("Closing device on %s interface %d.",
+ sdi->connection_id, acq->control_interface);
+ if (acq->control_interface_claimed) {
+ libusb_release_interface(usb->devhdl, acq->control_interface);
+ acq->control_interface_claimed = FALSE;
+ }
+ sr_usb_close(usb);
return SR_OK;
}
+static void clear_helper(struct dev_context *devc)
+{
+ greatfet_free_devc(devc);
+}
+
+static int dev_clear(const struct sr_dev_driver *driver)
+{
+ return std_dev_clear_with_callback(driver,
+ (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)
{
- int ret;
-
- (void)sdi;
- (void)data;
- (void)cg;
+ struct dev_context *devc;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+
+ /* Handle requests for the "Logic" channel group. */
+ if (cg) {
+ switch (key) {
+ default:
+ return SR_ERR_NA;
+ }
+ }
- ret = SR_OK;
+ /* Handle global options for the device. */
switch (key) {
- /* TODO */
+ case SR_CONF_CONN:
+ if (!sdi->connection_id)
+ return SR_ERR_NA;
+ *data = g_variant_new_string(sdi->connection_id);
+ return SR_OK;
+ case SR_CONF_CONTINUOUS:
+ *data = g_variant_new_boolean(TRUE);
+ return SR_OK;
+ case SR_CONF_SAMPLERATE:
+ if (!devc)
+ return SR_ERR_NA;
+ *data = g_variant_new_uint64(devc->samplerate);
+ return SR_OK;
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ if (!devc)
+ return SR_ERR_NA;
+ return sr_sw_limits_config_get(&devc->sw_limits, key, data);
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;
+ struct dev_context *devc;
- (void)sdi;
- (void)data;
- (void)cg;
+ devc = sdi->priv;
- ret = SR_OK;
+ /* Handle requests for the "Logic" channel group. */
+ if (cg) {
+ switch (key) {
+ default:
+ return SR_ERR_NA;
+ }
+ }
+
+ /* Handle global options for the device. */
switch (key) {
- /* TODO */
+ case SR_CONF_SAMPLERATE:
+ if (!devc)
+ return SR_ERR_NA;
+ devc->samplerate = g_variant_get_uint64(data);
+ return SR_OK;
+ case SR_CONF_LIMIT_SAMPLES:
+ case SR_CONF_LIMIT_MSEC:
+ if (!devc)
+ return SR_ERR_NA;
+ return sr_sw_limits_config_set(&devc->sw_limits, key, data);
default:
- ret = SR_ERR_NA;
+ 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)
{
- int ret;
- (void)sdi;
- (void)data;
- (void)cg;
+ /* Handle requests for the "Logic" channel group. */
+ if (cg) {
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ if (ARRAY_SIZE(devopts_cg) == 0)
+ return SR_ERR_NA;
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+ ARRAY_AND_SIZE(devopts_cg),
+ sizeof(devopts_cg[0]));
+ return SR_OK;
+ default:
+ return SR_ERR_NA;
+ }
+ }
- ret = SR_OK;
+ /* Handle global options for the device. */
switch (key) {
- /* TODO */
+ 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));
+ return SR_OK;
default:
return SR_ERR_NA;
}
-
- return ret;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
{
- /* TODO: configure hardware, reset acquisition state, set up
- * callbacks and send header packet. */
+ struct sr_dev_driver *di;
+ struct drv_context *drvc;
+ struct sr_context *ctx;
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ int ret;
- (void)sdi;
+ if (!sdi || !sdi->driver || !sdi->priv)
+ return SR_ERR_ARG;
+ di = sdi->driver;
+ drvc = di->context;
+ ctx = drvc->sr_ctx;
+ devc = sdi->priv;
+ acq = &devc->acquisition;
+
+ acq->acquisition_state = ACQ_PREPARE;
+
+ ret = greatfet_setup_acquisition(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ if (!acq->feed_queue) {
+ acq->feed_queue = feed_queue_logic_alloc(sdi,
+ BUFFER_SIZE, acq->unit_size);
+ if (!acq->feed_queue) {
+ sr_err("Cannot allocate session feed buffer.");
+ return SR_ERR_MALLOC;
+ }
+ }
+
+ sr_sw_limits_acquisition_start(&devc->sw_limits);
+
+ ret = greatfet_start_acquisition(sdi);
+ acq->start_req_sent = ret == SR_OK;
+ if (ret != SR_OK) {
+ greatfet_abort_acquisition(sdi);
+ feed_queue_logic_free(acq->feed_queue);
+ acq->feed_queue = NULL;
+ return ret;
+ }
+ acq->acquisition_state = ACQ_RECEIVE;
+
+ usb_source_add(sdi->session, ctx, 50,
+ greatfet_receive_data, (void *)sdi);
+
+ ret = std_session_send_df_header(sdi);
+ acq->frame_begin_sent = ret == SR_OK;
+ (void)sr_session_send_meta(sdi, SR_CONF_SAMPLERATE,
+ g_variant_new_uint64(acq->capture_samplerate));
return SR_OK;
}
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
{
- /* TODO: stop acquisition. */
-
- (void)sdi;
-
+ greatfet_abort_acquisition(sdi);
return SR_OK;
}
static struct sr_dev_driver greatfet_driver_info = {
.name = "greatfet",
- .longname = "GreatFET",
+ .longname = "Great Scott Gadgets GreatFET One",
.api_version = 1,
.init = std_init,
.cleanup = std_cleanup,
.scan = scan,
.dev_list = std_dev_list,
- .dev_clear = std_dev_clear,
+ .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) 2019 Katherine J. Temkin <k@ktemkin.com>
+ * Copyright (C) 2019 Mikaela Szekely <qyriad@gmail.com>
* Copyright (C) 2023 Gerhard Sittig <gerhard.sittig@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <config.h>
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
#include "protocol.h"
+/*
+ * Communicate to GreatFET firmware, especially its Logic Analyzer mode.
+ *
+ * Firmware communication is done by two means: Control transfers to
+ * EP0 for command execution. Bulk transfer from EP1 for sample data.
+ * The sample data endpoint number is also provided by firmware in
+ * responses to LA configuration requests.
+ *
+ * Control transfers have a fixed layout: 2x u32 class and verb numbers,
+ * and u8[] payload data up to 512 bytes length. Payload layout depends
+ * on commands and the verb's parameters. Binary data is represented in
+ * LE format (firmware executes on Cortex-M). Strings are limited to a
+ * maximum of 128 bytes.
+ *
+ * The set of commands used by this sigrok driver is minimal:
+ * - Get the GreatFET's firmware version and serial number.
+ * - String queries, a core verb, individual verb codes for the
+ * version and for the serial number.
+ * - Configure Logic Analyzer mode, start and stop captures.
+ * - Configure takes a u32 samplerate and u8 channel count. Yields
+ * u32 samplerate, u32 buffer size, u8 endpoint number.
+ * - Start takes a u32 samplerate (does it? depending on firmware
+ * version?). Empty/no response.
+ * - Stop has empty/no request and response payloads.
+ *
+ * Firmware implementation details, observed during sigrok driver
+ * creation.
+ * - Serial number "strings" in responses may carry binary data and
+ * not a text presentation of the serial number. It's uncertain
+ * whether that is by design or an oversight. This sigrok driver
+ * copes when it happens. (Remainder from another request which
+ * provided the part number as well?)
+ * - The GreatFET firmware is designed for exploration by host apps.
+ * The embedded classes, their methods, their in/out parameters,
+ * including builtin help texts, can get enumerated. This driver
+ * does not use this discovery approach, assumes a given protocol.
+ * - The NXP LPC4330 chip has 16 SGPIO pins. It's assumed that the
+ * GreatFET firmware currently does not support more than 8 logic
+ * channels due to constraints on bitbang machinery synchronization
+ * which is under construction (IIUC, it's about pin banks that
+ * run independently). When firmware versions get identified which
+ * transparently (from the host's perspective) support more than
+ * 8 channels, this host driver may need a little adjustment.
+ * - The device can sample and stream 8 channels to the host at a
+ * continuous rate of 40.8MHz. Higher rates are possible assuming
+ * that fewer pins get sampled. The firmware then provides sample
+ * memory where data taken at several sample points reside in the
+ * same byte of sample memory. It helps that power-of-two bitness
+ * is applied, IOW that there are either 1, 2, 4, or 8 bits per
+ * sample point. Even when say 3 or 5 channels are enabled. The
+ * device firmware may assume that a "dense" list of channels gets
+ * enabled, the sigrok driver supports when some disabled channels
+ * preceed other enabled channels. The device is then asked to get
+ * as many channels as are needed to cover all enabled channels,
+ * including potentially disabled channels before them.
+ * - The LA configure request returns a samplerate that is supported
+ * by the hardware/firmware combination and will be used during
+ * acquisition. This returned rate is at least as high as the
+ * requested samplerate. But might exceed the USB bandwidth which
+ * the firmware is capable to sustain. Users may not expect that
+ * since numbers add up differently from their perspective. In the
+ * example of 3 enabled channels and a requested 72MHz samplerate,
+ * the firmware will derive that it needs to sample 4 channels at
+ * a 102MHz rate. Which exceeds its capabilities while users may
+ * not be aware of these constraints. This sigrok driver attempts
+ * to detect the condition, and not start an acquisition. And also
+ * emits diagnostics (at info level which is silent by default).
+ * It's assumed that users increase verbosity when diagnosing
+ * issues they may experience.
+ */
+
+/*
+ * Assign a symbolic name to endpoint 0 which is used for USB control
+ * transfers. Although those "or 0" phrases don't take effect from the
+ * compiler's perspective, they hopefully increase readability of the
+ * USB related incantations.
+ *
+ * Endpoint 1 for sample data reception is not declared here. Its value
+ * is taken from logic analyzer configure response. Which remains more
+ * portable across firmware versions and supported device models.
+ */
+#define CONTROL_ENDPOINT 0
+
+/* Header fields for USB control requests. */
+#define LIBGREAT_REQUEST_NUMBER 0x65
+#define LIBGREAT_VALUE_EXECUTE 0
+#define LIBGREAT_FLAG_SKIP_RSP (1UL << 0)
+
+/* Classes and their verbs for core and logic analyzer. */
+#define GREATFET_CLASS_CORE 0x000
+#define CORE_VERB_READ_VERSION 0x1
+#define CORE_VERB_READ_SERIAL 0x3
+
+#define GREATFET_CLASS_LA 0x10d
+#define LA_VERB_CONFIGURE 0x0
+#define LA_VERB_START_CAPTURE 0x3
+#define LA_VERB_STOP_CAPTURE 0x4
+
+/* Maximum text string and binary payload sizes for control requests. */
+#define CORE_MAX_STRING_LENGTH 128
+#define LOGIC_MAX_PAYLOAD_DATA 512
+
+/* USB communication parameters, pool dimensions. */
+#define LOGIC_DEFAULT_TIMEOUT 1000
+#define TRANSFER_POOL_SIZE 16
+#define TRANSFER_BUFFER_SIZE (256 * 1024)
+
+static int greatfet_process_receive_data(const struct sr_dev_inst *sdi,
+ const uint8_t *data, size_t dlen);
+static int greatfet_cancel_transfers(const struct sr_dev_inst *sdi);
+
+/* Communicate a GreatFET request to EP0, and get its response. */
+static int greatfet_ctrl_out_in(const struct sr_dev_inst *sdi,
+ const uint8_t *tx_data, size_t tx_size,
+ uint8_t *rx_data, size_t rx_size, unsigned int timeout_ms)
+{
+ struct sr_usb_dev_inst *usb;
+ uint16_t flags;
+ int ret;
+ size_t sent, rcvd;
+
+ usb = sdi->conn;
+ if (!usb)
+ return SR_ERR_ARG;
+
+ /* Caller can request to skip transmission of a response. */
+ flags = 0;
+ if (!rx_size)
+ flags |= LIBGREAT_FLAG_SKIP_RSP;
+
+ /* Send USB Control OUT request. */
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ GString *dump = sr_hexdump_new(tx_data, tx_size);
+ sr_spew("USB out data: %s", dump->str);
+ sr_hexdump_free(dump);
+ }
+ ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT |
+ LIBUSB_ENDPOINT_OUT | CONTROL_ENDPOINT,
+ LIBGREAT_REQUEST_NUMBER, LIBGREAT_VALUE_EXECUTE,
+ flags, (void *)tx_data, tx_size, timeout_ms);
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ const char *msg;
+ msg = ret ? libusb_error_name(ret) : "-";
+ sr_spew("USB out, rc %d, %s", ret, msg);
+ }
+ if (ret < 0) {
+ /* Rate limit error messages. Skip "please retry" kinds. */
+ if (ret != LIBUSB_ERROR_BUSY) {
+ sr_err("USB out transfer failed: %s (%d)",
+ libusb_error_name(ret), ret);
+ }
+ return SR_ERR_IO;
+ }
+ sent = (size_t)ret;
+ if (sent != tx_size) {
+ sr_err("Short USB write: want %zu, got %zu: %s.",
+ tx_size, sent, libusb_error_name(ret));
+ return SR_ERR_IO;
+ }
+
+ /* Get the USB Control IN response. */
+ if (!rx_size)
+ return SR_OK;
+ ret = libusb_control_transfer(usb->devhdl,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT |
+ LIBUSB_ENDPOINT_IN | CONTROL_ENDPOINT,
+ LIBGREAT_REQUEST_NUMBER, LIBGREAT_VALUE_EXECUTE,
+ 0, rx_data, rx_size, timeout_ms);
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ const char *msg;
+ msg = ret ? libusb_error_name(ret) : "-";
+ sr_spew("USB in, rc %d, %s", ret, msg);
+ }
+ if (ret < 0) {
+ sr_err("USB in transfer failed: %s (%d)",
+ libusb_error_name(ret), ret);
+ return SR_ERR_IO;
+ }
+ rcvd = (size_t)ret;
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ GString *dump = sr_hexdump_new(rx_data, rcvd);
+ sr_spew("USB in data: %s", dump->str);
+ sr_hexdump_free(dump);
+ }
+ /* Short read, including zero length, is not fatal. */
+
+ return rcvd;
+}
+
+/*
+ * Use a string buffer in devc for USB transfers. This simplifies
+ * resource management in error paths.
+ */
+static int greatfet_prep_usb_buffer(const struct sr_dev_inst *sdi,
+ uint8_t **tx_buff, size_t *tx_size, uint8_t **rx_buff, size_t *rx_size)
+{
+ struct dev_context *devc;
+ size_t want_len;
+ GString *s;
+
+ if (tx_buff)
+ *tx_buff = NULL;
+ if (tx_size)
+ *tx_size = 0;
+ if (rx_buff)
+ *rx_buff = NULL;
+ if (rx_size)
+ *rx_size = 0;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+
+ /*
+ * Allocate the string buffer unless previously done.
+ * Ensure sufficient allocated space for request/response use.
+ * Assume that glib GString is suitable to hold uint8_t[] data.
+ */
+ if (!devc->usb_comm_buffer) {
+ want_len = 2 * sizeof(uint32_t) + LOGIC_MAX_PAYLOAD_DATA;
+ devc->usb_comm_buffer = g_string_sized_new(want_len);
+ if (!devc->usb_comm_buffer)
+ return SR_ERR_MALLOC;
+ }
+
+ /* Pass buffer start and size to the caller if requested. */
+ s = devc->usb_comm_buffer;
+ if (tx_buff)
+ *tx_buff = (uint8_t *)s->str;
+ if (tx_size)
+ *tx_size = s->allocated_len;
+ if (rx_buff)
+ *rx_buff = (uint8_t *)s->str;
+ if (rx_size)
+ *rx_size = s->allocated_len;
+
+ return SR_OK;
+}
+
+/* Retrieve a string by executing a core service. */
+static int greatfet_get_string(const struct sr_dev_inst *sdi,
+ uint32_t verb, char **value)
+{
+ uint8_t *req, *rsp;
+ size_t rsp_size;
+ uint8_t *wrptr;
+ size_t wrlen, rcvd;
+ const char *text;
+ int ret;
+
+ if (value)
+ *value = NULL;
+ if (!sdi)
+ return SR_ERR_ARG;
+ ret = greatfet_prep_usb_buffer(sdi, &req, NULL, &rsp, &rsp_size);
+ if (ret != SR_OK)
+ return ret;
+
+ wrptr = req;
+ write_u32le_inc(&wrptr, GREATFET_CLASS_CORE);
+ write_u32le_inc(&wrptr, verb);
+ wrlen = wrptr - req;
+ ret = greatfet_ctrl_out_in(sdi, req, wrlen,
+ rsp, rsp_size, LOGIC_DEFAULT_TIMEOUT);
+ if (ret < 0) {
+ sr_err("Cannot get core string.");
+ return ret;
+ }
+ rcvd = (size_t)ret;
+
+ rsp[rcvd] = '\0';
+ text = (const char *)rsp;
+ sr_dbg("got string, verb %u, text (%zu) %s", verb, rcvd, text);
+ if (value && *text) {
+ *value = g_strndup(text, rcvd);
+ } else if (value) {
+ /*
+ * g_strndup(3) does _not_ copy 'n' bytes. Instead it
+ * truncates the result at the first NUL character seen.
+ * That's why we need extra logic to pass binary data
+ * to callers, to not violate API layers and confuse
+ * USB readers with firmware implementation details
+ * (that may be version dependent).
+ * The very condition to determine whether text or some
+ * binary data was received is a simple check for NUL
+ * in the first position, implemented above. This is
+ * GoodEnough(TM) to handle the firmware version case.
+ */
+ *value = g_malloc0(rcvd + 1);
+ memcpy(*value, text, rcvd);
+ }
+
+ return rcvd;
+}
+
+SR_PRIV int greatfet_get_serial_number(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ char *text;
+ int ret;
+ const uint8_t *rdptr;
+ size_t rdlen;
+ GString *snr;
+ uint32_t chunk;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+
+ ret = greatfet_get_string(sdi, CORE_VERB_READ_SERIAL, &text);
+ if (ret < 0)
+ return ret;
+ if (!text)
+ return SR_ERR_DATA;
+
+ /*
+ * The simple case, we got a text string. The 2019 K.Temkin
+ * implementation took the received string as is. So there
+ * are firmware versions which provide this presentation.
+ */
+ if (*text) {
+ devc->serial_number = text;
+ return SR_OK;
+ }
+
+ /*
+ * The complex case. The received "string" looks binary. Local
+ * setups with v2018.12.1 and v2021.2.1 firmware versions yield
+ * response data that does not look like a text string. Instead
+ * it looks like four u32 fields which carry a binary value and
+ * leading padding. Try that interpreation as well. Construct a
+ * twenty character text presentation from that binary content.
+ *
+ * Implementation detail: Is the "leader" the part number which
+ * a different firmware request may yield? Are there other verbs
+ * which reliably yield the serial number in text format?
+ */
+ rdptr = (const uint8_t *)text;
+ rdlen = (size_t)ret;
+ sr_dbg("trying to read serial nr \"text\" as binary");
+ if (rdlen != 4 * sizeof(uint32_t)) {
+ g_free(text);
+ return SR_ERR_DATA;
+ }
+ snr = g_string_sized_new(20 + 1);
+ chunk = read_u32le_inc(&rdptr);
+ if (chunk) {
+ g_free(text);
+ return SR_ERR_DATA;
+ }
+ chunk = read_u32le_inc(&rdptr);
+ if (chunk) {
+ g_free(text);
+ return SR_ERR_DATA;
+ }
+ g_string_append_printf(snr, "%04" PRIx32, chunk);
+ chunk = read_u32le_inc(&rdptr);
+ g_string_append_printf(snr, "%08" PRIx32, chunk);
+ chunk = read_u32le_inc(&rdptr);
+ g_string_append_printf(snr, "%08" PRIx32, chunk);
+ sr_dbg("got serial number text %s", snr->str);
+ g_free(text);
+ text = g_string_free(snr, FALSE);
+ devc->serial_number = text;
+ return SR_OK;
+}
+
+SR_PRIV int greatfet_get_version_number(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ char *text;
+ int ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+
+ ret = greatfet_get_string(sdi, CORE_VERB_READ_VERSION, &text);
+ if (ret < SR_OK)
+ return ret;
+
+ devc->firmware_version = text;
+ return SR_OK;
+}
+
+/*
+ * Transmit a parameter-less request that wants no response. Or a
+ * request with just a few bytes worth of parameter values, still
+ * not expecting a response.
+ */
+static int greatfet_trivial_request(const struct sr_dev_inst *sdi,
+ uint32_t cls, uint32_t verb, const uint8_t *tx_data, size_t tx_dlen)
+{
+ struct dev_context *devc;
+ uint8_t *req;
+ uint8_t *wrptr;
+ size_t wrlen;
+ int ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+
+ ret = greatfet_prep_usb_buffer(sdi, &req, NULL, NULL, NULL);
+ if (ret != SR_OK)
+ return ret;
+
+ wrptr = req;
+ write_u32le_inc(&wrptr, cls);
+ write_u32le_inc(&wrptr, verb);
+ while (tx_dlen--)
+ write_u8_inc(&wrptr, *tx_data++);
+ wrlen = wrptr - req;
+ return greatfet_ctrl_out_in(sdi, req, wrlen,
+ NULL, 0, LOGIC_DEFAULT_TIMEOUT);
+}
+
+/*
+ * Transmit a "configure logic analyzer" request. Gets the resulting
+ * samplerate (which can differ from requested values) and endpoint
+ * (which is very useful for compatibility across devices/versions).
+ * Also gets the device firmware's buffer size, which is only used
+ * for information, while the host assumes a fixed larger buffer size
+ * for its own purposes.
+ */
+static int greatfet_logic_config(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ struct sr_usb_dev_inst *usb;
+ uint8_t *req, *rsp;
+ size_t rsp_size;
+ uint8_t *wrptr;
+ size_t wrlen, rcvd, want_len;
+ const uint8_t *rdptr;
+ uint64_t rate, bw;
+ size_t bufsize;
+ uint8_t ep;
+ char *print_bw;
+ int ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ usb = sdi->conn;
+ if (!devc || !usb)
+ return SR_ERR_ARG;
+ acq = &devc->acquisition;
+
+ ret = greatfet_prep_usb_buffer(sdi, &req, NULL, &rsp, &rsp_size);
+ if (ret != SR_OK)
+ return ret;
+
+ /*
+ * Prepare to get a specific amount of receive data. The logic
+ * analyzer configure response is strictly binary, in contrast
+ * to variable length string responses elsewhere.
+ */
+ want_len = 2 * sizeof(uint32_t) + sizeof(uint8_t);
+ if (rsp_size < want_len)
+ return SR_ERR_BUG;
+ rsp_size = want_len;
+
+ sr_dbg("about to config LA, rate %" PRIu64 ", chans %zu",
+ devc->samplerate, acq->capture_channels);
+ wrptr = req;
+ write_u32le_inc(&wrptr, GREATFET_CLASS_LA);
+ write_u32le_inc(&wrptr, LA_VERB_CONFIGURE);
+ write_u32le_inc(&wrptr, devc->samplerate);
+ write_u8_inc(&wrptr, acq->capture_channels);
+ wrlen = wrptr - req;
+ ret = greatfet_ctrl_out_in(sdi, req, wrlen,
+ rsp, rsp_size, LOGIC_DEFAULT_TIMEOUT);
+ if (ret < 0) {
+ sr_err("Cannot configure logic analyzer mode.");
+ return ret;
+ }
+ rcvd = (size_t)ret;
+ if (rcvd != want_len) {
+ sr_warn("Unexpected LA configuration response length.");
+ return SR_ERR_DATA;
+ }
+
+ rdptr = rsp;
+ rate = read_u32le_inc(&rdptr);
+ bufsize = read_u32le_inc(&rdptr);
+ ep = read_u8_inc(&rdptr);
+ sr_dbg("LA configured, rate %" PRIu64 ", buf %zu, ep %" PRIu8,
+ rate, bufsize, ep);
+ if (rate != devc->samplerate) {
+ sr_info("Configuration feedback, want rate %" PRIu64 ", got rate %." PRIu64,
+ devc->samplerate, rate);
+ devc->samplerate = rate;
+ }
+ acq->capture_samplerate = rate;
+ acq->firmware_bufsize = bufsize;
+ acq->samples_endpoint = ep;
+
+ /*
+ * The firmware does not reject requests that would exceed
+ * its capabilities. Yet the device becomes unaccessible when
+ * START is sent in that situation. (Observed with v2021.2.1
+ * firmware.)
+ *
+ * Assume a maximum USB bandwidth that we don't want to exceed.
+ * It's protecting the GreatFET's firmware. It's not a statement
+ * on the host's capability of keeping up with the GreatFET's
+ * firmware capabilities. :)
+ */
+ print_bw = sr_samplerate_string(acq->capture_samplerate);
+ sr_info("Capture configuration: %zu channels, samplerate %s.",
+ acq->capture_channels, print_bw);
+ g_free(print_bw);
+ bw = acq->capture_samplerate * 8 / acq->points_per_byte;
+ print_bw = sr_si_string_u64(bw, "bps");
+ sr_info("Resulting USB bandwidth: %s.", print_bw);
+ g_free(print_bw);
+ if (acq->bandwidth_threshold && bw > acq->bandwidth_threshold) {
+ sr_err("Configuration exceeds bandwidth limit. Aborting.");
+ return SR_ERR_SAMPLERATE;
+ }
+
+ return SR_OK;
+}
+
+/* Transmit "start logic capture" request. */
+static int greatfet_logic_start(const struct sr_dev_inst *sdi)
+{
+ int ret;
+
+ ret = greatfet_trivial_request(sdi,
+ GREATFET_CLASS_LA, LA_VERB_START_CAPTURE, NULL, 0);
+ sr_dbg("LA start, USB out, rc %d", ret);
+ if (ret != SR_OK)
+ sr_err("Cannot start logic analyzer capture.");
+
+ return ret;
+}
+
+/* Transmit "stop logic capture" request. */
+static int greatfet_logic_stop(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ int ret;
+
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ acq = &devc->acquisition;
+
+ /* Only send STOP when START was sent before. */
+ if (!acq->start_req_sent)
+ return SR_OK;
+
+ ret = greatfet_trivial_request(sdi,
+ GREATFET_CLASS_LA, LA_VERB_STOP_CAPTURE, NULL, 0);
+ sr_dbg("LA stop, USB out, rc %d", ret);
+ if (ret == SR_OK)
+ acq->start_req_sent = FALSE;
+ else
+ sr_warn("Cannot stop logic analyzer capture in the device.");
+
+ return ret;
+}
+
+/*
+ * Determine how many channels the device firmware needs to sample.
+ * So that resulting capture data will cover all those logic channels
+ * which currently are enabled on the sigrok side. We (have to) accept
+ * when the sequence of enabled channels "has gaps" in them. Disabling
+ * channels in the middle of the pin groups is a user's choice that we
+ * need to obey. The count of enabled channels is not good enough for
+ * the purpose of acquisition, it must be "a maximum index" or a total
+ * to-get-sampled count.
+ */
+static int greatfet_calc_capture_chans(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ GSList *l;
+ struct sr_channel *ch;
+ int last_used_idx;
+ size_t logic_ch_count, en_ch_count, fw_ch_count;
+ int ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ acq = &devc->acquisition;
+
+ last_used_idx = -1;
+ logic_ch_count = 0;
+ for (l = sdi->channels; l; l = l->next) {
+ ch = l->data;
+ if (ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ logic_ch_count++;
+ if (!ch->enabled)
+ continue;
+ if (last_used_idx < ch->index)
+ last_used_idx = ch->index;
+ }
+ en_ch_count = last_used_idx + 1;
+ sr_dbg("channel count, logic %zu, highest enabled idx %d -> count %zu",
+ logic_ch_count, last_used_idx, en_ch_count);
+ if (!en_ch_count)
+ return SR_ERR_ARG;
+
+ acq->capture_channels = en_ch_count;
+ ret = sr_next_power_of_two(last_used_idx, NULL, &fw_ch_count);
+ if (ret != SR_OK)
+ return ret;
+ if (!fw_ch_count)
+ return SR_ERR_ARG;
+ if (fw_ch_count > 8) {
+ acq->unit_size = sizeof(uint16_t);
+ acq->points_per_byte = 1;
+ } else {
+ acq->unit_size = sizeof(uint8_t);
+ acq->points_per_byte = 8 / fw_ch_count;
+ }
+ acq->channel_shift = fw_ch_count % 8;
+ sr_dbg("unit %zu, dense %d -> shift %zu, points %zu",
+ acq->unit_size, !!acq->channel_shift,
+ acq->channel_shift, acq->points_per_byte);
+
+ return SR_OK;
+}
+
+/*
+ * This is an opportunity to adapt the host's USB transfer size to
+ * the value which the device firmware has provided in the LA config
+ * response.
+ *
+ * We let the opportunity pass. Always use a fixed value for the host
+ * configuration. BULK transfers will adopt, which reduces the number
+ * of transfer completion events for the host.
+ *
+ * Notice that transfer size adjustment is _not_ a means to get user
+ * feedback earlier at low samplerates. This may be done in other
+ * drivers but does not take effect here. Because a buffer is used to
+ * submit sample values to the session. When in doubt, the feed queue
+ * needs flushing.
+ *
+ * TODO Consider whether sample data needs flushing when sample rates
+ * are low and buffers are deep. Ideally use common feed queue support
+ * if that becomes available in the future. Translate low samplerates
+ * (and channel counts) to the amount of samples after which the queue
+ * should get flushed.
+ *
+ * This implementation assumes that samplerates start at 1MHz, and
+ * flushing is not necessary.
+ */
+static int greatfet_calc_submit_size(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_transfers_t *dxfer;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ dxfer = &devc->transfers;
+
+ dxfer->capture_bufsize = dxfer->transfer_bufsize;
+ return SR_OK;
+}
+
+/*
+ * This routine is local to protocol.c and does mere data manipulation
+ * and a single attempt at sending "logic analyzer stop" to the device.
+ * This routine gets invoked from USB transfer completion callbacks as
+ * well as periodic timer or data availability callbacks. It is essential
+ * to not spend extended periods of time here.
+ */
+static void greatfet_abort_acquisition_quick(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+
+ if (!sdi)
+ return;
+ devc = sdi->priv;
+ if (!devc)
+ return;
+ acq = &devc->acquisition;
+
+ if (acq->acquisition_state == ACQ_RECEIVE)
+ acq->acquisition_state = ACQ_SHUTDOWN;
+
+ (void)greatfet_logic_stop(sdi);
+ greatfet_cancel_transfers(sdi);
+
+ feed_queue_logic_flush(acq->feed_queue);
+}
+
+/* Allocate USB transfers and associated receive buffers. */
+static int greatfet_allocate_transfers(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_transfers_t *dxfer;
+ size_t alloc_size, idx;
+ struct libusb_transfer *xfer;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ dxfer = &devc->transfers;
+
+ dxfer->transfer_bufsize = TRANSFER_BUFFER_SIZE;
+ dxfer->transfers_count = TRANSFER_POOL_SIZE;
+
+ alloc_size = dxfer->transfers_count * dxfer->transfer_bufsize;
+ dxfer->transfer_buffer = g_malloc0(alloc_size);
+ if (!dxfer->transfer_buffer)
+ return SR_ERR_MALLOC;
+
+ alloc_size = dxfer->transfers_count;
+ alloc_size *= sizeof(dxfer->transfers[0]);
+ dxfer->transfers = g_malloc0(alloc_size);
+ if (!dxfer->transfers)
+ return SR_ERR_MALLOC;
+
+ for (idx = 0; idx < dxfer->transfers_count; idx++) {
+ xfer = libusb_alloc_transfer(0);
+ if (!xfer)
+ return SR_ERR_MALLOC;
+ dxfer->transfers[idx] = xfer;
+ }
+
+ return SR_OK;
+}
+
+/* Submit USB transfers for reception, registers the data callback. */
+static int greatfet_prepare_transfers(const struct sr_dev_inst *sdi,
+ libusb_transfer_cb_fn callback)
+{
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ struct dev_transfers_t *dxfer;
+ struct sr_usb_dev_inst *conn;
+ uint8_t ep;
+ size_t submit_length;
+ size_t off, idx;
+ struct libusb_transfer *xfer;
+ int ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ conn = sdi->conn;
+ if (!devc || !conn)
+ return SR_ERR_ARG;
+ acq = &devc->acquisition;
+ dxfer = &devc->transfers;
+
+ ep = acq->samples_endpoint;
+ ret = greatfet_calc_submit_size(sdi);
+ if (ret != SR_OK)
+ return ret;
+ submit_length = dxfer->capture_bufsize;
+ if (submit_length > dxfer->transfer_bufsize)
+ submit_length = dxfer->transfer_bufsize;
+ sr_dbg("prep xfer, ep %u (%u), len %zu",
+ ep, ep & ~LIBUSB_ENDPOINT_IN, submit_length);
+
+ dxfer->active_transfers = 0;
+ off = 0;
+ for (idx = 0; idx < dxfer->transfers_count; idx++) {
+ xfer = dxfer->transfers[idx];
+ libusb_fill_bulk_transfer(xfer, conn->devhdl, ep,
+ &dxfer->transfer_buffer[off], submit_length,
+ callback, (void *)sdi, 0);
+ if (!xfer->buffer)
+ return SR_ERR_MALLOC;
+ ret = libusb_submit_transfer(xfer);
+ if (ret != 0) {
+ sr_spew("submit bulk xfer failed, idx %zu, %d: %s",
+ idx, ret, libusb_error_name(ret));
+ return SR_ERR_IO;
+ }
+ dxfer->active_transfers++;
+ off += submit_length;
+ }
+
+ return SR_OK;
+}
+
+/*
+ * Initiate the termination of an acquisition. Cancel all USB transfers.
+ * Their completion will drive further progress including resource
+ * release.
+ */
+static int greatfet_cancel_transfers(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_transfers_t *dxfer;
+ size_t idx;
+ struct libusb_transfer *xfer;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ dxfer = &devc->transfers;
+
+ for (idx = 0; idx < dxfer->transfers_count; idx++) {
+ xfer = dxfer->transfers[idx];
+ if (!xfer)
+ continue;
+ (void)libusb_cancel_transfer(xfer);
+ /*
+ * Cancelled transfers will cause acquisitions to abort
+ * in their callback. Keep the "active" count as is.
+ */
+ }
+
+ return SR_OK;
+}
+
+/*
+ * Free an individual transfer during its callback's execution.
+ * Releasing the last USB transfer also happens to drive more of
+ * the shutdown path.
+ */
+static void greatfet_free_transfer(const struct sr_dev_inst *sdi,
+ struct libusb_transfer *xfer)
+{
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ struct dev_transfers_t *dxfer;
+ size_t idx;
+
+ if (!sdi || !sdi->driver)
+ return;
+ drvc = sdi->driver->context;
+ usb = sdi->conn;
+ devc = sdi->priv;
+ if (!drvc || !usb || !devc)
+ return;
+ acq = &devc->acquisition;
+ dxfer = &devc->transfers;
+
+ /* Void the transfer in the driver's list of transfers. */
+ for (idx = 0; idx < dxfer->transfers_count; idx++) {
+ if (xfer != dxfer->transfers[idx])
+ continue;
+ dxfer->transfers[idx] = NULL;
+ dxfer->active_transfers--;
+ break;
+ }
+
+ /* Release the transfer from libusb use. */
+ libusb_free_transfer(xfer);
+
+ /* Done here when more transfers are still pending. */
+ if (!dxfer->active_transfers)
+ return;
+
+ /*
+ * The last USB transfer has been freed after completion.
+ * Post process the previous acquisition's execution.
+ */
+ (void)greatfet_stop_acquisition(sdi);
+ if (acq->frame_begin_sent) {
+ std_session_send_df_end(sdi);
+ acq->frame_begin_sent = FALSE;
+ }
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+ if (acq->samples_interface_claimed) {
+ libusb_release_interface(usb->devhdl, acq->samples_interface);
+ acq->samples_interface_claimed = FALSE;
+ }
+ feed_queue_logic_free(acq->feed_queue);
+ acq->feed_queue = NULL;
+ acq->acquisition_state = ACQ_IDLE;
+}
+
+/*
+ * Callback for the completion of previously submitted USB transfers.
+ * Processes received sample memory content. Initiates termination of
+ * the current acquisition in case of failed processing or failed
+ * communication to the acquisition device. Also initiates termination
+ * when previously configured acquisition limits were reached.
+ */
+static void LIBUSB_CALL xfer_complete_cb(struct libusb_transfer *xfer)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ const uint8_t *data;
+ size_t dlen;
+ gboolean was_completed, was_cancelled;
+ gboolean has_timedout, device_gone, is_stalled;
+ int level;
+ gboolean shall_abort;
+ int ret;
+
+ sdi = xfer ? xfer->user_data : NULL;
+ devc = sdi ? sdi->priv : NULL;
+ if (!sdi || !devc) {
+ /* ShouldNotHappen(TM) */
+ sr_warn("Completion of unregistered USB transfer.");
+ libusb_free_transfer(xfer);
+ return;
+ }
+ acq = &devc->acquisition;
+
+ /*
+ * Outside of an acquisition? Or in its shutdown path?
+ * Just release the USB transfer, don't process its data.
+ */
+ if (acq->acquisition_state != ACQ_RECEIVE) {
+ greatfet_free_transfer(sdi, xfer);
+ return;
+ }
+
+ /*
+ * Avoid the unfortunate libusb identifiers and data types.
+ * Simplify USB transfer status checks for later code paths.
+ * Optionally log the USB transfers' completion.
+ */
+ data = xfer->buffer;
+ dlen = xfer->actual_length;
+ was_completed = xfer->status == LIBUSB_TRANSFER_COMPLETED;
+ has_timedout = xfer->status == LIBUSB_TRANSFER_TIMED_OUT;
+ was_cancelled = xfer->status == LIBUSB_TRANSFER_CANCELLED;
+ device_gone = xfer->status == LIBUSB_TRANSFER_NO_DEVICE;
+ is_stalled = xfer->status == LIBUSB_TRANSFER_STALL;
+ level = sr_log_loglevel_get();
+ if (level >= SR_LOG_SPEW) {
+ sr_spew("USB transfer, status %s, byte count %zu.",
+ libusb_error_name(xfer->status), dlen);
+ } else if (level >= SR_LOG_DBG && !was_completed) {
+ sr_dbg("USB transfer, status %s, byte count %zu.",
+ libusb_error_name(xfer->status), dlen);
+ }
+
+ /*
+ * Timed out transfers may contain a little data. Warn but accept.
+ * Typical case will be completed transfers. Cancelled transfers
+ * are seen in shutdown paths, their data need not get processed.
+ * Terminate acquisition in case of communication or processing
+ * failure, or when limits were reached.
+ */
+ shall_abort = FALSE;
+ if (has_timedout)
+ sr_warn("USB transfer timed out. Using available data.");
+ if (was_completed || has_timedout) {
+ ret = greatfet_process_receive_data(sdi, data, dlen);
+ if (ret != SR_OK) {
+ sr_err("Error processing sample data. Aborting.");
+ shall_abort = TRUE;
+ }
+ if (acq->acquisition_state != ACQ_RECEIVE) {
+ sr_dbg("Sample data processing ends acquisition.");
+ feed_queue_logic_flush(acq->feed_queue);
+ shall_abort = TRUE;
+ }
+ } else if (device_gone) {
+ sr_err("Device gone during USB transfer. Aborting.");
+ shall_abort = TRUE;
+ } else if (was_cancelled) {
+ sr_dbg("Cancelled USB transfer. Terminating acquisition.");
+ shall_abort = TRUE;
+ } else if (is_stalled) {
+ sr_err("Device firmware is stalled on USB transfer. Aborting.");
+ shall_abort = TRUE;
+ } else {
+ sr_err("USB transfer failed (%s). Aborting.",
+ libusb_error_name(xfer->status));
+ shall_abort = TRUE;
+ }
+
+ /*
+ * Resubmit the USB transfer for continued reception of sample
+ * data. Or release the transfer when acquisition terminates
+ * after errors were seen, or limits were reached, or the end
+ * was requested in other regular ways.
+ *
+ * In the case of error or other terminating conditions cancel
+ * the currently executing acquisition, end all USB transfers.
+ */
+ if (!shall_abort) {
+ ret = libusb_submit_transfer(xfer);
+ if (ret < 0) {
+ sr_err("Cannot resubmit USB transfer. Aborting.");
+ shall_abort = TRUE;
+ }
+ }
+ if (shall_abort) {
+ greatfet_free_transfer(sdi, xfer);
+ greatfet_abort_acquisition_quick(sdi);
+ }
+}
+
+/* The public protocol.c API to start/stop acquisitions. */
+
+SR_PRIV int greatfet_setup_acquisition(const struct sr_dev_inst *sdi)
+{
+ int ret;
+
+ ret = greatfet_allocate_transfers(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = greatfet_calc_capture_chans(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ return SR_OK;
+}
+
+SR_PRIV int greatfet_start_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ struct sr_usb_dev_inst *usb;
+ int ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ usb = sdi->conn;
+ if (!devc || !usb)
+ return SR_ERR_ARG;
+ acq = &devc->acquisition;
+
+ /*
+ * Configure the logic analyzer. Claim the USB interface. This
+ * part of the sequence is not time critical.
+ */
+ ret = greatfet_logic_config(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = libusb_claim_interface(usb->devhdl, acq->samples_interface);
+ acq->samples_interface_claimed = ret == 0;
+
+ /*
+ * Ideally we could submit USB transfers before sending the
+ * logic analyzer start request. Experience suggests that this
+ * results in libusb IO errors. That's why we need to accept the
+ * window of blindness between sending the LA start request and
+ * initiating USB data reception.
+ */
+ ret = greatfet_logic_start(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ ret = greatfet_prepare_transfers(sdi, xfer_complete_cb);
+ if (ret != SR_OK)
+ return ret;
+
+ return SR_OK;
+}
+
+/*
+ * The public acquisition abort routine, invoked by api.c logic. Could
+ * optionally spend more time than the _quick() routine.
+ */
+SR_PRIV void greatfet_abort_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ if (!sdi)
+ return;
+ devc = sdi->priv;
+ if (!devc)
+ return;
+
+ (void)greatfet_logic_stop(sdi);
+ greatfet_abort_acquisition_quick(sdi);
+}
+
+SR_PRIV int greatfet_stop_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ int ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ usb = sdi->conn;
+ if (!usb)
+ return SR_ERR_ARG;
+
+ ret = greatfet_logic_stop(sdi);
+ if (ret != SR_OK)
+ return ret;
+
+ return SR_OK;
+}
+
+SR_PRIV void greatfet_release_resources(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct dev_transfers_t *dxfer;
+
+ if (!sdi)
+ return;
+ devc = sdi->priv;
+ if (!devc)
+ return;
+ dxfer = &devc->transfers;
+
+ /*
+ * Is there something that needs to be done here? Transfers'
+ * cancellation gets initiated and then happens as they keep
+ * completing. The completion handler releases their libusb
+ * resources. The last release also unregisters the periodic
+ * glib main loop callback.
+ *
+ * Can something be done here? The receive buffer still is
+ * allocated. As is the feed queue. Can we synchronize to the
+ * last release of the USB resources? Need we keep invoking
+ * the receive callback until the USB transfers pool has been
+ * released? Need we wait for the active transfers counter to
+ * drop to zero, is more checking involved?
+ */
+ if (dxfer->active_transfers)
+ sr_warn("Got active USB transfers in release code path.");
+}
+
+/*
+ * Process received sample date. There are two essential modes:
+ * - The straight forward case. The device provides 8 bits per sample
+ * point. Forward each byte as is to the sigrok session. It matches
+ * the sizeof(uint8_t) feed queue allocation parameter.
+ * - The compact presentation where a smaller number of channels is
+ * active, and their data spans only part of a byte per sample point.
+ * Multiple samples' data is sharing bytes, and bytes will carry data
+ * that was taken at different times. This requires some untangling
+ * before forwarding byte sized sample data to the sigrok session.
+ *
+ * Implementation details:
+ * - Samples taken first are found in the least significant bits of a
+ * byte. Samples taken next are found in upper bits of the byte. For
+ * example a byte containing 4x 2bit sample data is seen as 33221100.
+ * - Depending on the number of enabled channels there could be up to
+ * eight samples in one byte of sample memory. This implementation
+ * tries to accumulate one input byte's content, but not more. To
+ * simplify the implementation. Performance can get tuned later as
+ * the need gets identified. Sampling at 204MHz results in some 3%
+ * CPU load with Pulseview on the local workstation.
+ * - Samples for 16 channels transparently are handled by the simple
+ * 8 channel case above. All logic data of an individual samplepoint
+ * occupies full bytes, endianess of sample data as provided by the
+ * device firmware and the sigrok session are the same. No conversion
+ * is required.
+ */
+static int greatfet_process_receive_data(const struct sr_dev_inst *sdi,
+ const uint8_t *data, size_t dlen)
+{
+ static int diag_shown;
+
+ struct dev_context *devc;
+ struct dev_acquisition_t *acq;
+ struct feed_queue_logic *q;
+ uint64_t samples_remain;
+ gboolean exceeded;
+ size_t samples_rcvd;
+ uint8_t raw_mask;
+ size_t points_per_byte, points_count;
+ uint8_t raw_data;
+ uint8_t accum[8];
+ const uint8_t *rdptr;
+ uint8_t *wrptr;
+ int ret;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+ devc = sdi->priv;
+ if (!devc)
+ return SR_ERR_ARG;
+ acq = &devc->acquisition;
+ q = acq->feed_queue;
+
+ /*
+ * Check whether acquisition limits apply, and whether they
+ * were reached or exceeded before. Constrain the submission
+ * of more sample values to what's still within the limits of
+ * the current acquisition.
+ */
+ ret = sr_sw_limits_get_remain(&devc->sw_limits,
+ &samples_remain, NULL, NULL, &exceeded);
+ if (ret != SR_OK)
+ return ret;
+ if (exceeded)
+ return SR_OK;
+
+ /*
+ * Check for the simple case first. Where bytes carry samples
+ * of exactly one sample point. Pass memory in verbatim form.
+ * Notice that 16bit sample quantities happen to work here too.
+ */
+ if (!acq->channel_shift) {
+ samples_rcvd = dlen / acq->unit_size;
+ if (samples_remain && samples_rcvd > samples_remain)
+ samples_rcvd = samples_remain;
+ ret = feed_queue_logic_submit_many(q, data, samples_rcvd);
+ if (ret != SR_OK)
+ return ret;
+ sr_sw_limits_update_samples_read(&devc->sw_limits, samples_rcvd);
+ return SR_OK;
+ }
+
+ /*
+ * Handle the complex case by means of some naive logic. To
+ * simplify the implementation for now and see if the approach
+ * works out. It helps that the firmware provides sample data
+ * in units of power-of-two bit counts per sample point. This
+ * eliminates fragments which could span several transfers.
+ *
+ * Notice that dense sample memory only happens for channel
+ * counts under 8. That's why we read bytes here and need not
+ * dispatch based on unit size.
+ */
+ raw_mask = (1UL << acq->channel_shift) - 1;
+ points_per_byte = 8 / acq->channel_shift;
+ if (!diag_shown++) {
+ sr_dbg("sample memory: ch count %zu, ch shift %zu, mask 0x%x, points %zu",
+ acq->capture_channels, acq->channel_shift,
+ raw_mask, points_per_byte);
+ }
+ samples_rcvd = dlen * points_per_byte;
+ if (samples_remain && samples_rcvd > samples_remain) {
+ samples_rcvd = samples_remain;
+ dlen = samples_rcvd;
+ dlen += points_per_byte - 1;
+ dlen /= points_per_byte;
+ }
+ rdptr = data;
+ while (dlen--) {
+ raw_data = read_u8_inc(&rdptr);
+ wrptr = accum;
+ points_count = points_per_byte;
+ while (points_count--) {
+ write_u8_inc(&wrptr, raw_data & raw_mask);
+ raw_data >>= acq->channel_shift;
+ }
+ points_count = points_per_byte;
+ ret = feed_queue_logic_submit_many(q, accum, points_count);
+ if (ret != SR_OK)
+ return ret;
+ sr_sw_limits_update_samples_read(&devc->sw_limits, points_count);
+ }
+ return SR_OK;
+}
+
+/* Receive callback, invoked when data is available, or periodically. */
SR_PRIV int greatfet_receive_data(int fd, int revents, void *cb_data)
{
- const struct sr_dev_inst *sdi;
+ struct sr_dev_inst *sdi;
struct dev_context *devc;
+ struct drv_context *drvc;
+ libusb_context *ctx;
+ struct timeval tv;
(void)fd;
+ (void)revents;
sdi = cb_data;
- if (!sdi)
+ if (!sdi || !sdi->priv || !sdi->driver)
return TRUE;
-
devc = sdi->priv;
if (!devc)
return TRUE;
+ drvc = sdi->driver->context;
+ if (!drvc || !drvc->sr_ctx)
+ return TRUE;
+ ctx = drvc->sr_ctx->libusb_ctx;
+
+ /*
+ * Handle those USB transfers which have completed so far
+ * in a regular fashion. These carry desired sample values.
+ */
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(ctx, &tv);
- if (revents == G_IO_IN) {
- /* TODO */
+ /*
+ * End the current acquisition when limites were reached.
+ * Process USB transfers again here before returning, because
+ * acquisition termination will unregister the receive callback,
+ * and cancel previously submitted transfers. Reap those here.
+ */
+ if (sr_sw_limits_check(&devc->sw_limits)) {
+ greatfet_abort_acquisition_quick(sdi);
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(ctx, &tv);
}
return TRUE;