Compare commits
14 Commits
sq50-for-m
...
hw/sq50
Author | SHA1 | Date |
---|---|---|
Triss | b244ed5246 | |
Triss | 19a7aa375a | |
Triss | 243b525727 | |
Triss | 192cb0d897 | |
Triss | 2a815b8a5f | |
Triss | acad3c2123 | |
Triss | 78cf266f90 | |
Triss | 32817ebeaa | |
Triss | 33758a51e7 | |
Triss | d70c0bc625 | |
Triss | 638bdaec16 | |
Triss | d1074befa3 | |
Triss | d8f08acdc3 | |
Triss | 012becda49 |
|
@ -8,6 +8,8 @@
|
|||
/configure.lineno
|
||||
/m4/libtool.m4
|
||||
/m4/lt*.m4
|
||||
/build
|
||||
/inst
|
||||
|
||||
# Editor/IDE cruft
|
||||
*.kate-swp
|
||||
|
|
|
@ -411,6 +411,12 @@ src_libdrivers_la_SOURCES += \
|
|||
src/hardware/ikalogic-scanaplus/protocol.c \
|
||||
src/hardware/ikalogic-scanaplus/api.c
|
||||
endif
|
||||
if HW_IKALOGIC_SCANAQUAD
|
||||
src_libdrivers_la_SOURCES += \
|
||||
src/hardware/ikalogic-scanaquad/protocol.h \
|
||||
src/hardware/ikalogic-scanaquad/protocol.c \
|
||||
src/hardware/ikalogic-scanaquad/api.c
|
||||
endif
|
||||
if HW_IPDBG_LA
|
||||
src_libdrivers_la_SOURCES += \
|
||||
src/hardware/ipdbg-la/protocol.h \
|
||||
|
|
|
@ -289,6 +289,7 @@ SR_DRIVER([hp-59306a], [hp-59306a])
|
|||
SR_DRIVER([Hung-Chang DSO-2100], [hung-chang-dso-2100], [libieee1284])
|
||||
SR_DRIVER([Ikalogic Scanalogic-2], [ikalogic-scanalogic2], [libusb])
|
||||
SR_DRIVER([Ikalogic Scanaplus], [ikalogic-scanaplus], [libftdi])
|
||||
SR_DRIVER([Ikalogic ScanaQuad], [ikalogic-scanaquad], [libftdi])
|
||||
SR_DRIVER([IPDBG LA], [ipdbg-la])
|
||||
SR_DRIVER([ITECH IT8500], [itech-it8500], [serial_comm])
|
||||
SR_DRIVER([Kecheng KC-330B], [kecheng-kc-330b], [libusb])
|
||||
|
|
|
@ -625,7 +625,7 @@ static int sigma_fpga_init_bitbang_once(struct dev_context *devc)
|
|||
if (ret != SR_OK)
|
||||
return ret;
|
||||
g_usleep(10 * 1000);
|
||||
ftdi_usb_purge_buffers(&devc->ftdi.ctx);
|
||||
ftdi_tcioflush(&devc->ftdi.ctx);
|
||||
|
||||
/*
|
||||
* Wait until the FPGA asserts INIT_B. Check in a maximum number
|
||||
|
@ -889,7 +889,7 @@ static int upload_firmware(struct sr_context *ctx, struct dev_context *devc,
|
|||
ftdi_get_error_string(&devc->ftdi.ctx));
|
||||
return SR_ERR;
|
||||
}
|
||||
ftdi_usb_purge_buffers(&devc->ftdi.ctx);
|
||||
ftdi_tcioflush(&devc->ftdi.ctx);
|
||||
while (sigma_read_raw(devc, &pins, sizeof(pins)) > 0)
|
||||
;
|
||||
|
||||
|
|
|
@ -243,7 +243,7 @@ static int dev_open(struct sr_dev_inst *sdi)
|
|||
goto err_ftdi_free;
|
||||
}
|
||||
|
||||
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
|
||||
if ((ret = ftdi_tcioflush(devc->ftdic)) < 0) {
|
||||
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
||||
ret, ftdi_get_error_string(devc->ftdic));
|
||||
goto err_ftdi_free;
|
||||
|
|
|
@ -202,7 +202,7 @@ static int close_usb_reset_sequencer(struct dev_context *devc)
|
|||
sr_dbg("Purging buffers, resetting+closing FTDI device.");
|
||||
|
||||
/* Log errors, but ignore them (i.e., don't abort). */
|
||||
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0)
|
||||
if ((ret = ftdi_tcioflush(devc->ftdic)) < 0)
|
||||
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
||||
ret, ftdi_get_error_string(devc->ftdic));
|
||||
if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
|
||||
|
|
|
@ -325,7 +325,7 @@ static int dev_open(struct sr_dev_inst *sdi)
|
|||
goto err_ftdi_free;
|
||||
}
|
||||
|
||||
ret = ftdi_usb_purge_buffers(devc->ftdic);
|
||||
ret = ftdi_tcioflush(devc->ftdic);
|
||||
if (ret < 0) {
|
||||
sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
|
||||
ret, ftdi_get_error_string(devc->ftdic));
|
||||
|
|
|
@ -141,7 +141,7 @@ static int dev_open(struct sr_dev_inst *sdi)
|
|||
return SR_ERR;
|
||||
}
|
||||
|
||||
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
|
||||
if ((ret = ftdi_tcioflush(devc->ftdic)) < 0) {
|
||||
sr_err("Failed to purge FTDI RX/TX buffers (%d): %s.",
|
||||
ret, ftdi_get_error_string(devc->ftdic));
|
||||
goto err_dev_open_close_ftdic;
|
||||
|
|
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
* This file is part of the libsigrok project.
|
||||
*
|
||||
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>,
|
||||
* haskal <haskal@awoo.systems>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "protocol.h"
|
||||
|
||||
#define USB_VENDOR_ID 0x0403
|
||||
#define USB_PRODUCT_ID 0x7fd0
|
||||
|
||||
#define USB_IPRODUCT_25 "ScanaQuad SQ25"
|
||||
#define USB_IPRODUCT_50 "ScanaQuad SQ50"
|
||||
#define USB_IPRODUCT_100 "ScanaQuad SQ100"
|
||||
#define USB_IPRODUCT_200 "ScanaQuad SQ200"
|
||||
|
||||
static struct sr_dev_driver ikalogic_scanaquad_driver_info;
|
||||
|
||||
static const uint32_t drvopts[] = {
|
||||
SR_CONF_LOGIC_ANALYZER,
|
||||
};
|
||||
|
||||
static const uint32_t devopts[] = {
|
||||
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||
SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
|
||||
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
|
||||
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
|
||||
SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||
SR_CONF_LOGIC_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||
SR_CONF_LOGIC_THRESHOLD_CUSTOM | SR_CONF_GET | SR_CONF_SET,
|
||||
|
||||
/*SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,*/
|
||||
|
||||
/* list of TODO stuff:
|
||||
* * VOLTAGE_THRESHOLD vs LOGIC_THRESHOLD? LOGIC_THRESHOLD_CUSTOM?
|
||||
* * VOLTAGE_THRESHOLD: 2 double values (min, max) with a slider selection GUI
|
||||
* * LOGIC_THRESHOLD: selection of (max, not thresh value) voltages
|
||||
* * LOGIC_THRESHOLD_CUSTOM: custom ^, so kinda boils down to VOLTAGE_THRESHOLD?
|
||||
* * see kingst-la2016/api.c
|
||||
* * currently implemnted as all of them, pulseview gives a simple slider
|
||||
* without value indication, which is not that practical to use...
|
||||
* LOGIC_THRESHOLD(_CUSTOM) seems to give no UI?
|
||||
*
|
||||
* * BUFFERSIZE, DATALOG, PATTERN_MODE: should this be used?
|
||||
*
|
||||
* * trigger stuff:
|
||||
* * TRIGGER_MATCH: uncomment when TRIGGER_PATTERN is implemented
|
||||
* * TRIGGER_PATTERN: freeform string
|
||||
* * see asix-sigma/protocol.c:1603 , it has some proposed formats,
|
||||
* however, these aren't sufficient here as the SQ series also allows
|
||||
* varying pulse widths for each trigger step...
|
||||
* * TRIGGER_LEVEL, TRIGGER_SOURCE: not applicable here
|
||||
*
|
||||
* * the SQ50 accepts 200 MHz samplerate and a buffer size longer than it
|
||||
* has available physical backing for, so it's not unlikely that modding
|
||||
* it to have more memory is possible. so maybe memory and frequency
|
||||
* option shouldn't be strictly checked?
|
||||
* see also: https://git.lain.faith/BLAHAJ/sq50-re/wiki/Hardware-info
|
||||
*/
|
||||
};
|
||||
|
||||
static const char *channel_names[] = {
|
||||
"1", "2", "3", "4"
|
||||
};
|
||||
|
||||
static const uint64_t samplerates[] = {
|
||||
/* TODO: can this be an arbitrary value? at least the settings
|
||||
* blob makes it look like it can */
|
||||
/* also, looks like the SQ50 can totally do 100 and 200 MHz... */
|
||||
SR_MHZ(200), SR_MHZ(100),
|
||||
SR_MHZ( 50), SR_MHZ( 25), SR_MHZ( 10), SR_MHZ( 5), SR_MHZ( 1),
|
||||
SR_KHZ(500), SR_KHZ(250), SR_KHZ(100), SR_KHZ(50), SR_KHZ(10)
|
||||
};
|
||||
|
||||
static const char *thresholds[] = {
|
||||
"1.8V", /* 0x46 0x1e | 1.8 0.8 ~44% */
|
||||
"2.8V", /* 0x6e 0x2c | 2.8 1.1 ~40% */
|
||||
"3.3V", /* 0x81 0x46 | 3.3 1.8 ~55% */
|
||||
"3.6V", /* 0x8d 0x4f | 3.6 2.0 ~55% */
|
||||
"5V" , /* 0xc4 0x72 | 5.0 2.9 ~58% */
|
||||
"5VTTL",/* 0xc3 0x3b | 5.0 1.5 */
|
||||
"User" /* VOLTAGE_THRESHOLD == LOGIC_THRESHOLD_CUSTOM (?) */
|
||||
};
|
||||
|
||||
static const int matches[] = {
|
||||
SR_TRIGGER_ZERO,
|
||||
SR_TRIGGER_ONE,
|
||||
SR_TRIGGER_RISING,
|
||||
SR_TRIGGER_FALLING,
|
||||
};
|
||||
|
||||
static uint64_t dc_samples_to_msec(struct dev_context *dc, uint64_t samples)
|
||||
{
|
||||
float msec_per_sample = 1.0f / ((float)dc->samplerate * 0.001f);
|
||||
|
||||
return (uint64_t)((float)samples * msec_per_sample);
|
||||
}
|
||||
static uint64_t dc_msec_to_samples(struct dev_context *dc, uint64_t msec)
|
||||
{
|
||||
float samples_per_msec = ((float)dc->samplerate * 0.001f);
|
||||
|
||||
return (uint64_t)((float)msec * samples_per_msec);
|
||||
}
|
||||
|
||||
static int dev_clear(const struct sr_dev_driver *di)
|
||||
{
|
||||
return std_dev_clear_with_callback(di, (std_dev_clear_callback)sq_destroy);
|
||||
}
|
||||
|
||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||
{
|
||||
struct sr_dev_inst *sdi;
|
||||
struct dev_context *dc;
|
||||
struct ftdi_context *ft;
|
||||
char *manuf, *prod, *serial;
|
||||
unsigned int i;
|
||||
int rv;
|
||||
|
||||
(void)options;
|
||||
|
||||
if (!(ft = ftdi_new())) {
|
||||
sr_err("Failed to initialize libftdi.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dc = sq_new(ft);
|
||||
if (dc == NULL) {
|
||||
sr_err("Failed to initialize ScanaQuad device context.");
|
||||
ftdi_free(ft);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* open the FTDI device for a second to 1) check it exists, and
|
||||
* 2) check which device it is from the SQ series */
|
||||
|
||||
rv = ftdi_usb_open(ft, USB_VENDOR_ID, USB_PRODUCT_ID);
|
||||
if (rv < 0) {
|
||||
if (rv != -3) { /* -3: device not found */
|
||||
sr_err("Failed to open device (%d): %s", rv, ftdi_get_error_string(ft));
|
||||
sq_destroy(dc);
|
||||
g_free(dc);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
rv = ftdi_read_eeprom(ft);
|
||||
if (rv < 0) {
|
||||
sr_err("Failted to read FTDI EEPROM (%d): %s", rv, ftdi_get_error_string(ft));
|
||||
sq_destroy(dc);
|
||||
g_free(dc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rv = ftdi_eeprom_decode(ft, 0);
|
||||
/* retval -1 means a checksum error, which current libftdi
|
||||
* is not equipped to deal with yet, as it only knows of the
|
||||
* FT230X, while the SQ uses an FT240X */
|
||||
if (rv < 0) {
|
||||
sr_err("Failed to decode FTDI EEPROM (%d): %s", rv, ftdi_get_error_string(ft));
|
||||
sq_destroy(dc);
|
||||
g_free(dc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
manuf = g_malloc0(256);
|
||||
prod = g_malloc0(256);
|
||||
serial = g_malloc0(256);
|
||||
rv = ftdi_eeprom_get_strings(ft, manuf, 256, prod, 256, serial, 256);
|
||||
if (rv < 0) {
|
||||
sr_err("Failed to get device info strings (%d): %s", rv, ftdi_get_error_string(ft));
|
||||
g_free(serial);
|
||||
g_free(prod );
|
||||
g_free(manuf );
|
||||
sq_destroy(dc);
|
||||
g_free(dc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ftdi_usb_close(ft); /* enough for now */
|
||||
|
||||
if (strcmp(manuf, "IKALOGIC")) {
|
||||
sr_warn("Unexpected manufacturer name %s!", manuf);
|
||||
}
|
||||
|
||||
if (!strcmp(prod, USB_IPRODUCT_25)) {
|
||||
dc->devtype = 25;
|
||||
dc->memsize_max = MAX_MEMSIZE_SQ50 / 2;
|
||||
sr_info("%s", prod);
|
||||
} else if (!strcmp(prod, USB_IPRODUCT_50)) {
|
||||
dc->devtype = 50;
|
||||
dc->memsize_max = MAX_MEMSIZE_SQ50;
|
||||
sr_info("%s", prod);
|
||||
} else if (!strcmp(prod, USB_IPRODUCT_100)) {
|
||||
dc->devtype = 100;
|
||||
dc->memsize_max = MAX_MEMSIZE_SQ50 * 2;
|
||||
sr_info("%s", prod);
|
||||
} else if (!strcmp(prod, USB_IPRODUCT_200)) {
|
||||
dc->devtype = 200;
|
||||
dc->memsize_max = MAX_MEMSIZE_SQ50 * 4;
|
||||
sr_info("%s", prod);
|
||||
} else {
|
||||
sr_warn("Unexpected product name %s! Assuming SQ50 behavior...", prod);
|
||||
dc->devtype = 50;
|
||||
dc->memsize_max = MAX_MEMSIZE_SQ50;
|
||||
}
|
||||
dc->limit_samples = dc->memsize_max;
|
||||
|
||||
sr_info("%s ScanaQuad serial number: %s", manuf, serial);
|
||||
|
||||
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
||||
sdi->status = SR_ST_INACTIVE;
|
||||
sdi->vendor = g_strdup(manuf);
|
||||
sdi->model = g_strdup(prod);
|
||||
sdi->priv = dc;
|
||||
|
||||
g_free(serial);
|
||||
g_free(prod );
|
||||
g_free(manuf );
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(channel_names); ++i)
|
||||
sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE/* enabled */, channel_names[i]);
|
||||
|
||||
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
||||
}
|
||||
|
||||
#define CHECK_FTDI_RETVAL(msg, ft, rv) \
|
||||
do { \
|
||||
if ((rv) < 0) { \
|
||||
sr_err(msg " in %s (%d): %s.\n", __func__, (rv), ftdi_get_error_string(ft)); \
|
||||
return SR_ERR; \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
#define CHECK_SQ_RETVAL(rv, msg) \
|
||||
do { \
|
||||
if ((rv) < 0) { \
|
||||
sr_err(msg "in %s (%d).\n", __func__, (rv)); \
|
||||
return SR_ERR; \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
static int dev_open(struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct dev_context *dc;
|
||||
int rv;
|
||||
|
||||
dc = sdi->priv;
|
||||
|
||||
if (!dc || !dc->ft) return SR_ERR_BUG;
|
||||
|
||||
rv = ftdi_set_interface(dc->ft, INTERFACE_A);
|
||||
CHECK_FTDI_RETVAL("Failed to set FTDI interface A", dc->ft, rv);
|
||||
|
||||
rv = ftdi_usb_open(dc->ft, USB_VENDOR_ID, USB_PRODUCT_ID);
|
||||
CHECK_FTDI_RETVAL("Failed to open FTDI device", dc->ft, rv);
|
||||
|
||||
rv = ftdi_tcioflush(dc->ft);
|
||||
CHECK_FTDI_RETVAL("Failed to purge buffers", dc->ft, rv);
|
||||
|
||||
rv = ftdi_set_latency_timer(dc->ft, 2);
|
||||
CHECK_FTDI_RETVAL("Failed to set FTDI latency timer", dc->ft, rv);
|
||||
|
||||
/*rv = ftdi_read_data_set_chunksize(dc->ft, 64*1024);
|
||||
CHECK_FTDI_RETVAL("Failed to set FTDI read data chunk size", dc->ft, rv);*/
|
||||
|
||||
return scanaquad_init(dc);
|
||||
}
|
||||
|
||||
static int dev_close(struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct dev_context *dc;
|
||||
int rv;
|
||||
|
||||
dc = sdi->priv;
|
||||
|
||||
if (!dc || !dc->ft) return SR_ERR_BUG;
|
||||
|
||||
rv = scanaquad_cancel_all(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
||||
|
||||
rv = ftdi_tcioflush(dc->ft);
|
||||
CHECK_FTDI_RETVAL("Failed to purge buffers", dc->ft, rv);
|
||||
|
||||
rv = ftdi_usb_close(dc->ft);
|
||||
CHECK_FTDI_RETVAL("Failed to close FTDI device", dc->ft, rv);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int config_get(uint32_t key, GVariant **data,
|
||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
||||
{
|
||||
struct dev_context *dc;
|
||||
double rnded;
|
||||
int rv;
|
||||
|
||||
dc = sdi->priv;
|
||||
|
||||
(void)cg;
|
||||
|
||||
if (!dc) return SR_ERR_BUG;
|
||||
|
||||
rnded = (int)(dc->voltage.thresh * 10.0 / 39.2) * 0.1;
|
||||
|
||||
rv = SR_OK;
|
||||
switch (key) {
|
||||
case SR_CONF_SAMPLERATE:
|
||||
*data = g_variant_new_uint64(dc->samplerate);
|
||||
break;
|
||||
case SR_CONF_LIMIT_MSEC:
|
||||
*data = g_variant_new_uint64(dc_samples_to_msec(dc, dc->limit_samples));
|
||||
break;
|
||||
case SR_CONF_LIMIT_SAMPLES:
|
||||
*data = g_variant_new_uint64(dc->limit_samples);
|
||||
break;
|
||||
case SR_CONF_CAPTURE_RATIO:
|
||||
*data = g_variant_new_uint64(dc->capture_ratio);
|
||||
break;
|
||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
||||
*data = std_gvar_tuple_double(rnded, rnded + 0.1);
|
||||
break;
|
||||
case SR_CONF_LOGIC_THRESHOLD:
|
||||
*data = g_variant_new_string(thresholds[dc->voltage_idx]);
|
||||
break;
|
||||
case SR_CONF_LOGIC_THRESHOLD_CUSTOM:
|
||||
*data = g_variant_new_double(rnded);
|
||||
break;
|
||||
default:
|
||||
sr_err("%s no conf key %u", __func__, key);
|
||||
rv = SR_ERR_NA;
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int config_set(uint32_t key, GVariant *data,
|
||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
||||
{
|
||||
struct dev_context *dc;
|
||||
double volt, vlo, vhi;
|
||||
int rv;
|
||||
|
||||
dc = sdi->priv;
|
||||
|
||||
(void)cg;
|
||||
|
||||
if (!dc) return SR_ERR_BUG;
|
||||
|
||||
if (key == SR_CONF_LOGIC_THRESHOLD_CUSTOM)
|
||||
volt = g_variant_get_double(data);
|
||||
|
||||
rv = SR_OK;
|
||||
switch (key) {
|
||||
case SR_CONF_SAMPLERATE:
|
||||
dc->samplerate = g_variant_get_uint64(data);
|
||||
break;
|
||||
case SR_CONF_LIMIT_MSEC:
|
||||
dc->limit_samples = dc_msec_to_samples(dc, g_variant_get_uint64(data));
|
||||
/* TODO: make this check optional */
|
||||
if (dc->limit_samples > dc->memsize_max * 4)
|
||||
dc->limit_samples = dc->memsize_max * 4;
|
||||
break;
|
||||
case SR_CONF_LIMIT_SAMPLES:
|
||||
dc->limit_samples = g_variant_get_uint64(data);
|
||||
/* TODO: make this check optional */
|
||||
if (dc->limit_samples > dc->memsize_max * 4)
|
||||
dc->limit_samples = dc->memsize_max * 4;
|
||||
break;
|
||||
case SR_CONF_CAPTURE_RATIO:
|
||||
dc->capture_ratio = g_variant_get_uint64(data);
|
||||
break;
|
||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
||||
g_variant_get(data, "(dd)", &vlo, &vhi);
|
||||
volt = 0.5 * (vlo + vhi);
|
||||
/* fallthru! */
|
||||
case SR_CONF_LOGIC_THRESHOLD_CUSTOM:
|
||||
/* in the LOGIC_THRESHOLD_CUSTOM case: see 'if' stmt before switch */
|
||||
dc->voltage_idx = 6; /* user */
|
||||
dc->voltage.thresh = (int)(volt * 39.2);
|
||||
/* emulate vendor software values in a naive way. not 100% exact but
|
||||
* oh well. */
|
||||
dc->voltage.level = (dc->voltage.thresh < 0x30)
|
||||
? (int)(volt * 39.2 / 0.40)
|
||||
: (int)(volt * 39.2 / 0.55);
|
||||
break;
|
||||
default:
|
||||
sr_err("%s no conf key %u", __func__, key);
|
||||
rv = SR_ERR_NA;
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int config_list(uint32_t key, GVariant **data,
|
||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = SR_OK;
|
||||
switch (key) {
|
||||
case SR_CONF_DEVICE_OPTIONS:
|
||||
return STD_CONFIG_LIST(key, data, sdi, cg, NO_OPTS, drvopts, devopts);
|
||||
case SR_CONF_SAMPLERATE:
|
||||
*data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
|
||||
break;
|
||||
case SR_CONF_VOLTAGE_THRESHOLD:
|
||||
*data = std_gvar_min_max_step_thresholds(0.8, 2.9, 0.1);
|
||||
break;
|
||||
case SR_CONF_LOGIC_THRESHOLD:
|
||||
*data = g_variant_new_strv(ARRAY_AND_SIZE(thresholds));
|
||||
break;
|
||||
case SR_CONF_TRIGGER_MATCH:
|
||||
*data = std_gvar_array_i32(ARRAY_AND_SIZE(matches));
|
||||
break;
|
||||
default:
|
||||
sr_err("%s no conf key %u", __func__, key);
|
||||
rv = SR_ERR_NA;
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct dev_context *dc;
|
||||
int rv;
|
||||
|
||||
dc = sdi->priv;
|
||||
|
||||
if (!dc || !dc->ft) return SR_ERR_BUG;
|
||||
|
||||
rv = scanaquad_acquisition_start(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to start acquisition");
|
||||
|
||||
std_session_send_df_header(sdi);
|
||||
sr_session_source_add(sdi->session, -1, 0, 0, scanaquad_receive_data, (void *)sdi);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct dev_context *dc;
|
||||
int rv;
|
||||
|
||||
dc = sdi->priv;
|
||||
|
||||
if (!dc || !dc->ft) return SR_ERR_BUG;
|
||||
|
||||
sr_session_source_remove(sdi->session, -1);
|
||||
std_session_send_df_end(sdi);
|
||||
|
||||
rv = scanaquad_acquisition_stop(sdi, dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to stop acquisition");
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static struct sr_dev_driver ikalogic_scanaquad_driver_info = {
|
||||
.name = "ikalogic-scanaquad",
|
||||
.longname = "IKALOGIC ScanaQuad",
|
||||
.api_version = 1,
|
||||
.init = std_init,
|
||||
.cleanup = std_cleanup,
|
||||
.scan = scan,
|
||||
.dev_list = std_dev_list,
|
||||
.dev_clear = dev_clear,
|
||||
.config_get = config_get,
|
||||
.config_set = config_set,
|
||||
.config_list = config_list,
|
||||
.dev_open = dev_open,
|
||||
.dev_close = dev_close,
|
||||
.dev_acquisition_start = dev_acquisition_start,
|
||||
.dev_acquisition_stop = dev_acquisition_stop,
|
||||
.context = NULL,
|
||||
};
|
||||
SR_REGISTER_DEV_DRIVER(ikalogic_scanaquad_driver_info);
|
|
@ -0,0 +1,827 @@
|
|||
/*
|
||||
* This file is part of the libsigrok project.
|
||||
*
|
||||
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>,
|
||||
* haskal <haskal@awoo.systems>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "protocol.h"
|
||||
|
||||
SR_PRIV struct dev_context *sq_new(struct ftdi_context *ft)
|
||||
{
|
||||
struct dev_context *dc;
|
||||
|
||||
if (!ft) return NULL;
|
||||
|
||||
dc = (struct dev_context *)g_malloc0(sizeof(struct dev_context));
|
||||
dc->ft = ft;
|
||||
|
||||
/* some sensible defaults (mostly the vendor software defaults) */
|
||||
dc->samplerate = SR_MHZ(25);
|
||||
dc->capture_ratio = 10; /* 10% in */
|
||||
dc->voltage.level = 0x81; /* 3.3v */
|
||||
dc->voltage.thresh = 0x46; /* 1.8v (3.3v cmos thresh) */
|
||||
dc->voltage_idx = 2; /* "3.3V" */
|
||||
|
||||
return dc;
|
||||
}
|
||||
SR_PRIV void sq_destroy(struct dev_context *dc)
|
||||
{
|
||||
if (!dc) return;
|
||||
|
||||
if (dc->ft) {
|
||||
ftdi_free(dc->ft);
|
||||
dc->ft = NULL;
|
||||
}
|
||||
/*g_free(dc);*/
|
||||
}
|
||||
|
||||
#define CHECK_FTDI_RETVAL(write, ft, rv, expect, ...) \
|
||||
do { \
|
||||
if ((rv) < 0) { \
|
||||
sr_err("Failed to %s FTDI data in %s (%d): %s.\n", (write)?"write":"read", __func__, (rv), \
|
||||
ftdi_get_error_string(ft)); \
|
||||
return SR_ERR; \
|
||||
} else if ((size_t)(rv) != (expect) __VA_OPT__(&& __VA_ARGS__)) { \
|
||||
sr_err("FTDI %s error in %s, only %d/%zu bytes %s: %s.", (write)?"write":"read", __func__,\
|
||||
(rv), expect, (write)?"written":"read", ftdi_get_error_string(ft)); \
|
||||
return SR_ERR; \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
#define CHECK_SQ_RETVAL(rv, fmt, ...) \
|
||||
do { \
|
||||
if ((rv) < SR_ERR) { \
|
||||
if (FALSE) { \
|
||||
sr_err(fmt " in %s (%d)", ##__VA_ARGS__ , __func__, (rv)); \
|
||||
} else { \
|
||||
sr_err(fmt "in %s", ##__VA_ARGS__, __func__); \
|
||||
} \
|
||||
return rv; \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
/* low-level commands */
|
||||
|
||||
/* this is a macro instead of an inline static fn, because CHECK_FTDI_RETVAL
|
||||
* uses the __func__ pseudomacro for diagnostic purposes, which would be
|
||||
* rendered useless if this were a real function */
|
||||
#define read_ftdi_blocking(ft, data, s) ({\
|
||||
size_t __i = 0; \
|
||||
int __rv = 0; \
|
||||
do { \
|
||||
__rv = ftdi_read_data(ft, (uint8_t *)(data)+__i, (s)-__i); \
|
||||
CHECK_FTDI_RETVAL(FALSE, ft, __rv, (s)-__i, FALSE); \
|
||||
/*sr_spew("read_blocking: read %d\n", __rv);*/ \
|
||||
__i += __rv; \
|
||||
} while (__i < s); \
|
||||
__rv;}) \
|
||||
|
||||
SR_PRIV int sq_get_status(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t get_status_cmd[] = {0xfd,0x00,0x01,0x02,0xfe};
|
||||
uint8_t stat[4];
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
rv = ftdi_write_data(dc->ft, get_status_cmd, sizeof get_status_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof get_status_cmd);
|
||||
|
||||
rv = read_ftdi_blocking(dc->ft, stat, sizeof stat);
|
||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, sizeof stat);
|
||||
|
||||
if (stat[0] != stat[1] || stat[0] != stat[2] || stat[0] != stat[3])
|
||||
sr_warn("status: incoherent: %02x %02x %02x %02x", stat[0], stat[1], stat[2], stat[3]);
|
||||
else
|
||||
sr_spew("status: %02x", stat[0]);
|
||||
|
||||
return stat[0]; /* array contains the same value 4 times */
|
||||
}
|
||||
SR_PRIV int sq_reset_app(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t go_app_cmd[] = {0x93};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
sr_spew("reset to app");
|
||||
rv = ftdi_write_data(dc->ft, go_app_cmd, sizeof go_app_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof go_app_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_reset_bl(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t go_bl_cmd[] = {0x94};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
sr_spew("reset to bl");
|
||||
rv = ftdi_write_data(dc->ft, go_bl_cmd, sizeof go_bl_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof go_bl_cmd);
|
||||
|
||||
return SR_OK;
|
||||
|
||||
}
|
||||
SR_PRIV int sq_get_devid_from_eeprom(struct dev_context *dc, uint8_t *devid)
|
||||
{
|
||||
uint16_t val1, val2;
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
if ((rv = ftdi_read_eeprom_location(dc->ft, 0x12, &val1)) < 0) {
|
||||
sr_err("Failed to read EEPROM index 0x12 (%d): %s.",
|
||||
rv, ftdi_get_error_string(dc->ft));
|
||||
return SR_ERR;
|
||||
}
|
||||
if ((rv = ftdi_read_eeprom_location(dc->ft, 0x13, &val2)) < 0) {
|
||||
sr_err("Failed to read EEPROM index 0x13 (%d): %s.",
|
||||
rv, ftdi_get_error_string(dc->ft));
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
devid[0] = (val1 >> 0) & 0xff;
|
||||
devid[1] = (val1 >> 8) & 0xff;
|
||||
devid[2] = (val2 >> 0) & 0xff;
|
||||
|
||||
sr_dbg("got devid %02x %02x %02x", devid[0], devid[1], devid[2]);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
SR_PRIV int sq_bl_send_devid(struct dev_context *dc, const uint8_t *devid)
|
||||
{
|
||||
uint8_t send_devid_cmd[27] = {0xf1, 0,0,0, 0};
|
||||
int rv;
|
||||
|
||||
if (!dc || !devid) return SR_ERR;
|
||||
|
||||
send_devid_cmd[1] = devid[0];
|
||||
send_devid_cmd[2] = devid[1];
|
||||
send_devid_cmd[3] = devid[2];
|
||||
|
||||
sr_spew("auth with devid %02x %02x %02x", devid[0], devid[1], devid[2]);
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_devid_cmd, sizeof send_devid_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_devid_cmd);
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_bl_spi_chipsel(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t send_spi_chipsel_cmd[2] = {0x90,0x00};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
sr_spew("SPI chip select");
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_spi_chipsel_cmd, sizeof send_spi_chipsel_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_spi_chipsel_cmd);
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_bl_spi_chipdesel(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t send_spi_chipdesel_cmd[2] = {0x91,0x00};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
sr_spew("SPI chip deselect");
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_spi_chipdesel_cmd, sizeof send_spi_chipdesel_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_spi_chipdesel_cmd);
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_bl_spi_xfer(struct dev_context *dc, uint8_t val)
|
||||
{
|
||||
uint8_t send_spi_xfer_cmd[2] = {0x92, 0};
|
||||
int rv;
|
||||
uint8_t read = 0;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_spi_xfer_cmd, sizeof send_spi_xfer_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_spi_xfer_cmd);
|
||||
|
||||
rv = read_ftdi_blocking(dc->ft, &read, 1);
|
||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, (size_t)1);
|
||||
|
||||
sr_spew("SPI xfer: %02x -> %02x", val, read);
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
SR_PRIV int sq_app_cancel_capture(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t send_capture_cancel_cmd[] = {0xf0,0x00};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
sr_spew("cancel sc/wft");
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_capture_cancel_cmd, sizeof send_capture_cancel_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_capture_cancel_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_app_start_capture(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t send_start_capture_cmd[] = {0xf0,0x01};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR_BUG;
|
||||
|
||||
sr_spew("sc/wft");
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_start_capture_cmd, sizeof send_start_capture_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_start_capture_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_app_try_get_capture_result(struct dev_context *dc, uint32_t *trig_instant)
|
||||
{
|
||||
uint32_t ti;
|
||||
uint8_t result[4];
|
||||
int rv, todo, done;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
todo = 4;
|
||||
done = 0;
|
||||
|
||||
rv = ftdi_read_data(dc->ft, &result[done], todo - done);
|
||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, (size_t)0, FALSE);
|
||||
|
||||
if (rv == 0) return SR_ERR_ARG;
|
||||
|
||||
done += rv;
|
||||
|
||||
while (done < todo) {
|
||||
rv = ftdi_read_data(dc->ft, &result[done], todo - done);
|
||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, (size_t)0, FALSE);
|
||||
done += rv;
|
||||
}
|
||||
|
||||
ti = (uint32_t)result[0] | ((uint32_t)result[1] << 8) | ((uint32_t)result[2] << 16);
|
||||
if (trig_instant) *trig_instant = ti;
|
||||
|
||||
sr_spew("sc/wft -> %06x %02x", ti, result[3]);
|
||||
|
||||
return result[3];
|
||||
}
|
||||
SR_PRIV int sq_app_start_generate_inf(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t send_start_geninf_cmd[] = {0xf0,0x02};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
sr_spew("start generate inf");
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_start_geninf_cmd, sizeof send_start_geninf_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_start_geninf_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_app_start_capgenmix(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t send_start_capgenmix_cmd[] = {0xf0,0x03};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR_BUG;
|
||||
|
||||
sr_spew("start mixed sc/g/wft");
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_start_capgenmix_cmd, sizeof send_start_capgenmix_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_start_capgenmix_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_app_upload_genpattern(struct dev_context *dc, const void *data, size_t len)
|
||||
{
|
||||
static const uint8_t send_upl_genpat_cmd[] = {0xf0,0x05,0xf3};
|
||||
int rv;
|
||||
size_t pos = 0;
|
||||
|
||||
if (!dc || !data || !len) return SR_ERR;
|
||||
|
||||
sr_spew("upload genpatt %zx", len);
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_upl_genpat_cmd, sizeof send_upl_genpat_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_upl_genpat_cmd);
|
||||
|
||||
/* TODO: use async libftdi api? */
|
||||
do {
|
||||
rv = ftdi_write_data(dc->ft, (const uint8_t *)data + pos, len - pos);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, len, FALSE);
|
||||
|
||||
pos += rv;
|
||||
} while (pos < len);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_app_download_capture(struct dev_context *dc, void *data, size_t len)
|
||||
{
|
||||
static const uint8_t send_dl_capt_cmd[] = {0xf0,0x06};
|
||||
int rv;
|
||||
|
||||
if (!dc || !data || !len) return SR_ERR;
|
||||
|
||||
sr_spew("download capture %zx", len);
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_dl_capt_cmd, sizeof send_dl_capt_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_dl_capt_cmd);
|
||||
|
||||
rv = read_ftdi_blocking(dc->ft, data, len);
|
||||
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, len);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_app_start_generate_once(struct dev_context *dc)
|
||||
{
|
||||
static const uint8_t send_start_genonce_cmd[] = {0xf0,0x07};
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
sr_spew("start generate once");
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_start_genonce_cmd, sizeof send_start_genonce_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_start_genonce_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
SR_PRIV int sq_app_apply_settings(struct dev_context *dc, const struct sq_app_settings *sett)
|
||||
{
|
||||
uint8_t send_sett_cmd[25] = {0xf1, 0};
|
||||
uint8_t *blob;
|
||||
int rv;
|
||||
|
||||
if (!dc || !sett) return SR_ERR;
|
||||
|
||||
blob = &send_sett_cmd[1]; /* so i wont mess up offsets while writing this code */
|
||||
|
||||
blob[0x00] = sett->trigscale_us;
|
||||
blob[0x01] = sett->clockfreq & 0xff;
|
||||
blob[0x02] = (sett->clockfreq>>8) & 0xff;
|
||||
blob[0x03] = sett->trigger_pw_scale & 0xff;
|
||||
blob[0x04] = (sett->trigger_pw_scale>>8) & 0xff;
|
||||
blob[0x05] = sett->memsetting1[0];
|
||||
blob[0x06] = sett->memsetting1[1];
|
||||
blob[0x07] = sett->memsetting1[2];
|
||||
blob[0x08] = sett->memsetting2[0];
|
||||
blob[0x09] = sett->memsetting2[1];
|
||||
blob[0x0a] = sett->memsetting2[2];
|
||||
blob[0x0b] = sett->memsetting3[0];
|
||||
blob[0x0c] = sett->memsetting3[1];
|
||||
blob[0x0d] = sett->memsetting3[2];
|
||||
blob[0x0e] = 0;
|
||||
blob[0x0f] = sett->ntrigsteps;
|
||||
blob[0x10] = 0xf0;
|
||||
blob[0x11] = 0x0f;
|
||||
blob[0x12] = sett->chanoutmap;
|
||||
blob[0x13] = sett->voltage[0];
|
||||
blob[0x14] = sett->voltage[1];
|
||||
blob[0x15] = 0x32;
|
||||
blob[0x16] = sett->capture ? 0x01 : 0x00;
|
||||
blob[0x17] = sett->generate ? 0x01 : 0x00;
|
||||
|
||||
sr_spew("apply settings"); /* TODO: print info about applied settings */
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_sett_cmd, sizeof send_sett_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_sett_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int sq_app_apply_triggers(struct dev_context *dc, const struct sq_app_trigstep *steps, size_t nsteps)
|
||||
{
|
||||
uint8_t *send_trig_cmd;
|
||||
size_t i, blobsize;
|
||||
uint32_t bitfield;
|
||||
int rv;
|
||||
|
||||
if (!dc || !steps || !nsteps) return SR_ERR;
|
||||
|
||||
/* nsteps shouldn't exceed 8 or so */
|
||||
blobsize = 1 + nsteps * 4;
|
||||
send_trig_cmd = (uint8_t *)g_malloc0(blobsize);
|
||||
send_trig_cmd[0] = 0xf4;
|
||||
|
||||
for (i = 0; i < nsteps; ++i) {
|
||||
bitfield = 0;
|
||||
|
||||
bitfield |= (steps[i].level ? 1 : 0) << 31;
|
||||
bitfield |= (steps[i].pw_max & 0x1ff) << 21;
|
||||
bitfield |= (steps[i].lvloverride ? 1 : 0) << 20;
|
||||
bitfield |= (steps[i].pw_min & 0x1ff) << 10;
|
||||
bitfield |= (steps[i].ch_ignore & 0xf) << 6;
|
||||
bitfield |= (steps[i].nomax ? 1 : 0) << 5;
|
||||
bitfield |= (steps[i].nomin ? 1 : 0) << 4;
|
||||
bitfield |= (steps[i].ch_hi_rise_lo_fall & 0xf) << 0;
|
||||
|
||||
send_trig_cmd[1 + i*4 + 0] = (bitfield>> 0)&0xff;
|
||||
send_trig_cmd[1 + i*4 + 1] = (bitfield>> 8)&0xff;
|
||||
send_trig_cmd[1 + i*4 + 2] = (bitfield>>16)&0xff;
|
||||
send_trig_cmd[1 + i*4 + 3] = (bitfield>>14)&0xff;
|
||||
}
|
||||
|
||||
sr_spew("apply triggers"); /* TODO: print info about applied triggers */
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_trig_cmd, blobsize);
|
||||
g_free(send_trig_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_trig_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
SR_PRIV int sq_app_apply_default_settings(struct dev_context *dc)
|
||||
{
|
||||
/* buffer sizes meant for SQ25, but won't hurt on others I guess */
|
||||
static const uint8_t send_sdef_cmd[25] = {
|
||||
0xf1,
|
||||
0x01, 0x04, 0x00, 0x00, 0x00, 0x48, 0xe8, 0x01,
|
||||
0x48, 0xe8, 0x01, 0x08, 0x28, 0xf1, 0x00, 0x00,
|
||||
0xf0, 0x0f, 0x0f, 0x81, 0x4b, 0x32, 0x01, 0x00
|
||||
};
|
||||
int rv;
|
||||
|
||||
sr_spew("apply default settings");
|
||||
|
||||
rv = ftdi_write_data(dc->ft, send_sdef_cmd, sizeof send_sdef_cmd);
|
||||
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_sdef_cmd);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* higher-level routines (finally) */
|
||||
SR_PRIV int scanaquad_init(struct dev_context *dc)
|
||||
{
|
||||
size_t i;
|
||||
int rv;
|
||||
|
||||
sr_info("scanaquad: init!");
|
||||
|
||||
rv = sq_get_status(dc);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't get status");
|
||||
|
||||
if (rv == sq_status_app) {
|
||||
rv = sq_app_cancel_capture(dc);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't cancel capture/wait-for-trigger");
|
||||
}
|
||||
|
||||
rv = sq_reset_bl(dc);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't reset to bootloader");
|
||||
|
||||
/* wait until we are in bootloader mode */
|
||||
for (i = 0; i < 0x1000; ++i) {
|
||||
rv = sq_get_status(dc);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't get status");
|
||||
if (rv != sq_status_app) break;
|
||||
}
|
||||
if (rv == sq_status_app) {
|
||||
sr_err("Failed to enter bootloader mode (timed out)");
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
if (!dc->has_devid) {
|
||||
rv = sq_get_devid_from_eeprom(dc, &dc->devid[0]);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't get magic device ID");
|
||||
dc->has_devid = TRUE;
|
||||
}
|
||||
|
||||
rv = sq_bl_send_devid(dc, dc->devid);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't send magic device ID");
|
||||
|
||||
for (i = 0; i < 0x1000 /* should be enough */; ++i) {
|
||||
rv = sq_get_status(dc);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't get status");
|
||||
if (rv == sq_status_bl_auth) break;
|
||||
}
|
||||
if (rv != sq_status_bl_auth) {
|
||||
sr_err("Failed to authenticate ScanaQuad device (timed out)... devid=%02x %02x %02x",
|
||||
dc->devid[0], dc->devid[1], dc->devid[2]);
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
rv = sq_reset_app(dc);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't reset to application");
|
||||
|
||||
for (i = 0; i < 0x1000 /* should be enough */; ++i) {
|
||||
rv = sq_get_status(dc);
|
||||
CHECK_SQ_RETVAL(rv, "couldn't get status");
|
||||
if (rv == sq_status_app) break;
|
||||
}
|
||||
if (rv != sq_status_app) {
|
||||
sr_err("Failed to enter application mode (timed out)");
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
return sq_app_apply_default_settings(dc);
|
||||
}
|
||||
SR_PRIV int scanaquad_cancel_all(struct dev_context *dc)
|
||||
{
|
||||
int rv;
|
||||
|
||||
sr_spew("cancel everything");
|
||||
|
||||
dc->acq_started = FALSE;
|
||||
|
||||
rv = sq_get_status(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to get ScanaQuad status");
|
||||
|
||||
if (rv == sq_status_app) {
|
||||
/* cancel ongoing capture/wait-for-trigger */
|
||||
rv = sq_app_cancel_capture(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing SC/WFT");
|
||||
|
||||
/* cancel ongoing pattern generation */
|
||||
rv = scanaquad_apply_current_settings(dc, TRUE);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to apply default settings");
|
||||
|
||||
/* cancel ongoing capture/wait-for-trigger */
|
||||
rv = sq_app_cancel_capture(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing SC/WFT");
|
||||
|
||||
/* cancel ongoing pattern generation */
|
||||
rv = scanaquad_apply_current_settings(dc, TRUE);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to apply default settings");
|
||||
}
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
SR_PRIV int scanaquad_apply_current_settings(struct dev_context *dc, gboolean passive)
|
||||
{
|
||||
/* if passive:
|
||||
* * no triggers
|
||||
* * set to capture mode(?) */
|
||||
|
||||
uint64_t samplim;
|
||||
uint32_t ms1, ms2, ms3;
|
||||
struct sq_app_settings sett;
|
||||
|
||||
/* clockfreq == 100000 / samplerate_kHz */
|
||||
sett.clockfreq = (uint16_t)((100000uLL*1000) / dc->samplerate);
|
||||
sett.voltage[0] = dc->voltage.level;
|
||||
sett.voltage[1] = passive ? 0x4b : dc->voltage.thresh;
|
||||
sett.chanoutmap = 0x0f; /* TODO: hardcoded: all inputs */
|
||||
|
||||
/* TODO: more than just capture mode */
|
||||
samplim = dc->limit_samples / 4;
|
||||
/* sample limit vs LA physical memory size check is done in api.c */
|
||||
ms1 = samplim;
|
||||
ms2 = samplim;
|
||||
ms3 = (uint32_t)(dc->memsize_max * (1 - dc->capture_ratio * 0.01));
|
||||
|
||||
sett.memsetting1[0] = (ms1>> 0) & 0xff;
|
||||
sett.memsetting1[1] = (ms1>> 8) & 0xff;
|
||||
sett.memsetting1[2] = (ms1>>16) & 0xff;
|
||||
sett.memsetting2[0] = (ms2>> 0) & 0xff;
|
||||
sett.memsetting2[1] = (ms2>> 8) & 0xff;
|
||||
sett.memsetting2[2] = (ms2>>16) & 0xff;
|
||||
sett.memsetting3[0] = (ms3>> 0) & 0xff;
|
||||
sett.memsetting3[1] = (ms3>> 8) & 0xff;
|
||||
sett.memsetting3[2] = (ms3>>16) & 0xff;
|
||||
|
||||
/* TODO */
|
||||
sett.trigscale_us = 0x01;
|
||||
sett.ntrigsteps = 0;
|
||||
sett.trigger_pw_scale = 0;
|
||||
sett.capture = TRUE;
|
||||
sett.generate = FALSE;
|
||||
|
||||
dc->MS_capture = ms1;
|
||||
dc->MS_generate = ms2 - ms1;
|
||||
|
||||
return sq_app_apply_settings(dc, &sett);
|
||||
}
|
||||
SR_PRIV int scanaquad_apply_current_triggers(struct dev_context *dc)
|
||||
{
|
||||
(void)dc;
|
||||
return SR_OK; /* HUGE TODO */
|
||||
}
|
||||
|
||||
|
||||
/* finally, we can implement the higher-level functions. */
|
||||
|
||||
|
||||
SR_PRIV int scanaquad_acquisition_start(struct dev_context *dc)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (!dc) return SR_ERR;
|
||||
|
||||
if (dc->acq_started) return SR_ERR_BUG;
|
||||
|
||||
sr_spew("data acquisition start");
|
||||
|
||||
rv = scanaquad_cancel_all(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
||||
|
||||
rv = sq_get_status(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to get current device state");
|
||||
if (rv != sq_status_app) {
|
||||
sr_err("Device not in application mode.");
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
rv = scanaquad_apply_current_settings(dc, FALSE);
|
||||
if (rv == SR_OK) rv = scanaquad_apply_current_triggers(dc);
|
||||
if (rv < 0) {
|
||||
/* rollback */
|
||||
scanaquad_apply_current_settings(dc, TRUE);
|
||||
sq_app_cancel_capture(dc);
|
||||
}
|
||||
CHECK_SQ_RETVAL(rv, "Failed to apply device settings");
|
||||
|
||||
rv = sq_get_status(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to get current device state");
|
||||
if (rv != sq_status_app) {
|
||||
sr_err("Device not in application mode.");
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
rv = sq_app_cancel_capture(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
||||
|
||||
rv = sq_app_start_capture(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to start capture");
|
||||
|
||||
dc->acq_started = TRUE;
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static void scanaquad_unpack_samples(uint8_t *rdata, size_t totalunpackbytes)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = totalunpackbytes / 2; i > 0; --i) {
|
||||
rdata[i*2-1] = (rdata[i-1] & 0xf0) >> 4;
|
||||
rdata[i*2-2] = (rdata[i-1] & 0x0f) >> 0;
|
||||
}
|
||||
}
|
||||
|
||||
SR_PRIV int scanaquad_acquisition_finish(struct sr_dev_inst *sdi, struct dev_context *dc)
|
||||
{
|
||||
struct sr_datafeed_packet packet1, packet2, packet3;
|
||||
struct sr_datafeed_logic logic1, logic3;
|
||||
size_t nbytes;
|
||||
uint8_t *rdata;
|
||||
uint32_t trig_instant;
|
||||
int rv;
|
||||
|
||||
if (!dc || !dc->acq_started) return SR_ERR_BUG;
|
||||
|
||||
sr_spew("gracefully finish acquisition");
|
||||
|
||||
trig_instant = dc->trig_instant;
|
||||
dc->acq_started = FALSE;
|
||||
|
||||
sr_spew("trig_instant = %u", trig_instant);
|
||||
|
||||
nbytes = dc->MS_capture * 2;
|
||||
/* we need 2x the amount of bytes we'll read from the device, as these are
|
||||
* currently packed per nybbles, while sigrok wants the samples in separate
|
||||
* bytes */
|
||||
rdata = g_try_malloc(nbytes * 2);
|
||||
if (!rdata) {
|
||||
sr_err("Out of memory: cannot download captured samples");
|
||||
|
||||
/* rollback */
|
||||
scanaquad_apply_current_settings(dc, TRUE);
|
||||
sq_app_cancel_capture(dc);
|
||||
|
||||
return SR_ERR;
|
||||
}
|
||||
/* convert from trig_instant units (MS1*16) to bytes */
|
||||
trig_instant = (trig_instant * 2) / 8;
|
||||
|
||||
rv = sq_app_cancel_capture(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
||||
|
||||
rv = sq_app_download_capture(dc, rdata, nbytes);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to download captured samples");
|
||||
|
||||
rv = sq_app_cancel_capture(dc);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to cancel ongoing ScanaQuad transfers");
|
||||
|
||||
rv = scanaquad_apply_current_settings(dc, TRUE);
|
||||
CHECK_SQ_RETVAL(rv, "Failed to reset ScanaQuad settings");
|
||||
|
||||
scanaquad_unpack_samples(rdata, nbytes * 2);
|
||||
|
||||
/* 3 packets, for trigger indicator */
|
||||
logic1.length = trig_instant;
|
||||
logic1.unitsize = 1;
|
||||
logic1.data = rdata;
|
||||
packet1.type = SR_DF_LOGIC;
|
||||
packet1.payload = &logic1;
|
||||
|
||||
packet2.type = SR_DF_TRIGGER;
|
||||
packet2.payload = NULL;
|
||||
|
||||
logic3.length = (nbytes * 2) - trig_instant;
|
||||
logic3.unitsize = 1;
|
||||
logic3.data = rdata + trig_instant;
|
||||
packet3.type = SR_DF_LOGIC;
|
||||
packet3.payload = &logic3;
|
||||
|
||||
sr_spew("data sendoff! 2nb=%zu l1=%zu l3=%zu", nbytes*2, logic1.length, logic3.length);
|
||||
|
||||
sr_session_send(sdi, &packet1);
|
||||
sr_session_send(sdi, &packet2);
|
||||
sr_session_send(sdi, &packet3);
|
||||
|
||||
/* i hope this is fine to do? seems like it does work just fine with pulseview */
|
||||
g_free(rdata);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int scanaquad_acquisition_stop(struct sr_dev_inst *sdi, struct dev_context *dc)
|
||||
{
|
||||
int rv;
|
||||
|
||||
sr_spew("force-stop acquisition");
|
||||
|
||||
if (!dc->acq_started) {
|
||||
sr_spew("already stopped");
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
rv = sq_app_try_get_capture_result(dc, &dc->trig_instant);
|
||||
if (rv == SR_ERR_ARG) { /* still busy */
|
||||
sr_info("Canceling capture");
|
||||
return scanaquad_cancel_all(dc);
|
||||
} else if (rv < 0) {
|
||||
sr_err("Cannot get device state / capture result");
|
||||
return SR_ERR;
|
||||
} else if (rv == SQ_APP_START_CAPTURE_SUCCESS) {
|
||||
return scanaquad_acquisition_finish(sdi, dc);
|
||||
} else {
|
||||
sr_err("Error during capture: %02x", rv);
|
||||
return SR_ERR_BUG;
|
||||
}
|
||||
}
|
||||
|
||||
SR_PRIV int scanaquad_receive_data(int fd, int revents, void *cb_data)
|
||||
{
|
||||
struct sr_dev_inst *sdi;
|
||||
struct dev_context *dc;
|
||||
int rv;
|
||||
|
||||
(void)fd;
|
||||
(void)revents;
|
||||
|
||||
if (!(sdi = cb_data)) return TRUE;
|
||||
if (!(dc = sdi->priv)) return TRUE;
|
||||
if (!dc->ft) return TRUE;
|
||||
|
||||
/*if (revents != G_IO_IN) return TRUE;*/
|
||||
|
||||
rv = sq_app_try_get_capture_result(dc, &dc->trig_instant);
|
||||
if (rv == SR_ERR_ARG) { /* still busy */
|
||||
sr_spew("Acquisition still busy...");
|
||||
return TRUE;
|
||||
} else if (rv < 0) {
|
||||
sr_err("Cannot get device state / capture result");
|
||||
sr_dev_acquisition_stop(sdi);
|
||||
return FALSE;
|
||||
} else if (rv == SQ_APP_START_CAPTURE_SUCCESS) {
|
||||
rv = scanaquad_acquisition_finish(sdi, dc);
|
||||
if (rv == SR_ERR) {
|
||||
sr_err("acquisition_finish failed");
|
||||
sr_dev_acquisition_stop(sdi);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sr_spew("acquisition finished!");
|
||||
sr_dev_acquisition_stop(sdi);
|
||||
return TRUE;
|
||||
} else {
|
||||
sr_err("Error during acquisition: %02x", rv);
|
||||
sr_dev_acquisition_stop(sdi);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* This file is part of the libsigrok project.
|
||||
*
|
||||
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>,
|
||||
* haskal <haskal@awoo.systems>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LIBSIGROK_HARDWARE_IKALOGIC_SCANAQUAD_PROTOCOL_H
|
||||
#define LIBSIGROK_HARDWARE_IKALOGIC_SCANAQUAD_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include <ftdi.h>
|
||||
#include <libsigrok/libsigrok.h>
|
||||
#include "libsigrok-internal.h"
|
||||
|
||||
#define LOG_PREFIX "ikalogic-scanaquad"
|
||||
|
||||
#define MAX_MEMSIZE_SQ50 0x03d090
|
||||
|
||||
/*
|
||||
* For info on how the ScanaQuad works, see our docs here:
|
||||
* https://git.lain.faith/BLAHAJ/sq50-re/wiki
|
||||
*/
|
||||
|
||||
struct voltage_setting {
|
||||
uint8_t level;
|
||||
uint8_t thresh;
|
||||
};
|
||||
|
||||
struct dev_context {
|
||||
struct ftdi_context *ft;
|
||||
|
||||
/* settings for next capture */
|
||||
uint64_t samplerate;
|
||||
uint64_t limit_samples; /* samples */
|
||||
uint64_t capture_ratio; /* 0..100 */
|
||||
struct voltage_setting voltage;
|
||||
int16_t voltage_idx;
|
||||
|
||||
uint32_t trig_instant;
|
||||
|
||||
uint32_t MS_capture;
|
||||
uint32_t MS_generate;
|
||||
|
||||
uint32_t memsize_max;
|
||||
int devtype; /* 25, 50, 100 or 200 for SQ25, SQ50, and so on */
|
||||
uint8_t devid[3];
|
||||
gboolean has_devid;
|
||||
|
||||
gboolean acq_started;
|
||||
};
|
||||
|
||||
enum sq_status {
|
||||
sq_status_bl_auth = 0x01,
|
||||
sq_status_bl_boot = 0x09,
|
||||
sq_status_app = 0x22
|
||||
};
|
||||
struct sq_app_settings {
|
||||
uint16_t clockfreq;
|
||||
uint16_t trigger_pw_scale;
|
||||
uint8_t memsetting1[3];
|
||||
uint8_t memsetting2[3];
|
||||
uint8_t memsetting3[3];
|
||||
uint8_t voltage[2];
|
||||
uint8_t trigscale_us;
|
||||
uint8_t ntrigsteps;
|
||||
uint8_t chanoutmap;
|
||||
|
||||
gboolean capture;
|
||||
gboolean generate;
|
||||
};
|
||||
struct sq_app_trigstep {
|
||||
uint16_t pw_min;
|
||||
uint16_t pw_max;
|
||||
|
||||
uint8_t ch_ignore; /* set bit in this mask to ignore a channel */
|
||||
uint8_t ch_hi_rise_lo_fall; /* 1 in mask => match high/rise for chan, else low/fall */
|
||||
|
||||
gboolean level; /* false: edge */
|
||||
gboolean lvloverride;
|
||||
gboolean nomax;
|
||||
gboolean nomin;
|
||||
};
|
||||
|
||||
#define SQ_APP_START_CAPTURE_SUCCESS 0xdd
|
||||
|
||||
SR_PRIV struct dev_context *sq_new(struct ftdi_context *ft);
|
||||
SR_PRIV void sq_destroy(struct dev_context *dc);
|
||||
|
||||
/* low-level wire protocol functions. see
|
||||
* https://git.lain.faith/BLAHAJ/sq50-re/wiki/USB-protocol for docs on how
|
||||
* these work. */
|
||||
|
||||
/* general commands */
|
||||
SR_PRIV int sq_get_status(struct dev_context *dc);
|
||||
SR_PRIV int sq_reset_app(struct dev_context *dc);
|
||||
SR_PRIV int sq_reset_bl (struct dev_context *dc);
|
||||
SR_PRIV int sq_get_devid_from_eeprom(struct dev_context *dc, uint8_t *devid);
|
||||
|
||||
/* bootloader mode commands */
|
||||
SR_PRIV int sq_bl_send_devid(struct dev_context *dc, const uint8_t *devid);
|
||||
SR_PRIV int sq_bl_spi_chipsel(struct dev_context *dc);
|
||||
SR_PRIV int sq_bl_spi_chipdesel(struct dev_context *dc);
|
||||
SR_PRIV int sq_bl_spi_xfer(struct dev_context *dc, uint8_t val);
|
||||
/* two aliases for clarity */
|
||||
static inline int sq_bl_spi_xfer_write(struct dev_context *dc, uint8_t val)
|
||||
{
|
||||
return sq_bl_spi_xfer(dc, val);
|
||||
}
|
||||
static inline int sq_bl_spi_xfer_read(struct dev_context *dc)
|
||||
{
|
||||
return sq_bl_spi_xfer(dc, 0xff);
|
||||
}
|
||||
|
||||
/* application mode commands */
|
||||
/* 0xf0 0x0X commands */
|
||||
SR_PRIV int sq_app_cancel_capture(struct dev_context *dc);
|
||||
SR_PRIV int sq_app_start_capture(struct dev_context *dc);
|
||||
SR_PRIV int sq_app_try_get_capture_result(struct dev_context *dc, uint32_t *trig_instant);
|
||||
SR_PRIV int sq_app_start_generate_inf(struct dev_context *dc);
|
||||
SR_PRIV int sq_app_start_capgenmix(struct dev_context *dc);
|
||||
static inline int sq_app_try_get_capgenmix_result(struct dev_context *dc, uint32_t *trig_instant) {
|
||||
return sq_app_try_get_capture_result(dc, trig_instant);
|
||||
}
|
||||
/* NOTE: at this point, no checks are done whether the length of the data is
|
||||
* consistent with the current capture/genpattern memory size in the
|
||||
* device's settings */
|
||||
SR_PRIV int sq_app_upload_genpattern(struct dev_context *dc, const void *data, size_t len);
|
||||
SR_PRIV int sq_app_download_capture(struct dev_context *dc, void *data, size_t len);
|
||||
SR_PRIV int sq_app_start_generate_once(struct dev_context *dc);
|
||||
|
||||
/* 0xf1, 0xf4 */
|
||||
SR_PRIV int sq_app_apply_settings(struct dev_context *dc, const struct sq_app_settings *sett);
|
||||
SR_PRIV int sq_app_apply_triggers(struct dev_context *dc, const struct sq_app_trigstep *steps, size_t nsteps);
|
||||
|
||||
/* useful at device init & reset, as it cancels ongoing signal generator stuff */
|
||||
SR_PRIV int sq_app_apply_default_settings(struct dev_context *dc);
|
||||
|
||||
/* higher-level stuff used by api.c */
|
||||
|
||||
SR_PRIV int scanaquad_init(struct dev_context *dc);
|
||||
SR_PRIV int scanaquad_cancel_all(struct dev_context *dc);
|
||||
SR_PRIV int scanaquad_apply_current_settings(struct dev_context *dc, gboolean passive);
|
||||
SR_PRIV int scanaquad_apply_current_triggers(struct dev_context *dc);
|
||||
|
||||
SR_PRIV int scanaquad_acquisition_start(struct dev_context *dc);
|
||||
/* stuff to do when an acquisition ended successfully */
|
||||
SR_PRIV int scanaquad_acquisition_finish(struct sr_dev_inst *sdi, struct dev_context *dc);
|
||||
/* either finish when possible right now, or abort */
|
||||
SR_PRIV int scanaquad_acquisition_stop(struct sr_dev_inst *sdi, struct dev_context *dc);
|
||||
SR_PRIV int scanaquad_receive_data(int fd, int revents, void *cb_data);
|
||||
|
||||
#endif
|
|
@ -90,7 +90,7 @@ SR_PRIV int p_ols_open(struct dev_context *devc)
|
|||
return SR_ERR;
|
||||
}
|
||||
|
||||
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
|
||||
if ((ret = ftdi_tcioflush(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;
|
||||
|
|
Loading…
Reference in New Issue