X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fkingst-la2016%2Fapi.c;h=93bc4d668132c2452ebbdbf9ed4e2178926ab807;hb=7047acc8e85b38713450e800037dbcddb1fa98ac;hp=d89d25618ef81d0af2dd5404d54455ababa8b949;hpb=d466f61cdcf2eb49dad3849a416e141da1435755;p=libsigrok.git diff --git a/src/hardware/kingst-la2016/api.c b/src/hardware/kingst-la2016/api.c index d89d2561..93bc4d66 100644 --- a/src/hardware/kingst-la2016/api.c +++ b/src/hardware/kingst-la2016/api.c @@ -1,6 +1,7 @@ /* * This file is part of the libsigrok project. * + * Copyright (C) 2022 Gerhard Sittig * Copyright (C) 2020 Florian Schmidt * Copyright (C) 2013 Marcus Comstedt * Copyright (C) 2013 Bert Vermeulen @@ -33,22 +34,13 @@ #include "libsigrok-internal.h" #include "protocol.h" -/* - * Default device configuration. Must be applicable to any of the - * supported devices (no model specific default values yet). Specific - * firmware implementation details unfortunately won't let us detect - * and keep using previously configured values. - */ -#define LA2016_DFLT_SAMPLERATE SR_MHZ(100) -#define LA2016_DFLT_SAMPLEDEPTH (5 * 1000 * 1000) -#define LA2016_DFLT_CAPT_RATIO 5 /* Capture ratio, in percent. */ - static const uint32_t scanopts[] = { SR_CONF_CONN, }; static const uint32_t drvopts[] = { SR_CONF_LOGIC_ANALYZER, + SR_CONF_SIGNAL_GENERATOR, }; static const uint32_t devopts[] = { @@ -57,13 +49,25 @@ static const uint32_t devopts[] = { SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, +#if WITH_THRESHOLD_DEVCFG SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, - SR_CONF_LOGIC_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, - SR_CONF_LOGIC_THRESHOLD_CUSTOM | SR_CONF_GET | SR_CONF_SET, +#endif SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, }; +static const uint32_t devopts_cg_logic[] = { +#if !WITH_THRESHOLD_DEVCFG + SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +#endif +}; + +static const uint32_t devopts_cg_pwm[] = { + SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, + SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET | SR_CONF_SET, + SR_CONF_DUTY_CYCLE | SR_CONF_GET | SR_CONF_SET, +}; + static const int32_t trigger_matches[] = { SR_TRIGGER_ZERO, SR_TRIGGER_ONE, @@ -78,6 +82,10 @@ static const char *channel_names_logic[] = { "CH24", "CH25", "CH26", "CH27", "CH28", "CH29", "CH30", "CH31", }; +static const char *channel_names_pwm[] = { + "PWM1", "PWM2", +}; + /* * The hardware uses a 100/200/500MHz base clock (model dependent) and * a 16bit divider (common across all models). The range from 10kHz to @@ -142,32 +150,38 @@ static const uint64_t rates_100mhz[] = { SR_MHZ(100), }; -static const float logic_threshold_value[] = { - 1.58, - 2.5, - 1.165, - 1.5, - 1.25, - 0.9, - 0.75, - 0.60, - 0.45, +/* + * Only list a few discrete voltages, to form a useful set which covers + * most logic families. Too many choices can make some applications use + * a slider again. Which may lack a scale for the current value, and + * leave users without feedback what the currently used value might be. + */ +static const double threshold_ranges[][2] = { + { 0.4, 0.4, }, + { 0.6, 0.6, }, + { 0.9, 0.9, }, + { 1.2, 1.2, }, + { 1.4, 1.4, }, /* Default, 1.4V, index 4. */ + { 2.0, 2.0, }, + { 2.5, 2.5, }, + { 4.0, 4.0, }, }; +#define LOGIC_THRESHOLD_IDX_DFLT 4 -static const char *logic_threshold[] = { - "TTL 5V", - "CMOS 5V", - "CMOS 3.3V", - "CMOS 3.0V", - "CMOS 2.5V", - "CMOS 1.8V", - "CMOS 1.5V", - "CMOS 1.2V", - "CMOS 0.9V", - "USER", -}; +static double threshold_voltage(const struct sr_dev_inst *sdi, double *high) +{ + struct dev_context *devc; + size_t idx; + double voltage; -#define LOGIC_THRESHOLD_IDX_USER (ARRAY_SIZE(logic_threshold) - 1) + devc = sdi->priv; + idx = devc->threshold_voltage_idx; + voltage = threshold_ranges[idx][0]; + if (high) + *high = threshold_ranges[idx][1]; + + return voltage; +} /* Convenience. Release an allocated devc from error paths. */ static void kingst_la2016_free_devc(struct dev_context *devc) @@ -259,7 +273,17 @@ static int la2016_identify_read(struct sr_dev_inst *sdi, sr_err("Cannot communicate to MCU firmware."); return ret; } + + /* + * Also complete the hardware configuration (FPGA bitstream) + * when MCU firmware communication became operational. Either + * failure is considered fatal when probing for the device. + */ ret = la2016_identify_device(sdi, show_message); + if (ret == SR_OK) { + ret = la2016_init_hardware(sdi); + } + la2016_close_usb(usb); return ret; @@ -408,6 +432,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) char conn_id[64]; int ret; size_t ch_off, ch_max; + struct sr_channel *ch; + struct sr_channel_group *cg; drvc = di->context; ctx = drvc->sr_ctx;; @@ -492,9 +518,10 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) * this device. */ devc->fw_uploaded = 0; + devc->usb_pid = pid; if (des.iProduct != LA2016_IPRODUCT_INDEX) { sr_info("Uploading MCU firmware to '%s'.", conn_id); - ret = la2016_upload_firmware(sdi, ctx, dev, pid); + ret = la2016_upload_firmware(sdi, ctx, dev, FALSE); if (ret != SR_OK) { sr_err("MCU firmware upload failed."); kingst_la2016_free_sdi(sdi); @@ -504,6 +531,13 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) usb->address = 0xff; renum_devices = g_slist_append(renum_devices, sdi); continue; + } else { + ret = la2016_upload_firmware(sdi, NULL, NULL, TRUE); + if (ret != SR_OK) { + sr_err("MCU firmware filename check failed."); + kingst_la2016_free_sdi(sdi); + continue; + } } /* @@ -556,23 +590,54 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) sdi->model = g_strdup(devc->model->name); ch_off = 0; - /* Create the logic channels. */ + /* Create the "Logic" channel group. */ ch_max = ARRAY_SIZE(channel_names_logic); if (ch_max > devc->model->channel_count) ch_max = devc->model->channel_count; + cg = sr_channel_group_new(sdi, "Logic", NULL); + devc->cg_logic = cg; for (ch_idx = 0; ch_idx < ch_max; ch_idx++) { - sr_channel_new(sdi, ch_off, + ch = sr_channel_new(sdi, ch_off, SR_CHANNEL_LOGIC, TRUE, channel_names_logic[ch_idx]); ch_off++; + cg->channels = g_slist_append(cg->channels, ch); + } + + /* Create the "PWMx" channel groups. */ + ch_max = ARRAY_SIZE(channel_names_pwm); + for (ch_idx = 0; ch_idx < ch_max; ch_idx++) { + const char *name; + name = channel_names_pwm[ch_idx]; + cg = sr_channel_group_new(sdi, name, NULL); + if (!devc->cg_pwm) + devc->cg_pwm = cg; + ch = sr_channel_new(sdi, ch_off, + SR_CHANNEL_ANALOG, FALSE, name); + ch_off++; + cg->channels = g_slist_append(cg->channels, ch); } + /* + * Ideally we'd get the previous configuration from the + * hardware, but this device is write-only. So we have + * to assign a fixed set of initial configuration values. + */ sr_sw_limits_init(&devc->sw_limits); - devc->sw_limits.limit_samples = LA2016_DFLT_SAMPLEDEPTH; - devc->capture_ratio = LA2016_DFLT_CAPT_RATIO; - devc->cur_samplerate = LA2016_DFLT_SAMPLERATE; - devc->threshold_voltage_idx = 0; - devc->threshold_voltage = logic_threshold_value[devc->threshold_voltage_idx]; + devc->sw_limits.limit_samples = 0; + devc->capture_ratio = 50; + devc->samplerate = devc->model->samplerate; + devc->threshold_voltage_idx = LOGIC_THRESHOLD_IDX_DFLT; + if (ARRAY_SIZE(devc->pwm_setting) >= 1) { + devc->pwm_setting[0].enabled = FALSE; + devc->pwm_setting[0].freq = SR_KHZ(1); + devc->pwm_setting[0].duty = 50; + } + if (ARRAY_SIZE(devc->pwm_setting) >= 2) { + devc->pwm_setting[1].enabled = FALSE; + devc->pwm_setting[1].freq = SR_KHZ(100); + devc->pwm_setting[1].duty = 50; + } sdi->status = SR_ST_INACTIVE; devices = g_slist_append(devices, sdi); @@ -584,7 +649,11 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) static int dev_open(struct sr_dev_inst *sdi) { + struct dev_context *devc; int ret; + size_t ch; + + devc = sdi->priv; ret = la2016_open_enum(sdi); if (ret != SR_OK) { @@ -592,6 +661,13 @@ static int dev_open(struct sr_dev_inst *sdi) return ret; } + /* Send most recent PWM configuration to the device. */ + for (ch = 0; ch < ARRAY_SIZE(devc->pwm_setting); ch++) { + ret = la2016_write_pwm_config(sdi, ch); + if (ret != SR_OK) + return ret; + } + return SR_OK; } @@ -604,7 +680,8 @@ static int dev_close(struct sr_dev_inst *sdi) if (!usb->devhdl) return SR_ERR_BUG; - la2016_deinit_device(sdi); + if (WITH_DEINIT_IN_CLOSE) + la2016_deinit_hardware(sdi); sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.", usb->bus, usb->address, sdi->connection_id, USB_INTERFACE); @@ -613,36 +690,123 @@ static int dev_close(struct sr_dev_inst *sdi) return SR_OK; } +/* Config API helper. Get type and index of a channel group. */ +static int get_cg_index(const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg, + int *type, size_t *logic, size_t *analog) +{ + struct dev_context *devc; + GSList *l; + size_t idx; + + /* Preset return values. */ + if (type) + *type = 0; + if (logic) + *logic = 0; + if (analog) + *analog = 0; + + /* Start categorizing the received cg. */ + if (!sdi) + return SR_ERR_ARG; + devc = sdi->priv; + if (!cg) + return SR_OK; + l = sdi->channel_groups; + + /* First sdi->channelgroups item is "Logic". */ + if (!l) + return SR_ERR_BUG; + if (cg == l->data) { + if (type) + *type = SR_CHANNEL_LOGIC; + if (logic) + *logic = 0; + return SR_OK; + } + l = l->next; + + /* Next sdi->channelgroups items are "PWMx". */ + idx = 0; + while (l && l->data != cg) { + idx++; + l = l->next; + } + if (l && idx < ARRAY_SIZE(devc->pwm_setting)) { + if (type) + *type = SR_CHANNEL_ANALOG; + if (analog) + *analog = idx; + return SR_OK; + } + + return SR_ERR_ARG; +} + 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, cg_type; + size_t logic_idx, analog_idx; + struct pwm_setting *pwm; struct sr_usb_dev_inst *usb; - double rounded; - const char *label; + double voltage, rounded; - (void)cg; + (void)rounded; + (void)voltage; if (!sdi) return SR_ERR_ARG; devc = sdi->priv; + /* Check for types (and index) of channel groups. */ + ret = get_cg_index(sdi, cg, &cg_type, &logic_idx, &analog_idx); + if (cg && ret != SR_OK) + return SR_ERR_ARG; + + /* Handle requests for the "Logic" channel group. */ + if (cg && cg_type == SR_CHANNEL_LOGIC) { + switch (key) { +#if !WITH_THRESHOLD_DEVCFG + case SR_CONF_VOLTAGE_THRESHOLD: + voltage = threshold_voltage(sdi, NULL); + *data = std_gvar_tuple_double(voltage, voltage); + break; +#endif /* WITH_THRESHOLD_DEVCFG */ + default: + return SR_ERR_NA; + } + return SR_OK; + } + + /* Handle requests for the "PWMx" channel groups. */ + if (cg && cg_type == SR_CHANNEL_ANALOG) { + pwm = &devc->pwm_setting[analog_idx]; + switch (key) { + case SR_CONF_ENABLED: + *data = g_variant_new_boolean(pwm->enabled); + break; + case SR_CONF_OUTPUT_FREQUENCY: + *data = g_variant_new_double(pwm->freq); + break; + case SR_CONF_DUTY_CYCLE: + *data = g_variant_new_double(pwm->duty); + break; + default: + return SR_ERR_NA; + } + return SR_OK; + } + switch (key) { case SR_CONF_CONN: - if (!sdi->conn) - return SR_ERR_ARG; usb = sdi->conn; - if (usb->address == 0xff) { - /* - * Device still needs to re-enumerate after firmware - * upload, so we don't know its (future) address. - */ - return SR_ERR; - } *data = g_variant_new_printf("%d.%d", usb->bus, usb->address); break; case SR_CONF_SAMPLERATE: - *data = g_variant_new_uint64(devc->cur_samplerate); + *data = g_variant_new_uint64(devc->samplerate); break; case SR_CONF_LIMIT_SAMPLES: case SR_CONF_LIMIT_MSEC: @@ -650,18 +814,12 @@ static int config_get(uint32_t key, GVariant **data, case SR_CONF_CAPTURE_RATIO: *data = g_variant_new_uint64(devc->capture_ratio); break; +#if WITH_THRESHOLD_DEVCFG case SR_CONF_VOLTAGE_THRESHOLD: - rounded = (int)(devc->threshold_voltage / 0.1) * 0.1; - *data = std_gvar_tuple_double(rounded, rounded + 0.1); - return SR_OK; - case SR_CONF_LOGIC_THRESHOLD: - label = logic_threshold[devc->threshold_voltage_idx]; - *data = g_variant_new_string(label); + voltage = threshold_voltage(sdi, NULL); + *data = std_gvar_tuple_double(voltage, voltage); break; - case SR_CONF_LOGIC_THRESHOLD_CUSTOM: - *data = g_variant_new_double(devc->threshold_voltage); - break; - +#endif /* WITH_THRESHOLD_DEVCFG */ default: return SR_ERR_NA; } @@ -673,16 +831,74 @@ 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; - double low, high; + int ret, cg_type; + size_t logic_idx, analog_idx; + struct pwm_setting *pwm; + double value_f; int idx; - (void)cg; - devc = sdi->priv; + /* Check for types (and index) of channel groups. */ + ret = get_cg_index(sdi, cg, &cg_type, &logic_idx, &analog_idx); + if (cg && ret != SR_OK) + return SR_ERR_ARG; + + /* Handle requests for the "Logic" channel group. */ + if (cg && cg_type == SR_CHANNEL_LOGIC) { + switch (key) { +#if !WITH_THRESHOLD_DEVCFG + case SR_CONF_LOGIC_THRESHOLD: + idx = std_double_tuple_idx(data, + ARRAY_AND_SIZE(threshold_ranges)); + if (idx < 0) + return SR_ERR_ARG; + devc->threshold_voltage_idx = idx; + break; +#endif /* WITH_THRESHOLD_DEVCFG */ + default: + return SR_ERR_NA; + } + return SR_OK; + } + + /* Handle requests for the "PWMx" channel groups. */ + if (cg && cg_type == SR_CHANNEL_ANALOG) { + pwm = &devc->pwm_setting[analog_idx]; + switch (key) { + case SR_CONF_ENABLED: + pwm->enabled = g_variant_get_boolean(data); + ret = la2016_write_pwm_config(sdi, analog_idx); + if (ret != SR_OK) + return ret; + break; + case SR_CONF_OUTPUT_FREQUENCY: + value_f = g_variant_get_double(data); + if (value_f <= 0.0 || value_f > MAX_PWM_FREQ) + return SR_ERR_ARG; + pwm->freq = value_f; + ret = la2016_write_pwm_config(sdi, analog_idx); + if (ret != SR_OK) + return ret; + break; + case SR_CONF_DUTY_CYCLE: + value_f = g_variant_get_double(data); + if (value_f <= 0.0 || value_f > 100.0) + return SR_ERR_ARG; + pwm->duty = value_f; + ret = la2016_write_pwm_config(sdi, analog_idx); + if (ret != SR_OK) + return ret; + break; + default: + return SR_ERR_NA; + } + return SR_OK; + } + switch (key) { case SR_CONF_SAMPLERATE: - devc->cur_samplerate = g_variant_get_uint64(data); + devc->samplerate = g_variant_get_uint64(data); break; case SR_CONF_LIMIT_SAMPLES: case SR_CONF_LIMIT_MSEC: @@ -690,24 +906,15 @@ static int config_set(uint32_t key, GVariant *data, case SR_CONF_CAPTURE_RATIO: devc->capture_ratio = g_variant_get_uint64(data); break; +#if WITH_THRESHOLD_DEVCFG case SR_CONF_VOLTAGE_THRESHOLD: - g_variant_get(data, "(dd)", &low, &high); - devc->threshold_voltage = (low + high) / 2.0; - devc->threshold_voltage_idx = LOGIC_THRESHOLD_IDX_USER; - break; - case SR_CONF_LOGIC_THRESHOLD: { - idx = std_str_idx(data, ARRAY_AND_SIZE(logic_threshold)); + idx = std_double_tuple_idx(data, + ARRAY_AND_SIZE(threshold_ranges)); if (idx < 0) return SR_ERR_ARG; - if (idx != LOGIC_THRESHOLD_IDX_USER) { - devc->threshold_voltage = logic_threshold_value[idx]; - } devc->threshold_voltage_idx = idx; break; - } - case SR_CONF_LOGIC_THRESHOLD_CUSTOM: - devc->threshold_voltage = g_variant_get_double(data); - break; +#endif /* WITH_THRESHOLD_DEVCFG */ default: return SR_ERR_NA; } @@ -719,9 +926,51 @@ 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; + int ret, cg_type; + size_t logic_idx, analog_idx; devc = sdi ? sdi->priv : NULL; + /* Check for types (and index) of channel groups. */ + ret = get_cg_index(sdi, cg, &cg_type, &logic_idx, &analog_idx); + if (cg && ret != SR_OK) + return SR_ERR_ARG; + + /* Handle requests for the "Logic" channel group. */ + if (cg && cg_type == SR_CHANNEL_LOGIC) { + switch (key) { + case SR_CONF_DEVICE_OPTIONS: + if (ARRAY_SIZE(devopts_cg_logic) == 0) + return SR_ERR_NA; + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts_cg_logic, ARRAY_SIZE(devopts_cg_logic), + sizeof(devopts_cg_logic[0])); + break; +#if !WITH_THRESHOLD_DEVCFG + case SR_CONF_VOLTAGE_THRESHOLD: + *data = std_gvar_thresholds(ARRAY_AND_SIZE(threshold_ranges)); + break; +#endif /* WITH_THRESHOLD_DEVCFG */ + default: + return SR_ERR_NA; + } + return SR_OK; + } + + /* Handle requests for the "PWMx" channel groups. */ + if (cg && cg_type == SR_CHANNEL_ANALOG) { + switch (key) { + case SR_CONF_DEVICE_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts_cg_pwm, ARRAY_SIZE(devopts_cg_pwm), + sizeof(devopts_cg_pwm[0])); + break; + default: + return SR_ERR_NA; + } + return SR_OK; + } + switch (key) { case SR_CONF_SCAN_OPTIONS: case SR_CONF_DEVICE_OPTIONS: @@ -734,24 +983,22 @@ static int config_list(uint32_t key, GVariant **data, *data = std_gvar_samplerates(ARRAY_AND_SIZE(rates_500mhz)); else if (devc->model->samplerate == SR_MHZ(200)) *data = std_gvar_samplerates(ARRAY_AND_SIZE(rates_200mhz)); - else + else if (devc->model->samplerate == SR_MHZ(100)) *data = std_gvar_samplerates(ARRAY_AND_SIZE(rates_100mhz)); + else + return SR_ERR_BUG; break; case SR_CONF_LIMIT_SAMPLES: - *data = std_gvar_tuple_u64(LA2016_NUM_SAMPLES_MIN, - LA2016_NUM_SAMPLES_MAX); + *data = std_gvar_tuple_u64(0, LA2016_NUM_SAMPLES_MAX); break; +#if WITH_THRESHOLD_DEVCFG case SR_CONF_VOLTAGE_THRESHOLD: - *data = std_gvar_min_max_step_thresholds( - LA2016_THR_VOLTAGE_MIN, - LA2016_THR_VOLTAGE_MAX, 0.1); + *data = std_gvar_thresholds(ARRAY_AND_SIZE(threshold_ranges)); break; +#endif /* WITH_THRESHOLD_DEVCFG */ case SR_CONF_TRIGGER_MATCH: *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); break; - case SR_CONF_LOGIC_THRESHOLD: - *data = g_variant_new_strv(ARRAY_AND_SIZE(logic_threshold)); - break; default: return SR_ERR_NA; } @@ -765,6 +1012,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) struct drv_context *drvc; struct sr_context *ctx; struct dev_context *devc; + size_t unitsize; + double voltage; int ret; di = sdi->driver; @@ -773,17 +1022,27 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) devc = sdi->priv; if (!devc->feed_queue) { + if (devc->model->channel_count == 32) + unitsize = sizeof(uint32_t); + else if (devc->model->channel_count == 16) + unitsize = sizeof(uint16_t); + else + return SR_ERR_ARG; devc->feed_queue = feed_queue_logic_alloc(sdi, - LA2016_CONVBUFFER_SIZE, sizeof(uint16_t)); + LA2016_CONVBUFFER_SIZE, unitsize); if (!devc->feed_queue) { sr_err("Cannot allocate buffer for session feed."); return SR_ERR_MALLOC; } + devc->packets_per_chunk = TRANSFER_PACKET_LENGTH; + devc->packets_per_chunk--; + devc->packets_per_chunk /= unitsize + sizeof(uint8_t); } sr_sw_limits_acquisition_start(&devc->sw_limits); - ret = la2016_setup_acquisition(sdi); + voltage = threshold_voltage(sdi, NULL); + ret = la2016_setup_acquisition(sdi, voltage); if (ret != SR_OK) { feed_queue_logic_free(devc->feed_queue); devc->feed_queue = NULL;