ikalogic-scanaquad: low-level protocol stuff

This commit is contained in:
Triss 2021-06-24 02:43:49 +02:00
parent 012becda49
commit d8f08acdc3
4 changed files with 579 additions and 5 deletions

View File

@ -289,7 +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])
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])

View File

@ -1,7 +1,8 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>
* 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

View File

@ -1,7 +1,8 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>
* 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
@ -20,6 +21,485 @@
#include <config.h>
#include "protocol.h"
SR_PRIV struct dev_context *sq_new(struct ftdi_context *ft)
{
struct dev_context *dc;
int rv;
if (!ft) return NULL;
dc = (struct dev_context *)gmalloc0(sizeof(struct dev_context));
dc->ft = ft;
rv = ftdi_tcioflush(ft);
if (rv == -3 /* USB device unavailable */) {
sr_err("ftdi_tcioflush: (%d) USB device unavailable", rv);
dc->ft = NULL;
gfree(dc);
return NULL;
}
/* other errors (failed to purge buffers): eh, let's ignore that */
return dc;
}
SR_PRIV void sq_destroy(struct dev_context *dc)
{
if (!dc) return;
if (dc->ft) {
ftdi_free(dc->ft);
dc->ft = NULL;
}
gfree(dc);
}
#define CHECK_FTDI_RETVAL(write, ft, rv, expect, ...) \
do { \
if ((rv) < 0) { \
sr_err("Failed to %s FTDI data (%d): %s.\n", (write)?"write":"read", (rv), \
ftdi_get_error_string(ft)); \
return SR_ERR; \
} else if ((rv) != (expect) __VA_OPT__(&& __VA_ARGS__)) { \
sr_err("FTDI %s error, only %d/%zu bytes %s: %s.", (write)?"write":"read",\
(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 " (%d): %s", ##__VA_ARGS__ , (rv), ftdi_get_error_string(/*ft*/NULL)); \
} else { \
sr_err(fmt, ##__VA_ARGS__); \
} \
return rv; \
} \
} while (0) \
/* low-level commands */
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 = ftdi_read_data(dc->ft, stat, sizeof stat);
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, sizeof stat);
if (stat[0] != staŧ[1] || stat[0] != stat[2] || staŧ[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)
{
int val1, val2, 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.",
ret, 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.",
ret, 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 = ftdi_read_data(dc->ft, &read, 1);
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, 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};
uint8_t result[4];
int rv;
if (!dc) return SR_ERR;
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_get_capture_result(struct dev_context *dc, uint32_t *trig_instant)
{
uint8_t result[4];
int rv;
if (!dc || !trig_instant) return SR_ERR;
rv = ftdi_read_data(dc->ft, result, sizeof result);
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, sizeof result);
*trig_instant = (uint32_t)result[0] | ((uint32_t)result[1] << 8) | ((uint32_t)result[2] << 16);
sr_spew("sc/wft -> %06x %02x", *trig_instant, 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;
sc_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_capture_cmd[] = {0xf0,0x03};
int rv;
if (!dc) return SR_ERR;
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);
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;
size_t pos = 0;
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);
do {
rv = ftdi_read_data(dc->ft, (uint8_t *)data + pos, len - pos);
CHECK_FTDI_RETVAL(FALSE, dc->ft, rv, len, FALSE);
pos += rv;
} while (pos < len);
return SR_OK;
}
SR_PRIV 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 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 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 *)gmalloc0(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);
gfree(send_trig_cmd);
CHECK_FTDI_RETVAL(TRUE, dc->ft, rv, sizeof send_trig_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;
}
/* TODO: send default settings? */
return SR_OK;
}
SR_PRIV int ikalogic_scanaquad_receive_data(int fd, int revents, void *cb_data)
{
const struct sr_dev_inst *sdi;

View File

@ -1,7 +1,8 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2021 sys64738 <sys64738@disroot.org>
* 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
@ -21,15 +22,107 @@
#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"
struct dev_context {
struct ftdi_context *ft;
uint8_t devid[3];
gboolean has_devid;
};
SR_PRIV int ikalogic_scanaquad_receive_data(int fd, int revents, void *cb_data);
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 SR_PRIV int sq_bl_spi_xfer_write(struct dev_context *dc, uint8_t val)
{
return sq_bl_spi_xfer(dc, val);
}
static inline SR_PRIV 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_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 SR_PRIV int sq_app_get_capgenmix_result(struct dev_context *dc, uint32_t *trig_instant) {
return sq_app_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);
/* higher-level stuff used by api.c */
SR_PRIV int scanaquad_init(struct dev_context *devc);
SR_PRIV int scanaquad_receive_data(int fd, int revents, void *cb_data);
#endif