X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fsysclk-lwla%2Fapi.c;h=6055b83d4da6b6a46a65218c82918e7e75c6df43;hb=697fb6ddfc2522b2e6d35511c7837e2c61d8ae73;hp=1e3dd1d28ff6f725aa2d6b7571f5ebc3e7605e08;hpb=f57d8ffe66612a1fdc20ed09c222f8ea59bd84d4;p=libsigrok.git diff --git a/src/hardware/sysclk-lwla/api.c b/src/hardware/sysclk-lwla/api.c index 1e3dd1d2..6055b83d 100644 --- a/src/hardware/sysclk-lwla/api.c +++ b/src/hardware/sysclk-lwla/api.c @@ -17,28 +17,21 @@ * along with this program. If not, see . */ -#include "protocol.h" -#include "libsigrok.h" -#include "libsigrok-internal.h" +#include #include #include #include #include +#include +#include +#include "protocol.h" static const uint32_t scanopts[] = { SR_CONF_CONN, }; -static const uint32_t devopts[] = { +static const uint32_t drvopts[] = { SR_CONF_LOGIC_ANALYZER, - SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, - SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, - SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, - SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET, - SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, - SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, - SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, - SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, }; static const int32_t trigger_matches[] = { @@ -48,92 +41,111 @@ static const int32_t trigger_matches[] = { SR_TRIGGER_FALLING, }; -/* The hardware supports more samplerates than these, but these are the - * options hardcoded into the vendor's Windows GUI. - */ -static const uint64_t samplerates[] = { - SR_MHZ(125), SR_MHZ(100), - SR_MHZ(50), SR_MHZ(20), SR_MHZ(10), - SR_MHZ(5), SR_MHZ(2), SR_MHZ(1), - SR_KHZ(500), SR_KHZ(200), SR_KHZ(100), - SR_KHZ(50), SR_KHZ(20), SR_KHZ(10), - SR_KHZ(5), SR_KHZ(2), SR_KHZ(1), - SR_HZ(500), SR_HZ(200), SR_HZ(100), +static const char *trigger_source_names[] = { + [TRIGGER_CHANNELS] = "CH", + [TRIGGER_EXT_TRG] = "TRG", }; -/* Names assigned to available trigger sources. Indices must match - * trigger_source enum values. - */ -static const char *const trigger_source_names[] = { "CH", "TRG" }; - -/* Names assigned to available trigger slope choices. Indices must - * match the signal_edge enum values. - */ -static const char *const signal_edge_names[] = { "r", "f" }; - -SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info; -static struct sr_dev_driver *const di = &sysclk_lwla_driver_info; - -static int init(struct sr_context *sr_ctx) -{ - return std_init(sr_ctx, di, LOG_PREFIX); -} +static const char *signal_edge_names[] = { + [EDGE_POSITIVE] = "r", + [EDGE_NEGATIVE] = "f", +}; -static GSList *gen_channel_list(int num_channels) +static struct sr_dev_inst *dev_inst_new(const struct model_info *model) { - GSList *list; - struct sr_channel *ch; + struct sr_dev_inst *sdi; + struct dev_context *devc; int i; char name[8]; - list = NULL; + devc = g_malloc0(sizeof(struct dev_context)); + devc->model = model; + devc->active_fpga_config = FPGA_NOCONF; + devc->cfg_rle = TRUE; + devc->samplerate = model->samplerates[0]; + devc->channel_mask = (UINT64_C(1) << model->num_channels) - 1; - for (i = num_channels; i > 0; --i) { - /* The LWLA series simply number channels from CH1 to CHxx. */ - g_snprintf(name, sizeof(name), "CH%d", i); + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("SysClk"); + sdi->model = g_strdup(model->name); + sdi->priv = devc; - ch = sr_channel_new(i - 1, SR_CHANNEL_LOGIC, TRUE, name); - list = g_slist_prepend(list, ch); + for (i = 0; i < model->num_channels; i++) { + g_snprintf(name, sizeof(name), "CH%d", i + 1); + sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, name); } - return list; + return sdi; } -static struct sr_dev_inst *dev_inst_new(void) +/* Create a new device instance for a libusb device if it is a SysClk LWLA + * device and also matches the connection specification. + */ +static struct sr_dev_inst *dev_inst_new_matching(GSList *conn_matches, + libusb_device *dev) { + GSList *node; + struct sr_usb_dev_inst *usb; + const struct model_info *model; struct sr_dev_inst *sdi; - struct dev_context *devc; + struct libusb_device_descriptor des; + int bus, address, ret; + unsigned int vid, pid; - /* Allocate memory for our private driver context. */ - devc = g_malloc0(sizeof(struct dev_context)); + bus = libusb_get_bus_number(dev); + address = libusb_get_device_address(dev); - /* Register the device with libsigrok. */ - sdi = g_malloc0(sizeof(struct sr_dev_inst)); - sdi->status = SR_ST_INACTIVE; - sdi->vendor = g_strdup(VENDOR_NAME); - sdi->model = g_strdup(MODEL_NAME); + for (node = conn_matches; node != NULL; node = node->next) { + usb = node->data; + if (usb && usb->bus == bus && usb->address == address) + break; /* found */ + } + if (conn_matches && !node) + return NULL; /* no match */ - /* Enable all channels to match the default channel configuration. */ - devc->channel_mask = ALL_CHANNELS_MASK; - devc->samplerate = DEFAULT_SAMPLERATE; + ret = libusb_get_device_descriptor(dev, &des); + if (ret != 0) { + sr_err("Failed to get USB device descriptor: %s.", + libusb_error_name(ret)); + return NULL; + } + vid = des.idVendor; + pid = des.idProduct; + + /* Create sigrok device instance. */ + if (vid == USB_VID_SYSCLK && pid == USB_PID_LWLA1016) { + model = &lwla1016_info; + } else if (vid == USB_VID_SYSCLK && pid == USB_PID_LWLA1034) { + model = &lwla1034_info; + } else { + if (conn_matches) + sr_warn("USB device %d.%d (%04x:%04x) is not a" + " SysClk LWLA.", bus, address, vid, pid); + return NULL; + } + sdi = dev_inst_new(model); - sdi->priv = devc; - sdi->channels = gen_channel_list(NUM_CHANNELS); + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(bus, address, NULL); return sdi; } -static GSList *scan(GSList *options) +static GSList *scan(struct sr_dev_driver *di, GSList *options) { - GSList *usb_devices, *devices, *node; + GSList *conn_devices, *devices, *node; struct drv_context *drvc; struct sr_dev_inst *sdi; - struct sr_usb_dev_inst *usb; struct sr_config *src; const char *conn; + libusb_device **devlist; + ssize_t num_devs, i; - drvc = di->priv; - conn = USB_VID_PID; + drvc = di->context; + conn = NULL; + conn_devices = NULL; + devices = NULL; for (node = options; node != NULL; node = node->next) { src = node->data; @@ -142,131 +154,180 @@ static GSList *scan(GSList *options) break; } } - usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); - devices = NULL; + if (conn) { + /* Find devices matching the connection specification. */ + conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); + } - for (node = usb_devices; node != NULL; node = node->next) { - usb = node->data; + /* List all libusb devices. */ + num_devs = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + if (num_devs < 0) { + sr_err("Failed to list USB devices: %s.", + libusb_error_name(num_devs)); + g_slist_free_full(conn_devices, + (GDestroyNotify)&sr_usb_dev_inst_free); + return NULL; + } - /* Create sigrok device instance. */ - sdi = dev_inst_new(); - if (!sdi) { - sr_usb_dev_inst_free(usb); - continue; - } - sdi->driver = di; - sdi->inst_type = SR_INST_USB; - sdi->conn = usb; + /* Scan the USB device list for matching LWLA devices. */ + for (i = 0; i < num_devs; i++) { + sdi = dev_inst_new_matching(conn_devices, devlist[i]); + if (!sdi) + continue; /* no match */ /* Register device instance with driver. */ - drvc->instances = g_slist_append(drvc->instances, sdi); devices = g_slist_append(devices, sdi); } - g_slist_free(usb_devices); + libusb_free_device_list(devlist, 1); + g_slist_free_full(conn_devices, (GDestroyNotify)&sr_usb_dev_inst_free); - return devices; + return std_scan_complete(di, devices); } -static GSList *dev_list(void) -{ - struct drv_context *drvc; - - drvc = di->priv; - - return drvc->instances; -} - -static void clear_dev_context(void *priv) +/* Drain any pending data from the USB transfer buffers on the device. + * This may be necessary e.g. after a crash or generally to clean up after + * an abnormal condition. + */ +static int drain_usb(struct sr_usb_dev_inst *usb, unsigned int endpoint) { - struct dev_context *devc; - - devc = priv; - - sr_dbg("Device context cleared."); - - lwla_free_acquisition_state(devc->acquisition); - g_free(devc); -} + int drained, xfer_len, ret; + unsigned char buf[512]; + const unsigned int drain_timeout_ms = 10; + + drained = 0; + do { + xfer_len = 0; + ret = libusb_bulk_transfer(usb->devhdl, endpoint, + buf, sizeof(buf), &xfer_len, + drain_timeout_ms); + drained += xfer_len; + } while (ret == LIBUSB_SUCCESS && xfer_len != 0); + + if (ret != LIBUSB_SUCCESS && ret != LIBUSB_ERROR_TIMEOUT) { + sr_err("Failed to drain USB endpoint %u: %s.", + endpoint & (LIBUSB_ENDPOINT_IN - 1), + libusb_error_name(ret)); + return SR_ERR; + } + if (drained > 0) { + sr_warn("Drained %d bytes from USB endpoint %u.", + drained, endpoint & (LIBUSB_ENDPOINT_IN - 1)); + } -static int dev_clear(void) -{ - return std_dev_clear(di, &clear_dev_context); + return SR_OK; } static int dev_open(struct sr_dev_inst *sdi) { struct drv_context *drvc; + struct dev_context *devc; struct sr_usb_dev_inst *usb; - int ret; + int i, ret; - drvc = di->priv; + drvc = sdi->driver->context; + devc = sdi->priv; + usb = sdi->conn; - if (!drvc) { - sr_err("Driver was not initialized."); - return SR_ERR; - } + /* Try the whole shebang three times, fingers crossed. */ + for (i = 0; i < 3; i++) { + ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb); + if (ret != SR_OK) + return ret; + + ret = libusb_set_configuration(usb->devhdl, USB_CONFIG); + if (ret != LIBUSB_SUCCESS) { + sr_err("Failed to set USB configuration: %s.", + libusb_error_name(ret)); + sr_usb_close(usb); + return SR_ERR; + } - usb = sdi->conn; + ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); + if (ret != LIBUSB_SUCCESS) { + sr_err("Failed to claim interface: %s.", + libusb_error_name(ret)); + sr_usb_close(usb); + return SR_ERR; + } - ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb); - if (ret != SR_OK) - return ret; + ret = drain_usb(usb, EP_REPLY); + if (ret != SR_OK) { + sr_usb_close(usb); + return ret; + } + /* This delay appears to be necessary for reliable operation. */ + g_usleep(30 * 1000); - ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); - if (ret < 0) { - sr_err("Failed to claim interface: %s.", - libusb_error_name(ret)); - return SR_ERR; - } + devc->active_fpga_config = FPGA_NOCONF; + devc->short_transfer_quirk = FALSE; + devc->state = STATE_IDLE; - sdi->status = SR_ST_INITIALIZING; + ret = (*devc->model->apply_fpga_config)(sdi); - ret = lwla_init_device(sdi); + if (ret == SR_OK) + ret = (*devc->model->device_init_check)(sdi); + if (ret == SR_OK) + break; - if (ret == SR_OK) - sdi->status = SR_ST_ACTIVE; + /* Rinse and repeat. */ + sr_usb_close(usb); + } + if (ret == SR_OK && devc->short_transfer_quirk) + sr_warn("Short transfer quirk detected! " + "Memory reads will be slow."); return ret; } static int dev_close(struct sr_dev_inst *sdi) { + struct dev_context *devc; struct sr_usb_dev_inst *usb; + int ret; - if (!di->priv) { - sr_err("Driver was not initialized."); - return SR_ERR; - } - + devc = sdi->priv; usb = sdi->conn; - if (!usb->devhdl) - return SR_OK; - sdi->status = SR_ST_INACTIVE; + if (devc->acquisition) { + sr_err("Cannot close device during acquisition!"); + /* Request stop, leak handle, and prepare for the worst. */ + devc->cancel_requested = TRUE; + return SR_ERR_BUG; + } - /* Trigger download of the shutdown bitstream. */ - if (lwla_set_clock_config(sdi) != SR_OK) - sr_err("Unable to shut down device."); + /* Download of the shutdown bitstream, if any. */ + ret = (*devc->model->apply_fpga_config)(sdi); + if (ret != SR_OK) + sr_warn("Unable to shut down device."); libusb_release_interface(usb->devhdl, USB_INTERFACE); - libusb_close(usb->devhdl); - usb->devhdl = NULL; + sr_usb_close(usb); return SR_OK; } -static int cleanup(void) +/* Check whether the device options contain a specific key. + * Also match against get/set/list bits if specified. + */ +static int has_devopt(const struct model_info *model, uint32_t key) { - return dev_clear(); + unsigned int i; + + for (i = 0; i < model->num_devopts; i++) { + if ((model->devopts[i] & (SR_CONF_MASK | key)) == key) + return TRUE; + } + + return FALSE; } -static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel_group *cg) +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; - size_t idx; + unsigned int idx; (void)cg; @@ -275,6 +336,9 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s devc = sdi->priv; + if (!has_devopt(devc->model, key | SR_CONF_GET)) + return SR_ERR_NA; + switch (key) { case SR_CONF_SAMPLERATE: *data = g_variant_new_uint64(devc->samplerate); @@ -285,57 +349,41 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s case SR_CONF_LIMIT_SAMPLES: *data = g_variant_new_uint64(devc->limit_samples); break; + case SR_CONF_RLE: + *data = g_variant_new_boolean(devc->cfg_rle); + break; case SR_CONF_EXTERNAL_CLOCK: *data = g_variant_new_boolean(devc->cfg_clock_source == CLOCK_EXT_CLK); break; case SR_CONF_CLOCK_EDGE: idx = devc->cfg_clock_edge; - if (idx >= G_N_ELEMENTS(signal_edge_names)) + if (idx >= ARRAY_SIZE(signal_edge_names)) return SR_ERR_BUG; *data = g_variant_new_string(signal_edge_names[idx]); break; case SR_CONF_TRIGGER_SOURCE: idx = devc->cfg_trigger_source; - if (idx >= G_N_ELEMENTS(trigger_source_names)) + if (idx >= ARRAY_SIZE(trigger_source_names)) return SR_ERR_BUG; *data = g_variant_new_string(trigger_source_names[idx]); break; case SR_CONF_TRIGGER_SLOPE: idx = devc->cfg_trigger_slope; - if (idx >= G_N_ELEMENTS(signal_edge_names)) + if (idx >= ARRAY_SIZE(signal_edge_names)) return SR_ERR_BUG; *data = g_variant_new_string(signal_edge_names[idx]); break; default: - return SR_ERR_NA; + /* Must not happen for a key listed in devopts. */ + return SR_ERR_BUG; } return SR_OK; } -/* Helper for mapping a string-typed configuration value to an index - * within a table of possible values. - */ -static int lookup_index(GVariant *value, const char *const *table, int len) -{ - const char *entry; - int i; - - entry = g_variant_get_string(value, NULL); - if (!entry) - return -1; - - /* Linear search is fine for very small tables. */ - for (i = 0; i < len; ++i) { - if (strcmp(entry, table[i]) == 0) - return i; - } - return -1; -} - -static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, - const struct sr_channel_group *cg) +static int config_set(uint32_t key, GVariant *data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { uint64_t value; struct dev_context *devc; @@ -343,15 +391,19 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd (void)cg; + if (!sdi) + return SR_ERR_ARG; + devc = sdi->priv; - if (!devc) - return SR_ERR_DEV_CLOSED; + + if (!has_devopt(devc->model, key | SR_CONF_SET)) + return SR_ERR_NA; switch (key) { case SR_CONF_SAMPLERATE: value = g_variant_get_uint64(data); - if (value < samplerates[G_N_ELEMENTS(samplerates) - 1] - || value > samplerates[0]) + if (value < devc->model->samplerates[devc->model->num_samplerates - 1] + || value > devc->model->samplerates[0]) return SR_ERR_SAMPLERATE; devc->samplerate = value; break; @@ -367,56 +419,56 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd return SR_ERR_ARG; devc->limit_samples = value; break; + case SR_CONF_RLE: + devc->cfg_rle = g_variant_get_boolean(data); + break; case SR_CONF_EXTERNAL_CLOCK: devc->cfg_clock_source = (g_variant_get_boolean(data)) ? CLOCK_EXT_CLK : CLOCK_INTERNAL; break; case SR_CONF_CLOCK_EDGE: - idx = lookup_index(data, signal_edge_names, - G_N_ELEMENTS(signal_edge_names)); - if (idx < 0) + if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edge_names))) < 0) return SR_ERR_ARG; devc->cfg_clock_edge = idx; break; case SR_CONF_TRIGGER_SOURCE: - idx = lookup_index(data, trigger_source_names, - G_N_ELEMENTS(trigger_source_names)); - if (idx < 0) + if ((idx = std_str_idx(data, ARRAY_AND_SIZE(trigger_source_names))) < 0) return SR_ERR_ARG; devc->cfg_trigger_source = idx; break; case SR_CONF_TRIGGER_SLOPE: - idx = lookup_index(data, signal_edge_names, - G_N_ELEMENTS(signal_edge_names)); - if (idx < 0) + if ((idx = std_str_idx(data, ARRAY_AND_SIZE(signal_edge_names))) < 0) return SR_ERR_ARG; devc->cfg_trigger_slope = idx; break; default: - return SR_ERR_NA; + /* Must not happen for a key listed in devopts. */ + return SR_ERR_BUG; } return SR_OK; } static int config_channel_set(const struct sr_dev_inst *sdi, - struct sr_channel *ch, unsigned int changes) + struct sr_channel *ch, unsigned int changes) { uint64_t channel_bit; struct dev_context *devc; + if (!sdi) + return SR_ERR_ARG; + devc = sdi->priv; - if (!devc) - return SR_ERR_DEV_CLOSED; - if (ch->index < 0 || ch->index >= NUM_CHANNELS) { + if (ch->index < 0 || ch->index >= devc->model->num_channels) { sr_err("Channel index %d out of range.", ch->index); return SR_ERR_BUG; } - channel_bit = (uint64_t)1 << ch->index; if ((changes & SR_CHANNEL_SET_ENABLED) != 0) { - /* Enable or disable input channel for this channel. */ + channel_bit = UINT64_C(1) << ch->index; + + /* Enable or disable logic input for this channel. */ if (ch->enabled) devc->channel_mask |= channel_bit; else @@ -426,142 +478,169 @@ static int config_channel_set(const struct sr_dev_inst *sdi, return SR_OK; } -static int config_commit(const struct sr_dev_inst *sdi) +/* Derive trigger masks from the session's trigger configuration. */ +static int prepare_trigger_masks(const struct sr_dev_inst *sdi) { - if (sdi->status != SR_ST_ACTIVE) { - sr_err("Device not ready (status %d).", (int)sdi->status); - return SR_ERR; + uint64_t trigger_mask, trigger_values, trigger_edge_mask; + uint64_t level_bit, type_bit; + struct dev_context *devc; + struct sr_trigger *trigger; + struct sr_trigger_stage *stage; + struct sr_trigger_match *match; + const GSList *node; + int idx; + enum sr_trigger_matches trg; + + devc = sdi->priv; + + trigger = sr_session_trigger_get(sdi->session); + if (!trigger || !trigger->stages) + return SR_OK; + + if (trigger->stages->next) { + sr_err("This device only supports 1 trigger stage."); + return SR_ERR_ARG; } + stage = trigger->stages->data; - return lwla_set_clock_config(sdi); -} + trigger_mask = 0; + trigger_values = 0; + trigger_edge_mask = 0; -static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, - const struct sr_channel_group *cg) -{ - GVariant *gvar; - GVariantBuilder gvb; + for (node = stage->matches; node; node = node->next) { + match = node->data; - (void)sdi; - (void)cg; + if (!match->channel->enabled) + continue; /* Ignore disabled channel. */ - switch (key) { - case SR_CONF_SCAN_OPTIONS: - *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, - scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t)); - break; - case SR_CONF_DEVICE_OPTIONS: - *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, - devopts, G_N_ELEMENTS(devopts), sizeof(uint32_t)); - break; - case SR_CONF_SAMPLERATE: - g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); - gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), - samplerates, G_N_ELEMENTS(samplerates), - sizeof(uint64_t)); - g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar); - *data = g_variant_builder_end(&gvb); - break; - case SR_CONF_TRIGGER_MATCH: - *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, - trigger_matches, ARRAY_SIZE(trigger_matches), - sizeof(int32_t)); - break; - case SR_CONF_TRIGGER_SOURCE: - *data = g_variant_new_strv(trigger_source_names, - G_N_ELEMENTS(trigger_source_names)); - break; - case SR_CONF_TRIGGER_SLOPE: - case SR_CONF_CLOCK_EDGE: - *data = g_variant_new_strv(signal_edge_names, - G_N_ELEMENTS(signal_edge_names)); - break; - default: - return SR_ERR_NA; + idx = match->channel->index; + trg = match->match; + + if (idx < 0 || idx >= devc->model->num_channels) { + sr_err("Channel index %d out of range.", idx); + return SR_ERR_BUG; /* Should not happen. */ + } + if (trg != SR_TRIGGER_ZERO + && trg != SR_TRIGGER_ONE + && trg != SR_TRIGGER_RISING + && trg != SR_TRIGGER_FALLING) { + sr_err("Unsupported trigger match for CH%d.", idx + 1); + return SR_ERR_ARG; + } + level_bit = (trg == SR_TRIGGER_ONE + || trg == SR_TRIGGER_RISING) ? 1 : 0; + type_bit = (trg == SR_TRIGGER_RISING + || trg == SR_TRIGGER_FALLING) ? 1 : 0; + + trigger_mask |= UINT64_C(1) << idx; + trigger_values |= level_bit << idx; + trigger_edge_mask |= type_bit << idx; } + devc->trigger_mask = trigger_mask; + devc->trigger_values = trigger_values; + devc->trigger_edge_mask = trigger_edge_mask; return SR_OK; } -static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) +static int config_commit(const struct sr_dev_inst *sdi) { - struct drv_context *drvc; struct dev_context *devc; - struct acquisition_state *acq; int ret; - (void)cb_data; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; - devc = sdi->priv; - drvc = di->priv; if (devc->acquisition) { sr_err("Acquisition still in progress?"); return SR_ERR; } - acq = lwla_alloc_acquisition_state(); - if (!acq) - return SR_ERR_MALLOC; - - devc->stopping_in_progress = FALSE; - devc->transfer_error = FALSE; - sr_info("Starting acquisition."); + ret = prepare_trigger_masks(sdi); + if (ret != SR_OK) + return ret; - devc->acquisition = acq; - lwla_convert_trigger(sdi); - ret = lwla_setup_acquisition(sdi); + ret = (*devc->model->apply_fpga_config)(sdi); if (ret != SR_OK) { - sr_err("Failed to set up acquisition."); - devc->acquisition = NULL; - lwla_free_acquisition_state(acq); + sr_err("Failed to apply FPGA configuration."); return ret; } - ret = lwla_start_acquisition(sdi); - if (ret != SR_OK) { - sr_err("Failed to start acquisition."); - devc->acquisition = NULL; - lwla_free_acquisition_state(acq); - return ret; + 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) ? devc->model->devopts : NULL, + (devc) ? devc->model->num_devopts : 0); } - usb_source_add(sdi->session, drvc->sr_ctx, 100, &lwla_receive_data, - (struct sr_dev_inst *)sdi); - sr_info("Waiting for data."); + if (!has_devopt(devc->model, key | SR_CONF_LIST)) + return SR_ERR_NA; - /* Send header packet to the session bus. */ - std_session_send_df_header(sdi, LOG_PREFIX); + switch (key) { + case SR_CONF_SAMPLERATE: + *data = std_gvar_samplerates(devc->model->samplerates, devc->model->num_samplerates); + break; + case SR_CONF_TRIGGER_MATCH: + *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); + break; + case SR_CONF_TRIGGER_SOURCE: + *data = g_variant_new_strv(ARRAY_AND_SIZE(trigger_source_names)); + break; + case SR_CONF_TRIGGER_SLOPE: + case SR_CONF_CLOCK_EDGE: + *data = g_variant_new_strv(ARRAY_AND_SIZE(signal_edge_names)); + break; + default: + /* Must not happen for a key listed in devopts. */ + return SR_ERR_BUG; + } return SR_OK; } -static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) +/* Set up the device hardware to begin capturing samples as soon as the + * configured trigger conditions are met, or immediately if no triggers + * are configured. + */ +static int dev_acquisition_start(const struct sr_dev_inst *sdi) { - (void)cb_data; + return lwla_start_acquisition(sdi); +} - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; - sr_dbg("Stopping acquisition."); + devc = sdi->priv; - sdi->status = SR_ST_STOPPING; + if (devc->state != STATE_IDLE && !devc->cancel_requested) { + devc->cancel_requested = TRUE; + sr_dbg("Requesting cancel."); + } return SR_OK; } -SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info = { +static struct sr_dev_driver sysclk_lwla_driver_info = { .name = "sysclk-lwla", .longname = "SysClk LWLA series", .api_version = 1, - .init = init, - .cleanup = cleanup, + .init = std_init, + .cleanup = std_cleanup, .scan = scan, - .dev_list = dev_list, - .dev_clear = dev_clear, + .dev_list = std_dev_list, + .dev_clear = std_dev_clear, .config_get = config_get, .config_set = config_set, .config_channel_set = config_channel_set, @@ -571,5 +650,6 @@ SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info = { .dev_close = dev_close, .dev_acquisition_start = dev_acquisition_start, .dev_acquisition_stop = dev_acquisition_stop, - .priv = NULL, + .context = NULL, }; +SR_REGISTER_DEV_DRIVER(sysclk_lwla_driver_info);