]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/kingst-la2016/protocol.c
kingst-la2016: adjust config param checks, threshold range check
[libsigrok.git] / src / hardware / kingst-la2016 / protocol.c
index 102c5cc4b5da87ff16e44c69d13819d9453730fe..dc1a24a5193b6f0a96933e9f15443480b0eb2c5b 100644 (file)
@@ -95,6 +95,10 @@ static const struct kingst_model models[] = {
 #define RUNSTATE_TRGD_BIT      (1UL << 2)
 #define RUNSTATE_POST_BIT      (1UL << 3)
 
+/* Properties related to the layout of capture data downloads. */
+#define NUM_PACKETS_IN_CHUNK   5
+#define TRANSFER_PACKET_LENGTH 16
+
 static int ctrl_in(const struct sr_dev_inst *sdi,
        uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
        void *data, uint16_t wLength)
@@ -340,10 +344,10 @@ static int set_threshold_voltage(const struct sr_dev_inst *sdi, float voltage)
        devc = sdi->priv;
 
        /* Clamp threshold setting to valid range for LA2016. */
-       if (voltage > 4.0) {
-               voltage = 4.0;
-       } else if (voltage < -4.0) {
-               voltage = -4.0;
+       if (voltage > LA2016_THR_VOLTAGE_MAX) {
+               voltage = LA2016_THR_VOLTAGE_MAX;
+       } else if (voltage < -LA2016_THR_VOLTAGE_MAX) {
+               voltage = -LA2016_THR_VOLTAGE_MAX;
        }
 
        /*
@@ -392,110 +396,101 @@ static int set_threshold_voltage(const struct sr_dev_inst *sdi, float voltage)
        return SR_OK;
 }
 
-static int enable_pwm(const struct sr_dev_inst *sdi, gboolean p1, gboolean p2)
-{
-       struct dev_context *devc;
-       uint8_t cfg;
-       int ret;
-
-       devc = sdi->priv;
-
-       cfg = 0;
-       if (p1)
-               cfg |= 1U << 0;
-       if (p2)
-               cfg |= 1U << 1;
-       sr_dbg("Set PWM enable %d %d. Config 0x%02x.", p1, p2, cfg);
-
-       ret = ctrl_out(sdi, CMD_FPGA_SPI, REG_PWM_EN, 0, &cfg, sizeof(cfg));
-       if (ret != SR_OK) {
-               sr_err("Cannot setup PWM enabled state.");
-               return ret;
-       }
-
-       devc->pwm_setting[0].enabled = (p1) ? 1 : 0;
-       devc->pwm_setting[1].enabled = (p2) ? 1 : 0;
-
-       return SR_OK;
-}
-
-static int configure_pwm(const struct sr_dev_inst *sdi, uint8_t which,
-       float freq, float duty)
+/*
+ * Communicates a channel's configuration to the device after the
+ * parameters may have changed. Configuration of one channel may
+ * interfere with other channels since they share FPGA registers.
+ */
+static int set_pwm_config(const struct sr_dev_inst *sdi, size_t idx)
 {
-       static uint8_t ctrl_reg_tab[] = { REG_PWM1, REG_PWM2, };
+       static uint8_t reg_bases[] = { REG_PWM1, REG_PWM2, };
 
        struct dev_context *devc;
-       uint8_t ctrl_reg;
-       struct pwm_setting_dev cfg;
-       struct pwm_setting *setting;
+       struct pwm_setting *params;
+       uint8_t reg_base;
+       double val_f;
+       uint32_t val_u;
+       uint32_t period, duty;
+       size_t ch;
        int ret;
-       uint8_t buf[2 * sizeof(uint32_t)];
+       uint8_t enable_all, enable_cfg, reg_val;
+       uint8_t buf[REG_PWM2 - REG_PWM1]; /* Width of one REG_PWMx. */
        uint8_t *wrptr;
 
        devc = sdi->priv;
+       if (idx >= ARRAY_SIZE(devc->pwm_setting))
+               return SR_ERR_ARG;
+       params = &devc->pwm_setting[idx];
+       if (idx >= ARRAY_SIZE(reg_bases))
+               return SR_ERR_ARG;
+       reg_base = reg_bases[idx];
 
-       if (which < 1 || which > ARRAY_SIZE(ctrl_reg_tab)) {
-               sr_err("Invalid PWM channel: %d.", which);
-               return SR_ERR;
-       }
-       if (freq < 0 || freq > MAX_PWM_FREQ) {
-               sr_err("Too high a PWM frequency: %.1f.", freq);
-               return SR_ERR;
-       }
-       if (duty < 0 || duty > 100) {
-               sr_err("Invalid PWM duty cycle: %f.", duty);
-               return SR_ERR;
+       /*
+        * Map application's specs to hardware register values. Do math
+        * in floating point initially, but convert to u32 eventually.
+        */
+       sr_dbg("PWM config, app spec, ch %zu, en %d, freq %.1f, duty %.1f.",
+               idx, params->enabled ? 1 : 0, params->freq, params->duty);
+       val_f = PWM_CLOCK;
+       val_f /= params->freq;
+       val_u = val_f;
+       period = val_u;
+       val_f = period;
+       val_f *= params->duty;
+       val_f /= 100.0;
+       val_f += 0.5;
+       val_u = val_f;
+       duty = val_u;
+       sr_dbg("PWM config, reg 0x%04x, freq %u, duty %u.",
+               (unsigned)reg_base, (unsigned)period, (unsigned)duty);
+
+       /* Get the "enabled" state of all supported PWM channels. */
+       enable_all = 0;
+       for (ch = 0; ch < ARRAY_SIZE(devc->pwm_setting); ch++) {
+               if (!devc->pwm_setting[ch].enabled)
+                       continue;
+               enable_all |= 1U << ch;
        }
+       enable_cfg = 1U << idx;
+       sr_spew("PWM config, enable all 0x%02hhx, cfg 0x%02hhx.",
+               enable_all, enable_cfg);
 
-       memset(&cfg, 0, sizeof(cfg));
-       cfg.period = (uint32_t)(PWM_CLOCK / freq);
-       cfg.duty = (uint32_t)(0.5f + (cfg.period * duty / 100.));
-       sr_dbg("Set PWM%d period %d, duty %d.", which, cfg.period, cfg.duty);
-
-       ctrl_reg = ctrl_reg_tab[which - 1];
-       wrptr = buf;
-       write_u32le_inc(&wrptr, cfg.period);
-       write_u32le_inc(&wrptr, cfg.duty);
-       ret = ctrl_out(sdi, CMD_FPGA_SPI, ctrl_reg, 0, buf, wrptr - buf);
+       /*
+        * Disable the to-get-configured channel before its parameters
+        * will change. Or disable and exit when the channel is supposed
+        * to get turned off.
+        */
+       sr_spew("PWM config, disabling before param change.");
+       reg_val = enable_all & ~enable_cfg;
+       ret = ctrl_out(sdi, CMD_FPGA_SPI, REG_PWM_EN, 0,
+               &reg_val, sizeof(reg_val));
        if (ret != SR_OK) {
-               sr_err("Cannot setup PWM%d configuration %d %d.",
-                       which, cfg.period, cfg.duty);
+               sr_err("Cannot adjust PWM enabled state.");
                return ret;
        }
+       if (!params->enabled)
+               return SR_OK;
 
-       setting = &devc->pwm_setting[which - 1];
-       setting->freq = freq;
-       setting->duty = duty;
-
-       return SR_OK;
-}
-
-static int set_defaults(const struct sr_dev_inst *sdi)
-{
-       struct dev_context *devc;
-       int ret;
-
-       devc = sdi->priv;
-
-       ret = set_threshold_voltage(sdi, devc->threshold_voltage);
-       if (ret)
-               return ret;
-
-       ret = enable_pwm(sdi, FALSE, FALSE);
-       if (ret)
-               return ret;
-
-       ret = configure_pwm(sdi, 1, SR_KHZ(1), 50);
-       if (ret)
-               return ret;
-
-       ret = configure_pwm(sdi, 2, SR_KHZ(100), 50);
-       if (ret)
+       /* Write register values to device. */
+       sr_spew("PWM config, sending new parameters.");
+       wrptr = buf;
+       write_u32le_inc(&wrptr, period);
+       write_u32le_inc(&wrptr, duty);
+       ret = ctrl_out(sdi, CMD_FPGA_SPI, reg_base, 0, buf, wrptr - buf);
+       if (ret != SR_OK) {
+               sr_err("Cannot change PWM parameters.");
                return ret;
+       }
 
-       ret = enable_pwm(sdi, TRUE, TRUE);
-       if (ret)
+       /* Enable configured channel after write completion. */
+       sr_spew("PWM config, enabling after param change.");
+       reg_val = enable_all | enable_cfg;
+       ret = ctrl_out(sdi, CMD_FPGA_SPI, REG_PWM_EN, 0,
+               &reg_val, sizeof(reg_val));
+       if (ret != SR_OK) {
+               sr_err("Cannot adjust PWM enabled state.");
                return ret;
+       }
 
        return SR_OK;
 }
@@ -523,7 +518,12 @@ static int set_trigger_config(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct sr_trigger *trigger;
-       struct trigger_cfg cfg;
+       struct trigger_cfg {
+               uint32_t channels;
+               uint32_t enabled;
+               uint32_t level;
+               uint32_t high_or_falling;
+       } cfg;
        GSList *stages;
        GSList *channel;
        struct sr_trigger_stage *stage1;
@@ -615,7 +615,7 @@ static int set_trigger_config(const struct sr_dev_inst *sdi)
 static int set_sample_config(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
-       double clock_divisor;
+       uint64_t min_samplerate, eff_samplerate;
        uint16_t divider_u16;
        uint64_t limit_samples;
        uint64_t pre_trigger_samples;
@@ -631,17 +631,15 @@ static int set_sample_config(const struct sr_dev_inst *sdi)
                        devc->cur_samplerate);
                return SR_ERR_ARG;
        }
-       if (devc->cur_samplerate < MIN_SAMPLE_RATE_LA2016) {
+       min_samplerate = devc->model->samplerate;
+       min_samplerate /= 65536;
+       if (devc->cur_samplerate < min_samplerate) {
                sr_err("Too low a sample rate: %" PRIu64 ".",
                        devc->cur_samplerate);
                return SR_ERR_ARG;
        }
-
-       clock_divisor = devc->model->samplerate / (double)devc->cur_samplerate;
-       if (clock_divisor > 65535)
-               return SR_ERR_ARG;
-       divider_u16 = (uint16_t)(clock_divisor + 0.5);
-       devc->cur_samplerate = devc->model->samplerate / divider_u16;
+       divider_u16 = devc->model->samplerate / devc->cur_samplerate;
+       eff_samplerate = devc->model->samplerate / divider_u16;
 
        ret = sr_sw_limits_get_remain(&devc->sw_limits,
                &limit_samples, NULL, NULL, NULL);
@@ -650,12 +648,14 @@ static int set_sample_config(const struct sr_dev_inst *sdi)
                return ret;
        }
        if (limit_samples > LA2016_NUM_SAMPLES_MAX) {
-               sr_err("Too high a sample depth: %" PRIu64 ".", limit_samples);
-               return SR_ERR_ARG;
+               sr_warn("Too high a sample depth: %" PRIu64 ", capping.",
+                       limit_samples);
+               limit_samples = LA2016_NUM_SAMPLES_MAX;
        }
-       if (limit_samples < LA2016_NUM_SAMPLES_MIN) {
-               sr_err("Too low a sample depth: %" PRIu64 ".", limit_samples);
-               return SR_ERR_ARG;
+       if (limit_samples == 0) {
+               limit_samples = LA2016_NUM_SAMPLES_MAX;
+               sr_dbg("Passing %" PRIu64 " to HW for unlimited samples.",
+                       limit_samples);
        }
 
        /*
@@ -670,23 +670,36 @@ static int set_sample_config(const struct sr_dev_inst *sdi)
         * limit the amount of sample memory to use for pre-trigger
         * data. Only the upper 24 bits of that memory size spec get
         * communicated to the device (written to its FPGA register).
+        *
+        * TODO Determine whether the pre-trigger memory size gets
+        * specified in samples or in bytes. A previous implementation
+        * suggests bytes but this is suspicious when every other spec
+        * is in terms of samples.
         */
-       pre_trigger_samples = limit_samples * devc->capture_ratio / 100;
-       pre_trigger_memory = LA2016_PRE_MEM_LIMIT_BASE;
-       pre_trigger_memory *= devc->capture_ratio;
-       pre_trigger_memory /= 100;
+       if (devc->trigger_involved) {
+               pre_trigger_samples = limit_samples;
+               pre_trigger_samples *= devc->capture_ratio;
+               pre_trigger_samples /= 100;
+               pre_trigger_memory = devc->model->memory_bits;
+               pre_trigger_memory *= UINT64_C(1024 * 1024 * 1024);
+               pre_trigger_memory /= 8; /* devc->model->channel_count ? */
+               pre_trigger_memory *= devc->capture_ratio;
+               pre_trigger_memory /= 100;
+       } else {
+               sr_dbg("No trigger setup, skipping pre-trigger config.");
+               pre_trigger_samples = 1;
+               pre_trigger_memory = 0;
+       }
+       /* Ensure non-zero value after LSB shift out in HW reg. */
+       if (pre_trigger_memory < 0x100) {
+               pre_trigger_memory = 0x100;
+       }
 
        sr_dbg("Set sample config: %" PRIu64 "kHz, %" PRIu64 " samples.",
-               devc->cur_samplerate / 1000, limit_samples);
+               eff_samplerate / SR_KHZ(1), limit_samples);
        sr_dbg("Capture ratio %" PRIu64 "%%, count %" PRIu64 ", mem %" PRIu64 ".",
                devc->capture_ratio, pre_trigger_samples, pre_trigger_memory);
 
-       if (!devc->trigger_involved) {
-               sr_dbg("Voiding pre-trigger setup. No trigger involved.");
-               pre_trigger_samples = 1;
-               pre_trigger_memory = 0x100;
-       }
-
        /*
         * The acquisition configuration occupies a total of 16 bytes:
         * - A 34bit total samples count limit (up to 10 billions) that
@@ -1225,6 +1238,7 @@ SR_PRIV int la2016_identify_device(const struct sr_dev_inst *sdi,
 {
        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;
@@ -1242,13 +1256,23 @@ SR_PRIV int la2016_identify_device(const struct sr_dev_inst *sdi,
         * to 2020-04. This information can help identify the vintage of
         * devices when unknown magic numbers are seen.
         */
-       ret = ctrl_in(sdi, CMD_EEPROM, 0x20, 0, buf, 4 * sizeof(uint8_t));
+       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);
@@ -1298,18 +1322,27 @@ SR_PRIV int la2016_identify_device(const struct sr_dev_inst *sdi,
         * LA2016 by faking its EEPROM content.
         */
        devc->identify_magic = 0;
-       if ((ret = ctrl_in(sdi, CMD_EEPROM, 0x08, 0, &buf, sizeof(buf))) != SR_OK) {
+       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. */
-               sr_dbg("Using primary copy of device type magic number.");
                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. */
-               sr_dbg("Using backup copy of device type magic number.");
                magic = buf[4];
+               sr_dbg("Using backup magic, value %d.", (int)magic);
        } else {
                sr_err("Cannot find consistent device type identification.");
                magic = 0;
@@ -1322,16 +1355,15 @@ SR_PRIV int la2016_identify_device(const struct sr_dev_inst *sdi,
                if (model->magic != magic)
                        continue;
                devc->model = model;
-               sr_info("EEPROM magic %d, model '%s'.", (int)magic, model->name);
+               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("Max %zu channels at %" PRIu64 "MHz samplerate.",
-                       model->channel_count, model->samplerate / SR_MHZ(1));
                sr_info("FPGA bitstream file '%s'.", devc->fpga_bitstream);
                break;
        }
        if (!devc->model) {
-               sr_spew("Device type: magic number is %hhu.", magic);
                sr_err("Cannot identify as one of the supported models.");
                return SR_ERR;
        }
@@ -1339,7 +1371,7 @@ SR_PRIV int la2016_identify_device(const struct sr_dev_inst *sdi,
        return SR_OK;
 }
 
-SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
+SR_PRIV int la2016_init_hardware(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        const char *bitstream_fn;
@@ -1368,28 +1400,31 @@ SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
                sr_warn("Unexpected run state, want 0x85e9, got 0x%04x.", state);
        }
 
-       if ((ret = ctrl_out(sdi, CMD_BULK_RESET, 0x00, 0, NULL, 0)) != SR_OK) {
+       ret = ctrl_out(sdi, CMD_BULK_RESET, 0x00, 0, NULL, 0);
+       if (ret != SR_OK) {
                sr_err("Cannot reset USB bulk transfer.");
                return ret;
        }
 
        sr_dbg("Device should be initialized.");
 
-       ret = set_defaults(sdi);
-       if (ret != SR_OK)
-               return ret;
-
        return SR_OK;
 }
 
-SR_PRIV int la2016_deinit_device(const struct sr_dev_inst *sdi)
+SR_PRIV int la2016_deinit_hardware(const struct sr_dev_inst *sdi)
 {
        int ret;
 
-       if ((ret = ctrl_out(sdi, CMD_FPGA_ENABLE, 0x00, 0, NULL, 0)) != SR_OK) {
+       ret = ctrl_out(sdi, CMD_FPGA_ENABLE, 0x00, 0, NULL, 0);
+       if (ret != SR_OK) {
                sr_err("Cannot deinitialize device's FPGA.");
                return ret;
        }
 
        return SR_OK;
 }
+
+SR_PRIV int la2016_write_pwm_config(const struct sr_dev_inst *sdi, size_t idx)
+{
+       return set_pwm_config(sdi, idx);
+}