summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/src/backend')
-rw-r--r--Radio/HW/BladeRF/src/backend/backend.c172
-rw-r--r--Radio/HW/BladeRF/src/backend/backend.h364
-rw-r--r--Radio/HW/BladeRF/src/backend/backend_config.h88
-rw-r--r--Radio/HW/BladeRF/src/backend/backend_config.h.in88
-rw-r--r--Radio/HW/BladeRF/src/backend/dummy/dummy.c554
-rw-r--r--Radio/HW/BladeRF/src/backend/dummy/dummy.h32
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/cyapi.c854
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/libusb.c1504
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/nios_access.c1322
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/nios_access.h582
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c744
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h488
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/usb.c1430
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/usb.h168
14 files changed, 8390 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/backend/backend.c b/Radio/HW/BladeRF/src/backend/backend.c
new file mode 100644
index 0000000..28b5a6d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/backend.c
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "rel_assert.h"
+#include "log.h"
+
+#include "devinfo.h"
+
+#include "backend/backend.h"
+#include "backend/backend_config.h"
+
+static const struct backend_fns *backend_list[] = BLADERF_BACKEND_LIST;
+
+int open_with_any_backend(struct bladerf *dev,
+ struct bladerf_devinfo *info)
+{
+ size_t i;
+ int status = BLADERF_ERR_NODEV;
+ const size_t n_backends = ARRAY_SIZE(backend_list);
+
+ for (i = 0; i < n_backends && status != 0; i++) {
+ status = backend_list[i]->open(dev, info);
+ }
+
+ return status;
+}
+
+int backend_open(struct bladerf *dev, struct bladerf_devinfo *info)
+{
+ size_t i;
+ int status = BLADERF_ERR_NODEV;
+ const size_t n_backends = ARRAY_SIZE(backend_list);
+
+ if (info->backend == BLADERF_BACKEND_ANY) {
+ status = open_with_any_backend(dev, info);
+ } else {
+ for (i = 0; i < n_backends; i++) {
+ if (backend_list[i]->matches(info->backend)) {
+ status = backend_list[i]->open(dev, info);
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+int backend_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo **devinfo_items, size_t *num_items)
+{
+ int status;
+ int first_backend_error = 0;
+ struct bladerf_devinfo_list list;
+ size_t i;
+ const size_t n_backends = ARRAY_SIZE(backend_list);
+
+ *devinfo_items = NULL;
+ *num_items = 0;
+
+ status = bladerf_devinfo_list_init(&list);
+ if (status != 0) {
+ log_debug("Failed to initialize devinfo list: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ for (i = 0; i < n_backends; i++) {
+ status = backend_list[i]->probe(probe_target, &list);
+
+ if (status < 0 && status != BLADERF_ERR_NODEV) {
+ log_debug("Probe failed on backend %d: %s\n",
+ i, bladerf_strerror(status));
+
+ if (!first_backend_error) {
+ first_backend_error = status;
+ }
+ }
+ }
+
+ *num_items = list.num_elt;
+
+ if (*num_items != 0) {
+ *devinfo_items = list.elt;
+ } else {
+ /* For no items, we end up passing back a NULL list to the
+ * API caller, so we'll just free this up now */
+ free(list.elt);
+
+ /* Report the first error that occurred if we couldn't find anything */
+ status =
+ first_backend_error == 0 ? BLADERF_ERR_NODEV : first_backend_error;
+ }
+
+ return status;
+}
+
+int backend_load_fw_from_bootloader(bladerf_backend backend,
+ uint8_t bus, uint8_t addr,
+ struct fx3_firmware *fw)
+{
+ int status = BLADERF_ERR_NODEV;
+ size_t i;
+ const size_t n_backends = ARRAY_SIZE(backend_list);
+
+ for (i = 0; i < n_backends; i++) {
+ if (backend_list[i]->matches(backend)) {
+ status = backend_list[i]->load_fw_from_bootloader(backend, bus,
+ addr, fw);
+ break;
+ }
+ }
+
+ return status;
+}
+
+const char *backend2str(bladerf_backend backend)
+{
+ switch (backend) {
+ case BLADERF_BACKEND_LIBUSB:
+ return BACKEND_STR_LIBUSB;
+
+ case BLADERF_BACKEND_LINUX:
+ return BACKEND_STR_LINUX;
+
+ case BLADERF_BACKEND_CYPRESS:
+ return BACKEND_STR_CYPRESS;
+
+ default:
+ return BACKEND_STR_ANY;
+ }
+}
+
+int str2backend(const char *str, bladerf_backend *backend)
+{
+ int status = 0;
+
+ if (!strcasecmp(BACKEND_STR_LIBUSB, str)) {
+ *backend = BLADERF_BACKEND_LIBUSB;
+ } else if (!strcasecmp(BACKEND_STR_LINUX, str)) {
+ *backend = BLADERF_BACKEND_LINUX;
+ } else if (!strcasecmp(BACKEND_STR_CYPRESS, str)) {
+ *backend = BLADERF_BACKEND_CYPRESS;
+ } else if (!strcasecmp(BACKEND_STR_ANY, str)) {
+ *backend = BLADERF_BACKEND_ANY;
+ } else {
+ log_debug("Invalid backend: %s\n", str);
+ status = BLADERF_ERR_INVAL;
+ *backend = BLADERF_BACKEND_ANY;
+ }
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/backend/backend.h b/Radio/HW/BladeRF/src/backend/backend.h
new file mode 100644
index 0000000..e5e6d2c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/backend.h
@@ -0,0 +1,364 @@
+/**
+ * @file backend.h
+ *
+ * @brief This file defines the generic interface to bladeRF backends
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BACKEND_BACKEND_H_
+#define BACKEND_BACKEND_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <libbladeRF.h>
+
+#include "logger_entry.h"
+
+#define BACKEND_STR_ANY "*"
+#define BACKEND_STR_LIBUSB "libusb"
+#define BACKEND_STR_LINUX "linux"
+#define BACKEND_STR_CYPRESS "cypress"
+
+/**
+ * Specifies what to probe for
+ */
+typedef enum {
+ BACKEND_PROBE_BLADERF,
+ BACKEND_PROBE_FX3_BOOTLOADER,
+} backend_probe_target;
+
+/**
+ * Backend protocol to use
+ */
+typedef enum {
+ BACKEND_FPGA_PROTOCOL_NIOSII_LEGACY,
+ BACKEND_FPGA_PROTOCOL_NIOSII,
+} backend_fpga_protocol;
+
+struct bladerf_devinfo_list;
+struct fx3_firmware;
+
+/**
+ * Backend-specific function table
+ *
+ * The backend is responsible for making calls to
+ * capabilities_init_pre_fpga_load() and capabilities_init_post_fpga_load() when
+ * opening a device and after loading an FPGA, or detecting an FPGA is already
+ * loaded.
+ *
+ */
+struct backend_fns {
+ /* Returns true if a backend supports the specified backend type,
+ * and false otherwise */
+ bool (*matches)(bladerf_backend backend);
+
+ /* Backends probe for devices and append entries to this list using
+ * bladerf_devinfo_list_append() */
+ int (*probe)(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list);
+
+ /* Get VID and PID of the device */
+ int (*get_vid_pid)(struct bladerf *dev, uint16_t *vid, uint16_t *pid);
+
+ /* Get flash manufacturer/device IDs */
+ int (*get_flash_id)(struct bladerf *dev, uint8_t *mid, uint8_t *did);
+
+ /* Opening device based upon specified device info. */
+ int (*open)(struct bladerf *dev, struct bladerf_devinfo *info);
+
+ /* Set the FPGA protocol */
+ int (*set_fpga_protocol)(struct bladerf *dev,
+ backend_fpga_protocol fpga_protocol);
+
+ /* Closing of the device and freeing of the data */
+ void (*close)(struct bladerf *dev);
+
+ /* Is firmware ready */
+ int (*is_fw_ready)(struct bladerf *dev);
+
+ /* Get handle */
+ int (*get_handle)(struct bladerf *dev, void **handle);
+
+ /* FPGA Loading and checking */
+ int (*load_fpga)(struct bladerf *dev,
+ const uint8_t *image,
+ size_t image_size);
+ int (*is_fpga_configured)(struct bladerf *dev);
+ bladerf_fpga_source (*get_fpga_source)(struct bladerf *dev);
+
+ /* Version checking */
+ int (*get_fw_version)(struct bladerf *dev, struct bladerf_version *version);
+ int (*get_fpga_version)(struct bladerf *dev,
+ struct bladerf_version *version);
+
+ /* Flash operations */
+
+ /* Erase the specified number of erase blocks */
+ int (*erase_flash_blocks)(struct bladerf *dev, uint32_t eb, uint16_t count);
+
+ /* Read the specified number of pages */
+ int (*read_flash_pages)(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+ /* Write the specified number of pages */
+ int (*write_flash_pages)(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+ /* Device startup and reset */
+ int (*device_reset)(struct bladerf *dev);
+ int (*jump_to_bootloader)(struct bladerf *dev);
+
+ /* Platform information */
+ int (*get_cal)(struct bladerf *dev, char *cal);
+ int (*get_otp)(struct bladerf *dev, char *otp);
+ int (*write_otp)(struct bladerf *dev, char *otp);
+ int (*lock_otp)(struct bladerf *dev);
+ int (*get_device_speed)(struct bladerf *dev, bladerf_dev_speed *speed);
+
+ /* Configuration GPIO (NIOS II <-> logic) accessors */
+ int (*config_gpio_write)(struct bladerf *dev, uint32_t val);
+ int (*config_gpio_read)(struct bladerf *dev, uint32_t *val);
+
+ /* Expansion GPIO accessors */
+ int (*expansion_gpio_write)(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val);
+ int (*expansion_gpio_read)(struct bladerf *dev, uint32_t *val);
+ int (*expansion_gpio_dir_write)(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t outputs);
+ int (*expansion_gpio_dir_read)(struct bladerf *dev, uint32_t *outputs);
+
+ /* IQ Correction Settings */
+ int (*set_iq_gain_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+ int (*set_iq_phase_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+ int (*get_iq_gain_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+ int (*get_iq_phase_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+ /* AGC DC Correction Lookup Table */
+ int (*set_agc_dc_correction)(struct bladerf *dev,
+ int16_t q_max,
+ int16_t i_max,
+ int16_t q_mid,
+ int16_t i_mid,
+ int16_t q_low,
+ int16_t i_low);
+
+ /* Get current timestamp counter values */
+ int (*get_timestamp)(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *value);
+
+ /* Si5338 accessors */
+ int (*si5338_write)(struct bladerf *dev, uint8_t addr, uint8_t data);
+ int (*si5338_read)(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+ /* LMS6002D accessors */
+ int (*lms_write)(struct bladerf *dev, uint8_t addr, uint8_t data);
+ int (*lms_read)(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+ /* INA219 accessors */
+ int (*ina219_write)(struct bladerf *dev, uint8_t addr, uint16_t data);
+ int (*ina219_read)(struct bladerf *dev, uint8_t addr, uint16_t *data);
+
+ /* AD9361 accessors */
+ int (*ad9361_spi_write)(struct bladerf *dev, uint16_t cmd, uint64_t data);
+ int (*ad9361_spi_read)(struct bladerf *dev, uint16_t cmd, uint64_t *data);
+
+ /* AD9361 accessors */
+ int (*adi_axi_write)(struct bladerf *dev, uint32_t addr, uint32_t data);
+ int (*adi_axi_read)(struct bladerf *dev, uint32_t addr, uint32_t *data);
+
+ /* Wishbone Master accessors */
+ int (*wishbone_master_write)(struct bladerf *dev, uint32_t addr, uint32_t data);
+ int (*wishbone_master_read)(struct bladerf *dev, uint32_t addr, uint32_t *data);
+
+ /* RFIC command accessors */
+ int (*rfic_command_write)(struct bladerf *dev, uint16_t cmd, uint64_t data);
+ int (*rfic_command_read)(struct bladerf *dev, uint16_t cmd, uint64_t *data);
+
+ /* RFFE control accessors */
+ int (*rffe_control_write)(struct bladerf *dev, uint32_t value);
+ int (*rffe_control_read)(struct bladerf *dev, uint32_t *value);
+
+ /* RFFE-to-Nios fast lock profile saver */
+ int (*rffe_fastlock_save)(struct bladerf *dev,
+ bool is_tx,
+ uint8_t rffe_profile,
+ uint16_t nios_profile);
+
+ /* AD56X1 VCTCXO Trim DAC accessors */
+ int (*ad56x1_vctcxo_trim_dac_write)(struct bladerf *dev, uint16_t value);
+ int (*ad56x1_vctcxo_trim_dac_read)(struct bladerf *dev, uint16_t *value);
+
+ /* ADF400X accessors */
+ int (*adf400x_write)(struct bladerf *dev, uint8_t addr, uint32_t data);
+ int (*adf400x_read)(struct bladerf *dev, uint8_t addr, uint32_t *data);
+
+ /* VCTCXO accessors */
+ int (*vctcxo_dac_write)(struct bladerf *dev, uint8_t addr, uint16_t value);
+ int (*vctcxo_dac_read)(struct bladerf *dev, uint8_t addr, uint16_t *value);
+
+ int (*set_vctcxo_tamer_mode)(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode);
+ int (*get_vctcxo_tamer_mode)(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode);
+
+ /* Expansion board SPI */
+ int (*xb_spi)(struct bladerf *dev, uint32_t value);
+
+ /* Configure firmware loopback */
+ int (*set_firmware_loopback)(struct bladerf *dev, bool enable);
+ int (*get_firmware_loopback)(struct bladerf *dev, bool *is_enabled);
+
+ /* Sample stream */
+ int (*enable_module)(struct bladerf *dev,
+ bladerf_direction dir,
+ bool enable);
+
+ int (*init_stream)(struct bladerf_stream *stream, size_t num_transfers);
+ int (*stream)(struct bladerf_stream *stream, bladerf_channel_layout layout);
+ int (*submit_stream_buffer)(struct bladerf_stream *stream,
+ void *buffer,
+ size_t *length,
+ unsigned int timeout_ms,
+ bool nonblock);
+ void (*deinit_stream)(struct bladerf_stream *stream);
+
+ /* Schedule a frequency retune operation */
+ int (*retune)(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t timestamp,
+ uint16_t nint,
+ uint32_t nfrac,
+ uint8_t freqsel,
+ uint8_t vcocap,
+ bool low_band,
+ uint8_t xb_gpio,
+ bool quick_tune);
+
+ /* Schedule a frequency retune2 operation */
+ int (*retune2)(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t timestamp,
+ uint16_t nios_profile,
+ uint8_t rffe_profile,
+ uint8_t port,
+ uint8_t spdt);
+
+ /* Load firmware from FX3 bootloader */
+ int (*load_fw_from_bootloader)(bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ struct fx3_firmware *fw);
+
+ /* Read a log entry from the FX3 firmware */
+ int (*read_fw_log)(struct bladerf *dev, logger_entry *e);
+
+ /* Read and Write access to trigger registers */
+ int (*read_trigger)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val);
+ int (*write_trigger)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val);
+
+ /* Backend name */
+ const char *name;
+};
+
+/**
+ * Open the device using the backend specified in the provided
+ * bladerf_devinfo structure.
+ *
+ * @param[in] device Device to fill in backend info for
+ * @param[in] info Filled-in device info
+ *
+ * @return 0 on success, BLADERF_ERR_* code on failure
+ */
+int backend_open(struct bladerf *dev, struct bladerf_devinfo *info);
+
+/**
+ * Probe for devices, filling in the provided devinfo list and size of
+ * the list that gets populated
+ *
+ * @param[in] probe_target Device type to probe for
+ * @param[out] devinfo_items Device info for identified devices
+ * @param[out] num_items Number of items in the devinfo list.
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int backend_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo **devinfo_items,
+ size_t *num_items);
+
+/**
+ * Search for bootloader via provided specification, download firmware,
+ * and boot it.
+ *
+ * @param[in] backend Backend to use for this operation
+ * @param[in] bus USB bus the device is connected to
+ * @param[in] addr USB addr associated with the device
+ * @param[in] fw Firmware to load
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int backend_load_fw_from_bootloader(bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ struct fx3_firmware *fw);
+
+/**
+ * Convert a backend enumeration value to a string
+ *
+ * @param[in] backend Value to convert to a string
+ *
+ * @return A backend string for the associated enumeration value. An invalid
+ * value will yield the "ANY" wildcard.
+ */
+const char *backend2str(bladerf_backend backend);
+
+/**
+ * Convert a string to a backend type value
+ *
+ * @param[in] str Backend type, as a string.
+ * @param[out] backend Associated backend, on success
+ *
+ * @return 0 on success, BLADERF_ERR_INVAL on invalid type string
+ */
+int str2backend(const char *str, bladerf_backend *backend);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/backend_config.h b/Radio/HW/BladeRF/src/backend/backend_config.h
new file mode 100644
index 0000000..351d5d1
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/backend_config.h
@@ -0,0 +1,88 @@
+/**
+ * @file backend_config.h
+ *
+ * @brief Compile-time backend selection
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BACKEND_BACKEND_CONFIG_H_
+#define BACKEND_BACKEND_CONFIG_H_
+
+#define ENABLE_BACKEND_USB
+#define ENABLE_BACKEND_LIBUSB
+/* #undef ENABLE_BACKEND_CYAPI */
+/* #undef ENABLE_BACKEND_DUMMY */
+/* #undef ENABLE_BACKEND_LINUX_DRIVER */
+
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+
+#ifdef ENABLE_BACKEND_DUMMY
+extern const struct backend_fns backend_fns_dummy;
+#define BACKEND_DUMMY &backend_fns_dummy,
+#else
+#define BACKEND_DUMMY
+#endif
+
+#ifdef ENABLE_BACKEND_USB
+extern const struct backend_fns backend_fns_usb;
+#define BACKEND_USB &backend_fns_usb,
+
+#ifdef ENABLE_BACKEND_LIBUSB
+extern const struct usb_driver usb_driver_libusb;
+#define BACKEND_USB_LIBUSB &usb_driver_libusb,
+#else
+#define BACKEND_USB_LIBUSB
+#endif
+
+#ifdef ENABLE_BACKEND_CYAPI
+extern const struct usb_driver usb_driver_cypress;
+#define BACKEND_USB_CYAPI &usb_driver_cypress,
+#else
+#define BACKEND_USB_CYAPI
+#endif
+
+#define BLADERF_USB_BACKEND_LIST \
+ { \
+ BACKEND_USB_LIBUSB \
+ BACKEND_USB_CYAPI \
+ }
+
+#if !defined(ENABLE_BACKEND_LIBUSB) && !defined(ENABLE_BACKEND_CYAPI)
+#error "No USB backends are enabled. One or more must be enabled."
+#endif
+
+#else
+#define BACKEND_USB
+#endif
+
+#if !defined(ENABLE_BACKEND_USB) && !defined(ENABLE_BACKEND_DUMMY)
+#error "No backends are enabled. One more more must be enabled."
+#endif
+
+/* This list should be ordered by preference (highest first) */
+#define BLADERF_BACKEND_LIST \
+ { \
+ BACKEND_USB \
+ BACKEND_DUMMY \
+ }
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/backend_config.h.in b/Radio/HW/BladeRF/src/backend/backend_config.h.in
new file mode 100644
index 0000000..15ba6f4
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/backend_config.h.in
@@ -0,0 +1,88 @@
+/**
+ * @file backend_config.h
+ *
+ * @brief Compile-time backend selection
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BACKEND_BACKEND_CONFIG_H_
+#define BACKEND_BACKEND_CONFIG_H_
+
+#cmakedefine ENABLE_BACKEND_USB
+#cmakedefine ENABLE_BACKEND_LIBUSB
+#cmakedefine ENABLE_BACKEND_CYAPI
+#cmakedefine ENABLE_BACKEND_DUMMY
+#cmakedefine ENABLE_BACKEND_LINUX_DRIVER
+
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+
+#ifdef ENABLE_BACKEND_DUMMY
+extern const struct backend_fns backend_fns_dummy;
+#define BACKEND_DUMMY &backend_fns_dummy,
+#else
+#define BACKEND_DUMMY
+#endif
+
+#ifdef ENABLE_BACKEND_USB
+extern const struct backend_fns backend_fns_usb;
+#define BACKEND_USB &backend_fns_usb,
+
+#ifdef ENABLE_BACKEND_LIBUSB
+extern const struct usb_driver usb_driver_libusb;
+#define BACKEND_USB_LIBUSB &usb_driver_libusb,
+#else
+#define BACKEND_USB_LIBUSB
+#endif
+
+#ifdef ENABLE_BACKEND_CYAPI
+extern const struct usb_driver usb_driver_cypress;
+#define BACKEND_USB_CYAPI &usb_driver_cypress,
+#else
+#define BACKEND_USB_CYAPI
+#endif
+
+#define BLADERF_USB_BACKEND_LIST \
+ { \
+ BACKEND_USB_LIBUSB \
+ BACKEND_USB_CYAPI \
+ }
+
+#if !defined(ENABLE_BACKEND_LIBUSB) && !defined(ENABLE_BACKEND_CYAPI)
+#error "No USB backends are enabled. One or more must be enabled."
+#endif
+
+#else
+#define BACKEND_USB
+#endif
+
+#if !defined(ENABLE_BACKEND_USB) && !defined(ENABLE_BACKEND_DUMMY)
+#error "No backends are enabled. One more more must be enabled."
+#endif
+
+/* This list should be ordered by preference (highest first) */
+#define BLADERF_BACKEND_LIST \
+ { \
+ BACKEND_USB \
+ BACKEND_DUMMY \
+ }
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/dummy/dummy.c b/Radio/HW/BladeRF/src/backend/dummy/dummy.c
new file mode 100644
index 0000000..14d8a2c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/dummy/dummy.c
@@ -0,0 +1,554 @@
+/*
+ * Dummy backend to allow libbladeRF to build when no other backends are
+ * enabled. This is intended for development purposes only, and should
+ * generally should not be enabled for libbladeRF releases.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "backend/backend.h"
+
+#include "board/board.h"
+
+static bool dummy_matches(bladerf_backend backend)
+{
+ return false;
+}
+
+/* We never "find" dummy devices */
+static int dummy_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ return 0;
+}
+
+static int dummy_get_vid_pid(struct bladerf *dev, uint16_t *vid, uint16_t *pid)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int dummy_open(struct bladerf *dev, struct bladerf_devinfo *info)
+{
+ return BLADERF_ERR_NODEV;
+}
+
+static int dummy_set_fpga_protocol(struct bladerf *dev,
+ backend_fpga_protocol fpga_protocol)
+{
+ return 0;
+}
+
+static void dummy_close(struct bladerf *dev)
+{
+ /* Nothing to do */
+}
+
+static int dummy_is_fw_ready(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_get_handle(struct bladerf *dev, void **handle)
+{
+ return 0;
+}
+
+static int dummy_get_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int dummy_load_fpga(struct bladerf *dev,
+ const uint8_t *image,
+ size_t image_size)
+{
+ return 0;
+}
+
+static int dummy_is_fpga_configured(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_get_fw_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ return 0;
+}
+
+static int dummy_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ return 0;
+}
+
+static int dummy_erase_flash_blocks(struct bladerf *dev,
+ uint32_t eb,
+ uint16_t count)
+{
+ return 0;
+}
+
+static int dummy_read_flash_pages(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ return 0;
+}
+
+static int dummy_write_flash_pages(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ return 0;
+}
+
+static int dummy_device_reset(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_jump_to_bootloader(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_get_cal(struct bladerf *dev, char *cal)
+{
+ return 0;
+}
+
+static int dummy_get_otp(struct bladerf *dev, char *otp)
+{
+ return 0;
+}
+
+static int dummy_write_otp(struct bladerf *dev, char *otp)
+{
+ return 0;
+}
+
+static int dummy_lock_otp(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_get_device_speed(struct bladerf *dev,
+ bladerf_dev_speed *device_speed)
+{
+ return 0;
+}
+
+static int dummy_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ return 0;
+}
+
+static int dummy_config_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ return 0;
+}
+
+static int dummy_expansion_gpio_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val)
+{
+ return 0;
+}
+
+static int dummy_expansion_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ return 0;
+}
+
+static int dummy_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val)
+{
+ return 0;
+}
+
+static int dummy_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ return 0;
+}
+
+static int dummy_set_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value)
+{
+ return 0;
+}
+
+static int dummy_set_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value)
+{
+ return 0;
+}
+
+static int dummy_get_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value)
+{
+ *value = 0;
+ return 0;
+}
+
+static int dummy_get_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value)
+{
+ *value = 0;
+ return 0;
+}
+
+static int dummy_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *val)
+{
+ return 0;
+}
+
+static int dummy_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ return 0;
+}
+
+static int dummy_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ return 0;
+}
+
+static int dummy_lms_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ return 0;
+}
+
+static int dummy_lms_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ return 0;
+}
+
+static int dummy_ina219_write(struct bladerf *dev, uint8_t cmd, uint16_t data)
+{
+ return 0;
+}
+
+static int dummy_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data)
+{
+ return 0;
+}
+
+static int dummy_ad9361_spi_write(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t data)
+{
+ return 0;
+}
+
+static int dummy_ad9361_spi_read(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t *data)
+{
+ return 0;
+}
+
+static int dummy_adi_axi_write(struct bladerf *dev,
+ uint32_t addr,
+ uint32_t data)
+{
+ return 0;
+}
+
+static int dummy_adi_axi_read(struct bladerf *dev,
+ uint32_t addr,
+ uint32_t *data)
+{
+ return 0;
+}
+
+static int dummy_rffe_control_write(struct bladerf *dev, uint32_t value)
+{
+ return 0;
+}
+
+static int dummy_rffe_control_read(struct bladerf *dev, uint32_t *value)
+{
+ return 0;
+}
+
+static int dummy_rffe_fastlock_save(struct bladerf *dev,
+ bool is_tx,
+ uint8_t rffe_profile,
+ uint16_t nios_profile)
+{
+ return 0;
+}
+
+static int dummy_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev,
+ uint16_t value)
+{
+ return 0;
+}
+
+static int dummy_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev,
+ uint16_t *value)
+{
+ return 0;
+}
+
+static int dummy_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data)
+{
+ return 0;
+}
+
+static int dummy_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data)
+{
+ return 0;
+}
+
+static int dummy_vctcxo_dac_write(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t value)
+{
+ return 0;
+}
+
+static int dummy_vctcxo_dac_read(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t *value)
+{
+ return 0;
+}
+
+static int dummy_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ return 0;
+}
+
+static int dummy_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ *mode = BLADERF_VCTCXO_TAMER_INVALID;
+ return 0;
+}
+
+static int dummy_xb_spi(struct bladerf *dev, uint32_t value)
+{
+ return 0;
+}
+
+static int dummy_set_firmware_loopback(struct bladerf *dev, bool enable)
+{
+ return 0;
+}
+
+static int dummy_get_firmware_loopback(struct bladerf *dev, bool *is_enabled)
+{
+ *is_enabled = false;
+ return 0;
+}
+
+static int dummy_enable_module(struct bladerf *dev,
+ bladerf_direction dir,
+ bool enable)
+{
+ return 0;
+}
+
+static int dummy_init_stream(struct bladerf_stream *stream,
+ size_t num_transfers)
+{
+ return 0;
+}
+
+static int dummy_stream(struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ return 0;
+}
+
+static int dummy_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms,
+ bool nonblock)
+{
+ return 0;
+}
+
+static void dummy_deinit_stream(struct bladerf_stream *stream)
+{
+ return;
+}
+
+static int dummy_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t timestamp,
+ uint16_t nint,
+ uint32_t nfrac,
+ uint8_t freqsel,
+ uint8_t vcocap,
+ bool low_band,
+ bool quick_tune)
+{
+ return 0;
+}
+
+
+static int dummy_load_fw_from_bootloader(bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ struct fx3_firmware *fw)
+{
+ return 0;
+}
+
+static int dummy_read_fw_log(struct bladerf *dev, logger_entry *e)
+{
+ *e = LOG_EOF;
+ return 0;
+}
+
+static int dummy_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *value)
+{
+ *value = 0;
+ return 0;
+}
+
+static int dummy_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t value)
+{
+ return 0;
+}
+
+const struct backend_fns backend_fns_dummy = {
+ FIELD_INIT(.matches, dummy_matches),
+
+ FIELD_INIT(.probe, dummy_probe),
+
+ FIELD_INIT(.get_vid_pid, dummy_get_vid_pid),
+ FIELD_INIT(.get_flash_id, dummy_get_flash_id),
+ FIELD_INIT(.open, dummy_open),
+ FIELD_INIT(.set_fpga_protocol, dummy_set_fpga_protocol),
+ FIELD_INIT(.close, dummy_close),
+
+ FIELD_INIT(.is_fw_ready, dummy_is_fw_ready),
+
+ FIELD_INIT(.get_handle, dummy_get_handle),
+
+ FIELD_INIT(.load_fpga, dummy_load_fpga),
+ FIELD_INIT(.is_fpga_configured, dummy_is_fpga_configured),
+
+ FIELD_INIT(.get_fw_version, dummy_get_fw_version),
+ FIELD_INIT(.get_fpga_version, dummy_get_fpga_version),
+
+ FIELD_INIT(.erase_flash_blocks, dummy_erase_flash_blocks),
+ FIELD_INIT(.read_flash_pages, dummy_read_flash_pages),
+ FIELD_INIT(.write_flash_pages, dummy_write_flash_pages),
+
+ FIELD_INIT(.device_reset, dummy_device_reset),
+ FIELD_INIT(.jump_to_bootloader, dummy_jump_to_bootloader),
+
+ FIELD_INIT(.get_cal, dummy_get_cal),
+ FIELD_INIT(.get_otp, dummy_get_otp),
+ FIELD_INIT(.write_otp, dummy_write_otp),
+ FIELD_INIT(.lock_otp, dummy_lock_otp),
+ FIELD_INIT(.get_device_speed, dummy_get_device_speed),
+
+ FIELD_INIT(.config_gpio_write, dummy_config_gpio_write),
+ FIELD_INIT(.config_gpio_read, dummy_config_gpio_read),
+
+ FIELD_INIT(.expansion_gpio_write, dummy_expansion_gpio_write),
+ FIELD_INIT(.expansion_gpio_read, dummy_expansion_gpio_read),
+ FIELD_INIT(.expansion_gpio_dir_write, dummy_expansion_gpio_dir_write),
+ FIELD_INIT(.expansion_gpio_dir_read, dummy_expansion_gpio_dir_read),
+
+ FIELD_INIT(.set_iq_gain_correction, dummy_set_iq_gain_correction),
+ FIELD_INIT(.set_iq_phase_correction, dummy_set_iq_phase_correction),
+ FIELD_INIT(.get_iq_gain_correction, dummy_get_iq_gain_correction),
+ FIELD_INIT(.get_iq_phase_correction, dummy_get_iq_phase_correction),
+
+ FIELD_INIT(.get_timestamp, dummy_get_timestamp),
+
+ FIELD_INIT(.si5338_write, dummy_si5338_write),
+ FIELD_INIT(.si5338_read, dummy_si5338_read),
+
+ FIELD_INIT(.lms_write, dummy_lms_write),
+ FIELD_INIT(.lms_read, dummy_lms_read),
+
+ FIELD_INIT(.ina219_write, dummy_ina219_write),
+ FIELD_INIT(.ina219_read, dummy_ina219_read),
+
+ FIELD_INIT(.ad9361_spi_write, dummy_ad9361_spi_write),
+ FIELD_INIT(.ad9361_spi_read, dummy_ad9361_spi_read),
+
+ FIELD_INIT(.adi_axi_write, dummy_adi_axi_write),
+ FIELD_INIT(.adi_axi_read, dummy_adi_axi_read),
+
+ FIELD_INIT(.rffe_control_write, dummy_rffe_control_write),
+ FIELD_INIT(.rffe_control_read, dummy_rffe_control_read),
+
+ FIELD_INIT(.rffe_fastlock_save, dummy_rffe_fastlock_save),
+
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_write,
+ dummy_ad56x1_vctcxo_trim_dac_write),
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, dummy_ad56x1_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.adf400x_write, dummy_adf400x_write),
+ FIELD_INIT(.adf400x_read, dummy_adf400x_read),
+
+ FIELD_INIT(.vctcxo_dac_write, dummy_vctcxo_dac_write),
+ FIELD_INIT(.vctcxo_dac_read, dummy_vctcxo_dac_read),
+
+ FIELD_INIT(.set_vctcxo_tamer_mode, dummy_set_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_tamer_mode, dummy_get_vctcxo_tamer_mode),
+
+ FIELD_INIT(.xb_spi, dummy_xb_spi),
+
+ FIELD_INIT(.set_firmware_loopback, dummy_set_firmware_loopback),
+ FIELD_INIT(.get_firmware_loopback, dummy_get_firmware_loopback),
+
+ FIELD_INIT(.enable_module, dummy_enable_module),
+
+ FIELD_INIT(.init_stream, dummy_init_stream),
+ FIELD_INIT(.stream, dummy_stream),
+ FIELD_INIT(.submit_stream_buffer, dummy_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, dummy_deinit_stream),
+
+ FIELD_INIT(.retune, dummy_retune),
+
+ FIELD_INIT(.load_fw_from_bootloader, dummy_load_fw_from_bootloader),
+
+ FIELD_INIT(.read_fw_log, dummy_read_fw_log),
+
+ FIELD_INIT(.read_trigger, dummy_read_trigger),
+ FIELD_INIT(.write_trigger, dummy_write_trigger),
+
+ FIELD_INIT(.name, "dummy"),
+};
diff --git a/Radio/HW/BladeRF/src/backend/dummy/dummy.h b/Radio/HW/BladeRF/src/backend/dummy/dummy.h
new file mode 100644
index 0000000..2c86bd7
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/dummy/dummy.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#if defined(ENABLE_BACKEND_DUMMY)
+
+#define BACKEND_DUMMY_H_
+#ifndef BACKEND_DUMMY_H_
+
+#include "bladerf_priv.h"
+
+extern const struct bladerf_fn bladerf_dummy_fn;
+
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/usb/cyapi.c b/Radio/HW/BladeRF/src/backend/usb/cyapi.c
new file mode 100644
index 0000000..48bef43
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/cyapi.c
@@ -0,0 +1,854 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This is a Windows-specific USB backend using Cypress's CyAPI, which utilizes
+ * the CyUSB3.sys driver (with a CyUSB3.inf modified to include the bladeRF
+ * VID/PID).
+ */
+
+extern "C" {
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include "bladeRF.h" /* Firmware interface */
+
+#include "devinfo.h"
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+#include "streaming/async.h"
+#include "helpers/timeout.h"
+#include "log.h"
+}
+
+#include <CyAPI.h>
+
+/* This GUID must match that in the modified CyUSB3.inf used with the bladeRF */
+static const GUID driver_guid = {
+ 0x35D5D3F1, 0x9D0E, 0x4F62, 0xBC, 0xFB, 0xB0, 0xD4, 0x8E,0xA6, 0x34, 0x16
+};
+
+/* "Private data" for the CyAPI backend */
+struct bladerf_cyapi {
+ CCyUSBDevice *dev;
+ HANDLE mutex;
+};
+
+struct transfer {
+ OVERLAPPED event; /* Transfer completion event handle */
+ PUCHAR handle; /* Handle for in-flight transfer */
+ PUCHAR buffer; /* Buffer associated with transfer */
+};
+
+struct stream_data {
+ CCyBulkEndPoint *ep; /* Endpoint associated with the stream */
+
+ struct transfer *transfers; /* Transfer slots */
+
+ size_t num_transfers; /* Max # of in-flight transfers */
+ size_t num_avail;
+ size_t avail_i; /* Index of next available transfer slot */
+ size_t inflight_i; /* Index of in-flight transfer that is
+ * expected to finish next */
+};
+
+static inline struct bladerf_cyapi * get_backend_data(void *driver)
+{
+ assert(driver);
+ return (struct bladerf_cyapi *) driver;
+}
+
+static inline CCyUSBDevice * get_device(void *driver)
+{
+ struct bladerf_cyapi *backend_data = get_backend_data(driver);
+ assert(backend_data->dev);
+ return backend_data->dev;
+}
+
+static inline struct stream_data * get_stream_data(struct bladerf_stream *s)
+{
+ assert(s && s->backend_data);
+ return (struct stream_data *) s->backend_data;
+}
+
+static int open_device(CCyUSBDevice *dev, int instance, HANDLE *mutex)
+{
+ int status = 0;
+ bool success;
+ DWORD last_error;
+ const char prefix[] = "Global\\bladeRF-";
+ const size_t mutex_name_len = strlen(prefix) + BLADERF_SERIAL_LENGTH;
+ char *mutex_name = (char *) calloc(mutex_name_len, 1);
+
+ if (mutex_name == NULL) {
+ return BLADERF_ERR_MEM;
+ } else {
+ strcpy(mutex_name, prefix);
+ }
+
+ success = dev->Open(instance);
+ if (!success) {
+ status = BLADERF_ERR_IO;
+ goto out;
+ }
+
+ wcstombs(mutex_name + strlen(prefix), dev->SerialNumber, sizeof(mutex_name) - BLADERF_SERIAL_LENGTH - 1);
+ log_verbose("Mutex name: %s\n", mutex_name);
+
+ SetLastError(0);
+ *mutex = CreateMutex(NULL, true, mutex_name);
+ last_error = GetLastError();
+ if (mutex == NULL || last_error != 0) {
+ log_debug("Unable to create device mutex: mutex=%p, last_error=%ld\n",
+ mutex, last_error);
+ dev->Close();
+ status = BLADERF_ERR_NODEV;
+ }
+
+out:
+ free(mutex_name);
+ return status;
+}
+
+static inline bool has_bladerf_ids(CCyUSBDevice *dev)
+{
+ return ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF2_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_LEGACY_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_LEGACY_PRODUCT_ID));
+}
+
+static inline bool has_boot_ids(CCyUSBDevice *dev)
+{
+ return ((dev->VendorID == USB_CYPRESS_VENDOR_ID) && (dev->ProductID == USB_FX3_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_BOOT_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_LEGACY_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_LEGACY_BOOT_PRODUCT_ID));
+}
+
+static bool device_matches_target(CCyUSBDevice *dev,
+ backend_probe_target target)
+{
+ bool matches = false;
+
+ switch (target) {
+ case BACKEND_PROBE_BLADERF:
+ matches = has_bladerf_ids(dev);
+ break;
+
+ case BACKEND_PROBE_FX3_BOOTLOADER:
+ matches = has_boot_ids(dev);
+ break;
+ }
+
+ return matches;
+}
+
+static int cyapi_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ CCyUSBDevice *dev = new CCyUSBDevice(NULL, driver_guid);
+ if (dev == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ for (int i = 0; i < dev->DeviceCount(); i++) {
+ struct bladerf_devinfo info;
+ bool opened;
+ int status;
+
+ opened = dev->Open(i);
+ if (opened) {
+ if (device_matches_target(dev, probe_target)) {
+ const size_t max_serial = sizeof(info.serial) - 1;
+ info.instance = i;
+ memset(info.serial, 0, sizeof(info.serial));
+ wcstombs(info.serial, dev->SerialNumber, max_serial);
+ info.usb_addr = dev->USBAddress;
+ info.usb_bus = 0; /* CyAPI doesn't provide this */
+ info.backend = BLADERF_BACKEND_CYPRESS;
+ status = bladerf_devinfo_list_add(info_list, &info);
+ if (status != 0) {
+ log_error("Could not add device to list: %s\n",
+ bladerf_strerror(status));
+ } else {
+ log_verbose("Added instance %d to device list\n",
+ info.instance);
+ }
+ }
+
+ dev->Close();
+ }
+ }
+
+ delete dev;
+ return 0;
+}
+
+static int open_via_info(void **driver, backend_probe_target probe_target,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ int status;
+ int instance = -1;
+ struct bladerf_devinfo_list info_list;
+ CCyUSBDevice *dev;
+ struct bladerf_cyapi *cyapi_data;
+
+ dev = new CCyUSBDevice(NULL, driver_guid);
+ if (dev == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ cyapi_data = (struct bladerf_cyapi *) calloc(1, sizeof(cyapi_data[0]));
+ if (cyapi_data == NULL) {
+ delete dev;
+ return BLADERF_ERR_MEM;
+ }
+
+ bladerf_devinfo_list_init(&info_list);
+ status = cyapi_probe(probe_target, &info_list);
+
+ for (unsigned int i = 0; i < info_list.num_elt && status == 0; i++) {
+
+
+ if (bladerf_devinfo_matches(&info_list.elt[i], info_in)) {
+ instance = info_list.elt[i].instance;
+
+ status = open_device(dev, instance, &cyapi_data->mutex);
+ if (status == 0) {
+ cyapi_data->dev = dev;
+ *driver = cyapi_data;
+ if (info_out != NULL) {
+ memcpy(info_out,
+ &info_list.elt[instance],
+ sizeof(info_out[0]));
+ }
+ status = 0;
+ break;
+ }
+ }
+ }
+
+ if (status == 0 && instance < 0) {
+ status = BLADERF_ERR_NODEV;
+ }
+
+ if (status != 0) {
+ delete dev;
+ ReleaseMutex(cyapi_data->mutex);
+ CloseHandle(cyapi_data->mutex);
+ }
+
+ free(info_list.elt);
+ return status;
+}
+
+
+static int cyapi_open(void **driver,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ return open_via_info(driver, BACKEND_PROBE_BLADERF, info_in, info_out);
+}
+
+static int cyapi_change_setting(void *driver, uint8_t setting)
+{
+ CCyUSBDevice *dev = get_device(driver);
+
+ if (dev->SetAltIntfc(setting)) {
+ return 0;
+ } else {
+ return BLADERF_ERR_IO;
+ }
+}
+
+static void cyapi_close(void *driver)
+{
+ struct bladerf_cyapi *cyapi_data = get_backend_data(driver);
+ cyapi_data->dev->Close();
+ delete cyapi_data->dev;
+ ReleaseMutex(cyapi_data->mutex);
+ CloseHandle(cyapi_data->mutex);
+ free(driver);
+
+}
+
+static int cyapi_get_vid_pid(void *driver, uint16_t *vid,
+ uint16_t *pid)
+{
+ CCyUSBDevice *dev = get_device(driver);
+
+ *vid = dev->VendorID;
+ *pid = dev->ProductID;
+
+ return 0;
+}
+
+static int cyapi_get_flash_id(void *driver, uint8_t *mid, uint8_t *did)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int cyapi_get_handle(void *driver, void **handle)
+{
+ CCyUSBDevice *dev = get_device(driver);
+ *handle = dev;
+
+ return 0;
+}
+static int cyapi_get_speed(void *driver,
+ bladerf_dev_speed *device_speed)
+{
+ int status = 0;
+ CCyUSBDevice *dev = get_device(driver);
+
+ if (dev->bHighSpeed) {
+ *device_speed = BLADERF_DEVICE_SPEED_HIGH;
+ } else if (dev->bSuperSpeed) {
+ *device_speed = BLADERF_DEVICE_SPEED_SUPER;
+ } else {
+ *device_speed = BLADERF_DEVICE_SPEED_UNKNOWN;
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("%s: Unable to determine device speed.\n", __FUNCTION__);
+ }
+
+ return status;
+}
+
+static int cyapi_control_transfer(void *driver,
+ usb_target target_type, usb_request req_type,
+ usb_direction dir, uint8_t request,
+ uint16_t wvalue, uint16_t windex,
+ void *buffer, uint32_t buffer_len,
+ uint32_t timeout_ms)
+{
+ CCyUSBDevice *dev = get_device(driver);
+ LONG len;
+ bool success;
+
+ int status = 0;
+
+ switch (dir) {
+ case USB_DIR_DEVICE_TO_HOST:
+ dev->ControlEndPt->Direction = DIR_FROM_DEVICE;
+ break;
+ case USB_DIR_HOST_TO_DEVICE:
+ dev->ControlEndPt->Direction = DIR_TO_DEVICE;
+ break;
+ }
+
+ switch (req_type) {
+ case USB_REQUEST_CLASS:
+ dev->ControlEndPt->ReqType = REQ_CLASS;
+ break;
+ case USB_REQUEST_STANDARD:
+ dev->ControlEndPt->ReqType = REQ_STD;
+ break;
+ case USB_REQUEST_VENDOR:
+ dev->ControlEndPt->ReqType = REQ_VENDOR;
+ break;
+ }
+
+ switch (target_type) {
+ case USB_TARGET_DEVICE:
+ dev->ControlEndPt->Target = TGT_DEVICE;
+ break;
+ case USB_TARGET_ENDPOINT:
+ dev->ControlEndPt->Target = TGT_ENDPT;
+ break;
+ case USB_TARGET_INTERFACE:
+ dev->ControlEndPt->Target = TGT_INTFC;
+ break;
+ case USB_TARGET_OTHER:
+ dev->ControlEndPt->Target = TGT_OTHER;
+ break;
+ }
+
+ dev->ControlEndPt->MaxPktSize = buffer_len;
+ dev->ControlEndPt->ReqCode = request;
+ dev->ControlEndPt->Index = windex;
+ dev->ControlEndPt->Value = wvalue;
+ dev->ControlEndPt->TimeOut = timeout_ms ? timeout_ms : INFINITE;
+ len = buffer_len;
+
+ success = dev->ControlEndPt->XferData((PUCHAR)buffer, len);
+ if (!success) {
+ status = BLADERF_ERR_IO;
+ }
+
+ return status;
+
+}
+
+static CCyBulkEndPoint *get_ep(CCyUSBDevice *USBDevice, int id)
+{
+
+ int count;
+ count = USBDevice->EndPointCount();
+
+ for (int i = 1; i < count; i++) {
+ if (USBDevice->EndPoints[i]->Address == id) {
+ return (CCyBulkEndPoint *) USBDevice->EndPoints[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int cyapi_bulk_transfer(void *driver, uint8_t endpoint, void *buffer,
+ uint32_t buffer_len, uint32_t timeout_ms)
+{
+ bool success;
+ int status = BLADERF_ERR_IO;
+ CCyUSBDevice *dev = get_device(driver);
+ CCyBulkEndPoint *ep = get_ep(dev, endpoint);
+
+ if (ep != NULL) {
+ LONG len = buffer_len;
+ dev->ControlEndPt->TimeOut = timeout_ms ? timeout_ms : INFINITE;
+ success = ep->XferData((PUCHAR)buffer, len);
+
+ if (success) {
+ if (len == buffer_len) {
+ status = 0;
+ } else {
+ log_debug("Transfer len mismatch: %u vs %u\n",
+ (unsigned int) buffer_len, (unsigned int) len);
+ }
+ } else {
+ log_debug("Transfered failed.\n");
+ }
+ } else {
+ log_debug("Failed to get EP handle.\n");
+ }
+
+ return status;
+}
+
+static int cyapi_get_string_descriptor(void *driver, uint8_t index,
+ void *buffer, uint32_t buffer_len)
+{
+ int status;
+ char *str;
+
+ memset(buffer, 0, buffer_len);
+
+ status = cyapi_control_transfer(driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_STANDARD,
+ USB_DIR_DEVICE_TO_HOST,
+ 0x06, 0x0300 | index, 0,
+ buffer, buffer_len,
+ BLADE_USB_TIMEOUT_MS);
+
+ if (status != 0) {
+ return status;
+ }
+
+ str = (char*)buffer;
+ for (unsigned int i = 0; i < (buffer_len / 2); i++) {
+ str[i] = str[2 + (i * 2)];
+ }
+
+ return 0;
+}
+
+static int cyapi_deinit_stream(void *driver, struct bladerf_stream *stream)
+{
+ struct stream_data *data;
+
+ assert(stream != NULL);
+ data = get_stream_data(stream);
+ assert(data != NULL);
+
+ for (unsigned int i = 0; i < data->num_transfers; i++) {
+ CloseHandle(data->transfers[i].event.hEvent);
+ }
+
+ free(data->transfers);
+ free(data);
+
+ return 0;
+}
+
+static int cyapi_init_stream(void *driver, struct bladerf_stream *stream,
+ size_t num_transfers)
+{
+ int status = BLADERF_ERR_MEM;
+ CCyUSBDevice *dev = get_device(driver);
+ struct stream_data *data;
+
+ data = (struct stream_data *) calloc(1, sizeof(data[0]));
+ if (data != NULL) {
+ stream->backend_data = data;
+ } else {
+ return status;
+ }
+
+ data->transfers =
+ (struct transfer*) calloc(num_transfers, sizeof(struct transfer));
+
+ if (data->transfers == NULL) {
+ goto out;
+ }
+
+ for (unsigned int i = 0; i < num_transfers; i++) {
+ data->transfers[i].event.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (data->transfers[i].event.hEvent == NULL) {
+ log_debug("%s: Failed to create EventObject for transfer %u\n",
+ (unsigned int) i);
+ goto out;
+ }
+ }
+
+ data->num_transfers = num_transfers;
+ data->num_avail = num_transfers;
+ data->inflight_i = 0;
+ data->avail_i = 0;
+
+ status = 0;
+
+out:
+ if (status != 0) {
+ cyapi_deinit_stream(driver, stream);
+ stream->backend_data = NULL;
+ }
+
+ return status;
+}
+
+#ifdef LOGGING_ENABLED
+static inline int find_buf(void* ptr, struct bladerf_stream *stream)
+{
+ for (unsigned int i=0; i<stream->num_buffers; i++) {
+ if (stream->buffers[i] == ptr) {
+ return i;
+ }
+ }
+
+ log_debug("Unabled to find buffer %p\n:", ptr);
+ return -1;
+}
+#endif
+
+#ifndef ENABLE_LIBBLADERF_ASYNC_LOG_VERBOSE
+#undef log_verbose
+#define log_verbose(...)
+#endif
+
+static inline size_t next_idx(struct stream_data *data, size_t i)
+{
+ return (i + 1) % data->num_transfers;
+}
+
+/* Assumes a transfer is available and the stream lock is being held */
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len)
+{
+ int status = 0;
+ PUCHAR xfer;
+ struct stream_data *data = get_stream_data(stream);
+
+ LONG buffer_size = (LONG) len;
+
+ assert(data->transfers[data->avail_i].handle == NULL);
+ assert(data->transfers[data->avail_i].buffer == NULL);
+ assert(data->num_avail != 0);
+
+ xfer = data->ep->BeginDataXfer((PUCHAR) buffer, buffer_size,
+ &data->transfers[data->avail_i].event);
+
+ if (xfer != NULL) {
+ data->transfers[data->avail_i].handle = xfer;
+ data->transfers[data->avail_i].buffer = (PUCHAR) buffer;
+
+ log_verbose("Submitted buffer %p using transfer slot %u.\n",
+ buffer, (unsigned int) data->avail_i);
+
+ data->avail_i = next_idx(data, data->avail_i);
+ data->num_avail--;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("Failed to submit buffer %p in transfer slot %u.\n",
+ buffer, (unsigned int) data->avail_i);
+ }
+
+ return status;
+}
+
+static int cyapi_stream(void *driver, struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ int status;
+ int idx = 0;
+ long len;
+ void *next_buffer;
+ ULONG timeout_ms;
+ bool success, done;
+ struct stream_data *data = get_stream_data(stream);
+ struct bladerf_cyapi *cyapi = get_backend_data(driver);
+ struct bladerf_metadata meta;
+
+ assert(stream->transfer_timeout <= ULONG_MAX);
+ if (stream->transfer_timeout == 0) {
+ timeout_ms = INFINITE;
+ } else {
+ timeout_ms = stream->transfer_timeout;
+ }
+
+ switch (layout & BLADERF_DIRECTION_MASK) {
+ case BLADERF_RX:
+ data->ep = get_ep(cyapi->dev, SAMPLE_EP_IN);
+ break;
+
+ case BLADERF_TX:
+ data->ep = get_ep(cyapi->dev, SAMPLE_EP_OUT);
+ break;
+ }
+
+ if (data->ep == NULL) {
+ log_debug("Failed to get EP handle.\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ data->ep->XferMode = XMODE_DIRECT;
+ data->ep->Abort();
+ data->ep->Reset();
+
+ log_verbose("Starting stream...\n");
+ status = 0;
+ done = false;
+ memset(&meta, 0, sizeof(meta));
+
+ MUTEX_LOCK(&stream->lock);
+
+ for (unsigned int i = 0; i < data->num_transfers && status == 0; i++) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ next_buffer = stream->cb(stream->dev, stream, &meta, NULL,
+ stream->samples_per_buffer,
+ stream->user_data);
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ done = true;
+ break;
+ } else if (next_buffer == BLADERF_STREAM_NO_DATA) {
+ continue;
+ }
+ } else {
+ next_buffer = stream->buffers[i];
+ }
+
+ if ((stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, meta.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+ }
+
+ MUTEX_UNLOCK(&stream->lock);
+ if (status != 0) {
+ goto out;
+ }
+
+ while (!done) {
+ struct transfer *xfer;
+ size_t i;
+
+ i = data->inflight_i;
+ xfer = &data->transfers[i];
+ success = data->ep->WaitForXfer(&xfer->event, timeout_ms);
+
+ if (!success) {
+ status = BLADERF_ERR_TIMEOUT;
+ log_debug("Steam timed out.\n");
+ break;
+ }
+
+ len = 0;
+ next_buffer = NULL;
+
+ log_verbose("Got transfer complete in slot %u (buffer %p)\n",
+ i, data->transfers[i].buffer);
+
+ MUTEX_LOCK(&stream->lock);
+ success = data->ep->FinishDataXfer(data->transfers[i].buffer, (LONG &)len,
+ &data->transfers[i].event,
+ xfer->handle);
+
+ if (success) {
+ next_buffer = stream->cb(stream->dev, stream, &meta,
+ data->transfers[i].buffer,
+ bytes_to_samples(stream->format, (LONG &)len),
+ stream->user_data);
+
+ } else {
+ done = true;
+ status = BLADERF_ERR_IO;
+ log_debug("Failed to finish transfer %u, buf=%p.\n",
+ (unsigned int)i, &data->transfers[i].buffer);
+ }
+
+ data->transfers[i].buffer = NULL;
+ data->transfers[i].handle = NULL;
+ data->num_avail++;
+ pthread_cond_signal(&stream->can_submit_buffer);
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ done = true;
+ } else if (next_buffer != BLADERF_STREAM_NO_DATA) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, meta.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+
+ done = (status != 0);
+ }
+
+ data->inflight_i = next_idx(data, data->inflight_i);
+ MUTEX_UNLOCK(&stream->lock);
+ }
+
+out:
+
+ MUTEX_LOCK(&stream->lock);
+ stream->error_code = status;
+ stream->state = STREAM_SHUTTING_DOWN;
+
+ data->ep->Abort();
+ data->ep->Reset();
+
+
+ for (unsigned int i = 0; i < data->num_transfers; i++) {
+ LONG len = 0;
+ if (data->transfers[i].handle != NULL) {
+ data->ep->FinishDataXfer(data->transfers[i].buffer, (LONG &)len,
+ &data->transfers[i].event,
+ data->transfers[i].handle);
+
+
+ data->transfers[i].buffer = NULL;
+ data->transfers[i].handle = NULL;
+ data->num_avail++;
+ }
+ }
+
+ assert(data->num_avail == data->num_transfers);
+
+ stream->state = STREAM_DONE;
+ log_verbose("Stream done (error_code = %d)\n", stream->error_code);
+ MUTEX_UNLOCK(&stream->lock);
+
+ return 0;
+}
+
+/* The top-level code will have acquired the stream->lock for us */
+int cyapi_submit_stream_buffer(void *driver, struct bladerf_stream *stream,
+ void *buffer, size_t *length,
+ unsigned int timeout_ms, bool nonblock)
+{
+ int status = 0;
+ struct timespec timeout_abs;
+ struct stream_data *data = get_stream_data(stream);
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ if (data->num_avail == data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+
+ return 0;
+ }
+
+ if (data->num_avail == 0) {
+ if (nonblock) {
+ log_debug("Non-blocking buffer submission requested, but no "
+ "transfers are currently available.");
+
+ return BLADERF_ERR_WOULD_BLOCK;
+ }
+
+ if (timeout_ms != 0) {
+ status = populate_abs_timeout(&timeout_abs, timeout_ms);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ while (data->num_avail == 0 && status == 0) {
+ status = pthread_cond_timedwait(&stream->can_submit_buffer,
+ &stream->lock,
+ &timeout_abs);
+ }
+ } else {
+ while (data->num_avail == 0 && status == 0) {
+ status = pthread_cond_wait(&stream->can_submit_buffer,
+ &stream->lock);
+ }
+ }
+ }
+
+ if (status == ETIMEDOUT) {
+ return BLADERF_ERR_TIMEOUT;
+ } else if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return submit_transfer(stream, buffer, *length);
+ }
+}
+
+int cyapi_open_bootloader(void **driver, uint8_t bus, uint8_t addr)
+{
+ struct bladerf_devinfo info;
+
+ bladerf_init_devinfo(&info);
+ info.backend = BLADERF_BACKEND_CYPRESS;
+ info.usb_bus = bus;
+ info.usb_addr = addr;
+
+ return open_via_info(driver, BACKEND_PROBE_FX3_BOOTLOADER, &info, NULL);
+}
+
+extern "C" {
+ static const struct usb_fns cypress_fns = {
+ FIELD_INIT(.probe, cyapi_probe),
+ FIELD_INIT(.open, cyapi_open),
+ FIELD_INIT(.close, cyapi_close),
+ FIELD_INIT(.get_vid_pid, cyapi_get_vid_pid),
+ FIELD_INIT(.get_flash_id, cyapi_get_flash_id),
+ FIELD_INIT(.get_handle, cyapi_get_handle),
+ FIELD_INIT(.get_speed, cyapi_get_speed),
+ FIELD_INIT(.change_setting, cyapi_change_setting),
+ FIELD_INIT(.control_transfer, cyapi_control_transfer),
+ FIELD_INIT(.bulk_transfer, cyapi_bulk_transfer),
+ FIELD_INIT(.get_string_descriptor, cyapi_get_string_descriptor),
+ FIELD_INIT(.init_stream, cyapi_init_stream),
+ FIELD_INIT(.stream, cyapi_stream),
+ FIELD_INIT(.submit_stream_buffer, cyapi_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, cyapi_deinit_stream),
+ FIELD_INIT(.open_bootloader, cyapi_open_bootloader),
+ FIELD_INIT(.close_bootloader, cyapi_close),
+ };
+
+ struct usb_driver usb_driver_cypress = {
+ FIELD_INIT(.fn, &cypress_fns),
+ FIELD_INIT(.id, BLADERF_BACKEND_CYPRESS),
+ };
+}
diff --git a/Radio/HW/BladeRF/src/backend/usb/libusb.c b/Radio/HW/BladeRF/src/backend/usb/libusb.c
new file mode 100644
index 0000000..1f63bbc
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/libusb.c
@@ -0,0 +1,1504 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "host_config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <errno.h>
+#include "libusb.h"
+#if 1 == BLADERF_OS_FREEBSD
+#include <limits.h>
+#endif // BLADERF_OS_FREEBSD
+
+#include "log.h"
+
+#include "devinfo.h"
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+#include "streaming/async.h"
+#include "helpers/timeout.h"
+
+#include "bladeRF.h"
+
+#include "host_config.h"
+
+#ifndef LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC
+# define LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC (15 * 1000)
+#endif
+
+struct bladerf_lusb {
+ libusb_device *dev;
+ libusb_device_handle *handle;
+ libusb_context *context;
+#if 1 == BLADERF_OS_WINDOWS
+ HANDLE mutex;
+#endif // BLADERF_OS_WINDOWS
+};
+
+typedef enum {
+ TRANSFER_UNINITIALIZED = 0,
+ TRANSFER_AVAIL,
+ TRANSFER_IN_FLIGHT,
+ TRANSFER_CANCEL_PENDING
+} transfer_status;
+
+struct lusb_stream_data {
+ size_t num_transfers; /* Total # of allocated transfers */
+ size_t num_avail; /* # of currently available transfers */
+ size_t i; /* Index to next transfer */
+ struct libusb_transfer **transfers; /* Array of transfer metadata */
+ transfer_status *transfer_status; /* Status of each transfer */
+
+ /* Warn the first time we get a transfer callback out of order.
+ * This shouldn't happen normally, but we've seen it intermittently on
+ * libusb 1.0.19 for Windows. Further investigation required...
+ */
+ bool out_of_order_event;
+};
+
+static inline struct bladerf_lusb * lusb_backend(struct bladerf *dev)
+{
+ struct bladerf_usb *usb;
+
+ assert(dev && dev->backend_data);
+ usb = (struct bladerf_usb *) dev->backend_data;
+
+ assert(usb->driver);
+ return (struct bladerf_lusb *) usb->driver;
+}
+
+/* Convert libusb error codes to libbladeRF error codes */
+static int error_conv(int error)
+{
+ int ret;
+
+ switch (error) {
+ case 0:
+ ret = 0;
+ break;
+
+ case LIBUSB_ERROR_IO:
+ ret = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_ERROR_INVALID_PARAM:
+ ret = BLADERF_ERR_INVAL;
+ break;
+
+ case LIBUSB_ERROR_BUSY:
+ case LIBUSB_ERROR_NO_DEVICE:
+ ret = BLADERF_ERR_NODEV;
+ break;
+
+ case LIBUSB_ERROR_TIMEOUT:
+ ret = BLADERF_ERR_TIMEOUT;
+ break;
+
+ case LIBUSB_ERROR_NO_MEM:
+ ret = BLADERF_ERR_MEM;
+ break;
+
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ ret = BLADERF_ERR_UNSUPPORTED;
+ break;
+
+ case LIBUSB_ERROR_ACCESS:
+ ret = BLADERF_ERR_PERMISSION;
+ break;
+
+ case LIBUSB_ERROR_OVERFLOW:
+ case LIBUSB_ERROR_PIPE:
+ case LIBUSB_ERROR_INTERRUPTED:
+ case LIBUSB_ERROR_NOT_FOUND:
+ default:
+ ret = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return ret;
+}
+
+/* Returns libusb error codes */
+static int get_devinfo(libusb_device *dev, struct bladerf_devinfo *info)
+{
+ int status = 0;
+ libusb_device_handle *handle;
+ struct libusb_device_descriptor desc;
+
+ /* Populate device info */
+ bladerf_init_devinfo(info);
+ info->backend = BLADERF_BACKEND_LIBUSB;
+ info->usb_bus = libusb_get_bus_number(dev);
+ info->usb_addr = libusb_get_device_address(dev);
+
+ status = libusb_open(dev, &handle);
+
+ if (status == 0) {
+ status = libusb_get_device_descriptor(dev, &desc);
+ if (status != 0) {
+ memset(info->serial, 0, BLADERF_SERIAL_LENGTH);
+ } else {
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iSerialNumber, (unsigned char *)&info->serial,
+ BLADERF_SERIAL_LENGTH);
+
+ /* Consider this to be non-fatal, otherwise firmware <= 1.1
+ * wouldn't be able to get far enough to upgrade */
+ if (status < 0) {
+ log_debug("Failed to retrieve serial number\n");
+ memset(info->serial, 0, BLADERF_SERIAL_LENGTH);
+ }
+
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iManufacturer,
+ (unsigned char *)&info->manufacturer,
+ BLADERF_DESCRIPTION_LENGTH);
+
+ if (status < 0) {
+ log_debug("Failed to retrieve manufacturer string\n");
+ memset(info->manufacturer, 0, BLADERF_DESCRIPTION_LENGTH);
+ }
+
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iProduct, (unsigned char *)&info->product,
+ BLADERF_DESCRIPTION_LENGTH);
+
+ if (status < 0) {
+ log_debug("Failed to retrieve product string\n");
+ memset(info->product, 0, BLADERF_DESCRIPTION_LENGTH);
+ }
+
+ log_debug("Bus %03d Device %03d: %s %s, serial %s\n", info->usb_bus,
+ info->usb_addr, info->manufacturer, info->product,
+ info->serial);
+
+ if (status > 0) {
+ status = 0;
+ }
+ }
+
+ libusb_close(handle);
+ }
+
+ return status;
+}
+
+static bool device_has_vid_pid(libusb_device *dev, uint16_t vid, uint16_t pid)
+{
+ int status;
+ struct libusb_device_descriptor desc;
+ bool match = false;
+
+ status = libusb_get_device_descriptor(dev, &desc);
+ if (status != 0) {
+ log_debug("Couldn't get device descriptor: %s\n",
+ libusb_error_name(status));
+ } else {
+ match = (desc.idVendor == vid) && (desc.idProduct == pid);
+ }
+
+ return match;
+}
+
+static bool device_is_fx3_bootloader(libusb_device *dev)
+{
+ return device_has_vid_pid(dev, USB_CYPRESS_VENDOR_ID, USB_FX3_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF_BOOT_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_LEGACY_VENDOR_ID, USB_NUAND_BLADERF_LEGACY_BOOT_PRODUCT_ID);
+}
+
+static inline bool device_has_bladeRF_ids(libusb_device *dev)
+{
+ return device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF2_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_LEGACY_VENDOR_ID, USB_NUAND_BLADERF_LEGACY_PRODUCT_ID);
+
+}
+
+static bool device_is_bladerf(libusb_device *dev)
+{
+ struct libusb_config_descriptor *cfg;
+ int status;
+ bool ret;
+
+ if (!device_has_bladeRF_ids(dev)) {
+ return false;
+ }
+
+ status = libusb_get_config_descriptor(dev, 0, &cfg);
+ if (status != 0) {
+ log_debug("Failed to get configuration descriptor: %s\n",
+ libusb_error_name(status));
+ return false;
+ }
+
+ /* As of firmware v0.4, we expect there to be 4 altsettings on the
+ * first interface. */
+ if (cfg->interface->num_altsetting != 4) {
+ const uint8_t bus = libusb_get_bus_number(dev);
+ const uint8_t addr = libusb_get_device_address(dev);
+
+ log_warning("A bladeRF running incompatible firmware appears to be "
+ "present on bus=%u, addr=%u. If this is true, a firmware "
+ "update via the device's bootloader is required.\n\n",
+ bus, addr);
+
+ ret = false;
+ } else {
+ ret = true;
+ }
+
+ libusb_free_config_descriptor(cfg);
+ return ret;
+}
+
+static bool device_is_probe_target(backend_probe_target probe_target,
+ libusb_device *dev)
+{
+ bool is_probe_target = false;
+
+ switch (probe_target) {
+ case BACKEND_PROBE_BLADERF:
+ is_probe_target = device_is_bladerf(dev);
+ if (is_probe_target) {
+ log_verbose("Found a bladeRF\n");
+ }
+ break;
+
+ case BACKEND_PROBE_FX3_BOOTLOADER:
+ is_probe_target = device_is_fx3_bootloader(dev);
+ if (is_probe_target) {
+ log_verbose("Found an FX3 bootloader.\n");
+ }
+ break;
+
+ default:
+ assert(!"Invalid probe target");
+ }
+
+ return is_probe_target;
+}
+
+static int lusb_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ int status, i, n;
+ ssize_t count;
+ libusb_device **list;
+ struct bladerf_devinfo info;
+ bool printed_access_warning = false;
+
+ libusb_context *context;
+
+ /* Initialize libusb for device tree walking */
+ status = libusb_init(&context);
+ if (status) {
+ log_error("Could not initialize libusb: %s\n",
+ libusb_error_name(status));
+ goto lusb_probe_done;
+ }
+
+ count = libusb_get_device_list(context, &list);
+ /* Iterate through all the USB devices */
+ for (i = 0, n = 0; i < count && status == 0; i++) {
+ if (device_is_probe_target(probe_target, list[i])) {
+ bool do_add = true;
+
+ /* Open the USB device and get some information */
+ status = get_devinfo(list[i], &info);
+ if (status) {
+ /* We may not be able to open the device if another driver
+ * (e.g., CyUSB3) is associated with it. Therefore, just log to
+ * the debug level and carry on. */
+ log_debug("Could not open device: %s\n",
+ libusb_error_name(status));
+
+ if (status == LIBUSB_ERROR_ACCESS) {
+ /* If it's an access error, odds are good this is happening
+ * because we've already got the device open. Pass the info
+ * we have back to the caller. */
+ do_add = true;
+
+ if (!printed_access_warning) {
+ printed_access_warning = true;
+ log_warning(
+ "Found a bladeRF via VID/PID, but could not open "
+ "it due to insufficient permissions, or because "
+ "the device is already open.\n");
+ }
+ } else {
+ do_add = false;
+ }
+
+ /* Don't stop probing because one device was "problematic" */
+ status = 0;
+ }
+
+ if (do_add) {
+ info.instance = n++;
+
+ status = bladerf_devinfo_list_add(info_list, &info);
+ if (status) {
+ log_error("Could not add device to list: %s\n",
+ bladerf_strerror(status));
+ } else {
+ log_verbose("Added instance %d to device list\n",
+ info.instance);
+ }
+ }
+ }
+ }
+
+ libusb_free_device_list(list, 1);
+ libusb_exit(context);
+
+lusb_probe_done:
+ return status;
+}
+
+#ifdef HAVE_LIBUSB_GET_VERSION
+static inline void get_libusb_version(char *buf, size_t buf_len)
+{
+ const struct libusb_version *version;
+ version = libusb_get_version();
+
+ snprintf(buf, buf_len, "%d.%d.%d.%d%s", version->major, version->minor,
+ version->micro, version->nano, version->rc);
+}
+#else
+static void get_libusb_version(char *buf, size_t buf_len)
+{
+ snprintf(buf, buf_len, "<= 1.0.9");
+}
+#endif
+
+static int create_device_mutex(const struct bladerf_devinfo *info,
+ struct bladerf_lusb *dev)
+{
+ int status = 0;
+
+#if 1 == BLADERF_OS_WINDOWS
+ const char prefix[] = "Global\\bladeRF-";
+ const size_t mutex_name_len = strlen(prefix) + BLADERF_SERIAL_LENGTH;
+ char *mutex_name = (char *)calloc(mutex_name_len, 1);
+ DWORD last_error;
+
+ if (NULL == mutex_name) {
+ return BLADERF_ERR_MEM;
+ }
+
+ snprintf(mutex_name, mutex_name_len, "%s%s", prefix, info->serial);
+ log_verbose("Mutex name: %s\n", mutex_name);
+
+ SetLastError(0);
+ dev->mutex = CreateMutex(NULL, true, mutex_name);
+ last_error = GetLastError();
+ if (NULL == dev->mutex || last_error != 0) {
+ log_debug("Unable to create device mutex: mutex=%p, last_error=%ld\n",
+ dev->mutex, last_error);
+ status = BLADERF_ERR_NODEV;
+ ReleaseMutex(dev->mutex);
+ CloseHandle(dev->mutex);
+ }
+
+ free(mutex_name);
+#endif // BLADERF_OS_WINDOWS
+
+ return status;
+}
+
+static int open_device(const struct bladerf_devinfo *info,
+ libusb_context *context,
+ libusb_device *libusb_dev_in,
+ struct bladerf_lusb **dev_out)
+{
+ int status;
+ struct bladerf_lusb *dev;
+
+ *dev_out = NULL;
+
+ dev = (struct bladerf_lusb *) calloc(1, sizeof(dev[0]));
+ if (dev == NULL) {
+ log_debug("Failed to allocate handle for instance %d.\n",
+ info->instance);
+
+ /* Report "no device" so we could try again with
+ * another matching device */
+ return BLADERF_ERR_NODEV;
+ }
+
+ dev->context = context;
+ dev->dev = libusb_dev_in;
+
+ status = libusb_open(libusb_dev_in, &dev->handle);
+ if (status < 0) {
+ log_debug("Failed to open device instance %d: %s\n",
+ info->instance, libusb_error_name(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+ status = libusb_claim_interface(dev->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to claim interface 0 for instance %d: %s\n",
+ info->instance, libusb_error_name(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+ status = create_device_mutex(info, dev);
+ if (status < 0) {
+ log_debug("Failed to get device mutex for instance %d: %s\n",
+ info->instance, bladerf_strerror(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+out:
+ if (status != 0) {
+ if (dev->handle != NULL) {
+ libusb_close(dev->handle);
+ }
+
+ free(dev);
+ } else {
+ *dev_out = dev;
+ }
+
+ return status;
+}
+
+static int find_and_open_device(libusb_context *context,
+ const struct bladerf_devinfo *info_in,
+ struct bladerf_lusb **dev_out,
+ struct bladerf_devinfo *info_out)
+{
+ int status = BLADERF_ERR_NODEV;
+ int i, n;
+ ssize_t count;
+ struct libusb_device **list;
+ struct bladerf_devinfo curr_info;
+ bool printed_access_warning = false;
+
+ *dev_out = NULL;
+
+ count = libusb_get_device_list(context, &list);
+ if (count < 0) {
+ if (count < INT_MIN) {
+ /* Ensure we don't have a situation where we accidentally return 0
+ * due to a narrowing conversion */
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return error_conv((int) count);
+ }
+ }
+
+ for (i = 0, n = 0; (i < count) && (*dev_out == NULL); i++) {
+ if (device_is_bladerf(list[i])) {
+ log_verbose("Found a bladeRF (idx=%d)\n", i);
+
+ /* Open the USB device and get some information */
+ status = get_devinfo(list[i], &curr_info);
+ if (status < 0) {
+
+ /* Give the user a helpful hint in case the have forgotten
+ * to update their udev rules */
+ if (status == LIBUSB_ERROR_ACCESS && !printed_access_warning) {
+ printed_access_warning = true;
+ log_warning("Found a bladeRF via VID/PID, but could not "
+ "open it due to insufficient permissions.\n");
+ } else {
+ log_debug("Could not open bladeRF device: %s\n",
+ libusb_error_name(status) );
+ }
+
+ status = BLADERF_ERR_NODEV;
+ continue; /* Continue trying the next devices */
+ } else {
+ curr_info.instance = n++;
+ }
+
+ /* Check to see if this matches the info struct */
+ if (bladerf_devinfo_matches(&curr_info, info_in)) {
+ status = open_device(&curr_info, context, list[i], dev_out);
+ if (status < 0) {
+ status = BLADERF_ERR_NODEV;
+ continue; /* Continue trying the next matching device */
+ } else {
+ memcpy(info_out, &curr_info, sizeof(info_out[0]));
+ }
+ } else {
+ status = BLADERF_ERR_NODEV;
+
+ log_verbose("Devinfo doesn't match - skipping"
+ "(instance=%d, serial=%d, bus/addr=%d\n",
+ bladerf_instance_matches(&curr_info, info_in),
+ bladerf_serial_matches(&curr_info, info_in),
+ bladerf_bus_addr_matches(&curr_info, info_in));
+ }
+ }
+ }
+
+ if (status == 0) {
+ /* Returning 0 indicates this function is providing a device */
+ assert(*dev_out != NULL);
+ }
+
+ libusb_free_device_list(list, 1);
+ return status;
+}
+
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+static int reset_and_reopen(libusb_context *context,
+ struct bladerf_lusb **dev,
+ struct bladerf_devinfo *info)
+{
+ int status;
+
+ status = libusb_reset_device((*dev)->handle);
+ if (status == 0) {
+ log_verbose("USB port reset succeeded for bladeRF %s\n", info->serial);
+ return 0;
+ } else if (status == LIBUSB_ERROR_NO_DEVICE) {
+ struct bladerf_devinfo new_info;
+
+ /* The reset has caused the device to drop out and re-enumerate.
+ *
+ * We'll find it again via the info we gathered about it via its
+ * serial number, which is now stored in the devinfo
+ */
+ log_verbose("Re-scan required after port reset for bladeRF %s\n",
+ info->serial);
+
+
+ libusb_release_interface((*dev)->handle, 0);
+ libusb_close((*dev)->handle);
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex((*dev)->mutex);
+ CloseHandle((*dev)->mutex);
+#endif // BLADERF_OS_WINDOWS
+
+ *dev = NULL;
+
+ memcpy(&new_info, info, sizeof(new_info));
+ new_info.usb_bus = DEVINFO_BUS_ANY;
+ new_info.usb_addr = DEVINFO_ADDR_ANY;
+
+ status = find_and_open_device(context, &new_info, dev, info);
+
+ } else {
+ status = BLADERF_ERR_IO;
+ log_verbose("Port reset failed for bladerf %s: %s\n",
+ info->serial, libusb_error_name(status));
+ }
+
+ return status;
+}
+#endif
+
+static int lusb_open(void **driver,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ int status;
+ struct bladerf_lusb *lusb = NULL;
+ libusb_context *context;
+
+ /* Initialize libusb for device tree walking */
+ status = libusb_init(&context);
+ if (status) {
+ log_error("Could not initialize libusb: %s\n",
+ libusb_error_name(status));
+ return error_conv(status);
+ }
+
+ /* We can only print this out when log output is enabled, or else we'll
+ * get snagged by -Werror=unused-but-set-variable */
+# ifdef LOGGING_ENABLED
+ {
+ char buf[64];
+ get_libusb_version(buf, sizeof(buf));
+ log_verbose("Using libusb version: %s\n", buf);
+ }
+# endif
+
+ status = find_and_open_device(context, info_in, &lusb, info_out);
+ if (status != 0) {
+ libusb_exit(context);
+
+ if (status == BLADERF_ERR_NODEV) {
+ log_debug("No devices available on the libusb backend.\n");
+ } else {
+ log_debug("Failed to open bladeRF on libusb backend: %s\n",
+ bladerf_strerror(status));
+ }
+ } else {
+ assert(lusb != NULL);
+
+ /* Cosmin and Marian from Null Team (null.ro) and YateBTS(.com) found
+ * that it is possible to recover from "Issue #95: Not enough bandwidth
+ * for altsetting" by performing a USB port reset prior to actually
+ * trying to use the device.
+ */
+# if ENABLE_USB_DEV_RESET_ON_OPEN
+ if (bladerf_usb_reset_device_on_open) {
+ status = reset_and_reopen(context, &lusb, info_out);
+ }
+# endif
+
+ if (status == 0) {
+ *driver = (void *) lusb;
+ }
+ }
+
+ return status;
+}
+
+static int lusb_change_setting(void *driver, uint8_t setting)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ int status = libusb_set_interface_alt_setting(lusb->handle, 0, setting);
+
+ return error_conv(status);
+}
+
+static void lusb_close(void *driver)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_release_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_error("Failed to release interface: %s\n",
+ libusb_error_name(status));
+ }
+
+ libusb_close(lusb->handle);
+ libusb_exit(lusb->context);
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex(lusb->mutex);
+ CloseHandle(lusb->mutex);
+#endif // BLADERF_OS_WINDOWS
+ free(lusb);
+}
+
+static void lusb_close_bootloader(void *driver)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ if (lusb != NULL) {
+ if (lusb->handle != NULL) {
+ status = libusb_release_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to release interface: %s\n",
+ libusb_error_name(status));
+ }
+
+ libusb_close(lusb->handle);
+ }
+
+ if (lusb->context != NULL) {
+ libusb_exit(lusb->context);
+ }
+
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex(lusb->mutex);
+ CloseHandle(lusb->mutex);
+#endif // BLADERF_OS_WINDOWS
+
+ free(lusb);
+ }
+}
+
+static inline bool bus_matches(uint8_t bus, struct libusb_device *d)
+{
+ return (bus == DEVINFO_BUS_ANY) ||
+ (bus == libusb_get_bus_number(d));
+}
+
+static inline bool addr_matches(uint8_t addr, struct libusb_device *d)
+{
+ return (addr == DEVINFO_BUS_ANY) ||
+ (addr == libusb_get_device_address(d));
+}
+
+static int lusb_open_bootloader(void **driver, uint8_t bus, uint8_t addr)
+{
+ int status;
+ struct libusb_device **dev_list = NULL;
+ ssize_t dev_list_size, i;
+ struct bladerf_lusb *lusb;
+
+ *driver = NULL;
+
+ lusb = calloc(1, sizeof(lusb[0]));
+ if (lusb == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ status = libusb_init(&lusb->context);
+ if (status != 0) {
+ log_debug("Failed to initialize libusb context: %s\n",
+ libusb_error_name(status));
+ goto error;
+ }
+
+ dev_list_size = libusb_get_device_list(lusb->context, &dev_list);
+ if (dev_list_size < 0) {
+ log_debug("Failed to get device list: %s\n", libusb_error_name(status));
+ status = (int) dev_list_size;
+ goto error;
+ }
+
+ for (i = 0; i < dev_list_size; i++) {
+ if (device_is_fx3_bootloader(dev_list[i]) &&
+ bus_matches(bus, dev_list[i]) &&
+ addr_matches(addr, dev_list[i])) {
+
+
+ status = libusb_open(dev_list[i], &lusb->handle);
+ if (status != 0) {
+ log_debug("Failed to open device: %s\n",
+ libusb_error_name(status));
+ goto error;
+ } else {
+ status = libusb_claim_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to claim interface: %s\n",
+ libusb_error_name(status));
+
+ goto error;
+ } else {
+ log_verbose("Opened bootloader at %u:%u\n",
+ libusb_get_bus_number(dev_list[i]),
+ libusb_get_device_address(dev_list[i]));
+ *driver = lusb;
+ }
+ break;
+ }
+ }
+ }
+
+error:
+ if (dev_list != NULL) {
+ libusb_free_device_list(dev_list, 1);
+ }
+
+ if (status != 0) {
+ status = error_conv(status);
+ lusb_close_bootloader(lusb);
+ } else if (*driver == NULL) {
+ status = BLADERF_ERR_NODEV;
+ lusb_close_bootloader(lusb);
+ }
+
+ return status;
+}
+
+static int lusb_get_vid_pid(void *driver, uint16_t *vid,
+ uint16_t *pid)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *)driver;
+ struct libusb_device_descriptor desc;
+ int status;
+
+ status = libusb_get_device_descriptor(lusb->dev, &desc);
+ if (status != 0) {
+ log_debug("Couldn't get device descriptor: %s\n",
+ libusb_error_name(status));
+ return BLADERF_ERR_IO;
+ }
+
+ *vid = desc.idVendor;
+ *pid = desc.idProduct;
+
+ return 0;
+}
+
+static int lusb_get_handle(void *driver, void **handle)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ *handle = lusb->handle;
+
+ return 0;
+}
+static int lusb_get_speed(void *driver,
+ bladerf_dev_speed *device_speed)
+{
+ int speed;
+ int status = 0;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ speed = libusb_get_device_speed(lusb->dev);
+ if (speed == LIBUSB_SPEED_SUPER) {
+ *device_speed = BLADERF_DEVICE_SPEED_SUPER;
+ } else if (speed == LIBUSB_SPEED_HIGH) {
+ *device_speed = BLADERF_DEVICE_SPEED_HIGH;
+ } else {
+ *device_speed = BLADERF_DEVICE_SPEED_UNKNOWN;
+
+ if (speed == LIBUSB_SPEED_FULL) {
+ log_debug("Full speed connection is not suppored.\n");
+ status = BLADERF_ERR_UNSUPPORTED;
+ } else if (speed == LIBUSB_SPEED_LOW) {
+ log_debug("Low speed connection is not supported.\n");
+ status = BLADERF_ERR_UNSUPPORTED;
+ } else {
+ log_debug("Unknown/unexpected device speed (%d)\n", speed);
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+static inline uint8_t bm_request_type(usb_target target_type,
+ usb_request req_type,
+ usb_direction direction)
+{
+ uint8_t ret = 0;
+
+ switch (target_type) {
+ case USB_TARGET_DEVICE:
+ ret |= LIBUSB_RECIPIENT_DEVICE;
+ break;
+
+ case USB_TARGET_INTERFACE:
+ ret |= LIBUSB_RECIPIENT_INTERFACE;
+ break;
+
+ case USB_TARGET_ENDPOINT:
+ ret |= LIBUSB_RECIPIENT_ENDPOINT;
+ break;
+
+ default:
+ ret |= LIBUSB_RECIPIENT_OTHER;
+
+ }
+
+ switch (req_type) {
+ case USB_REQUEST_STANDARD:
+ ret |= LIBUSB_REQUEST_TYPE_STANDARD;
+ break;
+
+ case USB_REQUEST_CLASS:
+ ret |= LIBUSB_REQUEST_TYPE_CLASS;
+ break;
+
+ case USB_REQUEST_VENDOR:
+ ret |= LIBUSB_REQUEST_TYPE_VENDOR;
+ break;
+ }
+
+ switch (direction) {
+ case USB_DIR_HOST_TO_DEVICE:
+ ret |= LIBUSB_ENDPOINT_OUT;
+ break;
+
+ case USB_DIR_DEVICE_TO_HOST:
+ ret |= LIBUSB_ENDPOINT_IN;
+ break;
+ }
+
+ return ret;
+}
+
+static int lusb_control_transfer(void *driver,
+ usb_target target_type, usb_request req_type,
+ usb_direction dir, uint8_t request,
+ uint16_t wvalue, uint16_t windex,
+ void *buffer, uint32_t buffer_len,
+ uint32_t timeout_ms)
+{
+
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ const uint8_t bm_req_type = bm_request_type(target_type, req_type, dir);
+
+ status = libusb_control_transfer(lusb->handle,
+ bm_req_type, request,
+ wvalue, windex,
+ buffer, buffer_len,
+ timeout_ms);
+
+ if (status >= 0 && (uint32_t)status == buffer_len) {
+ status = 0;
+ } else {
+ log_debug("%s failed: status = %d\n", __FUNCTION__, status);
+ }
+
+ return error_conv(status);
+}
+
+static int lusb_bulk_transfer(void *driver, uint8_t endpoint, void *buffer,
+ uint32_t buffer_len, uint32_t timeout_ms)
+{
+ int status;
+ int n_transferred;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_bulk_transfer(lusb->handle, endpoint, buffer, buffer_len,
+ &n_transferred, timeout_ms);
+
+ status = error_conv(status);
+ if (status == 0 && ((uint32_t)n_transferred != buffer_len)) {
+ log_debug("Short bulk transfer: requested=%u, transferred=%u\n",
+ buffer_len, n_transferred);
+ status = BLADERF_ERR_IO;
+ }
+
+ return status;
+}
+
+static int lusb_get_string_descriptor(void *driver, uint8_t index,
+ void *buffer, uint32_t buffer_len)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_get_string_descriptor_ascii(lusb->handle, index,
+ (unsigned char*)buffer,
+ buffer_len);
+
+ if (status > 0 && (uint32_t)status < buffer_len) {
+ status = 0;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+/* At the risk of being a little inefficient, just keep attempting to cancel
+ * everything. If a transfer's no longer active, we'll just get a NOT_FOUND
+ * error -- no big deal. Just accepting that alleviates the need to track
+ * the status of each transfer...
+ */
+static inline void cancel_all_transfers(struct bladerf_stream *stream)
+{
+ size_t i;
+ int status;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if (stream_data->transfer_status[i] == TRANSFER_IN_FLIGHT) {
+ status = libusb_cancel_transfer(stream_data->transfers[i]);
+ if (status < 0 && status != LIBUSB_ERROR_NOT_FOUND) {
+ log_error("Error canceling transfer (%d): %s\n",
+ status, libusb_error_name(status));
+ } else {
+ stream_data->transfer_status[i] = TRANSFER_CANCEL_PENDING;
+ }
+ }
+ }
+}
+
+static inline size_t transfer_idx(struct lusb_stream_data *stream_data,
+ struct libusb_transfer *transfer)
+{
+ size_t i;
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if (stream_data->transfers[i] == transfer) {
+ return i;
+ }
+ }
+
+ return UINT_MAX;
+}
+
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len);
+
+static void LIBUSB_CALL lusb_stream_cb(struct libusb_transfer *transfer)
+{
+ struct bladerf_stream *stream = transfer->user_data;
+ void *next_buffer = NULL;
+ struct bladerf_metadata metadata;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ size_t transfer_i;
+
+ /* Currently unused - zero out for out own debugging sanity... */
+ memset(&metadata, 0, sizeof(metadata));
+
+ MUTEX_LOCK(&stream->lock);
+
+ transfer_i = transfer_idx(stream_data, transfer);
+ assert(stream_data->transfer_status[transfer_i] == TRANSFER_IN_FLIGHT ||
+ stream_data->transfer_status[transfer_i] == TRANSFER_CANCEL_PENDING);
+
+ if (transfer_i >= stream_data->num_transfers) {
+ log_error("Unable to find transfer\n");
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else {
+ stream_data->transfer_status[transfer_i] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ pthread_cond_signal(&stream->can_submit_buffer);
+ }
+
+ /* Check to see if the transfer has been cancelled or errored */
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ /* Errored out for some reason .. */
+ stream->state = STREAM_SHUTTING_DOWN;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_CANCELLED:
+ /* We expect this case when we begin tearing down the stream */
+ break;
+
+ case LIBUSB_TRANSFER_STALL:
+ log_error("Hit stall for buffer %p\n\r", transfer->buffer);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_ERROR:
+ log_error("Got transfer error for buffer %p\n\r",
+ transfer->buffer);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_OVERFLOW:
+ log_error("Got transfer over for buffer %p, "
+ "transfer \"actual_length\" = %d\n\r",
+ transfer->buffer, transfer->actual_length);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ log_error("Transfer timed out for buffer %p\n\r",
+ transfer->buffer);
+ stream->error_code = BLADERF_ERR_TIMEOUT;
+ break;
+
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ stream->error_code = BLADERF_ERR_NODEV;
+ break;
+
+ default:
+ log_error("Unexpected transfer status: %d\n\r", transfer->status);
+ break;
+ }
+ }
+
+ if (stream->state == STREAM_RUNNING) {
+ if (stream->format == BLADERF_FORMAT_PACKET_META) {
+ /* Call user callback requesting more data to transmit */
+ next_buffer = stream->cb(
+ stream->dev, stream, &metadata, transfer->buffer,
+ bytes_to_samples(stream->format, transfer->actual_length), stream->user_data);
+ } else {
+ /* Sanity check for debugging purposes */
+ if (transfer->length != transfer->actual_length) {
+ log_warning("Received short transfer\n");
+ }
+
+ /* Call user callback requesting more data to transmit */
+ next_buffer = stream->cb(
+ stream->dev, stream, &metadata, transfer->buffer,
+ bytes_to_samples(stream->format, transfer->actual_length), stream->user_data);
+ }
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else if (next_buffer != BLADERF_STREAM_NO_DATA) {
+ int status;
+ if((stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, metadata.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+ if (status != 0) {
+ /* If this fails, we probably have a serious problem...so just
+ * shut it down. */
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+ }
+ }
+
+
+ /* Check to see if all the transfers have been cancelled,
+ * and if so, clean up the stream */
+ if (stream->state == STREAM_SHUTTING_DOWN) {
+ /* We know we're done when all of our transfers have returned to their
+ * "available" states */
+ if (stream_data->num_avail == stream_data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ cancel_all_transfers(stream);
+ }
+ }
+
+ MUTEX_UNLOCK(&stream->lock);
+}
+
+static inline struct libusb_transfer *
+get_next_available_transfer(struct lusb_stream_data *stream_data)
+{
+ unsigned int n;
+ size_t i = stream_data->i;
+
+ for (n = 0; n < stream_data->num_transfers; n++) {
+ if (stream_data->transfer_status[i] == TRANSFER_AVAIL) {
+
+ if (stream_data->i != i &&
+ stream_data->out_of_order_event == false) {
+
+ log_warning("Transfer callback occurred out of order. "
+ "(Warning only this time.)\n");
+ stream_data->out_of_order_event = true;
+ }
+
+ stream_data->i = i;
+ return stream_data->transfers[i];
+ }
+
+ i = (i + 1) % stream_data->num_transfers;
+ }
+
+ return NULL;
+}
+
+/* Precondition: A transfer is available. */
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len)
+{
+ int status;
+ struct bladerf_lusb *lusb = lusb_backend(stream->dev);
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct libusb_transfer *transfer;
+ const size_t bytes_per_buffer = async_stream_buf_bytes(stream);
+ size_t prev_idx;
+ const unsigned char ep =
+ (stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX ? SAMPLE_EP_OUT : SAMPLE_EP_IN;
+
+ transfer = get_next_available_transfer(stream_data);
+ assert(transfer != NULL);
+
+ assert(bytes_per_buffer <= INT_MAX);
+ libusb_fill_bulk_transfer(transfer,
+ lusb->handle,
+ ep,
+ buffer,
+ (int)len,
+ lusb_stream_cb,
+ stream,
+ stream->transfer_timeout);
+
+ prev_idx = stream_data->i;
+ stream_data->transfer_status[stream_data->i] = TRANSFER_IN_FLIGHT;
+ stream_data->i = (stream_data->i + 1) % stream_data->num_transfers;
+ assert(stream_data->num_avail != 0);
+ stream_data->num_avail--;
+
+ /* FIXME We have an inherent issue here with lock ordering between
+ * stream->lock and libusb's underlying event lock, so we
+ * have to drop the stream->lock as a workaround.
+ *
+ * This implies that a callback can potentially execute,
+ * including a callback for this transfer. Therefore, the transfer
+ * has been setup and its metadata logged.
+ *
+ * Ultimately, we need to review our async scheme and associated
+ * lock schemes.
+ */
+ MUTEX_UNLOCK(&stream->lock);
+ status = libusb_submit_transfer(transfer);
+ MUTEX_LOCK(&stream->lock);
+
+ if (status != 0) {
+ log_error("Failed to submit transfer in %s: %s\n",
+ __FUNCTION__, libusb_error_name(status));
+
+ /* We need to undo the metadata we updated prior to dropping
+ * the lock and attempting to submit the transfer */
+ assert(stream_data->transfer_status[prev_idx] == TRANSFER_IN_FLIGHT);
+ stream_data->transfer_status[prev_idx] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ if (stream_data->i == 0) {
+ stream_data->i = stream_data->num_transfers - 1;
+ } else {
+ stream_data->i--;
+ }
+ }
+
+ return error_conv(status);
+}
+
+static int lusb_init_stream(void *driver, struct bladerf_stream *stream,
+ size_t num_transfers)
+{
+ int status = 0;
+ size_t i;
+ struct lusb_stream_data *stream_data;
+
+ /* Fill in backend stream information */
+ stream_data = malloc(sizeof(struct lusb_stream_data));
+ if (!stream_data) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Backend stream information */
+ stream->backend_data = stream_data;
+ stream_data->transfers = NULL;
+ stream_data->transfer_status = NULL;
+ stream_data->num_transfers = num_transfers;
+ stream_data->num_avail = 0;
+ stream_data->i = 0;
+ stream_data->out_of_order_event = false;
+
+ stream_data->transfers =
+ malloc(num_transfers * sizeof(struct libusb_transfer *));
+
+ if (stream_data->transfers == NULL) {
+ log_error("Failed to allocate libusb tranfers\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ stream_data->transfer_status =
+ calloc(num_transfers, sizeof(transfer_status));
+
+ if (stream_data->transfer_status == NULL) {
+ log_error("Failed to allocated libusb transfer status array\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ /* Create the libusb transfers */
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ stream_data->transfers[i] = libusb_alloc_transfer(0);
+
+ /* Upon error, start tearing down anything we've started allocating
+ * and report that the stream is in a bad state */
+ if (stream_data->transfers[i] == NULL) {
+
+ /* Note: <= 0 not appropriate as we're dealing
+ * with an unsigned index */
+ while (i > 0) {
+ if (--i) {
+ libusb_free_transfer(stream_data->transfers[i]);
+ stream_data->transfers[i] = NULL;
+ stream_data->transfer_status[i] = TRANSFER_UNINITIALIZED;
+ stream_data->num_avail--;
+ }
+ }
+
+ status = BLADERF_ERR_MEM;
+ break;
+ } else {
+ stream_data->transfer_status[i] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ }
+ }
+
+error:
+ if (status != 0) {
+ free(stream_data->transfer_status);
+ free(stream_data->transfers);
+ free(stream_data);
+ stream->backend_data = NULL;
+ }
+
+ return status;
+}
+
+static int lusb_stream(void *driver, struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ size_t i;
+ int status = 0;
+ void *buffer;
+ struct bladerf_metadata metadata;
+ struct bladerf *dev = stream->dev;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct timeval tv = { 0, LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC };
+
+ /* Currently unused, so zero it out for a sanity check when debugging */
+ memset(&metadata, 0, sizeof(metadata));
+
+ MUTEX_LOCK(&stream->lock);
+
+ /* Set up initial set of buffers */
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ buffer = stream->cb(dev,
+ stream,
+ &metadata,
+ NULL,
+ stream->samples_per_buffer,
+ stream->user_data);
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ /* If we have transfers in flight and the user prematurely
+ * cancels the stream, we'll start shutting down */
+ if (stream_data->num_avail != stream_data->num_transfers) {
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else {
+ /* No transfers have been shipped out yet so we can
+ * simply enter our "done" state */
+ stream->state = STREAM_DONE;
+ }
+
+ /* In either of the above we don't want to attempt to
+ * get any more buffers from the user */
+ break;
+ }
+ } else {
+ buffer = stream->buffers[i];
+ }
+
+ if (buffer != BLADERF_STREAM_NO_DATA) {
+ if((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, buffer, metadata.actual_count);
+ } else {
+ status = submit_transfer(stream, buffer, async_stream_buf_bytes(stream));
+ }
+
+ /* If we failed to submit any transfers, cancel everything in
+ * flight. We'll leave the stream in the running state so we can
+ * have libusb fire off callbacks with the cancelled status*/
+ if (status < 0) {
+ stream->error_code = status;
+ cancel_all_transfers(stream);
+ }
+ }
+ }
+ MUTEX_UNLOCK(&stream->lock);
+
+ /* This loop is required so libusb can do callbacks and whatnot */
+ while (stream->state != STREAM_DONE) {
+ status = libusb_handle_events_timeout(lusb->context, &tv);
+
+ if (status < 0 && status != LIBUSB_ERROR_INTERRUPTED) {
+ log_warning("unexpected value from events processing: "
+ "%d: %s\n", status, libusb_error_name(status));
+ status = error_conv(status);
+ }
+ }
+
+ return status;
+}
+
+/* The top-level code will have aquired the stream->lock for us */
+int lusb_submit_stream_buffer(void *driver, struct bladerf_stream *stream,
+ void *buffer, size_t *length,
+ unsigned int timeout_ms, bool nonblock)
+{
+ int status = 0;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct timespec timeout_abs;
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ if (stream_data->num_avail == stream_data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+
+ return 0;
+ }
+
+ if (stream_data->num_avail == 0) {
+ if (nonblock) {
+ log_debug("Non-blocking buffer submission requested, but no "
+ "transfers are currently available.\n");
+
+ return BLADERF_ERR_WOULD_BLOCK;
+ }
+
+ if (timeout_ms != 0) {
+ status = populate_abs_timeout(&timeout_abs, timeout_ms);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ while (stream_data->num_avail == 0 && status == 0) {
+ status = pthread_cond_timedwait(&stream->can_submit_buffer,
+ &stream->lock,
+ &timeout_abs);
+ }
+ } else {
+ while (stream_data->num_avail == 0 && status == 0) {
+ status = pthread_cond_wait(&stream->can_submit_buffer,
+ &stream->lock);
+ }
+ }
+ }
+
+ if (status == ETIMEDOUT) {
+ log_debug("%s: Timed out waiting for a transfer to become available.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_TIMEOUT;
+ } else if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return submit_transfer(stream, buffer, *length);
+ }
+}
+
+static int lusb_deinit_stream(void *driver, struct bladerf_stream *stream)
+{
+ size_t i;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ libusb_free_transfer(stream_data->transfers[i]);
+ stream_data->transfers[i] = NULL;
+ stream_data->transfer_status[i] = TRANSFER_UNINITIALIZED;
+ }
+
+ free(stream_data->transfers);
+ free(stream_data->transfer_status);
+ free(stream->backend_data);
+
+ stream->backend_data = NULL;
+ return 0;
+}
+
+static const struct usb_fns libusb_fns = {
+ FIELD_INIT(.probe, lusb_probe),
+ FIELD_INIT(.open, lusb_open),
+ FIELD_INIT(.close, lusb_close),
+ FIELD_INIT(.get_vid_pid, lusb_get_vid_pid),
+ FIELD_INIT(.get_flash_id, NULL),
+ FIELD_INIT(.get_handle, lusb_get_handle),
+ FIELD_INIT(.get_speed, lusb_get_speed),
+ FIELD_INIT(.change_setting, lusb_change_setting),
+ FIELD_INIT(.control_transfer, lusb_control_transfer),
+ FIELD_INIT(.bulk_transfer, lusb_bulk_transfer),
+ FIELD_INIT(.get_string_descriptor, lusb_get_string_descriptor),
+ FIELD_INIT(.init_stream, lusb_init_stream),
+ FIELD_INIT(.stream, lusb_stream),
+ FIELD_INIT(.submit_stream_buffer, lusb_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, lusb_deinit_stream),
+ FIELD_INIT(.open_bootloader, lusb_open_bootloader),
+ FIELD_INIT(.close_bootloader, lusb_close_bootloader),
+};
+
+const struct usb_driver usb_driver_libusb = {
+ FIELD_INIT(.fn, &libusb_fns),
+ FIELD_INIT(.id, BLADERF_BACKEND_LIBUSB),
+};
diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_access.c b/Radio/HW/BladeRF/src/backend/usb/nios_access.c
new file mode 100644
index 0000000..354d80b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/nios_access.c
@@ -0,0 +1,1322 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "log.h"
+#include "conversions.h"
+
+#include "usb.h"
+#include "nios_access.h"
+#include "nios_pkt_formats.h"
+
+#include "board/board.h"
+#include "helpers/version.h"
+
+#if 0
+static void print_buf(const char *msg, const uint8_t *buf, size_t len)
+{
+ size_t i;
+ if (msg != NULL) {
+ fputs(msg, stderr);
+ }
+
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, " %02x", buf[i]);
+ }
+
+ fputc('\n', stderr);
+}
+#else
+#define print_buf(msg, data, len) do {} while(0)
+#endif
+
+/* Buf is assumed to be NIOS_PKT_LEN bytes */
+static int nios_access(struct bladerf *dev, uint8_t *buf)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ int status;
+
+ print_buf("NIOS II REQ:", buf, NIOS_PKT_LEN);
+
+ /* Send the command */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, buf,
+ NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS);
+ if (status != 0) {
+ log_error("Failed to send NIOS II request: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Retrieve the request */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN, buf,
+ NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS);
+ if (status != 0) {
+ log_error("Failed to receive NIOS II response: %s\n",
+ bladerf_strerror(status));
+ }
+
+ print_buf("NIOS II res:", buf, NIOS_PKT_LEN);
+
+ return status;
+}
+
+/* Variant that doesn't output to log_error on error. */
+static int nios_access_quiet(struct bladerf *dev, uint8_t *buf)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ int status;
+
+ print_buf("NIOS II REQ:", buf, NIOS_PKT_LEN);
+
+ /* Send the command */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, buf,
+ NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Retrieve the request */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN, buf,
+ NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS);
+
+ print_buf("NIOS II res:", buf, NIOS_PKT_LEN);
+
+ return status;
+}
+
+static int nios_8x8_read(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint8_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x8_pack(buf, id, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x8_resp_unpack(buf, NULL, NULL, NULL, data, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ *data = 0;
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_8x8_write(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint8_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x8_pack(buf, id, true, addr, data);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x8_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_8x16_read(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint16_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+ uint16_t tmp;
+
+ nios_pkt_8x16_pack(buf, id, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x16_resp_unpack(buf, NULL, NULL, NULL, &tmp, &success);
+
+ if (success) {
+ *data = tmp;
+ return 0;
+ } else {
+ *data = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_8x16_write(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint16_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x16_pack(buf, id, true, addr, data);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x16_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static void log_debug_8x32_pkt(const uint8_t *buf) {
+ if (buf == NULL) {
+ log_debug("Failed to log packet: packet buffer is NULL\n");
+ return;
+ }
+
+ uint8_t target_id;
+ bool write;
+ uint8_t addr;
+ uint32_t data;
+ bool success;
+
+ nios_pkt_8x32_resp_unpack(buf, &target_id, &write, &addr, &data, &success);
+
+ const char *operation = write ? "Write" : "Read";
+ const char *status = success ? "Success" : "Failure";
+
+ log_debug("Packet Magic: 0x%02x\n", buf[NIOS_PKT_8x32_IDX_MAGIC]);
+ log_debug("Packet Target: %s\n", target2str(target_id));
+ log_debug("Packet Operation: %s\n", operation);
+ log_debug("Packet Address: 0x%02x\n", addr);
+ log_debug("Packet Data: 0x%08x\n", data);
+ log_debug("Packet Status: %s\n", status);
+}
+
+static int nios_8x32_read(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint32_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x32_pack(buf, id, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x32_resp_unpack(buf, NULL, NULL, NULL, data, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ *data = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ log_debug_8x32_pkt(buf);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_8x32_write(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint32_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x32_pack(buf, id, true, addr, data);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ log_debug_8x32_pkt(buf);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_16x64_read(struct bladerf *dev,
+ uint8_t id,
+ uint16_t addr,
+ uint64_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_16x64_pack(buf, id, false, addr, 0);
+
+ /* RFIC access times out occasionally, and this is fine. */
+ if (NIOS_PKT_16x64_TARGET_RFIC == id) {
+ status = nios_access_quiet(dev, buf);
+ } else {
+ status = nios_access(dev, buf);
+ }
+
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_16x64_resp_unpack(buf, NULL, NULL, NULL, data, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ *data = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_16x64_write(struct bladerf *dev,
+ uint8_t id,
+ uint16_t addr,
+ uint64_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_16x64_pack(buf, id, true, addr, data);
+
+ /* RFIC access times out occasionally, and this is fine. */
+ if (NIOS_PKT_16x64_TARGET_RFIC == id) {
+ status = nios_access_quiet(dev, buf);
+ } else {
+ status = nios_access(dev, buf);
+ }
+
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_16x64_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_32x32_read(struct bladerf *dev, uint32_t id,
+ uint32_t addr, uint32_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_32x32_pack(buf, id, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, data, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ *data = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_32x32_write(struct bladerf *dev, uint32_t id,
+ uint32_t addr, uint32_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_32x32_pack(buf, id, true, addr, data);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_32x32_masked_read(struct bladerf *dev, uint8_t id,
+ uint32_t mask, uint32_t *val)
+{
+ int status;
+ bool success;
+ uint8_t buf[NIOS_PKT_LEN];
+
+ /* The address is used as a mask of bits to read and return */
+ nios_pkt_32x32_pack(buf, id, false, mask, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, val, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ *val = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_32x32_masked_write(struct bladerf *dev, uint8_t id,
+ uint32_t mask, uint32_t val)
+{
+ int status;
+ bool success;
+ uint8_t buf[NIOS_PKT_LEN];
+
+ /* The address is used as a mask of bits to read and return */
+ nios_pkt_32x32_pack(buf, id, true, mask, val);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+int nios_config_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_CONTROL, 0, val);
+
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val);
+ }
+
+ return status;
+}
+
+int nios_config_write(struct bladerf *dev, uint32_t val)
+{
+ int status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_CONTROL, 0, val);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, val);
+ } else {
+ log_error("%s: Failed to write 0x%08x\n", __FUNCTION__, val);
+ }
+
+ return status;
+}
+
+int nios_get_fpga_version(struct bladerf *dev, struct bladerf_version *ver)
+{
+ uint32_t regval;
+ int status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_VERSION, 0, &regval);
+
+ if (status == 0) {
+ log_verbose("%s: Read FPGA version word: 0x%08x\n",
+ __FUNCTION__, regval);
+
+ ver->major = (regval >> 24) & 0xff;
+ ver->minor = (regval >> 16) & 0xff;
+ ver->patch = LE16_TO_HOST(regval & 0xffff);
+
+ snprintf((char*)ver->describe, BLADERF_VERSION_STR_MAX,
+ "%d.%d.%d", ver->major, ver->minor, ver->patch);
+
+ return 0;
+ }
+
+ return status;
+}
+
+int nios_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *timestamp)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ uint8_t addr;
+ bool success;
+
+ switch (dir) {
+ case BLADERF_RX:
+ addr = NIOS_PKT_8x64_TIMESTAMP_RX;
+ break;
+
+ case BLADERF_TX:
+ addr = NIOS_PKT_8x64_TIMESTAMP_TX;
+ break;
+
+ default:
+ log_debug("Invalid direction: %d\n", dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ nios_pkt_8x64_pack(buf, NIOS_PKT_8x64_TARGET_TIMESTAMP, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x64_resp_unpack(buf, NULL, NULL, NULL, timestamp, &success);
+
+ if (success) {
+ log_verbose("%s: Read %s timestamp: %" PRIu64 "\n", __FUNCTION__,
+ direction2str(dir), *timestamp);
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ *timestamp = 0;
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+int nios_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ int status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_SI5338, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%02x from addr 0x%02x\n",
+ __FUNCTION__, *data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ int status = nios_8x8_write(dev, NIOS_PKT_8x8_TARGET_SI5338, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%02x to addr 0x%02x\n",
+ __FUNCTION__, data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ int status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_LMS6, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%02x from addr 0x%02x\n",
+ __FUNCTION__, *data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ int status = nios_8x8_write(dev, NIOS_PKT_8x8_TARGET_LMS6, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%02x to addr 0x%02x\n",
+ __FUNCTION__, data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data)
+{
+ int status;
+
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_INA219, addr, data);
+ if (status == 0) {
+ log_verbose("%s: Read 0x%04x from addr 0x%02x\n",
+ __FUNCTION__, *data, addr);
+ }
+
+ return status;
+}
+
+int nios_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data)
+{
+ int status;
+
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_INA219, addr, data);
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%04x to addr 0x%02x\n",
+ __FUNCTION__, data, addr);
+ }
+
+ return status;
+}
+
+#define VERBOSE_OUT_SINGLEBYTE "%s: %s 0x%02x @ addr 0x%04x\n"
+#define VERBOSE_OUT_MULTIBYTE "%s: %s 0x%02x @ addr 0x%04x (%d/%d)\n"
+
+int nios_ad9361_spi_read(struct bladerf *dev, uint16_t cmd, uint64_t *data)
+{
+ int status;
+
+ status = nios_16x64_read(dev, NIOS_PKT_16x64_TARGET_AD9361, cmd, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (log_get_verbosity() == BLADERF_LOG_LEVEL_VERBOSE && status == 0) {
+ size_t bytes = (((cmd >> 12) & 0x7) + 1);
+ size_t addr = cmd & 0xFFF;
+
+ if (bytes > 1) {
+ size_t i;
+ for (i = 0; i < bytes; ++i) {
+ uint8_t byte = (*data >> (56 - 8 * i)) & 0xFF;
+ log_verbose(VERBOSE_OUT_MULTIBYTE, "ad9361_spi", " MRead", byte,
+ addr - i, i + 1, bytes);
+ }
+ } else {
+ uint8_t byte = (*data >> 56) & 0xFF;
+ log_verbose(VERBOSE_OUT_SINGLEBYTE, "ad9361_spi", " Read", byte,
+ addr);
+ }
+ }
+#endif
+
+ return status;
+}
+
+int nios_ad9361_spi_write(struct bladerf *dev, uint16_t cmd, uint64_t data)
+{
+ int status;
+
+ status = nios_16x64_write(dev, NIOS_PKT_16x64_TARGET_AD9361, cmd, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (log_get_verbosity() == BLADERF_LOG_LEVEL_VERBOSE && status == 0) {
+ size_t bytes = (((cmd >> 12) & 0x7) + 1) & 0xFF;
+ size_t addr = cmd & 0xFFF;
+
+ if (bytes > 1) {
+ size_t i;
+ for (i = 0; i < bytes; ++i) {
+ uint8_t byte = (data >> (56 - 8 * i)) & 0xFF;
+ log_verbose(VERBOSE_OUT_MULTIBYTE, "ad9361_spi", "MWrite", byte,
+ addr - i, i + 1, bytes);
+ }
+ } else {
+ uint8_t byte = (data >> 56) & 0xFF;
+ log_verbose(VERBOSE_OUT_SINGLEBYTE, "ad9361_spi", " Write", byte,
+ addr);
+ }
+ }
+#endif
+
+ return status;
+}
+
+int nios_adi_axi_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ int status;
+
+ status = nios_32x32_read(dev, NIOS_PKT_32x32_TARGET_ADI_AXI, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08" PRIx32 " from addr 0x%04" PRIx32 "\n",
+ __FUNCTION__, *data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_adi_axi_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ int status;
+
+ status = nios_32x32_write(dev, NIOS_PKT_32x32_TARGET_ADI_AXI, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08" PRIx32 " to addr 0x%04" PRIx32 "\n",
+ __FUNCTION__, data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ int status;
+
+ status = nios_32x32_read(dev, NIOS_PKT_32x32_TARGET_WB_MSTR, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08" PRIx32 " from addr 0x%04" PRIx32 "\n",
+ __FUNCTION__, *data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ int status;
+
+ status = nios_32x32_write(dev, NIOS_PKT_32x32_TARGET_WB_MSTR, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08" PRIx32 " to addr 0x%04" PRIx32 "\n",
+ __FUNCTION__, data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rfic_command_read(struct bladerf *dev, uint16_t cmd, uint64_t *data)
+{
+ int status;
+
+ status = nios_16x64_read(dev, NIOS_PKT_16x64_TARGET_RFIC, cmd, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%04x 0x%08x\n", __FUNCTION__, cmd, *data);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rfic_command_write(struct bladerf *dev, uint16_t cmd, uint64_t data)
+{
+ int status;
+
+ status = nios_16x64_write(dev, NIOS_PKT_16x64_TARGET_RFIC, cmd, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Write 0x%04x 0x%08x\n", __FUNCTION__, cmd, data);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rffe_control_read(struct bladerf *dev, uint32_t *value)
+{
+ int status;
+
+ status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_RFFE_CSR, 0, value);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *value);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rffe_control_write(struct bladerf *dev, uint32_t value)
+{
+ int status;
+
+ status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_RFFE_CSR, 0, value);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, value);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rffe_fastlock_save(struct bladerf *dev, bool is_tx,
+ uint8_t rffe_profile, uint16_t nios_profile)
+{
+ int status;
+ uint8_t addr;
+ uint32_t data = 0;
+
+ addr = is_tx ? 1 : 0;
+ data = (rffe_profile << 16) | nios_profile;
+
+ status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_FASTLOCK, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, data);
+ }
+#endif
+
+ return status;
+}
+
+int nios_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value)
+{
+ int status;
+
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_AD56X1_DAC, 0, value);
+ if (status == 0) {
+ log_verbose("%s: Read 0x%04x\n", __FUNCTION__, *value);
+ }
+
+ return status;
+}
+
+int nios_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value)
+{
+ int status;
+
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AD56X1_DAC, 0, value);
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%04x\n", __FUNCTION__, value);
+ }
+
+ return status;
+}
+
+int nios_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data)
+{
+ int status;
+
+ status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_ADF400X, addr, data);
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x from addr 0x%02x\n", __FUNCTION__, *data, addr);
+ }
+
+ return status;
+}
+
+int nios_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data)
+{
+ int status;
+
+ data &= ~(0x3);
+
+ status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_ADF400X, 0, data | (addr & 0x3));
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x to addr 0x%02x\n", __FUNCTION__, data, addr);
+ }
+
+ return status;
+}
+
+int nios_vctcxo_trim_dac_write(struct bladerf *dev, uint8_t addr, uint16_t value)
+{
+ return nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_VCTCXO_DAC, addr, value);
+}
+
+int nios_vctcxo_trim_dac_read(struct bladerf *dev, uint8_t addr, uint16_t *value)
+{
+ return nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_VCTCXO_DAC, addr, value);
+}
+
+int nios_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ int status;
+
+ status = nios_8x8_write(dev,
+ NIOS_PKT_8x8_TARGET_VCTCXO_TAMER, 0xff,
+ (uint8_t) mode);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote mode=0x%02x\n", __FUNCTION__, mode);
+ }
+
+ return status;
+}
+
+int nios_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ int status;
+ uint8_t tmp;
+
+ *mode = BLADERF_VCTCXO_TAMER_INVALID;
+
+ status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_VCTCXO_TAMER, 0xff, &tmp);
+ if (status == 0) {
+ log_verbose("%s: Read mode=0x%02x\n", __FUNCTION__, tmp);
+
+ switch ((bladerf_vctcxo_tamer_mode) tmp) {
+ case BLADERF_VCTCXO_TAMER_DISABLED:
+ case BLADERF_VCTCXO_TAMER_1_PPS:
+ case BLADERF_VCTCXO_TAMER_10_MHZ:
+ *mode = (bladerf_vctcxo_tamer_mode) tmp;
+ break;
+
+ default:
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+
+int nios_get_iq_gain_correction(struct bladerf *dev, bladerf_channel ch,
+ int16_t *value)
+{
+ int status = BLADERF_ERR_INVAL;
+ uint16_t tmp = 0;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_RX_GAIN, &tmp);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_TX_GAIN, &tmp);
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ }
+
+ *value = (int16_t) tmp;
+
+ if (status == 0) {
+ log_verbose("%s: Read %s %d\n",
+ __FUNCTION__, channel2str(ch), *value);
+ }
+
+ return status;
+}
+
+int nios_get_iq_phase_correction(struct bladerf *dev, bladerf_channel ch,
+ int16_t *value)
+{
+ int status = BLADERF_ERR_INVAL;
+ uint16_t tmp = 0;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_RX_PHASE, &tmp);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_TX_PHASE, &tmp);
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ }
+
+ *value = (int16_t) tmp;
+
+ if (status == 0) {
+ log_verbose("%s: Read %s %d\n",
+ __FUNCTION__, channel2str(ch), *value);
+ }
+
+ return status;
+}
+
+int nios_set_iq_gain_correction(struct bladerf *dev, bladerf_channel ch,
+ int16_t value)
+{
+ int status = BLADERF_ERR_INVAL;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ log_verbose("Setting RX IQ Correction gain: %d\n", value);
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_RX_GAIN, value);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ log_verbose("Setting TX IQ Correction gain: %d\n", value);
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_TX_GAIN, value);
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ }
+
+ if (status == 0) {
+ log_verbose("%s: Wrote %s %d\n",
+ __FUNCTION__, channel2str(ch), value);
+ }
+
+ return status;
+}
+
+int nios_set_iq_phase_correction(struct bladerf *dev, bladerf_channel ch,
+ int16_t value)
+{
+ int status = BLADERF_ERR_INVAL;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ log_verbose("Setting RX IQ Correction phase: %d\n", value);
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_RX_PHASE, value);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ log_verbose("Setting TX IQ Correction phase: %d\n", value);
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_TX_PHASE, value);
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ }
+
+ if (status == 0) {
+ log_verbose("%s: Wrote %s %d\n",
+ __FUNCTION__, channel2str(ch), value);
+ }
+
+
+ return status;
+}
+
+int nios_set_agc_dc_correction(struct bladerf *dev, int16_t q_max, int16_t i_max,
+ int16_t q_mid, int16_t i_mid,
+ int16_t q_low, int16_t i_low)
+{
+ int status;
+
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MAX, q_max);
+
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MAX, i_max);
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MID, q_mid);
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MID, i_mid);
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MIN, q_low);
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MIN, i_low);
+
+ return status;
+}
+
+int nios_xb200_synth_write(struct bladerf *dev, uint32_t value)
+{
+ int status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_ADF4351, 0, value);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, value);
+ }
+
+ return status;
+}
+
+int nios_expansion_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_32x32_masked_read(dev, NIOS_PKT_32x32_TARGET_EXP,
+ 0xffffffff, val);
+
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val);
+ }
+
+ return status;
+}
+
+int nios_expansion_gpio_write(struct bladerf *dev, uint32_t mask, uint32_t val)
+{
+ int status = nios_32x32_masked_write(dev, NIOS_PKT_32x32_TARGET_EXP,
+ mask, val);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x (with mask 0x%08x)\n",
+ __FUNCTION__, val, mask);
+ }
+
+ return status;
+}
+
+int nios_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_32x32_masked_read(dev, NIOS_PKT_32x32_TARGET_EXP_DIR,
+ 0xffffffff, val);
+
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val);
+ }
+
+ return status;
+}
+
+int nios_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask, uint32_t val)
+{
+ int status = nios_32x32_masked_write(dev, NIOS_PKT_32x32_TARGET_EXP_DIR,
+ mask, val);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x (with mask 0x%08x)\n",
+ __FUNCTION__, val, mask);
+ }
+
+ return status;
+}
+
+int nios_retune(struct bladerf *dev, bladerf_channel ch,
+ uint64_t timestamp, uint16_t nint, uint32_t nfrac,
+ uint8_t freqsel, uint8_t vcocap, bool low_band,
+ uint8_t xb_gpio, bool quick_tune)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+
+ uint8_t resp_flags;
+ uint64_t duration;
+
+ if (timestamp == NIOS_PKT_RETUNE_CLEAR_QUEUE) {
+ log_verbose("Clearing %s retune queue.\n", channel2str(ch));
+ } else {
+ log_verbose("%s: channel=%s timestamp=%"PRIu64" nint=%u nfrac=%u\n\t\t\t\t"
+ "freqsel=0x%02x vcocap=0x%02x low_band=%d quick_tune=%d\n",
+ __FUNCTION__, channel2str(ch), timestamp, nint, nfrac,
+ freqsel, vcocap, low_band, quick_tune);
+ }
+
+ nios_pkt_retune_pack(buf, ch, timestamp,
+ nint, nfrac, freqsel, vcocap, low_band,
+ xb_gpio, quick_tune);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_retune_resp_unpack(buf, &duration, &vcocap, &resp_flags);
+
+ if (resp_flags & NIOS_PKT_RETUNERESP_FLAG_TSVTUNE_VALID) {
+ log_verbose("%s retune operation: vcocap=%u, duration=%"PRIu64"\n",
+ channel2str(ch), vcocap, duration);
+ } else {
+ log_verbose("%s operation duration: %"PRIu64"\n",
+ channel2str(ch), duration);
+ }
+
+ if ((resp_flags & NIOS_PKT_RETUNERESP_FLAG_SUCCESS) == 0) {
+ if (timestamp == BLADERF_RETUNE_NOW) {
+ log_debug("FPGA tuning reported failure.\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ } else {
+ log_debug("The FPGA's retune queue is full. Try again after "
+ "a previous request has completed.\n");
+ status = BLADERF_ERR_QUEUE_FULL;
+ }
+ }
+
+ return status;
+}
+
+int nios_retune2(struct bladerf *dev, bladerf_channel ch,
+ uint64_t timestamp, uint16_t nios_profile,
+ uint8_t rffe_profile, uint8_t port,
+ uint8_t spdt)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+
+ uint8_t resp_flags;
+ uint64_t duration;
+
+ if (timestamp == NIOS_PKT_RETUNE2_CLEAR_QUEUE) {
+ log_verbose("Clearing %s retune queue.\n", channel2str(ch));
+ } else {
+ log_verbose("%s: channel=%s timestamp=%"PRIu64" nios_profile=%u "
+ "rffe_profile=%u\n\t\t\t\tport=0x%02x spdt=0x%02x\n",
+ __FUNCTION__, channel2str(ch), timestamp, nios_profile,
+ rffe_profile, port, spdt);
+ }
+
+ nios_pkt_retune2_pack(buf, ch, timestamp, nios_profile, rffe_profile,
+ port, spdt);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_retune2_resp_unpack(buf, &duration, &resp_flags);
+
+ if (resp_flags & NIOS_PKT_RETUNE2_RESP_FLAG_TSVTUNE_VALID) {
+ log_verbose("%s retune operation: duration=%"PRIu64"\n",
+ channel2str(ch), duration);
+ } else {
+ log_verbose("%s operation duration: %"PRIu64"\n",
+ channel2str(ch), duration);
+ }
+
+ if ((resp_flags & NIOS_PKT_RETUNE2_RESP_FLAG_SUCCESS) == 0) {
+ if (timestamp == BLADERF_RETUNE_NOW) {
+ log_debug("FPGA tuning reported failure.\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ } else {
+ log_debug("The FPGA's retune queue is full. Try again after "
+ "a previous request has completed.\n");
+ status = BLADERF_ERR_QUEUE_FULL;
+ }
+ }
+
+ return status;
+}
+
+int nios_read_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t *value)
+{
+ int status;
+ uint8_t nios_id;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ nios_id = NIOS_PKT_8x8_TX_TRIGGER_CTL;
+ break;
+
+ case BLADERF_CHANNEL_RX(0):
+ nios_id = NIOS_PKT_8x8_RX_TRIGGER_CTL;
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Only 1 external trigger is currently supported */
+ switch (trigger) {
+ case BLADERF_TRIGGER_J71_4:
+ case BLADERF_TRIGGER_J51_1:
+ case BLADERF_TRIGGER_MINI_EXP_1:
+ break;
+
+ default:
+ log_debug("Invalid trigger: %d\n", trigger);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = nios_8x8_read(dev, nios_id, 0, value);
+ if (status == 0) {
+ log_verbose("%s trigger read value 0x%02x\n", channel2str(ch), *value);
+ }
+
+ return status;
+}
+
+int nios_write_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t value)
+{
+ int status;
+ uint8_t nios_id;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ nios_id = NIOS_PKT_8x8_TX_TRIGGER_CTL;
+ break;
+
+ case BLADERF_CHANNEL_RX(0):
+ nios_id = NIOS_PKT_8x8_RX_TRIGGER_CTL;
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Only 1 external trigger is currently supported */
+ switch (trigger) {
+ case BLADERF_TRIGGER_J71_4:
+ case BLADERF_TRIGGER_J51_1:
+ case BLADERF_TRIGGER_MINI_EXP_1:
+ break;
+
+ default:
+ log_debug("Invalid trigger: %d\n", trigger);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = nios_8x8_write(dev, nios_id, 0, value);
+ if (status == 0) {
+ log_verbose("%s trigger write value 0x%02x\n", channel2str(ch), value);
+ }
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_access.h b/Radio/HW/BladeRF/src/backend/usb/nios_access.h
new file mode 100644
index 0000000..f02270e
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/nios_access.h
@@ -0,0 +1,582 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file defines functionality used to communicate with the NIOS II
+ * soft-core processor running on the FPGA versions >= v0.3.0
+ *
+ * The host communicates with the NIOS II via USB transfers to the Cypress FX3,
+ * which then communicates with the FPGA via a UART. This module packs and
+ * submits the requests, and receives and unpacks the responses.
+ *
+ * See the files in the top-level fpga_common/ directory for description
+ * and macros pertaining to the legacy packet format.
+ */
+
+#ifndef BACKEND_USB_NIOS_ACCESS_H_
+#define BACKEND_USB_NIOS_ACCESS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "board/board.h"
+#include "usb.h"
+
+/**
+ * Read from the FPGA's config register
+ *
+ * @param dev Device handle
+ * @param[out] val On success, updated with config register value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_config_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to FPGA's config register
+ *
+ * @param dev Device handle
+ * @param[in] val Register value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_config_write(struct bladerf *dev, uint32_t val);
+
+/**
+ * Read the FPGA version into the provided version structure
+ *
+ * @param dev Device handle
+ * @param[out] ver Updated with FPGA version (upon success)
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_fpga_version(struct bladerf *dev, struct bladerf_version *ver);
+
+/**
+ * Read a timestamp counter value
+ *
+ * @param dev Device handle
+ * @param[in] dir Stream direction
+ * @param[out] timestamp On success, updated with the timestamp value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *timestamp);
+
+/**
+ * Read from an Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data On success, updated with read data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+/**
+ * Write to an Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[in] data Data to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data);
+
+/**
+ * Read from an LMS6002D register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data On success, updated with data read from the device
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+/**
+ * Write to an LMS6002D register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data Register data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data);
+
+/**
+ * Read from an INA219 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data);
+
+/**
+ * Write to an INA219 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data);
+
+/**
+ * Read the AD9361 over SPI.
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ad9361_spi_read(struct bladerf *dev, uint16_t cmd, uint64_t *data);
+
+/**
+ * Write to the AD9361 over SPI.
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ad9361_spi_write(struct bladerf *dev, uint16_t cmd, uint64_t data);
+
+/**
+ * Read the ADI AXI memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_adi_axi_read(struct bladerf *dev, uint32_t cmd, uint32_t *data);
+
+/**
+ * Write to the ADI AXI memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_adi_axi_write(struct bladerf *dev, uint32_t cmd, uint32_t data);
+
+/**
+ * Read the Wishbone Master memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data);
+
+/**
+ * Write to the Wishbone Master memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data);
+
+/**
+ * Read RFIC command
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command: `(command & 0xFF) + ((channel & 0xF) << 8)`
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rfic_command_read(struct bladerf *dev, uint16_t cmd, uint64_t *data);
+
+/**
+ * Write RFIC command
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command: `(command & 0xFF) + ((channel & 0xF) << 8)`
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rfic_command_write(struct bladerf *dev, uint16_t cmd, uint64_t data);
+
+/**
+ * Read RFFE control register.
+ *
+ * @param dev Device handle
+ * @param[in] len Data buffer length
+ * @param[out] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rffe_control_read(struct bladerf *dev, uint32_t *value);
+
+/**
+ * Write RFFE control register.
+ *
+ * @param dev Device handle
+ * @param[in] len Data buffer length
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rffe_control_write(struct bladerf *dev, uint32_t value);
+
+/**
+ * Save an RFFE fast lock profile to the Nios.
+ *
+ * @param dev Device handle
+ * @param[in] is_tx True if TX profile, false if RX profile
+ * @param[in] rffe_profile RFFE profile to save
+ * @param[in] nios_profile Where to save the profile in the Nios
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rffe_fastlock_save(struct bladerf *dev, bool is_tx,
+ uint8_t rffe_profile, uint16_t nios_profile);
+
+/**
+ * Write to the AD56X1 VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value);
+
+/**
+ * Read the AD56X1 VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[out] value Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value);
+
+/**
+ * Write to the ADF400X.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data);
+
+/**
+ * Read from the ADF400X.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data);
+
+/**
+ * Write to a VCTCXO trim DAC register.
+ *
+ * @param dev Device handle
+ * @param[in] addr Register
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_vctcxo_trim_dac_write(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t value);
+
+/**
+ * Read from a VCTCXO trim DAC register.
+ *
+ * @param dev Device handle
+ * @param[in] addr Register
+ * @param[out] value Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_vctcxo_trim_dac_read(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t *value);
+
+/**
+ * Write VCTCXO tamer mode selection
+ *
+ * @param dev Device handle
+ * @param[in] mode Tamer mode to use, or BLADERF_VCTCXO_TAMER_DISABLED
+ * to disable the tamer.
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode);
+
+/**
+ * Read the current VCTCXO mode selection
+ *
+ * @param dev Device handle
+ * @param[out] mode Current tamer mode in use.
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode);
+
+/**
+ * Read a IQ gain correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] value On success, updated with phase correction value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+/**
+ * Read a IQ phase correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] value On success, updated with phase correction value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+/**
+ * Write an IQ gain correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] value Correction value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_set_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+
+/**
+ * Write an IQ phase correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] value Correction value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_set_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+
+/**
+ * Write AGC DC LUT values
+ *
+ * @param[in] dev Device handle
+ * @param[in] q_max Q DC offset at Max gain
+ * @param[in] i_max I DC offset at Max gain
+ * @param[in] q_mid Q DC offset at Mid gain
+ * @param[in] i_mid I DC offset at Mid gain
+ * @param[in] q_low Q DC offset at Low gain
+ * @param[in] i_low I DC offset at Low gain
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_set_agc_dc_correction(struct bladerf *dev,
+ int16_t q_max,
+ int16_t i_max,
+ int16_t q_mid,
+ int16_t i_mid,
+ int16_t q_low,
+ int16_t i_low);
+/**
+ * Write a value to the XB-200's ADF4351 synthesizer
+ *
+ * @param dev Device handle
+ * @param[in] value Write value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_xb200_synth_write(struct bladerf *dev, uint32_t value);
+
+/**
+ * Read from expanion GPIO
+ *
+ * @param dev Device handle
+ * @param[out] value On success, updated with read value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_expansion_gpio_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to expansion GPIO
+ *
+ * @param dev Device handle
+ * @param[in] mask Mask denoting bits to write
+ * @param[in] value Write value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_expansion_gpio_write(struct bladerf *dev, uint32_t mask, uint32_t val);
+
+/**
+ * Read from expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[out] value On success, updated with read value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[in] mask Mask denoting bits to configure
+ * @param[in] output '1' bit denotes output, '0' bit denotes input
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t outputs);
+
+/**
+ * Dummy handler for a retune request, which is not supported on
+ * earlier FPGA versions.
+ *
+ * All of the following parameters are ignored.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] timestamp Time to schedule retune at
+ * @param[in] nint Integer portion of frequency multiplier
+ * @param[in] nfrac Fractional portion of frequency multiplier
+ * @param[in] freqsel VCO and divider selection
+ * @param[in] low_band High vs low band selection
+ * @param[in] xb_gpio XB configuration bits
+ * @param[in] quick_tune Denotes quick tune should be used instead of
+ * tuning algorithm
+ *
+ * @return BLADERF_ERR_UNSUPPORTED
+ */
+int nios_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t timestamp,
+ uint16_t nint,
+ uint32_t nfrac,
+ uint8_t freqsel,
+ uint8_t vcocap,
+ bool low_band,
+ uint8_t xb_gpio,
+ bool quick_tune);
+
+/**
+ * Handler for a retune request on bladeRF2 devices. The RFFEs used in these
+ * devices have a concept called fast lock profiles that store all the VCO
+ * information needed to quickly retune to a different frequency. The RFFE
+ * can store up to 8 profiles for RX, and a separate 8 for TX. To use more
+ * profiles, they must be stored in the baseband processor (e.g. the FPGA
+ * or Nios) and loaded into one of the 8 slots as needed.
+ *
+ * Each of these profiles consumes 16 bytes not including the timestamp and
+ * RF port/switch information. This is too large to fit into a single Nios
+ * packet at this time, so to improve retune time, the profiles are stored
+ * in the Nios and the Host will schedule retunes by specifying the Nios
+ * profile to load into the specified RFFE profile slot.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] timestamp Time to schedule retune at
+ * @param[in] nios_profile Nios profile number (0-N) to load into the RFFE
+ * @param[in] rffe_profile RFFE fast lock profile number (0-7) into which
+ * the Nios profile will be loaded.
+ * @param[in] port RFFE port settings
+ * @param[in] spdt RF SPDT switch settings
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_retune2(struct bladerf *dev, bladerf_channel ch,
+ uint64_t timestamp, uint16_t nios_profile,
+ uint8_t rffe_profile, uint8_t port, uint8_t spdt);
+
+/**
+ * Read trigger register value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] trigger Trigger to read from
+ * @param[out] value On success, updated with register value
+ *
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error
+ */
+int nios_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *value);
+
+/**
+ * Write trigger register value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] trigger Trigger to read
+ * @param[in] value Value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error
+ */
+int nios_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t value);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c
new file mode 100644
index 0000000..e4fe727
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c
@@ -0,0 +1,744 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file implements functionality used to communicate with the NIOS II
+ * soft-core processor running on the FPGA versions prior to v0.2.x.
+ * (Although post v0.2.x FPGAs support this for reverse compatibility).
+ *
+ * The host communicates with the NIOS II via USB transfers to the Cypress FX3,
+ * which then communicates with the FPGA via a UART.
+ *
+ * See the files in the top-level fpga_common/ directory for description
+ * and macros pertaining to the legacy packet format.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "log.h"
+
+#include "usb.h"
+#include "nios_legacy_access.h"
+#include "nios_pkt_formats.h"
+
+#include "board/board.h"
+#include "board/bladerf1/capabilities.h"
+#include "helpers/version.h"
+
+#include "board/bladerf1/capabilities.h"
+
+#if 0
+static void print_buf(const char *msg, const uint8_t *buf, size_t len)
+{
+ size_t i;
+ if (msg != NULL) {
+ fputs(msg, stderr);
+ }
+
+ for (i = 0; i < len; i++) {
+ if (i == 0) {
+ fprintf(stderr, " 0x%02x,", buf[i]);
+ } else if ((i + 1) % 8 == 0) {
+ fprintf(stderr, " 0x%02x,\n ", buf[i]);
+ } else {
+ fprintf(stderr, " 0x%02x,", buf[i]);
+ }
+ }
+
+ fputc('\n', stderr);
+}
+#else
+#define print_buf(msg, data, len) do {} while(0)
+#endif
+
+/* Access device/module via the legacy NIOS II packet format. */
+static int nios_access(struct bladerf *dev, uint8_t peripheral,
+ usb_direction dir, struct uart_cmd *cmd,
+ size_t len)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ int status;
+ size_t i;
+ uint8_t buf[16] = { 0 };
+ const uint8_t pkt_mode_dir = (dir == USB_DIR_HOST_TO_DEVICE) ?
+ NIOS_PKT_LEGACY_MODE_DIR_WRITE :
+ NIOS_PKT_LEGACY_MODE_DIR_READ;
+
+ assert(len <= ((sizeof(buf) - 2) / 2));
+
+ /* Populate the buffer for transfer, given address data pairs */
+ buf[0] = NIOS_PKT_LEGACY_MAGIC;
+ buf[1] = pkt_mode_dir | peripheral | (uint8_t)len;
+
+ for (i = 0; i < len; i++) {
+ buf[i * 2 + 2] = cmd[i].addr;
+ buf[i * 2 + 3] = cmd[i].data;
+ }
+
+ print_buf("NIOS II access request:\n", buf, 16);
+
+ /* Send the command */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT,
+ buf, sizeof(buf),
+ PERIPHERAL_TIMEOUT_MS);
+ if (status != 0) {
+ log_debug("Failed to submit NIOS II request: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Read back the ACK. The command data is only used for a read operation,
+ * and is thrown away otherwise */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN,
+ buf, sizeof(buf),
+ PERIPHERAL_TIMEOUT_MS);
+
+ if (dir == NIOS_PKT_LEGACY_MODE_DIR_READ && status == 0) {
+ for (i = 0; i < len; i++) {
+ cmd[i].data = buf[i * 2 + 3];
+ }
+ }
+
+ if (status == 0) {
+ print_buf("NIOS II access response:\n", buf, 16);
+ } else {
+ log_debug("Failed to receive NIOS II response: %s\n",
+ bladerf_strerror(status));
+ }
+
+
+ return status;
+}
+
+int nios_legacy_pio_read(struct bladerf *dev, uint8_t addr, uint32_t *data)
+{
+ int status;
+ size_t i;
+ struct uart_cmd cmd;
+
+ *data = 0;
+
+ for (i = 0; i < sizeof(*data); i++) {
+ assert((addr + i) <= UINT8_MAX);
+ cmd.addr = (uint8_t)(addr + i);
+ cmd.data = 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ if (status < 0) {
+ *data = 0;
+ return status;
+ }
+
+ *data |= (cmd.data << (i * 8));
+ }
+
+ return 0;
+}
+
+int nios_legacy_pio_write(struct bladerf *dev, uint8_t addr, uint32_t data)
+{
+ int status;
+ size_t i;
+ struct uart_cmd cmd;
+
+ for (i = 0; i < sizeof(data); i++) {
+ assert((addr + i) <= UINT8_MAX);
+ cmd.addr = (uint8_t)(addr + i);
+ cmd.data = (data >> (i * 8)) & 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+
+ if (status < 0) {
+ return status;
+ }
+ }
+
+ return 0;
+}
+
+int nios_legacy_config_read(struct bladerf *dev, uint32_t *val)
+{
+ int status;
+
+ status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_CONTROL, val);
+ if (status == 0) {
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, val);
+ }
+
+ return status;
+}
+
+int nios_legacy_config_write(struct bladerf *dev, uint32_t val)
+{
+ log_verbose("%s: Writing 0x%08x\n", __FUNCTION__, val);
+ return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_CONTROL, val);
+}
+
+int nios_legacy_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *ver)
+{
+ int i, status;
+ struct uart_cmd cmd;
+
+ for (i = 0; i < 4; i++) {
+ cmd.addr = NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID + i;
+ cmd.data = 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ if (status != 0) {
+ log_debug("Failed to read FPGA version[%d]: %s\n", i,
+ bladerf_strerror(status));
+
+ return status;
+ }
+
+ switch (i) {
+ case 0:
+ ver->major = cmd.data;
+ break;
+ case 1:
+ ver->minor = cmd.data;
+ break;
+ case 2:
+ ver->patch = cmd.data;
+ break;
+ case 3:
+ ver->patch |= (cmd.data << 8);
+ break;
+ default:
+ assert(!"Bug");
+ }
+ }
+
+ snprintf((char*)ver->describe, BLADERF_VERSION_STR_MAX,
+ "%d.%d.%d", ver->major, ver->minor, ver->patch);
+
+ return status;
+}
+
+int nios_legacy_get_timestamp(struct bladerf *dev, bladerf_direction dir,
+ uint64_t *value)
+{
+ int status = 0;
+ struct uart_cmd cmds[4];
+ uint8_t timestamp_bytes[8];
+ size_t i;
+
+ /* Offset 16 is the time tamer according to the Nios firmware */
+ cmds[0].addr = (dir == BLADERF_RX ? 16 : 24);
+ cmds[1].addr = (dir == BLADERF_RX ? 17 : 25);
+ cmds[2].addr = (dir == BLADERF_RX ? 18 : 26);
+ cmds[3].addr = (dir == BLADERF_RX ? 19 : 27);
+ cmds[0].data = cmds[1].data = cmds[2].data = cmds[3].data = 0xff;
+
+ status = nios_access(dev,
+ NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST,
+ cmds, ARRAY_SIZE(cmds));
+ if (status != 0) {
+ return status;
+ } else {
+ for (i = 0; i < 4; i++) {
+ timestamp_bytes[i] = cmds[i].data;
+ }
+ }
+
+ cmds[0].addr = (dir == BLADERF_RX ? 20 : 28);
+ cmds[1].addr = (dir == BLADERF_RX ? 21 : 29);
+ cmds[2].addr = (dir == BLADERF_RX ? 22 : 30);
+ cmds[3].addr = (dir == BLADERF_RX ? 23 : 31);
+ cmds[0].data = cmds[1].data = cmds[2].data = cmds[3].data = 0xff;
+
+ status = nios_access(dev,
+ NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST,
+ cmds, ARRAY_SIZE(cmds));
+
+ if (status) {
+ return status;
+ } else {
+ for (i = 0; i < 4; i++) {
+ timestamp_bytes[i + 4] = cmds[i].data;
+ }
+ }
+
+ memcpy(value, timestamp_bytes, sizeof(*value));
+ *value = LE64_TO_HOST(*value);
+
+ return 0;
+}
+
+int nios_legacy_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ int status;
+ struct uart_cmd cmd;
+
+ cmd.addr = addr;
+ cmd.data = 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_SI5338,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ if (status == 0) {
+ *data = cmd.data;
+ log_verbose("%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, *data);
+ }
+
+ return status;
+}
+
+int nios_legacy_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ struct uart_cmd cmd;
+
+ cmd.addr = addr;
+ cmd.data = data;
+
+ log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, data );
+
+ return nios_access(dev, NIOS_PKT_LEGACY_DEV_SI5338,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+}
+
+int nios_legacy_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ int status;
+ struct uart_cmd cmd;
+
+ cmd.addr = addr;
+ cmd.data = 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_LMS,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ if (status == 0) {
+ *data = cmd.data;
+ log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, *data );
+ }
+
+ return status;
+}
+
+int nios_legacy_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ int status;
+ struct uart_cmd cmd;
+
+ cmd.addr = addr;
+ cmd.data = data;
+
+ log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, data );
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_LMS,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+
+ return status;
+}
+
+int nios_legacy_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ad9361_spi_read(struct bladerf *dev, uint16_t cmd,
+ uint64_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ad9361_spi_write(struct bladerf *dev, uint16_t cmd,
+ uint64_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_adi_axi_read(struct bladerf *dev, uint32_t addr,
+ uint32_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_adi_axi_write(struct bladerf *dev, uint32_t cmd,
+ uint32_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rfic_command_read(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rfic_command_write(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rffe_control_read(struct bladerf *dev, uint32_t *value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rffe_control_write(struct bladerf *dev, uint32_t value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rffe_fastlock_save(struct bladerf *dev, bool is_tx,
+ uint8_t rffe_profile, uint16_t nios_profile)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_vctcxo_trim_dac_write(struct bladerf *dev, uint8_t addr, uint16_t value)
+{
+ int status;
+ struct uart_cmd cmd;
+ int base;
+
+ /* Only pass through writes of the DAC value, for compatibility with
+ * dac161s055 driver */
+ if (addr != 0x08) {
+ return 0;
+ }
+
+ /* FPGA v0.0.4 introduced a change to the location of the DAC registers */
+ const bool legacy_location =
+ !have_cap(dev->board->get_capabilities(dev), BLADERF_CAP_UPDATED_DAC_ADDR);
+
+ base = legacy_location ? 0 : 34;
+
+ cmd.addr = base;
+ cmd.data = value & 0xff;
+ status = nios_access(dev,
+ legacy_location ?
+ NIOS_PKT_LEGACY_DEV_VCTCXO :
+ NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+
+ if (status < 0) {
+ return status;
+ }
+
+ cmd.addr = base + 1;
+ cmd.data = (value >> 8) & 0xff;
+ status = nios_access(dev,
+ legacy_location ?
+ NIOS_PKT_LEGACY_DEV_VCTCXO :
+ NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+
+ return status;
+}
+
+int nios_legacy_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ *mode = BLADERF_VCTCXO_TAMER_INVALID;
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int get_iq_correction(struct bladerf *dev, uint8_t addr, int16_t *value)
+{
+ int i;
+ int status;
+ struct uart_cmd cmd;
+
+ *value = 0;
+ for (i = status = 0; status == 0 && i < 2; i++) {
+ cmd.addr = i + addr;
+ cmd.data = 0xff;
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ *value |= (cmd.data << (i * 8));
+ }
+
+ return status;
+}
+
+static int set_iq_correction(struct bladerf *dev, uint8_t addr, int16_t value)
+{
+ int i;
+ int status;
+ struct uart_cmd cmd;
+
+ for (i = status = 0; status == 0 && i < 2; i++) {
+ cmd.addr = i + addr;
+
+ cmd.data = (value >> (i * 8)) & 0xff;
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+ }
+
+ return status;
+}
+
+
+int nios_legacy_get_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch, int16_t *value)
+{
+ int status;
+ uint8_t addr;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ addr = NIOS_PKT_LEGACY_DEV_RX_GAIN_ADDR;
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ addr = NIOS_PKT_LEGACY_DEV_TX_GAIN_ADDR;
+ break;
+
+ default:
+ log_debug("%s: invalid channel provided (0x%x)\n",
+ __FUNCTION__, ch);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = get_iq_correction(dev, addr, value);
+
+ return status;
+}
+
+int nios_legacy_get_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch, int16_t *value)
+{
+ uint8_t addr;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ addr = NIOS_PKT_LEGACY_DEV_RX_PHASE_ADDR;
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ addr = NIOS_PKT_LEGACY_DEV_TX_PHASE_ADDR;
+ break;
+
+ default:
+ log_debug("%s: invalid channel provided (0x%x)\n",
+ __FUNCTION__, ch);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ return get_iq_correction(dev, addr, value);
+}
+
+int nios_legacy_set_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch, int16_t value)
+{
+ uint8_t addr;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ addr = NIOS_PKT_LEGACY_DEV_RX_GAIN_ADDR;
+ log_verbose("Setting RX IQ Correction phase: %d\n", value);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ addr = NIOS_PKT_LEGACY_DEV_TX_GAIN_ADDR;
+ log_verbose("Setting TX IQ Correction phase: %d\n", value);
+ break;
+
+ default:
+ log_debug("%s: invalid channel provided (0x%x)\n",
+ __FUNCTION__, ch);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ return set_iq_correction(dev, addr, value);
+}
+
+int nios_legacy_set_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch, int16_t value)
+{
+ uint8_t addr;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ log_verbose("Setting RX IQ Correction phase: %d\n", value);
+ addr = NIOS_PKT_LEGACY_DEV_RX_PHASE_ADDR;
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ log_verbose("Setting TX IQ Correction phase: %d\n", value);
+ addr = NIOS_PKT_LEGACY_DEV_TX_PHASE_ADDR;
+ break;
+
+ default:
+ log_debug("%s: invalid channel provided (0x%x)\n",
+ __FUNCTION__, ch);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ return set_iq_correction(dev, addr, value);
+}
+
+int nios_legacy_xb200_synth_write(struct bladerf *dev, uint32_t value)
+{
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, value);
+ return nios_legacy_pio_write(dev,
+ NIOS_PKT_LEGACY_PIO_ADDR_XB200_SYNTH, value);
+}
+
+int nios_legacy_expansion_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, val);
+
+ if (status == 0) {
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, val);
+ }
+
+ return status;
+}
+
+int nios_legacy_expansion_gpio_write(struct bladerf *dev,
+ uint32_t mask, uint32_t val)
+{
+ int status;
+ uint32_t tmp;
+
+ if (mask != 0xffffffff) {
+ status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, &tmp);
+ if (status != 0) {
+ return status;
+ }
+
+ tmp &= ~mask;
+ tmp |= (val & mask);
+ val = tmp;
+ }
+
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, val);
+ return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, val);
+}
+
+int nios_legacy_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_legacy_pio_read(dev,
+ NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, val);
+
+ if (status == 0) {
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, val);
+ }
+
+ return status;
+}
+
+int nios_legacy_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask, uint32_t val)
+{
+ int status;
+ uint32_t tmp;
+
+ if (mask != 0xffffffff) {
+ status = nios_legacy_pio_read(dev,
+ NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, &tmp);
+
+ if (status != 0) {
+ return status;
+ }
+
+ tmp &= ~mask;
+ tmp |= (val & mask);
+ val = tmp;
+ }
+
+ log_verbose("%s: 0x%08\n", __FUNCTION__, val);
+ return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, val);
+}
+
+int nios_legacy_read_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t *value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_write_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h
new file mode 100644
index 0000000..92f4b77
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h
@@ -0,0 +1,488 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file defines functionality used to communicate with the NIOS II
+ * soft-core processor running on the FPGA versions prior to v0.2.x.
+ * (Although post v0.2.x FPGAs support this for reverse compatibility).
+ *
+ * The host communicates with the NIOS II via USB transfers to the Cypress FX3,
+ * which then communicates with the FPGA via a UART. This module packs and
+ * submits the requests, and receives and unpacks the responses.
+ *
+ * See the files in the top-level fpga_common/ directory for description
+ * and macros pertaining to the legacy packet format.
+ */
+
+#ifndef BACKEND_USB_NIOS_LEGACY_ACCESS_H_
+#define BACKEND_USB_NIOS_LEGACY_ACCESS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "board/board.h"
+#include "usb.h"
+
+/**
+ * Perform a read from device/blocks connected via NIOS II PIO.
+ *
+ * @param dev Device handle
+ * @param[in] addr Virtual "register address." Use NIOS_LEGACY_PIO_ADDR_*
+ * definitions.
+ * @param[out] data Read data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_pio_read(struct bladerf *dev, uint8_t addr, uint32_t *data);
+
+/**
+ * Perform a write to device/blocks connected via NIOS II PIO.
+ *
+ * @param dev Device handle
+ * @param[in] addr Virtual "register address." Use NIOS_LEGACY_PIO_ADDR_*
+ * definitions.
+ * @param[in] data Data to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_pio_write(struct bladerf *dev, uint8_t addr, uint32_t data);
+
+/**
+ * Read from the FPGA's config register
+ *
+ * @param dev Device handle
+ * @param[out] val On success, updated with config register value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_config_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to FPGA's config register
+ *
+ * @param dev Device handle
+ * @param[in] val Register value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_config_write(struct bladerf *dev, uint32_t val);
+
+/**
+ * Read the FPGA version into the provided version structure
+ *
+ * @param dev Device handle
+ * @param[out] ver Updated with FPGA version (upon success)
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *ver);
+
+/**
+ * Read a timestamp counter value
+ *
+ * @param dev Device handle
+ * @param[in] dir Stream direction
+ * @param[out] timestamp On success, updated with the timestamp value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *timestamp);
+
+/**
+ * Read from an Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data On success, updated with read data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+/**
+ * Write to an Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[in] data Data to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data);
+
+/**
+ * Read from an LMS6002D register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data On success, updated with data read from the device
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+/**
+ * Write to an LMS6002D register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data Register data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data);
+
+/**
+ * Read from an INA219 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data);
+
+/**
+ * Write to an INA219 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data);
+
+/**
+ * Read the AD9361 over SPI.
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ad9361_spi_read(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t *data);
+
+/**
+ * Write to the AD9361 over SPI.
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ad9361_spi_write(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t data);
+
+/**
+ * Read the ADI AXI memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_adi_axi_read(struct bladerf *dev,
+ uint32_t addr,
+ uint32_t *data);
+
+/**
+ * Write to the ADI AXI memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_adi_axi_write(struct bladerf *dev,
+ uint32_t addr,
+ uint32_t data);
+
+/**
+ * Read RFIC command
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rfic_command_read(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t *data);
+
+/**
+ * Write RFIC command
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rfic_command_write(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t data);
+
+/**
+ * Read RFFE control register.
+ *
+ * @param dev Device handle
+ * @param[in] len Data buffer length
+ * @param[out] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rffe_control_read(struct bladerf *dev, uint32_t *value);
+
+/**
+ * Write RFFE control register.
+ *
+ * @param dev Device handle
+ * @param[in] len Data buffer length
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rffe_control_write(struct bladerf *dev, uint32_t value);
+
+/**
+ * Save an RFFE fast lock profile to the Nios.
+ *
+ * @param dev Device handle
+ * @param[in] is_tx True if TX profile, false if RX profile
+ * @param[in] rffe_profile RFFE profile to save
+ * @param[in] nios_profile Where to save the profile in the Nios
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rffe_fastlock_save(struct bladerf *dev, bool is_tx,
+ uint8_t rffe_profile,
+ uint16_t nios_profile);
+
+/**
+ * Write to the AD56X1 VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev,
+ uint16_t value);
+
+/**
+ * Read the AD56X1 VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[out] value Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev,
+ uint16_t *value);
+
+/**
+ * Write to the ADF400X.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data);
+
+/**
+ * Read from the ADF400X.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data);
+
+/**
+ * Write to the VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[in] addr Register (should be 0x08)
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_vctcxo_trim_dac_write(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t value);
+
+/**
+ * Read a IQ gain correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] value On success, updated with phase correction value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_get_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+/**
+ * Read a IQ phase correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] value On success, updated with phase correction value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_get_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+/**
+ * Write an IQ gain correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] value Correction value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_set_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+
+/**
+ * Write an IQ phase correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] value Correction value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_set_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+
+/**
+ * Write a value to the XB-200's ADF4351 synthesizer
+ *
+ * @param dev Device handle
+ * @param[in] value Write value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_xb200_synth_write(struct bladerf *dev, uint32_t value);
+
+/**
+ * Read from expanion GPIO
+ *
+ * @param dev Device handle
+ * @param[out] value On success, updated with read value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_expansion_gpio_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to expansion GPIO
+ *
+ * @param dev Device handle
+ * @param[in[ mask Mask denoting bits to write
+ * @param[in] value Value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_expansion_gpio_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val);
+
+/**
+ * Read from expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[out] value On success, updated with read value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[in] mask Mask denoting bits to configure
+ * @param[in] output '1' bit denotes an output, '0' bit denotes an input
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t outputs);
+
+/**
+ * Read trigger register value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] trigger Trigger to read from
+ * @param[out] value On success, updated with register value
+ *
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error
+ */
+int nios_legacy_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *value);
+
+/**
+ * Write trigger register value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] trigger Trigger to read
+ * @param[in] value Value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error
+ */
+int nios_legacy_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t value);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/usb/usb.c b/Radio/HW/BladeRF/src/backend/usb/usb.c
new file mode 100644
index 0000000..fba2c33
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/usb.c
@@ -0,0 +1,1430 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "rel_assert.h"
+#include "log.h"
+#include "minmax.h"
+#include "conversions.h"
+#include "usb.h"
+
+#include "board/board.h"
+#include "backend/backend.h"
+#include "backend/backend_config.h"
+#include "backend/usb/usb.h"
+#include "driver/fx3_fw.h"
+#include "streaming/async.h"
+#include "helpers/version.h"
+
+#include "bladeRF.h"
+#include "nios_pkt_formats.h"
+#include "nios_legacy_access.h"
+#include "nios_access.h"
+
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+bool bladerf_usb_reset_device_on_open = true;
+#endif
+
+static const struct usb_driver *usb_driver_list[] = BLADERF_USB_BACKEND_LIST;
+
+/* FW declaration of fn table declared at the end of this file */
+const struct backend_fns backend_fns_usb_legacy;
+
+/* Vendor command wrapper to gets a 32-bit integer and supplies a wIndex */
+static inline int vendor_cmd_int_windex(struct bladerf *dev, uint8_t cmd,
+ uint16_t windex, int32_t *val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ cmd, 0, windex,
+ val, sizeof(uint32_t),
+ CTRL_TIMEOUT_MS);
+}
+
+/* Vendor command wrapper to get a 32-bit integer and supplies wValue */
+static inline int vendor_cmd_int_wvalue(struct bladerf *dev, uint8_t cmd,
+ uint16_t wvalue, int32_t *val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ cmd, wvalue, 0,
+ val, sizeof(uint32_t),
+ CTRL_TIMEOUT_MS);
+}
+
+
+/* Vendor command that gets/sets a 32-bit integer value */
+static inline int vendor_cmd_int(struct bladerf *dev, uint8_t cmd,
+ usb_direction dir, int32_t *val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ dir, cmd, 0, 0,
+ val, sizeof(int32_t),
+ CTRL_TIMEOUT_MS);
+}
+
+static inline int change_setting(struct bladerf *dev, uint8_t setting)
+{
+ int status;
+ struct bladerf_usb *usb = dev->backend_data;
+
+ log_verbose("Changing to USB alt setting %u\n", setting);
+
+ status = usb->fn->change_setting(usb->driver, setting);
+ if (status != 0) {
+ log_debug("Failed to change setting: %s\n", bladerf_strerror(status));
+ }
+
+ return status;
+}
+
+static int usb_is_fpga_configured(struct bladerf *dev)
+{
+ int result = -1;
+ int status;
+
+ /* This environment variable provides a means to force libbladeRF to not
+ * attempt to access the FPGA.
+ *
+ * This provides a workaround for the situation where a user did not remove
+ * an FPGA in SPI flash prior to flashing new firmware and updating
+ * libbladeRF. Specifically, this has proven to be a problem with pre-v0.0.1
+ * FPGA images, as they do not provide version readback functionality.
+ */
+ if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) {
+ log_debug("Reporting no FPGA present - "
+ "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n");
+ return 0;
+ }
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS,
+ USB_DIR_DEVICE_TO_HOST, &result);
+
+ if (status < 0) {
+ return status;
+ } else if (result == 0 || result == 1) {
+ return result;
+ } else {
+ log_debug("Unexpected result from FPGA status query: %d\n", result);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+}
+
+static int usb_set_fpga_protocol(struct bladerf *dev, backend_fpga_protocol fpga_protocol)
+{
+ if (fpga_protocol == BACKEND_FPGA_PROTOCOL_NIOSII_LEGACY) {
+ dev->backend = &backend_fns_usb_legacy;
+ } else if (fpga_protocol == BACKEND_FPGA_PROTOCOL_NIOSII) {
+ dev->backend = &backend_fns_usb;
+ } else {
+ log_error("Unknown FPGA protocol: %d\n", fpga_protocol);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+static bladerf_fpga_source usb_get_fpga_source(struct bladerf *dev)
+{
+ int result = -1;
+ int status;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FPGA_SOURCE,
+ USB_DIR_DEVICE_TO_HOST, &result);
+
+ if (status < 0) {
+ log_debug("%s: vendor_cmd_int returned %s\n", __FUNCTION__,
+ bladerf_strerror(status));
+ return BLADERF_FPGA_SOURCE_UNKNOWN;
+ } else if (0 == result || 1 == result || 2 == result) {
+ return (bladerf_fpga_source)result;
+ } else {
+ log_debug("Unexpected result from FPGA source query: %d\n", result);
+ return BLADERF_FPGA_SOURCE_UNKNOWN;
+ }
+}
+
+/* After performing a flash operation, switch back to either RF_LINK or the
+ * FPGA loader.
+ */
+static int restore_post_flash_setting(struct bladerf *dev)
+{
+ int fpga_loaded = usb_is_fpga_configured(dev);
+ int status;
+
+ if (fpga_loaded < 0) {
+ status = fpga_loaded;
+ log_debug("Failed to determine if FPGA is loaded (%d)\n", fpga_loaded);
+ } else if (fpga_loaded) {
+ status = change_setting(dev, USB_IF_RF_LINK);
+ } else {
+ status = change_setting(dev, USB_IF_CONFIG);
+ }
+
+ if (status < 0) {
+ log_debug("Failed to restore alt setting: %s\n",
+ bladerf_strerror(status));
+ }
+ return status;
+}
+
+static bool usb_matches(bladerf_backend backend)
+{
+ return backend == BLADERF_BACKEND_ANY ||
+ backend == BLADERF_BACKEND_LINUX ||
+ backend == BLADERF_BACKEND_LIBUSB ||
+ backend == BLADERF_BACKEND_CYPRESS;
+}
+
+static int usb_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ int status;
+ size_t i;
+
+ for (i = status = 0; i < ARRAY_SIZE(usb_driver_list); i++) {
+ status = usb_driver_list[i]->fn->probe(probe_target, info_list);
+ }
+
+ return status;
+}
+
+static void usb_close(struct bladerf *dev)
+{
+ int status;
+ struct bladerf_usb *usb = dev->backend_data;
+
+ if (usb != NULL) {
+ /* It seems we need to switch back to our NULL interface before closing,
+ * or else our device doesn't close upon exit in OSX and then fails to
+ * re-open cleanly */
+ status = usb->fn->change_setting(usb->driver, USB_IF_NULL);
+ if (status != 0) {
+ log_error("Failed to switch to NULL interface: %s\n",
+ bladerf_strerror(status));
+ }
+
+ usb->fn->close(usb->driver);
+ free(usb);
+ dev->backend_data = NULL;
+ }
+}
+
+static int usb_is_fw_ready(struct bladerf *dev)
+{
+ int status;
+ int result;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_DEVICE_READY,
+ USB_DIR_DEVICE_TO_HOST, &result);
+ if (status < 0) {
+ return status;
+ } else if (result == 0 || result == 1) {
+ return result;
+ } else {
+ log_debug("Unexpected result from firmware status query: %d\n", result);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+}
+
+static int usb_get_handle(struct bladerf *dev,
+ void **handle)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ int status;
+
+ status = usb->fn->get_handle(usb->driver, handle);
+
+ return status;
+}
+
+static int usb_get_fw_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ int status;
+
+ status = usb->fn->get_string_descriptor(usb->driver,
+ BLADE_USB_STR_INDEX_FW_VER,
+ (unsigned char *)version->describe,
+ BLADERF_VERSION_STR_MAX);
+ if (status == 0) {
+ status = str2version(version->describe, version);
+ } else {
+ log_warning("Failed to retrieve firmware version. This may be due "
+ "to an old firmware version that does not support "
+ "this request. A firmware update via the bootloader is "
+ "required.\n\n");
+ status = BLADERF_ERR_UPDATE_FW;
+ }
+
+ return status;
+}
+
+static int usb_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ int status;
+
+ status = change_setting(dev, USB_IF_RF_LINK);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Read and store FPGA version info. This is only possible after
+ * we've entered RF link mode.
+ *
+ * The legacy mode is used here since we can't yet determine if
+ * the FPGA is capable of using the newer packet formats. */
+ return nios_legacy_get_fpga_version(dev, version);
+}
+
+static int usb_open(struct bladerf *dev, struct bladerf_devinfo *info)
+{
+ int status;
+ size_t i;
+ struct bladerf_usb *usb;
+
+ usb = malloc(sizeof(*usb));
+ if (usb == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Try each matching usb driver */
+ for (i = 0; i < ARRAY_SIZE(usb_driver_list); i++) {
+ if (info->backend == BLADERF_BACKEND_ANY
+ || usb_driver_list[i]->id == info->backend) {
+ usb->fn = usb_driver_list[i]->fn;
+ status = usb->fn->open(&usb->driver, info, &dev->ident);
+ if (status == 0) {
+ break;
+ } else if (status == BLADERF_ERR_NODEV) {
+ continue;
+ } else {
+ free(usb);
+ return status;
+ }
+ }
+ }
+
+ /* If no usb driver was found */
+ if (i == ARRAY_SIZE(usb_driver_list)) {
+ free(usb);
+ return BLADERF_ERR_NODEV;
+ }
+
+ /* Default to legacy-mode access until we determine the FPGA is
+ * capable of handling newer request formats */
+ dev->backend = &backend_fns_usb_legacy;
+ dev->backend_data = usb;
+
+ /* Just out of paranoia, put the device into a known state */
+ status = change_setting(dev, USB_IF_NULL);
+ if (status < 0) {
+ log_debug("Failed to switch to USB_IF_NULL\n");
+ goto error;
+ }
+
+error:
+ if (status != 0) {
+ usb_close(dev);
+ }
+
+ return status;
+}
+
+static int usb_get_vid_pid(struct bladerf *dev, uint16_t *vid, uint16_t *pid)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->get_vid_pid(usb->driver, vid, pid);
+}
+
+static int usb_get_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did)
+{
+ int status;
+ int result;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FLASH_ID,
+ USB_DIR_DEVICE_TO_HOST, &result);
+ if (status < 0) {
+ log_debug("Could not read flash manufacturer ID and/or device ID. %s.\n",
+ bladerf_strerror(status));
+ } else {
+ *did = result & 0xFF;
+ *mid = (result >> 8) & 0xFF;
+ }
+ return status;
+}
+
+static int begin_fpga_programming(struct bladerf *dev)
+{
+ int result;
+ int status = vendor_cmd_int(dev, BLADE_USB_CMD_BEGIN_PROG,
+ USB_DIR_DEVICE_TO_HOST, &result);
+
+ if (status != 0) {
+ return status;
+ } else if (result != 0) {
+ log_debug("Startg fpga programming, result = %d\n", result);
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return 0;
+ }
+}
+
+static int usb_load_fpga(struct bladerf *dev, const uint8_t *image, size_t image_size)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ unsigned int wait_count;
+ const unsigned int timeout_ms = (3 * CTRL_TIMEOUT_MS);
+ int status;
+
+ /* Switch to the FPGA configuration interface */
+ status = change_setting(dev, USB_IF_CONFIG);
+ if(status < 0) {
+ log_debug("Failed to switch to FPGA config setting: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Begin programming */
+ status = begin_fpga_programming(dev);
+ if (status < 0) {
+ log_debug("Failed to initiate FPGA programming: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Send the file down */
+ assert(image_size <= UINT32_MAX);
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT,
+ (void *)image,
+ (uint32_t)image_size,
+ timeout_ms);
+ if (status < 0) {
+ log_debug("Failed to write FPGA bitstream to FPGA: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Poll FPGA status to determine if programming was a success */
+ wait_count = 10;
+ status = 0;
+
+ while (wait_count > 0 && status == 0) {
+ status = usb_is_fpga_configured(dev);
+ if (status == 1) {
+ break;
+ }
+
+ usleep(200000);
+ wait_count--;
+ }
+
+ /* Failed to determine if FPGA is loaded */
+ if (status < 0) {
+ log_debug("Failed to determine if FPGA is loaded: %s\n",
+ bladerf_strerror(status));
+ return status;
+ } else if (wait_count == 0 && status != 0) {
+ log_debug("Timeout while waiting for FPGA configuration status\n");
+ return BLADERF_ERR_TIMEOUT;
+ }
+
+ return 0;
+}
+
+static inline int perform_erase(struct bladerf *dev, uint16_t block)
+{
+ int status, erase_ret;
+ struct bladerf_usb *usb = dev->backend_data;
+
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ BLADE_USB_CMD_FLASH_ERASE,
+ 0, block,
+ &erase_ret, sizeof(erase_ret),
+ CTRL_TIMEOUT_MS);
+
+
+ return status;
+}
+
+static int usb_erase_flash_blocks(struct bladerf *dev,
+ uint32_t eb,
+ uint16_t count)
+{
+ int status, restore_status;
+ uint16_t i;
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status != 0) {
+ return status;
+ }
+
+ log_info("Erasing %u block%s starting at block %u\n", count,
+ 1 == count ? "" : "s", eb);
+
+ for (i = 0; i < count; i++) {
+ log_info("Erasing block %u (%u%%)...%c", eb + i,
+ (i + 1) == count ? 100 : 100 * i / count,
+ (i + 1) == count ? '\n' : '\r');
+
+ status = perform_erase(dev, eb + i);
+ if (status != 0) {
+ log_debug("Failed to erase block %u: %s\n", eb + i,
+ bladerf_strerror(status));
+ goto error;
+ }
+ }
+
+ log_info("Done erasing %u block%s\n", count, 1 == count ? "" : "s");
+
+error:
+ restore_status = restore_post_flash_setting(dev);
+ return status != 0 ? status : restore_status;
+}
+
+static inline int read_page(struct bladerf *dev, uint8_t read_operation,
+ uint16_t page, uint8_t *buf)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ bladerf_dev_speed usb_speed;
+ int status;
+ int32_t op_status;
+ uint16_t read_size;
+ uint16_t offset;
+ uint8_t request;
+
+ if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) {
+ log_debug("Error getting USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) {
+ read_size = dev->flash_arch->psize_bytes;
+ } else if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) {
+ read_size = 64;
+ } else {
+ log_debug("Encountered unknown USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (read_operation == BLADE_USB_CMD_FLASH_READ ||
+ read_operation == BLADE_USB_CMD_READ_OTP) {
+
+ status = vendor_cmd_int_windex(dev, read_operation, page, &op_status);
+ if (status != 0) {
+ return status;
+ } else if (op_status != 0) {
+ log_error("Firmware page read (op=%d) failed at page %u: %d\n",
+ read_operation, page, op_status);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Both of these operations require a read from the FW's page buffer */
+ request = BLADE_USB_CMD_READ_PAGE_BUFFER;
+
+ } else if (read_operation == BLADE_USB_CMD_READ_CAL_CACHE) {
+ request = read_operation;
+ } else {
+ assert(!"Bug - invalid read_operation value");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Retrieve data from the firmware page buffer */
+ for (offset = 0; offset < dev->flash_arch->psize_bytes; offset += read_size) {
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ request,
+ 0,
+ offset, /* in bytes */
+ buf + offset,
+ read_size,
+ CTRL_TIMEOUT_MS);
+
+ if(status < 0) {
+ log_debug("Failed to read page buffer at offset 0x%02x: %s\n",
+ offset, bladerf_strerror(status));
+ return status;
+ }
+ }
+
+ return 0;
+}
+
+static int usb_read_flash_pages(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page_u32,
+ uint32_t count_u32)
+{
+ int status;
+ size_t n_read;
+ uint16_t i;
+
+ /* 16-bit control transfer fields are used for these.
+ * The current bladeRF build only has a 4MiB flash, anyway. */
+ const uint16_t page = (uint16_t)page_u32;
+ const uint16_t count = (uint16_t)count_u32;
+
+ assert(page == page_u32);
+ assert(count == count_u32);
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status != 0) {
+ return status;
+ }
+
+ log_info("Reading %u page%s starting at page %u\n", count,
+ 1 == count ? "" : "s", page);
+
+ for (n_read = i = 0; i < count; i++) {
+ log_info("Reading page %u (%u%%)...%c", page + i,
+ (i + 1) == count ? 100 : 100 * i / count,
+ (i + 1) == count ? '\n' : '\r');
+
+ status =
+ read_page(dev, BLADE_USB_CMD_FLASH_READ, page + i, buf + n_read);
+ if (status != 0) {
+ goto error;
+ }
+
+ n_read += dev->flash_arch->psize_bytes;
+ }
+
+ log_info("Done reading %u page%s\n", count, 1 == count ? "" : "s");
+
+error:
+ status = restore_post_flash_setting(dev);
+ return status;
+}
+
+static int write_page(struct bladerf *dev, uint8_t write_operation,
+ uint16_t page, const uint8_t *buf)
+{
+ int status;
+ int32_t commit_status;
+ uint16_t offset;
+ uint16_t write_size;
+ struct bladerf_usb *usb = dev->backend_data;
+ bladerf_dev_speed usb_speed;
+
+ if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) {
+ log_debug("Error getting USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) {
+ write_size = dev->flash_arch->psize_bytes;
+ } else if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) {
+ write_size = 64;
+ } else {
+ assert(!"BUG - unexpected device speed");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Write the data to the firmware's page buffer.
+ * Casting away the buffer's const-ness here is gross, but this buffer
+ * will not be written to on an out transfer. */
+ for (offset = 0; offset < dev->flash_arch->psize_bytes; offset += write_size) {
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ BLADE_USB_CMD_WRITE_PAGE_BUFFER,
+ 0,
+ offset,
+ (uint8_t*)&buf[offset],
+ write_size,
+ CTRL_TIMEOUT_MS);
+
+ if(status < 0) {
+ log_error("Failed to write page buffer at offset 0x%02x "
+ "for page %u: %s\n",
+ offset, page, bladerf_strerror(status));
+ return status;
+ }
+ }
+
+ /* Commit the page to flash */
+ status = vendor_cmd_int_windex(dev, write_operation, page, &commit_status);
+
+ if (status != 0) {
+ log_error("Failed to commit page %u: %s\n", page,
+ bladerf_strerror(status));
+ return status;
+
+ } else if (commit_status != 0) {
+ log_error("Failed to commit page %u, FW returned %d\n", page,
+ commit_status);
+
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+static int usb_write_flash_pages(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page_u32,
+ uint32_t count_u32)
+
+{
+ int status, restore_status;
+ uint16_t i;
+ size_t n_written;
+
+ /* 16-bit control transfer fields are used for these.
+ * The current bladeRF build only has a 4MiB flash, anyway. */
+ const uint16_t page = (uint16_t)page_u32;
+ const uint16_t count = (uint16_t)count_u32;
+
+ assert(page == page_u32);
+ assert(count == count_u32);
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status != 0) {
+ return status;
+ }
+
+ log_info("Writing %u page%s starting at page %u\n", count,
+ 1 == count ? "" : "s", page);
+
+ n_written = 0;
+ for (i = 0; i < count; i++) {
+ log_info("Writing page %u (%u%%)...%c", page + i,
+ (i + 1) == count ? 100 : 100 * i / count,
+ (i + 1) == count ? '\n' : '\r');
+
+ status = write_page(dev, BLADE_USB_CMD_FLASH_WRITE, page + i, buf + n_written);
+ if (status) {
+ goto error;
+ }
+
+ n_written += dev->flash_arch->psize_bytes;
+ }
+ log_info("Done writing %u page%s\n", count, 1 == count ? "" : "s");
+
+error:
+ restore_status = restore_post_flash_setting(dev);
+ if (status != 0) {
+ return status;
+ } else if (restore_status != 0) {
+ return restore_status;
+ } else {
+ return 0;
+ }
+}
+
+static int usb_device_reset(struct bladerf *dev)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ BLADE_USB_CMD_RESET,
+ 0, 0, 0, 0, CTRL_TIMEOUT_MS);
+
+}
+
+static int usb_jump_to_bootloader(struct bladerf *dev)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ BLADE_USB_CMD_JUMP_TO_BOOTLOADER,
+ 0, 0, 0, 0, CTRL_TIMEOUT_MS);
+}
+
+static int usb_get_cal(struct bladerf *dev, char *cal)
+{
+ const uint16_t dummy_page = 0;
+ int status, restore_status;
+
+ assert(CAL_BUFFER_SIZE == dev->flash_arch->psize_bytes);
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status) {
+ return status;
+ }
+
+ status = read_page(dev, BLADE_USB_CMD_READ_CAL_CACHE,
+ dummy_page, (uint8_t*)cal);
+
+ restore_status = restore_post_flash_setting(dev);
+ return status == 0 ? restore_status : status;
+}
+
+static int usb_get_otp(struct bladerf *dev, char *otp)
+{
+ int status, restore_status;
+ const uint16_t dummy_page = 0;
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status) {
+ return status;
+ }
+
+ status = read_page(dev, BLADE_USB_CMD_READ_OTP, dummy_page, (uint8_t*)otp);
+ restore_status = restore_post_flash_setting(dev);
+ return status == 0 ? restore_status : status;
+}
+
+static int usb_write_otp(struct bladerf *dev, char *otp)
+{
+ int status, restore_status;
+ const uint16_t dummy_page = 0;
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status) {
+ return status;
+ }
+
+ status = write_page(dev, BLADE_USB_CMD_WRITE_OTP, dummy_page, (uint8_t*)otp);
+ restore_status = restore_post_flash_setting(dev);
+ return status == 0 ? restore_status : status;
+}
+
+static int usb_lock_otp(struct bladerf *dev)
+{
+ int status, restore_status, commit_status;
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status) {
+ return status;
+ }
+
+ status = vendor_cmd_int_windex(dev, BLADE_USB_CMD_LOCK_OTP,
+ 0, &commit_status);
+
+ if (commit_status != 0) {
+ log_error("Failed to lock OTP, FW returned %d\n", commit_status);
+ if (status == 0)
+ status = commit_status;
+ }
+
+ restore_status = restore_post_flash_setting(dev);
+ return status == 0 ? restore_status : status;
+}
+
+static int usb_get_device_speed(struct bladerf *dev, bladerf_dev_speed *speed)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->get_speed(usb->driver, speed);
+}
+
+static int usb_set_firmware_loopback(struct bladerf *dev, bool enable) {
+ int result;
+ int status;
+
+ status = vendor_cmd_int_wvalue(dev, BLADE_USB_CMD_SET_LOOPBACK,
+ enable, &result);
+ if (status != 0) {
+ return status;
+ }
+
+
+ status = change_setting(dev, USB_IF_NULL);
+ if (status == 0) {
+ status = change_setting(dev, USB_IF_RF_LINK);
+ }
+
+ return status;
+}
+
+static int usb_get_firmware_loopback(struct bladerf *dev, bool *is_enabled)
+{
+ int status, result;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_GET_LOOPBACK,
+ USB_DIR_DEVICE_TO_HOST, &result);
+
+ if (status == 0) {
+ *is_enabled = (result != 0);
+ }
+
+ return status;
+}
+
+static int usb_enable_module(struct bladerf *dev, bladerf_direction dir, bool enable)
+{
+ int status;
+ int32_t fx3_ret = -1;
+ const uint16_t val = enable ? 1 : 0;
+ const uint8_t cmd = (dir == BLADERF_RX) ?
+ BLADE_USB_CMD_RF_RX : BLADE_USB_CMD_RF_TX;
+
+ status = vendor_cmd_int_wvalue(dev, cmd, val, &fx3_ret);
+ if (status != 0) {
+ log_debug("Could not enable RF %s (%d): %s\n",
+ (dir == BLADERF_RX) ? "RX" : "TX",
+ status, bladerf_strerror(status));
+
+ } else if (fx3_ret != 0) {
+ log_warning("FX3 reported error=0x%x when %s RF %s\n",
+ fx3_ret,
+ enable ? "enabling" : "disabling",
+ (dir == BLADERF_RX) ? "RX" : "TX");
+
+ /* FIXME: Work around what seems to be a harmless failure.
+ * It appears that in firmware or in the lib, we may be
+ * attempting to disable an already disabled channel, or
+ * enabling an already enabled channel.
+ *
+ * Further investigation required
+ *
+ * 0x44 corresponds to CY_U3P_ERROR_ALREADY_STARTED
+ */
+ if (fx3_ret != 0x44) {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+static int usb_init_stream(struct bladerf_stream *stream, size_t num_transfers)
+{
+ struct bladerf_usb *usb = stream->dev->backend_data;
+ return usb->fn->init_stream(usb->driver, stream, num_transfers);
+}
+
+static int usb_stream(struct bladerf_stream *stream, bladerf_channel_layout layout)
+{
+ struct bladerf_usb *usb = stream->dev->backend_data;
+ return usb->fn->stream(usb->driver, stream, layout);
+}
+
+int usb_submit_stream_buffer(struct bladerf_stream *stream, void *buffer,
+ size_t *length, unsigned int timeout_ms, bool nonblock)
+{
+ struct bladerf_usb *usb = stream->dev->backend_data;
+ return usb->fn->submit_stream_buffer(usb->driver, stream, buffer,
+ length, timeout_ms, nonblock);
+}
+
+static void usb_deinit_stream(struct bladerf_stream *stream)
+{
+ struct bladerf_usb *usb = stream->dev->backend_data;
+ usb->fn->deinit_stream(usb->driver, stream);
+}
+
+/*
+ * Information about the boot image format and boot over USB can be found in
+ * Cypress AN76405: EZ-USB (R) FX3 (TM) Boot Options:
+ * http://www.cypress.com/?docID=49862
+ *
+ * There's a request (bRequset = 0xc0) for the bootloader revision.
+ * However, there doesn't appear to be any documented reason to check this and
+ * behave differently depending upon the returned value.
+ */
+
+/* Command fields for FX3 firmware upload vendor requests */
+#define FX3_BOOTLOADER_LOAD_BREQUEST 0xa0
+#define FX3_BOOTLOADER_ADDR_WVALUE(addr) (HOST_TO_LE16(addr & 0xffff))
+#define FX3_BOOTLOADER_ADDR_WINDEX(addr) (HOST_TO_LE16(((addr >> 16) & 0xffff)))
+#define FX3_BOOTLOADER_MAX_LOAD_LEN 4096
+
+static int write_and_verify_fw_chunk(struct bladerf_usb *usb, uint32_t addr,
+ uint8_t *data, uint32_t len,
+ uint8_t *readback_buf) {
+
+ int status;
+ log_verbose("Writing %u bytes to bootloader @ 0x%08x\n", len, addr);
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ FX3_BOOTLOADER_LOAD_BREQUEST,
+ FX3_BOOTLOADER_ADDR_WVALUE(addr),
+ FX3_BOOTLOADER_ADDR_WINDEX(addr),
+ data,
+ len,
+ CTRL_TIMEOUT_MS);
+
+ if (status != 0) {
+ log_debug("Failed to write FW chunk (%d)\n", status);
+ return status;
+ }
+
+ log_verbose("Reading back %u bytes from bootloader @ 0x%08x\n", len, addr);
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ FX3_BOOTLOADER_LOAD_BREQUEST,
+ FX3_BOOTLOADER_ADDR_WVALUE(addr),
+ FX3_BOOTLOADER_ADDR_WINDEX(addr),
+ readback_buf,
+ len,
+ CTRL_TIMEOUT_MS);
+
+ if (status != 0) {
+ log_debug("Failed to read back FW chunk (%d)\n", status);
+ return status;
+ }
+
+ if (memcmp(data, readback_buf, len) != 0) {
+ log_debug("Readback did match written data.\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+static int execute_fw_from_bootloader(struct bladerf_usb *usb, uint32_t addr)
+{
+ int status;
+
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ FX3_BOOTLOADER_LOAD_BREQUEST,
+ FX3_BOOTLOADER_ADDR_WVALUE(addr),
+ FX3_BOOTLOADER_ADDR_WINDEX(addr),
+ NULL,
+ 0,
+ CTRL_TIMEOUT_MS);
+
+ if (status != 0 && status != BLADERF_ERR_IO) {
+ log_debug("Failed to exec firmware: %s\n:",
+ bladerf_strerror(status));
+
+ } else if (status == BLADERF_ERR_IO) {
+ /* The device might drop out from underneath us as it starts executing
+ * the new firmware */
+ log_verbose("Device returned IO error due to FW boot.\n");
+ status = 0;
+ } else {
+ log_verbose("Booting new FW.\n");
+ }
+
+ return status;
+}
+
+static int write_fw_to_bootloader(void *driver, struct fx3_firmware *fw)
+{
+ int status = 0;
+ uint32_t to_write;
+ uint32_t data_len;
+ uint32_t addr;
+ uint8_t *data;
+ bool got_section;
+
+ uint8_t *readback = malloc(FX3_BOOTLOADER_MAX_LOAD_LEN);
+ if (readback == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ do {
+ got_section = fx3_fw_next_section(fw, &addr, &data, &data_len);
+ if (got_section) {
+ /* data_len should never be zero, as fw->num_sections should NOT
+ * include the terminating section in its count */
+ assert(data_len != 0);
+
+ do {
+ to_write = u32_min(data_len, FX3_BOOTLOADER_MAX_LOAD_LEN);
+
+ status = write_and_verify_fw_chunk(driver,
+ addr, data, to_write,
+ readback);
+
+ data_len -= to_write;
+ addr += to_write;
+ data += to_write;
+ } while (data_len != 0 && status == 0);
+ }
+ } while (got_section && status == 0);
+
+ if (status == 0) {
+ status = execute_fw_from_bootloader(driver, fx3_fw_entry_point(fw));
+ }
+
+ free(readback);
+ return status;
+}
+
+static int usb_load_fw_from_bootloader(bladerf_backend backend,
+ uint8_t bus, uint8_t addr,
+ struct fx3_firmware *fw)
+{
+ int status = 0;
+ size_t i;
+ struct bladerf_usb usb;
+
+ for (i = 0; i < ARRAY_SIZE(usb_driver_list); i++) {
+
+ if ((backend == BLADERF_BACKEND_ANY) ||
+ (usb_driver_list[i]->id == backend)) {
+
+ usb.fn = usb_driver_list[i]->fn;
+ status = usb.fn->open_bootloader(&usb.driver, bus, addr);
+ if (status == 0) {
+ status = write_fw_to_bootloader(&usb, fw);
+ usb.fn->close_bootloader(usb.driver);
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+/* Default handlers for operations unsupported by the NIOS II legacy packet
+ * format */
+static int set_vctcxo_tamer_mode_unsupported(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ log_debug("Operation not supported with legacy NIOS packet format.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int get_vctcxo_tamer_mode_unsupported(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ *mode = BLADERF_VCTCXO_TAMER_INVALID;
+ log_debug("Operation not supported with legacy NIOS packet format.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int usb_read_fw_log(struct bladerf *dev, logger_entry *e)
+{
+ int status;
+ *e = LOG_EOF;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_READ_LOG_ENTRY,
+ USB_DIR_DEVICE_TO_HOST, (int32_t*) e);
+
+ return status;
+}
+
+static int config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ bladerf_dev_speed usb_speed;
+
+ if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) {
+ log_debug("Error getting USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* If we're connected at HS, we need to use smaller DMA transfers */
+ if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) {
+ val |= BLADERF_GPIO_FEATURE_SMALL_DMA_XFER;
+ } else if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) {
+ val &= ~BLADERF_GPIO_FEATURE_SMALL_DMA_XFER;
+ } else {
+ assert(!"Encountered unknown USB speed");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return nios_config_write(dev, val);
+}
+
+static int set_agc_dc_correction_unsupported(struct bladerf *dev,
+ int16_t q_max, int16_t i_max,
+ int16_t q_mid, int16_t i_mid,
+ int16_t q_low, int16_t i_low)
+{
+ log_debug("Operation not supported with legacy NIOS packet format.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int legacy_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ bladerf_dev_speed usb_speed;
+
+ if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) {
+ log_debug("Error getting USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* If we're connected at HS, we need to use smaller DMA transfers */
+ if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) {
+ val |= BLADERF_GPIO_FEATURE_SMALL_DMA_XFER;
+ } else if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) {
+ val &= ~BLADERF_GPIO_FEATURE_SMALL_DMA_XFER;
+ } else {
+ assert(!"Encountered unknown USB speed");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return nios_legacy_config_write(dev, val);
+}
+
+/* USB backend that used legacy format for communicating with NIOS II */
+const struct backend_fns backend_fns_usb_legacy = {
+ FIELD_INIT(.matches, usb_matches),
+
+ FIELD_INIT(.probe, usb_probe),
+
+ FIELD_INIT(.get_vid_pid, usb_get_vid_pid),
+ FIELD_INIT(.get_flash_id, usb_get_flash_id),
+ FIELD_INIT(.open, usb_open),
+ FIELD_INIT(.set_fpga_protocol, usb_set_fpga_protocol),
+ FIELD_INIT(.close, usb_close),
+
+ FIELD_INIT(.is_fw_ready, usb_is_fw_ready),
+
+ FIELD_INIT(.get_handle, usb_get_handle),
+
+ FIELD_INIT(.load_fpga, usb_load_fpga),
+ FIELD_INIT(.is_fpga_configured, usb_is_fpga_configured),
+ FIELD_INIT(.get_fpga_source, usb_get_fpga_source),
+
+ FIELD_INIT(.get_fw_version, usb_get_fw_version),
+ FIELD_INIT(.get_fpga_version, usb_get_fpga_version),
+
+ FIELD_INIT(.erase_flash_blocks, usb_erase_flash_blocks),
+ FIELD_INIT(.read_flash_pages, usb_read_flash_pages),
+ FIELD_INIT(.write_flash_pages, usb_write_flash_pages),
+
+ FIELD_INIT(.device_reset, usb_device_reset),
+ FIELD_INIT(.jump_to_bootloader, usb_jump_to_bootloader),
+
+ FIELD_INIT(.get_cal, usb_get_cal),
+ FIELD_INIT(.get_otp, usb_get_otp),
+ FIELD_INIT(.write_otp, usb_write_otp),
+ FIELD_INIT(.lock_otp, usb_lock_otp),
+ FIELD_INIT(.get_device_speed, usb_get_device_speed),
+
+ FIELD_INIT(.config_gpio_write, legacy_config_gpio_write),
+ FIELD_INIT(.config_gpio_read, nios_legacy_config_read),
+
+ FIELD_INIT(.expansion_gpio_write, nios_legacy_expansion_gpio_write),
+ FIELD_INIT(.expansion_gpio_read, nios_legacy_expansion_gpio_read),
+ FIELD_INIT(.expansion_gpio_dir_write, nios_legacy_expansion_gpio_dir_write),
+ FIELD_INIT(.expansion_gpio_dir_read, nios_legacy_expansion_gpio_dir_read),
+
+ FIELD_INIT(.set_iq_gain_correction, nios_legacy_set_iq_gain_correction),
+ FIELD_INIT(.set_iq_phase_correction, nios_legacy_set_iq_phase_correction),
+ FIELD_INIT(.get_iq_gain_correction, nios_legacy_get_iq_gain_correction),
+ FIELD_INIT(.get_iq_phase_correction, nios_legacy_get_iq_phase_correction),
+
+ FIELD_INIT(.set_agc_dc_correction, set_agc_dc_correction_unsupported),
+
+ FIELD_INIT(.get_timestamp, nios_legacy_get_timestamp),
+
+ FIELD_INIT(.si5338_write, nios_legacy_si5338_write),
+ FIELD_INIT(.si5338_read, nios_legacy_si5338_read),
+
+ FIELD_INIT(.lms_write, nios_legacy_lms6_write),
+ FIELD_INIT(.lms_read, nios_legacy_lms6_read),
+
+ FIELD_INIT(.ina219_write, nios_legacy_ina219_write),
+ FIELD_INIT(.ina219_read, nios_legacy_ina219_read),
+
+ FIELD_INIT(.ad9361_spi_write, nios_legacy_ad9361_spi_write),
+ FIELD_INIT(.ad9361_spi_read, nios_legacy_ad9361_spi_read),
+
+ FIELD_INIT(.adi_axi_write, nios_legacy_adi_axi_write),
+ FIELD_INIT(.adi_axi_read, nios_legacy_adi_axi_read),
+
+ FIELD_INIT(.rfic_command_write, nios_legacy_rfic_command_write),
+ FIELD_INIT(.rfic_command_read, nios_legacy_rfic_command_read),
+
+ FIELD_INIT(.rffe_control_write, nios_legacy_rffe_control_write),
+ FIELD_INIT(.rffe_control_read, nios_legacy_rffe_control_read),
+
+ FIELD_INIT(.rffe_fastlock_save, nios_legacy_rffe_fastlock_save),
+
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_write, nios_legacy_ad56x1_vctcxo_trim_dac_write),
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, nios_legacy_ad56x1_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.adf400x_write, nios_legacy_adf400x_write),
+ FIELD_INIT(.adf400x_read, nios_legacy_adf400x_read),
+
+ FIELD_INIT(.vctcxo_dac_write, nios_legacy_vctcxo_trim_dac_write),
+ FIELD_INIT(.vctcxo_dac_read, nios_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.set_vctcxo_tamer_mode, set_vctcxo_tamer_mode_unsupported),
+ FIELD_INIT(.get_vctcxo_tamer_mode, get_vctcxo_tamer_mode_unsupported),
+
+ FIELD_INIT(.xb_spi, nios_legacy_xb200_synth_write),
+
+ FIELD_INIT(.set_firmware_loopback, usb_set_firmware_loopback),
+ FIELD_INIT(.get_firmware_loopback, usb_get_firmware_loopback),
+
+ FIELD_INIT(.enable_module, usb_enable_module),
+
+ FIELD_INIT(.init_stream, usb_init_stream),
+ FIELD_INIT(.stream, usb_stream),
+ FIELD_INIT(.submit_stream_buffer, usb_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, usb_deinit_stream),
+
+ FIELD_INIT(.retune, nios_retune),
+ FIELD_INIT(.retune2, nios_retune2),
+
+ FIELD_INIT(.load_fw_from_bootloader, usb_load_fw_from_bootloader),
+
+ FIELD_INIT(.read_fw_log, usb_read_fw_log),
+
+ FIELD_INIT(.read_trigger, nios_legacy_read_trigger),
+ FIELD_INIT(.write_trigger, nios_legacy_write_trigger),
+
+ FIELD_INIT(.name, "usb"),
+};
+
+/* USB backend for use with FPGA supporting update NIOS II packet formats */
+const struct backend_fns backend_fns_usb = {
+ FIELD_INIT(.matches, usb_matches),
+
+ FIELD_INIT(.probe, usb_probe),
+
+ FIELD_INIT(.get_vid_pid, usb_get_vid_pid),
+ FIELD_INIT(.get_flash_id, usb_get_flash_id),
+ FIELD_INIT(.open, usb_open),
+ FIELD_INIT(.set_fpga_protocol, usb_set_fpga_protocol),
+ FIELD_INIT(.close, usb_close),
+
+ FIELD_INIT(.is_fw_ready, usb_is_fw_ready),
+
+ FIELD_INIT(.get_handle, usb_get_handle),
+
+ FIELD_INIT(.load_fpga, usb_load_fpga),
+ FIELD_INIT(.is_fpga_configured, usb_is_fpga_configured),
+ FIELD_INIT(.get_fpga_source, usb_get_fpga_source),
+
+ FIELD_INIT(.get_fw_version, usb_get_fw_version),
+ FIELD_INIT(.get_fpga_version, usb_get_fpga_version),
+
+ FIELD_INIT(.erase_flash_blocks, usb_erase_flash_blocks),
+ FIELD_INIT(.read_flash_pages, usb_read_flash_pages),
+ FIELD_INIT(.write_flash_pages, usb_write_flash_pages),
+
+ FIELD_INIT(.device_reset, usb_device_reset),
+ FIELD_INIT(.jump_to_bootloader, usb_jump_to_bootloader),
+
+ FIELD_INIT(.get_cal, usb_get_cal),
+ FIELD_INIT(.get_otp, usb_get_otp),
+ FIELD_INIT(.write_otp, usb_write_otp),
+ FIELD_INIT(.lock_otp, usb_lock_otp),
+ FIELD_INIT(.get_device_speed, usb_get_device_speed),
+
+ FIELD_INIT(.config_gpio_write, config_gpio_write),
+ FIELD_INIT(.config_gpio_read, nios_config_read),
+
+ FIELD_INIT(.expansion_gpio_write, nios_expansion_gpio_write),
+ FIELD_INIT(.expansion_gpio_read, nios_expansion_gpio_read),
+ FIELD_INIT(.expansion_gpio_dir_write, nios_expansion_gpio_dir_write),
+ FIELD_INIT(.expansion_gpio_dir_read, nios_expansion_gpio_dir_read),
+
+ FIELD_INIT(.set_iq_gain_correction, nios_set_iq_gain_correction),
+ FIELD_INIT(.set_iq_phase_correction, nios_set_iq_phase_correction),
+ FIELD_INIT(.get_iq_gain_correction, nios_get_iq_gain_correction),
+ FIELD_INIT(.get_iq_phase_correction, nios_get_iq_phase_correction),
+
+ FIELD_INIT(.set_agc_dc_correction, nios_set_agc_dc_correction),
+
+ FIELD_INIT(.get_timestamp, nios_get_timestamp),
+
+ FIELD_INIT(.si5338_write, nios_si5338_write),
+ FIELD_INIT(.si5338_read, nios_si5338_read),
+
+ FIELD_INIT(.lms_write, nios_lms6_write),
+ FIELD_INIT(.lms_read, nios_lms6_read),
+
+ FIELD_INIT(.ina219_write, nios_ina219_write),
+ FIELD_INIT(.ina219_read, nios_ina219_read),
+
+ FIELD_INIT(.ad9361_spi_write, nios_ad9361_spi_write),
+ FIELD_INIT(.ad9361_spi_read, nios_ad9361_spi_read),
+
+ FIELD_INIT(.adi_axi_write, nios_adi_axi_write),
+ FIELD_INIT(.adi_axi_read, nios_adi_axi_read),
+
+ FIELD_INIT(.wishbone_master_write, nios_wishbone_master_write),
+ FIELD_INIT(.wishbone_master_read, nios_wishbone_master_read),
+
+ FIELD_INIT(.rfic_command_write, nios_rfic_command_write),
+ FIELD_INIT(.rfic_command_read, nios_rfic_command_read),
+
+ FIELD_INIT(.rffe_control_write, nios_rffe_control_write),
+ FIELD_INIT(.rffe_control_read, nios_rffe_control_read),
+
+ FIELD_INIT(.rffe_fastlock_save, nios_rffe_fastlock_save),
+
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_write, nios_ad56x1_vctcxo_trim_dac_write),
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, nios_ad56x1_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.adf400x_write, nios_adf400x_write),
+ FIELD_INIT(.adf400x_read, nios_adf400x_read),
+
+ FIELD_INIT(.vctcxo_dac_write, nios_vctcxo_trim_dac_write),
+ FIELD_INIT(.vctcxo_dac_read, nios_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.set_vctcxo_tamer_mode, nios_set_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_tamer_mode, nios_get_vctcxo_tamer_mode),
+
+ FIELD_INIT(.xb_spi, nios_xb200_synth_write),
+
+ FIELD_INIT(.set_firmware_loopback, usb_set_firmware_loopback),
+ FIELD_INIT(.get_firmware_loopback, usb_get_firmware_loopback),
+
+ FIELD_INIT(.enable_module, usb_enable_module),
+
+ FIELD_INIT(.init_stream, usb_init_stream),
+ FIELD_INIT(.stream, usb_stream),
+ FIELD_INIT(.submit_stream_buffer, usb_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, usb_deinit_stream),
+
+ FIELD_INIT(.retune, nios_retune),
+ FIELD_INIT(.retune2, nios_retune2),
+
+ FIELD_INIT(.load_fw_from_bootloader, usb_load_fw_from_bootloader),
+
+ FIELD_INIT(.read_fw_log, usb_read_fw_log),
+
+ FIELD_INIT(.read_trigger, nios_read_trigger),
+ FIELD_INIT(.write_trigger, nios_write_trigger),
+
+ FIELD_INIT(.name, "usb"),
+};
diff --git a/Radio/HW/BladeRF/src/backend/usb/usb.h b/Radio/HW/BladeRF/src/backend/usb/usb.h
new file mode 100644
index 0000000..644ed7a
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/usb.h
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BACKEND_USB_H_
+#define BACKEND_USB_H_
+
+#include "host_config.h"
+
+#include "board/board.h"
+
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+extern bool bladerf_usb_reset_device_on_open;
+#endif
+
+#ifndef SAMPLE_EP_IN
+#define SAMPLE_EP_IN 0x81
+#endif
+
+#ifndef SAMPLE_EP_OUT
+#define SAMPLE_EP_OUT 0x01
+#endif
+
+#ifndef PERIPHERAL_EP_IN
+#define PERIPHERAL_EP_IN 0x82
+#endif
+
+#ifndef PERIPHERAL_EP_OUT
+#define PERIPHERAL_EP_OUT 0x02
+#endif
+
+#ifndef PERIPHERAL_TIMEOUT_MS
+#define PERIPHERAL_TIMEOUT_MS 250
+#endif
+
+/* Be careful when lowering this value. The control request for flash erase
+ * operations take some time */
+#ifndef CTRL_TIMEOUT_MS
+#define CTRL_TIMEOUT_MS 1000
+#endif
+
+#ifndef BULK_TIMEOUT_MS
+#define BULK_TIMEOUT_MS 1000
+#endif
+
+/* Size of a host<->FPGA message in BYTES */
+#define USB_MSG_SIZE_SS 2048
+#define USB_MSG_SIZE_HS 1024
+
+typedef enum {
+ USB_TARGET_DEVICE,
+ USB_TARGET_INTERFACE,
+ USB_TARGET_ENDPOINT,
+ USB_TARGET_OTHER
+} usb_target;
+
+typedef enum {
+ USB_REQUEST_STANDARD,
+ USB_REQUEST_CLASS,
+ USB_REQUEST_VENDOR
+} usb_request;
+
+typedef enum {
+ USB_DIR_HOST_TO_DEVICE = 0x00,
+ USB_DIR_DEVICE_TO_HOST = 0x80
+} usb_direction;
+
+/**
+ * USB backend driver function table
+ *
+ * All return values are expected to be 0 on success, or a BLADERF_ERR_*
+ * value on failure
+ */
+struct usb_fns {
+ int (*probe)(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list);
+
+ /* Populates the `driver` pointer with a handle for the specific USB driver.
+ * `info_in` describes the device to open, and may contain wildcards.
+ * On success, the driver should fill in `info_out` with the complete
+ * details of the device. */
+ int (*open)(void **driver,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out);
+
+ void (*close)(void *driver);
+
+ int (*get_vid_pid)(void *driver, uint16_t *vid, uint16_t *pid);
+
+ int (*get_flash_id)(void *driver, uint8_t *mid, uint8_t *did);
+
+ int (*get_handle)(void *driver, void **handle);
+
+ int (*get_speed)(void *driver, bladerf_dev_speed *speed);
+
+ int (*change_setting)(void *driver, uint8_t setting);
+
+ int (*control_transfer)(void *driver,
+ usb_target target_type,
+ usb_request req_type,
+ usb_direction direction,
+ uint8_t request,
+ uint16_t wvalue,
+ uint16_t windex,
+ void *buffer,
+ uint32_t buffer_len,
+ uint32_t timeout_ms);
+
+ int (*bulk_transfer)(void *driver,
+ uint8_t endpoint,
+ void *buffer,
+ uint32_t buffer_len,
+ uint32_t timeout_ms);
+
+ int (*get_string_descriptor)(void *driver,
+ uint8_t index,
+ void *buffer,
+ uint32_t buffer_len);
+
+ int (*init_stream)(void *driver,
+ struct bladerf_stream *stream,
+ size_t num_transfers);
+
+ int (*stream)(void *driver,
+ struct bladerf_stream *stream,
+ bladerf_channel_layout layout);
+
+ int (*submit_stream_buffer)(void *driver,
+ struct bladerf_stream *stream,
+ void *buffer,
+ size_t *length,
+ unsigned int timeout_ms,
+ bool nonblock);
+
+ int (*deinit_stream)(void *driver, struct bladerf_stream *stream);
+
+ int (*open_bootloader)(void **driver, uint8_t bus, uint8_t addr);
+ void (*close_bootloader)(void *driver);
+};
+
+struct usb_driver {
+ const struct usb_fns *fn;
+ bladerf_backend id;
+};
+
+struct bladerf_usb {
+ const struct usb_fns *fn;
+ void *driver;
+};
+
+#endif