diff --git a/Makefile.am b/Makefile.am index 3b68142f2..c5d4e7da4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -777,6 +777,12 @@ src_libdrivers_la_SOURCES += \ src/hardware/zketech-ebd-usb/protocol.c \ src/hardware/zketech-ebd-usb/api.c endif +if HW_LABNATION_SMARTSCOPE +src_libdrivers_la_SOURCES += \ + src/hardware/labnation-smartscope/protocol.h \ + src/hardware/labnation-smartscope/protocol.c \ + src/hardware/labnation-smartscope/api.c +endif libsigrok_la_LIBADD = src/libdrivers.lo $(SR_EXTRA_LIBS) $(LIBSIGROK_LIBS) libsigrok_la_LDFLAGS = -version-info $(SR_LIB_VERSION) -no-undefined diff --git a/configure.ac b/configure.ac index 194559ed9..20c3734ec 100644 --- a/configure.ac +++ b/configure.ac @@ -396,6 +396,7 @@ SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [serial_comm]) SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm]) SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb]) SR_DRIVER([ZKETECH EBD-USB], [zketech-ebd-usb], [serial_comm]) +SR_DRIVER([LabNation SmartScope], [labnation-smartscope]) ############################### ## Language bindings setup ## diff --git a/src/hardware/labnation-smartscope/api.c b/src/hardware/labnation-smartscope/api.c new file mode 100644 index 000000000..7581aa522 --- /dev/null +++ b/src/hardware/labnation-smartscope/api.c @@ -0,0 +1,518 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2017 Karl Palsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "protocol.h" + +#define POLL_INTERVAL_MS 1 // 2048k bytes per second +// ./configure --enable-all-drivers=no --enable-demo --enable-labnation-smartscope --enable-fx2lafw + +/* scan options */ +static const uint32_t scanopts[] = { + //SR_CONF_NUM_LOGIC_CHANNELS, + //SR_CONF_NUM_ANALOG_CHANNELS, + SR_CONF_PROBE_NAMES +}; + +/* TODO - it can be much more, but one step at a time */ +static const uint32_t drvopts[] = { + SR_CONF_LOGIC_ANALYZER, +#if ENABLE_SCOPE + SR_CONF_OSCILLOSCOPE +#endif +}; + +static const uint32_t devopts[] = { + //SR_CONF_CONTINUOUS, + //SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_LIMIT_SAMPLES | SR_CONF_LIST | SR_CONF_GET | SR_CONF_SET, + SR_CONF_SAMPLERATE | SR_CONF_LIST | SR_CONF_GET | SR_CONF_SET, + SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, + SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, +}; + +static const int32_t trigger_matches[] = { + SR_TRIGGER_ZERO, + SR_TRIGGER_ONE, + SR_TRIGGER_RISING, + SR_TRIGGER_FALLING, + SR_TRIGGER_EDGE, +}; + +static const uint64_t samplerates[] = { + SR_MHZ(6.25), + SR_MHZ(12.5), + SR_MHZ(25), + SR_MHZ(50), + SR_MHZ(100) +}; + +static const char *channel_names_logic[] = { + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7" +}; + +#if ENABLE_SCOPE +static const char *channel_names_analog[] = { + "A", "B" +}; + +static const uint32_t devopts_cg_logic[] = { + +}; + +static const uint32_t devopts_cg_analog_group[] = { + +}; + +static const uint32_t devopts_cg_analog_channel[] = { + //SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET, + //SR_CONF_AMPLITUDE | SR_CONF_GET | SR_CONF_SET, + //SR_CONF_OFFSET | SR_CONF_GET | SR_CONF_SET, + +}; +#endif +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + struct drv_context *drvc; + struct dev_context *devc; + struct sr_dev_inst *sdi; + struct sr_channel *ch; + struct sr_channel_group *cg; + GSList *gs_list; + struct libusb_device_descriptor des; + libusb_device **devlist; + struct libusb_device_handle *hdl; + int ret, i, j; + char manufacturer[64], product[64], serial_num[64], connection_id[64]; + + (void)options; + + gs_list = NULL; + drvc = di->context; + drvc->instances = NULL; + + sr_dbg("--- Scanning for devices ---"); + + libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + + for (i = 0; devlist[i]; i++) { + libusb_get_device_descriptor(devlist[i], &des); + + if (des.idVendor != LNSS_VID && des.idProduct != LNSS_PID) { + continue; + } + + if ((ret = libusb_open(devlist[i], &hdl)) < 0) { + sr_warn("Failed to open potential device with " + "VID:PID %04x:%04x: %s.", des.idVendor, + des.idProduct, libusb_error_name(ret)); + continue; + } + + if (des.iManufacturer == 0) { + manufacturer[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iManufacturer, (unsigned char *) manufacturer, + sizeof(manufacturer))) < 0) { + sr_warn("Failed to get manufacturer string descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + if (des.iProduct == 0) { + product[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iProduct, (unsigned char *) product, + sizeof(product))) < 0) { + sr_warn("Failed to get product string descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + if (des.iSerialNumber == 0) { + serial_num[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iSerialNumber, (unsigned char *) serial_num, + sizeof(serial_num))) < 0) { + sr_warn("Failed to get serial number string descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)); + + libusb_close(hdl); + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INITIALIZING; + sdi->model = g_strdup(product); + sdi->vendor = g_strdup(manufacturer); + // TODO - print this to a string? Or query it another way? + //sdi->version = g_strdup(des.bcdDevice); + sdi->serial_num = g_strdup(serial_num); + sdi->connection_id = g_strdup(connection_id); + + sdi->status = SR_ST_INACTIVE; + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i]), NULL); + + /* TODO Store anything else in sdi->priv */ + devc = g_malloc0(sizeof(struct dev_context)); + memcpy(devc->hw_rev, sdi->serial_num + strlen(sdi->serial_num) - 3, 3); + devc->hw_rev[3] = 0; + devc->capture_ratio = DEFAULT_CAPTURE_RACIO; + devc->samplerate = DEFAULT_SAMPLERATE; + devc->limit_samples = DEFAULT_NUM_SAMPLES; + devc->acquisition_id = 255; + + sr_info("%s: Found device with sn: %s", __func__, sdi->serial_num); + + cg = sr_channel_group_new(sdi, "Logic", NULL); + for (j = 0; j < LNSS_NUM_CHANNELS; j++) { + ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE, channel_names_logic[j]); + cg->channels = g_slist_append(cg->channels, ch); + } +#if ENABLE_SCOPE + /* Group analog channels */ + cg = sr_channel_group_new(sdi, "Analog", NULL); + for (j = 0; j < LNSS_NUM_AN_CHANNELS; j++) { + ch = sr_channel_new(sdi, j + LNSS_NUM_CHANNELS, SR_CHANNEL_ANALOG, TRUE, channel_names_analog[j]); + cg->channels = g_slist_append(cg->channels, ch); + } +#endif + sdi->priv = devc; + + gs_list = g_slist_append(gs_list, sdi); + + // stop on first device + break; + } + + libusb_free_device_list(devlist, 1); + + return std_scan_complete(di, gs_list); +} + +static int dev_clear(const struct sr_dev_driver *di) +{ + return std_dev_clear(di); +} + +static int dev_open(struct sr_dev_inst *sdi) +{ + struct sr_dev_driver *di = sdi->driver; + struct drv_context *drvc = di->context; + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + int ret; + char version[9]; + + devc = sdi->priv; + usb = sdi->conn; + + sr_dbg("Karl - opening dev: hwrev: %s", devc->hw_rev); + + if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK) + return SR_ERR; + + if ((ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE)) < 0) { + sr_err("Failed to claim interface: %s.", + libusb_error_name(ret)); + return SR_ERR; + } + + bool ok = lnss_get_pic_firmware_version(sdi->conn, (uint8_t*)version); + if(ok){ + sr_dbg("PIC FW version: %s", version); + }else{ + sr_warn("Failed to get PIC FW version"); + } + + // check current fpga version, and potentially upload + ok = lnss_version_fpga(sdi->conn, version); + if (!ok) { + sr_dbg("fpga version was garbage, uploading based on hwrev"); + ok = lnss_load_fpga(sdi); + if (!ok) { + sr_err("Failed to load fpga on device!"); + return SR_ERR; + } + ok = lnss_version_fpga(sdi->conn, version); + if (!ok) { + sr_err("Failed to read back fpga version after load!"); + return SR_ERR; + } + sr_dbg("fpga version after load was: %s", version); + } else { + sr_dbg("fpga version sane: %s, no reason to upload", version); + } + + lnss_init(sdi); + + sdi->status = SR_ST_ACTIVE; + + return SR_OK; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + int res; + struct sr_usb_dev_inst *usb; + + usb = sdi->conn; + + sr_info("%s: Releasing SmartScope usb interface", __func__); + res = libusb_release_interface(usb->devhdl, 0); + + if(res){ + sr_err("libusb release error: %d", res); + //return SR_ERR; + }else{ + sr_err("Closing SmartScope usb device"); + libusb_close(usb->devhdl); + } + + sdi->status = SR_ST_INACTIVE; + + lnss_cleanup(sdi); + + 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 *devc; + int ret; + + (void)cg; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + ret = SR_OK; + switch (key) { + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(devc->samplerate); + break; + case SR_CONF_LIMIT_SAMPLES: + *data = g_variant_new_uint64(devc->limit_samples); + break; + case SR_CONF_CAPTURE_RATIO: + *data = g_variant_new_uint64(devc->capture_ratio); + break; + 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) +{ + struct dev_context *devc; + + (void)cg; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + switch (key) { + case SR_CONF_SAMPLERATE: + devc->samplerate = g_variant_get_uint64(data); + break; + case SR_CONF_LIMIT_SAMPLES: + devc->limit_samples = g_variant_get_uint64(data); + break; + case SR_CONF_CAPTURE_RATIO: + devc->capture_ratio = g_variant_get_uint64(data); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_list(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + struct dev_context *devc; + + devc = (sdi) ? sdi->priv : NULL; + + switch(key){ + case SR_CONF_SCAN_OPTIONS: + case SR_CONF_DEVICE_OPTIONS: + return std_opts_config_list(key, data, sdi, cg, + ARRAY_AND_SIZE(scanopts), + ARRAY_AND_SIZE(drvopts), + (devc) ? devopts : NULL, + (devc) ? ARRAY_SIZE(devopts) : 0); + case SR_CONF_SAMPLERATE: + *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates)); + break; + case SR_CONF_TRIGGER_MATCH: + *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); + break; + case SR_CONF_LIMIT_SAMPLES: + *data = std_gvar_tuple_u64(2000, 4000000); + break; + default: + return (sdi) ? SR_ERR_NA : SR_ERR_ARG; + } + + return SR_OK; +} + +static int dev_acquisition_start(const struct sr_dev_inst *sdi) +{ + const GSList *l; + struct dev_context *devc; + struct sr_trigger *trigger; + struct sr_trigger_stage *stage; + struct sr_trigger_match *match; + uint8_t trg_rising_mask = 0; + uint8_t trg_falling_mask = 0; + uint8_t trg_high_mask = 0; + uint8_t trg_low_mask = 0; + int sr_error; + + devc = sdi->priv; + devc->sent_samples = 0; + + /* Setup triggers */ + if ((trigger = sr_session_trigger_get(sdi->session))) { + if (g_slist_length(trigger->stages) > 1){ + return SR_ERR_NA; + } + + stage = g_slist_nth_data(trigger->stages, 0); + + if (!stage) + return SR_ERR_ARG; + + for (l = stage->matches; l; l = l->next) { + match = l->data; + + if (!match->match) + continue; + + if (!match->channel->enabled) + continue; + + int idx = match->channel->index; + + switch(match->match) { + case SR_TRIGGER_ZERO: trg_low_mask |= (1 << idx); break; + case SR_TRIGGER_ONE: trg_high_mask |= (1 << idx); break; + case SR_TRIGGER_RISING: trg_rising_mask |= (1 << idx); break; + case SR_TRIGGER_FALLING: trg_falling_mask |= (1 << idx); break; + case SR_TRIGGER_EDGE: + trg_rising_mask |= (1 << idx); + trg_falling_mask |= (1 << idx); + break; + default: + break; + } + } + } + +#if ENABLE_SCOPE + /* Prepare list of analog channels */ + g_slist_free(devc->enabled_analog_channels); + devc->enabled_analog_channels = NULL; + + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *ch = l->data; + if ((ch->type == SR_CHANNEL_ANALOG) && (ch->enabled)) { + devc->enabled_analog_channels = + g_slist_append(devc->enabled_analog_channels, ch); + } + } +#endif + + devc->acquisition_depth = lnss_acquisition_depth_set(sdi, devc->limit_samples); + + if((sr_error = lnss_subsamplerate_set(sdi, devc->samplerate)) != SR_OK) return sr_error; + + sr_error = lnss_triggers_set(sdi, trg_falling_mask, trg_rising_mask, trg_low_mask, trg_high_mask); + + if(sr_error){ + return sr_error; + } + + sr_session_source_add(sdi->session, -1, 0, POLL_INTERVAL_MS, + lnss_data_receive, + (void*)sdi); + + if((sr_error = lnss_aquisition_start(sdi)) != SR_OK) return sr_error; + + std_session_send_df_header(sdi); + + return SR_OK; +} + +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ + sr_session_source_remove(sdi->session, -1); + + std_session_send_df_end(sdi); + + return SR_OK; +} + +static int init (struct sr_dev_driver *di, struct sr_context *sr_ctx) +{ + return std_init(di, sr_ctx);; +} + +static int cleanup (const struct sr_dev_driver *di) +{ + return std_cleanup(di);; +} + +static struct sr_dev_driver labnation_smartscope_driver_info = { + .name = "labnation-smartscope", + .longname = "LabNation SmartScope", + .api_version = 1, + .init = init, + .cleanup = 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(labnation_smartscope_driver_info); diff --git a/src/hardware/labnation-smartscope/protocol.c b/src/hardware/labnation-smartscope/protocol.c new file mode 100644 index 000000000..3f8e36782 --- /dev/null +++ b/src/hardware/labnation-smartscope/protocol.c @@ -0,0 +1,1403 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2017 Karl Palsson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include "protocol.h" + + +#define EP_CMD_IN 0x83 +#define EP_CMD_OUT 0x2 +#define EP_DATA 0x81 + +#define HEADER_CMD_BYTE 0xC0 // C0 as in Command +#define HEADER_RESPONSE_BYTE 0xAD // AD as in Answer Dude + +#define COMMAND_WRITE_EP_SIZE 32 +#define COMMAND_READ_EP_SIZE 16 + +#define USB_TIMEOUT_DATA 500 + +#define DBG_INFO(fmt, ...) g_fprintf(stdout, fmt"\n", ##__VA_ARGS__) + +int g_fprintf(FILE *fptr, const char *str, ...); +static int adc_reg_get(struct sr_usb_dev_inst *usb, enum ADC reg, uint8_t *value); +static int controller_register_get (struct sr_usb_dev_inst *usb, enum controller ctrl, + uint32_t address, uint32_t length, + uint8_t *data); + +static void dump_regs(const struct sr_dev_inst *sdi) +{ +#if 1 + uint8_t regs[39], i; + uint8_t rom[8]; + uint8_t adc[10]; + + if(sr_log_loglevel_get() < SR_LOG_SPEW){ + return; + } + + if(controller_register_get(sdi->conn, CTRL_FPGA, FPGA_I2C_ADDRESS_ROM << 8, 8, rom)){ + return; + } + + for(i = 0; i < 10; i ++){ + if(adc_reg_get(sdi->conn, i, &adc[i])){ + return; + } + } + + /** + * Reading all regs at once fails reading control, however received data seams ok. + * + * It seams related with size 16. Length is limited to 12 that when adding 4 bytes of header + * totals 16. For now split into multiple transfers to avoid errors. + * */ + if(controller_register_get(sdi->conn, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8), 12, regs)) return; + if(controller_register_get(sdi->conn, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + 12, 12, ®s[12])) return; + if(controller_register_get(sdi->conn, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + 24, 12, ®s[24])) return; + if(controller_register_get(sdi->conn, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + 36, 2, ®s[36])) return; + + DBG_INFO("------------------------------------------------------------"); + DBG_INFO(" ROM 0xD00 ADC"); + DBG_INFO("FW_GIT0: 0x%02X POWER_MANAGMENT: 0x%02X", rom[0], adc[0]); + DBG_INFO("FW_GIT1: 0x%02X OUTPUT_FORMAT: 0x%02X", rom[1], adc[1]); + DBG_INFO("FW_GIT2: 0x%02X OUTPUT_PWR_MNGMNT: 0x%02X", rom[2], adc[2]); + DBG_INFO("FW_GIT3: 0x%02X DATA_CLK_TIMING: 0x%02X", rom[3], adc[3]); + DBG_INFO("SPI_RECEIVED_VALUE: 0x%02X CHA_TERMINATION: 0x%02X", rom[4], adc[4]); + DBG_INFO("STROBES0: 0x%02X CHB_TERMINATION: 0x%02X", rom[5], adc[5]); + DBG_INFO("STROBES1: 0x%02X FORMAT_PATTERN: 0x%02X", rom[6], adc[6]); + DBG_INFO("STROBES2: 0x%02X COMMON_MODE: 0x%02X", rom[7], adc[7]); + DBG_INFO(" SOFT_RESET: 0x%02X", adc[9]); + DBG_INFO("------------------------------------------------------------"); + DBG_INFO(" REG 0xC00 Strobe"); + DBG_INFO("STROBE_UPDATE: 0x%02X GLOBAL_RESET: %d", regs[0], !!(rom[5] & (1 << 0))); + DBG_INFO("SPI_ADDRESS: 0x%02X INIT_SPI_TRANSFER: %d", regs[1], !!(rom[5] & (1 << 1))); + DBG_INFO("SPI_WRITE_VALUE: 0x%02X GENERATOR_TO_AWG: %d", regs[2], !!(rom[5] & (1 << 2))); + DBG_INFO("DIVIDER_MULTIPLIER: 0x%02X LA_ENABLE: %d", regs[3], !!(rom[5] & (1 << 3))); + DBG_INFO("CHA_YOFFSET_VOLTAGE: 0x%02X SCOPE_ENABLE: %d", regs[4], !!(rom[5] & (1 << 4))); + DBG_INFO("CHB_YOFFSET_VOLTAGE: 0x%02X SCOPE_UPDATE: %d", regs[5], !!(rom[5] & (1 << 5))); + DBG_INFO("TRIGGER_PWM: 0x%02X FORCE_TRIGGER: %d", regs[6], !!(rom[5] & (1 << 6))); + DBG_INFO("TRIGGER_LEVEL: 0x%02X VIEW_UPDATE: %d", regs[7], !!(rom[5] & (1 << 7))); + DBG_INFO("TRIGGER_MODE: 0x%02X VIEW_SEND_OVERVIEW: %d", regs[8], !!(rom[6] & (1 << 0))); + DBG_INFO("TRIGGER_PW_MIN_B0: 0x%02X VIEW_SEND_PARTIAL: %d", regs[9], !!(rom[6] & (1 << 1))); + DBG_INFO("TRIGGER_PW_MIN_B1: 0x%02X ACQ_START: %d", regs[10], !!(rom[6] & (1 << 2))); + DBG_INFO("TRIGGER_PW_MIN_B2: 0x%02X ACQ_STOP: %d", regs[11], !!(rom[6] & (1 << 3))); + DBG_INFO("TRIGGER_PW_MAX_B0: 0x%02X CHA_DCCOUPLING: %d", regs[12], !!(rom[6] & (1 << 4))); + DBG_INFO("TRIGGER_PW_MAX_B1: 0x%02X CHB_DCCOUPLING: %d", regs[13], !!(rom[6] & (1 << 5))); + DBG_INFO("TRIGGER_PW_MAX_B2: 0x%02X ENABLE_ADC: %d", regs[14], !!(rom[6] & (1 << 6))); + DBG_INFO("INPUT_DECIMATION: 0x%02X ENABLE_NEG: %d", regs[15], !!(rom[6] & (1 << 7))); + DBG_INFO("ACQUISITION_DEPTH: 0x%02X ENABLE_RAM: %d", regs[16], !!(rom[7] & (1 << 0))); + DBG_INFO("TRIGGERHOLDOFF_B0: 0x%02X DOUT_3V_5V: %d", regs[17], !!(rom[7] & (1 << 1))); + DBG_INFO("TRIGGERHOLDOFF_B1: 0x%02X EN_OPAMP_B: %d", regs[18], !!(rom[7] & (1 << 2))); + DBG_INFO("TRIGGERHOLDOFF_B2: 0x%02X GENERATOR_TO_DIGITAL:%d", regs[19], !!(rom[7] & (1 << 3))); + DBG_INFO("TRIGGERHOLDOFF_B3: 0x%02X ROLL: %d", regs[20], !!(rom[7] & (1 << 4))); + DBG_INFO("VIEW_DECIMATION: 0x%02X LA_CHANNEL: %d", regs[21], !!(rom[7] & (1 << 5))); + DBG_INFO("VIEW_OFFSET_B0: 0x%02X", regs[22]); + DBG_INFO("VIEW_OFFSET_B1: 0x%02X", regs[23]); + DBG_INFO("VIEW_OFFSET_B2: 0x%02X", regs[24]); + DBG_INFO("VIEW_ACQUISITIONS: 0x%02X", regs[25]); + DBG_INFO("VIEW_BURSTS: 0x%02X", regs[26]); + DBG_INFO("VIEW_EXCESS_B0: 0x%02X", regs[27]); + DBG_INFO("VIEW_EXCESS_B1: 0x%02X", regs[28]); + DBG_INFO("DIGITAL_TRIGGER_RISING: 0x%02X", regs[29]); + DBG_INFO("DIGITAL_TRIGGER_FALLING: 0x%02X", regs[30]); + DBG_INFO("DIGITAL_TRIGGER_HIGH: 0x%02X", regs[31]); + DBG_INFO("DIGITAL_TRIGGER_LOW: 0x%02X", regs[32]); + DBG_INFO("DIGITAL_OUT: 0x%02X", regs[33]); + DBG_INFO("GENERATOR_DECIMATION_B0: 0x%02X", regs[34]); + DBG_INFO("GENERATOR_DECIMATION_B1: 0x%02X", regs[35]); + DBG_INFO("GENERATOR_DECIMATION_B2: 0x%02X", regs[36]); + DBG_INFO("GENERATOR_SAMPLES_B0: 0x%02X", regs[37]); + DBG_INFO("GENERATOR_SAMPLES_B1: 0x%02X", regs[38]); + DBG_INFO("------------------------------------------------------------"); +#endif +} + +#if 0 +static void dump_buf(void *buf, uint32_t len) +{ + g_fprintf(stdout, "["); + + if(buf == NULL) + len = 0; + + for(uint32_t i = 0; i < len; i++){ + uint8_t byte = ((uint8_t*)buf)[i]; + g_fprintf(stdout, "%02X%s", byte, (idevhdl, EP_CMD_OUT, wbuf, sizeof(wbuf), &len, USB_TIMEOUT_DATA))) { + sr_err("Failed to transfer wbuf: %s", libusb_error_name(ret)); + return SR_ERR_IO; + } + + if ((ret = libusb_bulk_transfer(usb->devhdl, EP_CMD_OUT, rbuf, sizeof(rbuf), &len, USB_TIMEOUT_DATA))) { + sr_err("Failed to transfer rbuf: %s", libusb_error_name(ret)); + return SR_ERR_IO; + } + if ((ret = libusb_bulk_transfer(usb->devhdl, EP_CMD_IN, inp, sizeof(inp), &len, USB_TIMEOUT_DATA))) { + sr_err("Failed to read i2c response: %s", libusb_error_name(ret)); + return SR_ERR_IO; + } + + //dump_buf(inp, 8); + + if(inp[0] != HEADER_RESPONSE_BYTE){ + sr_err("Response header mismatch"); + } + + *value = inp[4]; + + return SR_OK; +} + +/** + * @brief Write to command endpoint + */ +static int write_control_bytes_bulk(struct sr_usb_dev_inst *usb, uint32_t length, uint8_t *msg) +{ + int ret; + int n; + + ret = libusb_bulk_transfer(usb->devhdl, EP_CMD_OUT, msg, length, &n, USB_TIMEOUT_DATA); + + if (ret) { + sr_err("%s(): Failed write control bytes: %s", __func__, libusb_error_name(ret)); + return SR_ERR_IO; + } + + if(n != (int)length){ + sr_warn("%s(): Only wrote %d out of %d bytes", __func__, n, length); + return SR_ERR_DATA; + } + + return SR_OK; +} + +/** + * @brief Read from command endpoint + */ +static int read_control_bytes(struct sr_usb_dev_inst *usb, uint32_t length, uint8_t *msg) +{ + int ret; + int n; + + ret = libusb_bulk_transfer(usb->devhdl, EP_CMD_IN, msg, length, &n, USB_TIMEOUT_DATA); + + if (ret) { + sr_err("%s(): Failed read control bytes: %s", __func__, libusb_error_name(ret)); + return SR_ERR_IO; + } + + if(n != (int)length){ + sr_warn("%s(): Only read %d out of %d bytes", __func__, n, length); + return SR_ERR_DATA; + } + + return SR_OK; +} + +/** + * @brief Read from data endpoint + */ +static int read_data_bytes(struct sr_usb_dev_inst *usb, uint32_t length, uint8_t *buffer) +{ + int count; + + if (!usb) + return SR_ERR_ARG; + + int ret = libusb_bulk_transfer(usb->devhdl, EP_DATA, buffer, length, &count, USB_TIMEOUT_DATA); + + if(ret){ + sr_err("%s(): Failed read data: %s", __func__, libusb_error_name(ret)); + return SR_ERR_IO; + } + + if(count != (int)length){ + sr_warn("%s(): Only read %d out of %d bytes", __func__, count, length); + } + + return count; +} + +static int usb_send_command(struct sr_usb_dev_inst *usb, enum pic_cmd cmd) +{ + uint8_t to_send[2] = {HEADER_CMD_BYTE, (uint8_t)cmd}; + return write_control_bytes_bulk(usb, 2, to_send); +} + +/** + * @brief Forms a lnss command header packet + * containing a message to pic controller. + */ +static int usb_command_header_i2c (uint8_t I2cAddress, enum controller_op op, + uint32_t address, uint32_t length, + uint8_t *buffer) +{ + // Most common, will be overridden if necessary + buffer[0] = HEADER_CMD_BYTE; + + switch (op) + { + case WRITE: + buffer[1] = PICCMD_I2C_WRITE; + buffer[2] = (uint8_t) (length + 2); + buffer[3] = I2cAddress << 1; + buffer[4] = address; + return 5; + case READ: + buffer[1] = PICCMD_I2C_READ; + buffer[2] = I2cAddress; + buffer[3] = length; + return 4; + default: + sr_warn ("%s(): Unsupported operation for I2C Header", __func__); + } + + return -1; +} + +/** + * @brief Forms a lnss command header packet + * with message for a controller + */ +static int usb_command_header (enum controller ctrl, enum controller_op op, + uint32_t address, uint32_t length, + uint8_t *buffer) +{ + // Most common, will be overridden if necessary + buffer[0] = HEADER_CMD_BYTE; + buffer[2] = (uint8_t) (address); + buffer[3] = (uint8_t) (length); + // Set operation + switch (ctrl) + { + case CTRL_PIC: + buffer[1] = op == WRITE ? PICCMD_PIC_WRITE : PICCMD_PIC_READ; + return 4; + case CTRL_ROM: + buffer[1] = op == WRITE ? PICCMD_EEPROM_WRITE : PICCMD_EEPROM_READ; + return 4; + case CTRL_FLASH: + buffer[1] = + op == WRITE ? PICCMD_FLASH_ROM_WRITE : PICCMD_FLASH_ROM_READ; + buffer[4] = (uint8_t) (address >> 8); + return 5; + case CTRL_FPGA: + return usb_command_header_i2c ((uint8_t) ((address >> 8) & 0x7F), op, + (uint8_t) (address & 0xFF), length, + buffer); + case CTRL_AWG: + switch (op) + { + case WRITE: + return usb_command_header_i2c (FPGA_I2C_ADDRESS_AWG, op, address, + length, buffer); + case WRITE_BEGIN: + buffer[1] = PICCMD_I2C_WRITE_START; + buffer[2] = length + 2; + buffer[3] = FPGA_I2C_ADDRESS_AWG << 1; + buffer[4] = address; + return 5; + case WRITE_BODY: + buffer[1] = PICCMD_I2C_WRITE_BULK; + buffer[2] = (uint8_t) length; + return 3; + case WRITE_END: + buffer[1] = PICCMD_I2C_WRITE_STOP; + buffer[2] = (uint8_t) length; + return 3; + case READ: + sr_warn ("%s(): Can't read out AWG", __func__); + } + } + + return -1; +} + +/** + * @brief Writes to a controller regiter through libusb + */ +static int controller_register_set (struct sr_usb_dev_inst *usb, + enum controller ctrl, + uint32_t address, uint32_t length, + uint8_t *data) +{ + uint8_t msg[32]; + int msgLen = 0; + int sr_error = SR_OK; + + // dump_buf(data, length); + + if (data != NULL && length > I2C_MAX_WRITE_LENGTH) + { + uint32_t offset = 0; + int bytesLeft = length; + + if (ctrl != CTRL_AWG) + { + // Chop up in smaller chunks + while (bytesLeft > 0) + { + int length = bytesLeft > I2C_MAX_WRITE_LENGTH ? + I2C_MAX_WRITE_LENGTH : + bytesLeft; + sr_error = controller_register_set (usb, ctrl, address + offset, + length, &data[offset]); + + if (sr_error) + { + goto error; + } + + offset += length; + bytesLeft -= length; + } + return SR_OK; + } + + msgLen = usb_command_header (ctrl, WRITE_BEGIN, address, 0, msg); + sr_error = write_control_bytes_bulk (usb, msgLen, msg); + + if (sr_error) + { + goto error; + } + + while (bytesLeft > 0) + { + int length = bytesLeft > I2C_MAX_WRITE_LENGTH_BULK ? + I2C_MAX_WRITE_LENGTH_BULK : + bytesLeft; + msgLen = usb_command_header (ctrl, WRITE_BODY, address, length, msg); + + memcpy (&msg[msgLen], &data[offset], length); + sr_error = write_control_bytes_bulk (usb, 32, msg); + if (sr_error) + { + goto error; + } + offset += length; + bytesLeft -= length; + } + msgLen = usb_command_header (ctrl, WRITE_END, address, 0, msg); + sr_error = write_control_bytes_bulk (usb, msgLen, msg); + if (sr_error) + { + goto error; + } + } + else + { + msgLen = usb_command_header (ctrl, WRITE, address, length, msg); + if (length > 0) + memcpy (&msg[msgLen], data, length); + sr_error = write_control_bytes_bulk (usb, msgLen + length, msg); + if (sr_error) + { + goto error; + } + } + +error: + return sr_error; +} + + +/** + * @brief Reads a controller regiter through libusb + */ +static int controller_register_get (struct sr_usb_dev_inst *usb, + enum controller ctrl, + uint32_t address, uint32_t length, + uint8_t *data) +{ + uint8_t msg[PACKAGE_MAX]; + int sr_error; + + if (ctrl == CTRL_FPGA) + { + if ((sr_error = controller_register_set (usb, ctrl, address, 0, NULL)) != + SR_OK) + goto error; + } + else if (ctrl == CTRL_FLASH && + (address + length) > (FLASH_USER_ADDRESS_MASK + 1)) + { + sr_err ("%s(): Can't read flash rom beyond 0x%08X", __func__, + FLASH_USER_ADDRESS_MASK); + return SR_ERR_ARG; + } + + int len = usb_command_header (ctrl, READ, address, length, msg); + if ((sr_error = write_control_bytes_bulk (usb, len, msg)) != SR_OK) + goto error; + + // EP3 always contains 16 bytes xxx should be linked to constant + // FIXME: use endpoint length or so, or don't pass the argument to the + // function + if ((sr_error = read_control_bytes (usb, 16, msg)) != SR_OK) + goto error; + + memcpy (data, &msg[ctrl == CTRL_FLASH ? 5 : 4], length); + +error: + return sr_error; +} + +/** + * @brief Write to a FPGA register + */ +static int reg_set(struct sr_usb_dev_inst *usb, enum REG reg, uint8_t value) +{ + return controller_register_set(usb, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + reg, 1, &value); +} +#if 0 +/** + * @brief Read FPGA register + * There are 39 registers, see enum REG for register names + */ +static int reg_get(struct sr_usb_dev_inst *usb, enum REG reg, uint8_t *value) +{ + return controller_register_get(usb, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + reg, 1, value); +} +#endif +/** + * @brief Write FPGA strobe bit. + * Strobe bit is written to FPGA STROBE_UPDATE register + * + * + * STROBE_REGISTER + * 7 1 0 + * | Strobe Index | State | + * + * Index: 0-31 + * State: 0|1 + */ +static int strobe_set(struct sr_usb_dev_inst *usb, enum STR strobe, uint8_t state) +{ + uint8_t value; + + value = (strobe << 1) | (state & 1); + return controller_register_set(usb, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + STROBE_UPDATE, 1, &value); +} +#if 0 +/** + * @brief Read FPGA strobe bit. + * Strobe bit is read from FPGA ROM starting from STROBE0 register + * + * There are 21 strobe bits starting from STROBE0[0] to STROBE2[1] + */ +static int strobe_get(struct sr_usb_dev_inst *usb, enum STR strobe, uint8_t *state) +{ + uint8_t addr, value; + int res; + + addr = (uint8_t)STROBES0 + (uint8_t)strobe / 8; + + res = controller_register_get(usb, CTRL_FPGA, (FPGA_I2C_ADDRESS_ROM << 8) + addr, 1, &value); + + if(res != SR_OK){ + return res; + } + + *state = (value >> (addr % 8)) & 1; + + return SR_OK; +} +#endif +/** + * @brief Read ADC register + */ +static int adc_reg_get(struct sr_usb_dev_inst *usb, enum ADC reg, uint8_t *value) +{ + int res; + uint8_t reg_val = (uint8_t)reg | 0x80; + + res = controller_register_set(usb, CTRL_FPGA, ((FPGA_I2C_ADDRESS_REG << 8) + SPI_ADDRESS), 1, ®_val); + + if(res != SR_OK){ + return res; + } + + strobe_set(usb, INIT_SPI_TRANSFER, 0); + strobe_set(usb, INIT_SPI_TRANSFER, 1); + + res = controller_register_get(usb, CTRL_FPGA, (FPGA_I2C_ADDRESS_ROM << 8) + SPI_RECEIVED_VALUE, 1, value); + + return (res == SR_OK) ? SR_OK : res; +} + +/** + * @brief Write to ADC register + */ +static int adc_reg_set(struct sr_usb_dev_inst *usb, enum ADC reg, uint8_t value) +{ + int res; + + res = controller_register_set(usb, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + SPI_ADDRESS, 1, (uint8_t*)®); + + if(res != SR_OK){ + return res; + } + + res = controller_register_set(usb, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + SPI_WRITE_VALUE, 1, &value); + + if(res != SR_OK){ + return res; + } + + strobe_set(usb, INIT_SPI_TRANSFER, 0); + strobe_set(usb, INIT_SPI_TRANSFER, 1); + + return SR_OK; +} + +/** + * @brief Retrieves usb package + * usb packages have a maximum size of 64 + (2 * 2048) bytes + * + * 64 bytes for header + * 2048 bytes per channel + */ +static int acquisition_get(struct sr_usb_dev_inst *usb, + uint8_t *buffer, uint32_t length) +{ + struct header *hdr; + int tries; + + tries = 0; + + while(true) { + if(read_data_bytes(usb, SZ_HDR, buffer) <= 0){ + return 0; + } + + hdr = (struct header*)buffer; + + if(hdr->magic[0] == 'L' && hdr->magic[1] == 'N') + break; + + if(tries++ > PACKAGE_MAX){ + sr_err("%s(): Invalid header magic 0x%02X%02X at fetch %d", __func__, hdr->magic[0], hdr->magic[1], tries); + return 0; + } + } + + if(tries > 0) + sr_warn("%s(): Had to try %d times before a good header came through", __func__, tries + 1); + + sr_dbg("%s(): Pkt hdr: 0x%02x %s%s%s%s%s%s%s%s", __func__, hdr->flags, + hdr->flags & IsFullAcqusition ? "|Full Acquisition" : "", + hdr->flags & Armded ? "|Armed" : "", + hdr->flags & AwaitingTrigger ? "|AwaitingTrigger" : "", + hdr->flags & TimedOut ? "|TimedOut" : "", + hdr->flags & Rolling ? "|Rolling" : "", + hdr->flags & IsLastAcquisition ? "|LastAcquisition" : "", + hdr->flags & IsOverview ? "|Overview" : "", + hdr->flags & Acquiring ? "|Acquiring" : ""); + + if (hdr->flags & TimedOut){ + //sr_dbg("%s(): TimedOut, Header only (0x%02X)", __func__, hdr->flags); + return SZ_HDR; + } + + if (hdr->flags & IsOverview) { + //sr_dbg("%s(): Overview packet", __func__); + read_data_bytes(usb, SZ_OVERVIEW, buffer + SZ_HDR); + return SZ_HDR + SZ_OVERVIEW; + } + + if (hdr->n_bursts == 0){ + sr_err("%s(): number of bursts in this USB packet is 0, cannot fetch", __func__); + return 0; + } + + uint32_t size = hdr->n_bursts * hdr->bytes_per_burst; + + if (size + SZ_HDR > length) { + sr_err("%s(): Length of packet (%u) is bigger than buffer (%u)" + " (N_burst: %d, bytes per burst: %d) expect failure", + __func__, + size + SZ_HDR, length, + hdr->n_bursts, hdr->bytes_per_burst); + void* dummy = malloc(size); + read_data_bytes(usb, size, (uint8_t*)dummy); + free(dummy); + return 0; + } + + read_data_bytes(usb, size, buffer + SZ_HDR); + //sr_dbg("%s(): Data packet", __func__); + + return SZ_HDR + size; +} + +/** + * @brief + * + */ +static int adc_ramp_verify(uint8_t *data, uint32_t len) +{ + uint32_t failing_indexes = 0; + + if (len < 4){ + return 0; + } + + // Check samples from both channels + for(uint32_t i = 2; i < len; i++){ + if((uint8_t)(data[i - 2] + 1) != data[i]){ + failing_indexes++; + } + } + + return failing_indexes == 0; +} + +static int adc_ramp_test(struct sr_usb_dev_inst *usb) +{ + int sr_error = SR_ERR_TIMEOUT; + int tries, size; + struct header *hdr; + uint8_t *packet = g_try_malloc0(SZ_HDR + SZ_OVERVIEW); + + if(!packet){ + return SR_ERR_MALLOC; + } + + hdr = (struct header*)packet; + tries = 10; + + do{ + if((size = acquisition_get(usb, packet, SZ_HDR + SZ_OVERVIEW)) == SZ_HDR){ + strobe_set(usb, ACQ_START, 1); + strobe_set(usb, FORCE_TRIGGER, 1); + continue; + } + + if(!(hdr->flags & IsFullAcqusition)){ + continue; + } + + size -= SZ_HDR; + + sr_dbg("%s(): Checking %d samples", __func__, size); + + if(adc_ramp_verify(packet + SZ_HDR, size)){ + sr_error = SR_OK; + goto adc_ramp_end; + } + + }while(--tries); + + sr_err("%s(): Failed to get ADC calibration data", __func__); + +adc_ramp_end: + + if(packet) + free(packet); + + return sr_error; +} + +/** + * @brief Configures MAX19506 ADC registers + */ +static void adc_configure(struct sr_usb_dev_inst *usb) +{ + adc_reg_set(usb, SOFT_RESET, 90); + adc_reg_set(usb, POWER_MANAGMENT, 4); // CHA/B Standby + adc_reg_set(usb, OUTPUT_PWR_MNGMNT, 0); // Clock active + adc_reg_set(usb, FORMAT_PATTERN, 16); // Offset binary + adc_reg_set(usb, DATA_CLK_TIMING, 24); // + adc_reg_set(usb, CHA_TERMINATION, 0); // 50 Ohm + adc_reg_set(usb, POWER_MANAGMENT, 3); // CHA/B Active + adc_reg_set(usb, OUTPUT_FORMAT, 2); // Multiplexed data +} + +/** + * @brief Performs ADC calibration + */ +static int adc_calibrate(struct sr_usb_dev_inst *usb) +{ + adc_configure(usb); + + adc_reg_set(usb, FORMAT_PATTERN, 80); // Enable test data + + /* AcquisitionDepth */ + reg_set(usb, ACQUISITION_DEPTH, 1); + + /* SetViewPort */ + reg_set(usb, VIEW_DECIMATION, 1); + reg_set(usb, VIEW_BURSTS, 6); + + /* AcquisitionMode */ + reg_set(usb, TRIGGER_MODE, TRG_ACQ_SINGLE); + + /* SetTriggerByte */ + reg_set(usb, TRIGGER_LEVEL, 127); + + /* SendOverViewBuffer */ + strobe_set(usb, VIEW_SEND_OVERVIEW, 0); + + /* PreferPartial */ + strobe_set(usb, VIEW_SEND_PARTIAL, 0); + + /* Disable LA */ + strobe_set(usb, LA_CHANNEL, 0); + strobe_set(usb, LA_ENABLE, 0); + + // Apply register values + strobe_set(usb, SCOPE_UPDATE, 1); + + /* Start acquiring */ + strobe_set(usb, ACQ_START, 1); + + sr_info("Calibrating ADC timing..."); + + if(adc_ramp_test(usb) == SR_OK){ + sr_info("ADC calibration ok."); + strobe_set(usb, FORCE_TRIGGER, 1); + lnss_flush_data_pipe(usb); + + /* Reconfigure ADC with new timing */ + adc_configure(usb); + + strobe_set(usb, ACQ_STOP, 1); + return SR_OK; + } + + sr_warn("ADC calibration failed!"); + + return SR_ERR_TIMEOUT; +} + +/** + * @brief Send acquired data to session + * + */ +static void send_logic_data(struct sr_dev_inst *sdi, void *samples, uint32_t len) +{ + struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_logic logic; + int trigger_offset; + + devc = sdi->priv; + + logic.unitsize = sizeof(uint8_t); + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + + sr_dbg("%s(): Sending %d samples of %d, remaining: %d", __func__, + (int)len, + (int)devc->acquisition_depth, + (int)(devc->acquisition_depth - devc->sent_samples - len)); + + if(devc->trigger_enabled && !devc->trigger_sent){ + /** + * trigger_offset: + * + * > len, trigger is not on this packet + * < 0, trigger is on this packet + * = 0, is on first sample of next packet + */ + trigger_offset = (int)devc->pre_trigger_samples - (int)(devc->sent_samples + len); + + if(trigger_offset != 0){ + if(trigger_offset < 0){ + devc->trigger_sent = true; + trigger_offset = len - abs(trigger_offset); + + if(trigger_offset == 0){ + // trigger on first sample + std_session_send_df_trigger(sdi); + }else{ + trigger_offset -= devc->trigger_delay; + // send pre-trigger samples + logic.length = trigger_offset; + logic.data = samples; + sr_session_send(sdi, &packet); + sr_dbg("%s(): Send trigger set to offset %lu, with delay %u", + __func__, + devc->sent_samples + trigger_offset, + devc->trigger_delay); + // send trigger marker and pos-trigger samples + std_session_send_df_trigger(sdi); + logic.length = len - trigger_offset; + logic.data = samples + trigger_offset; + sr_session_send(sdi, &packet); + return; + } + } + } + }else if(!devc->trigger_sent){ + // no triggers configured, send trigger mark on first packet + std_session_send_df_trigger(sdi); + devc->trigger_sent = true; + } + + logic.length = len; + logic.data = samples; + sr_session_send(sdi, &packet); +} + +#if 0 +/** + * @brief Send analog data (oscilloscope data) + * + */ +static int send_analog_data(struct sr_dev_inst *sdi, + uint64_t samples_todo, uint64_t *samples_done, + void *samples) +{ + (void)sdi; + (void)samples_todo; + (void)samples_done; + (void)samples; + /* + struct sr_datafeed_analog analog; + struct sr_analog_encoding encoding; + struct sr_analog_meaning meaning; + struct sr_analog_spec spec; + + sr_analog_init(&analog, &encoding, &meaning, &spec, 2); + analog.meaning->channels = devc->enabled_analog_channels; + analog.meaning->mq = SR_MQ_VOLTAGE; + analog.meaning->unit = SR_UNIT_VOLT; + analog.meaning->mqflags = SR_MQFLAG_DC; + analog.num_samples = samples_total; + analog.data = analog_buffer; + + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + + //sr_session_send(sdi, &packet); + + sr_dev_acquisition_stop(sdi); +*/ + return 0; +} +#endif + +/** + * SmartScope class public functions + */ +SR_PRIV int lnss_init(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + struct sr_usb_dev_inst *usb; + + sr_dbg("%s()", __func__); + + usb = sdi->conn; + + /* Enable essentials */ + strobe_set(usb, GLOBAL_RESET, 1); + + /* DigitalOutput */ + reg_set(usb, DIGITAL_OUT, 0); + + /* Channels offset */ + reg_set(usb, DIVIDER_MULTIPLIER, 153); // Magic?? + reg_set(usb, CHA_YOFFSET_VOLTAGE, 114); + reg_set(usb, CHB_YOFFSET_VOLTAGE, 114); + + /*Trigger */ + reg_set(usb, TRIGGER_PWM, 0); + reg_set(usb, TRIGGER_LEVEL, 125); + reg_set(usb, TRIGGER_MODE, 0); + reg_set(usb, TRIGGER_PW_MIN_B0, 0); + reg_set(usb, TRIGGER_PW_MIN_B1, 0); + reg_set(usb, TRIGGER_PW_MIN_B2, 0); + reg_set(usb, TRIGGER_PW_MAX_B0, 255); + reg_set(usb, TRIGGER_PW_MAX_B1, 255); + reg_set(usb, TRIGGER_PW_MAX_B2, 255); + reg_set(usb, TRIGGERHOLDOFF_B0, 0); + reg_set(usb, TRIGGERHOLDOFF_B1, 0); + reg_set(usb, TRIGGERHOLDOFF_B2, 0); + reg_set(usb, TRIGGERHOLDOFF_B3, 0); + reg_set(usb, DIGITAL_TRIGGER_RISING, 0); + reg_set(usb, DIGITAL_TRIGGER_FALLING, 0); + reg_set(usb, DIGITAL_TRIGGER_LOW, 0); + reg_set(usb, DIGITAL_TRIGGER_HIGH, 0); + + /* Misc */ + reg_set(usb, INPUT_DECIMATION, 0); + reg_set(usb, ACQUISITION_DEPTH, 0); + + /* GeneratorStretching */ + reg_set(usb, GENERATOR_DECIMATION_B0, 0); + reg_set(usb, GENERATOR_DECIMATION_B1, 0); + reg_set(usb, GENERATOR_DECIMATION_B2, 0); + + /* SetViewPort */ + reg_set(usb, VIEW_DECIMATION, 0); + reg_set(usb, VIEW_BURSTS, 6); + reg_set(usb, VIEW_OFFSET_B0, 0); + reg_set(usb, VIEW_OFFSET_B1, 0); + reg_set(usb, VIEW_OFFSET_B2, 0); + reg_set(usb, VIEW_ACQUISITIONS, 0); + reg_set(usb, VIEW_EXCESS_B0, 0); + reg_set(usb, VIEW_EXCESS_B1, 0); + + /* GeneratorNumberOfSamples */ + reg_set(usb, GENERATOR_SAMPLES_B0, 0xFF); // 0x800 - 1 + reg_set(usb, GENERATOR_SAMPLES_B1, 0x07); + + /* Strobes */ + strobe_set(usb, SCOPE_UPDATE, 1); // apply register values + strobe_set(usb, GENERATOR_TO_AWG, 0); + strobe_set(usb, LA_ENABLE, 0); + strobe_set(usb, SCOPE_ENABLE, 1); + strobe_set(usb, FORCE_TRIGGER, 0); + strobe_set(usb, VIEW_UPDATE, 0); + strobe_set(usb, VIEW_SEND_OVERVIEW, 0); + strobe_set(usb, VIEW_SEND_PARTIAL, 0); + strobe_set(usb, ACQ_START, 0); + strobe_set(usb, ACQ_STOP, 0); + strobe_set(usb, CHA_DCCOUPLING, 1); + strobe_set(usb, CHB_DCCOUPLING, 1); + strobe_set(usb, ENABLE_ADC, 1); + strobe_set(usb, ENABLE_NEG, 1); + strobe_set(usb, ENABLE_RAM, 1); + strobe_set(usb, DOUT_3V_5V, 0); + strobe_set(usb, EN_OPAMP_B, 0); + strobe_set(usb, GENERATOR_TO_DIGITAL, 0); + strobe_set(usb, ROLL, 0); + strobe_set(usb, LA_CHANNEL, 0); + + lnss_flush_data_pipe(usb); + + adc_calibrate(usb); + + /* SendOverViewBuffer */ + strobe_set(usb, VIEW_SEND_OVERVIEW, 0); + reg_set(usb, VIEW_DECIMATION, 0); + + /* Enable LA*/ + strobe_set(usb, LA_ENABLE, 1); + + /* Configure trigger for full acquisitions */ + reg_set(usb, TRIGGER_MODE, + TRG_ACQ_SINGLE | + TRG_EDGE_ANY | + TRG_SOURCE_CHANNEL | + TRG_CHANNEL_A | + TRG_MODE_DIGITAL + ); + + reg_set(usb, DIVIDER_MULTIPLIER, 0); + reg_set(usb, TRIGGER_LEVEL, 0x7E); // got this value after calibration from SmartScope App + + strobe_set(usb, SCOPE_UPDATE, 1); // Update registers + + dump_regs(sdi); + + // Packets obtained from device have a fixed size + devc->rx_buffer = g_try_malloc0(SZ_HDR + (LNSS_MIN_ACQUISITION * LNSS_NUM_AN_CHANNELS)); + + return (devc->rx_buffer) ? SR_OK : SR_ERR_MALLOC; +} + +SR_PRIV void lnss_cleanup(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + + sr_dbg("%s()", __func__); + + g_free(devc->rx_buffer); +} + +SR_PRIV void lnss_flush_data_pipe(struct sr_usb_dev_inst *usb) +{ + uint8_t buffer[PACKAGE_MAX]; + int n; + int count = 0; + + do{ + count++; + libusb_bulk_transfer(usb->devhdl, EP_DATA, buffer, sizeof(buffer), &n, 100); + }while(n > 0); + + sr_dbg("%s(): flushed %d bytes", __func__, count * (int)sizeof(buffer)); +} + +SR_PRIV void lnss_reset(struct sr_usb_dev_inst *usb) +{ + usb_send_command(usb, PICCMD_PIC_RESET); +} + +SR_PRIV bool lnss_get_pic_firmware_version(struct sr_usb_dev_inst *usb, uint8_t *version) +{ + uint8_t response[16]; + + if(usb_send_command(usb, PICCMD_PIC_VERSION) != SR_OK){ + return false; + } + + if(read_control_bytes(usb, 16, response) != SR_OK){ + return false; + } + + sprintf((char*)version, "%u.%u.%u", response[6], response[5], response[4]); + + return true; +} + +SR_PRIV bool lnss_version_fpga(struct sr_usb_dev_inst *usb, char *dest) { + bool rv = false; + uint8_t x = 0; + + if (!dest) { + sr_warn("no destination?!"); + return false; + } + + for (int i = 0; i < 4; i++) { + // to match vendor sw, it's in reverse order + get_i2c_reg(usb, FPGA_I2C_ADDRESS_ROM, 3 - i, &x); + //sr_dbg("fpga byte %d = %02X", i, x); + /* if the git rev is _plausible_ it's true */ + if (x != 255) { + rv = true; + } + sprintf(dest + i*2, "%02X", x); + } + + dest[8] = 0; + + return rv; +} + +SR_PRIV bool lnss_load_fpga(const struct sr_dev_inst *sdi) { + struct drv_context *drvc = sdi->driver->context; + struct dev_context *devc = sdi->priv; + + char name[200]; + uint8_t *firmware; + size_t length; + bool res; + + snprintf(name, sizeof(name) - 1, "SmartScope_%s.bin", devc->hw_rev); + + /* All existing blogs are < 300k, don't really expect much change */ + firmware = sr_resource_load(drvc->sr_ctx, SR_RESOURCE_FIRMWARE, + name, &length, 400 * 1024); + if (!firmware) { + sr_err("Failed to load firmware :("); + return false; + } + + sr_info("Uploading firmware '%s'.", name); + + res = lnss_flash_fpga(sdi->conn, firmware, length); + + g_free(firmware); + + return res; +} + +SR_PRIV bool lnss_flash_fpga(struct sr_usb_dev_inst *usb, const uint8_t *firmware, size_t length) +{ + unsigned i, err; + /* Straight from labnation code. don't ask me what the real plan is */ + int PACKSIZE = 32; + unsigned PADDING = 2048/8; + int cmd = length / PACKSIZE + PADDING; + + uint8_t cmd_start[] = {HEADER_CMD_BYTE, PICCMD_PROGRAM_FPGA_START, cmd >> 8, cmd & 0xff}; + uint8_t cmd_end[] = {HEADER_CMD_BYTE, PICCMD_PROGRAM_FPGA_END}; + + err = libusb_bulk_transfer(usb->devhdl, EP_CMD_OUT, cmd_start, sizeof(cmd_start), NULL, 200); + if (err != 0) { + sr_err("Failed to start fpga programming: %s", libusb_error_name(err)); + return false; + } + + err = libusb_clear_halt(usb->devhdl, EP_DATA); + if (err != 0) { + sr_err("Failed to clear halt stage 1: %s", libusb_error_name(err)); + return false; + } + + int chunkcnt = 0; + int actual; + int actual_sum = 0; + for (i = 0; i < length; i += 2048) { + int desired = MIN(2048, length - i); + err = libusb_bulk_transfer(usb->devhdl, EP_CMD_OUT, (uint8_t*)(firmware + i), desired, &actual, 200); + if (err != 0) { + sr_err("Failed to write chunk %d (%d bytes): %s", chunkcnt, desired, libusb_error_name(err)); + return false; + } + chunkcnt++; + sr_dbg("wrote chunk %d for %d bytes, libusb: %d", chunkcnt, actual, err); + if (actual != desired) { + sr_warn("Failed to write a chunk %d : %d < 2048", chunkcnt, actual); + } + actual_sum += actual; + } + + sr_info("After %d chunks, have written %u, length=%lu", chunkcnt, actual_sum, length); + + /* this seems rather insane, but, hey, it's what the vendor code does... */ + uint8_t data[32]; + memset(data, 0xff, sizeof(data)); + for (i = 0; i < PADDING; i++) { + err = libusb_bulk_transfer(usb->devhdl, EP_CMD_OUT, data, 32, NULL, 200); + if (err != 0) { + sr_err("Failed to write 0xff trailer iteration: %d : %s", i, libusb_error_name(err)); + return false; + } + } + err = libusb_bulk_transfer(usb->devhdl, EP_CMD_OUT, cmd_end, sizeof(cmd_end), NULL, 200); + if (err != 0) { + sr_err("Failed to exit fpga programming : %s", libusb_error_name(err)); + return false; + } + err = libusb_clear_halt(usb->devhdl, EP_DATA); + if (err != 0) { + sr_err("Failed to clear halt stage 2: %s", libusb_error_name(err)); + return false; + } + return true; +} + +/** + * samplerate = 1/(10e-9 * 2^decimation) + */ +SR_PRIV int lnss_subsamplerate_set(const struct sr_dev_inst *sdi, uint64_t samplerate) +{ + uint8_t power; + + if(!sdi) + return SR_ERR_ARG; + + if(samplerate > LNSS_MAX_SAMPLERATE) + samplerate = LNSS_MAX_SAMPLERATE; + else if (samplerate < LNSS_MIN_SAMPLERATE) + samplerate = LNSS_MIN_SAMPLERATE; + + power = 0; + + while(((uint32_t)(LNSS_MAX_SAMPLERATE >> power)) > samplerate){ + power++; + } + + sr_dbg("%s(): Input decimation %u => %luHz", __func__, power, LNSS_MAX_SAMPLERATE / (1 << power)); + + return reg_set(sdi->conn, INPUT_DECIMATION, power); +} + +/** + * @brief Configures the number of samples per acquisition. + * Despite the 100MHz sample rate, this device is + * quite slow when transfering capture samples through usb. + * A 4M sample capture can take a while.... + * + * length = 2048 * 2^Acquisition + */ +SR_PRIV uint32_t lnss_acquisition_depth_set(const struct sr_dev_inst *sdi, uint32_t length) +{ + uint8_t power; + + if(!sdi) + return 0; + + if(length > LNSS_MAX_ACQUISITION) + length = LNSS_MAX_ACQUISITION; + else if (length < LNSS_MIN_ACQUISITION) + length = LNSS_MIN_ACQUISITION; + + power = 0; + + while(((uint32_t)(LNSS_MIN_ACQUISITION << power)) < length){ + power++; + } + + length = LNSS_MIN_ACQUISITION * (1 << power); + + reg_set(sdi->conn, ACQUISITION_DEPTH, power); + + sr_dbg("%s(): Acquisition depth: %u => %u samples", __func__, power, length); + + return length; +} + +SR_PRIV int lnss_triggers_set(const struct sr_dev_inst *sdi, uint8_t falling, uint8_t rising, uint8_t low, uint8_t high) +{ + struct dev_context *devc; + uint8_t buf[4]; + + devc = sdi->priv; + + buf[0] = rising; + buf[1] = falling; + buf[2] = high; + buf[3] = low; + + devc->trigger_enabled = falling | rising | low | high; + + sr_dbg("%s(): Falling 0x%02X Rising 0x%02X Low 0x%02X High 0x%02X ", + __func__, + falling, rising, + low, high); + + return controller_register_set(sdi->conn, CTRL_FPGA, (FPGA_I2C_ADDRESS_REG << 8) + DIGITAL_TRIGGER_RISING, 4, buf); +} + +SR_PRIV int lnss_aquisition_start(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + int sr_error; + + devc = sdi->priv; + + //lnss_flush_data_pipe(sdi->conn); + + devc->sent_samples = 0; + devc->trigger_sent = false; + devc->new_acquisition = true; + + devc->pre_trigger_samples = (devc->trigger_enabled) ? + devc->capture_ratio * ((double)devc->acquisition_depth / 100): + 0; + + reg_set(sdi->conn, TRIGGERHOLDOFF_B0, devc->pre_trigger_samples); + reg_set(sdi->conn, TRIGGERHOLDOFF_B1, devc->pre_trigger_samples >> 8); + reg_set(sdi->conn, TRIGGERHOLDOFF_B2, devc->pre_trigger_samples >> 16); + reg_set(sdi->conn, TRIGGERHOLDOFF_B3, devc->pre_trigger_samples >> 24); + + strobe_set(sdi->conn, SCOPE_UPDATE, 1); + dump_regs(sdi); + sr_error = strobe_set(sdi->conn, ACQ_START, 1); + + sr_info("Requested %lu samples, acquiring %u ...", devc->limit_samples, devc->acquisition_depth); + + return sr_error; +} + +/** + * @brief Acquire data from logic analyzer + * + */ +SR_PRIV int lnss_data_receive(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + uint64_t samples_todo; + uint32_t samples_received; + const uint8_t trigger_delay [] = {4,3,2,1,0,0,0,0,0,0}; + + (void)fd; + (void)revents; + + sdi = cb_data; + devc = sdi->priv; + + uint32_t rx_buffer_len = acquisition_get(sdi->conn, devc->rx_buffer, + SZ_HDR + (LNSS_MIN_ACQUISITION * LNSS_NUM_AN_CHANNELS)); + + if(rx_buffer_len == 0){ + // Something fail, stop all + sr_dev_acquisition_stop(sdi); + strobe_set(sdi->conn, ACQ_STOP, 1); + return false; + } + + struct header *hdr = (struct header*)devc->rx_buffer; + + if(hdr->flags & (TimedOut | IsOverview)){ + return G_SOURCE_CONTINUE; + } + + if(hdr->flags){ + if(!(hdr->flags & IsFullAcqusition)){ + return G_SOURCE_CONTINUE; + } + } + + // Received samples include two channels (CHA, CHB). For current setup + // LA samples are on CHA (set by LA_CHANNEL Strobe) + int n_channels = 2; + samples_received = (rx_buffer_len - SZ_HDR) / n_channels; + + sr_spew("%s(): packet flags: 0x%02X", __func__, hdr->flags); + sr_spew("%s(): packet acquisition_id: %u", __func__, hdr->acquisition_id); + sr_spew("%s(): packet bursts: %u", __func__, hdr->n_bursts); + sr_spew("%s(): packet bursts size: %u", __func__, hdr->bytes_per_burst); + sr_spew("%s(): packet in decimation: %u", __func__, hdr->regs[HDR_INPUT_DECIMATION]); + sr_spew("%s(): packet view decimation: %u", __func__, hdr->regs[HDR_VIEW_DECIMATION]); + sr_spew("%s(): packet acquisition depth: %u", __func__, hdr->regs[HDR_ACQUISITION_DEPTH]); + sr_spew("%s(): packet trigger holdoff: %u", __func__, *((uint32_t*)&hdr->regs[HDR_TRIGGERHOLDOFF_B0])); + sr_spew("%s(): packet view offset: %u", __func__, *((uint32_t*)&hdr->regs[HDR_VIEW_OFFSET_B0]) & 0x00FFFFFF); + + if(devc->new_acquisition){ + if(hdr->acquisition_id == devc->acquisition_id){ + sr_dbg("%s(): Ignoring packet from previous acquisition", __func__); + return G_SOURCE_CONTINUE; + } + devc->new_acquisition = false; + devc->acquisition_id = hdr->acquisition_id; + } + + sr_dbg("%s(): Received %d samples", __func__, samples_received); + + samples_todo = (devc->sent_samples < devc->acquisition_depth) ? + devc->acquisition_depth - devc->sent_samples : 0; + + int samples_to_send = (samples_received < samples_todo) ? samples_received : samples_todo; + + devc->trigger_delay = trigger_delay[hdr->regs[HDR_INPUT_DECIMATION]]; + + //Extract LA samples from CHA to buffer start smashing header (not needed at this point) + for(int i = 0; i < samples_to_send; i++){ + devc->rx_buffer[i] = devc->rx_buffer[SZ_HDR + (i * n_channels)]; + } + + send_logic_data(sdi, devc->rx_buffer, samples_to_send); + + devc->sent_samples += samples_to_send; + + if ((devc->acquisition_depth > 0) && (devc->sent_samples >= devc->acquisition_depth)) { + sr_dbg("%s(): Requested number of samples reached!", __func__); + sr_dev_acquisition_stop(sdi); + return false; + } + + return G_SOURCE_CONTINUE; +} diff --git a/src/hardware/labnation-smartscope/protocol.h b/src/hardware/labnation-smartscope/protocol.h new file mode 100644 index 000000000..c51dd3396 --- /dev/null +++ b/src/hardware/labnation-smartscope/protocol.h @@ -0,0 +1,312 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2017 Karl Palsson + * + * 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "libsigrok-internal.h" + +#define LOG_PREFIX "LNSS" + +#define USB_INTERFACE 0 + +#define LNSS_VID 0x4d8 +#define LNSS_PID 0xf4b5 +#define LNSS_NUM_CHANNELS 8 +#define LNSS_NUM_AN_CHANNELS 2 +#define LNSS_MAX_SAMPLERATE 100000000UL +#define LNSS_MIN_SAMPLERATE 195312 +#define LNSS_MAX_ACQUISITION (4 * 1024 * 1024) // Size of memory chip on device +#define LNSS_MIN_ACQUISITION 2048 +#define LNSS_INPUT_DECIMATION_MAX 9 + +#define SZ_HDR 64 +#define SZ_OVERVIEW (LNSS_MIN_ACQUISITION * LNSS_NUM_AN_CHANNELS) +#define PACKAGE_MAX 64 + +#define HDR_N_REGS 30 +#define HDR_N_STROBES 5 + +#define I2C_MAX_WRITE_LENGTH 27 +#define I2C_MAX_WRITE_LENGTH_BULK 29 + +#define FPGA_I2C_ADDRESS_REG 0x0C +#define FPGA_I2C_ADDRESS_ROM 0x0D +#define FPGA_I2C_ADDRESS_AWG 0x0E + +#define FLASH_USER_ADDRESS_MASK 0x0FFF + +/** + * TRIGGER_MODE Register Bits + * | ACQ Mode[1:0] | Edge[1:0] | Source | Channel | Mode [1:0] | + */ +#define TRG_ACQ_AUTO (0 << 6) // Auto acquisition +#define TRG_ACQ_NORMAL (1 << 6) // Normal acquisition +#define TRG_ACQ_SINGLE (2 << 6) // Single acquisition +#define TRG_EDGE_RISING (0 << 4) // Trigger on rising edge +#define TRG_EDGE_FALLING (1 << 4) // Trigger on falling +#define TRG_EDGE_ANY (2 << 4) // Trigger on any edge +#define TRG_SOURCE_CHANNEL (0 << 3) // A channel as trigger source +#define TRG_SOURCE_EXT (1 << 3) // External trigger source +#define TRG_CHANNEL_A (0 << 2) // Trigger on analog channel A +#define TRG_CHANNEL_B (1 << 2) // Trigger on analog channel B +#define TRG_MODE_EDGE (0 << 0) // Trigger on edge +#define TRG_MODE_TIMEOUT (1 << 0) +#define TRG_MODE_PULSE (2 << 0) +#define TRG_MODE_DIGITAL (3 << 0) // Trigger on digital logic + +#define DEFAULT_SAMPLERATE 6500000 //MHz, should match a value in samplerates array +#define DEFAULT_CAPTURE_RACIO 10 +#define DEFAULT_NUM_SAMPLES LNSS_MIN_ACQUISITION + +enum pic_cmd { + PICCMD_PIC_VERSION = 1, + PICCMD_PIC_WRITE = 2, + PICCMD_PIC_READ = 3, + PICCMD_PIC_RESET = 4, + PICCMD_PIC_BOOTLOADER = 5, + PICCMD_EEPROM_READ = 6, + PICCMD_EEPROM_WRITE = 7, + PICCMD_FLASH_ROM_READ = 8, + PICCMD_FLASH_ROM_WRITE = 9, + PICCMD_I2C_WRITE = 10, + PICCMD_I2C_READ = 11, + PICCMD_PROGRAM_FPGA_START = 12, + PICCMD_PROGRAM_FPGA_END = 13, + PICCMD_I2C_WRITE_START = 14, + PICCMD_I2C_WRITE_BULK = 15, + PICCMD_I2C_WRITE_STOP = 16 +}; + +enum controller_op { + READ = 0, + WRITE, + WRITE_BEGIN, + WRITE_BODY, + WRITE_END +}; + +enum controller { + CTRL_PIC = 0, + CTRL_ROM = 1, + CTRL_FLASH = 2, + CTRL_FPGA = 3, + CTRL_AWG = 4 +}; + +enum ROM { + FW_GIT0, + FW_GIT1, + FW_GIT2, + FW_GIT3, + SPI_RECEIVED_VALUE, + STROBES0, + STROBES1, + STROBES2, +}; + +enum ADC { + POWER_MANAGMENT = 0, + OUTPUT_FORMAT, + OUTPUT_PWR_MNGMNT, + DATA_CLK_TIMING, + CHA_TERMINATION, + CHB_TERMINATION, + FORMAT_PATTERN, + COMMON_MODE, + ADC_RESERVED, + SOFT_RESET +}; + +/** + * + */ +enum REG { + STROBE_UPDATE = 0, // strobe = (STROBE_UPDATE << 1) | state + SPI_ADDRESS = 1, // ADC Register address + SPI_WRITE_VALUE = 2, // ADC Register value + DIVIDER_MULTIPLIER = 3, // Selects input range. CHA bits 3:0, CHB bits 7:4 + CHA_YOFFSET_VOLTAGE = 4, // + CHB_YOFFSET_VOLTAGE = 5, // + TRIGGER_PWM = 6, // + TRIGGER_LEVEL = 7, // 0x80 -> 0V + TRIGGER_MODE = 8, // See trigger mode bits + TRIGGER_PW_MIN_B0 = 9, // + TRIGGER_PW_MIN_B1 = 10, // + TRIGGER_PW_MIN_B2 = 11, // + TRIGGER_PW_MAX_B0 = 12, // + TRIGGER_PW_MAX_B1 = 13, // + TRIGGER_PW_MAX_B2 = 14, // + INPUT_DECIMATION = 15, // sample rate = 100MHz / 2^INPUT_DECIMATION + ACQUISITION_DEPTH = 16, // number of samples = 2048 * 2^ACQUISITION_DEPTH + TRIGGERHOLDOFF_B0 = 17, // Trigger holdoff, number of samples before trigger. Note a delay must be applied depending sample rate + TRIGGERHOLDOFF_B1 = 18, // + TRIGGERHOLDOFF_B2 = 19, // + TRIGGERHOLDOFF_B3 = 20, // + VIEW_DECIMATION = 21, + VIEW_OFFSET_B0 = 22, + VIEW_OFFSET_B1 = 23, + VIEW_OFFSET_B2 = 24, + VIEW_ACQUISITIONS = 25, + VIEW_BURSTS = 26, // Number of data bursts = 2^VIEW_BURSTS + VIEW_EXCESS_B0 = 27, + VIEW_EXCESS_B1 = 28, + DIGITAL_TRIGGER_RISING = 29, // Trigger bit masks + DIGITAL_TRIGGER_FALLING = 30, // + DIGITAL_TRIGGER_HIGH = 31, // + DIGITAL_TRIGGER_LOW = 32, // + DIGITAL_OUT = 33, + GENERATOR_DECIMATION_B0 = 34, + GENERATOR_DECIMATION_B1 = 35, + GENERATOR_DECIMATION_B2 = 36, + GENERATOR_SAMPLES_B0 = 37, + GENERATOR_SAMPLES_B1 = 38, +}; + +enum STR { + GLOBAL_RESET = 0, + INIT_SPI_TRANSFER = 1, // 0: Start spi transfer, 1: Stop + GENERATOR_TO_AWG = 2, + LA_ENABLE = 3, // Enables Logic analyzer + SCOPE_ENABLE = 4, // Must be enable to perform acquisitions + SCOPE_UPDATE = 5, // Must be set to apply register values + FORCE_TRIGGER = 6, + VIEW_UPDATE = 7, // Sends updated view packet (after change view registers set this bit to get an updated view packet) + VIEW_SEND_OVERVIEW = 8, // Enables Overview data packets + VIEW_SEND_PARTIAL = 9, + ACQ_START = 10, // Start acquisition + ACQ_STOP = 11, // Stop acquisition + CHA_DCCOUPLING = 12, + CHB_DCCOUPLING = 13, + ENABLE_ADC = 14, // Must be enable to perform acquisitions + ENABLE_NEG = 15, + ENABLE_RAM = 16, + DOUT_3V_5V = 17, + EN_OPAMP_B = 18, + GENERATOR_TO_DIGITAL = 19, + ROLL = 20, + LA_CHANNEL = 21, // Selects which channel will hold LA samples, 0: CHA 1: CHB +}; + +/** + * Data packet header registers + */ +enum HDR_REGS { + HDR_TRIGGER_LEVEL = 0, + HDR_TRIGGER_MODE, + HDR_TRIGGERHOLDOFF_B0, + HDR_TRIGGERHOLDOFF_B1, + HDR_TRIGGERHOLDOFF_B2, + HDR_TRIGGERHOLDOFF_B3, + HDR_CHA_YOFFSET_VOLTAGE, + HDR_CHB_YOFFSET_VOLTAGE, + HDR_DIVIDER_MULTIPLIER, + HDR_INPUT_DECIMATION, + HDR_TRIGGER_PW_MIN_B0, + HDR_TRIGGER_PW_MIN_B1, + HDR_TRIGGER_PW_MIN_B2, + HDR_TRIGGER_PW_MAX_B0, + HDR_TRIGGER_PW_MAX_B1, + HDR_TRIGGER_PW_MAX_B2, + HDR_TRIGGER_PWM, + HDR_DIGITAL_TRIGGER_RISING, + HDR_DIGITAL_TRIGGER_FALLING, + HDR_DIGITAL_TRIGGER_HIGH, + HDR_DIGITAL_TRIGGER_LOW, + HDR_ACQUISITION_DEPTH, + /* Not valid for full acquisition packets?? */ + HDR_VIEW_DECIMATION, + HDR_VIEW_OFFSET_B0, + HDR_VIEW_OFFSET_B1, + HDR_VIEW_OFFSET_B2, + HDR_VIEW_ACQUISITIONS, + HDR_VIEW_BURSTS, + HDR_VIEW_EXCESS_B0, + HDR_VIEW_EXCESS_B1, +}; + +enum HDR_STROBES { + HDR_LA_ENABLE = 0, + HDR_CHA_DCCOUPLING, + HDR_CHB_DCCOUPLING, + HDR_ROLL, + HDR_LA_CHANNEL, +}; + +enum header_flags{ + Acquiring = 0x01, + IsOverview = 0x02, // Data is overview + IsLastAcquisition = 0x04, // Last data packet is being acquired + Rolling = 0x08, + TimedOut = 0x10, // No data available + AwaitingTrigger = 0x20, // Trigger has not been tripped + Armded = 0x40, // Trigger is configured + IsFullAcqusition = 0x80 // Packet belongs to a full acquisition +}; + +struct __attribute__ ((__packed__)) header { + uint8_t magic[2]; //0-1 "LN" + uint8_t header_offset; //2 + uint8_t bytes_per_burst; //3 + uint16_t n_bursts; //4-5 + uint16_t offset; //6-7 + uint8_t unused[2]; //8-9 + uint8_t flags; //10 + uint8_t acquisition_id; //11 Incremented at start of acquisition + uint8_t unused2[3]; //12-13-14 + uint8_t regs[HDR_N_REGS]; //15-44 + uint8_t strobes[(HDR_N_STROBES + 7) / 8];//45- +}; + +/** Private, per-device-instance driver context. */ +struct dev_context { + /* Model-specific information */ + char hw_rev[4]; + /* Acquisition settings */ + uint64_t samplerate; // Current selected sample rate + uint64_t limit_samples; // samples requested by UI + uint64_t sent_samples; // number of samples sent to UI + uint32_t acquisition_depth; // Real number of samples acquired, > limit_samples + uint32_t pre_trigger_samples; + uint8_t capture_ratio; // ratio between samples before and after trigger + uint8_t acquisition_id; // Number of acquisition, same for all packets of that acquisition + uint8_t new_acquisition; // A new acquisition was started by UI + uint8_t trigger_enabled; + uint8_t trigger_sent; + uint8_t trigger_delay; // Compensation for trigger, sample rate dependent + uint8_t *rx_buffer; +}; + +SR_PRIV int lnss_init(const struct sr_dev_inst *sdi); +SR_PRIV void lnss_cleanup(const struct sr_dev_inst *sdi); +SR_PRIV bool lnss_load_fpga(const struct sr_dev_inst *sdi); +SR_PRIV int lnss_aquisition_start(const struct sr_dev_inst *sdi); +SR_PRIV int lnss_subsamplerate_set(const struct sr_dev_inst *sdi, uint64_t rate); +SR_PRIV uint32_t lnss_acquisition_depth_set(const struct sr_dev_inst *sdi, uint32_t length); +SR_PRIV bool lnss_version_fpga(struct sr_usb_dev_inst *usb, char *dest); +SR_PRIV bool lnss_get_pic_firmware_version(struct sr_usb_dev_inst *usb, uint8_t *version); +SR_PRIV bool lnss_flash_fpga(struct sr_usb_dev_inst *usb, const uint8_t *firmware, size_t length); +SR_PRIV void lnss_flush_data_pipe(struct sr_usb_dev_inst *usb); +SR_PRIV void lnss_reset(struct sr_usb_dev_inst *usb); +SR_PRIV int lnss_data_receive(int fd, int revents, void *cb_data); +SR_PRIV int lnss_triggers_set(const struct sr_dev_inst *sdi, uint8_t falling, uint8_t rising, uint8_t low, uint8_t high); \ No newline at end of file