+/* HACK Experiment to spot FPGA registers of interest. */
+static void la2016_dump_fpga_registers(const struct sr_dev_inst *sdi,
+ const char *caption, size_t reg_lower, size_t reg_upper)
+{
+ static const size_t dump_chunk_len = 16;
+
+ size_t rdlen;
+ uint8_t rdbuf[0x80 - 0x00]; /* Span all FPGA registers. */
+ const uint8_t *rdptr;
+ int ret;
+ size_t dump_addr, indent, dump_len;
+ GString *txt;
+
+ if (sr_log_loglevel_get() < SR_LOG_SPEW)
+ return;
+
+ if (!reg_lower && !reg_upper) {
+ reg_lower = 0;
+ reg_upper = sizeof(rdbuf);
+ }
+ if (reg_upper - reg_lower > sizeof(rdbuf))
+ reg_upper = sizeof(rdbuf) - reg_lower;
+
+ rdlen = reg_upper - reg_lower;
+ ret = ctrl_in(sdi, CMD_FPGA_SPI, reg_lower, 0, rdbuf, rdlen);
+ if (ret != SR_OK) {
+ sr_err("Cannot get registers space.");
+ return;
+ }
+ rdptr = rdbuf;
+
+ sr_spew("FPGA registers dump: %s", caption ? : "for fun");
+ dump_addr = reg_lower;
+ while (rdlen) {
+ dump_len = rdlen;
+ indent = dump_addr % dump_chunk_len;
+ if (dump_len > dump_chunk_len)
+ dump_len = dump_chunk_len;
+ if (dump_len + indent > dump_chunk_len)
+ dump_len = dump_chunk_len - indent;
+ txt = sr_hexdump_new(rdptr, dump_len);
+ sr_spew(" %04zx %*s%s",
+ dump_addr, (int)(3 * indent), "", txt->str);
+ sr_hexdump_free(txt);
+ dump_addr += dump_len;
+ rdptr += dump_len;
+ rdlen -= dump_len;
+ }
+}
+
+/*
+ * Check the necessity for FPGA bitstream upload, because another upload
+ * would take some 600ms which is undesirable after program startup. Try
+ * to access some FPGA registers and check the values' plausibility. The
+ * check should fail on the safe side, request another upload when in
+ * doubt. A positive response (the request to continue operation with the
+ * currently active bitstream) should be conservative. Accessing multiple
+ * registers is considered cheap compared to the cost of bitstream upload.
+ *
+ * It helps though that both the vendor software and the sigrok driver
+ * use the same bundle of MCU firmware and FPGA bitstream for any of the
+ * supported models. We don't expect to successfully communicate to the
+ * device yet disagree on its protocol. Ideally we would access version
+ * identifying registers for improved robustness, but are not aware of
+ * any. A bitstream reload can always be forced by a power cycle.
+ */
+static int check_fpga_bitstream(const struct sr_dev_inst *sdi)
+{
+ uint8_t init_rsp;
+ uint8_t buff[REG_PWM_EN - REG_RUN]; /* Larger of REG_RUN, REG_PWM_EN. */
+ int ret;
+ uint16_t run_state;
+ uint8_t pwm_en;
+ size_t read_len;
+ const uint8_t *rdptr;
+
+ sr_dbg("Checking operation of the FPGA bitstream.");
+ la2016_dump_fpga_registers(sdi, "bitstream check", 0, 0);
+
+ init_rsp = ~0;
+ ret = ctrl_in(sdi, CMD_FPGA_INIT, 0x00, 0, &init_rsp, sizeof(init_rsp));
+ if (ret != SR_OK || init_rsp != 0) {
+ sr_dbg("FPGA init query failed, or unexpected response.");
+ return SR_ERR_IO;
+ }
+
+ read_len = sizeof(run_state);
+ ret = ctrl_in(sdi, CMD_FPGA_SPI, REG_RUN, 0, buff, read_len);
+ if (ret != SR_OK) {
+ sr_dbg("FPGA register access failed (run state).");
+ return SR_ERR_IO;
+ }
+ rdptr = buff;
+ run_state = read_u16le_inc(&rdptr);
+ sr_spew("FPGA register: run state 0x%04x.", run_state);
+ if (run_state && (run_state & 0x3) != 0x1) {
+ sr_dbg("Unexpected FPGA register content (run state).");
+ return SR_ERR_DATA;
+ }
+ if (run_state && (run_state & ~0xf) != 0x85e0) {
+ sr_dbg("Unexpected FPGA register content (run state).");
+ return SR_ERR_DATA;
+ }
+
+ read_len = sizeof(pwm_en);
+ ret = ctrl_in(sdi, CMD_FPGA_SPI, REG_PWM_EN, 0, buff, read_len);
+ if (ret != SR_OK) {
+ sr_dbg("FPGA register access failed (PWM enable).");
+ return SR_ERR_IO;
+ }
+ rdptr = buff;
+ pwm_en = read_u8_inc(&rdptr);
+ sr_spew("FPGA register: PWM enable 0x%02x.", pwm_en);
+ if ((pwm_en & 0x3) != 0x0) {
+ sr_dbg("Unexpected FPGA register content (PWM enable).");
+ return SR_ERR_DATA;
+ }
+
+ sr_info("Could re-use current FPGA bitstream. No upload required.");
+ return SR_OK;
+}
+
+static int upload_fpga_bitstream(const struct sr_dev_inst *sdi,
+ const char *bitstream_fname)