X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=blobdiff_plain;f=src%2Fhardware%2Fsysclk-lwla%2Fapi.c;h=c66cbeb06a6ab71a15fd45b935732dc4815277e6;hp=d093430d50dfec6027c27ca03f8467c5e58a0261;hb=dd5c48a6d567a3cac62c4b0058588273bbeea171;hpb=7ed808179f5542c903a476266c4b175a6f2a18a7 diff --git a/src/hardware/sysclk-lwla/api.c b/src/hardware/sysclk-lwla/api.c index d093430d..c66cbeb0 100644 --- a/src/hardware/sysclk-lwla/api.c +++ b/src/hardware/sysclk-lwla/api.c @@ -23,7 +23,7 @@ #include #include #include -#include "libsigrok-internal.h" +#include #include "protocol.h" /* Supported device scan options. @@ -61,13 +61,6 @@ static const char *const signal_edge_names[] = { [EDGE_NEGATIVE] = "f", }; -/* Initialize the SysClk LWLA driver. - */ -static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx) -{ - return std_init(sr_ctx, di, LOG_PREFIX); -} - /* Create a new sigrok device instance for the indicated LWLA model. */ static struct sr_dev_inst *dev_inst_new(const struct model_info *model) @@ -113,9 +106,8 @@ static struct sr_dev_inst *dev_inst_new_matching(GSList *conn_matches, const struct model_info *model; struct sr_dev_inst *sdi; struct libusb_device_descriptor des; - int bus, address; + int bus, address, ret; unsigned int vid, pid; - int ret; bus = libusb_get_bus_number(dev); address = libusb_get_device_address(dev); @@ -213,17 +205,6 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) return devices; } -/* Return the list of devices found during scan. - */ -static GSList *dev_list(const struct sr_dev_driver *di) -{ - struct drv_context *drvc; - - drvc = di->context; - - return drvc->instances; -} - /* Destroy the private device context. */ static void clear_dev_context(void *priv) @@ -234,7 +215,7 @@ static void clear_dev_context(void *priv) if (devc->acquisition) { sr_err("Cannot clear device context during acquisition!"); - return; /* leak and pray */ + return; /* Leak and pray. */ } sr_dbg("Device context cleared."); @@ -248,6 +229,39 @@ static int dev_clear(const struct sr_dev_driver *di) return std_dev_clear(di, &clear_dev_context); } +/* 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) +{ + 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)); + } + + return SR_OK; +} + /* Open and initialize device. */ static int dev_open(struct sr_dev_inst *sdi) @@ -255,59 +269,68 @@ 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 = sdi->driver->context; devc = sdi->priv; usb = sdi->conn; - if (!drvc) { - sr_err("Driver was not initialized."); - return SR_ERR; - } if (sdi->status != SR_ST_INACTIVE) { sr_err("Device already open."); return SR_ERR; } - ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb); - if (ret != SR_OK) - return ret; + /* 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; - /* Set the configuration twice to trigger a lightweight reset. - */ - ret = libusb_set_configuration(usb->devhdl, USB_CONFIG); - if (ret == 0) ret = libusb_set_configuration(usb->devhdl, USB_CONFIG); - if (ret != 0) { - sr_err("Failed to set USB configuration: %s.", - libusb_error_name(ret)); - sr_usb_close(usb); - return SR_ERR; - } + if (ret != LIBUSB_SUCCESS) { + sr_err("Failed to set USB configuration: %s.", + libusb_error_name(ret)); + sr_usb_close(usb); + return SR_ERR; + } - ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); - if (ret < 0) { - sr_err("Failed to claim interface: %s.", - libusb_error_name(ret)); - sr_usb_close(usb); - return SR_ERR; - } + 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; + } - sdi->status = SR_ST_ACTIVE; + 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); - devc->active_fpga_config = FPGA_NOCONF; - devc->state = STATE_IDLE; + sdi->status = SR_ST_ACTIVE; - ret = (*devc->model->apply_fpga_config)(sdi); + devc->active_fpga_config = FPGA_NOCONF; + devc->short_transfer_quirk = FALSE; + devc->state = STATE_IDLE; - if (ret == SR_OK) - ret = (*devc->model->device_init_check)(sdi); + ret = (*devc->model->apply_fpga_config)(sdi); - if (ret != SR_OK) { + if (ret == SR_OK) + ret = (*devc->model->device_init_check)(sdi); + if (ret == SR_OK) + break; + + /* Rinse and repeat. */ sdi->status = SR_ST_INACTIVE; 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; } @@ -315,19 +338,13 @@ static int dev_open(struct sr_dev_inst *sdi) */ static int dev_close(struct sr_dev_inst *sdi) { - struct drv_context *drvc; struct dev_context *devc; struct sr_usb_dev_inst *usb; int ret; - drvc = sdi->driver->context; devc = sdi->priv; usb = sdi->conn; - if (!drvc) { - sr_err("Driver was not initialized."); - return SR_ERR; - } if (sdi->status == SR_ST_INACTIVE) { sr_dbg("Device already closed."); return SR_OK; @@ -363,6 +380,7 @@ static int has_devopt(const struct model_info *model, uint32_t key) if ((model->devopts[i] & (SR_CONF_MASK | key)) == key) return TRUE; } + return FALSE; } @@ -440,10 +458,11 @@ static int lookup_index(GVariant *value, const char *const *table, int len) return -1; /* Linear search is fine for very small tables. */ - for (i = 0; i < len; ++i) { + for (i = 0; i < len; i++) { if (strcmp(entry, table[i]) == 0) return i; } + return -1; } @@ -557,16 +576,15 @@ static int config_channel_set(const struct sr_dev_inst *sdi, */ static int prepare_trigger_masks(const struct sr_dev_inst *sdi) { - uint64_t trigger_mask; - uint64_t trigger_values; - uint64_t trigger_edge_mask; - uint64_t channel_bit; + 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; @@ -588,33 +606,30 @@ static int prepare_trigger_masks(const struct sr_dev_inst *sdi) match = node->data; if (!match->channel->enabled) - continue; /* ignore disabled channel */ + continue; /* Ignore disabled channel. */ 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 */ + return SR_ERR_BUG; /* Should not happen. */ } - channel_bit = UINT64_C(1) << idx; - trigger_mask |= channel_bit; - - switch (match->match) { - case SR_TRIGGER_ZERO: - break; - case SR_TRIGGER_ONE: - trigger_values |= channel_bit; - break; - case SR_TRIGGER_RISING: - trigger_values |= channel_bit; - /* Fall through for edge mask. */ - case SR_TRIGGER_FALLING: - trigger_edge_mask |= channel_bit; - break; - default: + 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; @@ -667,8 +682,7 @@ static int config_list(uint32_t key, GVariant **data, if (key == SR_CONF_SCAN_OPTIONS) { *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, - scanopts, ARRAY_SIZE(scanopts), - sizeof(scanopts[0])); + scanopts, ARRAY_SIZE(scanopts), sizeof(scanopts[0])); return SR_OK; } if (!sdi) { @@ -677,8 +691,7 @@ static int config_list(uint32_t key, GVariant **data, /* List driver capabilities. */ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, - drvopts, ARRAY_SIZE(drvopts), - sizeof(drvopts[0])); + drvopts, ARRAY_SIZE(drvopts), sizeof(drvopts[0])); return SR_OK; } @@ -687,9 +700,8 @@ static int config_list(uint32_t key, GVariant **data, /* List the model's device options. */ if (key == SR_CONF_DEVICE_OPTIONS) { *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, - devc->model->devopts, - devc->model->num_devopts, - sizeof(devc->model->devopts[0])); + devc->model->devopts, devc->model->num_devopts, + sizeof(devc->model->devopts[0])); return SR_OK; } @@ -700,26 +712,24 @@ static int config_list(uint32_t key, GVariant **data, case SR_CONF_SAMPLERATE: g_variant_builder_init(&gvb, G_VARIANT_TYPE_VARDICT); gvar = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64, - devc->model->samplerates, - devc->model->num_samplerates, - sizeof(devc->model->samplerates[0])); + devc->model->samplerates, devc->model->num_samplerates, + sizeof(devc->model->samplerates[0])); 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(trigger_matches[0])); + trigger_matches, ARRAY_SIZE(trigger_matches), + sizeof(trigger_matches[0])); break; case SR_CONF_TRIGGER_SOURCE: *data = g_variant_new_strv(trigger_source_names, - ARRAY_SIZE(trigger_source_names)); + ARRAY_SIZE(trigger_source_names)); break; case SR_CONF_TRIGGER_SLOPE: case SR_CONF_CLOCK_EDGE: *data = g_variant_new_strv(signal_edge_names, - ARRAY_SIZE(signal_edge_names)); + ARRAY_SIZE(signal_edge_names)); break; default: /* Must not happen for a key listed in devopts. */ @@ -733,10 +743,8 @@ static int config_list(uint32_t key, GVariant **data, * 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) +static int dev_acquisition_start(const struct sr_dev_inst *sdi) { - (void)cb_data; - if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; @@ -747,11 +755,10 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) /* Request that a running capture operation be stopped. */ -static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) +static int dev_acquisition_stop(struct sr_dev_inst *sdi) { struct dev_context *devc; - (void)cb_data; devc = sdi->priv; if (sdi->status != SR_ST_ACTIVE) @@ -761,19 +768,20 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) devc->cancel_requested = TRUE; sr_dbg("Stopping acquisition."); } + return SR_OK; } /* SysClk LWLA driver descriptor. */ -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 = dev_clear, + .init = std_init, + .cleanup = std_cleanup, .scan = scan, - .dev_list = dev_list, + .dev_list = std_dev_list, .dev_clear = dev_clear, .config_get = config_get, .config_set = config_set, @@ -786,3 +794,4 @@ SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info = { .dev_acquisition_stop = dev_acquisition_stop, .context = NULL, }; +SR_REGISTER_DEV_DRIVER(sysclk_lwla_driver_info);