libsigrok/hardware/fx2lafw/api.c

565 lines
13 KiB
C
Raw Normal View History

/*
2013-04-23 20:24:30 +00:00
* This file is part of the libsigrok project.
*
2013-03-24 10:21:00 +00:00
* Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
* Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "protocol.h"
2012-03-12 21:06:45 +00:00
static const struct fx2lafw_profile supported_fx2[] = {
2012-03-25 20:02:19 +00:00
/*
* CWAV USBee AX
2012-03-12 21:03:13 +00:00
* EE Electronics ESLA201A
* ARMFLY AX-Pro
2012-03-12 21:03:13 +00:00
*/
{ 0x08a9, 0x0014, "CWAV", "USBee AX", NULL,
FIRMWARE_DIR "/fx2lafw-cwav-usbeeax.fw",
0 },
/*
* CWAV USBee DX
* XZL-Studio DX
*/
{ 0x08a9, 0x0015, "CWAV", "USBee DX", NULL,
FIRMWARE_DIR "/fx2lafw-cwav-usbeedx.fw",
DEV_CAPS_16BIT },
2012-03-25 20:02:19 +00:00
/*
* CWAV USBee SX
*/
{ 0x08a9, 0x0009, "CWAV", "USBee SX", NULL,
FIRMWARE_DIR "/fx2lafw-cwav-usbeesx.fw",
0 },
2012-03-25 20:02:19 +00:00
/*
* Saleae Logic
* EE Electronics ESLA100
* Robomotic MiniLogic
* Robomotic BugLogic 3
*/
{ 0x0925, 0x3881, "Saleae", "Logic", NULL,
FIRMWARE_DIR "/fx2lafw-saleae-logic.fw",
0 },
/*
* Default Cypress FX2 without EEPROM, e.g.:
* Lcsoft Mini Board
* Braintechnology USB Interface V2.x
*/
{ 0x04B4, 0x8613, "Cypress", "FX2", NULL,
FIRMWARE_DIR "/fx2lafw-cypress-fx2.fw",
DEV_CAPS_16BIT },
/*
* Braintechnology USB-LPS
*/
{ 0x16d0, 0x0498, "Braintechnology", "USB-LPS", NULL,
FIRMWARE_DIR "/fx2lafw-braintechnology-usb-lps.fw",
DEV_CAPS_16BIT },
{ 0, 0, 0, 0, 0, 0, 0 }
};
static const int32_t hwopts[] = {
SR_CONF_CONN,
};
static const int32_t hwcaps[] = {
SR_CONF_LOGIC_ANALYZER,
SR_CONF_TRIGGER_TYPE,
SR_CONF_SAMPLERATE,
2012-02-11 17:39:39 +00:00
/* These are really implemented in the driver, not the hardware. */
SR_CONF_LIMIT_SAMPLES,
SR_CONF_CONTINUOUS,
2012-02-11 17:39:39 +00:00
};
static const char *channel_names[] = {
2012-12-11 20:51:53 +00:00
"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15",
NULL,
2012-02-11 17:39:39 +00:00
};
static const uint64_t samplerates[] = {
SR_KHZ(20),
SR_KHZ(25),
SR_KHZ(50),
SR_KHZ(100),
2012-04-18 18:07:30 +00:00
SR_KHZ(200),
SR_KHZ(250),
SR_KHZ(500),
2012-02-11 17:39:39 +00:00
SR_MHZ(1),
SR_MHZ(2),
SR_MHZ(3),
SR_MHZ(4),
SR_MHZ(6),
SR_MHZ(8),
SR_MHZ(12),
SR_MHZ(16),
2012-02-27 22:40:38 +00:00
SR_MHZ(24),
2012-02-11 17:39:39 +00:00
};
SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
static struct sr_dev_driver *di = &fx2lafw_driver_info;
static int init(struct sr_context *sr_ctx)
{
return std_init(sr_ctx, di, LOG_PREFIX);
}
static GSList *scan(GSList *options)
{
struct drv_context *drvc;
struct dev_context *devc;
struct sr_dev_inst *sdi;
struct sr_usb_dev_inst *usb;
struct sr_channel *ch;
struct sr_config *src;
const struct fx2lafw_profile *prof;
GSList *l, *devices, *conn_devices;
struct libusb_device_descriptor des;
libusb_device **devlist;
int devcnt, num_logic_channels, ret, i, j;
const char *conn;
2012-07-08 17:03:36 +00:00
drvc = di->priv;
conn = NULL;
for (l = options; l; l = l->next) {
src = l->data;
switch (src->key) {
case SR_CONF_CONN:
conn = g_variant_get_string(src->data, NULL);
break;
}
}
if (conn)
conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
else
conn_devices = NULL;
2012-05-30 20:42:19 +00:00
/* Find all fx2lafw compatible devices and upload firmware to them. */
2012-07-08 17:03:36 +00:00
devices = NULL;
libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
for (i = 0; devlist[i]; i++) {
if (conn) {
usb = NULL;
for (l = conn_devices; l; l = l->next) {
usb = l->data;
if (usb->bus == libusb_get_bus_number(devlist[i])
&& usb->address == libusb_get_device_address(devlist[i]))
break;
}
if (!l)
/* This device matched none of the ones that
* matched the conn specification. */
continue;
}
if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) {
sr_warn("Failed to get device descriptor: %s.",
libusb_error_name(ret));
continue;
}
2012-03-29 06:55:12 +00:00
prof = NULL;
for (j = 0; supported_fx2[j].vid; j++) {
if (des.idVendor == supported_fx2[j].vid &&
des.idProduct == supported_fx2[j].pid) {
2012-03-29 06:55:12 +00:00
prof = &supported_fx2[j];
}
}
/* Skip if the device was not found. */
2012-03-29 06:55:12 +00:00
if (!prof)
continue;
devcnt = g_slist_length(drvc->instances);
sdi = sr_dev_inst_new(devcnt, SR_ST_INITIALIZING,
2012-03-29 06:55:12 +00:00
prof->vendor, prof->model, prof->model_version);
2012-03-25 13:20:55 +00:00
if (!sdi)
return NULL;
sdi->driver = di;
/* Fill in channellist according to this device's profile. */
num_logic_channels = prof->dev_caps & DEV_CAPS_16BIT ? 16 : 8;
for (j = 0; j < num_logic_channels; j++) {
if (!(ch = sr_channel_new(j, SR_CHANNEL_LOGIC, TRUE,
channel_names[j])))
return NULL;
sdi->channels = g_slist_append(sdi->channels, ch);
}
devc = fx2lafw_dev_new();
devc->profile = prof;
sdi->priv = devc;
drvc->instances = g_slist_append(drvc->instances, sdi);
devices = g_slist_append(devices, sdi);
if (fx2lafw_check_conf_profile(devlist[i])) {
2012-02-25 11:58:55 +00:00
/* Already has the firmware, so fix the new address. */
sr_dbg("Found an fx2lafw device.");
2012-02-25 11:58:55 +00:00
sdi->status = SR_ST_INACTIVE;
sdi->inst_type = SR_INST_USB;
sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
libusb_get_device_address(devlist[i]), NULL);
2012-02-25 11:58:55 +00:00
} else {
if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION,
2012-03-29 06:55:12 +00:00
prof->firmware) == SR_OK)
/* Store when this device's FW was updated. */
devc->fw_updated = g_get_monotonic_time();
2012-02-25 11:58:55 +00:00
else
sr_err("Firmware upload failed for "
2012-03-25 20:02:19 +00:00
"device %d.", devcnt);
sdi->inst_type = SR_INST_USB;
2013-12-29 13:09:34 +00:00
sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
0xff, NULL);
2012-02-25 11:58:55 +00:00
}
}
libusb_free_device_list(devlist, 1);
2013-05-06 18:44:26 +00:00
g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
2012-07-08 17:03:36 +00:00
return devices;
}
static GSList *dev_list(void)
{
return ((struct drv_context *)(di->priv))->instances;
}
static int dev_open(struct sr_dev_inst *sdi)
{
struct sr_usb_dev_inst *usb;
struct dev_context *devc;
int ret;
int64_t timediff_us, timediff_ms;
2012-02-26 12:39:40 +00:00
devc = sdi->priv;
usb = sdi->conn;
2012-02-26 12:39:40 +00:00
/*
* If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS
* milliseconds for the FX2 to renumerate.
2012-02-26 12:39:40 +00:00
*/
ret = SR_ERR;
if (devc->fw_updated > 0) {
sr_info("Waiting for device to reset.");
/* Takes >= 300ms for the FX2 to be gone from the USB bus. */
2012-02-26 12:39:40 +00:00
g_usleep(300 * 1000);
timediff_ms = 0;
while (timediff_ms < MAX_RENUM_DELAY_MS) {
if ((ret = fx2lafw_dev_open(sdi, di)) == SR_OK)
2012-02-26 12:39:40 +00:00
break;
g_usleep(100 * 1000);
timediff_us = g_get_monotonic_time() - devc->fw_updated;
timediff_ms = timediff_us / 1000;
sr_spew("Waited %" PRIi64 "ms.", timediff_ms);
2012-02-26 12:39:40 +00:00
}
if (ret != SR_OK) {
sr_err("Device failed to renumerate.");
return SR_ERR;
}
2012-12-18 00:30:34 +00:00
sr_info("Device came back after %" PRIi64 "ms.", timediff_ms);
2012-02-26 12:39:40 +00:00
} else {
sr_info("Firmware upload was not needed.");
ret = fx2lafw_dev_open(sdi, di);
2012-02-26 12:39:40 +00:00
}
if (ret != SR_OK) {
sr_err("Unable to open device.");
2012-02-26 12:39:40 +00:00
return SR_ERR;
}
ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
if (ret != 0) {
2013-12-29 13:09:34 +00:00
switch (ret) {
case LIBUSB_ERROR_BUSY:
sr_err("Unable to claim USB interface. Another "
"program or driver has already claimed it.");
break;
case LIBUSB_ERROR_NO_DEVICE:
sr_err("Device has been disconnected.");
break;
default:
sr_err("Unable to claim interface: %s.",
libusb_error_name(ret));
break;
}
2012-02-26 12:39:40 +00:00
return SR_ERR;
}
if (devc->cur_samplerate == 0) {
/* Samplerate hasn't been set; default to the slowest one. */
devc->cur_samplerate = samplerates[0];
}
return SR_OK;
}
static int dev_close(struct sr_dev_inst *sdi)
{
struct sr_usb_dev_inst *usb;
usb = sdi->conn;
if (usb->devhdl == NULL)
return SR_ERR;
2012-02-27 19:00:22 +00:00
sr_info("fx2lafw: Closing device %d on %d.%d interface %d.",
sdi->index, usb->bus, usb->address, USB_INTERFACE);
libusb_release_interface(usb->devhdl, USB_INTERFACE);
libusb_close(usb->devhdl);
usb->devhdl = NULL;
sdi->status = SR_ST_INACTIVE;
2012-02-27 19:00:22 +00:00
return SR_OK;
}
static int cleanup(void)
{
int ret;
struct drv_context *drvc;
if (!(drvc = di->priv))
return SR_OK;
ret = std_dev_clear(di, NULL);
g_free(drvc);
di->priv = NULL;
2012-02-27 19:02:52 +00:00
return ret;
}
static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg)
{
struct dev_context *devc;
struct sr_usb_dev_inst *usb;
char str[128];
2012-02-11 17:39:39 +00:00
(void)cg;
switch (id) {
case SR_CONF_CONN:
if (!sdi || !sdi->conn)
return SR_ERR_ARG;
usb = sdi->conn;
if (usb->address == 255)
/* Device still needs to re-enumerate after firmware
* upload, so we don't know its (future) address. */
return SR_ERR;
snprintf(str, 128, "%d.%d", usb->bus, usb->address);
*data = g_variant_new_string(str);
break;
case SR_CONF_SAMPLERATE:
if (!sdi)
return SR_ERR;
devc = sdi->priv;
*data = g_variant_new_uint64(devc->cur_samplerate);
break;
default:
return SR_ERR_NA;
2012-02-11 17:39:39 +00:00
}
return SR_OK;
}
static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg)
{
struct dev_context *devc;
int ret;
(void)cg;
if (sdi->status != SR_ST_ACTIVE)
return SR_ERR;
devc = sdi->priv;
if (id == SR_CONF_SAMPLERATE) {
devc->cur_samplerate = g_variant_get_uint64(data);
ret = SR_OK;
} else if (id == SR_CONF_LIMIT_SAMPLES) {
devc->limit_samples = g_variant_get_uint64(data);
ret = SR_OK;
} else {
ret = SR_ERR_NA;
}
return ret;
}
static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg)
2013-01-25 01:32:05 +00:00
{
GVariant *gvar;
GVariantBuilder gvb;
2013-01-25 01:32:05 +00:00
(void)sdi;
(void)cg;
2013-01-25 01:32:05 +00:00
switch (key) {
case SR_CONF_SCAN_OPTIONS:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
break;
case SR_CONF_DEVICE_OPTIONS:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
break;
2013-01-25 01:32:05 +00:00
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,
ARRAY_SIZE(samplerates), sizeof(uint64_t));
g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
*data = g_variant_builder_end(&gvb);
2013-01-25 01:32:05 +00:00
break;
case SR_CONF_TRIGGER_TYPE:
*data = g_variant_new_string(TRIGGER_TYPE);
break;
2013-01-25 01:32:05 +00:00
default:
return SR_ERR_NA;
2013-01-25 01:32:05 +00:00
}
return SR_OK;
}
2012-02-29 21:32:34 +00:00
static int receive_data(int fd, int revents, void *cb_data)
{
struct timeval tv;
struct drv_context *drvc;
(void)fd;
(void)revents;
2012-02-29 21:32:34 +00:00
(void)cb_data;
drvc = di->priv;
tv.tv_sec = tv.tv_usec = 0;
libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
return TRUE;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
{
struct dev_context *devc;
struct drv_context *drvc;
struct sr_usb_dev_inst *usb;
struct libusb_transfer *transfer;
unsigned int i, timeout, num_transfers;
sr: fx2lafw: Calculate buffer size and timeout based on sample rate Currently timeout and buffer size are hard-coded in the fx2lafw driver which is non-optimal if we want to get good results at both high and low sample rates. The timeout is hard-coded to 40ms, which doesn't work well when sampling at a low sample rate. E.g. at 20kHz filling all available buffer space alone takes 6 seconds. So naturally we'll see a lot of transfers timeout in this case. The buffer size is hard-coded to 4096 bytes, which does not work well with high sample rates. E.g. at 24MHz these 4096 bytes are enough space for 0.17ms of data. The total buffer size is enough for about 5ms of data. Sooner or later the application won't be able to resubmit a transfer within this time span and the device will abort data acquisition. Usually this happens within the first few seconds of sampling. This patch adds a few new helper functions which calculate the buffer size and timeout based on the current sample rate. The buffer size is chosen to be large enough to hold about 10ms of data and it also must be a multiple of 512 bytes since the firmware will send us the data in 512 byte chunks. The timeout is set to the time it would take to fill the whole available buffer space plus a 25% headroom to accommodate for jitter. This is more than enough, but there is no need to make the timeout a tight deadline, since it is only meant as a last resort in case the device stops submitting data. And in this case data acquisition will be aborted anyway. The patch also limits the the number of transfers so that the total buffer space is not much more of 500ms. This will ensure that we do not have to wait too long when aborting data acquisition. This patch also significantly reduces the number of context switches when sampling at a higher sample rate. On my system for example the CPU load of sigrok-cli when sampling at 24MHz goes down from ~25% to 3-4%. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2012-06-25 20:15:59 +00:00
int ret;
unsigned char *buf;
size_t size;
if (sdi->status != SR_ST_ACTIVE)
return SR_ERR_DEV_CLOSED;
drvc = di->priv;
devc = sdi->priv;
usb = sdi->conn;
/* Configures devc->trigger_* and devc->sample_wide */
if (fx2lafw_configure_channels(sdi) != SR_OK) {
sr_err("Failed to configure channels.");
return SR_ERR;
}
devc->cb_data = cb_data;
devc->num_samples = 0;
devc->empty_transfer_count = 0;
timeout = fx2lafw_get_timeout(devc);
num_transfers = fx2lafw_get_number_of_transfers(devc);
size = fx2lafw_get_buffer_size(devc);
devc->submitted_transfers = 0;
sr: fx2lafw: Calculate buffer size and timeout based on sample rate Currently timeout and buffer size are hard-coded in the fx2lafw driver which is non-optimal if we want to get good results at both high and low sample rates. The timeout is hard-coded to 40ms, which doesn't work well when sampling at a low sample rate. E.g. at 20kHz filling all available buffer space alone takes 6 seconds. So naturally we'll see a lot of transfers timeout in this case. The buffer size is hard-coded to 4096 bytes, which does not work well with high sample rates. E.g. at 24MHz these 4096 bytes are enough space for 0.17ms of data. The total buffer size is enough for about 5ms of data. Sooner or later the application won't be able to resubmit a transfer within this time span and the device will abort data acquisition. Usually this happens within the first few seconds of sampling. This patch adds a few new helper functions which calculate the buffer size and timeout based on the current sample rate. The buffer size is chosen to be large enough to hold about 10ms of data and it also must be a multiple of 512 bytes since the firmware will send us the data in 512 byte chunks. The timeout is set to the time it would take to fill the whole available buffer space plus a 25% headroom to accommodate for jitter. This is more than enough, but there is no need to make the timeout a tight deadline, since it is only meant as a last resort in case the device stops submitting data. And in this case data acquisition will be aborted anyway. The patch also limits the the number of transfers so that the total buffer space is not much more of 500ms. This will ensure that we do not have to wait too long when aborting data acquisition. This patch also significantly reduces the number of context switches when sampling at a higher sample rate. On my system for example the CPU load of sigrok-cli when sampling at 24MHz goes down from ~25% to 3-4%. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2012-06-25 20:15:59 +00:00
devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
if (!devc->transfers) {
sr_err("USB transfers malloc failed.");
return SR_ERR_MALLOC;
}
devc->num_transfers = num_transfers;
sr: fx2lafw: Calculate buffer size and timeout based on sample rate Currently timeout and buffer size are hard-coded in the fx2lafw driver which is non-optimal if we want to get good results at both high and low sample rates. The timeout is hard-coded to 40ms, which doesn't work well when sampling at a low sample rate. E.g. at 20kHz filling all available buffer space alone takes 6 seconds. So naturally we'll see a lot of transfers timeout in this case. The buffer size is hard-coded to 4096 bytes, which does not work well with high sample rates. E.g. at 24MHz these 4096 bytes are enough space for 0.17ms of data. The total buffer size is enough for about 5ms of data. Sooner or later the application won't be able to resubmit a transfer within this time span and the device will abort data acquisition. Usually this happens within the first few seconds of sampling. This patch adds a few new helper functions which calculate the buffer size and timeout based on the current sample rate. The buffer size is chosen to be large enough to hold about 10ms of data and it also must be a multiple of 512 bytes since the firmware will send us the data in 512 byte chunks. The timeout is set to the time it would take to fill the whole available buffer space plus a 25% headroom to accommodate for jitter. This is more than enough, but there is no need to make the timeout a tight deadline, since it is only meant as a last resort in case the device stops submitting data. And in this case data acquisition will be aborted anyway. The patch also limits the the number of transfers so that the total buffer space is not much more of 500ms. This will ensure that we do not have to wait too long when aborting data acquisition. This patch also significantly reduces the number of context switches when sampling at a higher sample rate. On my system for example the CPU load of sigrok-cli when sampling at 24MHz goes down from ~25% to 3-4%. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2012-06-25 20:15:59 +00:00
for (i = 0; i < num_transfers; i++) {
if (!(buf = g_try_malloc(size))) {
sr_err("USB transfer buffer malloc failed.");
return SR_ERR_MALLOC;
}
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, usb->devhdl,
2 | LIBUSB_ENDPOINT_IN, buf, size,
fx2lafw_receive_transfer, devc, timeout);
if ((ret = libusb_submit_transfer(transfer)) != 0) {
sr_err("Failed to submit transfer: %s.",
libusb_error_name(ret));
libusb_free_transfer(transfer);
g_free(buf);
fx2lafw_abort_acquisition(devc);
return SR_ERR;
}
devc->transfers[i] = transfer;
devc->submitted_transfers++;
}
devc->ctx = drvc->sr_ctx;
usb_source_add(devc->ctx, timeout, receive_data, NULL);
/* Send header packet to the session bus. */
std_session_send_df_header(cb_data, LOG_PREFIX);
if ((ret = fx2lafw_command_start_acquisition(usb->devhdl,
devc->cur_samplerate, devc->sample_wide)) != SR_OK) {
fx2lafw_abort_acquisition(devc);
return ret;
}
return SR_OK;
}
static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
{
(void)cb_data;
fx2lafw_abort_acquisition(sdi->priv);
return SR_OK;
}
2012-02-28 22:52:30 +00:00
SR_PRIV struct sr_dev_driver fx2lafw_driver_info = {
.name = "fx2lafw",
.longname = "fx2lafw (generic driver for FX2 based LAs)",
.api_version = 1,
.init = init,
.cleanup = cleanup,
.scan = scan,
.dev_list = dev_list,
.dev_clear = NULL,
.config_get = config_get,
.config_set = config_set,
2013-01-25 01:32:05 +00:00
.config_list = config_list,
.dev_open = dev_open,
.dev_close = dev_close,
.dev_acquisition_start = dev_acquisition_start,
.dev_acquisition_stop = dev_acquisition_stop,
.priv = NULL,
};