+ return TRUE;
+}
+
+SR_PRIV int la2016_identify_device(const struct sr_dev_inst *sdi,
+ gboolean show_message)
+{
+ struct dev_context *devc;
+ uint8_t buf[8];
+ size_t rdoff, rdlen;
+ const uint8_t *rdptr;
+ uint8_t date_yy, date_mm;
+ uint8_t dinv_yy, dinv_mm;
+ uint8_t magic;
+ size_t model_idx;
+ const struct kingst_model *model;
+ int ret;
+
+ devc = sdi->priv;
+
+ /*
+ * Four EEPROM bytes at offset 0x20 are the manufacturing date,
+ * year and month in BCD format, followed by inverted values for
+ * consistency checks. For example bytes 20 04 df fb translate
+ * to 2020-04. This information can help identify the vintage of
+ * devices when unknown magic numbers are seen.
+ */
+ rdoff = 0x20;
+ rdlen = 4 * sizeof(uint8_t);
+ ret = ctrl_in(sdi, CMD_EEPROM, rdoff, 0, buf, rdlen);
+ if (ret != SR_OK && !show_message) {
+ /* Non-fatal weak attempt during probe. Not worth logging. */
+ sr_dbg("Cannot access EEPROM.");
+ return SR_ERR_IO;
+ } else if (ret != SR_OK) {
+ /* Failed attempt in regular use. Non-fatal. Worth logging. */
+ sr_err("Cannot read manufacture date in EEPROM.");
+ } else {
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ GString *txt;
+ txt = sr_hexdump_new(buf, rdlen);
+ sr_spew("Manufacture date bytes %s.", txt->str);
+ sr_hexdump_free(txt);
+ }
+ rdptr = &buf[0];
+ date_yy = read_u8_inc(&rdptr);
+ date_mm = read_u8_inc(&rdptr);
+ dinv_yy = read_u8_inc(&rdptr);
+ dinv_mm = read_u8_inc(&rdptr);
+ sr_info("Manufacture date: 20%02hx-%02hx.", date_yy, date_mm);
+ if ((date_mm ^ dinv_mm) != 0xff || (date_yy ^ dinv_yy) != 0xff)
+ sr_warn("Manufacture date fails checksum test.");
+ }
+
+ /*
+ * Several Kingst logic analyzer devices share the same USB VID
+ * and PID. The product ID determines which MCU firmware to load.
+ * The MCU firmware provides access to EEPROM content which then
+ * allows to identify the device model. Which in turn determines
+ * which FPGA bitstream to load. Eight bytes at offset 0x08 are
+ * to get inspected.
+ *
+ * EEPROM content for model identification is kept redundantly
+ * in memory. The values are stored in verbatim and in inverted
+ * form, multiple copies are kept at different offsets. Example
+ * data:
+ *
+ * magic 0x08
+ * | ~magic 0xf7
+ * | |
+ * 08f7000008f710ef
+ * | |
+ * | ~magic backup
+ * magic backup
+ *
+ * Exclusively inspecting the magic byte appears to be sufficient,
+ * other fields seem to be 'don't care'.
+ *
+ * magic 2 == LA2016 using "kingst-la2016-fpga.bitstream"
+ * magic 3 == LA1016 using "kingst-la1016-fpga.bitstream"
+ * magic 8 == LA2016a using "kingst-la2016a1-fpga.bitstream"
+ * (latest v1.3.0 PCB, perhaps others)
+ * magic 9 == LA1016a using "kingst-la1016a1-fpga.bitstream"
+ * (latest v1.3.0 PCB, perhaps others)
+ *
+ * When EEPROM content does not match the hardware configuration
+ * (the board layout), the software may load but yield incorrect
+ * results (like swapped channels). The FPGA bitstream itself
+ * will authenticate with IC U10 and fail when its capabilities
+ * do not match the hardware model. An LA1016 won't become a
+ * LA2016 by faking its EEPROM content.
+ */
+ devc->identify_magic = 0;
+ rdoff = 0x08;
+ rdlen = 8 * sizeof(uint8_t);
+ ret = ctrl_in(sdi, CMD_EEPROM, rdoff, 0, &buf, rdlen);
+ if (ret != SR_OK) {
+ sr_err("Cannot read EEPROM device identifier bytes.");
+ return ret;
+ }
+ if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+ GString *txt;
+ txt = sr_hexdump_new(buf, rdlen);
+ sr_spew("EEPROM magic bytes %s.", txt->str);
+ sr_hexdump_free(txt);
+ }
+ if ((buf[0] ^ buf[1]) == 0xff) {
+ /* Primary copy of magic passes complement check. */
+ magic = buf[0];
+ sr_dbg("Using primary magic, value %d.", (int)magic);
+ } else if ((buf[4] ^ buf[5]) == 0xff) {
+ /* Backup copy of magic passes complement check. */
+ magic = buf[4];
+ sr_dbg("Using backup magic, value %d.", (int)magic);
+ } else {
+ sr_err("Cannot find consistent device type identification.");
+ magic = 0;
+ }
+ devc->identify_magic = magic;
+
+ devc->model = NULL;
+ for (model_idx = 0; model_idx < ARRAY_SIZE(models); model_idx++) {
+ model = &models[model_idx];
+ if (model->magic != magic)
+ continue;
+ devc->model = model;
+ sr_info("Model '%s', %zu channels, max %" PRIu64 "MHz.",
+ model->name, model->channel_count,
+ model->samplerate / SR_MHZ(1));
+ devc->fpga_bitstream = g_strdup_printf(FPGA_FWFILE_FMT,
+ model->fpga_stem);
+ sr_info("FPGA bitstream file '%s'.", devc->fpga_bitstream);
+ break;
+ }
+ if (!devc->model) {
+ sr_err("Cannot identify as one of the supported models.");