summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/src')
-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
-rw-r--r--Radio/HW/BladeRF/src/bladerf.c2387
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/bladerf1.c4166
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/calibration.c518
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/calibration.h107
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/capabilities.c118
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/capabilities.h52
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/compatibility.c75
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/compatibility.h9
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/flash.c543
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/flash.h172
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/image.c592
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c3744
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/capabilities.c106
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/capabilities.h52
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/common.c415
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/common.h494
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/compatibility.c50
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/compatibility.h9
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c767
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c1058
-rw-r--r--Radio/HW/BladeRF/src/board/board.c13
-rw-r--r--Radio/HW/BladeRF/src/board/board.h495
-rw-r--r--Radio/HW/BladeRF/src/device_calibration.c522
-rw-r--r--Radio/HW/BladeRF/src/devinfo.c420
-rw-r--r--Radio/HW/BladeRF/src/devinfo.h131
-rw-r--r--Radio/HW/BladeRF/src/driver/dac161s055.c65
-rw-r--r--Radio/HW/BladeRF/src/driver/dac161s055.h51
-rw-r--r--Radio/HW/BladeRF/src/driver/fpga_trigger.c196
-rw-r--r--Radio/HW/BladeRF/src/driver/fpga_trigger.h128
-rw-r--r--Radio/HW/BladeRF/src/driver/fx3_fw.c343
-rw-r--r--Radio/HW/BladeRF/src/driver/fx3_fw.h79
-rw-r--r--Radio/HW/BladeRF/src/driver/ina219.c154
-rw-r--r--Radio/HW/BladeRF/src/driver/ina219.h83
-rw-r--r--Radio/HW/BladeRF/src/driver/si5338.c669
-rw-r--r--Radio/HW/BladeRF/src/driver/si5338.h134
-rw-r--r--Radio/HW/BladeRF/src/driver/smb_clock.c211
-rw-r--r--Radio/HW/BladeRF/src/driver/smb_clock.h47
-rw-r--r--Radio/HW/BladeRF/src/driver/spi_flash.c134
-rw-r--r--Radio/HW/BladeRF/src/driver/spi_flash.h99
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb100.c98
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb100.h47
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb200.c543
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb200.h105
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb300.c299
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb300.h94
-rw-r--r--Radio/HW/BladeRF/src/helpers/configfile.c342
-rw-r--r--Radio/HW/BladeRF/src/helpers/configfile.h27
-rw-r--r--Radio/HW/BladeRF/src/helpers/file.c511
-rw-r--r--Radio/HW/BladeRF/src/helpers/file.h97
-rw-r--r--Radio/HW/BladeRF/src/helpers/have_cap.h43
-rw-r--r--Radio/HW/BladeRF/src/helpers/interleave.c182
-rw-r--r--Radio/HW/BladeRF/src/helpers/interleave.h44
-rw-r--r--Radio/HW/BladeRF/src/helpers/timeout.c52
-rw-r--r--Radio/HW/BladeRF/src/helpers/timeout.h40
-rw-r--r--Radio/HW/BladeRF/src/helpers/version.c177
-rw-r--r--Radio/HW/BladeRF/src/helpers/version.h142
-rw-r--r--Radio/HW/BladeRF/src/helpers/wallclock.c47
-rw-r--r--Radio/HW/BladeRF/src/helpers/wallclock.h29
-rw-r--r--Radio/HW/BladeRF/src/init_fini.c103
-rw-r--r--Radio/HW/BladeRF/src/libbladerf-Bridging-Header.h8
-rw-r--r--Radio/HW/BladeRF/src/streaming/async.c294
-rw-r--r--Radio/HW/BladeRF/src/streaming/async.h111
-rw-r--r--Radio/HW/BladeRF/src/streaming/format.h109
-rw-r--r--Radio/HW/BladeRF/src/streaming/metadata.h180
-rw-r--r--Radio/HW/BladeRF/src/streaming/sync.c1339
-rw-r--r--Radio/HW/BladeRF/src/streaming/sync.h193
-rw-r--r--Radio/HW/BladeRF/src/streaming/sync_worker.c532
-rw-r--r--Radio/HW/BladeRF/src/streaming/sync_worker.h130
-rw-r--r--Radio/HW/BladeRF/src/test.swift8
-rw-r--r--Radio/HW/BladeRF/src/version.h12
-rw-r--r--Radio/HW/BladeRF/src/version.h.in12
85 files changed, 33748 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
diff --git a/Radio/HW/BladeRF/src/bladerf.c b/Radio/HW/BladeRF/src/bladerf.c
new file mode 100644
index 0000000..f19c3a6
--- /dev/null
+++ b/Radio/HW/BladeRF/src/bladerf.c
@@ -0,0 +1,2387 @@
+/*
+ * 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 <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libbladeRF.h>
+
+#include "log.h"
+#include "rel_assert.h"
+#define LOGGER_ID_STRING
+#include "logger_entry.h"
+#include "logger_id.h"
+
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+#include "board/board.h"
+#include "conversions.h"
+#include "driver/fx3_fw.h"
+#include "device_calibration.h"
+#include "streaming/async.h"
+#include "version.h"
+
+#include "expansion/xb100.h"
+#include "expansion/xb200.h"
+#include "expansion/xb300.h"
+
+#include "devinfo.h"
+#include "helpers/configfile.h"
+#include "helpers/file.h"
+#include "helpers/have_cap.h"
+#include "helpers/interleave.h"
+
+#define CHECK_NULL(...) do { \
+ const void* _args[] = { __VA_ARGS__, NULL }; \
+ for (size_t _i = 0; _args[_i] != NULL; ++_i) { \
+ if (_args[_i] == NULL) { \
+ log_error("%s:%d: Argument %zu is a NULL pointer\n", __FILE__, __LINE__, _i + 1); \
+ return BLADERF_ERR_INVAL; \
+ } \
+ } \
+} while (0)
+
+#define CHECK_STATUS(fn) \
+ do { \
+ status = fn; \
+ if (status != 0) { \
+ log_error("%s: %s %s\n", __FUNCTION__, #fn, \
+ bladerf_strerror(status)); \
+ goto error; \
+ } \
+ } while (0)
+
+
+/******************************************************************************/
+/* Private Function Declarations */
+/******************************************************************************/
+
+/**
+ * Sets up the register configuration for oversample feature.
+ *
+ * @param dev Device handle
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int bladerf_set_oversample_register_config(struct bladerf *dev);
+
+/******************************************************************************/
+/* Open / Close */
+/******************************************************************************/
+
+/* dev path becomes device specifier string (osmosdr-like) */
+int bladerf_open(struct bladerf **dev, const char *dev_id)
+{
+ struct bladerf_devinfo devinfo;
+ int status;
+
+ *dev = NULL;
+
+ /* Populate dev-info from string */
+ status = str2devinfo(dev_id, &devinfo);
+ if (!status) {
+ status = bladerf_open_with_devinfo(dev, &devinfo);
+ }
+
+ return status;
+}
+
+int bladerf_open_with_devinfo(struct bladerf **opened_device,
+ struct bladerf_devinfo *devinfo)
+{
+ struct bladerf *dev;
+ struct bladerf_devinfo any_device;
+ unsigned int i;
+ int status;
+
+ if (devinfo == NULL) {
+ bladerf_init_devinfo(&any_device);
+ devinfo = &any_device;
+ }
+
+ *opened_device = NULL;
+
+ dev = calloc(1, sizeof(struct bladerf));
+ if (dev == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Open backend */
+ status = backend_open(dev, devinfo);
+ if (status != 0) {
+ free(dev);
+ return status;
+ }
+
+ /* Find matching board */
+ for (i = 0; i < bladerf_boards_len; i++) {
+ if (bladerf_boards[i]->matches(dev)) {
+ dev->board = bladerf_boards[i];
+ break;
+ }
+ }
+ /* If no matching board was found */
+ if (i == bladerf_boards_len) {
+ dev->backend->close(dev);
+ free(dev);
+ return BLADERF_ERR_NODEV;
+ }
+
+ MUTEX_INIT(&dev->lock);
+
+ /* Open board */
+ status = dev->board->open(dev, devinfo);
+
+ if (status < 0) {
+ bladerf_close(dev);
+ return status;
+ }
+
+ /* Load configuration file */
+ status = config_load_options_file(dev);
+
+ if (status < 0) {
+ bladerf_close(dev);
+ return status;
+ }
+
+ *opened_device = dev;
+
+ return 0;
+}
+
+int bladerf_get_devinfo(struct bladerf *dev, struct bladerf_devinfo *info)
+{
+ if (dev) {
+ MUTEX_LOCK(&dev->lock);
+ memcpy(info, &dev->ident, sizeof(struct bladerf_devinfo));
+ MUTEX_UNLOCK(&dev->lock);
+ return 0;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+int bladerf_get_backendinfo(struct bladerf *dev, struct bladerf_backendinfo *info)
+{
+ if (dev) {
+ MUTEX_LOCK(&dev->lock);
+ info->lock_count = 1;
+ info->lock = &dev->lock;
+
+ info->handle_count = 1;
+ dev->backend->get_handle(dev, &info->handle);
+ MUTEX_UNLOCK(&dev->lock);
+ return 0;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+void bladerf_close(struct bladerf *dev)
+{
+ if (dev) {
+ MUTEX_LOCK(&dev->lock);
+
+ dev->board->close(dev);
+
+ if (dev->backend) {
+ dev->backend->close(dev);
+ }
+
+ /** Free gain table entries */
+ for (int i = 0; i < NUM_GAIN_CAL_TBLS; i++) {
+ gain_cal_tbl_free(&dev->gain_tbls[i]);
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ free(dev);
+ }
+}
+
+/******************************************************************************/
+/* FX3 Firmware (common to bladerf1 and bladerf2) */
+/******************************************************************************/
+
+int bladerf_jump_to_bootloader(struct bladerf *dev)
+{
+ int status;
+
+ if (!dev->backend->jump_to_bootloader) {
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->jump_to_bootloader(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_bootloader_list(struct bladerf_devinfo **devices)
+{
+ return probe(BACKEND_PROBE_FX3_BOOTLOADER, devices);
+}
+
+int bladerf_load_fw_from_bootloader(const char *device_identifier,
+ bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ const char *file)
+{
+ int status;
+ uint8_t *buf;
+ size_t buf_len;
+ struct fx3_firmware *fw = NULL;
+ struct bladerf_devinfo devinfo;
+
+ if (device_identifier == NULL) {
+ bladerf_init_devinfo(&devinfo);
+ devinfo.backend = backend;
+ devinfo.usb_bus = bus;
+ devinfo.usb_addr = addr;
+ } else {
+ status = str2devinfo(device_identifier, &devinfo);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = file_read_buffer(file, &buf, &buf_len);
+ if (status != 0) {
+ return status;
+ }
+
+ status = fx3_fw_parse(&fw, buf, buf_len);
+ free(buf);
+ if (status != 0) {
+ return status;
+ }
+
+ assert(fw != NULL);
+
+ status = backend_load_fw_from_bootloader(devinfo.backend, devinfo.usb_bus,
+ devinfo.usb_addr, fw);
+
+ fx3_fw_free(fw);
+
+ return status;
+}
+
+int bladerf_get_fw_log(struct bladerf *dev, const char *filename)
+{
+ int status;
+ FILE *f = NULL;
+ logger_entry e;
+
+ MUTEX_LOCK(&dev->lock);
+
+ if (!have_cap(dev->board->get_capabilities(dev),
+ BLADERF_CAP_READ_FW_LOG_ENTRY)) {
+ struct bladerf_version fw_version;
+
+ if (dev->board->get_fw_version(dev, &fw_version) == 0) {
+ log_debug("FX3 FW v%s does not support log retrieval.\n",
+ fw_version.describe);
+ }
+
+ status = BLADERF_ERR_UNSUPPORTED;
+ goto error;
+ }
+
+ if (filename != NULL) {
+ f = fopen(filename, "w");
+ if (f == NULL) {
+ switch (errno) {
+ case ENOENT:
+ status = BLADERF_ERR_NO_FILE;
+ break;
+ case EACCES:
+ status = BLADERF_ERR_PERMISSION;
+ break;
+ default:
+ status = BLADERF_ERR_IO;
+ break;
+ }
+ goto error;
+ }
+ } else {
+ f = stdout;
+ }
+
+ do {
+ status = dev->backend->read_fw_log(dev, &e);
+ if (status != 0) {
+ log_debug("Failed to read FW log: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ if (e == LOG_ERR) {
+ fprintf(f, "<Unexpected error>,,\n");
+ } else if (e != LOG_EOF) {
+ uint8_t file_id;
+ uint16_t line;
+ uint16_t data;
+ const char *src_file;
+
+ logger_entry_unpack(e, &file_id, &line, &data);
+ src_file = logger_id_string(file_id);
+
+ fprintf(f, "%s, %u, 0x%04x\n", src_file, line, data);
+ }
+ } while (e != LOG_EOF && e != LOG_ERR);
+
+error:
+ MUTEX_UNLOCK(&dev->lock);
+
+ if (f != NULL && f != stdout) {
+ fclose(f);
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Properties */
+/******************************************************************************/
+
+bladerf_dev_speed bladerf_device_speed(struct bladerf *dev)
+{
+ bladerf_dev_speed speed;
+ MUTEX_LOCK(&dev->lock);
+
+ speed = dev->board->device_speed(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return speed;
+}
+
+int bladerf_get_serial(struct bladerf *dev, char *serial)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ /** TODO: add deprecation warning */
+
+ status = dev->board->get_serial(dev, serial);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_serial_struct(struct bladerf *dev,
+ struct bladerf_serial *serial)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ char serialstr[sizeof(serial->serial)];
+
+ status = dev->board->get_serial(dev, serialstr);
+
+ if (status >= 0) {
+ strncpy(serial->serial, serialstr, sizeof(serial->serial));
+ serial->serial[sizeof(serial->serial)-1] = '\0';
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_fpga_size(struct bladerf *dev, bladerf_fpga_size *size)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fpga_size(dev, size);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_fpga_bytes(struct bladerf *dev, size_t *size)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fpga_bytes(dev, size);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_flash_size(struct bladerf *dev, uint32_t *size, bool *is_guess)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_flash_size(dev, size, is_guess);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_is_fpga_configured(struct bladerf *dev)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->is_fpga_configured(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_fpga_source(struct bladerf *dev, bladerf_fpga_source *source)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fpga_source(dev, source);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+const char *bladerf_get_board_name(struct bladerf *dev)
+{
+ return dev->board->name;
+}
+
+size_t bladerf_get_channel_count(struct bladerf *dev, bladerf_direction dir)
+{
+ return dev->board->get_channel_count(dev, dir);
+}
+
+/******************************************************************************/
+/* Versions */
+/******************************************************************************/
+
+int bladerf_fpga_version(struct bladerf *dev, struct bladerf_version *version)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fpga_version(dev, version);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_fw_version(struct bladerf *dev, struct bladerf_version *version)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fw_version(dev, version);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+void bladerf_version(struct bladerf_version *version)
+{
+/* Sanity checks for version reporting mismatches */
+#ifndef LIBBLADERF_API_VERSION
+#error LIBBLADERF_API_VERSION is missing
+#endif
+#if LIBBLADERF_VERSION_MAJOR != ((LIBBLADERF_API_VERSION >> 24) & 0xff)
+#error LIBBLADERF_API_VERSION: Major version mismatch
+#endif
+#if LIBBLADERF_VERSION_MINOR != ((LIBBLADERF_API_VERSION >> 16) & 0xff)
+#error LIBBLADERF_API_VERSION: Minor version mismatch
+#endif
+#if LIBBLADERF_VERSION_PATCH != ((LIBBLADERF_API_VERSION >> 8) & 0xff)
+#error LIBBLADERF_API_VERSION: Patch version mismatch
+#endif
+ version->major = LIBBLADERF_VERSION_MAJOR;
+ version->minor = LIBBLADERF_VERSION_MINOR;
+ version->patch = LIBBLADERF_VERSION_PATCH;
+ version->describe = LIBBLADERF_VERSION;
+}
+
+/******************************************************************************/
+/* Enable/disable */
+/******************************************************************************/
+
+int bladerf_enable_module(struct bladerf *dev, bladerf_channel ch, bool enable)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->enable_module(dev, ch, enable);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Gain */
+/******************************************************************************/
+
+int bladerf_set_gain(struct bladerf *dev, bladerf_channel ch, int gain)
+{
+ int status;
+ bladerf_gain_mode gain_mode;
+ bladerf_frequency freq;
+ bladerf_gain assigned_gain = gain;
+ MUTEX_LOCK(&dev->lock);
+
+ /* Change gain mode to manual if ch = RX */
+ if (BLADERF_CHANNEL_IS_TX(ch) == false) {
+ status = dev->board->get_gain_mode(dev, ch, &gain_mode);
+ if (status != 0) {
+ log_error("Failed to get gain mode\n");
+ goto error;
+ }
+
+ if (gain_mode != BLADERF_GAIN_MGC) {
+ log_warning("Setting gain mode to manual\n");
+ status = dev->board->set_gain_mode(dev, ch, BLADERF_GAIN_MGC);
+ if (status != 0) {
+ log_error("Failed to set gain mode\n");
+ goto error;
+ }
+ }
+ }
+
+ dev->gain_tbls[ch].gain_target = gain;
+
+ if (dev->gain_tbls[ch].enabled == true) {
+ dev->board->get_frequency(dev, ch, &freq);
+ get_gain_correction(dev, freq, ch, &assigned_gain);
+ }
+
+ status = dev->board->set_gain(dev, ch, assigned_gain);
+ if (status != 0) {
+ log_error("Failed to set gain\n");
+ goto error;
+ }
+
+error:
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain(struct bladerf *dev, bladerf_channel ch, int *gain)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_gain(dev, ch, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_gain_mode(dev, ch, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_gain_mode(dev, ch, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain_modes(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_gain_modes const **modes)
+{
+ return dev->board->get_gain_modes(dev, ch, modes);
+}
+
+int bladerf_get_gain_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ return dev->board->get_gain_range(dev, ch, range);
+}
+
+int bladerf_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain gain)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_gain_stage(dev, ch, stage, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain *gain)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_gain_stage(dev, ch, stage, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain_stage_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ struct bladerf_range const **range)
+{
+ return dev->board->get_gain_stage_range(dev, ch, stage, range);
+}
+
+int bladerf_get_gain_stages(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **stages,
+ size_t count)
+{
+ return dev->board->get_gain_stages(dev, ch, stages, count);
+}
+
+/******************************************************************************/
+/* Sample Rate */
+/******************************************************************************/
+
+int bladerf_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate,
+ bladerf_sample_rate *actual)
+{
+ int status;
+ bladerf_feature feature = dev->feature;
+
+ MUTEX_LOCK(&dev->lock);
+ status = dev->board->set_sample_rate(dev, ch, rate, actual);
+ MUTEX_UNLOCK(&dev->lock);
+
+ /*****************************************************
+ Sample rate assignments clear previous register
+ values. We must reassign oversample register config
+ for every set_samplerate().
+ *******************************************************/
+ if ((feature & BLADERF_FEATURE_OVERSAMPLE)) {
+ status = bladerf_set_oversample_register_config(dev);
+ if (status != 0) {
+ log_error("Oversample register config failure\n");
+ }
+ }
+
+ return status;
+}
+
+int bladerf_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_sample_rate(dev, ch, rate);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_sample_rate_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ return dev->board->get_sample_rate_range(dev, ch, range);
+}
+
+int bladerf_set_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ int status;
+ bladerf_feature feature = dev->feature;
+
+ MUTEX_LOCK(&dev->lock);
+ status = dev->board->set_rational_sample_rate(dev, ch, rate, actual);
+ MUTEX_UNLOCK(&dev->lock);
+
+ /*****************************************************
+ Register config for OVERSAMPLE operation
+
+ Sample rate assignments clear previous register
+ values. We must reassign for every set_samplerate().
+
+ Note: bladerf_set_rfic_register is mutex locked. Must
+ be placed outside of a mutex lock like above.
+ *******************************************************/
+ if ((feature & BLADERF_FEATURE_OVERSAMPLE)) {
+ status = bladerf_set_oversample_register_config(dev);
+ if (status != 0) {
+ log_error("Oversample register config failure\n");
+ }
+ }
+
+ return status;
+}
+
+int bladerf_get_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_rational_sample_rate(dev, ch, rate);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+int bladerf_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_bandwidth(dev, ch, bandwidth, actual);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_bandwidth(dev, ch, bandwidth);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_bandwidth_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ return dev->board->get_bandwidth_range(dev, ch, range);
+}
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+int bladerf_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_frequency(dev, ch, frequency);
+
+ if (dev->gain_tbls[ch].enabled && status == 0) {
+ status = apply_gain_correction(dev, ch, frequency);
+ if (status != 0) {
+ log_error("Failed to set gain correction\n");
+ }
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_frequency(dev, ch, frequency);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_frequency_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ return dev->board->get_frequency_range(dev, ch, range);
+}
+
+int bladerf_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->select_band(dev, ch, frequency);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* RF Ports*/
+/******************************************************************************/
+
+int bladerf_set_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_rf_port(dev, ch, port);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **port)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_rf_port(dev, ch, port);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_rf_ports(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **ports,
+ unsigned int count)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_rf_ports(dev, ch, ports, count);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Scheduled Tuning */
+/******************************************************************************/
+
+int bladerf_get_quick_tune(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_quick_tune(dev, ch, quick_tune);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_print_quick_tune(struct bladerf *dev, const struct bladerf_quick_tune *qt) {
+ if (dev == NULL || qt == NULL) {
+ log_error("Device handle or quick tune structure is NULL.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ const char *board_name = bladerf_get_board_name(dev);
+ if (board_name == NULL) {
+ log_error("Failed to get board name.\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ printf("board: %s\n", board_name);
+ if (strcmp(board_name, "bladerf1") == 0) {
+ printf("freqsel: %u\n", qt->freqsel);
+ printf("vcocap: %u\n", qt->vcocap);
+ printf("nint: %u\n", qt->nint);
+ printf("nfrac: %u\n", qt->nfrac);
+ printf("flags: %u\n", qt->flags);
+ printf("xb_gpio: %u\n", qt->xb_gpio);
+ } else if (strcmp(board_name, "bladerf2") == 0) {
+ printf("nios_profile: %u\n", qt->nios_profile);
+ printf("rffe_profile: %u\n", qt->rffe_profile);
+ printf("port: %u\n", qt->port);
+ printf("spdt: %u\n", qt->spdt);
+ } else {
+ log_error("Unknown bladeRF board name: %s\n", board_name);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+int bladerf_schedule_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune)
+
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status =
+ dev->board->schedule_retune(dev, ch, timestamp, frequency, quick_tune);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_cancel_scheduled_retunes(struct bladerf *dev, bladerf_channel ch)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->cancel_scheduled_retunes(dev, ch);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* DC/Phase/Gain Correction */
+/******************************************************************************/
+
+int bladerf_get_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ bladerf_correction_value *value)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_correction(dev, ch, corr, value);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_set_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ bladerf_correction_value value)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_correction(dev, ch, corr, value);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Trigger */
+/******************************************************************************/
+
+int bladerf_trigger_init(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trigger_init(dev, ch, signal, trigger);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trigger_arm(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool arm,
+ uint64_t resv1,
+ uint64_t resv2)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trigger_arm(dev, trigger, arm, resv1, resv2);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trigger_fire(struct bladerf *dev,
+ const struct bladerf_trigger *trigger)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trigger_fire(dev, trigger);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trigger_state(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested,
+ uint64_t *reserved1,
+ uint64_t *reserved2)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trigger_state(dev, trigger, is_armed, has_fired,
+ fire_requested, reserved1, reserved2);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Streaming */
+/******************************************************************************/
+
+int bladerf_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *data)
+{
+ int status;
+ bladerf_sample_rate tx_samp_rate;
+ bladerf_sample_rate rx_samp_rate;
+ MUTEX_LOCK(&dev->lock);
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) {
+ log_error("bladeRF 2.0 required for 8bit format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ status = dev->board->init_stream(stream, dev, callback, buffers,
+ num_buffers, format, samples_per_buffer,
+ num_transfers, data);
+
+ dev->board->get_sample_rate(dev, BLADERF_MODULE_TX, &tx_samp_rate);
+ if (tx_samp_rate) {
+ if (tx_samp_rate < num_transfers * samples_per_buffer / (*stream)->transfer_timeout) {
+ log_warning("TX samples may be dropped.\n");
+ log_warning("Condition to meet: samp_rate > num_transfers * samples_per_buffer / transfer_timeout\n");
+ }
+ }
+
+ dev->board->get_sample_rate(dev, BLADERF_MODULE_RX, &rx_samp_rate);
+ if (rx_samp_rate) {
+ if (rx_samp_rate < num_transfers * samples_per_buffer / (*stream)->transfer_timeout) {
+ log_warning("RX samples may be dropped.\n");
+ log_warning("Condition to meet: samp_rate > num_transfers * samples_per_buffer / transfer_timeout\n");
+ }
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_stream(struct bladerf_stream *stream, bladerf_channel_layout layout)
+{
+ return stream->dev->board->stream(stream, layout);
+}
+
+int bladerf_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms)
+{
+ return stream->dev->board->submit_stream_buffer(stream, buffer, timeout_ms,
+ false);
+}
+
+int bladerf_submit_stream_buffer_nb(struct bladerf_stream *stream, void *buffer)
+{
+ return stream->dev->board->submit_stream_buffer(stream, buffer, 0, true);
+}
+
+void bladerf_deinit_stream(struct bladerf_stream *stream)
+{
+ if (stream) {
+ stream->dev->board->deinit_stream(stream);
+ }
+}
+
+int bladerf_set_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int timeout)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_stream_timeout(dev, dir, timeout);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int *timeout)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_stream_timeout(dev, dir, timeout);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_sync_config(struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ unsigned int buffer_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) {
+ log_error("bladeRF 2.0 required for 8bit format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ status =
+ dev->board->sync_config(dev, layout, format, num_buffers, buffer_size,
+ num_transfers, stream_timeout);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_sync_tx(struct bladerf *dev,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ CHECK_NULL(samples);
+ return dev->board->sync_tx(dev, samples, num_samples, metadata, timeout_ms);
+}
+
+int bladerf_sync_rx(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ return dev->board->sync_rx(dev, samples, num_samples, metadata, timeout_ms);
+}
+
+int bladerf_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *timestamp)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_timestamp(dev, dir, timestamp);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_interleave_stream_buffer(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples)
+{
+ return _interleave_interleave_buf(layout, format, buffer_size, samples);
+}
+
+int bladerf_deinterleave_stream_buffer(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples)
+{
+ return _interleave_deinterleave_buf(layout, format, buffer_size, samples);
+}
+
+/******************************************************************************/
+/* FPGA/Firmware Loading/Flashing */
+/******************************************************************************/
+
+int bladerf_load_fpga(struct bladerf *dev, const char *fpga_file)
+{
+ uint8_t *buf = NULL;
+ size_t buf_size;
+ int status;
+
+ status = file_read_buffer(fpga_file, &buf, &buf_size);
+ if (status != 0) {
+ log_error("Failed to read FPGA image: %s\n", bladerf_strerror(status));
+ goto exit;
+ }
+
+ status = dev->board->load_fpga(dev, buf, buf_size);
+
+exit:
+ free(buf);
+ return status;
+}
+
+int bladerf_flash_fpga(struct bladerf *dev, const char *fpga_file)
+{
+ uint8_t *buf = NULL;
+ size_t buf_size;
+ int status;
+
+ status = file_read_buffer(fpga_file, &buf, &buf_size);
+ if (status != 0) {
+ goto exit;
+ }
+
+ MUTEX_LOCK(&dev->lock);
+ status = dev->board->flash_fpga(dev, buf, buf_size);
+ MUTEX_UNLOCK(&dev->lock);
+
+exit:
+ free(buf);
+ return status;
+}
+
+int bladerf_erase_stored_fpga(struct bladerf *dev)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->erase_stored_fpga(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_flash_firmware(struct bladerf *dev, const char *firmware_file)
+{
+ uint8_t *buf = NULL;
+ size_t buf_size;
+ int status;
+
+ status = file_read_buffer(firmware_file, &buf, &buf_size);
+ if (status != 0) {
+ goto exit;
+ }
+
+ MUTEX_LOCK(&dev->lock);
+ status = dev->board->flash_firmware(dev, buf, buf_size);
+ MUTEX_UNLOCK(&dev->lock);
+
+exit:
+ free(buf);
+ return status;
+}
+
+int bladerf_device_reset(struct bladerf *dev)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->device_reset(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Tuning mode */
+/******************************************************************************/
+
+int bladerf_set_tuning_mode(struct bladerf *dev, bladerf_tuning_mode mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_tuning_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_tuning_mode(struct bladerf *dev, bladerf_tuning_mode *mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_tuning_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Loopback */
+/******************************************************************************/
+
+int bladerf_get_loopback_modes(struct bladerf *dev,
+ struct bladerf_loopback_modes const **modes)
+{
+ int status;
+
+ status = dev->board->get_loopback_modes(dev, modes);
+
+ return status;
+}
+
+bool bladerf_is_loopback_mode_supported(struct bladerf *dev,
+ bladerf_loopback mode)
+{
+ struct bladerf_loopback_modes modes;
+ struct bladerf_loopback_modes const *modesptr = &modes;
+ int i, count;
+
+ count = bladerf_get_loopback_modes(dev, &modesptr);
+
+ for (i = 0; i < count; ++i) {
+ if (modesptr[i].mode == mode) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int bladerf_set_loopback(struct bladerf *dev, bladerf_loopback l)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_loopback(dev, l);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_loopback(struct bladerf *dev, bladerf_loopback *l)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_loopback(dev, l);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Sample RX FPGA Mux */
+/******************************************************************************/
+
+int bladerf_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mux)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_rx_mux(dev, mux);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mux)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_rx_mux(dev, mux);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level VCTCXO Tamer Mode */
+/******************************************************************************/
+
+int bladerf_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_vctcxo_tamer_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_vctcxo_tamer_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level VCTCXO Trim DAC access */
+/******************************************************************************/
+
+int bladerf_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_vctcxo_trim(dev, trim);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trim_dac_read(struct bladerf *dev, uint16_t *trim)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trim_dac_read(dev, trim);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trim_dac_write(struct bladerf *dev, uint16_t trim)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trim_dac_write(dev, trim);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_dac_read(struct bladerf *dev, uint16_t *trim)
+{
+ return bladerf_trim_dac_read(dev, trim);
+}
+int bladerf_dac_write(struct bladerf *dev, uint16_t trim)
+{
+ return bladerf_trim_dac_write(dev, trim);
+}
+
+/******************************************************************************/
+/* Low-level Trigger control access */
+/******************************************************************************/
+
+int bladerf_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->read_trigger(dev, ch, trigger, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->write_trigger(dev, ch, trigger, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level Wishbone Master access */
+/******************************************************************************/
+int bladerf_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->wishbone_master_read(dev, addr, data);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->wishbone_master_write(dev, addr, data);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level Configuration GPIO access */
+/******************************************************************************/
+
+int bladerf_config_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->config_gpio_read(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->config_gpio_write(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level SPI Flash access */
+/******************************************************************************/
+
+int bladerf_erase_flash(struct bladerf *dev,
+ uint32_t erase_block,
+ uint32_t count)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->erase_flash(dev, erase_block, count);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_erase_flash_bytes(struct bladerf *dev,
+ uint32_t address,
+ uint32_t length)
+{
+ int status;
+ uint32_t eb;
+ uint32_t count;
+
+ /* Make sure address is aligned to an erase block boundary */
+ if( (address % dev->flash_arch->ebsize_bytes) == 0 ) {
+ /* Convert into units of flash pages */
+ eb = address / dev->flash_arch->ebsize_bytes;
+ } else {
+ log_error("Address or length not aligned on a flash page boundary.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Check for the case of erasing less than 1 erase block.
+ * For example, the calibration data. If so, round up to 1 EB.
+ * If erasing more than 1 EB worth of data, make sure the length
+ * is aligned to an EB boundary. */
+ if( (length > 0) && (length < dev->flash_arch->ebsize_bytes) ) {
+ count = 1;
+ } else if ((length % dev->flash_arch->ebsize_bytes) == 0) {
+ /* Convert into units of flash pages */
+ count = length / dev->flash_arch->ebsize_bytes;
+ } else {
+ log_error("Address or length not aligned on a flash page boundary.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_erase_flash(dev, eb, count);
+
+ return status;
+}
+
+int bladerf_read_flash(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->read_flash(dev, buf, page, count);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_read_flash_bytes(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t address,
+ uint32_t length)
+{
+ int status;
+ uint32_t page;
+ uint32_t count;
+
+ /* Check alignment */
+ if( ((address % dev->flash_arch->psize_bytes) != 0) ||
+ ((length % dev->flash_arch->psize_bytes) != 0) ) {
+ log_error("Address or length not aligned on a flash page boundary.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Convert into units of flash pages */
+ page = address / dev->flash_arch->psize_bytes;
+ count = length / dev->flash_arch->psize_bytes;
+
+ status = bladerf_read_flash(dev, buf, page, count);
+
+ return status;
+}
+
+int bladerf_write_flash(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->write_flash(dev, buf, page, count);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_write_flash_bytes(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t address,
+ uint32_t length)
+{
+ int status;
+ uint32_t page;
+ uint32_t count;
+
+ /* Check alignment */
+ if( ((address % dev->flash_arch->psize_bytes) != 0) ||
+ ((length % dev->flash_arch->psize_bytes) != 0) ) {
+ log_error("Address or length not aligned on a flash page boundary.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Convert address and length into units of flash pages */
+ page = address / dev->flash_arch->psize_bytes;
+ count = length / dev->flash_arch->psize_bytes;
+
+ status = bladerf_write_flash(dev, buf, page, count);
+ return status;
+}
+
+int bladerf_read_otp(struct bladerf *dev,
+ uint8_t *buf)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->get_otp(dev, (char *)buf);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_write_otp(struct bladerf *dev,
+ uint8_t *buf)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->write_otp(dev, (char *)buf);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_lock_otp(struct bladerf *dev)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->lock_otp(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Helpers & Miscellaneous */
+/******************************************************************************/
+
+const char *bladerf_strerror(int error)
+{
+ switch (error) {
+ case BLADERF_ERR_UNEXPECTED:
+ return "An unexpected error occurred";
+ case BLADERF_ERR_RANGE:
+ return "Provided parameter was out of the allowable range";
+ case BLADERF_ERR_INVAL:
+ return "Invalid operation or parameter";
+ case BLADERF_ERR_MEM:
+ return "A memory allocation error occurred";
+ case BLADERF_ERR_IO:
+ return "File or device I/O failure";
+ case BLADERF_ERR_TIMEOUT:
+ return "Operation timed out";
+ case BLADERF_ERR_NODEV:
+ return "No devices available";
+ case BLADERF_ERR_UNSUPPORTED:
+ return "Operation not supported";
+ case BLADERF_ERR_MISALIGNED:
+ return "Misaligned flash access";
+ case BLADERF_ERR_CHECKSUM:
+ return "Invalid checksum";
+ case BLADERF_ERR_NO_FILE:
+ return "File not found";
+ case BLADERF_ERR_UPDATE_FPGA:
+ return "An FPGA update is required";
+ case BLADERF_ERR_UPDATE_FW:
+ return "A firmware update is required";
+ case BLADERF_ERR_TIME_PAST:
+ return "Requested timestamp is in the past";
+ case BLADERF_ERR_QUEUE_FULL:
+ return "Could not enqueue data into full queue";
+ case BLADERF_ERR_FPGA_OP:
+ return "An FPGA operation reported a failure";
+ case BLADERF_ERR_PERMISSION:
+ return "Insufficient permissions for the requested operation";
+ case BLADERF_ERR_WOULD_BLOCK:
+ return "The operation would block, but has been requested to be "
+ "non-blocking";
+ case BLADERF_ERR_NOT_INIT:
+ return "Insufficient initialization for the requested operation";
+ case 0:
+ return "Success";
+ default:
+ return "Unknown error code";
+ }
+}
+
+const char *bladerf_backend_str(bladerf_backend backend)
+{
+ return backend2str(backend);
+}
+
+void bladerf_log_set_verbosity(bladerf_log_level level)
+{
+ log_set_verbosity(level);
+#if defined(LOG_SYSLOG_ENABLED)
+ log_debug("Log verbosity has been set to: %d", level);
+#endif
+}
+
+void bladerf_set_usb_reset_on_open(bool enabled)
+{
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+ bladerf_usb_reset_device_on_open = enabled;
+
+ log_verbose("USB reset on open %s\n", enabled ? "enabled" : "disabled");
+#else
+ log_verbose("%s has no effect. "
+ "ENABLE_USB_DEV_RESET_ON_OPEN not set at compile-time.\n",
+ __FUNCTION__);
+#endif
+}
+
+/******************************************************************************/
+/* Expansion board APIs */
+/******************************************************************************/
+
+int bladerf_expansion_attach(struct bladerf *dev, bladerf_xb xb)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->expansion_attach(dev, xb);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_get_attached(struct bladerf *dev, bladerf_xb *xb)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->expansion_get_attached(dev, xb);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/* XB100 */
+
+int bladerf_expansion_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_read(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_write(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_masked_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_masked_write(dev, mask, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_dir_read(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_dir_write(struct bladerf *dev, uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_dir_write(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_dir_masked_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_dir_masked_write(dev, mask, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/* XB200 */
+
+int bladerf_xb200_set_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter filter)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb200_set_filterbank(dev, ch, filter);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb200_get_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter *filter)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb200_get_filterbank(dev, ch, filter);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb200_set_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path path)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb200_set_path(dev, ch, path);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb200_get_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path *path)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb200_get_path(dev, ch, path);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/* XB300 */
+
+int bladerf_xb300_set_trx(struct bladerf *dev, bladerf_xb300_trx trx)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_set_trx(dev, trx);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb300_get_trx(struct bladerf *dev, bladerf_xb300_trx *trx)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_get_trx(dev, trx);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb300_set_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool enable)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_set_amplifier_enable(dev, amp, enable);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb300_get_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool *enable)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_get_amplifier_enable(dev, amp, enable);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb300_get_output_power(struct bladerf *dev, float *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_get_output_power(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Features */
+/******************************************************************************/
+
+int bladerf_enable_feature(struct bladerf *dev, bladerf_feature feature, bool enable)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = 0;
+
+ if(feature == BLADERF_FEATURE_DEFAULT) {
+ dev->feature = 0;
+ } else {
+ if(feature == BLADERF_FEATURE_OVERSAMPLE) {
+ if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) {
+ log_error("BladeRF2 required for OVERSAMPLE feature\n");
+ status = BLADERF_ERR_UNSUPPORTED;
+ }
+ } else {
+ /* Unknown / Unsupported feature */
+ status = BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (status == 0) {
+ if (enable) {
+ dev->feature |= feature;
+ } else {
+ dev->feature &= ~feature;
+ }
+ }
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_feature(struct bladerf *dev, bladerf_feature* feature)
+{
+ MUTEX_LOCK(&dev->lock);
+ *feature = dev->feature;
+ MUTEX_UNLOCK(&dev->lock);
+
+ return 0;
+}
+
+int bladerf_set_oversample_register_config(struct bladerf *dev) {
+ const char *board_name;
+ board_name = bladerf_get_board_name(dev);
+
+ if (strcmp(board_name, "bladerf2") != 0) {
+ log_error("Oversample register config only applicable\n"
+ "for the bladeRF 2.0");
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ bladerf_set_rfic_register(dev,0x003,0x54); // OC Register
+
+ /* TX Register Assignments */
+ bladerf_set_rfic_register(dev,0x02,0xc0); // TX Enable and Filter Control
+ bladerf_set_rfic_register(dev,0xc2,0x9f); // TX BBF R1
+ bladerf_set_rfic_register(dev,0xc3,0x9f); // TX baseband filter R2
+ bladerf_set_rfic_register(dev,0xc4,0x9f); // TX baseband filter R3
+ bladerf_set_rfic_register(dev,0xc5,0x9f); // TX baseband filter R4
+ bladerf_set_rfic_register(dev,0xc6,0x9f); // TX baseband filter real pole word
+ bladerf_set_rfic_register(dev,0xc7,0x00); // TX baseband filter C1
+ bladerf_set_rfic_register(dev,0xc8,0x00); // TX baseband filter C2
+ bladerf_set_rfic_register(dev,0xc9,0x00); // TX baseband filter real pole word
+
+ /* RX Register Assignments */
+ // Gain and calibration
+ bladerf_set_rfic_register(dev,0x1e0,0x00); // RX1 BBF R1A
+ bladerf_set_rfic_register(dev,0x1e1,0x00); // RX2 BBF R1A
+ bladerf_set_rfic_register(dev,0x1e2,0x00); // RX1 tune control
+ bladerf_set_rfic_register(dev,0x1e3,0x00); // RX2 tune control
+ bladerf_set_rfic_register(dev,0x1e4,0x00); // RX1 BBF R5
+ bladerf_set_rfic_register(dev,0x1e5,0x00); // RX2 BBF R5
+ bladerf_set_rfic_register(dev,0x1e6,0x00); // RX BBF R2346
+
+ // Miller and BBF caps
+ bladerf_set_rfic_register(dev,0x1e7,0x00); // RX BBF C1 MSB
+ bladerf_set_rfic_register(dev,0x1e8,0x00); // RX BBF C1 LSB
+ bladerf_set_rfic_register(dev,0x1e9,0x00); // RX baseband filter real pole word
+ bladerf_set_rfic_register(dev,0x1ea,0x00);
+ bladerf_set_rfic_register(dev,0x1eb,0x00);
+ bladerf_set_rfic_register(dev,0x1ec,0x00);
+ bladerf_set_rfic_register(dev,0x1ed,0x00);
+ bladerf_set_rfic_register(dev,0x1ee,0x00);
+ bladerf_set_rfic_register(dev,0x1ef,0x00);
+
+ // BIST and Data Port Test Config [D1:D0] "Must be 2’b00"
+ bladerf_set_rfic_register(dev,0x3f6,0x03);
+
+ return 0;
+}
+
+/******************************************************************************/
+/* Calibration */
+/******************************************************************************/
+
+int bladerf_load_gain_calibration(struct bladerf *dev, bladerf_channel ch, const char* cal_file_loc)
+{
+ int status = 0;
+ const char *board_name;
+ char *full_path = NULL;
+ char *full_path_bin = NULL;
+ char *ext;
+
+ size_t filename_len = PATH_MAX;
+ char *filename = (char *)calloc(1, filename_len + 1);
+ CHECK_NULL(filename);
+
+ bladerf_gain_mode gain_mode_before_gain_reset;
+
+ log_debug("Loading gain calibration\n");
+ MUTEX_LOCK(&dev->lock);
+
+ board_name = bladerf_get_board_name(dev);
+ if (strcmp(board_name, "bladerf2") != 0) {
+ log_error("Gain calibration unsupported on this device: %s\n", board_name);
+ status = BLADERF_ERR_UNSUPPORTED;
+ goto error;
+ }
+
+ if (cal_file_loc != NULL) {
+ strcpy(filename, cal_file_loc);
+ } else {
+ log_debug("No calibration file specified, using serial number\n");
+ strcpy(filename, dev->ident.serial);
+ filename_len -= strlen(filename);
+
+ if (BLADERF_CHANNEL_IS_TX(ch))
+ strncat(filename, "_tx_gain_cal.tbl", filename_len);
+ else
+ strncat(filename, "_rx_gain_cal.tbl", filename_len);
+ }
+
+ full_path = file_find(filename);
+ if (full_path == NULL) {
+ log_error("Failed to find gain calibration file: %s\n", filename);
+ status = BLADERF_ERR_NO_FILE;
+ goto error;
+ }
+
+ /** Convert to binary format if CSV */
+ full_path_bin = (char*)malloc(strlen(full_path) + 1);
+ strcpy(full_path_bin, full_path);
+ ext = strstr(full_path_bin, ".csv");
+ if (ext) {
+ log_debug("Converting gain calibration to binary format\n");
+ strcpy(ext, ".tbl");
+ status = gain_cal_csv_to_bin(dev, full_path, full_path_bin, ch);
+ if (status != 0) {
+ log_error("Failed to convert csv to binary: %s -> %s\n",
+ full_path, full_path_bin);
+ status = EXIT_FAILURE;
+ goto error;
+ }
+ }
+
+ status = load_gain_calibration(dev, ch, full_path_bin);
+ if (status != 0) {
+ log_error("Failed to load calibration\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ goto error;
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ /* Save current gain mode before gain reset */
+ if (BLADERF_CHANNEL_IS_TX(ch) == false)
+ dev->board->get_gain_mode(dev, ch, &gain_mode_before_gain_reset);
+
+ /* Reset gain to ensure calibration adjustment is applied after loading */
+ status = bladerf_set_gain(dev, ch, dev->gain_tbls[ch].gain_target);
+ if (status != 0) {
+ log_error("%s: Failed to reset gain.\n", __FUNCTION__);
+ goto error;
+ }
+
+ /** Restore previous gain mode */
+ if (BLADERF_CHANNEL_IS_TX(ch) == false) {
+ status = bladerf_set_gain_mode(dev, ch, gain_mode_before_gain_reset);
+ if (status != 0) {
+ log_error("%s: Failed to reset gain mode.\n", __FUNCTION__);
+ goto error;
+ }
+ }
+
+error:
+ if (full_path)
+ free(full_path);
+ if (full_path_bin)
+ free(full_path_bin);
+ if (filename)
+ free(filename);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_enable_gain_calibration(struct bladerf *dev, bladerf_channel ch, bool en)
+{
+ CHECK_NULL(dev);
+ int status = 0;
+
+ if (dev->gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) {
+ log_warning("%s: Gain calibration not loaded\n", __FUNCTION__);
+ return 0;
+ }
+
+ dev->gain_tbls[ch].enabled = en;
+ status = bladerf_set_gain(dev, ch, dev->gain_tbls[ch].gain_target);
+ if (status != 0) {
+ log_error("%s: Failed to reset gain.\n", __FUNCTION__);
+ return status;
+ }
+
+ return status;
+}
+
+int bladerf_print_gain_calibration(struct bladerf *dev, bladerf_channel ch, bool with_entries)
+{
+ CHECK_NULL(dev);
+ int status = 0;
+ const char *board_name;
+ struct bladerf_gain_cal_tbl *gain_tbls = dev->gain_tbls;
+
+ board_name = bladerf_get_board_name(dev);
+ if (strcmp(board_name, "bladerf2") != 0) {
+ log_error("Gain calibration unsupported on this device: %s\n", board_name);
+ status = BLADERF_ERR_UNSUPPORTED;
+ goto error;
+ }
+
+ if (gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) {
+ printf("Gain Calibration [%s]: uninitialized\n", channel2str(ch));
+ return 0;
+ }
+
+ printf("Gain Calibration [%s]: loaded\n", channel2str(ch));
+ printf(" Status: %s\n", (gain_tbls[ch].enabled) ? "enabled" : "disabled");
+ printf(" Version: %i.%i.%i\n",
+ gain_tbls[ch].version.major, gain_tbls[ch].version.minor, gain_tbls[ch].version.patch);
+ printf(" Number of Entries: %u\n", gain_tbls[ch].n_entries);
+ printf(" Start Frequency: %" PRIu64 " Hz\n", gain_tbls[ch].start_freq);
+ printf(" Stop Frequency: %" PRIu64 " Hz\n", gain_tbls[ch].stop_freq);
+ printf(" File Path: %s\n", gain_tbls[ch].file_path);
+
+ if (with_entries) {
+ for (size_t i = 0; i < gain_tbls[ch].n_entries; i++) {
+ printf("%" PRIu64 ",%f\n", gain_tbls[ch].entries[i].freq, gain_tbls[ch].entries[i].gain_corr);
+ }
+ }
+
+error:
+ return status;
+}
+
+int bladerf_get_gain_calibration(struct bladerf *dev, bladerf_channel ch, const struct bladerf_gain_cal_tbl **tbl)
+{
+ CHECK_NULL(dev);
+ MUTEX_LOCK(&dev->lock);
+
+ if (dev->gain_tbls[ch].state != BLADERF_GAIN_CAL_LOADED) {
+ log_error("%s: Gain calibration not loaded\n", __FUNCTION__);
+ MUTEX_UNLOCK(&dev->lock);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ *tbl = &(dev->gain_tbls[ch]);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return 0;
+}
+
+int bladerf_get_gain_target(struct bladerf *dev, bladerf_channel ch, int *gain_target)
+{
+ int status = 0;
+ CHECK_NULL(dev);
+ MUTEX_LOCK(&dev->lock);
+ bladerf_frequency current_frequency;
+ struct bladerf_gain_cal_tbl *cal_table = &dev->gain_tbls[ch];
+ struct bladerf_gain_cal_entry current_entry;
+ bladerf_gain current_gain;
+ bladerf_gain_mode gain_mode;
+
+
+ if (dev->gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) {
+ log_error("Gain calibration not loaded\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ goto error;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch) == true) {
+ *gain_target = cal_table->gain_target;
+ goto error;
+ }
+
+ dev->board->get_gain_mode(dev, ch, &gain_mode);
+
+ if (gain_mode == BLADERF_GAIN_MGC) {
+ *gain_target = cal_table->gain_target;
+ goto error;
+ }
+
+ CHECK_STATUS(dev->board->get_gain(dev, ch, &current_gain));
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &current_frequency));
+ CHECK_STATUS(get_gain_cal_entry(cal_table, current_frequency, &current_entry));
+ *gain_target = current_gain + current_entry.gain_corr;
+
+error:
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/bladerf1.c b/Radio/HW/BladeRF/src/board/bladerf1/bladerf1.c
new file mode 100644
index 0000000..85f4cce
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/bladerf1.c
@@ -0,0 +1,4166 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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 <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libbladeRF.h"
+
+#include "log.h"
+#include "conversions.h"
+#include "bladeRF.h"
+
+#include "board/board.h"
+
+#include "compatibility.h"
+#include "capabilities.h"
+#include "calibration.h"
+#include "flash.h"
+
+#include "driver/smb_clock.h"
+#include "driver/si5338.h"
+#include "driver/dac161s055.h"
+#include "driver/spi_flash.h"
+#include "driver/fpga_trigger.h"
+#include "lms.h"
+#include "nios_pkt_retune.h"
+#include "band_select.h"
+
+#include "backend/usb/usb.h"
+#include "backend/backend_config.h"
+
+#include "expansion/xb100.h"
+#include "expansion/xb200.h"
+#include "expansion/xb300.h"
+
+#include "streaming/async.h"
+#include "streaming/sync.h"
+
+#include "devinfo.h"
+#include "helpers/version.h"
+#include "helpers/file.h"
+#include "version.h"
+
+/******************************************************************************
+ * bladeRF1 board state *
+ ******************************************************************************/
+
+/* 1 TX, 1 RX */
+#define NUM_MODULES 2
+
+struct bladerf1_board_data {
+ /* Board state */
+ enum {
+ STATE_UNINITIALIZED,
+ STATE_FIRMWARE_LOADED,
+ STATE_FPGA_LOADED,
+ STATE_INITIALIZED,
+ } state;
+
+ /* Bitmask of capabilities determined by version numbers */
+ uint64_t capabilities;
+
+ /* Format currently being used with a module, or -1 if module is not used */
+ bladerf_format module_format[NUM_MODULES];
+
+ /* Which mode of operation we use for tuning */
+ bladerf_tuning_mode tuning_mode;
+
+ /* Calibration data */
+ struct calibrations {
+ struct dc_cal_tbl *dc_rx;
+ struct dc_cal_tbl *dc_tx;
+ } cal;
+ uint16_t dac_trim;
+
+ /* Board properties */
+ bladerf_fpga_size fpga_size;
+ /* Data message size */
+ size_t msg_size;
+
+ /* Version information */
+ struct bladerf_version fpga_version;
+ struct bladerf_version fw_version;
+ char fpga_version_str[BLADERF_VERSION_STR_MAX+1];
+ char fw_version_str[BLADERF_VERSION_STR_MAX+1];
+
+ /* Synchronous interface handles */
+ struct bladerf_sync sync[NUM_MODULES];
+};
+
+#define _CHECK_BOARD_STATE(_state, _locked) \
+ do { \
+ struct bladerf1_board_data *board_data = dev->board_data; \
+ if (board_data->state < _state) { \
+ log_error("Board state insufficient for operation " \
+ "(current \"%s\", requires \"%s\").\n", \
+ bladerf1_state_to_string[board_data->state], \
+ bladerf1_state_to_string[_state]); \
+ if (_locked) { \
+ MUTEX_UNLOCK(&dev->lock); \
+ } \
+ return BLADERF_ERR_NOT_INIT; \
+ } \
+ } while(0)
+
+#define CHECK_BOARD_STATE(_state) _CHECK_BOARD_STATE(_state, false)
+#define CHECK_BOARD_STATE_LOCKED(_state) _CHECK_BOARD_STATE(_state, true)
+
+#define __round_int(x) (x >= 0 ? (int)(x + 0.5) : (int)(x - 0.5))
+#define __round_int64(x) (x >= 0 ? (int64_t)(x + 0.5) : (int64_t)(x - 0.5))
+
+#define __scale(r, v) ((float)(v) / (r)->scale)
+#define __scale_int(r, v) (__round_int(__scale(r, v)))
+#define __scale_int64(r, v) (__round_int64(__scale(r, v)))
+
+#define __unscale(r, v) ((float)(v) * (r)->scale)
+#define __unscale_int(r, v) (__round_int(__unscale(r, v)))
+#define __unscale_int64(r, v) (__round_int64(__unscale(r, v)))
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+
+/* Board state to string map */
+
+static const char *bladerf1_state_to_string[] = {
+ [STATE_UNINITIALIZED] = "Uninitialized",
+ [STATE_FIRMWARE_LOADED] = "Firmware Loaded",
+ [STATE_FPGA_LOADED] = "FPGA Loaded",
+ [STATE_INITIALIZED] = "Initialized",
+};
+
+/* RX gain offset */
+#define BLADERF1_RX_GAIN_OFFSET -6.0f
+
+/* Overall RX gain range */
+static const struct bladerf_range bladerf1_rx_gain_range = {
+ FIELD_INIT(.min, __round_int64(BLADERF_RXVGA1_GAIN_MIN + BLADERF_RXVGA2_GAIN_MIN + BLADERF1_RX_GAIN_OFFSET)),
+ FIELD_INIT(.max, __round_int64(BLADERF_LNA_GAIN_MAX_DB + BLADERF_RXVGA1_GAIN_MAX + BLADERF_RXVGA2_GAIN_MAX + BLADERF1_RX_GAIN_OFFSET)),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* TX gain offset: 60 dB system gain ~= 0 dBm output */
+#define BLADERF1_TX_GAIN_OFFSET 52.0f
+
+/* Overall TX gain range */
+static const struct bladerf_range bladerf1_tx_gain_range = {
+ FIELD_INIT(.min, __round_int64(BLADERF_TXVGA1_GAIN_MIN + BLADERF_TXVGA2_GAIN_MIN + BLADERF1_TX_GAIN_OFFSET)),
+ FIELD_INIT(.max, __round_int64(BLADERF_TXVGA1_GAIN_MAX + BLADERF_TXVGA2_GAIN_MAX + BLADERF1_TX_GAIN_OFFSET)),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* RX gain modes */
+
+static const struct bladerf_gain_modes bladerf1_rx_gain_modes[] = {
+ {
+ FIELD_INIT(.name, "automatic"),
+ FIELD_INIT(.mode, BLADERF_GAIN_DEFAULT)
+ },
+ {
+ FIELD_INIT(.name, "manual"),
+ FIELD_INIT(.mode, BLADERF_GAIN_MGC)
+ },
+};
+
+struct bladerf_gain_stage_info {
+ const char *name;
+ struct bladerf_range range;
+};
+
+/* RX gain stages */
+
+static const struct bladerf_gain_stage_info bladerf1_rx_gain_stages[] = {
+ {
+ FIELD_INIT(.name, "lna"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, BLADERF_LNA_GAIN_MAX_DB),
+ FIELD_INIT(.step, 3),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+ {
+ FIELD_INIT(.name, "rxvga1"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, BLADERF_RXVGA1_GAIN_MIN),
+ FIELD_INIT(.max, BLADERF_RXVGA1_GAIN_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+ {
+ FIELD_INIT(.name, "rxvga2"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, BLADERF_RXVGA2_GAIN_MIN),
+ FIELD_INIT(.max, BLADERF_RXVGA2_GAIN_MAX),
+ FIELD_INIT(.step, 3),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+};
+
+/* TX gain stages */
+
+static const struct bladerf_gain_stage_info bladerf1_tx_gain_stages[] = {
+ {
+ FIELD_INIT(.name, "txvga1"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, BLADERF_TXVGA1_GAIN_MIN),
+ FIELD_INIT(.max, BLADERF_TXVGA1_GAIN_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+ {
+ FIELD_INIT(.name, "txvga2"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, BLADERF_TXVGA2_GAIN_MIN),
+ FIELD_INIT(.max, BLADERF_TXVGA2_GAIN_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+};
+
+/* Sample Rate Range */
+
+static const struct bladerf_range bladerf1_sample_rate_range = {
+ FIELD_INIT(.min, BLADERF_SAMPLERATE_MIN),
+ FIELD_INIT(.max, BLADERF_SAMPLERATE_REC_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Bandwidth Range */
+
+static const struct bladerf_range bladerf1_bandwidth_range = {
+ FIELD_INIT(.min, BLADERF_BANDWIDTH_MIN),
+ FIELD_INIT(.max, BLADERF_BANDWIDTH_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Frequency Range */
+
+static const struct bladerf_range bladerf1_frequency_range = {
+ FIELD_INIT(.min, BLADERF_FREQUENCY_MIN),
+ FIELD_INIT(.max, BLADERF_FREQUENCY_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+static const struct bladerf_range bladerf1_xb200_frequency_range = {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, BLADERF_FREQUENCY_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Loopback modes */
+
+static const struct bladerf_loopback_modes bladerf1_loopback_modes[] = {
+ {
+ FIELD_INIT(.name, "none"),
+ FIELD_INIT(.mode, BLADERF_LB_NONE)
+ },
+ {
+ FIELD_INIT(.name, "firmware"),
+ FIELD_INIT(.mode, BLADERF_LB_FIRMWARE)
+ },
+ {
+ FIELD_INIT(.name, "bb_txlpf_rxvga2"),
+ FIELD_INIT(.mode, BLADERF_LB_BB_TXLPF_RXVGA2)
+ },
+ {
+ FIELD_INIT(.name, "bb_txlpf_rxlpf"),
+ FIELD_INIT(.mode, BLADERF_LB_BB_TXLPF_RXLPF)
+ },
+ {
+ FIELD_INIT(.name, "bb_txvga1_rxvga2"),
+ FIELD_INIT(.mode, BLADERF_LB_BB_TXVGA1_RXVGA2)
+ },
+ {
+ FIELD_INIT(.name, "bb_txvga1_rxlpf"),
+ FIELD_INIT(.mode, BLADERF_LB_BB_TXVGA1_RXLPF)
+ },
+ {
+ FIELD_INIT(.name, "rf_lna1"),
+ FIELD_INIT(.mode, BLADERF_LB_RF_LNA1)
+ },
+ {
+ FIELD_INIT(.name, "rf_lna2"),
+ FIELD_INIT(.mode, BLADERF_LB_RF_LNA2)
+ },
+ {
+ FIELD_INIT(.name, "rf_lna3"),
+ FIELD_INIT(.mode, BLADERF_LB_RF_LNA3)
+ },
+};
+
+/* RF ports */
+
+struct bladerf_lms_port_name_map {
+ const char *name;
+ union {
+ lms_lna rx_lna;
+ lms_pa tx_pa;
+ };
+};
+
+static const struct bladerf_lms_port_name_map bladerf1_rx_port_map[] = {
+ {
+ FIELD_INIT(.name, "none"),
+ FIELD_INIT(.rx_lna, LNA_NONE),
+ },
+ {
+ FIELD_INIT(.name, "lna1"),
+ FIELD_INIT(.rx_lna, LNA_1),
+ },
+ {
+ FIELD_INIT(.name, "lna2"),
+ FIELD_INIT(.rx_lna, LNA_2),
+ },
+ {
+ FIELD_INIT(.name, "lna3"),
+ FIELD_INIT(.rx_lna, LNA_3),
+ },
+};
+
+static const struct bladerf_lms_port_name_map bladerf1_tx_port_map[] = {
+ {
+ FIELD_INIT(.name, "aux"),
+ FIELD_INIT(.tx_pa, PA_AUX),
+ },
+ {
+ FIELD_INIT(.name, "pa1"),
+ FIELD_INIT(.tx_pa, PA_1),
+ },
+ {
+ FIELD_INIT(.name, "pa2"),
+ FIELD_INIT(.tx_pa, PA_2),
+ },
+ {
+ FIELD_INIT(.name, "none"),
+ FIELD_INIT(.tx_pa, PA_NONE),
+ },
+};
+
+/******************************************************************************/
+/* Low-level Initialization */
+/******************************************************************************/
+
+static bladerf_tuning_mode tuning_get_default_mode(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ bladerf_tuning_mode mode;
+ const char *env_var;
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) {
+ mode = BLADERF_TUNING_MODE_FPGA;
+ } else {
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ env_var = getenv("BLADERF_DEFAULT_TUNING_MODE");
+
+ if (env_var != NULL) {
+ if (!strcasecmp("host", env_var)) {
+ mode = BLADERF_TUNING_MODE_HOST;
+ } else if (!strcasecmp("fpga", env_var)) {
+ mode = BLADERF_TUNING_MODE_FPGA;
+
+ /* Just a friendly reminder... */
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) {
+ log_warning("The loaded FPGA version (%u.%u.%u) does not "
+ "support the tuning mode being used to override "
+ "the default.\n",
+ board_data->fpga_version.major, board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+ }
+ } else {
+ log_debug("Invalid tuning mode override: %s\n", env_var);
+ }
+ }
+
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ log_debug("Default tuning mode: host\n");
+ break;
+ case BLADERF_TUNING_MODE_FPGA:
+ log_debug("Default tuning mode: FPGA\n");
+ break;
+ default:
+ assert(!"Bug encountered.");
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ return mode;
+}
+
+static int bladerf1_apply_lms_dc_cals(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ int status = 0;
+ struct bladerf_lms_dc_cals cals;
+ const bool have_rx = (board_data->cal.dc_rx != NULL);
+ const bool have_tx = (board_data->cal.dc_tx != NULL);
+
+ cals.lpf_tuning = -1;
+ cals.tx_lpf_i = -1;
+ cals.tx_lpf_q = -1;
+ cals.rx_lpf_i = -1;
+ cals.rx_lpf_q = -1;
+ cals.dc_ref = -1;
+ cals.rxvga2a_i = -1;
+ cals.rxvga2a_q = -1;
+ cals.rxvga2b_i = -1;
+ cals.rxvga2b_q = -1;
+
+ if (have_rx) {
+ const struct bladerf_lms_dc_cals *reg_vals =
+ &board_data->cal.dc_rx->reg_vals;
+
+ cals.lpf_tuning = reg_vals->lpf_tuning;
+ cals.rx_lpf_i = reg_vals->rx_lpf_i;
+ cals.rx_lpf_q = reg_vals->rx_lpf_q;
+ cals.dc_ref = reg_vals->dc_ref;
+ cals.rxvga2a_i = reg_vals->rxvga2a_i;
+ cals.rxvga2a_q = reg_vals->rxvga2a_q;
+ cals.rxvga2b_i = reg_vals->rxvga2b_i;
+ cals.rxvga2b_q = reg_vals->rxvga2b_q;
+
+ log_verbose("Fetched register values from RX DC cal table.\n");
+ }
+
+ if (have_tx) {
+ const struct bladerf_lms_dc_cals *reg_vals =
+ &board_data->cal.dc_tx->reg_vals;
+
+ cals.tx_lpf_i = reg_vals->tx_lpf_i;
+ cals.tx_lpf_q = reg_vals->tx_lpf_q;
+
+ if (have_rx) {
+ if (cals.lpf_tuning != reg_vals->lpf_tuning) {
+ log_warning("LPF tuning mismatch in tables. "
+ "RX=0x%04x, TX=0x%04x",
+ cals.lpf_tuning, reg_vals->lpf_tuning);
+ }
+ } else {
+ /* Have TX cal but no RX cal -- use the RX values that came along
+ * for the ride when the TX table was generated */
+ cals.rx_lpf_i = reg_vals->rx_lpf_i;
+ cals.rx_lpf_q = reg_vals->rx_lpf_q;
+ cals.dc_ref = reg_vals->dc_ref;
+ cals.rxvga2a_i = reg_vals->rxvga2a_i;
+ cals.rxvga2a_q = reg_vals->rxvga2a_q;
+ cals.rxvga2b_i = reg_vals->rxvga2b_i;
+ cals.rxvga2b_q = reg_vals->rxvga2b_q;
+ }
+
+ log_verbose("Fetched register values from TX DC cal table.\n");
+ }
+
+ /* No TX table was loaded, so load LMS TX register cals from the RX table,
+ * if available */
+ if (have_rx && !have_tx) {
+ const struct bladerf_lms_dc_cals *reg_vals =
+ &board_data->cal.dc_rx->reg_vals;
+
+ cals.tx_lpf_i = reg_vals->tx_lpf_i;
+ cals.tx_lpf_q = reg_vals->tx_lpf_q;
+ }
+
+ if (have_rx || have_tx) {
+ status = lms_set_dc_cals(dev, &cals);
+
+ /* Force a re-tune so that we can apply the appropriate I/Q DC offset
+ * values from our calibration table */
+ if (status == 0) {
+ int rx_status = 0;
+ int tx_status = 0;
+
+ if (have_rx) {
+ bladerf_frequency rx_f;
+ rx_status = dev->board->get_frequency(
+ dev, BLADERF_CHANNEL_RX(0), &rx_f);
+ if (rx_status == 0) {
+ rx_status = dev->board->set_frequency(
+ dev, BLADERF_CHANNEL_RX(0), rx_f);
+ }
+ }
+
+ if (have_tx) {
+ bladerf_frequency rx_f;
+ rx_status = dev->board->get_frequency(
+ dev, BLADERF_CHANNEL_RX(0), &rx_f);
+ if (rx_status == 0) {
+ rx_status = dev->board->set_frequency(
+ dev, BLADERF_CHANNEL_RX(0), rx_f);
+ }
+ }
+
+ /* Report the first of any failures */
+ status = (rx_status == 0) ? tx_status : rx_status;
+ if (status != 0) {
+ return status;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Initialize device registers - required after power-up, but safe
+ * to call multiple times after power-up (e.g., multiple close and reopens)
+ */
+static int bladerf1_initialize(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ struct bladerf_version required_fw_version;
+ struct bladerf_version required_fpga_version;
+ int status;
+ uint32_t val;
+
+ /* Read FPGA version */
+ status = dev->backend->get_fpga_version(dev, &board_data->fpga_version);
+ if (status < 0) {
+ log_debug("Failed to get FPGA version: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Read FPGA version: %s\n", board_data->fpga_version.describe);
+
+ /* Determine FPGA capabilities */
+ board_data->capabilities |= bladerf1_get_fpga_capabilities(&board_data->fpga_version);
+ log_verbose("Capability mask after FPGA load: 0x%016"PRIx64"\n",
+ board_data->capabilities);
+
+ if (getenv("BLADERF_FORCE_LEGACY_NIOS_PKT")) {
+ board_data->capabilities &= ~BLADERF_CAP_PKT_HANDLER_FMT;
+ log_verbose("Using legacy packet handler format due to env var\n");
+ }
+
+ /* If the FPGA version check fails, just warn, but don't error out.
+ *
+ * If an error code caused this function to bail out, it would prevent a
+ * user from being able to unload and reflash a bitstream being
+ * "autoloaded" from SPI flash. */
+ status = version_check(&bladerf1_fw_compat_table, &bladerf1_fpga_compat_table,
+ &board_data->fw_version, &board_data->fpga_version,
+ &required_fw_version, &required_fpga_version);
+ if (status < 0) {
+#if LOGGING_ENABLED
+ if (status == BLADERF_ERR_UPDATE_FPGA) {
+ log_warning("FPGA v%u.%u.%u was detected. Firmware v%u.%u.%u "
+ "requires FPGA v%u.%u.%u or later. Please load a "
+ "different FPGA version before continuing.\n\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch,
+ board_data->fw_version.major,
+ board_data->fw_version.minor,
+ board_data->fw_version.patch,
+ required_fpga_version.major,
+ required_fpga_version.minor,
+ required_fpga_version.patch);
+ } else if (status == BLADERF_ERR_UPDATE_FW) {
+ log_warning("FPGA v%u.%u.%u was detected, which requires firmware "
+ "v%u.%u.%u or later. The device firmware is currently "
+ "v%u.%u.%u. Please upgrade the device firmware before "
+ "continuing.\n\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch,
+ required_fw_version.major,
+ required_fw_version.minor,
+ required_fw_version.patch,
+ board_data->fw_version.major,
+ board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+#endif
+ }
+
+ /* Detect AGC FPGA bug and report warning */
+ if( have_cap(board_data->capabilities, BLADERF_CAP_AGC_DC_LUT) &&
+ version_fields_less_than(&board_data->fpga_version, 0, 8, 0) ) {
+ log_warning("AGC commands for FPGA v%u.%u.%u are incompatible with "
+ "this version of libbladeRF. Please update to FPGA "
+ "v%u.%u.%u or newer to use AGC.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch,
+ 0, 8, 0 );
+ }
+
+ /* Set FPGA packet protocol */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_PKT_HANDLER_FMT)) {
+ status = dev->backend->set_fpga_protocol(dev, BACKEND_FPGA_PROTOCOL_NIOSII);
+ } else {
+ status = dev->backend->set_fpga_protocol(dev, BACKEND_FPGA_PROTOCOL_NIOSII_LEGACY);
+ }
+ if (status < 0) {
+ log_error("Unable to set backend FPGA protocol: %d\n", status);
+ return status;
+ }
+
+ /* Readback the GPIO values to see if they are default or already set */
+ status = dev->backend->config_gpio_read(dev, &val);
+ if (status != 0) {
+ log_debug("Failed to read GPIO config %s\n", bladerf_strerror(status));
+ return status;
+ }
+
+ if ((val & 0x7f) == 0) {
+ log_verbose( "Default GPIO value found - initializing device\n" );
+
+ /* Set the GPIO pins to enable the LMS and select the low band */
+ status = dev->backend->config_gpio_write(dev, 0x57);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Disable the front ends */
+ status = lms_enable_rffe(dev, BLADERF_CHANNEL_TX(0), false);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_enable_rffe(dev, BLADERF_CHANNEL_RX(0), false);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set the internal LMS register to enable RX and TX */
+ status = LMS_WRITE(dev, 0x05, 0x3e);
+ if (status != 0) {
+ return status;
+ }
+
+ /* LMS FAQ: Improve TX spurious emission performance */
+ status = LMS_WRITE(dev, 0x47, 0x40);
+ if (status != 0) {
+ return status;
+ }
+
+ /* LMS FAQ: Improve ADC performance */
+ status = LMS_WRITE(dev, 0x59, 0x29);
+ if (status != 0) {
+ return status;
+ }
+
+ /* LMS FAQ: Common mode voltage for ADC */
+ status = LMS_WRITE(dev, 0x64, 0x36);
+ if (status != 0) {
+ return status;
+ }
+
+ /* LMS FAQ: Higher LNA Gain */
+ status = LMS_WRITE(dev, 0x79, 0x37);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Power down DC calibration comparators until they are need, as they
+ * have been shown to introduce undesirable artifacts into our signals.
+ * (This is documented in the LMS6 FAQ). */
+
+ status = lms_set(dev, 0x3f, 0x80); /* TX LPF DC cal comparator */
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set(dev, 0x5f, 0x80); /* RX LPF DC cal comparator */
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set(dev, 0x6e, 0xc0); /* RXVGA2A/B DC cal comparators */
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure charge pump current offsets */
+ status = lms_config_charge_pumps(dev, BLADERF_CHANNEL_TX(0));
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_config_charge_pumps(dev, BLADERF_CHANNEL_RX(0));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set a default samplerate */
+ status = si5338_set_sample_rate(dev, BLADERF_CHANNEL_TX(0), 1000000, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = si5338_set_sample_rate(dev, BLADERF_CHANNEL_RX(0), 1000000, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ board_data->tuning_mode = tuning_get_default_mode(dev);
+
+ status = dev->board->set_frequency(dev, BLADERF_CHANNEL_TX(0), 2447000000U);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->board->set_frequency(dev, BLADERF_CHANNEL_RX(0), 2484000000U);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set the calibrated VCTCXO DAC value */
+ status = dac161s055_write(dev, board_data->dac_trim);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set the default gain mode */
+ status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_DEFAULT);
+ if (status != 0 && status != BLADERF_ERR_UNSUPPORTED) {
+ return status;
+ }
+ } else {
+ board_data->tuning_mode = tuning_get_default_mode(dev);
+ }
+
+ /* Check if we have an expansion board attached */
+ status = dev->board->expansion_get_attached(dev, &dev->xb);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Update device state */
+ board_data->state = STATE_INITIALIZED;
+
+ /* Set up LMS DC offset register calibration and initial IQ settings,
+ * if any tables have been loaded already.
+ *
+ * This is done every time the device is opened (with an FPGA loaded),
+ * as the user may change/update DC calibration tables without reloading the
+ * FPGA.
+ */
+ status = bladerf1_apply_lms_dc_cals(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ * Generic Board Functions *
+ ******************************************************************************/
+
+/******************************************************************************/
+/* Matches */
+/******************************************************************************/
+
+static bool bladerf1_matches(struct bladerf *dev)
+{
+ uint16_t vid, pid;
+ int status;
+
+ status = dev->backend->get_vid_pid(dev, &vid, &pid);
+ if (status < 0) {
+ return false;
+ }
+
+ if (vid == USB_NUAND_VENDOR_ID && pid == USB_NUAND_BLADERF_PRODUCT_ID) {
+ return true;
+ } else if (vid == USB_NUAND_LEGACY_VENDOR_ID && pid == USB_NUAND_BLADERF_LEGACY_PRODUCT_ID) {
+ return true;
+ }
+
+ return false;
+}
+
+/******************************************************************************/
+/* Open/close */
+/******************************************************************************/
+
+static int bladerf1_open(struct bladerf *dev, struct bladerf_devinfo *devinfo)
+{
+ struct bladerf1_board_data *board_data;
+ struct bladerf_version required_fw_version;
+ bladerf_dev_speed usb_speed;
+ char filename[FILENAME_MAX];
+ char *full_path;
+ int status;
+
+ /* Allocate board data */
+ board_data = calloc(1, sizeof(struct bladerf1_board_data));
+ if (board_data == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+ dev->board_data = board_data;
+
+ /* Allocate flash architecture */
+ dev->flash_arch = calloc(1, sizeof(struct bladerf_flash_arch));
+ if (dev->flash_arch == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Initialize board data */
+ board_data->fpga_version.describe = board_data->fpga_version_str;
+ board_data->fw_version.describe = board_data->fw_version_str;
+
+ board_data->module_format[BLADERF_RX] = -1;
+ board_data->module_format[BLADERF_TX] = -1;
+
+ dev->flash_arch->status = STATUS_FLASH_UNINITIALIZED;
+ dev->flash_arch->manufacturer_id = 0x0;
+ dev->flash_arch->device_id = 0x0;
+
+ /* Read firmware version */
+ status = dev->backend->get_fw_version(dev, &board_data->fw_version);
+ if (status < 0) {
+ log_debug("Failed to get FW version: %s\n", bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Read Firmware version: %s\n", board_data->fw_version.describe);
+
+ /* Update device state */
+ board_data->state = STATE_FIRMWARE_LOADED;
+
+ /* Determine firmware capabilities */
+ board_data->capabilities |=
+ bladerf1_get_fw_capabilities(&board_data->fw_version);
+ log_verbose("Capability mask before FPGA load: 0x%016" PRIx64 "\n",
+ board_data->capabilities);
+
+ /* Wait until firmware is ready */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_QUERY_DEVICE_READY)) {
+ const unsigned int max_retries = 30;
+ unsigned int i;
+ int ready;
+
+ for (i = 0; i < max_retries; i++) {
+ ready = dev->backend->is_fw_ready(dev);
+ if (ready != 1) {
+ if (i == 0) {
+ log_info("Waiting for device to become ready...\n");
+ } else {
+ log_debug("Retry %02u/%02u.\n", i + 1, max_retries);
+ }
+ usleep(1000000);
+ } else {
+ break;
+ }
+ }
+
+ if (i >= max_retries) {
+ log_debug("Timed out while waiting for device.\n");
+ return BLADERF_ERR_TIMEOUT;
+ }
+ } else {
+ log_info(
+ "FX3 FW v%u.%u.%u does not support the \"device ready\" query.\n"
+ "\tEnsure flash-autoloading completes before opening a device.\n"
+ "\tUpgrade the FX3 firmware to avoid this message in the future.\n"
+ "\n",
+ board_data->fw_version.major, board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+
+ /* Determine data message size */
+ status = dev->backend->get_device_speed(dev, &usb_speed);
+ if (status < 0) {
+ log_debug("Failed to get device speed: %s\n", bladerf_strerror(status));
+ return status;
+ }
+ switch (usb_speed) {
+ case BLADERF_DEVICE_SPEED_SUPER:
+ board_data->msg_size = USB_MSG_SIZE_SS;
+ break;
+
+ case BLADERF_DEVICE_SPEED_HIGH:
+ board_data->msg_size = USB_MSG_SIZE_HS;
+ break;
+
+ default:
+ log_error("Unsupported device speed: %d\n", usb_speed);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Verify that we have a sufficent firmware version before continuing. */
+ status = version_check_fw(&bladerf1_fw_compat_table,
+ &board_data->fw_version, &required_fw_version);
+ if (status != 0) {
+#ifdef LOGGING_ENABLED
+ if (status == BLADERF_ERR_UPDATE_FW) {
+ log_warning("Firmware v%u.%u.%u was detected. libbladeRF v%s "
+ "requires firmware v%u.%u.%u or later. An upgrade via "
+ "the bootloader is required.\n\n",
+ board_data->fw_version.major,
+ board_data->fw_version.minor,
+ board_data->fw_version.patch, LIBBLADERF_VERSION,
+ required_fw_version.major, required_fw_version.minor,
+ required_fw_version.patch);
+ }
+#endif
+ return status;
+ }
+
+ /* Probe SPI flash architecture information */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FW_FLASH_ID)) {
+ status = spi_flash_read_flash_id(dev, &dev->flash_arch->manufacturer_id,
+ &dev->flash_arch->device_id);
+ if (status < 0) {
+ log_error("Failed to probe SPI flash ID information.\n");
+ }
+ } else {
+ log_debug("FX3 firmware v%u.%u.%u does not support SPI flash ID. A "
+ "firmware update is recommended in order to probe the SPI "
+ "flash ID information.\n",
+ board_data->fw_version.major, board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+
+ /* Decode SPI flash ID information to figure out its architecture.
+ * We need to know a little about the flash architecture before we can
+ * read anything from it, including FPGA size and other cal data.
+ * If the firmware does not have the capability to get the flash ID,
+ * sane defaults will be chosen.
+ *
+ * Not checking return code because it is irrelevant. */
+ spi_flash_decode_flash_architecture(dev, &board_data->fpga_size);
+
+ /* VCTCXO trim and FPGA size are non-fatal indicators that we've
+ * trashed the calibration region of flash. If these were made fatal,
+ * we wouldn't be able to open the device to restore them. */
+
+ status = spi_flash_read_vctcxo_trim(dev, &board_data->dac_trim);
+ if (status < 0) {
+ log_warning("Failed to get VCTCXO trim value: %s\n",
+ bladerf_strerror(status));
+ log_debug("Defaulting DAC trim to 0x8000.\n");
+ board_data->dac_trim = 0x8000;
+ }
+
+ status = spi_flash_read_fpga_size(dev, &board_data->fpga_size);
+ if (status < 0) {
+ log_warning("Failed to get FPGA size %s\n", bladerf_strerror(status));
+ }
+
+ /* If the flash architecture could not be decoded earlier, try again now
+ * that the FPGA size is known. */
+ if (dev->flash_arch->status != STATUS_SUCCESS) {
+ status =
+ spi_flash_decode_flash_architecture(dev, &board_data->fpga_size);
+ if (status < 0) {
+ log_debug("Assumptions were made about the SPI flash architecture! "
+ "Flash commands may not function as expected.\n");
+ }
+ }
+
+ /* Skip further work if BLADERF_FORCE_NO_FPGA_PRESENT is set */
+ if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) {
+ log_debug("Skipping FPGA configuration and initialization - "
+ "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n");
+ return 0;
+ }
+
+ /* Check for possible mismatch between the USB device identification and
+ * the board's own knowledge. We need this to be a non-fatal condition,
+ * so that the problem can be fixed easily. */
+ if (board_data->fpga_size == BLADERF_FPGA_A4 ||
+ board_data->fpga_size == BLADERF_FPGA_A5 ||
+ board_data->fpga_size == BLADERF_FPGA_A9) {
+ uint16_t vid, pid;
+
+ log_critical("Device type mismatch! FPGA size %d is a bladeRF2 "
+ "characteristic, but the USB PID indicates bladeRF1. "
+ "Initialization cannot continue.\n",
+ board_data->fpga_size);
+ log_info("You must download firmware v2.2.0 or later from "
+ "https://www.nuand.com/fx3/ and flash it (bladeRF-cli -f "
+ "/path/to/bladeRF_fw.img) before using this device.\n");
+
+ status = dev->backend->get_vid_pid(dev, &vid, &pid);
+ if (status < 0) {
+ log_error("%s: get_vid_pid returned status %s\n", __FUNCTION__,
+ bladerf_strerror(status));
+ }
+
+ log_debug("vid_pid=%04x:%04x fpga_size=%d fw_version=%u.%u.%u\n", vid,
+ pid, board_data->fpga_size, board_data->fw_version.major,
+ board_data->fw_version.minor, board_data->fw_version.patch);
+
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ }
+
+ /* This will be set in initialize() after we can determine which
+ * methods the FPGA supports (based upon version number). */
+ board_data->tuning_mode = BLADERF_TUNING_MODE_INVALID;
+
+ /* Load any available calibration tables so that the LMS DC register
+ * configurations may be loaded in initialize() */
+ snprintf(filename, sizeof(filename), "%s_dc_rx.tbl", dev->ident.serial);
+ full_path = file_find(filename);
+ if (full_path != NULL) {
+ log_debug("Loading RX calibration image %s\n", full_path);
+ dc_cal_tbl_image_load(dev, &board_data->cal.dc_rx, full_path);
+ }
+ free(full_path);
+ full_path = NULL;
+
+ snprintf(filename, sizeof(filename), "%s_dc_tx.tbl", dev->ident.serial);
+ full_path = file_find(filename);
+ if (full_path != NULL) {
+ log_debug("Loading TX calibration image %s\n", full_path);
+ dc_cal_tbl_image_load(dev, &board_data->cal.dc_tx, full_path);
+ }
+ free(full_path);
+ full_path = NULL;
+
+ status = dev->backend->is_fpga_configured(dev);
+ if (status < 0) {
+ return status;
+ } else if (status == 1) {
+ board_data->state = STATE_FPGA_LOADED;
+ } else if (status != 1 && board_data->fpga_size == BLADERF_FPGA_UNKNOWN) {
+ log_warning("Unknown FPGA size. Skipping FPGA configuration...\n");
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ } else if (status != 1) {
+ /* Try searching for an FPGA in the config search path */
+ if (board_data->fpga_size == BLADERF_FPGA_40KLE) {
+ full_path = file_find("hostedx40.rbf");
+ } else if (board_data->fpga_size == BLADERF_FPGA_115KLE) {
+ full_path = file_find("hostedx115.rbf");
+ } else {
+ log_error("Invalid FPGA size %d.\n", board_data->fpga_size);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (full_path != NULL) {
+ uint8_t *buf;
+ size_t buf_size;
+
+ log_debug("Loading FPGA from: %s\n", full_path);
+
+ status = file_read_buffer(full_path, &buf, &buf_size);
+
+ free(full_path);
+ full_path = NULL;
+
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->backend->load_fpga(dev, buf, buf_size);
+ if (status != 0) {
+ log_warning("Failure loading FPGA: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ board_data->state = STATE_FPGA_LOADED;
+ } else {
+ log_warning("FPGA bitstream file not found.\n");
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ }
+ }
+
+ /* Initialize the device before we try to interact with it. In the case
+ * of an autoloaded FPGA, we need to ensure the clocks are all running
+ * before we can try to cancel any scheduled retunes or else the NIOS
+ * hangs. */
+ status = bladerf1_initialize(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ /* Cancel any pending re-tunes that may have been left over as the
+ * result of a user application crashing or forgetting to call
+ * bladerf_close() */
+ status =
+ dev->board->cancel_scheduled_retunes(dev, BLADERF_CHANNEL_RX(0));
+ if (status != 0) {
+ log_warning("Failed to cancel any pending RX retunes: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ status =
+ dev->board->cancel_scheduled_retunes(dev, BLADERF_CHANNEL_TX(0));
+ if (status != 0) {
+ log_warning("Failed to cancel any pending TX retunes: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+ }
+
+ return 0;
+}
+
+static void bladerf1_close(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ struct bladerf_flash_arch *flash_arch = dev->flash_arch;
+ int status;
+
+ if (board_data) {
+ sync_deinit(&board_data->sync[BLADERF_CHANNEL_RX(0)]);
+ sync_deinit(&board_data->sync[BLADERF_CHANNEL_TX(0)]);
+
+ status = dev->backend->is_fpga_configured(dev);
+ if (status == 1 && have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ /* We cancel schedule retunes here to avoid the device retuning
+ * underneath the user, should they open it again in the future.
+ *
+ * This is intended to help developers avoid a situation during
+ * debugging where they schedule "far" into the future, but then
+ * hit a case where their program abort or exit early. If we didn't
+ * cancel these scheduled retunes, they could potentially be left
+ * wondering why the device is starting up or "unexpectedly"
+ * switching to a different frequency later.
+ */
+ dev->board->cancel_scheduled_retunes(dev, BLADERF_CHANNEL_RX(0));
+ dev->board->cancel_scheduled_retunes(dev, BLADERF_CHANNEL_TX(0));
+ }
+
+ /* Detach expansion board */
+ switch (dev->xb) {
+ case BLADERF_XB_100:
+ xb100_detach(dev);
+ break;
+ case BLADERF_XB_200:
+ xb200_detach(dev);
+ break;
+ case BLADERF_XB_300:
+ xb300_detach(dev);
+ break;
+ default:
+ break;
+ }
+
+ dc_cal_tbl_free(&board_data->cal.dc_rx);
+ dc_cal_tbl_free(&board_data->cal.dc_tx);
+
+ free(board_data);
+ board_data = NULL;
+ }
+
+ if( flash_arch != NULL ) {
+ free(flash_arch);
+ flash_arch = NULL;
+ }
+}
+
+/******************************************************************************/
+/* Properties */
+/******************************************************************************/
+
+static bladerf_dev_speed bladerf1_device_speed(struct bladerf *dev)
+{
+ int status;
+ bladerf_dev_speed usb_speed;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ status = dev->backend->get_device_speed(dev, &usb_speed);
+ if (status < 0) {
+ return BLADERF_DEVICE_SPEED_UNKNOWN;
+ }
+
+ return usb_speed;
+}
+
+static int bladerf1_get_serial(struct bladerf *dev, char *serial)
+{
+ strcpy(serial, dev->ident.serial);
+
+ return 0;
+}
+
+static int bladerf1_get_fpga_size(struct bladerf *dev, bladerf_fpga_size *size)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ *size = board_data->fpga_size;
+
+ return 0;
+}
+
+static int bladerf1_get_fpga_bytes(struct bladerf *dev, size_t *size)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ switch (board_data->fpga_size) {
+ case BLADERF_FPGA_40KLE:
+ *size = 1191788;
+ break;
+
+ case BLADERF_FPGA_115KLE:
+ *size = 3571462;
+ break;
+
+ default:
+ log_debug("%s: unknown fpga_size: %x\n", board_data->fpga_size);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+static int bladerf1_get_flash_size(struct bladerf *dev, uint32_t *size, bool *is_guess)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ *size = dev->flash_arch->tsize_bytes;
+ *is_guess = (dev->flash_arch->status != STATUS_SUCCESS);
+
+ return 0;
+}
+
+static int bladerf1_is_fpga_configured(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return dev->backend->is_fpga_configured(dev);
+}
+
+static int bladerf1_get_fpga_source(struct bladerf *dev,
+ bladerf_fpga_source *source)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FW_FPGA_SOURCE)) {
+ log_debug("%s: not supported by firmware\n", __FUNCTION__);
+ *source = BLADERF_FPGA_SOURCE_UNKNOWN;
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ *source = dev->backend->get_fpga_source(dev);
+
+ return 0;
+}
+
+static uint64_t bladerf1_get_capabilities(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ return board_data->capabilities;
+}
+
+static size_t bladerf1_get_channel_count(struct bladerf *dev,
+ bladerf_direction dir)
+{
+ return 1;
+}
+
+/******************************************************************************/
+/* Versions */
+/******************************************************************************/
+
+static int bladerf1_get_fpga_version(struct bladerf *dev, struct bladerf_version *version)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ memcpy(version, &board_data->fpga_version, sizeof(*version));
+
+ return 0;
+}
+
+static int bladerf1_get_fw_version(struct bladerf *dev, struct bladerf_version *version)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ memcpy(version, &board_data->fw_version, sizeof(*version));
+
+ return 0;
+}
+
+/******************************************************************************/
+/* Enable/disable */
+/******************************************************************************/
+
+static int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir);
+
+static int bladerf1_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool enable)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ log_debug("Enable channel: %s - %s\n",
+ BLADERF_CHANNEL_IS_TX(ch) ? "TX" : "RX",
+ enable ? "True" : "False");
+
+ if (enable == false) {
+ sync_deinit(&board_data->sync[ch]);
+ perform_format_deconfig(
+ dev, BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX);
+ }
+
+ lms_enable_rffe(dev, ch, enable);
+ status = dev->backend->enable_module(
+ dev, BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX, enable);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Gain */
+/******************************************************************************/
+
+/**
+ * @brief applies overall_gain to stage_gain, within the range max
+ *
+ * "Moves" gain from overall_gain to stage_gain, ensuring that overall_gain
+ * doesn't go negative and stage_gain doesn't exceed range->max.
+ *
+ * @param[in] range The range for stage_gain
+ * @param stage_gain The stage gain
+ * @param overall_gain The overall gain
+ */
+static void _apportion_gain(struct bladerf_range const *range,
+ int *stage_gain,
+ int *overall_gain)
+{
+ int headroom = __unscale_int(range, range->max) - *stage_gain;
+ int allotment = (headroom >= *overall_gain) ? *overall_gain : headroom;
+
+ /* Enforce step size */
+ while (0 != (allotment % range->step)) {
+ --allotment;
+ }
+
+ *stage_gain += allotment;
+ *overall_gain -= allotment;
+}
+
+static bladerf_lna_gain _convert_gain_to_lna_gain(int gain)
+{
+ if (gain >= BLADERF_LNA_GAIN_MAX_DB) {
+ return BLADERF_LNA_GAIN_MAX;
+ }
+
+ if (gain >= BLADERF_LNA_GAIN_MID_DB) {
+ return BLADERF_LNA_GAIN_MID;
+ }
+
+ return BLADERF_LNA_GAIN_BYPASS;
+}
+
+static int _convert_lna_gain_to_gain(bladerf_lna_gain lnagain)
+{
+ switch (lnagain) {
+ case BLADERF_LNA_GAIN_MAX:
+ return BLADERF_LNA_GAIN_MAX_DB;
+ case BLADERF_LNA_GAIN_MID:
+ return BLADERF_LNA_GAIN_MID_DB;
+ case BLADERF_LNA_GAIN_BYPASS:
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+static int bladerf1_get_gain_stage_range(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ struct bladerf_range const **range)
+{
+ struct bladerf_gain_stage_info const *stage_infos;
+ unsigned int stage_infos_len;
+ size_t i;
+
+ if (NULL == stage) {
+ log_error("%s: stage is null\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ stage_infos = bladerf1_tx_gain_stages;
+ stage_infos_len = ARRAY_SIZE(bladerf1_tx_gain_stages);
+ } else {
+ stage_infos = bladerf1_rx_gain_stages;
+ stage_infos_len = ARRAY_SIZE(bladerf1_rx_gain_stages);
+ }
+
+ for (i = 0; i < stage_infos_len; i++) {
+ if (strcmp(stage_infos[i].name, stage) == 0) {
+ if (NULL != range) {
+ *range = &(stage_infos[i].range);
+ }
+ return 0;
+ }
+ }
+
+ return BLADERF_ERR_INVAL;
+}
+
+static int set_rx_gain(struct bladerf *dev, int gain)
+{
+ struct bladerf_range const *lna_range = NULL;
+ struct bladerf_range const *rxvga1_range = NULL;
+ struct bladerf_range const *rxvga2_range = NULL;
+ bladerf_channel const ch = BLADERF_CHANNEL_RX(0);
+ int orig_gain = gain;
+ int lna, rxvga1, rxvga2;
+ int status;
+
+ // get our gain stage ranges!
+ status = bladerf1_get_gain_stage_range(dev, ch, "lna", &lna_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf1_get_gain_stage_range(dev, ch, "rxvga1", &rxvga1_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf1_get_gain_stage_range(dev, ch, "rxvga2", &rxvga2_range);
+ if (status < 0) {
+ return status;
+ }
+
+ lna = __unscale_int(lna_range, lna_range->min);
+ rxvga1 = __unscale_int(rxvga1_range, rxvga1_range->min);
+ rxvga2 = __unscale_int(rxvga2_range, rxvga2_range->min);
+
+ // offset gain so that we can use it as a counter when apportioning gain
+ gain -= __round_int((BLADERF1_RX_GAIN_OFFSET +
+ __unscale_int(lna_range, lna_range->min) +
+ __unscale_int(rxvga1_range, rxvga1_range->min) +
+ __unscale_int(rxvga2_range, rxvga2_range->min)));
+
+ // apportion some gain to RXLNA (but only half of it for now)
+ _apportion_gain(lna_range, &lna, &gain);
+ if (lna > BLADERF_LNA_GAIN_MID_DB) {
+ gain += (lna - BLADERF_LNA_GAIN_MID_DB);
+ lna -= (lna - BLADERF_LNA_GAIN_MID_DB);
+ }
+
+ // apportion gain to RXVGA1
+ _apportion_gain(rxvga1_range, &rxvga1, &gain);
+
+ // apportion more gain to RXLNA
+ _apportion_gain(lna_range, &lna, &gain);
+
+ // apportion gain to RXVGA2
+ _apportion_gain(rxvga2_range, &rxvga2, &gain);
+
+ // if we still have remaining gain, it's because rxvga2 has a step size of
+ // 3 dB. Steal a few dB from rxvga1...
+ if (gain > 0 && rxvga1 >= __unscale_int(rxvga1_range, rxvga1_range->max)) {
+ rxvga1 -= __unscale_int(rxvga2_range, rxvga2_range->step);
+ gain += __unscale_int(rxvga2_range, rxvga2_range->step);
+
+ _apportion_gain(rxvga2_range, &rxvga2, &gain);
+ _apportion_gain(rxvga1_range, &rxvga1, &gain);
+ }
+
+ // verification
+ if (gain != 0) {
+ log_warning("%s: unable to achieve requested gain %d (missed by %d)\n",
+ __FUNCTION__, orig_gain, gain);
+ log_debug("%s: gain=%d -> rxvga1=%d lna=%d rxvga2=%d remainder=%d\n",
+ __FUNCTION__, orig_gain, rxvga1, lna, rxvga2, gain);
+ }
+
+ // that should do it. actually apply the changes:
+ status = lms_lna_set_gain(dev, _convert_gain_to_lna_gain(lna));
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_rxvga1_set_gain(dev, __scale_int(rxvga1_range, rxvga1));
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_rxvga2_set_gain(dev, __scale_int(rxvga2_range, rxvga2));
+ if (status < 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+static int get_rx_gain(struct bladerf *dev, int *gain)
+{
+ int status;
+ bladerf_lna_gain lnagain;
+ int lnagain_db;
+ int rxvga1;
+ int rxvga2;
+
+ status = lms_lna_get_gain(dev, &lnagain);
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_rxvga1_get_gain(dev, &rxvga1);
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_rxvga2_get_gain(dev, &rxvga2);
+ if (status < 0) {
+ return status;
+ }
+
+ switch (lnagain) {
+ case BLADERF_LNA_GAIN_BYPASS:
+ lnagain_db = 0;
+ break;
+
+ case BLADERF_LNA_GAIN_MID:
+ lnagain_db = BLADERF_LNA_GAIN_MID_DB;
+ break;
+
+ case BLADERF_LNA_GAIN_MAX:
+ lnagain_db = BLADERF_LNA_GAIN_MAX_DB;
+ break;
+
+ default:
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ *gain = __round_int(lnagain_db + rxvga1 + rxvga2 + BLADERF1_RX_GAIN_OFFSET);
+
+ return 0;
+}
+
+static int set_tx_gain(struct bladerf *dev, int gain)
+{
+ struct bladerf_range const *txvga1_range = NULL;
+ struct bladerf_range const *txvga2_range = NULL;
+ bladerf_channel const ch = BLADERF_CHANNEL_TX(0);
+ int orig_gain = gain;
+ int txvga1, txvga2;
+ int status;
+
+ // get our gain stage ranges!
+ status = bladerf1_get_gain_stage_range(dev, ch, "txvga1", &txvga1_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf1_get_gain_stage_range(dev, ch, "txvga2", &txvga2_range);
+ if (status < 0) {
+ return status;
+ }
+
+ txvga1 = __unscale_int(txvga1_range, txvga1_range->min);
+ txvga2 = __unscale_int(txvga2_range, txvga2_range->min);
+
+ // offset gain so that we can use it as a counter when apportioning gain
+ gain -= __round_int((BLADERF1_TX_GAIN_OFFSET +
+ __unscale_int(txvga1_range, txvga1_range->min) +
+ __unscale_int(txvga2_range, txvga2_range->min)));
+
+ // apportion gain to TXVGA2
+ _apportion_gain(txvga2_range, &txvga2, &gain);
+
+ // apportion gain to TXVGA1
+ _apportion_gain(txvga1_range, &txvga1, &gain);
+
+ // verification
+ if (gain != 0) {
+ log_warning("%s: unable to achieve requested gain %d (missed by %d)\n",
+ __FUNCTION__, orig_gain, gain);
+ log_debug("%s: gain=%d -> txvga2=%d txvga1=%d remainder=%d\n",
+ __FUNCTION__, orig_gain, txvga2, txvga1, gain);
+ }
+
+ status = lms_txvga1_set_gain(dev, txvga1);
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_txvga2_set_gain(dev, txvga2);
+ if (status < 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+static int get_tx_gain(struct bladerf *dev, int *gain)
+{
+ int status;
+ int txvga1;
+ int txvga2;
+
+ status = lms_txvga1_get_gain(dev, &txvga1);
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_txvga2_get_gain(dev, &txvga2);
+ if (status < 0) {
+ return status;
+ }
+
+ *gain = __round_int(txvga1 + txvga2 + BLADERF1_TX_GAIN_OFFSET);
+
+ return 0;
+}
+
+static int bladerf1_set_gain(struct bladerf *dev, bladerf_channel ch, int gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (BLADERF_CHANNEL_TX(0) == ch) {
+ return set_tx_gain(dev, gain);
+ } else if (BLADERF_CHANNEL_RX(0) == ch) {
+ return set_rx_gain(dev, gain);
+ }
+
+ return BLADERF_ERR_INVAL;
+}
+
+static int bladerf1_set_gain_mode(struct bladerf *dev,
+ bladerf_module mod,
+ bladerf_gain_mode mode)
+{
+ int status;
+ uint32_t config_gpio;
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ uint32_t const TABLE_VERSION = 2; /* Required RX DC cal table version */
+
+ /* Strings used in AGC-unavailable warnings */
+ char const *MGC_WARN = "Manual gain control will be used instead.";
+ char const *FPGA_STR = "download and install FPGA v0.7.0 or newer from "
+ "https://nuand.com/fpga/";
+ char const *DCCAL_STR = "see \"Generating a DC offset table\" at "
+ "https://github.com/Nuand/bladeRF/wiki/"
+ "DC-offset-and-IQ-Imbalance-Correction";
+
+ if (mod != BLADERF_MODULE_RX) {
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if ((status = dev->backend->config_gpio_read(dev, &config_gpio))) {
+ return status;
+ }
+
+ if (mode == BLADERF_GAIN_AUTOMATIC || mode == BLADERF_GAIN_DEFAULT) {
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_AGC_DC_LUT)) {
+ log_warning("AGC not supported by FPGA. %s\n", MGC_WARN);
+ log_info("To enable AGC, %s, then %s\n", FPGA_STR, DCCAL_STR);
+ log_debug("%s: expected FPGA >= v0.7.0, got v%u.%u.%u\n",
+ __FUNCTION__, board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (!board_data->cal.dc_rx) {
+ log_warning("RX DC calibration table not found. %s\n", MGC_WARN);
+ log_info("To enable AGC, %s\n", DCCAL_STR);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (board_data->cal.dc_rx->version != TABLE_VERSION) {
+ log_warning("RX DC calibration table is out-of-date. %s\n",
+ MGC_WARN);
+ log_info("To enable AGC, %s\n", DCCAL_STR);
+ log_debug("%s: expected version %u, got %u\n", __FUNCTION__,
+ TABLE_VERSION, board_data->cal.dc_rx->version);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ config_gpio |= BLADERF_GPIO_AGC_ENABLE;
+ } else if (mode == BLADERF_GAIN_MANUAL || mode == BLADERF_GAIN_MGC) {
+ config_gpio &= ~BLADERF_GPIO_AGC_ENABLE;
+ }
+
+ return dev->backend->config_gpio_write(dev, config_gpio);
+}
+
+static int bladerf1_get_gain_mode(struct bladerf *dev,
+ bladerf_module mod,
+ bladerf_gain_mode *mode)
+{
+ int status;
+ uint32_t config_gpio;
+
+ if ((status = dev->backend->config_gpio_read(dev, &config_gpio))) {
+ return status;
+ }
+
+ if (config_gpio & BLADERF_GPIO_AGC_ENABLE) {
+ *mode = BLADERF_GAIN_DEFAULT;
+ } else {
+ *mode = BLADERF_GAIN_MGC;
+ }
+
+ return status;
+}
+
+static int bladerf1_get_gain(struct bladerf *dev, bladerf_channel ch, int *gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (BLADERF_CHANNEL_TX(0) == ch) {
+ return get_tx_gain(dev, gain);
+ } else if (BLADERF_CHANNEL_RX(0) == ch) {
+ return get_rx_gain(dev, gain);
+ }
+
+ return BLADERF_ERR_INVAL;
+}
+
+static int bladerf1_get_gain_modes(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_gain_modes const **modes)
+{
+ struct bladerf_gain_modes const *mode_infos;
+ unsigned int mode_infos_len;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ mode_infos = NULL;
+ mode_infos_len = 0;
+ } else {
+ mode_infos = bladerf1_rx_gain_modes;
+ mode_infos_len = ARRAY_SIZE(bladerf1_rx_gain_modes);
+ }
+
+ if (modes != NULL) {
+ *modes = mode_infos;
+ }
+
+ return mode_infos_len;
+}
+
+static int bladerf1_get_gain_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ *range = &bladerf1_tx_gain_range;
+ } else {
+ *range = &bladerf1_rx_gain_range;
+ }
+
+ return 0;
+}
+
+static int bladerf1_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ /* TODO implement gain clamping */
+
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ if (strcmp(stage, "txvga1") == 0) {
+ return lms_txvga1_set_gain(dev, gain);
+ } else if (strcmp(stage, "txvga2") == 0) {
+ return lms_txvga2_set_gain(dev, gain);
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__,
+ stage);
+ return 0;
+ }
+
+ case BLADERF_CHANNEL_RX(0):
+ if (strcmp(stage, "rxvga1") == 0) {
+ return lms_rxvga1_set_gain(dev, gain);
+ } else if (strcmp(stage, "rxvga2") == 0) {
+ return lms_rxvga2_set_gain(dev, gain);
+ } else if (strcmp(stage, "lna") == 0) {
+ return lms_lna_set_gain(dev, _convert_gain_to_lna_gain(gain));
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__,
+ stage);
+ return 0;
+ }
+
+ default:
+ log_error("%s: channel %d invalid\n", __FUNCTION__, ch);
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+static int bladerf1_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ if (strcmp(stage, "txvga1") == 0) {
+ return lms_txvga1_get_gain(dev, gain);
+ } else if (strcmp(stage, "txvga2") == 0) {
+ return lms_txvga2_get_gain(dev, gain);
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__,
+ stage);
+ return 0;
+ }
+
+ case BLADERF_CHANNEL_RX(0):
+ if (strcmp(stage, "rxvga1") == 0) {
+ return lms_rxvga1_get_gain(dev, gain);
+ } else if (strcmp(stage, "rxvga2") == 0) {
+ return lms_rxvga2_get_gain(dev, gain);
+ } else if (strcmp(stage, "lna") == 0) {
+ int status;
+ bladerf_lna_gain lnagain;
+ status = lms_lna_get_gain(dev, &lnagain);
+ if (status == 0) {
+ *gain = _convert_lna_gain_to_gain(lnagain);
+ }
+ return status;
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__,
+ stage);
+ return 0;
+ }
+
+ default:
+ log_error("%s: channel %d invalid\n", __FUNCTION__, ch);
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+static int bladerf1_get_gain_stages(struct bladerf *dev,
+ bladerf_channel ch,
+ char const **stages,
+ size_t count)
+{
+ struct bladerf_gain_stage_info const *stage_infos;
+ unsigned int stage_infos_len;
+ unsigned int i;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ stage_infos = bladerf1_tx_gain_stages;
+ stage_infos_len = ARRAY_SIZE(bladerf1_tx_gain_stages);
+ } else {
+ stage_infos = bladerf1_rx_gain_stages;
+ stage_infos_len = ARRAY_SIZE(bladerf1_rx_gain_stages);
+ }
+
+ if (stages != NULL) {
+ count = (stage_infos_len < count) ? stage_infos_len : count;
+
+ for (i = 0; i < count; i++) {
+ stages[i] = stage_infos[i].name;
+ }
+ }
+
+ return stage_infos_len;
+}
+
+/******************************************************************************/
+/* Sample Rate */
+/******************************************************************************/
+
+static int bladerf1_set_sample_rate(struct bladerf *dev, bladerf_channel ch, unsigned int rate, unsigned int *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return si5338_set_sample_rate(dev, ch, rate, actual);
+}
+
+static int bladerf1_get_sample_rate(struct bladerf *dev, bladerf_channel ch, unsigned int *rate)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return si5338_get_sample_rate(dev, ch, rate);
+}
+
+static int bladerf1_get_sample_rate_range(struct bladerf *dev, bladerf_channel ch, const struct bladerf_range **range)
+{
+ *range = &bladerf1_sample_rate_range;
+ return 0;
+}
+
+static int bladerf1_set_rational_sample_rate(struct bladerf *dev, bladerf_channel ch, struct bladerf_rational_rate *rate, struct bladerf_rational_rate *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return si5338_set_rational_sample_rate(dev, ch, rate, actual);
+}
+
+static int bladerf1_get_rational_sample_rate(struct bladerf *dev, bladerf_channel ch, struct bladerf_rational_rate *rate)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return si5338_get_rational_sample_rate(dev, ch, rate);
+}
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+static int bladerf1_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ int status;
+ lms_bw bw;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (bandwidth < BLADERF_BANDWIDTH_MIN) {
+ bandwidth = BLADERF_BANDWIDTH_MIN;
+ log_info("Clamping bandwidth to %d Hz\n", bandwidth);
+ } else if (bandwidth > BLADERF_BANDWIDTH_MAX) {
+ bandwidth = BLADERF_BANDWIDTH_MAX;
+ log_info("Clamping bandwidth to %d Hz\n", bandwidth);
+ }
+
+ bw = lms_uint2bw(bandwidth);
+
+ status = lms_lpf_enable(dev, ch, true);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set_bandwidth(dev, ch, bw);
+ if (actual != NULL) {
+ if (status == 0) {
+ *actual = lms_bw2uint(bw);
+ } else {
+ *actual = 0;
+ }
+ }
+
+ return status;
+}
+
+static int bladerf1_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ int status;
+ lms_bw bw;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ status = lms_get_bandwidth(dev, ch, &bw);
+ if (status == 0) {
+ *bandwidth = lms_bw2uint(bw);
+ } else {
+ *bandwidth = 0;
+ }
+
+ return status;
+}
+
+static int bladerf1_get_bandwidth_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ *range = &bladerf1_bandwidth_range;
+ return 0;
+}
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+static int bladerf1_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ const bladerf_xb attached = dev->xb;
+ int status;
+ int16_t dc_i, dc_q;
+ struct dc_cal_entry entry;
+ const struct dc_cal_tbl *dc_cal = (ch == BLADERF_CHANNEL_RX(0))
+ ? board_data->cal.dc_rx
+ : board_data->cal.dc_tx;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ log_debug("Setting %s frequency to %" BLADERF_PRIuFREQ "\n",
+ channel2str(ch), frequency);
+
+ if (attached == BLADERF_XB_200) {
+ if (frequency < BLADERF_FREQUENCY_MIN) {
+ status = xb200_set_path(dev, ch, BLADERF_XB200_MIX);
+ if (status) {
+ return status;
+ }
+
+ status = xb200_auto_filter_selection(dev, ch, frequency);
+ if (status) {
+ return status;
+ }
+
+ frequency = 1248000000 - frequency;
+ } else {
+ status = xb200_set_path(dev, ch, BLADERF_XB200_BYPASS);
+ if (status) {
+ return status;
+ }
+ }
+ }
+
+ switch (board_data->tuning_mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ status = lms_set_frequency(dev, ch, (uint32_t)frequency);
+ if (status != 0) {
+ return status;
+ }
+
+ status = band_select(dev, ch, frequency < BLADERF1_BAND_HIGH);
+ break;
+
+ case BLADERF_TUNING_MODE_FPGA: {
+ status = dev->board->schedule_retune(dev, ch, BLADERF_RETUNE_NOW,
+ frequency, NULL);
+ break;
+ }
+
+ default:
+ log_debug("Invalid tuning mode: %d\n", board_data->tuning_mode);
+ status = BLADERF_ERR_INVAL;
+ break;
+ }
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_cal != NULL) {
+ dc_cal_tbl_entry(dc_cal, (uint32_t)frequency, &entry);
+
+ dc_i = entry.dc_i;
+ dc_q = entry.dc_q;
+
+ status = lms_set_dc_offset_i(dev, ch, dc_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set_dc_offset_q(dev, ch, dc_q);
+ if (status != 0) {
+ return status;
+ }
+
+ if (ch == BLADERF_CHANNEL_RX(0) &&
+ have_cap(board_data->capabilities, BLADERF_CAP_AGC_DC_LUT)) {
+ status = dev->backend->set_agc_dc_correction(
+ dev, entry.max_dc_q, entry.max_dc_i, entry.mid_dc_q,
+ entry.mid_dc_i, entry.min_dc_q, entry.min_dc_i);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Set AGC DC offset cal (I, Q) to: Max (%d, %d) "
+ " Mid (%d, %d) Min (%d, %d)\n",
+ entry.max_dc_q, entry.max_dc_i, entry.mid_dc_q,
+ entry.mid_dc_i, entry.min_dc_q, entry.min_dc_i);
+ }
+
+ log_verbose("Set %s DC offset cal (I, Q) to: (%d, %d)\n",
+ (ch == BLADERF_CHANNEL_RX(0)) ? "RX" : "TX", dc_i, dc_q);
+ }
+
+ return 0;
+}
+
+static int bladerf1_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ bladerf_xb200_path path;
+ struct lms_freq f;
+ int status = 0;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ status = lms_get_frequency(dev, ch, &f);
+ if (status != 0) {
+ return status;
+ }
+
+ if (f.x == 0) {
+ /* If we see this, it's most often an indication that communication
+ * with the LMS6002D is not occuring correctly */
+ *frequency = 0;
+ status = BLADERF_ERR_IO;
+ } else {
+ *frequency = lms_frequency_to_hz(&f);
+ }
+ if (status != 0) {
+ return status;
+ }
+
+ if (dev->xb == BLADERF_XB_200) {
+ status = xb200_get_path(dev, ch, &path);
+ if (status != 0) {
+ return status;
+ }
+ if (path == BLADERF_XB200_MIX) {
+ *frequency = 1248000000 - *frequency;
+ }
+ }
+
+ return 0;
+}
+
+static int bladerf1_get_frequency_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ if (dev->xb == BLADERF_XB_200) {
+ *range = &bladerf1_xb200_frequency_range;
+ } else {
+ *range = &bladerf1_frequency_range;
+ }
+
+ return 0;
+}
+
+static int bladerf1_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return band_select(dev, ch, frequency < BLADERF1_BAND_HIGH);
+}
+
+/******************************************************************************/
+/* RF ports */
+/******************************************************************************/
+
+static int bladerf1_set_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port)
+{
+ const struct bladerf_lms_port_name_map *port_map;
+ unsigned int port_map_len;
+ int status;
+ size_t i;
+
+ lms_lna rx_lna = LNA_NONE;
+ lms_pa tx_pa = PA_NONE;
+ bool ok = false;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ /* TODO: lms_pa_enable is not currently implemented */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_debug("%s: not implemented for TX channels, silently ignoring\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ port_map = bladerf1_tx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_tx_port_map);
+ } else {
+ port_map = bladerf1_rx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_rx_port_map);
+ }
+
+ for (i = 0; i < port_map_len; i++) {
+ if (strcmp(port_map[i].name, port) == 0) {
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ tx_pa = port_map[i].tx_pa;
+ } else {
+ rx_lna = port_map[i].rx_lna;
+ }
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ log_error("port '%s' not valid for channel %s\n", port,
+ channel2str(ch));
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ for (i = 0; i < port_map_len; i++) {
+ bool enable = (port_map[i].tx_pa == tx_pa);
+#if 0
+ status = lms_pa_enable(dev, port_map[i].tx_pa, enable);
+#else
+ log_verbose("%s: would %s pa %d but this is not implemented\n",
+ __FUNCTION__, enable ? "enable" : "disable", tx_pa);
+ status = 0;
+#endif // 0
+ if (status < 0) {
+ break;
+ }
+ }
+ } else {
+ status = lms_select_lna(dev, rx_lna);
+ }
+
+ return status;
+}
+
+static int bladerf1_get_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **port)
+{
+ const struct bladerf_lms_port_name_map *port_map;
+ unsigned int port_map_len;
+ int status;
+ size_t i;
+
+ lms_lna rx_lna = LNA_NONE;
+ lms_pa tx_pa = PA_NONE;
+ bool ok = false;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ /* TODO: pa getter not currently implemented */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_debug("%s: not implemented for TX channels\n", __FUNCTION__);
+ if (port != NULL) {
+ *port = "auto";
+ }
+ return 0;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ port_map = bladerf1_tx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_tx_port_map);
+
+#if 0
+ status = lms_get_pa(dev, &tx_pa);
+#else
+ status = 0;
+#endif // 0
+ } else {
+ port_map = bladerf1_rx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_rx_port_map);
+
+ status = lms_get_lna(dev, &rx_lna);
+ }
+
+ if (status < 0) {
+ return status;
+ }
+
+ if (port != NULL) {
+ for (i = 0; i < port_map_len; i++) {
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (tx_pa == port_map[i].tx_pa) {
+ *port = port_map[i].name;
+ ok = true;
+ break;
+ }
+ } else {
+ if (rx_lna == port_map[i].rx_lna) {
+ *port = port_map[i].name;
+ ok = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!ok) {
+ if (port != NULL) {
+ *port = "unknown";
+ }
+ log_error("%s: unexpected port id %d\n", __FUNCTION__,
+ BLADERF_CHANNEL_IS_TX(ch) ? tx_pa : rx_lna);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+static int bladerf1_get_rf_ports(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **ports,
+ unsigned int count)
+{
+ const struct bladerf_lms_port_name_map *port_map;
+ unsigned int port_map_len;
+ size_t i;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ /* Return a null list instead of bladerf1_tx_port_map */
+ port_map = NULL;
+ port_map_len = 0;
+ } else {
+ port_map = bladerf1_rx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_rx_port_map);
+ }
+
+ if (ports != NULL) {
+ count = (port_map_len < count) ? port_map_len : count;
+
+ for (i = 0; i < count; i++) {
+ ports[i] = port_map[i].name;
+ }
+ }
+
+ return port_map_len;
+}
+
+/******************************************************************************/
+/* Scheduled Tuning */
+/******************************************************************************/
+
+static int bladerf1_get_quick_tune(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return lms_get_quick_tune(dev, ch, quick_tune);
+}
+
+static int bladerf1_schedule_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune)
+
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+ struct lms_freq f;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ log_debug("This FPGA version (%u.%u.%u) does not support "
+ "scheduled retunes.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (quick_tune == NULL) {
+ if (dev->xb == BLADERF_XB_200) {
+ log_info("Consider supplying the quick_tune parameter to"
+ " bladerf_schedule_retune() when the XB-200 is enabled.\n");
+ }
+ status = lms_calculate_tuning_params((uint32_t)frequency, &f);
+ if (status != 0) {
+ return status;
+ }
+ } else {
+ f.freqsel = quick_tune->freqsel;
+ f.vcocap = quick_tune->vcocap;
+ f.nint = quick_tune->nint;
+ f.nfrac = quick_tune->nfrac;
+ f.flags = quick_tune->flags;
+ f.xb_gpio = quick_tune->xb_gpio;
+ f.x = 0;
+ f.vcocap_result = 0;
+ }
+
+ return dev->backend->retune(dev, ch, timestamp, f.nint, f.nfrac, f.freqsel,
+ f.vcocap,
+ (f.flags & LMS_FREQ_FLAGS_LOW_BAND) != 0,
+ f.xb_gpio,
+ (f.flags & LMS_FREQ_FLAGS_FORCE_VCOCAP) != 0);
+}
+
+static int bladerf1_cancel_scheduled_retunes(struct bladerf *dev,
+ bladerf_channel ch)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ status = dev->backend->retune(dev, ch, NIOS_PKT_RETUNE_CLEAR_QUEUE, 0,
+ 0, 0, 0, false, 0, false);
+ } else {
+ log_debug("This FPGA version (%u.%u.%u) does not support "
+ "scheduled retunes.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* DC/Phase/Gain Correction */
+/******************************************************************************/
+
+static int bladerf1_get_correction(struct bladerf *dev, bladerf_channel ch,
+ bladerf_correction corr, int16_t *value)
+{
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ switch (corr) {
+ case BLADERF_CORR_PHASE:
+ status = dev->backend->get_iq_phase_correction(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_GAIN:
+ status = dev->backend->get_iq_gain_correction(dev, ch, value);
+
+ /* Undo the gain control offset */
+ if (status == 0) {
+ *value -= 4096;
+ }
+ break;
+
+ case BLADERF_CORR_DCOFF_I:
+ status = lms_get_dc_offset_i(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_DCOFF_Q:
+ status = lms_get_dc_offset_q(dev, ch, value);
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ log_debug("Invalid correction type: %d\n", corr);
+ break;
+ }
+
+ return status;
+}
+
+static int bladerf1_set_correction(struct bladerf *dev, bladerf_channel ch,
+ bladerf_correction corr, int16_t value)
+{
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ switch (corr) {
+ case BLADERF_CORR_PHASE:
+ status = dev->backend->set_iq_phase_correction(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_GAIN:
+ /* Gain correction requires than an offset be applied */
+ value += (int16_t) 4096;
+ status = dev->backend->set_iq_gain_correction(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_DCOFF_I:
+ status = lms_set_dc_offset_i(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_DCOFF_Q:
+ status = lms_set_dc_offset_q(dev, ch, value);
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ log_debug("Invalid correction type: %d\n", corr);
+ break;
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Trigger */
+/******************************************************************************/
+
+static int bladerf1_trigger_init(struct bladerf *dev, bladerf_channel ch, bladerf_trigger_signal signal, struct bladerf_trigger *trigger)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_TRX_SYNC_TRIG)) {
+ log_debug("FPGA v%s does not support synchronization triggers.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return fpga_trigger_init(dev, ch, signal, trigger);
+}
+
+static int bladerf1_trigger_arm(struct bladerf *dev, const struct bladerf_trigger *trigger, bool arm, uint64_t resv1, uint64_t resv2)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_TRX_SYNC_TRIG)) {
+ log_debug("FPGA v%s does not support synchronization triggers.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ /* resv1 & resv2 unused - may be allocated for use as timestamp and
+ * other flags in the future */
+
+ return fpga_trigger_arm(dev, trigger, arm);
+}
+
+static int bladerf1_trigger_fire(struct bladerf *dev, const struct bladerf_trigger *trigger)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_TRX_SYNC_TRIG)) {
+ log_debug("FPGA v%s does not support synchronization triggers.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return fpga_trigger_fire(dev, trigger);
+}
+
+static int bladerf1_trigger_state(struct bladerf *dev, const struct bladerf_trigger *trigger, bool *is_armed, bool *has_fired, bool *fire_requested, uint64_t *resv1, uint64_t *resv2)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_TRX_SYNC_TRIG)) {
+ log_debug("FPGA v%s does not support synchronization triggers.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ status = fpga_trigger_state(dev, trigger, is_armed, has_fired, fire_requested);
+
+ /* Reserved for future metadata (e.g., trigger counts, timestamp) */
+ if (resv1 != NULL) {
+ *resv1 = 0;
+ }
+
+ if (resv2 != NULL) {
+ *resv2 = 0;
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Streaming */
+/******************************************************************************/
+
+static inline int requires_timestamps(bladerf_format format, bool *required)
+{
+ int status = 0;
+
+ switch (format) {
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ *required = true;
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11:
+ *required = false;
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+
+/**
+ * Perform the neccessary device configuration for the specified format
+ * (e.g., enabling/disabling timestamp support), first checking that the
+ * requested format would not conflict with the other stream direction.
+ *
+ * @param dev Device handle
+ * @param[in] dir Direction that is currently being configured
+ * @param[in] format Format the channel is being configured for
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+static int perform_format_config(struct bladerf *dev, bladerf_direction dir,
+ bladerf_format format)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ int status = 0;
+ bool use_timestamps;
+ bladerf_channel other;
+ bool other_using_timestamps;
+ uint32_t gpio_val;
+
+ status = requires_timestamps(format, &use_timestamps);
+ if (status != 0) {
+ log_debug("%s: Invalid format: %d\n", __FUNCTION__, format);
+ return status;
+ }
+
+ if (use_timestamps && !have_cap(board_data->capabilities, BLADERF_CAP_TIMESTAMPS)) {
+ log_warning("Timestamp support requires FPGA v0.1.0 or later.\n");
+ return BLADERF_ERR_UPDATE_FPGA;
+ }
+
+ switch (dir) {
+ case BLADERF_RX:
+ other = BLADERF_TX;
+ break;
+
+ case BLADERF_TX:
+ other = BLADERF_RX;
+ break;
+
+ default:
+ log_debug("Invalid direction: %d\n", dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = requires_timestamps(board_data->module_format[other],
+ &other_using_timestamps);
+
+ if ((status == 0) && (other_using_timestamps != use_timestamps)) {
+ log_debug("Format conflict detected: RX=%d, TX=%d\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = dev->backend->config_gpio_read(dev, &gpio_val);
+ if (status != 0) {
+ return status;
+ }
+
+ if (format == BLADERF_FORMAT_PACKET_META) {
+ gpio_val |= BLADERF_GPIO_PACKET;
+ use_timestamps = true;
+ } else {
+ gpio_val &= ~BLADERF_GPIO_PACKET;
+ }
+
+ if (use_timestamps) {
+ gpio_val |= (BLADERF_GPIO_TIMESTAMP | BLADERF_GPIO_TIMESTAMP_DIV2);
+ } else {
+ gpio_val &= ~(BLADERF_GPIO_TIMESTAMP | BLADERF_GPIO_TIMESTAMP_DIV2);
+ }
+
+ status = dev->backend->config_gpio_write(dev, gpio_val);
+ if (status == 0) {
+ board_data->module_format[dir] = format;
+ }
+
+ return status;
+}
+
+/**
+ * Deconfigure and update any state pertaining what a format that a stream
+ * direction is no longer using.
+ *
+ * @param dev Device handle
+ * @param[in] dir Direction that is currently being deconfigured
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+static int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ switch (dir) {
+ case BLADERF_RX:
+ case BLADERF_TX:
+ /* We'll reconfigure the HW when we call perform_format_config, so
+ * we just need to update our stored information */
+ board_data->module_format[dir] = -1;
+ break;
+
+ default:
+ log_debug("%s: Invalid direction: %d\n", __FUNCTION__, dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+static int bladerf1_init_stream(struct bladerf_stream **stream, struct bladerf *dev, bladerf_stream_cb callback, void ***buffers, size_t num_buffers, bladerf_format format, size_t samples_per_buffer, size_t num_transfers, void *user_data)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return async_init_stream(stream, dev, callback, buffers, num_buffers,
+ format, samples_per_buffer, num_transfers, user_data);
+}
+
+static int bladerf1_stream(struct bladerf_stream *stream, bladerf_channel_layout layout)
+{
+ bladerf_direction dir = layout & BLADERF_DIRECTION_MASK;
+ int stream_status, fmt_status;
+
+ if (layout != BLADERF_RX_X1 && layout != BLADERF_TX_X1) {
+ return -EINVAL;
+ }
+
+ fmt_status = perform_format_config(stream->dev, dir, stream->format);
+ if (fmt_status != 0) {
+ return fmt_status;
+ }
+
+ stream_status = async_run_stream(stream, layout);
+
+ fmt_status = perform_format_deconfig(stream->dev, dir);
+ if (fmt_status != 0) {
+ return fmt_status;
+ }
+
+ return stream_status;
+}
+
+static int bladerf1_submit_stream_buffer(struct bladerf_stream *stream, void *buffer, unsigned int timeout_ms, bool nonblock)
+{
+ size_t len;
+ len = async_stream_buf_bytes(stream);
+ return async_submit_stream_buffer(stream, buffer, &len, timeout_ms, nonblock);
+}
+
+static void bladerf1_deinit_stream(struct bladerf_stream *stream)
+{
+ async_deinit_stream(stream);
+}
+
+static int bladerf1_set_stream_timeout(struct bladerf *dev, bladerf_direction dir, unsigned int timeout)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ MUTEX_LOCK(&board_data->sync[dir].lock);
+ board_data->sync[dir].stream_config.timeout_ms = timeout;
+ MUTEX_UNLOCK(&board_data->sync[dir].lock);
+
+ return 0;
+}
+
+static int bladerf1_get_stream_timeout(struct bladerf *dev, bladerf_direction dir, unsigned int *timeout)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ MUTEX_LOCK(&board_data->sync[dir].lock);
+ *timeout = board_data->sync[dir].stream_config.timeout_ms;
+ MUTEX_UNLOCK(&board_data->sync[dir].lock);
+
+ return 0;
+}
+
+static int bladerf1_sync_config(struct bladerf *dev, bladerf_channel_layout layout, bladerf_format format, unsigned int num_buffers, unsigned int buffer_size, unsigned int num_transfers, unsigned int stream_timeout)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ bladerf_direction dir = layout & BLADERF_DIRECTION_MASK;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (layout != BLADERF_RX_X1 && layout != BLADERF_TX_X1) {
+ return -EINVAL;
+ }
+
+ status = perform_format_config(dev, dir, format);
+ if (status == 0) {
+ status = sync_init(&board_data->sync[dir], dev, layout,
+ format, num_buffers, buffer_size,
+ board_data->msg_size, num_transfers,
+ stream_timeout);
+ if (status != 0) {
+ perform_format_deconfig(dev, dir);
+ }
+ }
+
+ return status;
+}
+
+static int bladerf1_sync_tx(struct bladerf *dev,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ if (!board_data->sync[BLADERF_TX].initialized) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = sync_tx(&board_data->sync[BLADERF_TX], samples, num_samples,
+ metadata, timeout_ms);
+
+ return status;
+}
+
+static int bladerf1_sync_rx(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ if (!board_data->sync[BLADERF_RX].initialized) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = sync_rx(&board_data->sync[BLADERF_RX], samples, num_samples,
+ metadata, timeout_ms);
+
+ return status;
+}
+
+static int bladerf1_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *value)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return dev->backend->get_timestamp(dev, dir, value);
+}
+
+/******************************************************************************/
+/* FPGA/Firmware Loading/Flashing */
+/******************************************************************************/
+
+static bool is_valid_fpga_size(struct bladerf *dev,
+ bladerf_fpga_size fpga,
+ size_t len)
+{
+ static const char env_override[] = "BLADERF_SKIP_FPGA_SIZE_CHECK";
+ bool valid;
+ size_t expected;
+ int status;
+
+ status = dev->board->get_fpga_bytes(dev, &expected);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Provide a means to override this check. This is intended to allow
+ * folks who know what they're doing to work around this quickly without
+ * needing to make a code change. (e.g., someone building a custom FPGA
+ * image that enables compressoin) */
+ if (getenv(env_override)) {
+ log_info("Overriding FPGA size check per %s\n", env_override);
+ valid = true;
+ } else if (expected > 0) {
+ valid = (len == expected);
+ } else {
+ log_debug("Unknown FPGA type (%d). Using relaxed size criteria.\n",
+ fpga);
+
+ if (len < (1 * 1024 * 1024)) {
+ valid = false;
+ } else if (len >
+ (dev->flash_arch->tsize_bytes - BLADERF_FLASH_ADDR_FPGA)) {
+ valid = false;
+ } else {
+ valid = true;
+ }
+ }
+
+ if (!valid) {
+ log_warning("Detected potentially incorrect FPGA file (length was %d, "
+ "expected %d).\n",
+ len, expected);
+
+ log_debug("If you are certain this file is valid, you may define\n"
+ "BLADERF_SKIP_FPGA_SIZE_CHECK in your environment to skip "
+ "this check.\n\n");
+ }
+
+ return valid;
+}
+
+static int bladerf1_load_fpga(struct bladerf *dev, const uint8_t *buf, size_t length)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->load_fpga(dev, buf, length);
+ if (status != 0) {
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+ }
+
+ /* Update device state */
+ board_data->state = STATE_FPGA_LOADED;
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ status = bladerf1_initialize(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+static int bladerf1_flash_fpga(struct bladerf *dev, const uint8_t *buf, size_t length)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ return spi_flash_write_fpga_bitstream(dev, buf, length);
+}
+
+static int bladerf1_erase_stored_fpga(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_erase_fpga(dev);
+}
+
+static bool is_valid_fw_size(size_t len)
+{
+ /* Simple FW applications generally are significantly larger than this */
+ if (len < (50 * 1024)) {
+ return false;
+ } else if (len > BLADERF_FLASH_BYTE_LEN_FIRMWARE) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+static int bladerf1_flash_firmware(struct bladerf *dev, const uint8_t *buf, size_t length)
+{
+ const char env_override[] = "BLADERF_SKIP_FW_SIZE_CHECK";
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ /* Sanity check firmware length.
+ *
+ * TODO in the future, better sanity checks can be performed when
+ * using the bladerf image format currently used to backup/restore
+ * calibration data
+ */
+ if (!getenv(env_override) && !is_valid_fw_size(length)) {
+ log_info("Detected potentially invalid firmware file.\n");
+ log_info("Define BLADERF_SKIP_FW_SIZE_CHECK in your evironment "
+ "to skip this check.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ return spi_flash_write_fx3_fw(dev, buf, length);
+}
+
+static int bladerf1_device_reset(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return dev->backend->device_reset(dev);
+}
+
+/******************************************************************************/
+/* Tuning mode */
+/******************************************************************************/
+
+static int bladerf1_set_tuning_mode(struct bladerf *dev, bladerf_tuning_mode mode)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (mode == BLADERF_TUNING_MODE_FPGA &&
+ !have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) {
+ log_debug("The loaded FPGA version (%u.%u.%u) does not support the "
+ "provided tuning mode (%d)\n",
+ board_data->fpga_version.major, board_data->fpga_version.minor,
+ board_data->fpga_version.patch, mode);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ log_debug("Tuning mode: host\n");
+ break;
+ case BLADERF_TUNING_MODE_FPGA:
+ log_debug("Tuning mode: FPGA\n");
+ break;
+ default:
+ assert(!"Invalid tuning mode.");
+ return BLADERF_ERR_INVAL;
+ }
+
+ board_data->tuning_mode = mode;
+
+ return 0;
+}
+
+static int bladerf1_get_tuning_mode(struct bladerf *dev, bladerf_tuning_mode *mode)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ *mode = board_data->tuning_mode;
+
+ return 0;
+}
+
+/******************************************************************************/
+/* Loopback */
+/******************************************************************************/
+
+static int bladerf1_get_loopback_modes(
+ struct bladerf *dev, struct bladerf_loopback_modes const **modes)
+{
+ if (modes != NULL) {
+ *modes = bladerf1_loopback_modes;
+ }
+
+ return ARRAY_SIZE(bladerf1_loopback_modes);
+}
+
+static int bladerf1_set_loopback(struct bladerf *dev, bladerf_loopback l)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (l == BLADERF_LB_FIRMWARE) {
+ /* Firmware loopback was fully implemented in FW v1.7.1
+ * (1.7.0 could enable it, but 1.7.1 also allowed readback,
+ * so we'll enforce 1.7.1 here. */
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FW_LOOPBACK)) {
+ log_warning("Firmware v1.7.1 or later is required "
+ "to use firmware loopback.\n\n");
+ status = BLADERF_ERR_UPDATE_FW;
+ return status;
+ } else {
+ /* Samples won't reach the LMS when the device is in firmware
+ * loopback mode. By placing the LMS into a loopback mode, we ensure
+ * that the PAs will be disabled, and remain enabled across
+ * frequency changes.
+ */
+ status = lms_set_loopback_mode(dev, BLADERF_LB_RF_LNA3);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->backend->set_firmware_loopback(dev, true);
+ }
+ } else {
+ /* If applicable, ensure FW loopback is disabled */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FW_LOOPBACK)) {
+ bool fw_lb_enabled = false;
+
+ /* Query first, as the implementation of setting the mode
+ * may interrupt running streams. The API don't guarantee that
+ * switching loopback modes on the fly to work, but we can at least
+ * try to avoid unnecessarily interrupting things...*/
+ status = dev->backend->get_firmware_loopback(dev, &fw_lb_enabled);
+ if (status != 0) {
+ return status;
+ }
+
+ if (fw_lb_enabled) {
+ status = dev->backend->set_firmware_loopback(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+ }
+
+ status = lms_set_loopback_mode(dev, l);
+ }
+
+ return status;
+}
+
+static int bladerf1_get_loopback(struct bladerf *dev, bladerf_loopback *l)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ *l = BLADERF_LB_NONE;
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FW_LOOPBACK)) {
+ bool fw_lb_enabled;
+ status = dev->backend->get_firmware_loopback(dev, &fw_lb_enabled);
+ if (status == 0 && fw_lb_enabled) {
+ *l = BLADERF_LB_FIRMWARE;
+ }
+ }
+
+ if (*l == BLADERF_LB_NONE) {
+ status = lms_get_loopback_mode(dev, l);
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Sample RX FPGA Mux */
+/******************************************************************************/
+
+static int bladerf1_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mode)
+{
+ uint32_t rx_mux_val;
+ uint32_t config_gpio;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ /* Validate desired mux mode */
+ switch (mode) {
+ case BLADERF_RX_MUX_BASEBAND:
+ case BLADERF_RX_MUX_12BIT_COUNTER:
+ case BLADERF_RX_MUX_32BIT_COUNTER:
+ case BLADERF_RX_MUX_DIGITAL_LOOPBACK:
+ rx_mux_val = ((uint32_t) mode) << BLADERF_GPIO_RX_MUX_SHIFT;
+ break;
+
+ default:
+ log_debug("Invalid RX mux mode setting passed to %s(): %d\n",
+ mode, __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = dev->backend->config_gpio_read(dev, &config_gpio);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Clear out and assign the associated RX mux bits */
+ config_gpio &= ~BLADERF_GPIO_RX_MUX_MASK;
+ config_gpio |= rx_mux_val;
+
+ return dev->backend->config_gpio_write(dev, config_gpio);
+}
+
+static int bladerf1_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mode)
+{
+ bladerf_rx_mux val;
+ uint32_t config_gpio;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ status = dev->backend->config_gpio_read(dev, &config_gpio);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Extract RX mux bits */
+ config_gpio &= BLADERF_GPIO_RX_MUX_MASK;
+ config_gpio >>= BLADERF_GPIO_RX_MUX_SHIFT;
+ val = (bladerf_rx_mux) (config_gpio);
+
+ /* Enure it's a valid/supported value */
+ switch (val) {
+ case BLADERF_RX_MUX_BASEBAND:
+ case BLADERF_RX_MUX_12BIT_COUNTER:
+ case BLADERF_RX_MUX_32BIT_COUNTER:
+ case BLADERF_RX_MUX_DIGITAL_LOOPBACK:
+ *mode = val;
+ break;
+
+ default:
+ *mode = BLADERF_RX_MUX_INVALID;
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("Invalid rx mux mode %d read from config gpio\n", val);
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level VCTCXO Tamer Mode */
+/******************************************************************************/
+
+static int bladerf1_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_VCTCXO_TAMING_MODE)) {
+ log_debug("FPGA %s does not support VCTCXO taming via an input source\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dev->backend->set_vctcxo_tamer_mode(dev, mode);
+}
+
+static int bladerf1_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_VCTCXO_TAMING_MODE)) {
+ log_debug("FPGA %s does not support VCTCXO taming via an input source\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dev->backend->get_vctcxo_tamer_mode(dev, mode);
+}
+
+/******************************************************************************/
+/* Low-level VCTCXO Trim DAC access */
+/******************************************************************************/
+
+static int bladerf1_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ *trim = board_data->dac_trim;
+
+ return 0;
+}
+
+static int bladerf1_trim_dac_read(struct bladerf *dev, uint16_t *trim)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_VCTCXO_TRIMDAC_READ)) {
+ log_debug("FPGA %s does not support VCTCXO trimdac readback.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dac161s055_read(dev, trim);
+}
+
+static int bladerf1_trim_dac_write(struct bladerf *dev, uint16_t trim)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dac161s055_write(dev, trim);
+}
+
+/******************************************************************************/
+/* Low-level Trigger control access */
+/******************************************************************************/
+
+static int bladerf1_read_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t *val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return fpga_trigger_read(dev, ch, trigger, val);
+}
+
+static int bladerf1_write_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return fpga_trigger_write(dev, ch, trigger, val);
+}
+
+/******************************************************************************/
+/* Low-level Wishbone Master access */
+/******************************************************************************/
+
+static int bladerf1_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->wishbone_master_read(dev, addr, data);
+}
+
+static int bladerf1_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->wishbone_master_write(dev, addr, data);
+}
+
+/******************************************************************************/
+/* Low-level Configuration GPIO access */
+/******************************************************************************/
+
+static int bladerf1_config_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->config_gpio_read(dev, val);
+}
+
+static int bladerf1_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->config_gpio_write(dev, val);
+}
+
+/******************************************************************************/
+/* Low-level SPI Flash access */
+/******************************************************************************/
+
+static int bladerf1_erase_flash(struct bladerf *dev, uint32_t erase_block, uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_erase(dev, erase_block, count);
+}
+
+static int bladerf1_read_flash(struct bladerf *dev, uint8_t *buf,
+ uint32_t page, uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_read(dev, buf, page, count);
+}
+
+static int bladerf1_write_flash(struct bladerf *dev, const uint8_t *buf,
+ uint32_t page, uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_write(dev, buf, page, count);
+}
+
+/******************************************************************************/
+/* Expansion support */
+/******************************************************************************/
+
+static int bladerf1_expansion_attach(struct bladerf *dev, bladerf_xb xb)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ bladerf_xb attached;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ status = dev->board->expansion_get_attached(dev, &attached);
+ if (status != 0) {
+ return status;
+ }
+
+ if (xb != attached && attached != BLADERF_XB_NONE) {
+ log_debug("%s: Switching XB types is not supported.\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (xb == BLADERF_XB_100) {
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_MASKED_XBIO_WRITE)) {
+ log_debug("%s: XB100 support requires FPGA v0.4.1 or later.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ log_verbose("Attaching XB100\n");
+ status = xb100_attach(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Enabling XB100\n");
+ status = xb100_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Initializing XB100\n");
+ status = xb100_init(dev);
+ if (status != 0) {
+ return status;
+ }
+ } else if (xb == BLADERF_XB_200) {
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_XB200)) {
+ log_debug("%s: XB200 support requires FPGA v0.0.5 or later\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UPDATE_FPGA;
+ }
+
+ log_verbose("Attaching XB200\n");
+ status = xb200_attach(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Enabling XB200\n");
+ status = xb200_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Initializing XB200\n");
+ status = xb200_init(dev);
+ if (status != 0) {
+ return status;
+ }
+ } else if (xb == BLADERF_XB_300) {
+ log_verbose("Attaching XB300\n");
+ status = xb300_attach(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Enabling XB300\n");
+ status = xb300_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Initializing XB300\n");
+ status = xb300_init(dev);
+ if (status != 0) {
+ return status;
+ }
+ } else if (xb == BLADERF_XB_NONE) {
+ log_debug("%s: Disabling an attached XB is not supported.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ } else {
+ log_debug("%s: Unknown xb type: %d\n", __FUNCTION__, xb);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Cache what we have attached in our device handle to alleviate the
+ * need to go read the device state */
+ dev->xb = xb;
+
+ return 0;
+}
+
+static int bladerf1_expansion_get_attached(struct bladerf *dev, bladerf_xb *xb)
+{
+ int status;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ switch (dev->xb) {
+ case BLADERF_XB_NONE:
+ case BLADERF_XB_100:
+ case BLADERF_XB_200:
+ case BLADERF_XB_300:
+ *xb = dev->xb;
+ status = 0;
+ break;
+ default:
+ log_debug("Device handle contains invalid XB id: %d\n", dev->xb);
+ status = BLADERF_ERR_UNEXPECTED;
+ break;
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Board binding */
+/******************************************************************************/
+
+const struct board_fns bladerf1_board_fns = {
+ FIELD_INIT(.matches, bladerf1_matches),
+ FIELD_INIT(.open, bladerf1_open),
+ FIELD_INIT(.close, bladerf1_close),
+ FIELD_INIT(.device_speed, bladerf1_device_speed),
+ FIELD_INIT(.get_serial, bladerf1_get_serial),
+ FIELD_INIT(.get_fpga_size, bladerf1_get_fpga_size),
+ FIELD_INIT(.get_fpga_bytes, bladerf1_get_fpga_bytes),
+ FIELD_INIT(.get_flash_size, bladerf1_get_flash_size),
+ FIELD_INIT(.is_fpga_configured, bladerf1_is_fpga_configured),
+ FIELD_INIT(.get_fpga_source, bladerf1_get_fpga_source),
+ FIELD_INIT(.get_capabilities, bladerf1_get_capabilities),
+ FIELD_INIT(.get_channel_count, bladerf1_get_channel_count),
+ FIELD_INIT(.get_fpga_version, bladerf1_get_fpga_version),
+ FIELD_INIT(.get_fw_version, bladerf1_get_fw_version),
+ FIELD_INIT(.set_gain, bladerf1_set_gain),
+ FIELD_INIT(.get_gain, bladerf1_get_gain),
+ FIELD_INIT(.set_gain_mode, bladerf1_set_gain_mode),
+ FIELD_INIT(.get_gain_mode, bladerf1_get_gain_mode),
+ FIELD_INIT(.get_gain_modes, bladerf1_get_gain_modes),
+ FIELD_INIT(.get_gain_range, bladerf1_get_gain_range),
+ FIELD_INIT(.set_gain_stage, bladerf1_set_gain_stage),
+ FIELD_INIT(.get_gain_stage, bladerf1_get_gain_stage),
+ FIELD_INIT(.get_gain_stage_range, bladerf1_get_gain_stage_range),
+ FIELD_INIT(.get_gain_stages, bladerf1_get_gain_stages),
+ FIELD_INIT(.set_sample_rate, bladerf1_set_sample_rate),
+ FIELD_INIT(.set_rational_sample_rate, bladerf1_set_rational_sample_rate),
+ FIELD_INIT(.get_sample_rate, bladerf1_get_sample_rate),
+ FIELD_INIT(.get_sample_rate_range, bladerf1_get_sample_rate_range),
+ FIELD_INIT(.get_rational_sample_rate, bladerf1_get_rational_sample_rate),
+ FIELD_INIT(.set_bandwidth, bladerf1_set_bandwidth),
+ FIELD_INIT(.get_bandwidth, bladerf1_get_bandwidth),
+ FIELD_INIT(.get_bandwidth_range, bladerf1_get_bandwidth_range),
+ FIELD_INIT(.get_frequency, bladerf1_get_frequency),
+ FIELD_INIT(.set_frequency, bladerf1_set_frequency),
+ FIELD_INIT(.get_frequency_range, bladerf1_get_frequency_range),
+ FIELD_INIT(.select_band, bladerf1_select_band),
+ FIELD_INIT(.set_rf_port, bladerf1_set_rf_port),
+ FIELD_INIT(.get_rf_port, bladerf1_get_rf_port),
+ FIELD_INIT(.get_rf_ports, bladerf1_get_rf_ports),
+ FIELD_INIT(.get_quick_tune, bladerf1_get_quick_tune),
+ FIELD_INIT(.schedule_retune, bladerf1_schedule_retune),
+ FIELD_INIT(.cancel_scheduled_retunes, bladerf1_cancel_scheduled_retunes),
+ FIELD_INIT(.get_correction, bladerf1_get_correction),
+ FIELD_INIT(.set_correction, bladerf1_set_correction),
+ FIELD_INIT(.trigger_init, bladerf1_trigger_init),
+ FIELD_INIT(.trigger_arm, bladerf1_trigger_arm),
+ FIELD_INIT(.trigger_fire, bladerf1_trigger_fire),
+ FIELD_INIT(.trigger_state, bladerf1_trigger_state),
+ FIELD_INIT(.enable_module, bladerf1_enable_module),
+ FIELD_INIT(.init_stream, bladerf1_init_stream),
+ FIELD_INIT(.stream, bladerf1_stream),
+ FIELD_INIT(.submit_stream_buffer, bladerf1_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, bladerf1_deinit_stream),
+ FIELD_INIT(.set_stream_timeout, bladerf1_set_stream_timeout),
+ FIELD_INIT(.get_stream_timeout, bladerf1_get_stream_timeout),
+ FIELD_INIT(.sync_config, bladerf1_sync_config),
+ FIELD_INIT(.sync_tx, bladerf1_sync_tx),
+ FIELD_INIT(.sync_rx, bladerf1_sync_rx),
+ FIELD_INIT(.get_timestamp, bladerf1_get_timestamp),
+ FIELD_INIT(.load_fpga, bladerf1_load_fpga),
+ FIELD_INIT(.flash_fpga, bladerf1_flash_fpga),
+ FIELD_INIT(.erase_stored_fpga, bladerf1_erase_stored_fpga),
+ FIELD_INIT(.flash_firmware, bladerf1_flash_firmware),
+ FIELD_INIT(.device_reset, bladerf1_device_reset),
+ FIELD_INIT(.set_tuning_mode, bladerf1_set_tuning_mode),
+ FIELD_INIT(.get_tuning_mode, bladerf1_get_tuning_mode),
+ FIELD_INIT(.get_loopback_modes, bladerf1_get_loopback_modes),
+ FIELD_INIT(.set_loopback, bladerf1_set_loopback),
+ FIELD_INIT(.get_loopback, bladerf1_get_loopback),
+ FIELD_INIT(.get_rx_mux, bladerf1_get_rx_mux),
+ FIELD_INIT(.set_rx_mux, bladerf1_set_rx_mux),
+ FIELD_INIT(.set_vctcxo_tamer_mode, bladerf1_set_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_tamer_mode, bladerf1_get_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_trim, bladerf1_get_vctcxo_trim),
+ FIELD_INIT(.trim_dac_read, bladerf1_trim_dac_read),
+ FIELD_INIT(.trim_dac_write, bladerf1_trim_dac_write),
+ FIELD_INIT(.read_trigger, bladerf1_read_trigger),
+ FIELD_INIT(.write_trigger, bladerf1_write_trigger),
+ FIELD_INIT(.wishbone_master_read, bladerf1_wishbone_master_read),
+ FIELD_INIT(.wishbone_master_write, bladerf1_wishbone_master_write),
+ FIELD_INIT(.config_gpio_read, bladerf1_config_gpio_read),
+ FIELD_INIT(.config_gpio_write, bladerf1_config_gpio_write),
+ FIELD_INIT(.erase_flash, bladerf1_erase_flash),
+ FIELD_INIT(.read_flash, bladerf1_read_flash),
+ FIELD_INIT(.write_flash, bladerf1_write_flash),
+ FIELD_INIT(.expansion_attach, bladerf1_expansion_attach),
+ FIELD_INIT(.expansion_get_attached, bladerf1_expansion_get_attached),
+ FIELD_INIT(.name, "bladerf1"),
+};
+
+/******************************************************************************
+ ******************************************************************************
+ * bladeRF1-specific Functions *
+ ******************************************************************************
+ ******************************************************************************/
+
+/******************************************************************************/
+/* TX Gain */
+/******************************************************************************/
+
+int bladerf_set_txvga2(struct bladerf *dev, int gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_txvga2_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_txvga2(struct bladerf *dev, int *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_txvga2_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_txvga1(struct bladerf *dev, int gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_txvga1_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_txvga1(struct bladerf *dev, int *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_txvga1_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* RX Gain */
+/******************************************************************************/
+
+int bladerf_set_lna_gain(struct bladerf *dev, bladerf_lna_gain gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_lna_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_lna_gain(struct bladerf *dev, bladerf_lna_gain *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_lna_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_rxvga1(struct bladerf *dev, int gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_rxvga1_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_rxvga1(struct bladerf *dev, int *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_rxvga1_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_rxvga2(struct bladerf *dev, int gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_rxvga2_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_rxvga2(struct bladerf *dev, int *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_rxvga2_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* LPF Bypass */
+/******************************************************************************/
+
+int bladerf_set_lpf_mode(struct bladerf *dev, bladerf_channel ch,
+ bladerf_lpf_mode mode)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_lpf_set_mode(dev, ch, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_lpf_mode(struct bladerf *dev, bladerf_channel ch,
+ bladerf_lpf_mode *mode)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_lpf_get_mode(dev, ch, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Sample Internal/Direct */
+/******************************************************************************/
+
+int bladerf_get_sampling(struct bladerf *dev, bladerf_sampling *sampling)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_get_sampling(dev, sampling);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_sampling(struct bladerf *dev, bladerf_sampling sampling)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_select_sampling(dev, sampling);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* SMB Clock Configuration */
+/******************************************************************************/
+
+int bladerf_get_smb_mode(struct bladerf *dev, bladerf_smb_mode *mode)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = smb_clock_get_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_smb_mode(struct bladerf *dev, bladerf_smb_mode mode)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = smb_clock_set_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_smb_frequency(struct bladerf *dev, unsigned int *rate)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = si5338_get_smb_freq(dev, rate);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_smb_frequency(struct bladerf *dev, uint32_t rate, uint32_t *actual)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = si5338_set_smb_freq(dev, rate, actual);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_rational_smb_frequency(struct bladerf *dev, struct bladerf_rational_rate *rate)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = si5338_get_rational_smb_freq(dev, rate);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_rational_smb_frequency(struct bladerf *dev, struct bladerf_rational_rate *rate, struct bladerf_rational_rate *actual)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = si5338_set_rational_smb_freq(dev, rate, actual);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* DC Calibration */
+/******************************************************************************/
+
+int bladerf_calibrate_dc(struct bladerf *dev, bladerf_cal_module module)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_calibrate_dc(dev, module);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level Si5338 access */
+/******************************************************************************/
+
+int bladerf_si5338_read(struct bladerf *dev, uint8_t address, uint8_t *val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->si5338_read(dev,address,val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_si5338_write(struct bladerf *dev, uint8_t address, uint8_t val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->si5338_write(dev,address,val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level LMS access */
+/******************************************************************************/
+
+int bladerf_lms_read(struct bladerf *dev, uint8_t address, uint8_t *val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->lms_read(dev,address,val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_lms_write(struct bladerf *dev, uint8_t address, uint8_t val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->lms_write(dev,address,val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_lms_set_dc_cals(struct bladerf *dev,
+ const struct bladerf_lms_dc_cals *dc_cals)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_set_dc_cals(dev, dc_cals);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_lms_get_dc_cals(struct bladerf *dev,
+ struct bladerf_lms_dc_cals *dc_cals)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_get_dc_cals(dev, dc_cals);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level XB SPI access */
+/******************************************************************************/
+
+int bladerf_xb_spi_write(struct bladerf *dev, uint32_t val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->xb_spi(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/calibration.c b/Radio/HW/BladeRF/src/board/bladerf1/calibration.c
new file mode 100644
index 0000000..4918fca
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/calibration.c
@@ -0,0 +1,518 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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
+ */
+
+/* The binary DC calibration data is stored as follows. All values are
+ * little-endian byte order.
+ *
+ * 0x0000 [uint16_t: Fixed value of 0x9a51]
+ * 0x0002 [uint32_t: Reserved. Set to 0x00000000]
+ * 0x0006 [uint32_t: Table format version]
+ * 0x000a [uint32_t: Number of entries]
+ * 0x000e [uint8_t: LMS LPF tuning register value]
+ * 0x000f [uint8_t: LMS TX LPF I register value]
+ * 0x0010 [uint8_t: LMS TX LPF Q register value]
+ * 0x0011 [uint8_t: LMS RX LPF I register value]
+ * 0x0012 [uint8_t: LMS RX LPF Q register value]
+ * 0x0013 [uint8_t: LMS DC REF register value]
+ * 0x0014 [uint8_t: LMS RX VGA2a I register value]
+ * 0x0015 [uint8_t: LMS RX VGA2a Q register value]
+ * 0x0016 [uint8_t: LMS RX VGA2b I register value]
+ * 0x0017 [uint8_t: LMS RX VGA2b Q register value]
+ * 0x0018 [Start of table entries]
+ *
+ * Where a table entry is:
+ * [uint32_t: Frequency]
+ * [int16_t: DC I correction value]
+ * [int16_t: DC Q correction value]
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <float.h>
+
+#include "host_config.h"
+#include "minmax.h"
+
+#include "calibration.h"
+
+#ifdef TEST_DC_CAL_TABLE
+# include <stdio.h>
+# define SHORT_SEARCH 4
+# define WARN(str) fprintf(stderr, str)
+#else
+# include "log.h"
+# define SHORT_SEARCH 10
+# define WARN(str) log_warning(str)
+#endif
+
+#define DC_CAL_TBL_MAGIC 0x1ab1
+
+#define DC_CAL_TBL_META_SIZE 0x18
+#define DC_CAL_TBL_ENTRY_SIZE (sizeof(uint32_t) + 2 * sizeof(int16_t))
+#define DC_CAL_TBL_MIN_SIZE (DC_CAL_TBL_META_SIZE + DC_CAL_TBL_ENTRY_SIZE)
+
+static inline bool entry_matches(const struct dc_cal_tbl *tbl,
+ unsigned int entry_idx, unsigned int freq)
+{
+ if (entry_idx >= (tbl->n_entries - 1)) {
+ return freq >= tbl->entries[entry_idx].freq;
+ } else {
+ return freq >= tbl->entries[entry_idx].freq &&
+ freq < tbl->entries[entry_idx + 1].freq;
+ }
+}
+
+static unsigned int find_entry(const struct dc_cal_tbl *tbl,
+ unsigned int curr_idx,
+ unsigned int min_idx, unsigned int max_idx,
+ unsigned int freq, bool *hit_limit)
+{
+ /* Converged to a single entry - this is the best we can do */
+ if ((max_idx < min_idx) || (max_idx == min_idx && max_idx == curr_idx)) {
+ *hit_limit = true;
+ return curr_idx;
+ }
+
+ if (!entry_matches(tbl, curr_idx, freq)) {
+ if (tbl->entries[curr_idx].freq > freq) {
+ if (curr_idx > 0) {
+ max_idx = (curr_idx - 1);
+ } else {
+ /* Lower limit hit - return first entry */
+ *hit_limit = true;
+ return 0;
+ }
+ } else {
+ if (curr_idx < (tbl->n_entries - 1)) {
+ min_idx = curr_idx + 1;
+ } else {
+ /* Upper limit hit - return last entry */
+ *hit_limit = true;
+ return tbl->n_entries - 1;
+ }
+ }
+
+ curr_idx = min_idx + (max_idx - min_idx) / 2;
+
+ return find_entry(tbl, curr_idx, min_idx, max_idx, freq, hit_limit);
+ } else {
+ return curr_idx;
+ }
+}
+
+unsigned int dc_cal_tbl_lookup(const struct dc_cal_tbl *tbl, unsigned int freq)
+{
+ unsigned int ret = 0;
+ bool limit = false; /* Hit a limit before finding a match */
+
+ /* First check if we're at a nearby change. This is generally the case
+ * when the frequecy change */
+ if (tbl->n_entries > SHORT_SEARCH) {
+ const unsigned int min_idx =
+ (unsigned int) i64_max(0, tbl->curr_idx - (int64_t)SHORT_SEARCH / 2);
+
+ const unsigned int max_idx =
+ (unsigned int) i64_min(tbl->n_entries - 1, tbl->curr_idx + SHORT_SEARCH / 2);
+
+ ret = find_entry(tbl, tbl->curr_idx, min_idx, max_idx, freq, &limit);
+ if (!limit) {
+ return ret;
+ }
+ }
+
+ return find_entry(tbl, tbl->curr_idx, 0, tbl->n_entries - 1, freq, &limit);
+}
+
+struct dc_cal_tbl * dc_cal_tbl_load(const uint8_t *buf, size_t buf_len)
+{
+ struct dc_cal_tbl *ret;
+ uint32_t i;
+ uint16_t magic;
+
+ if (buf_len < DC_CAL_TBL_MIN_SIZE) {
+ return NULL;
+ }
+
+ memcpy(&magic, buf, sizeof(magic));
+ if (LE16_TO_HOST(magic) != DC_CAL_TBL_MAGIC) {
+ log_debug("Invalid magic value in cal table: %d\n", magic);
+ return NULL;
+ }
+ buf += sizeof(magic);
+
+ ret = malloc(sizeof(ret[0]));
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ buf += sizeof(uint32_t); /* Skip reserved bytes */
+
+ memcpy(&ret->version, buf, sizeof(ret->version));
+ ret->version = LE32_TO_HOST(ret->version);
+ buf += sizeof(ret->version);
+
+ memcpy(&ret->n_entries, buf, sizeof(ret->n_entries));
+ ret->n_entries = LE32_TO_HOST(ret->n_entries);
+ buf += sizeof(ret->n_entries);
+
+ if (buf_len <
+ (DC_CAL_TBL_META_SIZE + DC_CAL_TBL_ENTRY_SIZE * ret->n_entries) ) {
+
+ free(ret);
+ return NULL;
+ }
+
+ ret->entries = malloc(sizeof(ret->entries[0]) * ret->n_entries);
+ if (ret->entries == NULL) {
+ free(ret);
+ return NULL;
+ }
+
+ ret->reg_vals.lpf_tuning = *buf++;
+ ret->reg_vals.tx_lpf_i = *buf++;
+ ret->reg_vals.tx_lpf_q = *buf++;
+ ret->reg_vals.rx_lpf_i = *buf++;
+ ret->reg_vals.rx_lpf_q = *buf++;
+ ret->reg_vals.dc_ref = *buf++;
+ ret->reg_vals.rxvga2a_i = *buf++;
+ ret->reg_vals.rxvga2a_q = *buf++;
+ ret->reg_vals.rxvga2b_i = *buf++;
+ ret->reg_vals.rxvga2b_q = *buf++;
+
+ ret->curr_idx = ret->n_entries / 2;
+ for (i = 0; i < ret->n_entries; i++) {
+ memcpy(&ret->entries[i].freq, buf, sizeof(uint32_t));
+ buf += sizeof(uint32_t);
+
+ memcpy(&ret->entries[i].dc_i, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].dc_q, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ ret->entries[i].freq = LE32_TO_HOST(ret->entries[i].freq);
+ ret->entries[i].dc_i = LE32_TO_HOST(ret->entries[i].dc_i);
+ ret->entries[i].dc_q = LE32_TO_HOST(ret->entries[i].dc_q);
+
+ if (ret->version >= 2) {
+ memcpy(&ret->entries[i].max_dc_i, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].max_dc_q, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].mid_dc_i, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].mid_dc_q, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].min_dc_i, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].min_dc_q, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ ret->entries[i].max_dc_i = LE32_TO_HOST(ret->entries[i].max_dc_i);
+ ret->entries[i].max_dc_q = LE32_TO_HOST(ret->entries[i].max_dc_q);
+ ret->entries[i].mid_dc_i = LE32_TO_HOST(ret->entries[i].mid_dc_i);
+ ret->entries[i].mid_dc_q = LE32_TO_HOST(ret->entries[i].mid_dc_q);
+ ret->entries[i].min_dc_i = LE32_TO_HOST(ret->entries[i].min_dc_i);
+ ret->entries[i].min_dc_q = LE32_TO_HOST(ret->entries[i].min_dc_q);
+ }
+ }
+
+ return ret;
+}
+
+int dc_cal_tbl_image_load(struct bladerf *dev,
+ struct dc_cal_tbl **tbl, const char *img_file)
+{
+ int status;
+ struct bladerf_image *img;
+
+ img = bladerf_alloc_image(dev, BLADERF_IMAGE_TYPE_INVALID, 0, 0);
+ if (img == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ status = bladerf_image_read(img, img_file);
+ if (status != 0) {
+ return status;
+ }
+
+ if (img->type == BLADERF_IMAGE_TYPE_RX_DC_CAL ||
+ img->type == BLADERF_IMAGE_TYPE_TX_DC_CAL) {
+ *tbl = dc_cal_tbl_load(img->data, img->length);
+ status = 0;
+ } else {
+ status = BLADERF_ERR_INVAL;
+ }
+
+ bladerf_free_image(img);
+
+ return status;
+}
+
+/* Interpolate a y value given two points and a desired x value
+ *
+ * y = interp( (x0, y0), (x1, y1), x )
+ *
+ * Returns
+ */
+static inline unsigned int interp(unsigned int x0, unsigned int y0,
+ unsigned int x1, unsigned int y1,
+ unsigned int x)
+{
+ const float num = (float) y1 - y0;
+ const float den = (float) x1 - x0;
+ const float m = den == 0 ? FLT_MAX : num / den;
+ const float y = (x - x0) * m + y0;
+
+ return (unsigned int) y;
+}
+
+static inline void dc_cal_interp_entry(const struct dc_cal_tbl *tbl,
+ unsigned int idx_low,
+ unsigned int idx_high,
+ unsigned int freq,
+ struct dc_cal_entry *entry)
+{
+ const unsigned int f_low = tbl->entries[idx_low].freq;
+ const unsigned int f_high = tbl->entries[idx_high].freq;
+
+#define ENTRY_VAR(x) \
+ entry->x = (int16_t) interp(f_low, tbl->entries[idx_low].x, \
+ f_high, tbl->entries[idx_low].x, \
+ freq)
+
+ ENTRY_VAR(dc_i);
+ ENTRY_VAR(dc_q);
+
+ ENTRY_VAR(max_dc_i);
+ ENTRY_VAR(max_dc_q);
+ ENTRY_VAR(mid_dc_i);
+ ENTRY_VAR(mid_dc_q);
+ ENTRY_VAR(min_dc_i);
+ ENTRY_VAR(min_dc_q);
+}
+
+void dc_cal_tbl_entry(const struct dc_cal_tbl *tbl, unsigned int freq,
+ struct dc_cal_entry *entry)
+{
+ const unsigned int idx = dc_cal_tbl_lookup(tbl, freq);
+
+ if (tbl->entries[idx].freq == freq) {
+ memcpy(entry, &tbl->entries[idx], sizeof(struct dc_cal_entry));
+ } else if (idx == (tbl->n_entries - 1)) {
+ dc_cal_interp_entry(tbl, idx - 1, idx, freq, entry);
+ } else {
+ dc_cal_interp_entry(tbl, idx, idx + 1, freq, entry);
+ }
+}
+
+void dc_cal_tbl_free(struct dc_cal_tbl **tbl)
+{
+ if (*tbl != NULL) {
+ free((*tbl)->entries);
+ free(*tbl);
+ *tbl = NULL;
+ }
+}
+
+#ifdef TEST_DC_CAL_TABLE
+
+#define ENTRY(f) { f, 0, 0 }
+
+#define TBL(entries, curr_idx) { \
+ entries != NULL ? sizeof(entries) / sizeof(entries[0]) : 0, \
+ curr_idx, entries \
+}
+
+#define TEST_CASE(exp_idx, entries, default_idx, freq) { \
+ TBL(entries, default_idx), \
+ freq, \
+ exp_idx, \
+ exp_idx > -2, \
+}
+
+
+struct dc_cal_entry unsorted_entries[] = {
+ ENTRY(300e6), ENTRY(400e6), ENTRY(320e6),
+ ENTRY(310e6), ENTRY(550e6), ENTRY(500e6)
+};
+
+struct dc_cal_entry single_entry[] = { ENTRY(2.4e9) };
+
+struct dc_cal_entry three_entries[] = {
+ ENTRY(300e6), ENTRY(1.5e9), ENTRY(2.4e9)
+};
+
+struct dc_cal_entry entries[] = {
+ ENTRY(300e6), ENTRY(400e6), ENTRY(500e6), ENTRY(600e6), ENTRY(700e6),
+ ENTRY(800e6), ENTRY(900e6), ENTRY(1.0e9), ENTRY(1.1e9), ENTRY(1.2e9),
+ ENTRY(1.3e9), ENTRY(1.4e9), ENTRY(1.5e9), ENTRY(1.6e9), ENTRY(1.7e9),
+ ENTRY(1.8e9), ENTRY(1.9e9), ENTRY(2.0e9), ENTRY(2.1e9), ENTRY(2.2e9),
+ ENTRY(2.3e9), ENTRY(2.4e9), ENTRY(2.5e9), ENTRY(2.6e9), ENTRY(2.7e9),
+ ENTRY(2.8e9), ENTRY(2.9e9), ENTRY(3.0e9), ENTRY(3.1e9), ENTRY(3.2e9),
+ ENTRY(3.3e9), ENTRY(3.4e9), ENTRY(3.5e9), ENTRY(3.6e9), ENTRY(3.7e9),
+ ENTRY(3.8e9),
+};
+
+struct test {
+ const struct dc_cal_tbl tbl;
+ unsigned int freq;
+ int expected_idx;
+ bool check_result;
+} tests[] = {
+ /* Invalid due to unsorted entries. These won't neccessarily work,
+ * but shouldn't crash */
+ TEST_CASE(-2, unsorted_entries, 0, 300e6),
+ TEST_CASE(-2, unsorted_entries, 1, 300e6),
+ TEST_CASE(-2, unsorted_entries, 2, 300e6),
+ TEST_CASE(-2, unsorted_entries, 3, 300e6),
+ TEST_CASE(-2, unsorted_entries, 4, 300e6),
+ TEST_CASE(-2, unsorted_entries, 5, 300e6),
+ TEST_CASE(-2, unsorted_entries, 0, 310e6),
+ TEST_CASE(-2, unsorted_entries, 1, 401e6),
+ TEST_CASE(-2, unsorted_entries, 2, 550e6),
+ TEST_CASE(-2, unsorted_entries, 3, 100e5),
+ TEST_CASE(-2, unsorted_entries, 4, 3.8e9),
+ TEST_CASE(-2, unsorted_entries, 5, 321e6),
+
+ /* Single entry - should just return whatever is availble */
+ TEST_CASE(0, single_entry, 0, 300e6),
+ TEST_CASE(0, single_entry, 0, 2.4e9),
+ TEST_CASE(0, single_entry, 0, 3.8e9),
+
+ /* Three entries, exact matches */
+ TEST_CASE(0, three_entries, 0, 300e6),
+ TEST_CASE(0, three_entries, 1, 300e6),
+ TEST_CASE(0, three_entries, 2, 300e6),
+ TEST_CASE(1, three_entries, 0, 1.5e9),
+ TEST_CASE(1, three_entries, 1, 1.5e9),
+ TEST_CASE(1, three_entries, 2, 1.5e9),
+ TEST_CASE(2, three_entries, 0, 2.4e9),
+ TEST_CASE(2, three_entries, 1, 2.4e9),
+ TEST_CASE(2, three_entries, 2, 2.4e9),
+
+ /* Three entries, non-exact matches */
+ TEST_CASE(0, three_entries, 0, 435e6),
+ TEST_CASE(0, three_entries, 1, 435e6),
+ TEST_CASE(0, three_entries, 2, 435e6),
+ TEST_CASE(1, three_entries, 0, 2.0e9),
+ TEST_CASE(1, three_entries, 1, 2.0e9),
+ TEST_CASE(1, three_entries, 2, 2.0e9),
+ TEST_CASE(2, three_entries, 0, 3.8e9),
+ TEST_CASE(2, three_entries, 1, 3.8e9),
+ TEST_CASE(2, three_entries, 2, 3.8e9),
+
+ /* Larger table, lower limits */
+ TEST_CASE(0, entries, 0, 0),
+ TEST_CASE(0, entries, 0, 300e6),
+ TEST_CASE(0, entries, 0, 350e6),
+ TEST_CASE(0, entries, 17, 0),
+ TEST_CASE(0, entries, 17, 300e6),
+ TEST_CASE(0, entries, 17, 350e6),
+ TEST_CASE(0, entries, 35, 0),
+ TEST_CASE(0, entries, 35, 300e6),
+ TEST_CASE(0, entries, 35, 350e6),
+
+ /* Larger table, upper limits */
+ TEST_CASE(35, entries, 0, 3.8e9),
+ TEST_CASE(35, entries, 0, 4e9),
+ TEST_CASE(35, entries, 17, 3.8e9),
+ TEST_CASE(35, entries, 17, 4e9),
+ TEST_CASE(35, entries, 35, 3.8e9),
+ TEST_CASE(35, entries, 35, 4e9),
+
+ /* Larger table, exact matches */
+ TEST_CASE(4, entries, 0, 700e6),
+ TEST_CASE(4, entries, 4, 700e6),
+ TEST_CASE(4, entries, 15, 700e6),
+ TEST_CASE(4, entries, 30, 700e6),
+ TEST_CASE(4, entries, 35, 700e6),
+
+ TEST_CASE(12, entries, 0, 1.5e9),
+ TEST_CASE(12, entries, 12, 1.5e9),
+ TEST_CASE(12, entries, 15, 1.5e9),
+ TEST_CASE(12, entries, 30, 1.5e9),
+ TEST_CASE(12, entries, 35, 1.5e9),
+
+ TEST_CASE(30, entries, 0, 3.3e9),
+ TEST_CASE(30, entries, 10, 3.3e9),
+ TEST_CASE(30, entries, 20, 3.3e9),
+ TEST_CASE(30, entries, 30, 3.3e9),
+ TEST_CASE(30, entries, 35, 3.3e9),
+
+ /* Larger table, approximate matches */
+ TEST_CASE(4, entries, 0, 701e6),
+ TEST_CASE(4, entries, 4, 701e6),
+ TEST_CASE(4, entries, 15, 701e6),
+ TEST_CASE(4, entries, 30, 701e6),
+ TEST_CASE(4, entries, 35, 701e6),
+
+ TEST_CASE(12, entries, 0, 1.59e9),
+ TEST_CASE(12, entries, 12, 1.59e9),
+ TEST_CASE(12, entries, 15, 1.59e9),
+ TEST_CASE(12, entries, 30, 1.59e9),
+ TEST_CASE(12, entries, 35, 1.59e9),
+
+ TEST_CASE(30, entries, 0, 3.35e9),
+ TEST_CASE(30, entries, 10, 3.35e9),
+ TEST_CASE(30, entries, 20, 3.35e9),
+ TEST_CASE(30, entries, 30, 3.35e9),
+ TEST_CASE(30, entries, 35, 3.35e9),
+};
+
+static inline void print_entry(const struct dc_cal_tbl *t,
+ const char *prefix, int idx)
+{
+ if (idx >= 0) {
+ fprintf(stderr, "%s: %u Hz\n", prefix, t->entries[idx].freq);
+ } else {
+ fprintf(stderr, "%s: None (%d)\n", prefix, idx);
+ }
+}
+
+int main(void)
+{
+ unsigned int i;
+ unsigned int num_failures = 0;
+
+ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ const int expected_idx = tests[i].expected_idx;
+ const int entry_idx = dc_cal_tbl_lookup(&tests[i].tbl, tests[i].freq);
+
+ if (tests[i].check_result && entry_idx != expected_idx) {
+ fprintf(stderr, "Test case %u: failed.\n", i);
+ print_entry(&tests[i].tbl, " Got", entry_idx);
+ print_entry(&tests[i].tbl, " Expected", expected_idx);
+ num_failures++;
+ } else {
+ printf("Test case %u: passed.\n", i);
+ }
+ }
+
+ return num_failures;
+}
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/calibration.h b/Radio/HW/BladeRF/src/board/bladerf1/calibration.h
new file mode 100644
index 0000000..98e28a5
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/calibration.h
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 BLADERF1_CALIBRATION_H_
+#define BLADERF1_CALIBRATION_H_
+
+#include <stdint.h>
+
+#include <libbladeRF.h>
+
+struct dc_cal_entry {
+ unsigned int freq; /* Frequency (Hz) associated with this entry */
+ int16_t dc_i;
+ int16_t dc_q;
+
+ int16_t max_dc_i;
+ int16_t max_dc_q;
+ int16_t mid_dc_i;
+ int16_t mid_dc_q;
+ int16_t min_dc_i;
+ int16_t min_dc_q;
+};
+
+struct dc_cal_tbl {
+ uint32_t version;
+ uint32_t n_entries;
+ struct bladerf_lms_dc_cals reg_vals;
+
+ unsigned int curr_idx;
+ struct dc_cal_entry *entries; /* Sorted (increasing) by freq */
+};
+
+extern struct dc_cal_tbl rx_cal_test;
+
+/**
+ * Get the index of an (approximate) match from the specific dc cal table
+ *
+ * @param[in] tbl Table to search
+ * @param[in] freq Desired frequency
+ *
+ * @return index into tbl->entries[].
+ */
+unsigned int dc_cal_tbl_lookup(const struct dc_cal_tbl *tbl, unsigned int freq);
+
+/**
+ * Get the DC cal values associated with the specified frequencies. If the
+ * specified frequency is not in the table, the DC calibration values will
+ * be interpolated from surrounding entries.
+ *
+ * @param[in] tbl Table to search
+ * @param[in] freq Desired frequency
+ * @param[out] entry Found or interpolated DC calibration values
+ */
+void dc_cal_tbl_entry(const struct dc_cal_tbl *tbl,
+ unsigned int freq,
+ struct dc_cal_entry *entry);
+
+/**
+ * Load a DC calibration table from the provided data
+ *
+ * @param[in] buf Packed table data
+ * @param[in] len Length of packed data, in bytes
+ *
+ * @return Loaded DC calibration table, or NULL on error
+ */
+struct dc_cal_tbl *dc_cal_tbl_load(const uint8_t *buf, size_t buf_len);
+
+/**
+ * Load a DC calibration table from an image file
+ *
+ * @param[in] dev bladeRF device handle
+ * @param[out] tbl DC calibration Table
+ * @param[in] img_file Path to image file
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int dc_cal_tbl_image_load(struct bladerf *dev,
+ struct dc_cal_tbl **tbl, const char *img_file);
+
+/**
+ * Free a DC calibration table
+ *
+ * @param[inout] tbl Pointer to table to free
+ *
+ * The table pointer will be set to NULL after freeing it.
+ */
+void dc_cal_tbl_free(struct dc_cal_tbl **tbl);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/capabilities.c b/Radio/HW/BladeRF/src/board/bladerf1/capabilities.c
new file mode 100644
index 0000000..8454f5c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/capabilities.c
@@ -0,0 +1,118 @@
+/*
+ * 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 <inttypes.h>
+
+#include "log.h"
+#include "helpers/version.h"
+
+#include "capabilities.h"
+
+uint64_t bladerf1_get_fw_capabilities(const struct bladerf_version *fw_version)
+{
+ uint64_t capabilities = 0;
+
+ if (version_fields_greater_or_equal(fw_version, 1, 7, 1)) {
+ capabilities |= BLADERF_CAP_FW_LOOPBACK;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 1, 8, 0)) {
+ capabilities |= BLADERF_CAP_QUERY_DEVICE_READY;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 1, 9, 0)) {
+ capabilities |= BLADERF_CAP_READ_FW_LOG_ENTRY;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 3, 0)) {
+ capabilities |= BLADERF_CAP_FW_FLASH_ID;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 3, 1)) {
+ capabilities |= BLADERF_CAP_FW_FPGA_SOURCE;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 4, 0)) {
+ capabilities |= BLADERF_CAP_FW_SHORT_PACKET;
+ }
+
+ return capabilities;
+}
+
+uint64_t bladerf1_get_fpga_capabilities(const struct bladerf_version *fpga_version)
+{
+ uint64_t capabilities = 0;
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 0, 4)) {
+ capabilities |= BLADERF_CAP_UPDATED_DAC_ADDR;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 0, 5)) {
+ capabilities |= BLADERF_CAP_XB200;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 1, 0)) {
+ capabilities |= BLADERF_CAP_TIMESTAMPS;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 2, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_TUNING;
+ capabilities |= BLADERF_CAP_SCHEDULED_RETUNE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 3, 0)) {
+ capabilities |= BLADERF_CAP_PKT_HANDLER_FMT;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 3, 2)) {
+ capabilities |= BLADERF_CAP_VCTCXO_TRIMDAC_READ;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 4, 0)) {
+ capabilities |= BLADERF_CAP_ATOMIC_NINT_NFRAC_WRITE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 4, 1)) {
+ capabilities |= BLADERF_CAP_MASKED_XBIO_WRITE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 5, 0)) {
+ capabilities |= BLADERF_CAP_VCTCXO_TAMING_MODE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 6, 0)) {
+ capabilities |= BLADERF_CAP_TRX_SYNC_TRIG;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 7, 0)) {
+ capabilities |= BLADERF_CAP_AGC_DC_LUT;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 12, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_PACKET_META;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 15, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_8BIT_SAMPLES;
+ }
+
+ return capabilities;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/capabilities.h b/Radio/HW/BladeRF/src/board/bladerf1/capabilities.h
new file mode 100644
index 0000000..5b2eb43
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/capabilities.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015-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
+ */
+
+/* This file defines device capabilities added across libbladeRF, FX3, and FPGA
+ * versions that we can check for */
+
+#ifndef BLADERF1_CAPABILITIES_H_
+#define BLADERF1_CAPABILITIES_H_
+
+#include <stdint.h>
+
+#include "board/board.h"
+#include "helpers/have_cap.h"
+
+/**
+ * Determine device's firmware capabilities.
+ *
+ * @param[in] fw_version Firmware version
+ *
+ * @return Capabilities bitmask
+ */
+uint64_t bladerf1_get_fw_capabilities(const struct bladerf_version *fw_version);
+
+/**
+ * Add capability bits based upon FPGA version stored in the device handle
+ *
+ * @param[in] fpga_version FPGA version
+ *
+ * @return Capabilities bitmask
+ */
+uint64_t bladerf1_get_fpga_capabilities(
+ const struct bladerf_version *fpga_version);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/compatibility.c b/Radio/HW/BladeRF/src/board/bladerf1/compatibility.c
new file mode 100644
index 0000000..6d76d3f
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/compatibility.c
@@ -0,0 +1,75 @@
+#include "host_config.h"
+
+#include "helpers/version.h"
+
+/* Firmware-FPGA compatibility tables
+ *
+ * This list should be kept in decending order, such that the most recent
+ * versions are first, and the last entry should contain the earliest version
+ * that libbladeRF supports.
+ */
+
+#define VERSION(major, minor, patch) { major, minor, patch, NULL }
+
+static const struct compat fw_compat[] = {
+ /* Firmware requires >= FPGA */
+ { VERSION(2, 4, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 3, 2), VERSION(0, 0, 2) },
+ { VERSION(2, 3, 1), VERSION(0, 0, 2) },
+ { VERSION(2, 3, 0), VERSION(0, 0, 2) },
+ { VERSION(2, 2, 0), VERSION(0, 0, 2) },
+ { VERSION(2, 1, 1), VERSION(0, 0, 2) },
+ { VERSION(2, 1, 0), VERSION(0, 0, 2) },
+ { VERSION(2, 0, 0), VERSION(0, 0, 2) },
+ { VERSION(1, 9, 1), VERSION(0, 0, 2) },
+ { VERSION(1, 9, 0), VERSION(0, 0, 2) },
+ { VERSION(1, 8, 1), VERSION(0, 0, 2) },
+ { VERSION(1, 8, 0), VERSION(0, 0, 2) },
+ { VERSION(1, 7, 1), VERSION(0, 0, 2) },
+ { VERSION(1, 7, 0), VERSION(0, 0, 2) },
+ { VERSION(1, 6, 1), VERSION(0, 0, 2) },
+ { VERSION(1, 6, 0), VERSION(0, 0, 1) },
+};
+
+const struct version_compat_table bladerf1_fw_compat_table = {fw_compat, ARRAY_SIZE(fw_compat)};
+
+static const struct compat fpga_compat[] = {
+ /* FPGA requires >= Firmware */
+ { VERSION(0, 15, 1), VERSION(2, 4, 0) },
+ { VERSION(0, 15, 0), VERSION(2, 4, 0) },
+ { VERSION(0, 14, 0), VERSION(2, 4, 0) },
+ { VERSION(0, 12, 0), VERSION(2, 2, 0) },
+ { VERSION(0, 11, 1), VERSION(2, 1, 0) },
+ { VERSION(0, 11, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 10, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 10, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 10, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 9, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 8, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 7, 3), VERSION(1, 6, 1) },
+ { VERSION(0, 7, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 7, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 7, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 6, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 5, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 4, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 4, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 5), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 4), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 3), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 2, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 1, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 1, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 1, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 6), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 5), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 4), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 3), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 1), VERSION(1, 6, 0) },
+};
+
+const struct version_compat_table bladerf1_fpga_compat_table = {fpga_compat, ARRAY_SIZE(fpga_compat)};
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/compatibility.h b/Radio/HW/BladeRF/src/board/bladerf1/compatibility.h
new file mode 100644
index 0000000..b91d5ef
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/compatibility.h
@@ -0,0 +1,9 @@
+#ifndef BLADERF1_COMPATIBILITY_H_
+#define BLADERF1_COMPATIBILITY_H_
+
+#include "helpers/version.h"
+
+extern const struct version_compat_table bladerf1_fw_compat_table;
+extern const struct version_compat_table bladerf1_fpga_compat_table;
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/flash.c b/Radio/HW/BladeRF/src/board/bladerf1/flash.c
new file mode 100644
index 0000000..6850543
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/flash.c
@@ -0,0 +1,543 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "log.h"
+#include "minmax.h"
+#include "misc.h"
+#include "conversions.h"
+
+#include "bladeRF.h"
+#include "board/board.h"
+
+#include "driver/spi_flash.h"
+
+#include "flash.h"
+
+#define OTP_BUFFER_SIZE 256
+
+int spi_flash_write_fx3_fw(struct bladerf *dev, const uint8_t *image, size_t len)
+{
+ int status;
+ uint8_t *readback_buf;
+ uint8_t *padded_image;
+ uint32_t padded_image_len;
+
+ /* Pad firwmare data out to a page size */
+ const uint32_t page_size = dev->flash_arch->psize_bytes;
+ const uint32_t padding_len =
+ (len % page_size == 0) ? 0 : page_size - (len % page_size);
+
+ /* Flash page where FX3 firmware starts */
+ const uint32_t flash_page_fw = BLADERF_FLASH_ADDR_FIRMWARE /
+ dev->flash_arch->psize_bytes;
+
+ /* Flash erase block where FX3 firmware starts */
+ const uint32_t flash_eb_fw = BLADERF_FLASH_ADDR_FIRMWARE /
+ dev->flash_arch->ebsize_bytes;
+
+ /** Length of firmware region of flash, in erase blocks */
+ const uint32_t flash_eb_len_fw = BLADERF_FLASH_BYTE_LEN_FIRMWARE /
+ dev->flash_arch->ebsize_bytes;
+
+ if (len >= (UINT32_MAX - padding_len)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ padded_image_len = (uint32_t) len + padding_len;
+
+ readback_buf = malloc(padded_image_len);
+ if (readback_buf == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ padded_image = malloc(padded_image_len);
+ if (padded_image == NULL) {
+ free(readback_buf);
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Copy image */
+ memcpy(padded_image, image, len);
+
+ /* Clear the padded region */
+ memset(padded_image + len, 0xFF, padded_image_len - len);
+
+ /* Erase the entire firmware region */
+ status = spi_flash_erase(dev, flash_eb_fw, flash_eb_len_fw);
+ if (status != 0) {
+ log_debug("Failed to erase firmware region: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Convert the image length to pages */
+ padded_image_len /= page_size;
+
+ /* Write the firmware image to flash */
+ status = spi_flash_write(dev, padded_image,
+ flash_page_fw, padded_image_len);
+
+ if (status < 0) {
+ log_debug("Failed to write firmware: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and double-check what we just wrote */
+ status = spi_flash_verify(dev, readback_buf, padded_image,
+ flash_page_fw, padded_image_len);
+ if (status != 0) {
+ log_debug("Flash verification failed: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+error:
+ free(padded_image);
+ free(readback_buf);
+ return status;
+}
+
+static inline void fill_fpga_metadata_page(struct bladerf *dev,
+ uint8_t *metadata,
+ size_t actual_bitstream_len)
+{
+ char len_str[12];
+ int idx = 0;
+
+ memset(len_str, 0, sizeof(len_str));
+ memset(metadata, 0xff, dev->flash_arch->psize_bytes);
+
+ snprintf(len_str, sizeof(len_str), "%u",
+ (unsigned int)actual_bitstream_len);
+
+ binkv_encode_field((char *)metadata, dev->flash_arch->psize_bytes,
+ &idx, "LEN", len_str);
+}
+
+static inline size_t get_flash_eb_len_fpga(struct bladerf *dev)
+{
+ int status;
+ size_t fpga_bytes;
+ size_t eb_count;
+
+ status = dev->board->get_fpga_bytes(dev, &fpga_bytes);
+ if (status < 0) {
+ return status;
+ }
+
+ eb_count = fpga_bytes / dev->flash_arch->ebsize_bytes;
+
+ if ((fpga_bytes % dev->flash_arch->ebsize_bytes) > 0) {
+ // Round up to nearest full block
+ ++eb_count;
+ }
+
+ return eb_count;
+}
+
+#define METADATA_LEN 256
+
+int spi_flash_write_fpga_bitstream(struct bladerf *dev,
+ const uint8_t *bitstream,
+ size_t len)
+{
+ /* Pad data to be page-aligned */
+ const uint32_t page_size = dev->flash_arch->psize_bytes;
+ const uint32_t padding_len =
+ (len % page_size == 0) ? 0 : page_size - (len % page_size);
+
+ /** Flash page where FPGA metadata and bitstream start */
+ const uint32_t flash_page_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->psize_bytes;
+
+ /** Flash erase block where FPGA metadata and bitstream start */
+ const uint32_t flash_eb_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->ebsize_bytes;
+
+ /** Length of entire FPGA region, in units of erase blocks */
+ const uint32_t flash_eb_len_fpga = (uint32_t)get_flash_eb_len_fpga(dev);
+
+ assert(METADATA_LEN <= page_size);
+
+ int status;
+ uint8_t *readback_buf;
+ uint8_t *padded_bitstream;
+ uint8_t metadata[METADATA_LEN];
+ uint32_t padded_bitstream_len;
+
+ if (len >= (UINT32_MAX - padding_len)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ padded_bitstream_len = (uint32_t)len + padding_len;
+
+ /* Fill in metadata with the *actual* FPGA bitstream length */
+ fill_fpga_metadata_page(dev, metadata, len);
+
+ readback_buf = malloc(padded_bitstream_len);
+ if (readback_buf == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ padded_bitstream = malloc(padded_bitstream_len);
+ if (padded_bitstream == NULL) {
+ free(readback_buf);
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Copy bitstream */
+ memcpy(padded_bitstream, bitstream, len);
+
+ /* Clear the padded region */
+ memset(padded_bitstream + len, 0xFF, padded_bitstream_len - len);
+
+ /* Erase FPGA metadata and bitstream region */
+ status = spi_flash_erase(dev, flash_eb_fpga, flash_eb_len_fpga);
+ if (status != 0) {
+ log_debug("Failed to erase FPGA meta & bitstream regions: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Write the metadata page */
+ status = spi_flash_write(dev, metadata, flash_page_fpga, 1);
+ if (status != 0) {
+ log_debug("Failed to write FPGA metadata page: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Convert the padded bitstream length to pages */
+ padded_bitstream_len /= page_size;
+
+ /* Write the padded bitstream */
+ status = spi_flash_write(dev, padded_bitstream, flash_page_fpga + 1,
+ padded_bitstream_len);
+ if (status != 0) {
+ log_debug("Failed to write bitstream: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and verify metadata */
+ status = spi_flash_verify(dev, readback_buf, metadata, flash_page_fpga, 1);
+ if (status != 0) {
+ log_debug("Failed to verify metadata: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and verify the bitstream data */
+ status = spi_flash_verify(dev, readback_buf, padded_bitstream,
+ flash_page_fpga + 1, padded_bitstream_len);
+ if (status != 0) {
+ log_debug("Failed to verify bitstream data: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+error:
+ free(padded_bitstream);
+ free(readback_buf);
+ return status;
+}
+
+int spi_flash_erase_fpga(struct bladerf *dev)
+{
+ int status;
+ size_t fpga_bytes;
+
+ status = dev->board->get_fpga_bytes(dev, &fpga_bytes);
+ if (status < 0) {
+ return status;
+ }
+
+ /** Flash erase block where FPGA metadata and bitstream start */
+ const uint32_t flash_eb_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->ebsize_bytes;
+
+ /** Length of entire FPGA region, in units of erase blocks */
+ const uint32_t flash_eb_len_fpga = (uint32_t)get_flash_eb_len_fpga(dev);
+
+ /* Erase the entire FPGA region, including both autoload metadata and the
+ * actual bitstream data */
+ return spi_flash_erase(dev, flash_eb_fpga, flash_eb_len_fpga);
+}
+
+int spi_flash_read_otp(struct bladerf *dev, char *field,
+ char *data, size_t data_size)
+{
+ int status;
+ char otp[OTP_BUFFER_SIZE];
+
+ memset(otp, 0xff, OTP_BUFFER_SIZE);
+
+ status = dev->backend->get_otp(dev, otp);
+ if (status < 0)
+ return status;
+ else
+ return binkv_decode_field(otp, OTP_BUFFER_SIZE, field, data, data_size);
+}
+
+int spi_flash_read_cal(struct bladerf *dev, char *field,
+ char *data, size_t data_size)
+{
+ int status;
+ char cal[CAL_BUFFER_SIZE];
+
+ status = dev->backend->get_cal(dev, cal);
+ if (status < 0)
+ return status;
+ else
+ return binkv_decode_field(cal, CAL_BUFFER_SIZE, field, data, data_size);
+}
+
+int spi_flash_read_serial(struct bladerf *dev, char *serial_buf)
+{
+ int status;
+
+ status = spi_flash_read_otp(dev, "S", serial_buf, BLADERF_SERIAL_LENGTH - 1);
+
+ if (status < 0) {
+ log_info("Unable to fetch serial number. Defaulting to 0's.\n");
+ memset(dev->ident.serial, '0', BLADERF_SERIAL_LENGTH - 1);
+
+ /* Treat this as non-fatal */
+ status = 0;
+ }
+
+ serial_buf[BLADERF_SERIAL_LENGTH - 1] = '\0';
+
+ return status;
+}
+
+int spi_flash_read_vctcxo_trim(struct bladerf *dev, uint16_t *dac_trim)
+{
+ int status;
+ bool ok;
+ int16_t trim;
+ char tmp[7] = { 0 };
+
+ status = spi_flash_read_cal(dev, "DAC", tmp, sizeof(tmp) - 1);
+ if (status < 0) {
+ return status;
+ }
+
+ trim = str2uint(tmp, 0, 0xffff, &ok);
+ if (ok == false) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ *dac_trim = trim;
+
+ return 0;
+}
+
+int spi_flash_read_fpga_size(struct bladerf *dev, bladerf_fpga_size *fpga_size)
+{
+ int status;
+ char tmp[7] = { 0 };
+
+ status = spi_flash_read_cal(dev, "B", tmp, sizeof(tmp) - 1);
+ if (status < 0) {
+ return status;
+ }
+
+ if (!strcmp("40", tmp)) {
+ *fpga_size = BLADERF_FPGA_40KLE;
+ } else if(!strcmp("115", tmp)) {
+ *fpga_size = BLADERF_FPGA_115KLE;
+ } else if(!strcmp("A4", tmp)) {
+ *fpga_size = BLADERF_FPGA_A4;
+ } else if(!strcmp("A5", tmp)) {
+ *fpga_size = BLADERF_FPGA_A5;
+ } else if(!strcmp("A9", tmp)) {
+ *fpga_size = BLADERF_FPGA_A9;
+ } else {
+ *fpga_size = BLADERF_FPGA_UNKNOWN;
+ }
+
+ return status;
+}
+
+int spi_flash_read_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did)
+{
+ int status;
+
+ status = dev->backend->get_flash_id(dev, mid, did);
+
+ return status;
+}
+
+int spi_flash_decode_flash_architecture(struct bladerf *dev,
+ bladerf_fpga_size *fpga_size)
+{
+ int status;
+ struct bladerf_flash_arch *flash_arch;
+
+ status = 0;
+ flash_arch = dev->flash_arch;
+
+ /* Fill in defaults */
+ flash_arch->tsize_bytes = 32 << 17; /* 32 Mbit */
+ flash_arch->psize_bytes = 256;
+ flash_arch->ebsize_bytes = 64 << 10; /* 64 Kbyte */
+ flash_arch->status = STATUS_ASSUMED;
+
+ /* First try to decode the MID/DID of the flash chip */
+ switch( flash_arch->manufacturer_id ) {
+ case 0xC2: /* MACRONIX */
+ log_verbose( "Found SPI flash manufacturer: MACRONIX.\n" );
+ switch( flash_arch->device_id ) {
+ case 0x36:
+ log_verbose( "Found SPI flash device: MX25U3235E (32 Mbit).\n" );
+ flash_arch->tsize_bytes = 32 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ default:
+ log_debug( "Unknown Macronix flash device ID.\n" );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ break;
+
+ case 0xEF: /* WINBOND */
+ log_verbose( "Found SPI flash manufacturer: WINBOND.\n" );
+ switch( flash_arch->device_id ) {
+ case 0x15:
+ log_verbose( "Found SPI flash device: W25Q32JV (32 Mbit).\n" );
+ flash_arch->tsize_bytes = 32 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ case 0x16:
+ log_verbose( "Found SPI flash device: W25Q64JV (64 Mbit).\n" );
+ flash_arch->tsize_bytes = 64 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ case 0x17:
+ log_verbose( "Found SPI flash device: W25Q128JV (128 Mbit).\n" );
+ flash_arch->tsize_bytes = 128 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ default:
+ log_debug( "Unknown Winbond flash device ID [0x%02X].\n" , flash_arch->device_id );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ break;
+
+ default:
+ log_debug( "Unknown flash manufacturer ID.\n" );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Could not decode flash MID/DID, so assume based on FPGA size */
+ if( status < 0 || flash_arch->status != STATUS_SUCCESS ) {
+ if( (fpga_size == NULL) || (*fpga_size == BLADERF_FPGA_UNKNOWN) ) {
+ log_debug( "Could not decode flash manufacturer/device ID and have "
+ "an unknown FPGA size. Assume default flash "
+ "architecture.\n" );
+ } else {
+ switch( *fpga_size ) {
+ case BLADERF_FPGA_A9:
+ flash_arch->tsize_bytes = 128 << 17;
+ break;
+ default:
+ flash_arch->tsize_bytes = 32 << 17;
+ }
+ log_debug( "Could not decode flash manufacturer/device ID, but "
+ "found a %u kLE FPGA. Setting the most probable "
+ "flash architecture.\n", *fpga_size );
+ }
+ }
+
+ flash_arch->num_pages = flash_arch->tsize_bytes / flash_arch->psize_bytes;
+ flash_arch->num_ebs = flash_arch->tsize_bytes / flash_arch->ebsize_bytes;
+
+ log_verbose("SPI flash total size = %u Mbit\n", (flash_arch->tsize_bytes >> 17));
+ log_verbose("SPI flash page size = %u bytes\n", flash_arch->psize_bytes);
+ log_verbose("SPI flash erase block size = %u bytes\n", flash_arch->ebsize_bytes);
+ log_verbose("SPI flash number of pages = %u\n", flash_arch->num_pages);
+ log_verbose("SPI flash number of erase blocks = %u pages\n", flash_arch->num_ebs);
+
+ return status;
+}
+
+
+int binkv_decode_field(char *ptr, int len, char *field,
+ char *val, size_t maxlen)
+{
+ int c;
+ unsigned char *ub, *end;
+ unsigned short a1, a2;
+ size_t flen, wlen;
+
+ flen = strlen(field);
+
+ ub = (unsigned char *)ptr;
+ end = ub + len;
+ while (ub < end) {
+ c = *ub;
+
+ if (c == 0xff) // flash and OTP are 0xff if they've never been written to
+ break;
+
+ a1 = LE16_TO_HOST(*(unsigned short *)(&ub[c+1])); // read checksum
+ a2 = zcrc(ub, c+1); // calculate checksum
+
+ if (a1 == a2) {
+ if (!strncmp((char *)ub + 1, field, flen)) {
+ wlen = min_sz(c - flen, maxlen);
+ strncpy(val, (char *)ub + 1 + flen, wlen);
+ val[wlen] = 0;
+ return 0;
+ }
+ } else {
+ log_debug( "%s: Field checksum mismatch\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+ ub += c + 3; //skip past `c' bytes, 2 byte CRC field, and 1 byte len field
+ }
+ return BLADERF_ERR_INVAL;
+}
+
+int binkv_encode_field(char *ptr, int len, int *idx,
+ const char *field, const char *val)
+{
+ int vlen, flen, tlen;
+ flen = (int)strlen(field);
+ vlen = (int)strlen(val);
+ tlen = flen + vlen + 1;
+
+ if (tlen >= 256 || *idx + tlen >= len)
+ return BLADERF_ERR_MEM;
+
+ ptr[*idx] = flen + vlen;
+ strcpy(&ptr[*idx + 1], field);
+ strcpy(&ptr[*idx + 1 + flen], val);
+ *(unsigned short *)(&ptr[*idx + tlen ]) = HOST_TO_LE16(zcrc((uint8_t *)&ptr[*idx ], tlen));
+ *idx += tlen + 2;
+ return 0;
+}
+
+int binkv_add_field(char *buf, int buf_len, const char *field_name, const char *val)
+{
+ int dummy_idx = 0;
+ int i = 0;
+ int rv;
+
+ /* skip to the end, ignoring crc (don't want to further corrupt partially
+ * corrupt data) */
+ while(i < buf_len) {
+ uint8_t field_len = buf[i];
+
+ if(field_len == 0xff)
+ break;
+
+ /* skip past `field_len' bytes, 2 byte CRC field, and 1 byte len
+ * field */
+ i += field_len + 3;
+ }
+
+ rv = binkv_encode_field(buf + i, buf_len - i, &dummy_idx, field_name, val);
+ if(rv < 0)
+ return rv;
+
+ return 0;
+}
+
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/flash.h b/Radio/HW/BladeRF/src/board/bladerf1/flash.h
new file mode 100644
index 0000000..be8f02d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/flash.h
@@ -0,0 +1,172 @@
+#ifndef BLADERF1_FLASH_H_
+#define BLADERF1_FLASH_H_
+
+#include <libbladeRF.h>
+
+#include <stdint.h>
+
+/**
+ * Write the provided data to the FX3 Firmware region to flash.
+ *
+ * This function does no validation of the data (i.e., that it's valid FW).
+ *
+ * @param dev bladeRF handle
+ * @param[in] image Firmware image data
+ * @param[in] len Length of firmware image data
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int spi_flash_write_fx3_fw(struct bladerf *dev,
+ const uint8_t *image,
+ size_t len);
+
+/**
+ * Write the provided FPGA bitstream to flash and enable autoloading via
+ * writing the associated metadata.
+ *
+ * @param dev bladeRF handle
+ * @param[in] bitstream FPGA bitstream data
+ * @param[in] len Length of the bitstream data
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int spi_flash_write_fpga_bitstream(struct bladerf *dev,
+ const uint8_t *bitstream,
+ size_t len);
+
+/**
+ * Erase FPGA metadata and bitstream regions of flash.
+ *
+ * @param dev bladeRF handle
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int spi_flash_erase_fpga(struct bladerf *dev);
+
+/**
+ * Read data from OTP ("otp") section of flash.
+ *
+ * @param dev Device handle
+ * @param[in] field OTP field
+ * @param[out] data Populated with retrieved data
+ * @param[in] data_size Size of the data to read
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_otp(struct bladerf *dev,
+ char *field,
+ char *data,
+ size_t data_size);
+
+/**
+ * Read data from calibration ("cal") section of flash.
+ *
+ * @param dev Device handle
+ * @param[in] field Calibration field
+ * @param[out] data Populated with retrieved data
+ * @param[in] data_size Size of the data to read
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_cal(struct bladerf *dev,
+ char *field,
+ char *data,
+ size_t data_size);
+
+/**
+ * Retrieve the device serial from flash.
+ *
+ * @pre The provided buffer is BLADERF_SERIAL_LENGTH in size
+ *
+ * @param dev Device handle. On success, serial field is updated
+ * @param[out] serial_buf Populated with device serial
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_serial(struct bladerf *dev, char *serial_buf);
+
+/**
+ * Retrieve VCTCXO calibration value from flash.
+ *
+ * @param dev Device handle
+ * @param[out] dac_trim DAC trim
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_vctcxo_trim(struct bladerf *dev, uint16_t *dac_trim);
+
+/**
+ * Retrieve FPGA size variant from flash.
+ *
+ * @param dev Device handle.
+ * @param[out] fpga_size FPGA size
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_fpga_size(struct bladerf *dev, bladerf_fpga_size *fpga_size);
+
+/**
+ * Retrieve SPI flash manufacturer ID and device ID.
+ *
+ * @param dev Device handle.
+ * @param[out] mid Flash manufacturer ID
+ * @param[out] did Flash device ID
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did);
+
+/**
+ * Decode SPI flash architecture given manufacturer and device IDs.
+ *
+ * @param dev Device handle.
+ * @param fpga_size FPGA size
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_decode_flash_architecture(struct bladerf *dev,
+ bladerf_fpga_size *fpga_size);
+
+/**
+ * Encode a binary key-value pair.
+ *
+ * @param[in] ptr Pointer to data buffer that will contain encoded
+ * data
+ * @param[in] len Length of data buffer that will contain encoded data
+ * @param[inout] idx Pointer indicating next free byte inside of data
+ * buffer that will contain encoded data
+ * @param[in] field Key of value to be stored in encoded data buffer
+ * @param[in] val Value to be stored in encoded data buffer
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int binkv_encode_field(
+ char *ptr, int len, int *idx, const char *field, const char *val);
+
+/**
+ * Decode a binary key-value pair.
+ *
+ * @param[in] ptr Pointer to data buffer containing encoded data
+ * @param[in] len Length of data buffer containing encoded data
+ * @param[in] field Key of value to be decoded in encoded data buffer
+ * @param[out] val Value to be retrieved from encoded data buffer
+ * @param[in] maxlen Maximum length of value to be retrieved
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int binkv_decode_field(
+ char *ptr, int len, char *field, char *val, size_t maxlen);
+
+/**
+ * Add a binary key-value pair to an existing binkv data buffer.
+ *
+ * @param[in] buf Buffer to add field to
+ * @param[in] len Length of `buf' in bytes
+ * @param[in] field Key of value to be stored in encoded data buffer
+ * @param[in] val Value associated with key `field'
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int binkv_add_field(char *buf, int len, const char *field, const char *val);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/image.c b/Radio/HW/BladeRF/src/board/bladerf1/image.c
new file mode 100644
index 0000000..ddea18b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/image.c
@@ -0,0 +1,592 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org>
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <libbladeRF.h>
+
+#include "bladeRF.h"
+#include "rel_assert.h"
+#include "host_config.h"
+#include "sha256.h"
+#include "log.h"
+#include "minmax.h"
+
+#include "board/board.h"
+#include "driver/spi_flash.h"
+#include "helpers/file.h"
+
+#include "flash.h"
+
+/* These two are used interchangeably - ensure they're the same! */
+#if SHA256_DIGEST_SIZE != BLADERF_IMAGE_CHECKSUM_LEN
+#error "Image checksum size mismatch"
+#endif
+
+#define CALC_IMAGE_SIZE(len) ((size_t) \
+ ( \
+ BLADERF_IMAGE_MAGIC_LEN + \
+ BLADERF_IMAGE_CHECKSUM_LEN + \
+ 3 * sizeof(uint16_t) + \
+ sizeof(uint64_t) + \
+ BLADERF_SERIAL_LENGTH + \
+ BLADERF_IMAGE_RESERVED_LEN + \
+ 3 * sizeof(uint32_t) + \
+ len \
+ ) \
+)
+
+static const char image_magic[] = "bladeRF";
+
+#if BLADERF_OS_WINDOWS
+#include <time.h>
+static uint64_t get_timestamp()
+{
+ __time64_t now = _time64(NULL);
+ return (uint64_t)now;
+}
+#else
+#include <sys/time.h>
+static inline uint64_t get_timestamp()
+{
+ uint64_t ret;
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) == 0) {
+ ret = tv.tv_sec;
+ } else {
+ log_verbose("gettimeofday failed: %s\n", strerror(errno));
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
+static void sha256_buffer(const char *buf, size_t len,
+ char digest[SHA256_DIGEST_SIZE])
+{
+ SHA256_CTX ctx;
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, buf, len);
+ SHA256_Final((uint8_t*)digest, &ctx);
+}
+
+static int verify_checksum(uint8_t *buf, size_t buf_len)
+{
+ char checksum_expected[SHA256_DIGEST_SIZE];
+ char checksum_calc[SHA256_DIGEST_SIZE];
+
+ if (buf_len <= CALC_IMAGE_SIZE(0)) {
+ log_debug("Provided buffer isn't a full image\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Backup and clear the expected checksum before we calculate the
+ * expected checksum */
+ memcpy(checksum_expected, &buf[BLADERF_IMAGE_MAGIC_LEN],
+ sizeof(checksum_expected));
+
+ memset(&buf[BLADERF_IMAGE_MAGIC_LEN], 0, SHA256_DIGEST_SIZE);
+
+ sha256_buffer((const char *)buf, buf_len, checksum_calc);
+
+ if (memcmp(checksum_expected, checksum_calc, SHA256_DIGEST_SIZE) != 0) {
+ return BLADERF_ERR_CHECKSUM;
+ } else {
+ /* Restore the buffer's checksum so the caller can still use it */
+ memcpy(&buf[BLADERF_IMAGE_MAGIC_LEN], checksum_expected,
+ sizeof(checksum_expected));
+
+ return 0;
+ }
+}
+
+static bool image_type_is_valid(bladerf_image_type type) {
+ switch (type) {
+ case BLADERF_IMAGE_TYPE_RAW:
+ case BLADERF_IMAGE_TYPE_FIRMWARE:
+ case BLADERF_IMAGE_TYPE_FPGA_40KLE:
+ case BLADERF_IMAGE_TYPE_FPGA_115KLE:
+ case BLADERF_IMAGE_TYPE_FPGA_A4:
+ case BLADERF_IMAGE_TYPE_FPGA_A5:
+ case BLADERF_IMAGE_TYPE_FPGA_A9:
+ case BLADERF_IMAGE_TYPE_CALIBRATION:
+ case BLADERF_IMAGE_TYPE_RX_DC_CAL:
+ case BLADERF_IMAGE_TYPE_TX_DC_CAL:
+ case BLADERF_IMAGE_TYPE_RX_IQ_CAL:
+ case BLADERF_IMAGE_TYPE_TX_IQ_CAL:
+ case BLADERF_IMAGE_TYPE_GAIN_CAL:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Serialize image contents and fill in checksum */
+static size_t pack_image(struct bladerf_image *img, uint8_t *buf)
+{
+ size_t i = 0;
+ uint16_t ver_field;
+ uint32_t type, len, addr;
+ uint64_t timestamp;
+ char checksum[BLADERF_IMAGE_CHECKSUM_LEN];
+
+ memcpy(&buf[i], img->magic, BLADERF_IMAGE_MAGIC_LEN);
+ i += BLADERF_IMAGE_MAGIC_LEN;
+
+ memset(&buf[i], 0, BLADERF_IMAGE_CHECKSUM_LEN);
+ i += BLADERF_IMAGE_CHECKSUM_LEN;
+
+ ver_field = HOST_TO_BE16(img->version.major);
+ memcpy(&buf[i], &ver_field, sizeof(ver_field));
+ i += sizeof(ver_field);
+
+ ver_field = HOST_TO_BE16(img->version.minor);
+ memcpy(&buf[i], &ver_field, sizeof(ver_field));
+ i += sizeof(ver_field);
+
+ ver_field = HOST_TO_BE16(img->version.patch);
+ memcpy(&buf[i], &ver_field, sizeof(ver_field));
+ i += sizeof(ver_field);
+
+ timestamp = HOST_TO_BE64(img->timestamp);
+ memcpy(&buf[i], &timestamp, sizeof(timestamp));
+ i += sizeof(timestamp);
+
+ memcpy(&buf[i], &img->serial, BLADERF_SERIAL_LENGTH);
+ i += BLADERF_SERIAL_LENGTH;
+
+ memset(&buf[i], 0, BLADERF_IMAGE_RESERVED_LEN);
+ i += BLADERF_IMAGE_RESERVED_LEN;
+
+ type = HOST_TO_BE32((uint32_t)img->type);
+ memcpy(&buf[i], &type, sizeof(type));
+ i += sizeof(type);
+
+ addr = HOST_TO_BE32(img->address);
+ memcpy(&buf[i], &addr, sizeof(addr));
+ i += sizeof(addr);
+
+ len = HOST_TO_BE32(img->length);
+ memcpy(&buf[i], &len, sizeof(len));
+ i += sizeof(len);
+
+ memcpy(&buf[i], img->data, img->length);
+ i += img->length;
+
+ sha256_buffer((const char *)buf, i, checksum);
+ memcpy(&buf[BLADERF_IMAGE_MAGIC_LEN], checksum, BLADERF_IMAGE_CHECKSUM_LEN);
+
+ return i;
+}
+
+/* Unpack flash image from file and validate fields */
+static int unpack_image(struct bladerf_image *img, uint8_t *buf, size_t len)
+{
+ size_t i = 0;
+ uint32_t type;
+
+ /* Ensure we have at least a full set of metadata */
+ if (len < CALC_IMAGE_SIZE(0)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ memcpy(img->magic, &buf[i], BLADERF_IMAGE_MAGIC_LEN);
+ img->magic[BLADERF_IMAGE_MAGIC_LEN] = '\0';
+ if (strncmp(img->magic, image_magic, BLADERF_IMAGE_MAGIC_LEN)) {
+ return BLADERF_ERR_INVAL;
+ }
+ i += BLADERF_IMAGE_MAGIC_LEN;
+
+ memcpy(img->checksum, &buf[i], BLADERF_IMAGE_CHECKSUM_LEN);
+ i += BLADERF_IMAGE_CHECKSUM_LEN;
+
+ memcpy(&img->version.major, &buf[i], sizeof(img->version.major));
+ i += sizeof(img->version.major);
+ img->version.major = BE16_TO_HOST(img->version.major);
+
+ memcpy(&img->version.minor, &buf[i], sizeof(img->version.minor));
+ i += sizeof(img->version.minor);
+ img->version.minor = BE16_TO_HOST(img->version.minor);
+
+ memcpy(&img->version.patch, &buf[i], sizeof(img->version.patch));
+ i += sizeof(img->version.patch);
+ img->version.patch = BE16_TO_HOST(img->version.patch);
+
+ memcpy(&img->timestamp, &buf[i], sizeof(img->timestamp));
+ i += sizeof(img->timestamp);
+ img->timestamp = BE64_TO_HOST(img->timestamp);
+
+ memcpy(img->serial, &buf[i], BLADERF_SERIAL_LENGTH);
+ img->serial[BLADERF_SERIAL_LENGTH] = '\0';
+ i += BLADERF_SERIAL_LENGTH;
+
+ memcpy(img->reserved, &buf[i], BLADERF_IMAGE_RESERVED_LEN);
+ i += BLADERF_IMAGE_RESERVED_LEN;
+
+ memcpy(&type, &buf[i], sizeof(type));
+ i += sizeof(type);
+ type = BE32_TO_HOST(type);
+
+ if (!image_type_is_valid((bladerf_image_type)type)) {
+ log_debug("Invalid type value in image: %d\n", (int)type);
+ return BLADERF_ERR_INVAL;
+ } else {
+ img->type = (bladerf_image_type)type;
+ }
+
+ memcpy(&img->address, &buf[i], sizeof(img->address));
+ i += sizeof(img->address);
+ img->address = BE32_TO_HOST(img->address);
+
+ memcpy(&img->length, &buf[i], sizeof(img->length));
+ i += sizeof(img->length);
+ img->length = BE32_TO_HOST(img->length);
+
+ if (len != CALC_IMAGE_SIZE(img->length)) {
+ log_debug("Image contains more or less data than expected\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Just slide the data over */
+ memmove(&buf[0], &buf[i], img->length);
+ img->data = buf;
+
+ return 0;
+}
+
+int bladerf_image_print_metadata(const struct bladerf_image *image) {
+ if (!image) {
+ return BLADERF_ERR_MEM;
+ }
+
+ printf("Magic: %s\n", image->magic);
+ printf("Type: %s\n", bladerf_image_type_to_string(image->type));
+ printf("Version: %d.%d.%d\n",
+ image->version.major, image->version.minor, image->version.patch);
+ printf("Timestamp: %" PRIx64 "\n", image->timestamp);
+ printf("Serial: %s\n", image->serial);
+ printf("Address: %x\n", image->address);
+ printf("Length: %u\n", image->length);
+ fflush(stdout);
+
+ return 0;
+}
+
+const char* bladerf_image_type_to_string(bladerf_image_type type) {
+ switch (type) {
+ case BLADERF_IMAGE_TYPE_INVALID:
+ return "Invalid";
+ case BLADERF_IMAGE_TYPE_RAW:
+ return "Raw Data";
+ case BLADERF_IMAGE_TYPE_FIRMWARE:
+ return "Firmware";
+ case BLADERF_IMAGE_TYPE_FPGA_40KLE:
+ return "FPGA 40 KLE Bitstream";
+ case BLADERF_IMAGE_TYPE_FPGA_115KLE:
+ return "FPGA 115 KLE Bitstream";
+ case BLADERF_IMAGE_TYPE_FPGA_A4:
+ return "FPGA A4 Bitstream";
+ case BLADERF_IMAGE_TYPE_FPGA_A9:
+ return "FPGA A9 Bitstream";
+ case BLADERF_IMAGE_TYPE_CALIBRATION:
+ return "Board Calibration";
+ case BLADERF_IMAGE_TYPE_RX_DC_CAL:
+ return "RX DC Offset Calibration Table";
+ case BLADERF_IMAGE_TYPE_TX_DC_CAL:
+ return "TX DC Offset Calibration Table";
+ case BLADERF_IMAGE_TYPE_RX_IQ_CAL:
+ return "RX IQ Balance Calibration Table";
+ case BLADERF_IMAGE_TYPE_TX_IQ_CAL:
+ return "TX IQ Balance Calibration Table";
+ case BLADERF_IMAGE_TYPE_FPGA_A5:
+ return "FPGA A5 Bitstream";
+ case BLADERF_IMAGE_TYPE_GAIN_CAL:
+ return "Gain Calibration";
+ default:
+ return "Unknown Type";
+ }
+}
+
+int bladerf_image_write(struct bladerf *dev,
+ struct bladerf_image *img, const char *file)
+{
+ int rv;
+ FILE *f = NULL;
+ uint8_t *buf = NULL;
+ size_t buf_len;
+
+ /* Ensure the format identifier is correct */
+ if (memcmp(img->magic, image_magic, BLADERF_IMAGE_MAGIC_LEN) != 0) {
+#ifdef LOGGING_ENABLED
+ char badmagic[BLADERF_IMAGE_MAGIC_LEN + 1];
+ memset(badmagic, 0, sizeof(badmagic));
+ memcpy(&badmagic, &img->magic, BLADERF_IMAGE_MAGIC_LEN);
+ log_debug("Invalid file format magic value: %s\n", badmagic);
+#endif
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Check for a valid image type */
+ if (!image_type_is_valid(img->type)) {
+ log_debug("Invalid image type: %d\n", img->type);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Just to be tiny bit paranoid... */
+ if (!img->data) {
+ log_debug("Image data pointer is NULL\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ buf_len = CALC_IMAGE_SIZE(img->length);
+ buf = (uint8_t *)calloc(1, buf_len);
+ if (!buf) {
+ log_verbose("calloc failed: %s\n", strerror(errno));
+ return BLADERF_ERR_MEM;
+ }
+
+ /* If the type is RAW, we should only allow erase-block aligned
+ * addresses and lengths */
+ if (img->type == BLADERF_IMAGE_TYPE_RAW && img->address != 0xffffffff) {
+ if (img->address % dev->flash_arch->ebsize_bytes != 0) {
+ log_debug("Image address must be erase block-aligned for RAW.\n");
+ rv = BLADERF_ERR_INVAL;
+ goto error;
+ } else if (img->length % dev->flash_arch->ebsize_bytes != 0) {
+ log_debug("Image length must be erase block-aligned for RAW.\n");
+ rv = BLADERF_ERR_INVAL;
+ goto error;
+ }
+ }
+
+ pack_image(img, buf);
+
+ f = fopen(file, "wb");
+ if (!f) {
+ if (errno == EACCES) {
+ rv = BLADERF_ERR_PERMISSION;
+ } else {
+ rv = BLADERF_ERR_IO;
+ }
+
+ log_debug("Failed to open \"%s\": %s\n", file, strerror(errno));
+
+ goto error;
+ }
+
+ rv = file_write(f, buf, buf_len);
+
+error:
+ if (f) {
+ fclose(f);
+ }
+ free(buf);
+ return rv;
+}
+
+int bladerf_image_read(struct bladerf_image *img, const char *file)
+{
+ int rv = -1;
+ uint8_t *buf = NULL;
+ size_t buf_len;
+
+ rv = file_read_buffer(file, &buf, &buf_len);
+ if (rv < 0) {
+ goto bladerf_image_read_out;
+ }
+
+ rv = verify_checksum(buf, buf_len);
+ if (rv < 0) {
+ goto bladerf_image_read_out;
+ }
+
+ /* Note: On success, buf->data = buf, with the data memmove'd over.
+ * Static analysis tools might indicate a false postive leak when
+ * buf goes out of scope with rv == 0 */
+ rv = unpack_image(img, buf, buf_len);
+
+bladerf_image_read_out:
+ if (rv != 0) {
+ free(buf);
+ }
+
+ return rv;
+}
+
+static inline bool is_page_aligned(struct bladerf *dev, uint32_t val)
+{
+ return val % dev->flash_arch->psize_bytes == 0;
+}
+
+static inline bool is_valid_addr_len(struct bladerf *dev,
+ uint32_t addr, uint32_t len)
+{
+ if (addr >= dev->flash_arch->tsize_bytes) {
+ return false;
+ } else if (len > dev->flash_arch->tsize_bytes) {
+ return false;
+ } else if ((addr + len) > dev->flash_arch->tsize_bytes) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+struct bladerf_image * bladerf_alloc_image(struct bladerf *dev,
+ bladerf_image_type type,
+ uint32_t address,
+ uint32_t length)
+{
+ struct bladerf_image *image;
+
+ assert(BLADERF_IMAGE_MAGIC_LEN == (sizeof(image_magic) - 1));
+
+ /* 0xffffffff is a placeholder for images that use the format but don't
+ * currently have an address in flash to live in */
+ if (address != 0xffffffff) {
+ if (!is_page_aligned(dev, address)) {
+ log_debug("Address is not page-aligned: 0x%08x\n", address);
+ return NULL;
+ } else if (!is_page_aligned(dev, length)) {
+ log_debug("Length is not page-aligned: 0x%08x\n", length);
+ return NULL;
+ } else if (!is_valid_addr_len(dev, address, length)) {
+ log_debug("Invalid address=0x%08x or length=0x%08x\n", address, length);
+ return NULL;
+ }
+ }
+
+ image = (struct bladerf_image *)calloc(1, sizeof(*image));
+
+ if (!image) {
+ return NULL;
+ }
+
+ if (length) {
+ image->data = (uint8_t *)calloc(1, length);
+ if (!image->data) {
+ free(image);
+ return NULL;
+ }
+ }
+
+ memcpy(image->magic, &image_magic, BLADERF_IMAGE_MAGIC_LEN);
+
+ image->version.major = 0;
+ image->version.minor = 1;
+ image->version.patch = 0;
+ image->timestamp = get_timestamp();
+ image->address = address;
+ image->length = length;
+ image->type = type;
+
+ return image;
+}
+
+static int make_cal_region(bladerf_fpga_size size, uint16_t vctcxo_trim,
+ uint8_t *buf, size_t len)
+{
+ int rv;
+ static const char fpga_size_40[] = "40";
+ static const char fpga_size_115[] = "115";
+ static const char fpga_size_A4[] = "A4";
+ static const char fpga_size_A5[] = "A5";
+ static const char fpga_size_A9[] = "A9";
+ const char *fpga_size;
+ char dac[7] = {0};
+
+ if (size == BLADERF_FPGA_40KLE) {
+ fpga_size = fpga_size_40;
+ } else if (size == BLADERF_FPGA_115KLE) {
+ fpga_size = fpga_size_115;
+ } else if (size == BLADERF_FPGA_A4) {
+ fpga_size = fpga_size_A4;
+ } else if (size == BLADERF_FPGA_A5) {
+ fpga_size = fpga_size_A5;
+ } else if (size == BLADERF_FPGA_A9) {
+ fpga_size = fpga_size_A9;
+ } else {
+ assert(0); /* Bug catcher */
+ return BLADERF_ERR_INVAL;
+ }
+
+ memset(buf, 0xff, len);
+
+ assert(len < INT_MAX);
+ rv = binkv_add_field((char*)buf, (int)len, "B", fpga_size);
+
+ if (rv < 0) {
+ return rv;
+ }
+
+ sprintf(dac, "%u", vctcxo_trim);
+
+ rv = binkv_add_field((char*)buf, (int)len, "DAC", dac);
+ if (rv < 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+struct bladerf_image * bladerf_alloc_cal_image(struct bladerf *dev,
+ bladerf_fpga_size fpga_size,
+ uint16_t vctcxo_trim)
+{
+ struct bladerf_image *image;
+ int status;
+
+ image = bladerf_alloc_image(dev,
+ BLADERF_IMAGE_TYPE_CALIBRATION,
+ BLADERF_FLASH_ADDR_CAL,
+ BLADERF_FLASH_BYTE_LEN_CAL);
+
+ if (!image) {
+ return NULL;
+ }
+
+ status = make_cal_region(fpga_size, vctcxo_trim,
+ image->data, image->length);
+
+ if (status != 0) {
+ bladerf_free_image(image);
+ image = NULL;
+ }
+
+ return image;
+}
+
+void bladerf_free_image(struct bladerf_image *image)
+{
+ if (image) {
+ free(image->data);
+ free(image);
+ }
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c b/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c
new file mode 100644
index 0000000..8d56149
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c
@@ -0,0 +1,3744 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017-2018 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 "libbladeRF.h"
+
+#include "bladeRF.h"
+#include "host_config.h"
+
+#include "log.h"
+#define LOGGER_ID_STRING
+#include "logger_id.h"
+#include "rel_assert.h"
+
+#include "../bladerf1/flash.h"
+#include "board/board.h"
+#include "capabilities.h"
+#include "compatibility.h"
+
+#include "ad936x.h"
+#include "ad936x_helpers.h"
+
+#include "driver/fpga_trigger.h"
+#include "driver/fx3_fw.h"
+#include "driver/ina219.h"
+#include "driver/spi_flash.h"
+
+#include "backend/backend_config.h"
+#include "backend/usb/usb.h"
+
+#include "streaming/async.h"
+#include "streaming/sync.h"
+
+#include "conversions.h"
+#include "devinfo.h"
+#include "helpers/file.h"
+#include "helpers/version.h"
+#include "helpers/wallclock.h"
+#include "iterators.h"
+#include "version.h"
+
+#include "bladerf2_common.h"
+#include "common.h"
+
+
+/******************************************************************************/
+/* Forward declarations */
+/******************************************************************************/
+
+static int bladerf2_read_flash_vctcxo_trim(struct bladerf *dev, uint16_t *trim);
+
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+
+// clang-format off
+
+/* REFIN frequency range */
+static struct bladerf_range const bladerf2_pll_refclk_range = {
+ FIELD_INIT(.min, 5000000),
+ FIELD_INIT(.max, 300000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Loopback modes */
+static struct bladerf_loopback_modes const bladerf2_loopback_modes[] = {
+ {
+ FIELD_INIT(.name, "none"),
+ FIELD_INIT(.mode, BLADERF_LB_NONE)
+ },
+ {
+ FIELD_INIT(.name, "firmware"),
+ FIELD_INIT(.mode, BLADERF_LB_FIRMWARE)
+ },
+ {
+ FIELD_INIT(.name, "rf_bist"),
+ FIELD_INIT(.mode, BLADERF_LB_RFIC_BIST)
+ },
+};
+// clang-format on
+
+
+/******************************************************************************/
+/* Low-level Initialization */
+/******************************************************************************/
+
+static int _bladerf2_initialize(struct bladerf *dev)
+{
+ struct bladerf2_board_data *board_data;
+ struct bladerf_version required_fw_version, required_fpga_version;
+ int status;
+
+ /* Test for uninitialized dev struct */
+ NULL_CHECK(dev);
+ NULL_CHECK(dev->board_data);
+
+ /* Initialize board_data struct and members */
+ board_data = dev->board_data;
+
+ /* Read FPGA version */
+ CHECK_STATUS(
+ dev->backend->get_fpga_version(dev, &board_data->fpga_version));
+
+ log_verbose("Read FPGA version: %s\n", board_data->fpga_version.describe);
+
+ /* Determine FPGA capabilities */
+ board_data->capabilities |=
+ bladerf2_get_fpga_capabilities(&board_data->fpga_version);
+
+ log_verbose("Capability mask after FPGA load: 0x%016" PRIx64 "\n",
+ board_data->capabilities);
+
+ /* If the FPGA version check fails, just warn, but don't error out.
+ *
+ * If an error code caused this function to bail out, it would prevent a
+ * user from being able to unload and reflash a bitstream being
+ * "autoloaded" from SPI flash. */
+ status =
+ version_check(&bladerf2_fw_compat_table, &bladerf2_fpga_compat_table,
+ &board_data->fw_version, &board_data->fpga_version,
+ &required_fw_version, &required_fpga_version);
+ if (status < 0) {
+#if LOGGING_ENABLED
+ if (BLADERF_ERR_UPDATE_FPGA == status) {
+ log_warning(
+ "FPGA v%u.%u.%u was detected. Firmware v%u.%u.%u "
+ "requires FPGA v%u.%u.%u or later. Please load a "
+ "different FPGA version before continuing.\n\n",
+ board_data->fpga_version.major, board_data->fpga_version.minor,
+ board_data->fpga_version.patch, board_data->fw_version.major,
+ board_data->fw_version.minor, board_data->fw_version.patch,
+ required_fpga_version.major, required_fpga_version.minor,
+ required_fpga_version.patch);
+ } else if (BLADERF_ERR_UPDATE_FW == status) {
+ log_warning(
+ "FPGA v%u.%u.%u was detected, which requires firmware "
+ "v%u.%u.%u or later. The device firmware is currently "
+ "v%u.%u.%u. Please upgrade the device firmware before "
+ "continuing.\n\n",
+ board_data->fpga_version.major, board_data->fpga_version.minor,
+ board_data->fpga_version.patch, required_fw_version.major,
+ required_fw_version.minor, required_fw_version.patch,
+ board_data->fw_version.major, board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+#endif
+ }
+
+ /* Set FPGA packet protocol */
+ CHECK_STATUS(
+ dev->backend->set_fpga_protocol(dev, BACKEND_FPGA_PROTOCOL_NIOSII));
+
+ /* Initialize INA219 */
+ CHECK_STATUS(ina219_init(dev, ina219_r_shunt));
+
+ /* Set tuning mode. This will trigger initialization of the RFIC.
+ *
+ * RFIC initialization consists of:
+ * - RFFE register initialization
+ * - RFIC initialization
+ * - Setting initial frequency
+ * - Setting up FIR filters
+ * - Disabling RX and TX on the RFIC
+ * - Muting the TX
+ */
+ CHECK_STATUS(dev->board->set_tuning_mode(dev, default_tuning_mode(dev)));
+
+ /* Update device state */
+ board_data->state = STATE_INITIALIZED;
+
+ /* Initialize VCTCXO trim DAC to stored value */
+ uint16_t *trimval = &(board_data->trimdac_stored_value);
+
+ CHECK_STATUS(bladerf2_read_flash_vctcxo_trim(dev, trimval));
+ CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_write(dev, *trimval));
+
+ board_data->trim_source = TRIM_SOURCE_TRIM_DAC;
+
+ /* Configure PLL */
+ CHECK_STATUS(bladerf_set_pll_refclk(dev, BLADERF_REFIN_DEFAULT));
+
+ /* Reset current quick tune profile number */
+ board_data->quick_tune_rx_profile = 0;
+ board_data->quick_tune_tx_profile = 0;
+
+ log_debug("%s: complete\n", __FUNCTION__);
+
+ return 0;
+}
+
+
+/******************************************************************************
+ * Generic Board Functions *
+ ******************************************************************************/
+
+/******************************************************************************/
+/* Matches */
+/******************************************************************************/
+
+static bool bladerf2_matches(struct bladerf *dev)
+{
+ NULL_CHECK(dev);
+ NULL_CHECK(dev->backend);
+
+ uint16_t vid, pid;
+ int status;
+
+ status = dev->backend->get_vid_pid(dev, &vid, &pid);
+ if (status < 0) {
+ log_error("%s: get_vid_pid returned status %s\n", __FUNCTION__,
+ bladerf_strerror(status));
+ return false;
+ }
+
+ if (USB_NUAND_VENDOR_ID == vid && USB_NUAND_BLADERF2_PRODUCT_ID == pid) {
+ return true;
+ }
+
+ return false;
+}
+
+
+/******************************************************************************/
+/* Open/close */
+/******************************************************************************/
+
+static int bladerf2_open(struct bladerf *dev, struct bladerf_devinfo *devinfo)
+{
+ NULL_CHECK(dev);
+ NULL_CHECK(dev->backend);
+
+ struct bladerf2_board_data *board_data;
+ struct bladerf_version required_fw_version;
+ char *full_path;
+ bladerf_dev_speed usb_speed;
+ size_t i;
+ int ready, status;
+
+ size_t const MAX_RETRIES = 30;
+
+ /* Allocate board data */
+ board_data = calloc(1, sizeof(struct bladerf2_board_data));
+ if (NULL == board_data) {
+ RETURN_ERROR_STATUS("calloc board_data", BLADERF_ERR_MEM);
+ }
+ dev->board_data = board_data;
+ board_data->phy = NULL;
+ board_data->rfic_init_params = (void *)&bladerf2_rfic_init_params;
+
+ /* Allocate flash architecture */
+ dev->flash_arch = calloc(1, sizeof(struct bladerf_flash_arch));
+ if (NULL == dev->flash_arch) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Initialize board data */
+ board_data->fpga_version.describe = board_data->fpga_version_str;
+ board_data->fw_version.describe = board_data->fw_version_str;
+
+ board_data->module_format[BLADERF_RX] = -1;
+ board_data->module_format[BLADERF_TX] = -1;
+
+ dev->flash_arch->status = STATUS_FLASH_UNINITIALIZED;
+ dev->flash_arch->manufacturer_id = 0x0;
+ dev->flash_arch->device_id = 0x0;
+
+ board_data->rfic_reset_on_close = false;
+
+ /* Read firmware version */
+ CHECK_STATUS(dev->backend->get_fw_version(dev, &board_data->fw_version));
+
+ log_verbose("Read Firmware version: %s\n", board_data->fw_version.describe);
+
+ /* Determine firmware capabilities */
+ board_data->capabilities |=
+ bladerf2_get_fw_capabilities(&board_data->fw_version);
+
+ log_verbose("Capability mask before FPGA load: 0x%016" PRIx64 "\n",
+ board_data->capabilities);
+
+ /* Update device state */
+ board_data->state = STATE_FIRMWARE_LOADED;
+
+ /* Wait until firmware is ready */
+ for (i = 0; i < MAX_RETRIES; i++) {
+ ready = dev->backend->is_fw_ready(dev);
+ if (ready != 1) {
+ if (0 == i) {
+ log_info("Waiting for device to become ready...\n");
+ } else {
+ log_debug("Retry %02u/%02u.\n", i + 1, MAX_RETRIES);
+ }
+ usleep(1000000);
+ } else {
+ break;
+ }
+ }
+
+ if (ready != 1) {
+ RETURN_ERROR_STATUS("is_fw_ready", BLADERF_ERR_TIMEOUT);
+ }
+
+ /* Determine data message size */
+ CHECK_STATUS(dev->backend->get_device_speed(dev, &usb_speed));
+
+ switch (usb_speed) {
+ case BLADERF_DEVICE_SPEED_SUPER:
+ board_data->msg_size = USB_MSG_SIZE_SS;
+ break;
+ case BLADERF_DEVICE_SPEED_HIGH:
+ board_data->msg_size = USB_MSG_SIZE_HS;
+ break;
+ default:
+ log_error("%s: unsupported device speed (%d)\n", __FUNCTION__,
+ usb_speed);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ /* Verify that we have a sufficent firmware version before continuing. */
+ status = version_check_fw(&bladerf2_fw_compat_table,
+ &board_data->fw_version, &required_fw_version);
+ if (status != 0) {
+#ifdef LOGGING_ENABLED
+ if (BLADERF_ERR_UPDATE_FW == status) {
+ log_warning("Firmware v%u.%u.%u was detected. libbladeRF v%s "
+ "requires firmware v%u.%u.%u or later. An upgrade via "
+ "the bootloader is required.\n\n",
+ &board_data->fw_version.major,
+ &board_data->fw_version.minor,
+ &board_data->fw_version.patch, LIBBLADERF_VERSION,
+ required_fw_version.major, required_fw_version.minor,
+ required_fw_version.patch);
+ }
+#endif
+ return status;
+ }
+
+ /* Probe SPI flash architecture information */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FW_FLASH_ID)) {
+ status = spi_flash_read_flash_id(dev, &dev->flash_arch->manufacturer_id,
+ &dev->flash_arch->device_id);
+ if (status < 0) {
+ log_error("Failed to probe SPI flash ID information.\n");
+ }
+ } else {
+ log_debug("FX3 firmware v%u.%u.%u does not support SPI flash ID. A "
+ "firmware update is recommended in order to probe the SPI "
+ "flash ID information.\n",
+ board_data->fw_version.major, board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+
+ /* Decode SPI flash ID information to figure out its architecture.
+ * We need to know a little about the flash architecture before we can
+ * read anything from it, including FPGA size and other cal data.
+ * If the firmware does not have the capability to get the flash ID,
+ * sane defaults will be chosen.
+ *
+ * Not checking return code because it is irrelevant. */
+ spi_flash_decode_flash_architecture(dev, &board_data->fpga_size);
+
+ /* Get FPGA size */
+ status = spi_flash_read_fpga_size(dev, &board_data->fpga_size);
+ if (status < 0) {
+ log_warning("Failed to get FPGA size %s\n", bladerf_strerror(status));
+ }
+
+ if (getenv("BLADERF_FORCE_FPGA_A9")) {
+ log_info("BLADERF_FORCE_FPGA_A9 is set, assuming A9 FPGA\n");
+ board_data->fpga_size = BLADERF_FPGA_A9;
+ }
+
+ /* If the flash architecture could not be decoded earlier, try again now
+ * that the FPGA size is known. */
+ if (dev->flash_arch->status != STATUS_SUCCESS) {
+ status =
+ spi_flash_decode_flash_architecture(dev, &board_data->fpga_size);
+ if (status < 0) {
+ log_debug("Assumptions were made about the SPI flash architecture! "
+ "Flash commands may not function as expected.\n");
+ }
+ }
+
+ /* Skip further work if BLADERF_FORCE_NO_FPGA_PRESENT is set */
+ if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) {
+ log_debug("Skipping FPGA configuration and initialization - "
+ "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n");
+ return 0;
+ }
+
+ /* Check if FPGA is configured */
+ status = dev->backend->is_fpga_configured(dev);
+ if (status < 0) {
+ RETURN_ERROR_STATUS("is_fpga_configured", status);
+ } else if (1 == status) {
+ board_data->state = STATE_FPGA_LOADED;
+ } else if (status != 1 && BLADERF_FPGA_UNKNOWN == board_data->fpga_size) {
+ log_warning("Unknown FPGA size. Skipping FPGA configuration...\n");
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ } else if (status != 1) {
+ /* Try searching for an FPGA in the config search path */
+ switch (board_data->fpga_size) {
+ case BLADERF_FPGA_A4:
+ full_path = file_find("hostedxA4.rbf");
+ break;
+
+ case BLADERF_FPGA_A5:
+ full_path = file_find("hostedxA5.rbf");
+ break;
+
+ case BLADERF_FPGA_A9:
+ full_path = file_find("hostedxA9.rbf");
+ break;
+
+ default:
+ log_error("%s: invalid FPGA size: %d\n", __FUNCTION__,
+ board_data->fpga_size);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (full_path != NULL) {
+ uint8_t *buf;
+ size_t buf_size;
+
+ log_debug("Loading FPGA from: %s\n", full_path);
+
+ status = file_read_buffer(full_path, &buf, &buf_size);
+ free(full_path);
+ full_path = NULL;
+
+ if (status != 0) {
+ RETURN_ERROR_STATUS("file_read_buffer", status);
+ }
+
+ CHECK_STATUS(dev->backend->load_fpga(dev, buf, buf_size));
+
+ board_data->state = STATE_FPGA_LOADED;
+ } else {
+ log_warning("FPGA bitstream file not found.\n");
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ }
+ }
+
+ /* Initialize the board */
+ CHECK_STATUS(_bladerf2_initialize(dev));
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ /* Cancel any pending re-tunes that may have been left over as the
+ * result of a user application crashing or forgetting to call
+ * bladerf_close() */
+
+ bladerf_direction dir;
+
+ FOR_EACH_DIRECTION(dir)
+ {
+ size_t idx;
+ bladerf_channel ch;
+
+ FOR_EACH_CHANNEL(dir, 1, idx, ch)
+ {
+ CHECK_STATUS(dev->board->cancel_scheduled_retunes(dev, ch));
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void bladerf2_close(struct bladerf *dev)
+{
+ if (dev != NULL) {
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_flash_arch *flash_arch = dev->flash_arch;
+
+ if (board_data != NULL) {
+ bladerf_direction dir;
+
+ FOR_EACH_DIRECTION(dir)
+ {
+ size_t idx;
+ bladerf_channel ch;
+
+ FOR_EACH_CHANNEL(dir, 1, idx, ch)
+ {
+ sync_deinit(&board_data->sync[ch]);
+
+ /* Cancel scheduled retunes here to avoid the device
+ * retuning underneath the user should they open it again in
+ * the future.
+ *
+ * This is intended to help developers avoid a situation
+ * during debugging where they schedule "far" into the
+ * future, but hit a case where their program aborts or
+ * exits early. If we do not cancel these scheduled retunes,
+ * the device could start up and/or "unexpectedly" switch to
+ * a different frequency.
+ */
+ if (dev->backend->is_fpga_configured(dev) &&
+ have_cap(board_data->capabilities,
+ BLADERF_CAP_SCHEDULED_RETUNE)) {
+ dev->board->cancel_scheduled_retunes(dev, ch);
+ }
+ }
+ }
+
+ if (board_data->state >= STATE_INITIALIZED && rfic != NULL) {
+ if (board_data->rfic_reset_on_close) {
+ /* We need to fully de-initialize the RFIC, so it can be
+ * reset on the next open. This seems to be necessary after
+ * doing direct SPI control of the RFIC.
+ */
+ rfic->deinitialize(dev);
+ } else {
+ /* Put the RFIC into standby mode. This will shut down any
+ * current RF activity, but it will not lose the RF state.
+ */
+ rfic->standby(dev);
+ }
+ }
+
+ free(board_data);
+ board_data = NULL;
+ }
+
+ if (flash_arch != NULL) {
+ free(flash_arch);
+ flash_arch = NULL;
+ }
+ }
+}
+
+
+/******************************************************************************/
+/* Properties */
+/******************************************************************************/
+
+static bladerf_dev_speed bladerf2_device_speed(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ bladerf_dev_speed usb_speed;
+ int status;
+
+ status = dev->backend->get_device_speed(dev, &usb_speed);
+ if (status < 0) {
+ log_error("%s: get_device_speed failed: %s\n", __FUNCTION__,
+ bladerf_strerror(status));
+ return BLADERF_DEVICE_SPEED_UNKNOWN;
+ }
+
+ return usb_speed;
+}
+
+static int bladerf2_get_serial(struct bladerf *dev, char *serial)
+{
+ CHECK_BOARD_STATE(STATE_UNINITIALIZED);
+ NULL_CHECK(serial);
+
+ // TODO: don't use strcpy
+ strcpy(serial, dev->ident.serial);
+
+ return 0;
+}
+
+static int bladerf2_get_fpga_size(struct bladerf *dev, bladerf_fpga_size *size)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(size);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ *size = board_data->fpga_size;
+
+ return 0;
+}
+
+static int bladerf2_get_fpga_bytes(struct bladerf *dev, size_t *size)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(size);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ switch (board_data->fpga_size) {
+ case BLADERF_FPGA_A4:
+ *size = 2632660;
+ break;
+
+ case BLADERF_FPGA_A5:
+ *size = 4244820;
+ break;
+
+ case BLADERF_FPGA_A9:
+ *size = 12858972;
+ break;
+
+ default:
+ log_debug("%s: unknown fpga_size: %x\n", board_data->fpga_size);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_flash_size(struct bladerf *dev,
+ uint32_t *size,
+ bool *is_guess)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(size);
+ NULL_CHECK(is_guess);
+
+ *size = dev->flash_arch->tsize_bytes;
+ *is_guess = (dev->flash_arch->status != STATUS_SUCCESS);
+
+ return 0;
+}
+
+static int bladerf2_is_fpga_configured(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return dev->backend->is_fpga_configured(dev);
+}
+
+static int bladerf2_get_fpga_source(struct bladerf *dev,
+ bladerf_fpga_source *source)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(source);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FW_FPGA_SOURCE)) {
+ log_debug("%s: not supported by firmware\n", __FUNCTION__);
+ *source = BLADERF_FPGA_SOURCE_UNKNOWN;
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ *source = dev->backend->get_fpga_source(dev);
+
+ return 0;
+}
+
+static uint64_t bladerf2_get_capabilities(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_UNINITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->capabilities;
+}
+
+static size_t bladerf2_get_channel_count(struct bladerf *dev,
+ bladerf_direction dir)
+{
+ return 2;
+}
+
+
+/******************************************************************************/
+/* Versions */
+/******************************************************************************/
+
+static int bladerf2_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(version);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ memcpy(version, &board_data->fpga_version, sizeof(*version));
+
+ return 0;
+}
+
+static int bladerf2_get_fw_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(version);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ memcpy(version, &board_data->fw_version, sizeof(*version));
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Enable/disable */
+/******************************************************************************/
+
+static int bladerf2_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool enable)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->enable_module(dev, ch, enable);
+}
+
+
+/******************************************************************************/
+/* Gain */
+/******************************************************************************/
+
+static int bladerf2_get_gain_stage_range(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ struct bladerf_range const **range)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(range);
+
+ struct bladerf_gain_range const *ranges = NULL;
+ bladerf_frequency frequency = 0;
+ size_t i, ranges_len;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ ranges = bladerf2_tx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges);
+ } else {
+ ranges = bladerf2_rx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges);
+ }
+
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &frequency));
+
+ for (i = 0; i < ranges_len; ++i) {
+ struct bladerf_gain_range const *r = &(ranges[i]);
+ struct bladerf_range const *rfreq = &(r->frequency);
+
+ // if the frequency range matches, and either:
+ // both the range name and the stage name are null, or
+ // neither name is null and the strings match
+ // then we found our match
+ if (is_within_range(rfreq, frequency) &&
+ ((NULL == r->name && NULL == stage) ||
+ (r->name != NULL && stage != NULL &&
+ (strcmp(r->name, stage) == 0)))) {
+ *range = &(r->gain);
+ return 0;
+ }
+ }
+
+ return BLADERF_ERR_INVAL;
+}
+
+static int bladerf2_get_gain_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ return dev->board->get_gain_stage_range(dev, ch, NULL, range);
+}
+
+static int bladerf2_get_gain_modes(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_gain_modes const **modes)
+{
+ struct bladerf_gain_modes const *mode_infos;
+ unsigned int mode_infos_len;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ mode_infos = NULL;
+ mode_infos_len = 0;
+ } else {
+ mode_infos = bladerf2_rx_gain_modes;
+ mode_infos_len = ARRAY_SIZE(bladerf2_rx_gain_modes);
+ }
+
+ if (modes != NULL) {
+ *modes = mode_infos;
+ }
+
+ return mode_infos_len;
+}
+
+static int bladerf2_get_gain_stages(struct bladerf *dev,
+ bladerf_channel ch,
+ char const **stages,
+ size_t count)
+{
+ NULL_CHECK(dev);
+
+ struct bladerf_gain_range const *ranges = NULL;
+ char const **names = NULL;
+ size_t stage_count = 0;
+ unsigned int ranges_len;
+ size_t i, j;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ *stages = NULL;
+ return 0;
+ });
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ ranges = bladerf2_tx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges);
+ } else {
+ ranges = bladerf2_rx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges);
+ }
+
+ names = calloc(ranges_len + 1, sizeof(char *));
+ if (NULL == names) {
+ RETURN_ERROR_STATUS("calloc names", BLADERF_ERR_MEM);
+ }
+
+ // Iterate through all the ranges...
+ for (i = 0; i < ranges_len; ++i) {
+ struct bladerf_gain_range const *range = &(ranges[i]);
+
+ if (NULL == range->name) {
+ // this is system gain, skip it
+ continue;
+ }
+
+ // loop through the output array to make sure we record this one
+ for (j = 0; j < ranges_len; ++j) {
+ if (NULL == names[j]) {
+ // Made it to the end of names without finding a match
+ names[j] = range->name;
+ ++stage_count;
+ break;
+ } else if (strcmp(range->name, names[j]) == 0) {
+ // found a match, break
+ break;
+ }
+ }
+ }
+
+ if (NULL != stages && 0 != count) {
+ count = (stage_count < count) ? stage_count : count;
+
+ for (i = 0; i < count; i++) {
+ stages[i] = names[i];
+ }
+ }
+
+ free((char **)names);
+ return (int)stage_count;
+}
+
+static int bladerf2_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(mode);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_gain_mode(dev, ch, mode);
+}
+
+static int bladerf2_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->set_gain_mode(dev, ch, mode);
+}
+
+static int bladerf2_get_gain(struct bladerf *dev, bladerf_channel ch, int *gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(gain);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_gain(dev, ch, gain);
+}
+
+static int bladerf2_set_gain(struct bladerf *dev, bladerf_channel ch, int gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_gain_range(dev, ch, &range));
+
+ return board_data->rfic->set_gain(dev, ch, clamp_to_range(range, gain));
+}
+
+static int bladerf2_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(stage);
+ NULL_CHECK(gain);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_gain_stage(dev, ch, stage, gain);
+}
+
+static int bladerf2_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(stage);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ return board_data->rfic->set_gain_stage(dev, ch, stage,
+ clamp_to_range(range, gain));
+}
+
+
+/******************************************************************************/
+/* Sample Rate */
+/******************************************************************************/
+
+static int bladerf2_get_sample_rate_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ NULL_CHECK(range);
+
+ *range = &bladerf2_sample_rate_range_base;
+
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) {
+ *range = &bladerf2_sample_rate_range_oversample;
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(rate);
+
+ bladerf_sample_rate integer_rate;
+
+ CHECK_STATUS(dev->board->get_sample_rate(dev, ch, &integer_rate));
+
+ rate->integer = integer_rate;
+ rate->num = 0;
+ rate->den = 1;
+
+ return 0;
+}
+
+static int bladerf2_set_rational_sample_rate(
+ struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(rate);
+
+ bladerf_sample_rate integer_rate, actual_integer_rate;
+
+ integer_rate = (bladerf_sample_rate)(rate->integer + rate->num / rate->den);
+
+ CHECK_STATUS(dev->board->set_sample_rate(dev, ch, integer_rate,
+ &actual_integer_rate));
+
+ if (actual != NULL) {
+ CHECK_STATUS(dev->board->get_rational_sample_rate(dev, ch, actual));
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(rate);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ bladerf_sample_rate double_rate;
+ CHECK_STATUS(board_data->rfic->get_sample_rate(dev, ch, rate));
+
+ /* OVERSAMPLE feature reports half of the actual
+ sample rate so we have to double it on return */
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) {
+ double_rate = *rate*2;
+ *rate = double_rate;
+ }
+
+ return 0;
+}
+
+static int bladerf2_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate,
+ bladerf_sample_rate *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const *range = NULL;
+ bladerf_sample_rate current;
+ bool old_low, new_low;
+ bladerf_rfic_rxfir rxfir;
+ bladerf_rfic_txfir txfir;
+
+ /* Range checking */
+ CHECK_STATUS(dev->board->get_sample_rate_range(dev, ch, &range));
+
+ if (!is_within_range(range, rate)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ /* Feature range check */
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE &&
+ !is_within_range(&bladerf2_sample_rate_range_oversample, rate)) {
+ log_error("Sample rate outside of OVERSAMPLE feature range\n");
+ return BLADERF_ERR_RANGE;
+ } else if (dev->feature == BLADERF_FEATURE_DEFAULT &&
+ !is_within_range(&bladerf2_sample_rate_range_base, rate)) {
+ log_error("Sample rate outside of DEFAULT feature range\n");
+ return BLADERF_ERR_RANGE;
+ }
+
+ /* Get current sample rate, and check it against the low-rate range */
+ CHECK_STATUS(dev->board->get_sample_rate(dev, ch, &current));
+
+ old_low = is_within_range(&bladerf2_sample_rate_range_4x, current);
+ new_low = is_within_range(&bladerf2_sample_rate_range_4x, rate);
+
+ /* Get current filter status */
+ if (new_low || old_low) {
+ CHECK_STATUS(
+ rfic->get_filter(dev, BLADERF_CHANNEL_RX(0), &rxfir, NULL));
+ CHECK_STATUS(
+ rfic->get_filter(dev, BLADERF_CHANNEL_TX(0), NULL, &txfir));
+ }
+
+ /* If the requested sample rate is below the native range, we must implement
+ * a 4x decimation/interpolation filter on the RFIC. */
+ if (new_low) {
+ bool fir_set_failed = false;
+ int status;
+
+ if (rxfir != BLADERF_RFIC_RXFIR_DEC4 ||
+ txfir != BLADERF_RFIC_TXFIR_INT4) {
+ log_debug("%s: enabling 4x decimation/interpolation filters\n",
+ __FUNCTION__);
+
+ /* Intermidiate sample rate assignment to circumvent rfic->set_filter error */
+ if ((current > 40e6 && rate < 2083334) || (rate > 40e6 && current < 2083334)) {
+ CHECK_STATUS(rfic->set_sample_rate(dev, ch, 30e6));
+ }
+
+ status = rfic->set_filter(dev, BLADERF_CHANNEL_RX(0),
+ BLADERF_RFIC_RXFIR_DEC4, 0);
+ if (status < 0) {
+ log_error("%s: could not set RX filter mode: %s\n",
+ __FUNCTION__, bladerf_strerror(status));
+ fir_set_failed = true;
+ }
+
+ status = rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0,
+ BLADERF_RFIC_TXFIR_INT4);
+ if (status < 0) {
+ log_error("%s: could not set TX filter mode: %s\n",
+ __FUNCTION__, bladerf_strerror(status));
+ fir_set_failed = true;
+ }
+ }
+
+ /* Try to restore default operations if there was a failure */
+ if (fir_set_failed) {
+ log_debug("%s: attempting to reset filters to default...\n",
+ __FUNCTION__);
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0),
+ BLADERF_RFIC_RXFIR_DEFAULT, 0));
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0,
+ BLADERF_RFIC_TXFIR_DEFAULT));
+
+ return BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ /* The AD9361 doubles the sampling rate in OVERSAMPLE mode
+ so we must halve the sampling rate prior to setting */
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) {
+ rate /= 2;
+ }
+
+ /* Set the sample rate */
+ CHECK_STATUS(rfic->set_sample_rate(dev, ch, rate));
+
+ /* If the previous sample rate was below the native range, but the new one
+ * isn't, switch back to the default filters. */
+ if (old_low && !new_low) {
+ if (rxfir != BLADERF_RFIC_RXFIR_DEFAULT ||
+ txfir != BLADERF_RFIC_TXFIR_DEFAULT) {
+ log_debug("%s: disabling 4x decimation/interpolation filters\n",
+ __FUNCTION__);
+
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0),
+ BLADERF_RFIC_RXFIR_DEFAULT, 0));
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0,
+ BLADERF_RFIC_TXFIR_DEFAULT));
+ }
+ }
+
+ /* If requested, fetch the new sample rate and return it. */
+ if (actual != NULL) {
+ CHECK_STATUS(dev->board->get_sample_rate(dev, ch, actual));
+ }
+
+ /* Warn the user if this isn't achievable */
+ check_total_sample_rate(dev);
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+static int bladerf2_get_bandwidth_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ NULL_CHECK(range);
+
+ *range = &bladerf2_bandwidth_range;
+
+ return 0;
+}
+
+static int bladerf2_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_bandwidth(dev, ch, bandwidth);
+}
+
+static int bladerf2_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) {
+ log_warning("bandwidth assignements with oversample feature enabled yields unkown results\n");
+ }
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->set_bandwidth(dev, ch, bandwidth, actual);
+}
+
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+static int bladerf2_get_frequency_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ NULL_CHECK(range);
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ *range = &bladerf2_tx_frequency_range;
+ } else {
+ *range = &bladerf2_rx_frequency_range;
+ }
+
+ return 0;
+}
+
+static int bladerf2_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->select_band(dev, ch, frequency);
+}
+
+static int bladerf2_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(frequency);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_frequency(dev, ch, frequency);
+}
+
+static int bladerf2_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->set_frequency(dev, ch, frequency);
+}
+
+
+/******************************************************************************/
+/* RF ports */
+/******************************************************************************/
+
+static int bladerf2_set_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct bladerf_rfic_port_name_map const *pm = NULL;
+ unsigned int pm_len = 0;
+ uint32_t port_id = UINT32_MAX;
+ size_t i;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ pm = bladerf2_tx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_tx_port_map);
+ } else {
+ pm = bladerf2_rx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_rx_port_map);
+ }
+
+ for (i = 0; i < pm_len; i++) {
+ if (strcmp(pm[i].name, port) == 0) {
+ port_id = pm[i].id;
+ break;
+ }
+ }
+
+ if (UINT32_MAX == port_id) {
+ RETURN_INVAL("port", "is not valid");
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_set_tx_rf_port_output(phy, port_id));
+ } else {
+ CHECK_AD936X(ad9361_set_rx_rf_port_input(phy, port_id));
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ char const **port)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(port);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct bladerf_rfic_port_name_map const *pm = NULL;
+ unsigned int pm_len = 0;
+ uint32_t port_id;
+ bool ok;
+ size_t i;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ pm = bladerf2_tx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_tx_port_map);
+ CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &port_id));
+ } else {
+ pm = bladerf2_rx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_rx_port_map);
+ CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &port_id));
+ }
+
+ ok = false;
+
+ for (i = 0; i < pm_len; i++) {
+ if (port_id == pm[i].id) {
+ *port = pm[i].name;
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ *port = "unknown";
+ log_error("%s: unexpected port_id %u\n", __FUNCTION__, port_id);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_rf_ports(struct bladerf *dev,
+ bladerf_channel ch,
+ char const **ports,
+ unsigned int count)
+{
+ struct bladerf_rfic_port_name_map const *pm = NULL;
+ unsigned int pm_len;
+ size_t i;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ *ports = NULL;
+ return 0;
+ });
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ pm = bladerf2_tx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_tx_port_map);
+ } else {
+ pm = bladerf2_rx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_rx_port_map);
+ }
+
+ if (ports != NULL) {
+ count = (pm_len < count) ? pm_len : count;
+
+ for (i = 0; i < count; i++) {
+ ports[i] = pm[i].name;
+ }
+ }
+
+ return pm_len;
+}
+
+
+/******************************************************************************/
+/* Scheduled Tuning */
+/******************************************************************************/
+
+static int bladerf2_get_quick_tune(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(quick_tune);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct band_port_map const *pm = NULL;
+
+ bladerf_frequency freq;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) &&
+ ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) {
+ RETURN_INVAL_ARG("channel", ch, "is not valid");
+ }
+
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &freq));
+
+ pm = _get_band_port_map_by_freq(ch, freq);
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (board_data->quick_tune_tx_profile < NUM_BBP_FASTLOCK_PROFILES) {
+ /* Assign Nios and RFFE profile numbers */
+ quick_tune->nios_profile = board_data->quick_tune_tx_profile++;
+ log_verbose("Quick tune assigned Nios TX fast lock index: %u\n",
+ quick_tune->nios_profile);
+ quick_tune->rffe_profile =
+ quick_tune->nios_profile % NUM_RFFE_FASTLOCK_PROFILES;
+ log_verbose("Quick tune assigned RFFE TX fast lock index: %u\n",
+ quick_tune->rffe_profile);
+ } else {
+ log_error("Reached maximum number of TX quick tune profiles.");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Create a fast lock profile in the RFIC */
+ CHECK_STATUS(
+ rfic->store_fastlock_profile(dev, ch, quick_tune->rffe_profile));
+
+ /* Save a copy of the TX fast lock profile to the Nios */
+ dev->backend->rffe_fastlock_save(dev, true, quick_tune->rffe_profile,
+ quick_tune->nios_profile);
+
+ /* Set the TX band */
+ quick_tune->port = (pm->rfic_port << 6);
+
+ /* Set the TX SPDTs */
+ quick_tune->spdt = (pm->spdt << 6) | (pm->spdt << 4);
+
+ } else {
+ if (board_data->quick_tune_rx_profile < NUM_BBP_FASTLOCK_PROFILES) {
+ /* Assign Nios and RFFE profile numbers */
+ quick_tune->nios_profile = board_data->quick_tune_rx_profile++;
+ log_verbose("Quick tune assigned Nios RX fast lock index: %u\n",
+ quick_tune->nios_profile);
+ quick_tune->rffe_profile =
+ quick_tune->nios_profile % NUM_RFFE_FASTLOCK_PROFILES;
+ log_verbose("Quick tune assigned RFFE RX fast lock index: %u\n",
+ quick_tune->rffe_profile);
+ } else {
+ log_error("Reached maximum number of RX quick tune profiles.");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Create a fast lock profile in the RFIC */
+ CHECK_STATUS(
+ rfic->store_fastlock_profile(dev, ch, quick_tune->rffe_profile));
+
+ /* Save a copy of the RX fast lock profile to the Nios */
+ dev->backend->rffe_fastlock_save(dev, false, quick_tune->rffe_profile,
+ quick_tune->nios_profile);
+
+ /* Set the RX bit */
+ quick_tune->port = NIOS_PKT_RETUNE2_PORT_IS_RX_MASK;
+
+ /* Set the RX band */
+ if (pm->rfic_port < 3) {
+ quick_tune->port |= (3 << (pm->rfic_port << 1));
+ } else {
+ quick_tune->port |= (1 << (pm->rfic_port - 3));
+ }
+
+ /* Set the RX SPDTs */
+ quick_tune->spdt = (pm->spdt << 2) | (pm->spdt);
+ }
+
+ /* Workaround: the RFIC can end up in a bad state after fastlock use, and
+ * needs to be reset and re-initialized. This is likely due to our direct
+ * SPI writes causing state incongruence. */
+ board_data->rfic_reset_on_close = true;
+
+ return 0;
+}
+
+static int bladerf2_schedule_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(quick_tune);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ log_debug("This FPGA version (%u.%u.%u) does not support "
+ "scheduled retunes.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dev->backend->retune2(dev, ch, timestamp, quick_tune->nios_profile,
+ quick_tune->rffe_profile, quick_tune->port,
+ quick_tune->spdt);
+}
+
+static int bladerf2_cancel_scheduled_retunes(struct bladerf *dev,
+ bladerf_channel ch)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ log_debug("This FPGA version (%u.%u.%u) does not support "
+ "scheduled retunes.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dev->backend->retune2(dev, ch, NIOS_PKT_RETUNE2_CLEAR_QUEUE, 0, 0, 0,
+ 0);
+}
+
+
+/******************************************************************************/
+/* DC/Phase/Gain Correction */
+/******************************************************************************/
+
+// clang-format off
+static const struct {
+ struct {
+ uint16_t reg[2]; /* Low/High band */
+ unsigned int shift; /* Value scaling */
+ } corr[4];
+} ad9361_correction_reg_table[4] = {
+ [BLADERF_CHANNEL_RX(0)].corr = {
+ [BLADERF_CORR_DCOFF_I] = {
+ FIELD_INIT(.reg, {0, 0}), /* More complex look up */
+ FIELD_INIT(.shift, 0),
+ },
+ [BLADERF_CORR_DCOFF_Q] = {
+ FIELD_INIT(.reg, {0, 0}), /* More complex look up */
+ FIELD_INIT(.shift, 0),
+ },
+ [BLADERF_CORR_PHASE] = {
+ FIELD_INIT(.reg, { AD936X_REG_RX1_INPUT_A_PHASE_CORR,
+ AD936X_REG_RX1_INPUT_BC_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ },
+ [BLADERF_CORR_GAIN] = {
+ FIELD_INIT(.reg, { AD936X_REG_RX1_INPUT_A_GAIN_CORR,
+ AD936X_REG_RX1_INPUT_BC_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ }
+ },
+ [BLADERF_CHANNEL_RX(1)].corr = {
+ [BLADERF_CORR_DCOFF_I] = {
+ FIELD_INIT(.reg, {0, 0}), /* More complex look up */
+ FIELD_INIT(.shift, 0),
+ },
+ [BLADERF_CORR_DCOFF_Q] = {
+ FIELD_INIT(.reg, {0, 0}), /* More complex look up */
+ FIELD_INIT(.shift, 0),
+ },
+ [BLADERF_CORR_PHASE] = {
+ FIELD_INIT(.reg, { AD936X_REG_RX2_INPUT_A_PHASE_CORR,
+ AD936X_REG_RX2_INPUT_BC_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ },
+ [BLADERF_CORR_GAIN] = {
+ FIELD_INIT(.reg, { AD936X_REG_RX2_INPUT_A_GAIN_CORR,
+ AD936X_REG_RX2_INPUT_BC_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ }
+ },
+ [BLADERF_CHANNEL_TX(0)].corr = {
+ [BLADERF_CORR_DCOFF_I] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_OFFSET_I,
+ AD936X_REG_TX1_OUT_2_OFFSET_I }),
+ FIELD_INIT(.shift, 5),
+ },
+ [BLADERF_CORR_DCOFF_Q] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_OFFSET_Q,
+ AD936X_REG_TX1_OUT_2_OFFSET_Q }),
+ FIELD_INIT(.shift, 5),
+ },
+ [BLADERF_CORR_PHASE] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_PHASE_CORR,
+ AD936X_REG_TX1_OUT_2_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ },
+ [BLADERF_CORR_GAIN] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_GAIN_CORR,
+ AD936X_REG_TX1_OUT_2_GAIN_CORR }),
+ FIELD_INIT(.shift, 6),
+ }
+ },
+ [BLADERF_CHANNEL_TX(1)].corr = {
+ [BLADERF_CORR_DCOFF_I] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_OFFSET_I,
+ AD936X_REG_TX2_OUT_2_OFFSET_I }),
+ FIELD_INIT(.shift, 5),
+ },
+ [BLADERF_CORR_DCOFF_Q] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_OFFSET_Q,
+ AD936X_REG_TX2_OUT_2_OFFSET_Q }),
+ FIELD_INIT(.shift, 5),
+ },
+ [BLADERF_CORR_PHASE] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_PHASE_CORR,
+ AD936X_REG_TX2_OUT_2_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ },
+ [BLADERF_CORR_GAIN] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_GAIN_CORR,
+ AD936X_REG_TX2_OUT_2_GAIN_CORR }),
+ FIELD_INIT(.shift, 6),
+ }
+ },
+};
+
+static const struct {
+ uint16_t reg_top;
+ uint16_t reg_bot;
+} ad9361_correction_rx_dcoff_reg_table[4][2][2] = {
+ /* Channel 1 */
+ [BLADERF_CHANNEL_RX(0)] = {
+ /* A band */
+ {
+ /* I */
+ {AD936X_REG_INPUT_A_OFFSETS_1, AD936X_REG_RX1_INPUT_A_OFFSETS},
+ /* Q */
+ {AD936X_REG_RX1_INPUT_A_OFFSETS, AD936X_REG_RX1_INPUT_A_Q_OFFSET},
+ },
+ /* B/C band */
+ {
+ /* I */
+ {AD936X_REG_INPUT_BC_OFFSETS_1, AD936X_REG_RX1_INPUT_BC_OFFSETS},
+ /* Q */
+ {AD936X_REG_RX1_INPUT_BC_OFFSETS, AD936X_REG_RX1_INPUT_BC_Q_OFFSET},
+ },
+ },
+ /* Channel 2 */
+ [BLADERF_CHANNEL_RX(1)] = {
+ /* A band */
+ {
+ /* I */
+ {AD936X_REG_RX2_INPUT_A_I_OFFSET, AD936X_REG_RX2_INPUT_A_OFFSETS},
+ /* Q */
+ {AD936X_REG_RX2_INPUT_A_OFFSETS, AD936X_REG_INPUT_A_OFFSETS_1},
+ },
+ /* B/C band */
+ {
+ /* I */
+ {AD936X_REG_RX2_INPUT_BC_I_OFFSET, AD936X_REG_RX2_INPUT_BC_OFFSETS},
+ /* Q */
+ {AD936X_REG_RX2_INPUT_BC_OFFSETS, AD936X_REG_INPUT_BC_OFFSETS_1},
+ },
+ },
+};
+
+static const int ad9361_correction_force_bit[2][4][2] = {
+ [0] = {
+ [BLADERF_CORR_DCOFF_I] = {2, 6},
+ [BLADERF_CORR_DCOFF_Q] = {2, 6},
+ [BLADERF_CORR_PHASE] = {0, 4},
+ [BLADERF_CORR_GAIN] = {0, 4},
+ },
+ [1] = {
+ [BLADERF_CORR_DCOFF_I] = {3, 7},
+ [BLADERF_CORR_DCOFF_Q] = {3, 7},
+ [BLADERF_CORR_PHASE] = {1, 5},
+ [BLADERF_CORR_GAIN] = {1, 5},
+ },
+};
+// clang-format on
+
+static int bladerf2_get_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ int16_t *value)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(value);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ bool low_band;
+ uint16_t reg, data;
+ unsigned int shift;
+ int32_t val;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ /* Validate channel */
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) &&
+ ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) {
+ RETURN_INVAL_ARG("channel", ch, "is not valid");
+ }
+
+ /* Validate correction */
+ if (corr != BLADERF_CORR_DCOFF_I && corr != BLADERF_CORR_DCOFF_Q &&
+ corr != BLADERF_CORR_PHASE && corr != BLADERF_CORR_GAIN) {
+ RETURN_ERROR_STATUS("corr", BLADERF_ERR_UNSUPPORTED);
+ }
+
+ /* Look up band */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ uint32_t mode;
+
+ CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &mode));
+
+ low_band = (mode != AD936X_TXA);
+ } else {
+ uint32_t mode;
+
+ CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &mode));
+
+ /* Check if RX RF port mode is supported */
+ if (mode != AD936X_A_BALANCED && mode != AD936X_B_BALANCED &&
+ mode != AD936X_C_BALANCED) {
+ RETURN_ERROR_STATUS("mode", BLADERF_ERR_UNSUPPORTED);
+ }
+
+ low_band = (mode != AD936X_A_BALANCED);
+ }
+
+ if ((corr == BLADERF_CORR_DCOFF_I || corr == BLADERF_CORR_DCOFF_Q) &&
+ (ch & BLADERF_DIRECTION_MASK) == BLADERF_RX) {
+ /* RX DC offset corrections are stuffed in a super convoluted way in
+ * the register map. See AD9361 register map page 51. */
+ bool is_q = (corr == BLADERF_CORR_DCOFF_Q);
+ uint8_t data_top, data_bot;
+ uint16_t data;
+
+ /* Read top register */
+ val = ad9361_spi_read(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(top)", val);
+ }
+
+ data_top = val;
+
+ /* Read bottom register */
+ val = ad9361_spi_read(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(bottom)", val);
+ }
+
+ data_bot = val;
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ if (!is_q) {
+ /* top: | x x x x 9 8 7 6 | */
+ /* bottom: | 5 4 3 2 1 0 x x | */
+ data = ((data_top & 0xf) << 6) | (data_bot >> 2);
+ } else {
+ /* top: | x x x x x x 9 8 | */
+ /* bottom: | 7 6 5 4 3 2 1 0 | */
+ data = ((data_top & 0x3) << 8) | data_bot;
+ }
+ } else {
+ if (!is_q) {
+ /* top: | 9 8 7 6 5 4 3 2 | */
+ /* bottom: | x x x x x x 1 0 | */
+ data = (data_top << 2) | (data_bot & 0x3);
+ } else {
+ /* top: | x x 9 8 7 6 5 4 | */
+ /* bottom: | 3 2 1 0 x x x x | */
+ data = (data_top << 4) | (data_bot >> 4);
+ }
+ }
+
+ /* Scale 10-bit to 13-bit */
+ data = data << 3;
+
+ /* Sign extend value */
+ *value = data | ((data & (1 << 12)) ? 0xf000 : 0x0000);
+ } else {
+ /* Look up correction register and value shift in table */
+ reg = ad9361_correction_reg_table[ch].corr[corr].reg[low_band];
+ shift = ad9361_correction_reg_table[ch].corr[corr].shift;
+
+ /* Read register and scale value */
+ val = ad9361_spi_read(phy->spi, reg);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(reg)", val);
+ }
+
+ /* Scale 8-bit to 12-bit/13-bit */
+ data = val << shift;
+
+ /* Sign extend value */
+ if (shift == 5) {
+ *value = data | ((data & (1 << 12)) ? 0xf000 : 0x0000);
+ } else {
+ *value = data | ((data & (1 << 13)) ? 0xc000 : 0x0000);
+ }
+ }
+
+ return 0;
+}
+
+static int bladerf2_set_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ int16_t value)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ bool low_band;
+ uint16_t reg, data;
+ unsigned int shift;
+ int32_t val;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ /* Validate channel */
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) &&
+ ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) {
+ RETURN_INVAL_ARG("channel", ch, "is not valid");
+ }
+
+ /* Validate correction */
+ if (corr != BLADERF_CORR_DCOFF_I && corr != BLADERF_CORR_DCOFF_Q &&
+ corr != BLADERF_CORR_PHASE && corr != BLADERF_CORR_GAIN) {
+ RETURN_ERROR_STATUS("corr", BLADERF_ERR_UNSUPPORTED);
+ }
+
+ /* Look up band */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ uint32_t mode;
+
+ CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &mode));
+
+ low_band = (mode != AD936X_TXA);
+ } else {
+ uint32_t mode;
+
+ CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &mode));
+
+ /* Check if RX RF port mode is supported */
+ if (mode != AD936X_A_BALANCED && mode != AD936X_B_BALANCED &&
+ mode != AD936X_C_BALANCED) {
+ RETURN_ERROR_STATUS("mode", BLADERF_ERR_UNSUPPORTED);
+ }
+
+ low_band = (mode != AD936X_A_BALANCED);
+ }
+
+ if ((corr == BLADERF_CORR_DCOFF_I || corr == BLADERF_CORR_DCOFF_Q) &&
+ (ch & BLADERF_DIRECTION_MASK) == BLADERF_RX) {
+ /* RX DC offset corrections are stuffed in a super convoluted way in
+ * the register map. See AD9361 register map page 51. */
+ bool is_q = (corr == BLADERF_CORR_DCOFF_Q);
+ uint8_t data_top, data_bot;
+
+ /* Scale 13-bit to 10-bit */
+ data = value >> 3;
+
+ /* Read top register */
+ val = ad9361_spi_read(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(top)", val);
+ }
+
+ data_top = val;
+
+ /* Read bottom register */
+ val = ad9361_spi_read(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(bottom)", val);
+ }
+
+ data_bot = val;
+
+ /* Modify registers */
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ if (!is_q) {
+ /* top: | x x x x 9 8 7 6 | */
+ /* bottom: | 5 4 3 2 1 0 x x | */
+ data_top = (data_top & 0xf0) | ((data >> 6) & 0x0f);
+ data_bot = (data_bot & 0x03) | ((data & 0x3f) << 2);
+ } else {
+ /* top: | x x x x x x 9 8 | */
+ /* bottom: | 7 6 5 4 3 2 1 0 | */
+ data_top = (data_top & 0xfc) | ((data >> 8) & 0x03);
+ data_bot = data & 0xff;
+ }
+ } else {
+ if (!is_q) {
+ /* top: | 9 8 7 6 5 4 3 2 | */
+ /* bottom: | x x x x x x 1 0 | */
+ data_top = (data >> 2) & 0xff;
+ data_bot = (data_bot & 0xfc) | (data & 0x03);
+ } else {
+ /* top: | x x 9 8 7 6 5 4 | */
+ /* bottom: | 3 2 1 0 x x x x | */
+ data_top = (data & 0xc0) | ((data >> 4) & 0x3f);
+ data_bot = (data & 0x0f) | ((data & 0x0f) << 4);
+ }
+ }
+
+ /* Write top register */
+ CHECK_AD936X(ad9361_spi_write(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top,
+ data_top));
+
+ /* Write bottom register */
+ CHECK_AD936X(ad9361_spi_write(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot,
+ data_bot));
+ } else {
+ /* Look up correction register and value shift in table */
+ reg = ad9361_correction_reg_table[ch].corr[corr].reg[low_band];
+ shift = ad9361_correction_reg_table[ch].corr[corr].shift;
+
+ /* Scale 12-bit/13-bit to 8-bit */
+ data = (value >> shift) & 0xff;
+
+ /* Write register */
+ CHECK_AD936X(ad9361_spi_write(phy->spi, reg, data & 0xff));
+ }
+
+ reg = (BLADERF_CHANNEL_IS_TX(ch)) ? AD936X_REG_TX_FORCE_BITS
+ : AD936X_REG_FORCE_BITS;
+
+ /* Read force bit register */
+ val = ad9361_spi_read(phy->spi, reg);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(force)", val);
+ }
+
+ /* Modify register */
+ data = val | (1 << ad9361_correction_force_bit[ch >> 1][corr][low_band]);
+
+ /* Write force bit register */
+ CHECK_AD936X(ad9361_spi_write(phy->spi, reg, data));
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Trigger */
+/******************************************************************************/
+
+static int bladerf2_trigger_init(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(trigger);
+
+ return fpga_trigger_init(dev, ch, signal, trigger);
+}
+
+static int bladerf2_trigger_arm(struct bladerf *dev,
+ struct bladerf_trigger const *trigger,
+ bool arm,
+ uint64_t resv1,
+ uint64_t resv2)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(trigger);
+
+ return fpga_trigger_arm(dev, trigger, arm);
+}
+
+static int bladerf2_trigger_fire(struct bladerf *dev,
+ struct bladerf_trigger const *trigger)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(trigger);
+
+ return fpga_trigger_fire(dev, trigger);
+}
+
+static int bladerf2_trigger_state(struct bladerf *dev,
+ struct bladerf_trigger const *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested,
+ uint64_t *resv1,
+ uint64_t *resv2)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(trigger);
+ NULL_CHECK(is_armed);
+ NULL_CHECK(has_fired);
+ NULL_CHECK(fire_requested);
+
+ /* Reserved for future metadata (e.g., trigger counts, timestamp) */
+ if (resv1 != NULL) {
+ *resv1 = 0;
+ }
+
+ if (resv2 != NULL) {
+ *resv2 = 0;
+ }
+
+ return fpga_trigger_state(dev, trigger, is_armed, has_fired,
+ fire_requested);
+}
+
+
+/******************************************************************************/
+/* Streaming */
+/******************************************************************************/
+
+static int bladerf2_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *user_data)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return async_init_stream(stream, dev, callback, buffers, num_buffers,
+ format, samples_per_buffer, num_transfers,
+ user_data);
+}
+
+static int bladerf2_stream(struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ bladerf_direction dir = layout & BLADERF_DIRECTION_MASK;
+ int rv;
+
+ switch (layout) {
+ case BLADERF_RX_X1:
+ case BLADERF_RX_X2:
+ case BLADERF_TX_X1:
+ case BLADERF_TX_X2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ WITH_MUTEX(&stream->dev->lock, {
+ CHECK_STATUS_LOCKED(
+ perform_format_config(stream->dev, dir, stream->format));
+ });
+
+ rv = async_run_stream(stream, layout);
+
+ WITH_MUTEX(&stream->dev->lock, {
+ CHECK_STATUS_LOCKED(
+ perform_format_deconfig(stream->dev, dir));
+ });
+
+ return rv;
+}
+
+static int bladerf2_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms,
+ bool nonblock)
+{
+ size_t len;
+ len = async_stream_buf_bytes(stream);
+ return async_submit_stream_buffer(stream, buffer, &len, timeout_ms, nonblock);
+}
+
+static void bladerf2_deinit_stream(struct bladerf_stream *stream)
+{
+ async_deinit_stream(stream);
+}
+
+static int bladerf2_set_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int timeout)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ WITH_MUTEX(&board_data->sync[dir].lock,
+ { board_data->sync[dir].stream_config.timeout_ms = timeout; });
+
+ return 0;
+}
+
+static int bladerf2_get_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int *timeout)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(timeout);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ WITH_MUTEX(&board_data->sync[dir].lock,
+ { *timeout = board_data->sync[dir].stream_config.timeout_ms; });
+
+ return 0;
+}
+
+static int bladerf2_sync_config(struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ unsigned int buffer_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ bladerf_direction dir = layout & BLADERF_DIRECTION_MASK;
+ int status;
+
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE
+ && (format == BLADERF_FORMAT_SC16_Q11 || format == BLADERF_FORMAT_SC16_Q11_META)) {
+ log_error("16bit format unsupported with OVERSAMPLE feature enabled\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ switch (layout) {
+ case BLADERF_RX_X1:
+ case BLADERF_RX_X2:
+ case BLADERF_TX_X1:
+ case BLADERF_TX_X2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ status = perform_format_config(dev, dir, format);
+ if (0 == status) {
+ status = sync_init(&board_data->sync[dir], dev, layout, format,
+ num_buffers, buffer_size, board_data->msg_size,
+ num_transfers, stream_timeout);
+ if (status != 0) {
+ perform_format_deconfig(dev, dir);
+ }
+ }
+
+ return status;
+}
+
+static int bladerf2_sync_tx(struct bladerf *dev,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!board_data->sync[BLADERF_TX].initialized) {
+ RETURN_INVAL("sync tx", "not initialized");
+ }
+
+ return sync_tx(&board_data->sync[BLADERF_TX], samples, num_samples,
+ metadata, timeout_ms);
+}
+
+static int bladerf2_sync_rx(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!board_data->sync[BLADERF_RX].initialized) {
+ RETURN_INVAL("sync rx", "not initialized");
+ }
+
+ return sync_rx(&board_data->sync[BLADERF_RX], samples, num_samples,
+ metadata, timeout_ms);
+}
+
+static int bladerf2_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *value)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(value);
+
+ return dev->backend->get_timestamp(dev, dir, value);
+}
+
+
+/******************************************************************************/
+/* FPGA/Firmware Loading/Flashing */
+/******************************************************************************/
+
+static int bladerf2_load_fpga(struct bladerf *dev,
+ uint8_t const *buf,
+ size_t length)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) {
+ RETURN_INVAL("fpga file", "incorrect file size");
+ }
+
+ CHECK_STATUS(dev->backend->load_fpga(dev, buf, length));
+
+ /* Update device state */
+ board_data->state = STATE_FPGA_LOADED;
+
+ CHECK_STATUS(_bladerf2_initialize(dev));
+
+ return 0;
+}
+
+static int bladerf2_flash_fpga(struct bladerf *dev,
+ uint8_t const *buf,
+ size_t length)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) {
+ RETURN_INVAL("fpga file", "incorrect file size");
+ }
+
+ return spi_flash_write_fpga_bitstream(dev, buf, length);
+}
+
+static int bladerf2_erase_stored_fpga(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_erase_fpga(dev);
+}
+
+static int bladerf2_flash_firmware(struct bladerf *dev,
+ uint8_t const *buf,
+ size_t length)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ char const env_override[] = "BLADERF_SKIP_FW_SIZE_CHECK";
+
+ /* Sanity check firmware length.
+ *
+ * TODO in the future, better sanity checks can be performed when
+ * using the bladerf image format currently used to backup/restore
+ * calibration data
+ */
+ if (!getenv(env_override) && !is_valid_fw_size(length)) {
+ log_info("Detected potentially invalid firmware file.\n");
+ log_info("Define BLADERF_SKIP_FW_SIZE_CHECK in your environment "
+ "to skip this check.\n");
+ RETURN_INVAL_ARG("firmware size", length, "is not valid");
+ }
+
+ return spi_flash_write_fx3_fw(dev, buf, length);
+}
+
+static int bladerf2_device_reset(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return dev->backend->device_reset(dev);
+}
+
+
+/******************************************************************************/
+/* Tuning mode */
+/******************************************************************************/
+
+static inline bool _supports_fpga_tuning(struct bladerf *dev)
+{
+ extern struct controller_fns const rfic_fpga_control;
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return (have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING) &&
+ rfic_fpga_control.is_present(dev));
+}
+
+static int bladerf2_set_tuning_mode(struct bladerf *dev,
+ bladerf_tuning_mode mode)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ extern struct controller_fns const rfic_host_control;
+ extern struct controller_fns const rfic_fpga_control;
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic_new = NULL;
+ struct controller_fns const *rfic_other = NULL;
+
+ bladerf_tuning_mode mode_other;
+ bladerf_rfic_init_state init_state;
+
+ log_debug("%s: New tuning mode: %s\n", __FUNCTION__, tuningmode2str(mode));
+
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ rfic_new = &rfic_host_control;
+ rfic_other = _supports_fpga_tuning(dev) ? &rfic_fpga_control : NULL;
+ mode_other = BLADERF_TUNING_MODE_FPGA;
+ break;
+
+ case BLADERF_TUNING_MODE_FPGA:
+ /* Test capability */
+ if (!_supports_fpga_tuning(dev)) {
+ log_debug("%s: The loaded FPGA version (%u.%u.%u) does not "
+ "support FPGA RFIC control\n",
+ __FUNCTION__, board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ rfic_new = &rfic_fpga_control;
+ rfic_other = &rfic_host_control;
+ mode_other = BLADERF_TUNING_MODE_HOST;
+ break;
+
+ default:
+ log_error("%s: invalid tuning mode (%d)\n", mode);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* De-initialize RFIC if it's initialized by another tuning mode */
+ if (NULL != rfic_other) {
+ CHECK_STATUS(rfic_other->get_init_state(dev, &init_state));
+
+ if (init_state != BLADERF_RFIC_INIT_STATE_OFF) {
+ log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Releasing",
+ tuningmode2str(mode_other));
+ CHECK_STATUS(rfic_other->deinitialize(dev));
+ }
+ }
+
+ /* Set board data */
+ board_data->rfic = rfic_new;
+ board_data->tuning_mode = mode;
+
+ /* Bring RFIC to initialized state */
+ CHECK_STATUS(rfic_new->get_init_state(dev, &init_state));
+
+ switch (init_state) {
+ case BLADERF_RFIC_INIT_STATE_OFF:
+ log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Initializing",
+ tuningmode2str(mode));
+ return rfic_new->initialize(dev);
+
+ case BLADERF_RFIC_INIT_STATE_STANDBY:
+ log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Restoring",
+ tuningmode2str(mode));
+ return rfic_new->initialize(dev);
+
+ case BLADERF_RFIC_INIT_STATE_ON:
+ log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Maintaining",
+ tuningmode2str(mode));
+ return 0;
+
+ default:
+ log_error("%s: invalid RFIC initialization state (%d)\n",
+ init_state);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+}
+
+static int bladerf2_get_tuning_mode(struct bladerf *dev,
+ bladerf_tuning_mode *mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(mode);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ *mode = board_data->tuning_mode;
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Loopback */
+/******************************************************************************/
+
+static int bladerf2_get_loopback_modes(
+ struct bladerf *dev, struct bladerf_loopback_modes const **modes)
+{
+ if (modes != NULL) {
+ *modes = bladerf2_loopback_modes;
+ }
+
+ return ARRAY_SIZE(bladerf2_loopback_modes);
+}
+
+static int bladerf2_set_loopback(struct bladerf *dev, bladerf_loopback mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ bool firmware_loopback = false;
+ int32_t bist_loopback = 0;
+
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ if (BLADERF_LB_RFIC_BIST == mode) {
+ log_debug(
+ "%s: BLADERF_LB_RFIC_BIST not supported in FPGA command mode\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ });
+
+ switch (mode) {
+ case BLADERF_LB_NONE:
+ break;
+ case BLADERF_LB_FIRMWARE:
+ firmware_loopback = true;
+ break;
+ case BLADERF_LB_RFIC_BIST:
+ bist_loopback = 1;
+ break;
+ default:
+ log_error("%s: unknown loopback mode (%d)\n", __FUNCTION__, mode);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_HOST, {
+ /* Set digital loopback state */
+ CHECK_AD936X(ad9361_bist_loopback(phy, bist_loopback));
+ });
+
+ /* Set firmware loopback state */
+ CHECK_STATUS(dev->backend->set_firmware_loopback(dev, firmware_loopback));
+
+ return 0;
+}
+
+static int bladerf2_get_loopback(struct bladerf *dev, bladerf_loopback *mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(mode);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ int32_t ad9361_loopback;
+ bool fw_loopback;
+
+ /* Read firwmare loopback */
+ CHECK_STATUS(dev->backend->get_firmware_loopback(dev, &fw_loopback));
+
+ if (fw_loopback) {
+ *mode = BLADERF_LB_FIRMWARE;
+ return 0;
+ }
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_HOST, {
+ /* Read AD9361 bist loopback */
+ /* Note: this returns void */
+ ad9361_get_bist_loopback(phy, &ad9361_loopback);
+
+ if (ad9361_loopback == 1) {
+ *mode = BLADERF_LB_RFIC_BIST;
+ return 0;
+ }
+ });
+
+ *mode = BLADERF_LB_NONE;
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Sample RX FPGA Mux */
+/******************************************************************************/
+
+static int bladerf2_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ uint32_t rx_mux_val, config_gpio;
+
+ /* Validate desired mux mode */
+ switch (mode) {
+ case BLADERF_RX_MUX_BASEBAND:
+ case BLADERF_RX_MUX_12BIT_COUNTER:
+ case BLADERF_RX_MUX_32BIT_COUNTER:
+ case BLADERF_RX_MUX_DIGITAL_LOOPBACK:
+ rx_mux_val = ((uint32_t)mode) << BLADERF_GPIO_RX_MUX_SHIFT;
+ break;
+
+ default:
+ log_debug("Invalid RX mux mode setting passed to %s(): %d\n", mode,
+ __FUNCTION__);
+ RETURN_INVAL_ARG("bladerf_rx_mux", mode, "is invalid");
+ }
+
+ CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio));
+
+ /* Clear out and assign the associated RX mux bits */
+ config_gpio &= ~BLADERF_GPIO_RX_MUX_MASK;
+ config_gpio |= rx_mux_val;
+
+ CHECK_STATUS(dev->backend->config_gpio_write(dev, config_gpio));
+
+ return 0;
+}
+
+static int bladerf2_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(mode);
+
+ bladerf_rx_mux val;
+ uint32_t config_gpio;
+
+ CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio));
+
+ /* Extract RX mux bits */
+ config_gpio &= BLADERF_GPIO_RX_MUX_MASK;
+ config_gpio >>= BLADERF_GPIO_RX_MUX_SHIFT;
+ val = config_gpio;
+
+ /* Ensure it's a valid/supported value */
+ switch (val) {
+ case BLADERF_RX_MUX_BASEBAND:
+ case BLADERF_RX_MUX_12BIT_COUNTER:
+ case BLADERF_RX_MUX_32BIT_COUNTER:
+ case BLADERF_RX_MUX_DIGITAL_LOOPBACK:
+ *mode = val;
+ break;
+
+ default:
+ *mode = BLADERF_RX_MUX_INVALID;
+ log_debug("Invalid rx mux mode %d read from config gpio\n", val);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low-level VCTCXO Tamer Mode */
+/******************************************************************************/
+
+static int bladerf2_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int bladerf2_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+
+/******************************************************************************/
+/* Low-level VCTCXO Trim DAC access */
+/******************************************************************************/
+
+static int _bladerf2_get_trim_dac_enable(struct bladerf *dev, bool *enable)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(enable);
+
+ uint16_t trim;
+
+ // Read current trim DAC setting
+ CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_read(dev, &trim));
+
+ // Determine if it's enabled...
+ *enable = (TRIMDAC_EN_ACTIVE == (trim >> TRIMDAC_EN));
+
+ log_debug("trim DAC is %s\n", (*enable ? "enabled" : "disabled"));
+
+ if ((trim >> TRIMDAC_EN) != TRIMDAC_EN_ACTIVE &&
+ (trim >> TRIMDAC_EN) != TRIMDAC_EN_HIGHZ) {
+ log_warning("unknown trim DAC state: 0x%x\n", (trim >> TRIMDAC_EN));
+ }
+
+ return 0;
+}
+
+static int _bladerf2_set_trim_dac_enable(struct bladerf *dev, bool enable)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ uint16_t trim;
+ bool current_state;
+
+ // See if we have anything to do
+ CHECK_STATUS(_bladerf2_get_trim_dac_enable(dev, &current_state));
+
+ if (enable == current_state) {
+ log_debug("trim DAC already %s, nothing to do\n",
+ enable ? "enabled" : "disabled");
+ return 0;
+ }
+
+ // Read current trim DAC setting
+ CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_read(dev, &trim));
+
+ // Set the trim DAC to high z if applicable
+ if (!enable && trim != (TRIMDAC_EN_HIGHZ << TRIMDAC_EN)) {
+ board_data->trimdac_last_value = trim;
+ log_debug("saving current trim DAC value: 0x%04x\n", trim);
+ trim = TRIMDAC_EN_HIGHZ << TRIMDAC_EN;
+ } else if (enable && trim == (TRIMDAC_EN_HIGHZ << TRIMDAC_EN)) {
+ trim = board_data->trimdac_last_value;
+ log_debug("restoring old trim DAC value: 0x%04x\n", trim);
+ }
+
+ // Write back the trim DAC setting
+ CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_write(dev, trim));
+
+ // Update our state flag
+ board_data->trim_source = enable ? TRIM_SOURCE_TRIM_DAC : TRIM_SOURCE_NONE;
+
+ return 0;
+}
+
+/**
+ * @brief Read the VCTCXO trim value from the SPI flash
+ *
+ * Retrieves the factory VCTCXO value from the SPI flash. This function
+ * should not be used while sample streaming is in progress.
+ *
+ * @param dev Device handle
+ * @param trim Pointer to populate with the trim value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+static int bladerf2_read_flash_vctcxo_trim(struct bladerf *dev, uint16_t *trim)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(trim);
+
+ int status;
+
+ status = spi_flash_read_vctcxo_trim(dev, trim);
+ if (status < 0) {
+ log_warning("Failed to get VCTCXO trim value: %s\n",
+ bladerf_strerror(status));
+ log_debug("Defaulting DAC trim to 0x1ffc.\n");
+ *trim = 0x1ffc;
+ return 0;
+ }
+
+ return status;
+}
+
+static int bladerf2_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(trim);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ *trim = board_data->trimdac_stored_value;
+
+ return 0;
+}
+
+static int bladerf2_trim_dac_read(struct bladerf *dev, uint16_t *trim)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(trim);
+
+ return dev->backend->ad56x1_vctcxo_trim_dac_read(dev, trim);
+}
+
+static int bladerf2_trim_dac_write(struct bladerf *dev, uint16_t trim)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ uint16_t trim_control = (trim >> TRIMDAC_EN) & TRIMDAC_EN_MASK;
+ uint16_t trim_value = trim & TRIMDAC_MASK;
+ bool enable;
+
+ log_debug("requested trim 0x%04x (control 0x%01x value 0x%04x)\n", trim,
+ trim_control, trim_value);
+
+ // Is the trimdac enabled?
+ CHECK_STATUS(_bladerf2_get_trim_dac_enable(dev, &enable));
+
+ // If the trimdac is not enabled, save this value for later but don't
+ // apply it.
+ if (!enable && trim_control != TRIMDAC_EN_HIGHZ) {
+ log_warning("trim DAC is disabled. New value will be saved until "
+ "trim DAC is enabled\n");
+ board_data->trimdac_last_value = trim_value;
+ return 0;
+ }
+
+ return dev->backend->ad56x1_vctcxo_trim_dac_write(dev, trim);
+}
+
+
+/******************************************************************************/
+/* Low-level Trigger control access */
+/******************************************************************************/
+
+static int bladerf2_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ return fpga_trigger_read(dev, ch, trigger, val);
+}
+
+static int bladerf2_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return fpga_trigger_write(dev, ch, trigger, val);
+}
+
+/******************************************************************************/
+/* Low-level Wishbone Master access */
+/******************************************************************************/
+
+static int bladerf2_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(data);
+
+ return dev->backend->wishbone_master_read(dev, addr, data);
+}
+
+static int bladerf2_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->wishbone_master_write(dev, addr, data);
+}
+
+
+/******************************************************************************/
+/* Low-level Configuration GPIO access */
+/******************************************************************************/
+
+static int bladerf2_config_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ return dev->backend->config_gpio_read(dev, val);
+}
+
+static int bladerf2_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->config_gpio_write(dev, val);
+}
+
+
+/******************************************************************************/
+/* Low-level SPI Flash access */
+/******************************************************************************/
+
+static int bladerf2_erase_flash(struct bladerf *dev,
+ uint32_t erase_block,
+ uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_erase(dev, erase_block, count);
+}
+
+static int bladerf2_read_flash(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ return spi_flash_read(dev, buf, page, count);
+}
+
+static int bladerf2_write_flash(struct bladerf *dev,
+ uint8_t const *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ return spi_flash_write(dev, buf, page, count);
+}
+
+
+/******************************************************************************/
+/* Expansion support */
+/******************************************************************************/
+
+static int bladerf2_expansion_attach(struct bladerf *dev, bladerf_xb xb)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int bladerf2_expansion_get_attached(struct bladerf *dev, bladerf_xb *xb)
+{
+ NULL_CHECK(xb);
+
+ *xb = BLADERF_XB_NONE;
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Board binding */
+/******************************************************************************/
+
+struct board_fns const bladerf2_board_fns = {
+ FIELD_INIT(.matches, bladerf2_matches),
+ FIELD_INIT(.open, bladerf2_open),
+ FIELD_INIT(.close, bladerf2_close),
+ FIELD_INIT(.device_speed, bladerf2_device_speed),
+ FIELD_INIT(.get_serial, bladerf2_get_serial),
+ FIELD_INIT(.get_fpga_size, bladerf2_get_fpga_size),
+ FIELD_INIT(.get_fpga_bytes, bladerf2_get_fpga_bytes),
+ FIELD_INIT(.get_flash_size, bladerf2_get_flash_size),
+ FIELD_INIT(.is_fpga_configured, bladerf2_is_fpga_configured),
+ FIELD_INIT(.get_fpga_source, bladerf2_get_fpga_source),
+ FIELD_INIT(.get_capabilities, bladerf2_get_capabilities),
+ FIELD_INIT(.get_channel_count, bladerf2_get_channel_count),
+ FIELD_INIT(.get_fpga_version, bladerf2_get_fpga_version),
+ FIELD_INIT(.get_fw_version, bladerf2_get_fw_version),
+ FIELD_INIT(.set_gain, bladerf2_set_gain),
+ FIELD_INIT(.get_gain, bladerf2_get_gain),
+ FIELD_INIT(.set_gain_mode, bladerf2_set_gain_mode),
+ FIELD_INIT(.get_gain_mode, bladerf2_get_gain_mode),
+ FIELD_INIT(.get_gain_modes, bladerf2_get_gain_modes),
+ FIELD_INIT(.get_gain_range, bladerf2_get_gain_range),
+ FIELD_INIT(.set_gain_stage, bladerf2_set_gain_stage),
+ FIELD_INIT(.get_gain_stage, bladerf2_get_gain_stage),
+ FIELD_INIT(.get_gain_stage_range, bladerf2_get_gain_stage_range),
+ FIELD_INIT(.get_gain_stages, bladerf2_get_gain_stages),
+ FIELD_INIT(.set_sample_rate, bladerf2_set_sample_rate),
+ FIELD_INIT(.set_rational_sample_rate, bladerf2_set_rational_sample_rate),
+ FIELD_INIT(.get_sample_rate, bladerf2_get_sample_rate),
+ FIELD_INIT(.get_sample_rate_range, bladerf2_get_sample_rate_range),
+ FIELD_INIT(.get_rational_sample_rate, bladerf2_get_rational_sample_rate),
+ FIELD_INIT(.set_bandwidth, bladerf2_set_bandwidth),
+ FIELD_INIT(.get_bandwidth, bladerf2_get_bandwidth),
+ FIELD_INIT(.get_bandwidth_range, bladerf2_get_bandwidth_range),
+ FIELD_INIT(.get_frequency, bladerf2_get_frequency),
+ FIELD_INIT(.set_frequency, bladerf2_set_frequency),
+ FIELD_INIT(.get_frequency_range, bladerf2_get_frequency_range),
+ FIELD_INIT(.select_band, bladerf2_select_band),
+ FIELD_INIT(.set_rf_port, bladerf2_set_rf_port),
+ FIELD_INIT(.get_rf_port, bladerf2_get_rf_port),
+ FIELD_INIT(.get_rf_ports, bladerf2_get_rf_ports),
+ FIELD_INIT(.get_quick_tune, bladerf2_get_quick_tune),
+ FIELD_INIT(.schedule_retune, bladerf2_schedule_retune),
+ FIELD_INIT(.cancel_scheduled_retunes, bladerf2_cancel_scheduled_retunes),
+ FIELD_INIT(.get_correction, bladerf2_get_correction),
+ FIELD_INIT(.set_correction, bladerf2_set_correction),
+ FIELD_INIT(.trigger_init, bladerf2_trigger_init),
+ FIELD_INIT(.trigger_arm, bladerf2_trigger_arm),
+ FIELD_INIT(.trigger_fire, bladerf2_trigger_fire),
+ FIELD_INIT(.trigger_state, bladerf2_trigger_state),
+ FIELD_INIT(.enable_module, bladerf2_enable_module),
+ FIELD_INIT(.init_stream, bladerf2_init_stream),
+ FIELD_INIT(.stream, bladerf2_stream),
+ FIELD_INIT(.submit_stream_buffer, bladerf2_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, bladerf2_deinit_stream),
+ FIELD_INIT(.set_stream_timeout, bladerf2_set_stream_timeout),
+ FIELD_INIT(.get_stream_timeout, bladerf2_get_stream_timeout),
+ FIELD_INIT(.sync_config, bladerf2_sync_config),
+ FIELD_INIT(.sync_tx, bladerf2_sync_tx),
+ FIELD_INIT(.sync_rx, bladerf2_sync_rx),
+ FIELD_INIT(.get_timestamp, bladerf2_get_timestamp),
+ FIELD_INIT(.load_fpga, bladerf2_load_fpga),
+ FIELD_INIT(.flash_fpga, bladerf2_flash_fpga),
+ FIELD_INIT(.erase_stored_fpga, bladerf2_erase_stored_fpga),
+ FIELD_INIT(.flash_firmware, bladerf2_flash_firmware),
+ FIELD_INIT(.device_reset, bladerf2_device_reset),
+ FIELD_INIT(.set_tuning_mode, bladerf2_set_tuning_mode),
+ FIELD_INIT(.get_tuning_mode, bladerf2_get_tuning_mode),
+ FIELD_INIT(.get_loopback_modes, bladerf2_get_loopback_modes),
+ FIELD_INIT(.set_loopback, bladerf2_set_loopback),
+ FIELD_INIT(.get_loopback, bladerf2_get_loopback),
+ FIELD_INIT(.get_rx_mux, bladerf2_get_rx_mux),
+ FIELD_INIT(.set_rx_mux, bladerf2_set_rx_mux),
+ FIELD_INIT(.set_vctcxo_tamer_mode, bladerf2_set_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_tamer_mode, bladerf2_get_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_trim, bladerf2_get_vctcxo_trim),
+ FIELD_INIT(.trim_dac_read, bladerf2_trim_dac_read),
+ FIELD_INIT(.trim_dac_write, bladerf2_trim_dac_write),
+ FIELD_INIT(.read_trigger, bladerf2_read_trigger),
+ FIELD_INIT(.write_trigger, bladerf2_write_trigger),
+ FIELD_INIT(.wishbone_master_read, bladerf2_wishbone_master_read),
+ FIELD_INIT(.wishbone_master_write, bladerf2_wishbone_master_write),
+ FIELD_INIT(.config_gpio_read, bladerf2_config_gpio_read),
+ FIELD_INIT(.config_gpio_write, bladerf2_config_gpio_write),
+ FIELD_INIT(.erase_flash, bladerf2_erase_flash),
+ FIELD_INIT(.read_flash, bladerf2_read_flash),
+ FIELD_INIT(.write_flash, bladerf2_write_flash),
+ FIELD_INIT(.expansion_attach, bladerf2_expansion_attach),
+ FIELD_INIT(.expansion_get_attached, bladerf2_expansion_get_attached),
+ FIELD_INIT(.name, "bladerf2"),
+};
+
+
+/******************************************************************************
+ ******************************************************************************
+ * bladeRF2-specific Functions *
+ ******************************************************************************
+ ******************************************************************************/
+
+/******************************************************************************/
+/* Bias Tee Control */
+/******************************************************************************/
+
+int bladerf_get_bias_tee(struct bladerf *dev, bladerf_channel ch, bool *enable)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(enable);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t reg;
+ uint32_t shift;
+
+ shift = BLADERF_CHANNEL_IS_TX(ch) ? RFFE_CONTROL_TX_BIAS_EN
+ : RFFE_CONTROL_RX_BIAS_EN;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ /* Check register value */
+ *enable = (reg >> shift) & 0x1;
+ });
+
+ return 0;
+}
+
+int bladerf_set_bias_tee(struct bladerf *dev, bladerf_channel ch, bool enable)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t reg;
+ uint32_t shift;
+
+ shift = BLADERF_CHANNEL_IS_TX(ch) ? RFFE_CONTROL_TX_BIAS_EN
+ : RFFE_CONTROL_RX_BIAS_EN;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ /* Clear register value */
+ reg &= ~(1 << shift);
+
+ /* Set register value */
+ if (enable) {
+ reg |= (1 << shift);
+ }
+
+ /* Write RFFE control register */
+ log_debug("%s: rffe_control_write %08x\n", __FUNCTION__, reg);
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_write(dev, reg));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level RFIC Accessors */
+/******************************************************************************/
+
+int bladerf_get_rfic_register(struct bladerf *dev,
+ uint16_t address,
+ uint8_t *val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ WITH_MUTEX(&dev->lock, {
+ uint64_t data;
+
+ address |= (AD936X_READ | AD936X_CNT(1));
+
+ CHECK_AD936X_LOCKED(dev->backend->ad9361_spi_read(dev, address, &data));
+
+ *val = (data >> 56) & 0xff;
+ });
+
+ return 0;
+}
+
+int bladerf_set_rfic_register(struct bladerf *dev,
+ uint16_t address,
+ uint8_t val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ uint64_t data = (((uint64_t)val) << 56);
+
+ address |= (AD936X_WRITE | AD936X_CNT(1));
+
+ CHECK_AD936X_LOCKED(dev->backend->ad9361_spi_write(dev, address, data));
+ });
+
+ return 0;
+}
+
+int bladerf_get_rfic_temperature(struct bladerf *dev, float *val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ WITH_MUTEX(&dev->lock, { *val = ad9361_get_temp(phy) / 1000.0F; });
+
+ return 0;
+}
+
+int bladerf_get_rfic_rssi(struct bladerf *dev,
+ bladerf_channel ch,
+ int *pre_rssi,
+ int *sym_rssi)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(pre_rssi);
+ NULL_CHECK(sym_rssi);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+
+ WITH_MUTEX(&dev->lock, {
+ CHECK_STATUS_LOCKED(rfic->get_rssi(dev, ch, pre_rssi, sym_rssi));
+ });
+
+ return 0;
+}
+
+int bladerf_get_rfic_ctrl_out(struct bladerf *dev, uint8_t *ctrl_out)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(ctrl_out);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t reg;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ *ctrl_out = (uint8_t)((reg >> RFFE_CONTROL_CTRL_OUT) & 0xFF);
+ });
+
+ return 0;
+}
+
+int bladerf_get_rfic_rx_fir(struct bladerf *dev, bladerf_rfic_rxfir *rxfir)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(rxfir);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ bladerf_channel const ch = BLADERF_CHANNEL_RX(0);
+
+ WITH_MUTEX(&dev->lock, {
+ CHECK_STATUS_LOCKED(rfic->get_filter(dev, ch, rxfir, NULL));
+ });
+
+ return 0;
+}
+
+int bladerf_set_rfic_rx_fir(struct bladerf *dev, bladerf_rfic_rxfir rxfir)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const sr_range = bladerf2_sample_rate_range_4x;
+ bladerf_channel const ch = BLADERF_CHANNEL_RX(0);
+
+ WITH_MUTEX(&dev->lock, {
+ /* Verify that sample rate is not too low */
+ if (rxfir != BLADERF_RFIC_RXFIR_DEC4) {
+ bladerf_sample_rate sr;
+
+ CHECK_STATUS_LOCKED(dev->board->get_sample_rate(dev, ch, &sr));
+
+ if (is_within_range(&sr_range, sr)) {
+ log_error("%s: sample rate too low for filter (%d < %d)\n",
+ __FUNCTION__, sr, sr_range.min);
+ MUTEX_UNLOCK(&dev->lock);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ CHECK_STATUS_LOCKED(rfic->set_filter(dev, ch, rxfir, 0));
+ });
+
+ return 0;
+}
+
+int bladerf_get_rfic_tx_fir(struct bladerf *dev, bladerf_rfic_txfir *txfir)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(txfir);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ bladerf_channel const ch = BLADERF_CHANNEL_TX(0);
+
+ WITH_MUTEX(&dev->lock, {
+ CHECK_STATUS_LOCKED(rfic->get_filter(dev, ch, NULL, txfir));
+ });
+
+ return 0;
+}
+
+int bladerf_set_rfic_tx_fir(struct bladerf *dev, bladerf_rfic_txfir txfir)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const sr_range = bladerf2_sample_rate_range_4x;
+ bladerf_channel const ch = BLADERF_CHANNEL_TX(0);
+
+ WITH_MUTEX(&dev->lock, {
+ /* Verify that sample rate is not too low */
+ if (txfir != BLADERF_RFIC_TXFIR_INT4) {
+ bladerf_sample_rate sr;
+
+ CHECK_STATUS_LOCKED(dev->board->get_sample_rate(dev, ch, &sr));
+
+ if (is_within_range(&sr_range, sr)) {
+ log_error("%s: sample rate too low for filter (%d < %d)\n",
+ __FUNCTION__, sr, sr_range.min);
+ MUTEX_UNLOCK(&dev->lock);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ CHECK_STATUS_LOCKED(rfic->set_filter(dev, ch, 0, txfir));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level PLL Accessors */
+/******************************************************************************/
+
+static int bladerf_pll_configure(struct bladerf *dev, uint16_t R, uint16_t N)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ uint32_t init_array[3];
+ bool is_enabled;
+ uint8_t i;
+
+ if (R < 1 || R > 16383) {
+ RETURN_INVAL("R", "outside range [1,16383]");
+ }
+
+ if (N < 1 || N > 8191) {
+ RETURN_INVAL("N", "outside range [1,8191]");
+ }
+
+ /* Get the present state... */
+ CHECK_STATUS(bladerf_get_pll_enable(dev, &is_enabled));
+
+ /* Enable the chip if applicable */
+ if (!is_enabled) {
+ CHECK_STATUS(bladerf_set_pll_enable(dev, true));
+ }
+
+ /* Register 0: Reference Counter Latch */
+ init_array[0] = 0;
+ /* R Counter: */
+ init_array[0] |= (R & ((1 << 14) - 1)) << 2;
+ /* Hardcoded values: */
+ /* Anti-backlash: 00 (2.9 ns) */
+ /* Lock detect precision: 0 (three cycles) */
+
+ /* Register 1: N Counter Latch */
+ init_array[1] = 1;
+ /* N Counter: */
+ init_array[1] |= (N & ((1 << 13) - 1)) << 8;
+ /* Hardcoded values: */
+ /* CP Gain: 0 (Setting 1) */
+
+ /* Register 2: Function Latch */
+ init_array[2] = 2;
+ /* Hardcoded values: */
+ /* Counter operation: 0 (Normal) */
+ /* Power down control: 00 (Normal) */
+ /* Muxout control: 0x1 (digital lock detect) */
+ init_array[2] |= (1 & ((1 << 3) - 1)) << 4;
+ /* PD Polarity: 1 (positive) */
+ init_array[2] |= 1 << 7;
+ /* CP three-state: 0 (normal) */
+ /* Fastlock Mode: 00 (disabled) */
+ /* Timer Counter Control: 0000 (3 PFD cycles) */
+ /* Current Setting 1: 111 (5 mA) */
+ init_array[2] |= 0x7 << 15;
+ /* Current Setting 2: 111 (5 mA) */
+ init_array[2] |= 0x7 << 18;
+
+ /* Write the values to the chip */
+ for (i = 0; i < ARRAY_SIZE(init_array); ++i) {
+ CHECK_STATUS(bladerf_set_pll_register(dev, i, init_array[i]));
+ }
+
+ /* Re-disable the chip if applicable */
+ if (!is_enabled) {
+ CHECK_STATUS(bladerf_set_pll_enable(dev, false));
+ }
+
+ return 0;
+}
+
+static int bladerf_pll_calculate_ratio(bladerf_frequency ref_freq,
+ bladerf_frequency clock_freq,
+ uint16_t *R,
+ uint16_t *N)
+{
+ NULL_CHECK(R);
+ NULL_CHECK(N);
+
+ size_t const Rmax = 16383;
+ double const tol = 0.00001;
+ double target = (double)clock_freq / (double)ref_freq;
+ uint16_t R_try, N_try;
+
+ struct bladerf_range const clock_frequency_range = {
+ FIELD_INIT(.min, 5000000),
+ FIELD_INIT(.max, 400000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ };
+
+ if (!is_within_range(&bladerf2_pll_refclk_range, ref_freq)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ if (!is_within_range(&clock_frequency_range, clock_freq)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ for (R_try = 1; R_try < Rmax; ++R_try) {
+ double ratio, delta;
+
+ N_try = (uint16_t)(target * R_try + 0.5);
+
+ if (N_try > 8191) {
+ continue;
+ }
+
+ ratio = (double)N_try / (double)R_try;
+ delta = (ratio > target) ? (ratio - target) : (target - ratio);
+
+ if (delta < tol) {
+ *R = R_try;
+ *N = N_try;
+
+ return 0;
+ }
+ }
+
+ RETURN_INVAL("requested ratio", "not achievable");
+}
+
+int bladerf_get_pll_lock_state(struct bladerf *dev, bool *locked)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(locked);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t reg;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ *locked = (reg >> RFFE_CONTROL_ADF_MUXOUT) & 0x1;
+ });
+
+ return 0;
+}
+
+int bladerf_get_pll_enable(struct bladerf *dev, bool *enabled)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(enabled);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t data;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data));
+
+ *enabled = (data >> CFG_GPIO_PLL_EN) & 0x01;
+ });
+
+ return 0;
+}
+
+int bladerf_set_pll_enable(struct bladerf *dev, bool enable)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ struct bladerf2_board_data *board_data = dev->board_data;
+ uint32_t data;
+
+ // Disable the trim DAC when we're using the PLL
+ if (enable) {
+ CHECK_STATUS_LOCKED(_bladerf2_set_trim_dac_enable(dev, false));
+ }
+
+ // Read current config GPIO value
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data));
+
+ // Set the PLL enable bit accordingly
+ data &= ~(1 << CFG_GPIO_PLL_EN);
+ data |= ((enable ? 1 : 0) << CFG_GPIO_PLL_EN);
+
+ // Write back the config GPIO
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, data));
+
+ // Update our state flag
+ board_data->trim_source = enable ? TRIM_SOURCE_PLL : TRIM_SOURCE_NONE;
+
+ // Enable the trim DAC if we're done with the
+ // PLL
+ if (!enable) {
+ CHECK_STATUS_LOCKED(_bladerf2_set_trim_dac_enable(dev, true));
+ }
+ });
+
+ return 0;
+}
+
+int bladerf_get_pll_refclk_range(struct bladerf *dev,
+ const struct bladerf_range **range)
+{
+ NULL_CHECK(range);
+
+ *range = &bladerf2_pll_refclk_range;
+
+ return 0;
+}
+
+int bladerf_get_pll_refclk(struct bladerf *dev, bladerf_frequency *frequency)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(frequency);
+
+ uint8_t const R_LATCH_REG = 0;
+ size_t const R_LATCH_SHIFT = 2;
+ uint32_t const R_LATCH_MASK = 0x3fff;
+ uint8_t const N_LATCH_REG = 1;
+ size_t const N_LATCH_SHIFT = 8;
+ uint32_t const N_LATCH_MASK = 0x1fff;
+ uint32_t reg;
+ uint16_t R, N;
+
+ // Get the current R value (latch 0, bits 2-15)
+ CHECK_STATUS(bladerf_get_pll_register(dev, R_LATCH_REG, &reg));
+ R = (reg >> R_LATCH_SHIFT) & R_LATCH_MASK;
+
+ // Get the current N value (latch 1, bits 8-20)
+ CHECK_STATUS(bladerf_get_pll_register(dev, N_LATCH_REG, &reg));
+ N = (reg >> N_LATCH_SHIFT) & N_LATCH_MASK;
+
+ // We assume the system clock frequency is
+ // BLADERF_VCTCXO_FREQUENCY. If it isn't, do your
+ // own math
+ *frequency = R * BLADERF_VCTCXO_FREQUENCY / N;
+
+ return 0;
+}
+
+int bladerf_set_pll_refclk(struct bladerf *dev, bladerf_frequency frequency)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ uint16_t R, N;
+
+ // We assume the system clock frequency is
+ // BLADERF_VCTCXO_FREQUENCY. If it isn't, do your
+ // own math
+ CHECK_STATUS(bladerf_pll_calculate_ratio(frequency,
+ BLADERF_VCTCXO_FREQUENCY, &R, &N));
+
+ CHECK_STATUS(bladerf_pll_configure(dev, R, N));
+
+ return 0;
+}
+
+int bladerf_get_pll_register(struct bladerf *dev,
+ uint8_t address,
+ uint32_t *val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t data;
+
+ address &= 0x03;
+
+ CHECK_STATUS_LOCKED(dev->backend->adf400x_read(dev, address, &data));
+
+ *val = data;
+ });
+
+ return 0;
+}
+
+int bladerf_set_pll_register(struct bladerf *dev, uint8_t address, uint32_t val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t data;
+
+ address &= 0x03;
+
+ data = val;
+
+ CHECK_STATUS_LOCKED(dev->backend->adf400x_write(dev, address, data));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level Power Source Accessors */
+/******************************************************************************/
+
+int bladerf_get_power_source(struct bladerf *dev, bladerf_power_sources *src)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(src);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t data;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data));
+
+ if ((data >> CFG_GPIO_POWERSOURCE) & 0x01) {
+ *src = BLADERF_PS_USB_VBUS;
+ } else {
+ *src = BLADERF_PS_DC;
+ }
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level clock source selection accessors */
+/******************************************************************************/
+
+int bladerf_get_clock_select(struct bladerf *dev, bladerf_clock_select *sel)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(sel);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t gpio;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio));
+
+ if ((gpio & (1 << CFG_GPIO_CLOCK_SELECT)) == 0x0) {
+ *sel = CLOCK_SELECT_ONBOARD;
+ } else {
+ *sel = CLOCK_SELECT_EXTERNAL;
+ }
+ });
+
+ return 0;
+}
+
+int bladerf_set_clock_select(struct bladerf *dev, bladerf_clock_select sel)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ if (bladerf_device_speed(dev) == BLADERF_DEVICE_SPEED_HIGH) {
+ log_warning("USB 3.0 recommended for reliable clock select assignment.\n");
+ }
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t gpio;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio));
+
+ // Set the clock select bit(s) accordingly
+ switch (sel) {
+ case CLOCK_SELECT_ONBOARD:
+ gpio &= ~(1 << CFG_GPIO_CLOCK_SELECT);
+ break;
+ case CLOCK_SELECT_EXTERNAL:
+ gpio |= (1 << CFG_GPIO_CLOCK_SELECT);
+ break;
+ default:
+ break;
+ }
+
+ // Write back the config GPIO
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, gpio));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level clock buffer output accessors */
+/******************************************************************************/
+
+int bladerf_get_clock_output(struct bladerf *dev, bool *state)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(state);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t gpio;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio));
+
+ *state = ((gpio & (1 << CFG_GPIO_CLOCK_OUTPUT)) != 0x0);
+ });
+
+ return 0;
+}
+
+int bladerf_set_clock_output(struct bladerf *dev, bool enable)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t gpio;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio));
+
+ // Set or clear the clock output enable bit
+ gpio &= ~(1 << CFG_GPIO_CLOCK_OUTPUT);
+ gpio |= ((enable ? 1 : 0) << CFG_GPIO_CLOCK_OUTPUT);
+
+ // Write back the config GPIO
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, gpio));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level INA219 Accessors */
+/******************************************************************************/
+
+int bladerf_get_pmic_register(struct bladerf *dev,
+ bladerf_pmic_register reg,
+ void *val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ int rv;
+
+ WITH_MUTEX(&dev->lock, {
+ switch (reg) {
+ case BLADERF_PMIC_CONFIGURATION:
+ case BLADERF_PMIC_CALIBRATION:
+ default:
+ rv = BLADERF_ERR_UNSUPPORTED;
+ break;
+
+ case BLADERF_PMIC_VOLTAGE_SHUNT:
+ rv = ina219_read_shunt_voltage(dev, (float *)val);
+ break;
+
+ case BLADERF_PMIC_VOLTAGE_BUS:
+ rv = ina219_read_bus_voltage(dev, (float *)val);
+ break;
+
+ case BLADERF_PMIC_POWER:
+ rv = ina219_read_power(dev, (float *)val);
+ break;
+
+ case BLADERF_PMIC_CURRENT:
+ rv = ina219_read_current(dev, (float *)val);
+ break;
+ }
+ });
+
+ return rv;
+}
+
+
+/******************************************************************************/
+/* Low level RF Switch Accessors */
+/******************************************************************************/
+
+int bladerf_get_rf_switch_config(struct bladerf *dev,
+ bladerf_rf_switch_config *config)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(config);
+
+ WITH_MUTEX(&dev->lock, {
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ uint32_t val;
+ uint32_t reg;
+
+ /* Get AD9361 status */
+ if (RFIC_COMMAND_HOST == rfic->command_mode) {
+ CHECK_AD936X_LOCKED(ad9361_get_tx_rf_port_output(phy, &val));
+ } else {
+ val = 0xFF;
+ }
+
+ config->tx1_rfic_port = val;
+ config->tx2_rfic_port = val;
+
+ if (RFIC_COMMAND_HOST == rfic->command_mode) {
+ CHECK_AD936X_LOCKED(ad9361_get_rx_rf_port_input(phy, &val));
+ } else {
+ val = 0xFF;
+ }
+
+ config->rx1_rfic_port = val;
+ config->rx2_rfic_port = val;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ config->rx1_spdt_port =
+ (reg >> RFFE_CONTROL_RX_SPDT_1) & RFFE_CONTROL_SPDT_MASK;
+ config->rx2_spdt_port =
+ (reg >> RFFE_CONTROL_RX_SPDT_2) & RFFE_CONTROL_SPDT_MASK;
+ config->tx1_spdt_port =
+ (reg >> RFFE_CONTROL_TX_SPDT_1) & RFFE_CONTROL_SPDT_MASK;
+ config->tx2_spdt_port =
+ (reg >> RFFE_CONTROL_TX_SPDT_2) & RFFE_CONTROL_SPDT_MASK;
+ });
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c
new file mode 100644
index 0000000..1373a0b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c
@@ -0,0 +1,106 @@
+/*
+ * 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 <inttypes.h>
+
+#include "helpers/version.h"
+#include "log.h"
+
+#include "capabilities.h"
+
+uint64_t bladerf2_get_fw_capabilities(const struct bladerf_version *fw_version)
+{
+ uint64_t capabilities = 0;
+
+ if (version_fields_greater_or_equal(fw_version, 1, 7, 1)) {
+ capabilities |= BLADERF_CAP_FW_LOOPBACK;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 1, 8, 0)) {
+ capabilities |= BLADERF_CAP_QUERY_DEVICE_READY;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 1, 9, 0)) {
+ capabilities |= BLADERF_CAP_READ_FW_LOG_ENTRY;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 1, 0)) {
+ capabilities |= BLADERF_CAP_FW_SUPPORTS_BLADERF2;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 3, 0)) {
+ capabilities |= BLADERF_CAP_FW_FLASH_ID;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 3, 1)) {
+ capabilities |= BLADERF_CAP_FW_FPGA_SOURCE;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 4, 0)) {
+ capabilities |= BLADERF_CAP_FW_SHORT_PACKET;
+ }
+
+ return capabilities;
+}
+
+uint64_t bladerf2_get_fpga_capabilities(
+ const struct bladerf_version *fpga_version)
+{
+ uint64_t capabilities = 0;
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 1, 0)) {
+ capabilities |= BLADERF_CAP_TIMESTAMPS;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 3, 0)) {
+ capabilities |= BLADERF_CAP_PKT_HANDLER_FMT;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 3, 2)) {
+ capabilities |= BLADERF_CAP_VCTCXO_TRIMDAC_READ;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 4, 1)) {
+ capabilities |= BLADERF_CAP_MASKED_XBIO_WRITE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 6, 0)) {
+ capabilities |= BLADERF_CAP_TRX_SYNC_TRIG;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 10, 0)) {
+ capabilities |= BLADERF_CAP_SCHEDULED_RETUNE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 10, 1)) {
+ capabilities |= BLADERF_CAP_FPGA_TUNING;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 12, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_PACKET_META;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 15, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_8BIT_SAMPLES;
+ }
+
+ return capabilities;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h
new file mode 100644
index 0000000..43f3bde
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015-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
+ */
+
+/* This file defines device capabilities added across libbladeRF, FX3, and FPGA
+ * versions that we can check for */
+
+#ifndef BLADERF2_CAPABILITIES_H_
+#define BLADERF2_CAPABILITIES_H_
+
+#include <stdint.h>
+
+#include "board/board.h"
+#include "helpers/have_cap.h"
+
+/**
+ * Determine device's firmware capabilities.
+ *
+ * @param[in] fw_version Firmware version
+ *
+ * @return Capabilities bitmask
+ */
+uint64_t bladerf2_get_fw_capabilities(const struct bladerf_version *fw_version);
+
+/**
+ * Add capability bits based upon FPGA version stored in the device handle
+ *
+ * @param[in] fpga_version FPGA version
+ *
+ * @return Capabilities bitmask
+ */
+uint64_t bladerf2_get_fpga_capabilities(
+ const struct bladerf_version *fpga_version);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/common.c b/Radio/HW/BladeRF/src/board/bladerf2/common.c
new file mode 100644
index 0000000..15202f8
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/common.c
@@ -0,0 +1,415 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 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 "board/board.h"
+#include "capabilities.h"
+#include "common.h"
+
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+
+/* Board state to string map */
+char const *bladerf2_state_to_string[] = {
+ [STATE_UNINITIALIZED] = "Uninitialized",
+ [STATE_FIRMWARE_LOADED] = "Firmware Loaded",
+ [STATE_FPGA_LOADED] = "FPGA Loaded",
+ [STATE_INITIALIZED] = "Initialized",
+};
+
+
+/******************************************************************************/
+/* Sample format control */
+/******************************************************************************/
+
+static inline int requires_timestamps(bladerf_format format, bool *required)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7_META:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ *required = true;
+ break;
+
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC16_Q11:
+ *required = false;
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+int perform_format_config(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_format format)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ bool use_timestamps, other_using_timestamps;
+ bladerf_channel other;
+ uint32_t gpio_val;
+ int status;
+
+ status = requires_timestamps(format, &use_timestamps);
+ if (status != 0) {
+ log_debug("%s: Invalid format: %d\n", __FUNCTION__, format);
+ return status;
+ }
+
+ switch (dir) {
+ case BLADERF_RX:
+ other = BLADERF_TX;
+ break;
+
+ case BLADERF_TX:
+ other = BLADERF_RX;
+ break;
+
+ default:
+ log_debug("Invalid direction: %d\n", dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = requires_timestamps(board_data->module_format[other],
+ &other_using_timestamps);
+ if ((status == 0) && (other_using_timestamps != use_timestamps)) {
+ log_debug("Format conflict detected: RX=%d, TX=%d\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ CHECK_STATUS(dev->backend->config_gpio_read(dev, &gpio_val));
+
+ if (use_timestamps) {
+ gpio_val |= BLADERF_GPIO_TIMESTAMP;
+ } else {
+ gpio_val &= ~BLADERF_GPIO_TIMESTAMP;
+ }
+
+ if (format == BLADERF_FORMAT_PACKET_META) {
+ gpio_val |= BLADERF_GPIO_PACKET | BLADERF_GPIO_TIMESTAMP;
+ } else {
+ gpio_val &= ~BLADERF_GPIO_PACKET;
+ }
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ gpio_val |= BLADERF_GPIO_8BIT_MODE;
+ } else {
+ gpio_val &= ~BLADERF_GPIO_8BIT_MODE;
+ }
+
+ CHECK_STATUS(dev->backend->config_gpio_write(dev, gpio_val));
+
+ board_data->module_format[dir] = format;
+
+ return 0;
+}
+
+int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ switch (dir) {
+ case BLADERF_RX:
+ case BLADERF_TX:
+ /* We'll reconfigure the HW when we call perform_format_config,
+ * so we just need to update our stored information */
+ board_data->module_format[dir] = -1;
+ break;
+
+ default:
+ log_debug("%s: Invalid direction: %d\n", __FUNCTION__, dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Size checks */
+/******************************************************************************/
+
+bool is_valid_fpga_size(struct bladerf *dev, bladerf_fpga_size fpga, size_t len)
+{
+ /* We do not build FPGAs with compression enabled. Therfore, they
+ * will always have a fixed file size.
+ */
+ char const env_override[] = "BLADERF_SKIP_FPGA_SIZE_CHECK";
+
+ bool valid;
+ size_t expected;
+ int status;
+
+ status = dev->board->get_fpga_bytes(dev, &expected);
+ if (status < 0) {
+ log_error(
+ "Error %d querying FPGA size.\n",
+ status);
+ return false;
+ }
+
+ /* Provide a means to override this check. This is intended to allow
+ * folks who know what they're doing to work around this quickly without
+ * needing to make a code change. (e.g., someone building a custom FPGA
+ * image that enables compressoin) */
+ if (getenv(env_override)) {
+ log_info("Overriding FPGA size check per %s\n", env_override);
+ valid = true;
+ } else if (expected > 0) {
+ valid = (len == expected);
+ } else {
+ log_debug("Unknown FPGA type (%d). Using relaxed size criteria.\n",
+ fpga);
+
+ if (len < (1 * 1024 * 1024)) {
+ valid = false;
+ } else if (len >
+ (dev->flash_arch->tsize_bytes - BLADERF_FLASH_ADDR_FPGA)) {
+ valid = false;
+ } else {
+ valid = true;
+ }
+ }
+
+ if (!valid) {
+ log_warning("Detected potentially incorrect FPGA file (length was %d, "
+ "expected %d).\n",
+ len, expected);
+
+ log_debug("If you are certain this file is valid, you may define\n"
+ "BLADERF_SKIP_FPGA_SIZE_CHECK in your environment to skip "
+ "this check.\n\n");
+ }
+
+ return valid;
+}
+
+bool is_valid_fw_size(size_t len)
+{
+ /* Simple FW applications generally are significantly larger than this
+ */
+ if (len < (50 * 1024)) {
+ return false;
+ } else if (len > BLADERF_FLASH_BYTE_LEN_FIRMWARE) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bladerf_tuning_mode default_tuning_mode(struct bladerf *dev)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ bladerf_tuning_mode mode;
+ char const *env_var;
+ extern struct controller_fns const rfic_fpga_control;
+
+ mode = BLADERF_TUNING_MODE_HOST;
+
+ /* Detect TX FPGA bug and report warning */
+ if (BLADERF_TUNING_MODE_FPGA == mode && rfic_fpga_control.is_present(dev) &&
+ version_fields_less_than(&board_data->fpga_version, 0, 10, 2)) {
+ log_warning("FPGA v%u.%u.%u has errata related to FPGA-based tuning; "
+ "defaulting to host-based tuning. To use FPGA-based "
+ "tuning, update to FPGA v%u.%u.%u, or set the "
+ "BLADERF_DEFAULT_TUNING_MODE enviroment variable to "
+ "'fpga'.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch, 0, 10, 2);
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ env_var = getenv("BLADERF_DEFAULT_TUNING_MODE");
+
+ if (env_var != NULL) {
+ if (!strcasecmp("host", env_var)) {
+ mode = BLADERF_TUNING_MODE_HOST;
+ } else if (!strcasecmp("fpga", env_var)) {
+ /* Capability check */
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) {
+ log_error("The loaded FPGA version (%u.%u.%u) does not support "
+ "the tuning mode being used to override the default. "
+ "Ignoring BLADERF_DEFAULT_TUNING_MODE.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+ } else {
+ mode = BLADERF_TUNING_MODE_FPGA;
+ }
+ } else {
+ log_debug("Invalid tuning mode override: %s\n", env_var);
+ }
+ }
+
+ /* Check if the FPGA actually has RFIC control built in */
+ if (BLADERF_TUNING_MODE_FPGA == mode &&
+ !rfic_fpga_control.is_present(dev)) {
+ log_debug("FPGA does not have RFIC tuning capabilities, "
+ "defaulting to host-based control.\n");
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ log_debug("Default tuning mode: Host\n");
+ break;
+ case BLADERF_TUNING_MODE_FPGA:
+ log_debug("Default tuning mode: FPGA\n");
+ break;
+ default:
+ assert(!"Bug encountered.");
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ return mode;
+}
+
+
+/******************************************************************************/
+/* Misc */
+/******************************************************************************/
+
+bool check_total_sample_rate(struct bladerf *dev)
+{
+ int status;
+ uint32_t reg;
+ size_t i;
+
+ bladerf_sample_rate rate_accum = 0;
+ size_t active_channels = 0;
+
+ const bladerf_sample_rate MAX_SAMPLE_THROUGHPUT = 80000000;
+
+ /* Read RFFE control register */
+ status = dev->backend->rffe_control_read(dev, &reg);
+ if (status < 0) {
+ return false;
+ }
+
+ /* Accumulate sample rates for all channels */
+ if (_rffe_dir_enabled(reg, BLADERF_RX)) {
+ bladerf_sample_rate rx_rate;
+
+ status =
+ dev->board->get_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rx_rate);
+ if (status < 0) {
+ return false;
+ }
+
+ for (i = 0; i < dev->board->get_channel_count(dev, BLADERF_RX); ++i) {
+ if (_rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(i))) {
+ rate_accum += rx_rate;
+ ++active_channels;
+ }
+ }
+ }
+
+ if (_rffe_dir_enabled(reg, BLADERF_TX)) {
+ bladerf_sample_rate tx_rate;
+
+ status =
+ dev->board->get_sample_rate(dev, BLADERF_CHANNEL_TX(0), &tx_rate);
+ if (status < 0) {
+ return false;
+ }
+
+ for (i = 0; i < dev->board->get_channel_count(dev, BLADERF_TX); ++i) {
+ if (_rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(i))) {
+ rate_accum += tx_rate;
+ ++active_channels;
+ }
+ }
+ }
+
+ log_verbose("%s: active_channels=%d, rate_accum=%d, maximum=%d\n",
+ __FUNCTION__, active_channels, rate_accum,
+ MAX_SAMPLE_THROUGHPUT);
+
+ if (rate_accum > MAX_SAMPLE_THROUGHPUT) {
+ log_warning("The total sample throughput for the %d active channel%s, "
+ "%g Msps, is greater than the recommended maximum sample "
+ "throughput, %g Msps. You may experience dropped samples "
+ "unless the sample rate is reduced, or some channels are "
+ "deactivated.\n",
+ active_channels, (active_channels == 1 ? "" : "s"),
+ rate_accum / 1e6, MAX_SAMPLE_THROUGHPUT / 1e6);
+ return false;
+ }
+
+ return true;
+}
+
+bool does_rffe_dir_have_enabled_ch(uint32_t reg, bladerf_direction dir)
+{
+ switch (dir) {
+ case BLADERF_RX:
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(0)) ||
+ _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(1));
+ case BLADERF_TX:
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(0)) ||
+ _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(1));
+ }
+
+ return false;
+}
+
+int get_gain_offset(struct bladerf *dev, bladerf_channel ch, float *offset)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(offset);
+
+ struct bladerf_gain_range const *ranges = NULL;
+ bladerf_frequency frequency = 0;
+ size_t i, ranges_len;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ ranges = bladerf2_tx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges);
+ } else {
+ ranges = bladerf2_rx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges);
+ }
+
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &frequency));
+
+ for (i = 0; i < ranges_len; ++i) {
+ struct bladerf_gain_range const *r = &(ranges[i]);
+ struct bladerf_range const *rfreq = &(r->frequency);
+
+ // if the frequency range matches, and the range name is null,
+ // then we found our match
+ if (is_within_range(rfreq, frequency) && (NULL == r->name)) {
+ *offset = r->offset;
+ return 0;
+ }
+ }
+
+ return BLADERF_ERR_INVAL;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/common.h b/Radio/HW/BladeRF/src/board/bladerf2/common.h
new file mode 100644
index 0000000..5a88cb4
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/common.h
@@ -0,0 +1,494 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 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 BLADERF2_COMMON_H_
+#define BLADERF2_COMMON_H_
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include "log.h"
+#endif
+
+#include "bladerf2_common.h"
+#include "helpers/version.h"
+#include "streaming/sync.h"
+
+
+/******************************************************************************/
+/* Types */
+/******************************************************************************/
+
+enum bladerf2_vctcxo_trim_source {
+ TRIM_SOURCE_NONE,
+ TRIM_SOURCE_TRIM_DAC,
+ TRIM_SOURCE_PLL,
+ TRIM_SOURCE_AUXDAC
+};
+
+enum bladerf2_rfic_command_mode {
+ RFIC_COMMAND_HOST, /**< Host-based control */
+ RFIC_COMMAND_FPGA, /**< FPGA-based control */
+};
+
+struct controller_fns {
+ bool (*is_present)(struct bladerf *dev);
+ bool (*is_initialized)(struct bladerf *dev);
+ bool (*is_standby)(struct bladerf *dev);
+ int (*get_init_state)(struct bladerf *dev, bladerf_rfic_init_state *state);
+
+ int (*initialize)(struct bladerf *dev);
+ int (*standby)(struct bladerf *dev);
+ int (*deinitialize)(struct bladerf *dev);
+
+ int (*enable_module)(struct bladerf *dev, bladerf_channel ch, bool enable);
+
+ int (*get_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate);
+ int (*set_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate);
+
+ int (*get_frequency)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency);
+ int (*set_frequency)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+ int (*select_band)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+
+ int (*get_bandwidth)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth);
+ int (*set_bandwidth)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual);
+
+ int (*get_gain_mode)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode);
+ int (*set_gain_mode)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode);
+
+ int (*get_gain)(struct bladerf *dev, bladerf_channel ch, int *gain);
+ int (*set_gain)(struct bladerf *dev, bladerf_channel ch, int gain);
+
+ int (*get_gain_stage)(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain);
+ int (*set_gain_stage)(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain);
+
+ int (*get_rssi)(struct bladerf *dev,
+ bladerf_channel ch,
+ int *pre_rssi,
+ int *sym_rssi);
+
+ int (*get_filter)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir *rxfir,
+ bladerf_rfic_txfir *txfir);
+ int (*set_filter)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir rxfir,
+ bladerf_rfic_txfir txfir);
+
+ int (*get_txmute)(struct bladerf *dev, bladerf_channel ch, bool *state);
+ int (*set_txmute)(struct bladerf *dev, bladerf_channel ch, bool state);
+
+ int (*store_fastlock_profile)(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile);
+
+ int (*save_fastlock_profile)(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile,
+ uint8_t *values);
+
+ enum bladerf2_rfic_command_mode const command_mode;
+};
+
+struct bladerf2_board_data {
+ /* Board state */
+ enum {
+ STATE_UNINITIALIZED,
+ STATE_FIRMWARE_LOADED,
+ STATE_FPGA_LOADED,
+ STATE_INITIALIZED,
+ } state;
+
+ /* AD9361 PHY Handle */
+ struct ad9361_rf_phy *phy;
+
+ /* RFIC configuration parameters */
+ void *rfic_init_params;
+
+ /* Bitmask of capabilities determined by version numbers */
+ uint64_t capabilities;
+
+ /* Format currently being used with a module, or -1 if module is not used */
+ bladerf_format module_format[NUM_MODULES];
+
+ /* Which mode of operation we use for tuning */
+ bladerf_tuning_mode tuning_mode;
+
+ /* Board properties */
+ bladerf_fpga_size fpga_size;
+ /* Data message size */
+ size_t msg_size;
+
+ /* Version information */
+ struct bladerf_version fpga_version;
+ struct bladerf_version fw_version;
+ char fpga_version_str[BLADERF_VERSION_STR_MAX + 1];
+ char fw_version_str[BLADERF_VERSION_STR_MAX + 1];
+
+ /* Synchronous interface handles */
+ struct bladerf_sync sync[2];
+
+ /* VCTCXO trim state */
+ enum bladerf2_vctcxo_trim_source trim_source;
+ uint16_t trimdac_last_value; /**< saved running value */
+ uint16_t trimdac_stored_value; /**< cached value read from SPI flash */
+
+ /* Quick Tune Profile Status */
+ uint16_t quick_tune_tx_profile;
+ uint16_t quick_tune_rx_profile;
+
+ /* RFIC backend command handling */
+ struct controller_fns const *rfic;
+
+ /* RFIC FIR Filter status */
+ bladerf_rfic_rxfir rxfir;
+ bladerf_rfic_txfir txfir;
+
+ /* If true, RFIC control will be fully de-initialized on close, instead of
+ * just put into a standby state. */
+ bool rfic_reset_on_close;
+};
+
+struct bladerf_rfic_status_register {
+ bool rfic_initialized;
+ size_t write_queue_length;
+};
+
+
+/******************************************************************************/
+/* Externs */
+/******************************************************************************/
+
+extern AD9361_InitParam bladerf2_rfic_init_params;
+extern AD9361_InitParam bladerf2_rfic_init_params_fastagc_burst;
+extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config;
+extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config;
+extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec2;
+extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int2;
+extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec4;
+extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int4;
+extern const float ina219_r_shunt;
+
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+
+extern char const *bladerf2_state_to_string[4];
+
+
+/******************************************************************************/
+/* Macros */
+/******************************************************************************/
+
+/* Macro for logging and returning an error status. This should be used for
+ * errors defined in the \ref RETCODES list. */
+#define RETURN_ERROR_STATUS(_what, _status) \
+ do { \
+ log_error("%s: %s failed: %s\n", __FUNCTION__, _what, \
+ bladerf_strerror(_status)); \
+ return _status; \
+ } while (0)
+
+/* Macro for converting, logging, and returning libad9361 error codes. */
+#define RETURN_ERROR_AD9361(_what, _status) \
+ do { \
+ RETURN_ERROR_STATUS(_what, errno_ad9361_to_bladerf(_status)); \
+ } while (0)
+
+/* Macro for logging and returning ::BLADERF_ERR_INVAL */
+#define RETURN_INVAL_ARG(_what, _arg, _why) \
+ do { \
+ log_error("%s: %s '%s' invalid: %s\n", __FUNCTION__, _what, #_arg, \
+ _why); \
+ return BLADERF_ERR_INVAL; \
+ } while (0)
+
+#define RETURN_INVAL(_what, _why) \
+ do { \
+ log_error("%s: %s invalid: %s\n", __FUNCTION__, _what, _why); \
+ return BLADERF_ERR_INVAL; \
+ } while (0)
+
+/**
+ * @brief Null test
+ *
+ * @param _var The variable to check
+ *
+ * @return RETURN_INVAL if _var is null, continues otherwise
+ */
+#define NULL_CHECK(_var) \
+ do { \
+ if (NULL == _var) { \
+ RETURN_INVAL(#_var, "is null"); \
+ } \
+ } while (0)
+
+/**
+ * @brief Null test, with mutex unlock on failure
+ *
+ * @param _var The variable to check
+ *
+ * @return RETURN_INVAL if _var is null, continues otherwise
+ */
+#define NULL_CHECK_LOCKED(_var) \
+ do { \
+ NULL_CHECK(dev); \
+ \
+ if (NULL == _var) { \
+ MUTEX_UNLOCK(__lock); \
+ RETURN_INVAL(#_var, "is null"); \
+ } \
+ } while (0)
+
+/**
+ * @brief Board state check
+ *
+ * @param _state Minimum sufficient board state
+ *
+ * @return BLADERF_ERR_NOT_INIT if board's state is less than _state, continues
+ * otherwise
+ */
+#define CHECK_BOARD_STATE(_state) \
+ do { \
+ NULL_CHECK(dev); \
+ NULL_CHECK(dev->board); \
+ \
+ struct bladerf2_board_data *_bd = dev->board_data; \
+ \
+ if (_bd->state < _state) { \
+ log_error("%s: Board state insufficient for operation " \
+ "(current \"%s\", requires \"%s\").\n", \
+ __FUNCTION__, bladerf2_state_to_string[_bd->state], \
+ bladerf2_state_to_string[_state]); \
+ \
+ return BLADERF_ERR_NOT_INIT; \
+ } \
+ } while (0)
+
+/**
+ * @brief Test if board is a bladeRF 2
+ *
+ * @param _dev Device handle
+ *
+ * @return BLADERF_ERR_UNSUPPORTED if board is not a bladeRF 2, continues
+ * otherwise
+ */
+#define CHECK_BOARD_IS_BLADERF2(_dev) \
+ do { \
+ NULL_CHECK(_dev); \
+ NULL_CHECK(_dev->board); \
+ \
+ if (_dev->board != &bladerf2_board_fns) { \
+ log_error("%s: Board type \"%s\" not supported\n", __FUNCTION__, \
+ _dev->board->name); \
+ return BLADERF_ERR_UNSUPPORTED; \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and return early if it fails
+ *
+ * @param _fn The function
+ *
+ * @return function return value if less than zero; continues otherwise
+ */
+#define CHECK_STATUS(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ RETURN_ERROR_STATUS(#_fn, _s); \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and goto error if it fails
+ *
+ * @param _fn The function
+ *
+ * @note `int status` must be declared in the including scope
+ */
+#define CHECK_STATUS_GOTO(_fn) \
+ do { \
+ status = _fn; \
+ if (status < 0) { \
+ log_error("%s: %i failed: %s\n", #_fn, status, \
+ bladerf_strerror(status)); \
+ goto error; \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and, if it fails, unlock the mutex and return
+ *
+ * @param _fn The function
+ *
+ * @return function return value if less than zero; continues otherwise
+ */
+#define CHECK_STATUS_LOCKED(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ MUTEX_UNLOCK(__lock); \
+ RETURN_ERROR_STATUS(#_fn, _s); \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and return early if it fails, with error translation
+ * from AD936x to bladeRF return codes
+ *
+ * @param _fn The function
+ *
+ * @return function return value if less than zero; continues otherwise
+ */
+#define CHECK_AD936X(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ RETURN_ERROR_AD9361(#_fn, _s); \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and, if it fails, unlock the mutex and return, with
+ * error translation from AD936x to bladeRF return codes
+ *
+ * @param _fn The function
+ *
+ * @return function return value if less than zero; continues otherwise
+ */
+#define CHECK_AD936X_LOCKED(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ MUTEX_UNLOCK(__lock); \
+ RETURN_ERROR_AD9361(#_fn, _s); \
+ } \
+ } while (0)
+
+/**
+ * @brief Execute a command block with a mutex lock
+ *
+ * @note Variables declared within the including scope must be declared
+ * one-per-line.
+ *
+ * @param _lock Lock to hold
+ * @param _thing Block to execute
+ */
+#define WITH_MUTEX(_lock, _thing) \
+ do { \
+ MUTEX *__lock = _lock; \
+ \
+ MUTEX_LOCK(__lock); \
+ _thing; \
+ MUTEX_UNLOCK(__lock); \
+ } while (0)
+
+/**
+ * @brief Execute command block, conditional to _mode
+ *
+ * @param _dev Device handle
+ * @param _mode Command mode
+ * @param _thing Block to do if it happens
+ */
+#define IF_COMMAND_MODE(_dev, _mode, _thing) \
+ do { \
+ NULL_CHECK(_dev); \
+ NULL_CHECK(_dev->board_data); \
+ \
+ struct bladerf2_board_data *bd = _dev->board_data; \
+ \
+ if (bd->rfic->command_mode == _mode) { \
+ _thing; \
+ }; \
+ } while (0)
+
+
+/******************************************************************************/
+/* Functions */
+/******************************************************************************/
+
+/**
+ * Perform the neccessary device configuration for the specified format
+ * (e.g., enabling/disabling timestamp support), first checking that the
+ * requested format would not conflict with the other stream direction.
+ *
+ * @param dev Device handle
+ * @param[in] dir Direction that is currently being configured
+ * @param[in] format Format the channel is being configured for
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int perform_format_config(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_format format);
+
+/**
+ * Deconfigure and update any state pertaining what a format that a stream
+ * direction is no longer using.
+ *
+ * @param dev Device handle
+ * @param[in] dir Direction that is currently being deconfigured
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir);
+
+bool is_valid_fpga_size(struct bladerf *dev,
+ bladerf_fpga_size fpga,
+ size_t len);
+
+bool is_valid_fw_size(size_t len);
+
+bladerf_tuning_mode default_tuning_mode(struct bladerf *dev);
+
+bool check_total_sample_rate(struct bladerf *dev);
+
+bool does_rffe_dir_have_enabled_ch(uint32_t reg, bladerf_direction dir);
+
+int get_gain_offset(struct bladerf *dev, bladerf_channel ch, float *offset);
+
+#endif // BLADERF2_COMMON_H_
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c
new file mode 100644
index 0000000..7ee0b8e
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c
@@ -0,0 +1,50 @@
+#include "host_config.h"
+
+#include "helpers/version.h"
+
+/* Firmware-FPGA compatibility tables
+ *
+ * This list should be kept in decending order, such that the most recent
+ * versions are first, and the last entry should contain the earliest version
+ * that libbladeRF supports.
+ */
+
+#define VERSION(major, minor, patch) { major, minor, patch, NULL }
+
+static const struct compat fw_compat[] = {
+ /* Firmware requires >= FPGA */
+ { VERSION(2, 4, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 3, 2), VERSION(0, 6, 0) },
+ { VERSION(2, 3, 1), VERSION(0, 6, 0) },
+ { VERSION(2, 3, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 2, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 1, 1), VERSION(0, 6, 0) },
+ { VERSION(2, 1, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 0, 0), VERSION(0, 6, 0) },
+};
+
+const struct version_compat_table bladerf2_fw_compat_table = {fw_compat, ARRAY_SIZE(fw_compat)};
+
+static const struct compat fpga_compat[] = {
+ /* FPGA requires >= Firmware */
+ { VERSION(0, 15, 3), VERSION(2, 4, 0) },
+ { VERSION(0, 15, 2), VERSION(2, 4, 0) },
+ { VERSION(0, 15, 1), VERSION(2, 4, 0) },
+ { VERSION(0, 15, 0), VERSION(2, 4, 0) },
+ { VERSION(0, 14, 0), VERSION(2, 4, 0) },
+ { VERSION(0, 12, 0), VERSION(2, 2, 0) },
+ { VERSION(0, 11, 1), VERSION(2, 1, 0) },
+ { VERSION(0, 11, 0), VERSION(2, 1, 0) },
+ { VERSION(0, 10, 2), VERSION(2, 1, 0) },
+ { VERSION(0, 10, 1), VERSION(2, 1, 0) },
+ { VERSION(0, 10, 0), VERSION(2, 1, 0) },
+ { VERSION(0, 9, 0), VERSION(2, 1, 0) },
+ { VERSION(0, 8, 0), VERSION(2, 1, 0) },
+ { VERSION(0, 7, 3), VERSION(2, 1, 0) },
+ { VERSION(0, 7, 2), VERSION(2, 1, 0) },
+ { VERSION(0, 7, 1), VERSION(2, 0, 0) },
+ { VERSION(0, 7, 0), VERSION(2, 0, 0) },
+ { VERSION(0, 6, 0), VERSION(2, 0, 0) },
+};
+
+const struct version_compat_table bladerf2_fpga_compat_table = {fpga_compat, ARRAY_SIZE(fpga_compat)};
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h
new file mode 100644
index 0000000..67447b9
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h
@@ -0,0 +1,9 @@
+#ifndef BLADERF2_COMPATIBILITY_H_
+#define BLADERF2_COMPATIBILITY_H_
+
+#include "helpers/version.h"
+
+extern const struct version_compat_table bladerf2_fw_compat_table;
+extern const struct version_compat_table bladerf2_fpga_compat_table;
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c b/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c
new file mode 100644
index 0000000..089a8cb
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c
@@ -0,0 +1,767 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 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 <libbladeRF.h>
+
+#include "board/board.h"
+#include "common.h"
+#include "conversions.h"
+#include "iterators.h"
+#include "log.h"
+
+// #define BLADERF_HEADLESS_C_DEBUG
+
+
+/******************************************************************************/
+/* Declarations */
+/******************************************************************************/
+
+/**
+ * @brief RFIC Command Read
+ *
+ * Executes a command using the FPGA-based RFIC interface, returning the reply
+ * value in *data.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel to act upon; use ::BLADERF_CHANNEL_INVALID for
+ * non-channel-specific commands
+ * @param[in] cmd Command
+ * @param[out] data Pointer for response data
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+static int _rfic_cmd_read(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_command cmd,
+ uint64_t *data);
+
+/**
+ * @brief RFIC Command Write
+ *
+ * Executes a command using the FPGA-based RFIC interface, using the supplied
+ * data value as an argument. Waits until the command has been executed before
+ * returning.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel to act upon; use ::BLADERF_CHANNEL_INVALID for
+ * non-channel-specific commands
+ * @param[in] cmd Command
+ * @param[in] data Argument for command
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+static int _rfic_cmd_write(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_command cmd,
+ uint64_t data);
+
+
+/******************************************************************************/
+/* Helpers */
+/******************************************************************************/
+
+/* Build RFIC address from bladerf_rfic_command and bladerf_channel */
+#define RFIC_ADDRESS(cmd, ch) ((cmd & 0xFF) + ((ch & 0xF) << 8))
+
+static int _rfic_fpga_get_status(
+ struct bladerf *dev, struct bladerf_rfic_status_register *rfic_status)
+{
+ uint64_t sreg = 0;
+ int status;
+
+ status = _rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_STATUS, &sreg);
+
+ rfic_status->rfic_initialized = ((sreg >> 0) & 0x1);
+ rfic_status->write_queue_length = ((sreg >> 8) & 0xFF);
+
+ return status;
+}
+
+static int _rfic_fpga_get_status_wqlen(struct bladerf *dev)
+{
+ struct bladerf_rfic_status_register rfic_status;
+ int status;
+
+ status = _rfic_fpga_get_status(dev, &rfic_status);
+ if (status < 0) {
+ return status;
+ }
+
+#ifdef BLADERF_HEADLESS_C_DEBUG
+ if (rfic_status.write_queue_length > 0) {
+ log_verbose("%s: queue len = %d\n", __FUNCTION__,
+ rfic_status.write_queue_length);
+ }
+#endif
+
+ return (int)rfic_status.write_queue_length;
+}
+
+static int _rfic_fpga_spinwait(struct bladerf *dev)
+{
+ size_t const TRIES = 30;
+ unsigned int const DELAY = 100;
+ size_t count = 0;
+ int jobs;
+
+ /* Poll the CPU and spin until the job has been completed. */
+ do {
+ jobs = _rfic_fpga_get_status_wqlen(dev);
+ if (0 != jobs) {
+ usleep(DELAY);
+ }
+ } while ((0 != jobs) && (++count < TRIES));
+
+ /* If it's simply taking too long to dequeue the command, status will
+ * have the number of items in the queue. Bonk this down to a timeout. */
+ if (jobs > 0) {
+ jobs = BLADERF_ERR_TIMEOUT;
+ }
+
+ return jobs;
+}
+
+
+/******************************************************************************/
+/* Low level RFIC Accessors */
+/******************************************************************************/
+
+static int _rfic_cmd_read(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_command cmd,
+ uint64_t *data)
+{
+ return dev->backend->rfic_command_read(dev, RFIC_ADDRESS(cmd, ch), data);
+}
+
+static int _rfic_cmd_write(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_command cmd,
+ uint64_t data)
+{
+ /* Perform the write command. */
+ CHECK_STATUS(
+ dev->backend->rfic_command_write(dev, RFIC_ADDRESS(cmd, ch), data));
+
+ /* Block until the job has been completed. */
+ return _rfic_fpga_spinwait(dev);
+}
+
+
+/******************************************************************************/
+/* Initialization */
+/******************************************************************************/
+
+static bool _rfic_fpga_is_present(struct bladerf *dev)
+{
+ uint64_t sreg = 0;
+ int status;
+
+ status = _rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_STATUS, &sreg);
+ if (status < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static int _rfic_fpga_get_init_state(struct bladerf *dev,
+ bladerf_rfic_init_state *state)
+{
+ uint64_t data;
+
+ CHECK_STATUS(_rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_INIT, &data));
+
+ *state = (bladerf_rfic_init_state)data;
+
+ return 0;
+}
+
+static bool _rfic_fpga_is_initialized(struct bladerf *dev)
+{
+ bladerf_rfic_init_state state;
+ int status;
+
+ status = _rfic_fpga_get_init_state(dev, &state);
+ if (status < 0) {
+ log_error("%s: failed to get RFIC initialization state: %s\n",
+ __FUNCTION__, bladerf_strerror(status));
+ return false;
+ }
+
+ return BLADERF_RFIC_INIT_STATE_ON == state;
+}
+
+static bool _rfic_fpga_is_standby(struct bladerf *dev)
+{
+ bladerf_rfic_init_state state;
+ int status;
+
+ status = _rfic_fpga_get_init_state(dev, &state);
+ if (status < 0) {
+ log_error("%s: failed to get RFIC initialization state: %s\n",
+ __FUNCTION__, bladerf_strerror(status));
+ return false;
+ }
+
+ return BLADERF_RFIC_INIT_STATE_STANDBY == state;
+}
+
+static int _rfic_fpga_initialize(struct bladerf *dev)
+{
+ log_debug("%s: initializing\n", __FUNCTION__);
+
+ return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_INIT,
+ BLADERF_RFIC_INIT_STATE_ON);
+}
+
+static int _rfic_fpga_standby(struct bladerf *dev)
+{
+ log_debug("%s: entering standby mode\n", __FUNCTION__);
+
+ return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_INIT,
+ BLADERF_RFIC_INIT_STATE_STANDBY);
+}
+
+static int _rfic_fpga_deinitialize(struct bladerf *dev)
+{
+ log_debug("%s: deinitializing\n", __FUNCTION__);
+
+ return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_INIT,
+ BLADERF_RFIC_INIT_STATE_OFF);
+}
+
+
+/******************************************************************************/
+/* Enable */
+/******************************************************************************/
+
+static int _rfic_fpga_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool ch_enable)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ bladerf_direction dir = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX;
+ uint32_t reg; /* RFFE register value */
+ bool ch_enabled; /* Channel: initial state */
+ bool ch_pending; /* Channel: target state is not initial state */
+ bool dir_enabled; /* Direction: initial state */
+ bool dir_enable; /* Direction: target state */
+ bool dir_pending; /* Direction: target state is not initial state */
+ bool be_toggle; /* Backend: toggle off/on to reset backend FIFO */
+ bool be_teardown; /* Backend: disable backend module */
+ bool be_setup; /* Backend: enable backend module */
+
+ /* Read RFFE control register */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+
+#ifdef BLADERF_HEADLESS_C_DEBUG
+ uint32_t reg_old = reg;
+#endif
+
+ /* Calculate initial and target states */
+ ch_enabled = _rffe_ch_enabled(reg, ch);
+ dir_enabled = _rffe_dir_enabled(reg, dir);
+ dir_enable = ch_enable || _rffe_dir_otherwise_enabled(reg, ch);
+ ch_pending = ch_enabled != ch_enable;
+ dir_pending = dir_enabled != dir_enable;
+ be_toggle = !BLADERF_CHANNEL_IS_TX(ch) && ch_enable && !dir_pending;
+ be_setup = be_toggle || (dir_pending && dir_enable);
+ be_teardown = be_toggle || (dir_pending && !dir_enable);
+
+ /* Perform Direction Teardown */
+ if (dir_pending && !dir_enable) {
+ sync_deinit(&board_data->sync[dir]);
+ perform_format_deconfig(dev, dir);
+ }
+
+ /* Perform Channel Setup/Teardown */
+ if (ch_pending) {
+ /* Set/unset TX mute */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_STATUS(rfic->set_txmute(dev, ch, !ch_enable));
+ }
+
+ /* Execute RFIC enable command. */
+ CHECK_STATUS(_rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_ENABLE,
+ ch_enable ? 1 : 0));
+ }
+
+ /* Perform backend teardown */
+ if (be_teardown) {
+ CHECK_STATUS(dev->backend->enable_module(dev, dir, false));
+ }
+
+ /* Perform backend setup */
+ if (be_setup) {
+ CHECK_STATUS(dev->backend->enable_module(dev, dir, true));
+ }
+
+ /* Warn the user if the sample rate isn't reasonable */
+ if (ch_pending && ch_enable) {
+ check_total_sample_rate(dev);
+ }
+
+#ifdef BLADERF_HEADLESS_C_DEBUG
+ /* Debug output... */
+ if (BLADERF_LOG_LEVEL_VERBOSE == log_get_verbosity()) {
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+
+ log_verbose(
+ "%s: %s ch[en=%d->%d pend=%d] dir[en=%d->%d pend=%d] be[clr=%d "
+ "su=%d td=%d] reg=0x%08x->0x%08x\n",
+ __FUNCTION__, channel2str(ch), ch_enabled, ch_enable, ch_pending,
+ dir_enabled, dir_enable, dir_pending, be_toggle, be_setup,
+ be_teardown, reg_old, reg);
+ }
+#endif
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Sample rate */
+/******************************************************************************/
+
+static int _rfic_fpga_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate)
+{
+ uint64_t readval;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_SAMPLERATE, &readval));
+
+ *rate = (uint32_t)readval;
+
+ return 0;
+}
+
+static int _rfic_fpga_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate)
+{
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_SAMPLERATE, rate);
+}
+
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+static int _rfic_fpga_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ bladerf_frequency freq;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_FREQUENCY, &freq));
+
+ *frequency = freq;
+
+ return 0;
+}
+
+static int _rfic_fpga_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_frequency_range(dev, ch, &range));
+
+ if (!is_within_range(range, frequency)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FREQUENCY, frequency);
+}
+
+static int _rfic_fpga_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ // This functionality is unnecessary with the headless architecture.
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+static int _rfic_fpga_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ uint64_t readval;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_BANDWIDTH, &readval));
+
+ *bandwidth = (uint32_t)readval;
+
+ return 0;
+}
+
+static int _rfic_fpga_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_bandwidth_range(dev, ch, &range));
+
+ if (!is_within_range(range, bandwidth)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ CHECK_STATUS(
+ _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_BANDWIDTH, bandwidth));
+
+ if (actual != NULL) {
+ return rfic->get_bandwidth(dev, ch, actual);
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Gain */
+/* These functions handle offset */
+/******************************************************************************/
+
+static int _rfic_fpga_get_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ int *gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ char const *stage = BLADERF_CHANNEL_IS_TX(ch) ? "dsa" : "full";
+ int val;
+ float offset;
+
+ CHECK_STATUS(get_gain_offset(dev, ch, &offset));
+
+ CHECK_STATUS(rfic->get_gain_stage(dev, ch, stage, &val));
+
+ *gain = __round_int(val + offset);
+
+ return 0;
+}
+
+static int _rfic_fpga_set_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ int gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ char const *stage = BLADERF_CHANNEL_IS_TX(ch) ? "dsa" : "full";
+ float offset;
+
+ CHECK_STATUS(get_gain_offset(dev, ch, &offset));
+
+ return rfic->set_gain_stage(dev, ch, stage, __round_int(gain - offset));
+}
+
+
+/******************************************************************************/
+/* Gain mode */
+/******************************************************************************/
+
+static int _rfic_fpga_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode)
+{
+ uint64_t readval;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_warning("%s: automatic gain control not valid for TX channels\n",
+ __FUNCTION__);
+ *mode = BLADERF_GAIN_DEFAULT;
+ return 0;
+ }
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_GAINMODE, &readval));
+
+ *mode = (bladerf_gain_mode)readval;
+
+ return 0;
+}
+
+static int _rfic_fpga_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode)
+{
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_warning("%s: automatic gain control not valid for TX channels\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_GAINMODE, mode);
+}
+
+
+/******************************************************************************/
+/* Gain stage */
+/* These functions handle scaling */
+/******************************************************************************/
+
+static int _rfic_fpga_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain)
+{
+ struct bladerf_range const *range = NULL;
+ uint64_t val;
+
+ if ((BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "dsa") != 0) ||
+ (!BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "full") != 0)) {
+ log_error("%s: unknown gain stage '%s'\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ CHECK_STATUS(_rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_GAIN, &val));
+
+ *gain = __unscale_int(range, val);
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ *gain = -(*gain);
+ }
+
+ return 0;
+}
+
+static int _rfic_fpga_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain)
+{
+ struct bladerf_range const *range = NULL;
+ int64_t val;
+
+ if ((BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "dsa") != 0) ||
+ (!BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "full") != 0)) {
+ log_error("%s: unknown gain stage '%s'\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ if (BLADERF_CHANNEL_IS_TX(ch) && gain < -89) {
+ val = -89750;
+ } else {
+ val = __scale_int64(range, clamp_to_range(range, gain));
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ val = -val;
+ }
+
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_GAIN, (uint64_t)val);
+}
+
+
+/******************************************************************************/
+/* RSSI */
+/******************************************************************************/
+
+static int _rfic_fpga_get_rssi(struct bladerf *dev,
+ bladerf_channel ch,
+ int *pre_rssi,
+ int *sym_rssi)
+{
+ uint64_t readval;
+ int16_t mult, pre, sym;
+
+ CHECK_STATUS(_rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_RSSI, &readval));
+
+ mult = (int16_t)((readval >> BLADERF_RFIC_RSSI_MULT_SHIFT) &
+ BLADERF_RFIC_RSSI_MULT_MASK);
+ pre = (int16_t)((readval >> BLADERF_RFIC_RSSI_PRE_SHIFT) &
+ BLADERF_RFIC_RSSI_PRE_MASK);
+ sym = (int16_t)((readval >> BLADERF_RFIC_RSSI_SYM_SHIFT) &
+ BLADERF_RFIC_RSSI_SYM_MASK);
+
+ *pre_rssi = __round_int(pre / (float)mult);
+ *sym_rssi = __round_int(sym / (float)mult);
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Filter */
+/******************************************************************************/
+
+static int _rfic_fpga_get_filter(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir *rxfir,
+ bladerf_rfic_txfir *txfir)
+{
+ uint64_t readval;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_FILTER, &readval));
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (NULL != txfir) {
+ *txfir = (bladerf_rfic_txfir)readval;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+ } else {
+ if (NULL != rxfir) {
+ *rxfir = (bladerf_rfic_rxfir)readval;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int _rfic_fpga_set_filter(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir rxfir,
+ bladerf_rfic_txfir txfir)
+{
+ uint64_t data;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ data = (uint64_t)txfir;
+ } else {
+ data = (uint64_t)rxfir;
+ }
+
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FILTER, data);
+}
+
+
+/******************************************************************************/
+/* TX Mute */
+/******************************************************************************/
+
+static int _rfic_fpga_get_txmute(struct bladerf *dev,
+ bladerf_channel ch,
+ bool *state)
+{
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ uint64_t readval;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_TXMUTE, &readval));
+
+ return (readval > 0);
+ }
+
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int _rfic_fpga_set_txmute(struct bladerf *dev,
+ bladerf_channel ch,
+ bool state)
+{
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_TXMUTE,
+ state ? 1 : 0);
+ }
+
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+
+/******************************************************************************/
+/* Fastlock */
+/******************************************************************************/
+
+static int _rfic_fpga_store_fastlock_profile(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile)
+{
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FASTLOCK, profile);
+}
+
+
+/******************************************************************************/
+/* Function pointers */
+/******************************************************************************/
+
+struct controller_fns const rfic_fpga_control = {
+ FIELD_INIT(.is_present, _rfic_fpga_is_present),
+ FIELD_INIT(.is_standby, _rfic_fpga_is_standby),
+ FIELD_INIT(.is_initialized, _rfic_fpga_is_initialized),
+ FIELD_INIT(.get_init_state, _rfic_fpga_get_init_state),
+
+ FIELD_INIT(.initialize, _rfic_fpga_initialize),
+ FIELD_INIT(.standby, _rfic_fpga_standby),
+ FIELD_INIT(.deinitialize, _rfic_fpga_deinitialize),
+
+ FIELD_INIT(.enable_module, _rfic_fpga_enable_module),
+
+ FIELD_INIT(.get_sample_rate, _rfic_fpga_get_sample_rate),
+ FIELD_INIT(.set_sample_rate, _rfic_fpga_set_sample_rate),
+
+ FIELD_INIT(.get_frequency, _rfic_fpga_get_frequency),
+ FIELD_INIT(.set_frequency, _rfic_fpga_set_frequency),
+ FIELD_INIT(.select_band, _rfic_fpga_select_band),
+
+ FIELD_INIT(.get_bandwidth, _rfic_fpga_get_bandwidth),
+ FIELD_INIT(.set_bandwidth, _rfic_fpga_set_bandwidth),
+
+ FIELD_INIT(.get_gain_mode, _rfic_fpga_get_gain_mode),
+ FIELD_INIT(.set_gain_mode, _rfic_fpga_set_gain_mode),
+
+ FIELD_INIT(.get_gain, _rfic_fpga_get_gain),
+ FIELD_INIT(.set_gain, _rfic_fpga_set_gain),
+
+ FIELD_INIT(.get_gain_stage, _rfic_fpga_get_gain_stage),
+ FIELD_INIT(.set_gain_stage, _rfic_fpga_set_gain_stage),
+
+ FIELD_INIT(.get_rssi, _rfic_fpga_get_rssi),
+
+ FIELD_INIT(.get_filter, _rfic_fpga_get_filter),
+ FIELD_INIT(.set_filter, _rfic_fpga_set_filter),
+
+ FIELD_INIT(.get_txmute, _rfic_fpga_get_txmute),
+ FIELD_INIT(.set_txmute, _rfic_fpga_set_txmute),
+
+ FIELD_INIT(.store_fastlock_profile, _rfic_fpga_store_fastlock_profile),
+
+ FIELD_INIT(.command_mode, RFIC_COMMAND_FPGA),
+};
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c b/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c
new file mode 100644
index 0000000..7cccbe6
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c
@@ -0,0 +1,1058 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 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 <libbladeRF.h>
+
+#include "ad936x_helpers.h"
+#include "bladerf2_common.h"
+#include "board/board.h"
+#include "common.h"
+#include "helpers/wallclock.h"
+#include "iterators.h"
+#include "log.h"
+
+// #define BLADERF_HOSTED_C_DEBUG
+
+
+/******************************************************************************/
+/* Initialization */
+/******************************************************************************/
+
+static bool _rfic_host_is_present(struct bladerf *dev)
+{
+ return true;
+}
+
+static bool _rfic_host_is_initialized(struct bladerf *dev)
+{
+ if (NULL == dev || NULL == dev->board_data) {
+ return false;
+ }
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return (NULL != board_data->phy);
+}
+
+static int _rfic_host_get_init_state(struct bladerf *dev,
+ bladerf_rfic_init_state *state)
+{
+ if (_rfic_host_is_initialized(dev)) {
+ *state = BLADERF_RFIC_INIT_STATE_ON;
+ } else {
+ *state = BLADERF_RFIC_INIT_STATE_OFF;
+ }
+
+ return 0;
+}
+
+static bool _rfic_host_is_standby(struct bladerf *dev)
+{
+ return false;
+}
+
+static int _rfic_host_initialize(struct bladerf *dev)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = NULL;
+ struct controller_fns const *rfic = board_data->rfic;
+ uint32_t reg;
+ bladerf_direction dir;
+ bladerf_channel ch;
+ size_t i;
+ uint32_t config_gpio;
+
+ log_debug("%s: initializating\n", __FUNCTION__);
+
+ /* Initialize RFFE control */
+ CHECK_STATUS(dev->backend->rffe_control_write(
+ dev, (1 << RFFE_CONTROL_ENABLE) | (1 << RFFE_CONTROL_TXNRX)));
+
+
+ CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio));
+
+ board_data->rfic_init_params = (config_gpio & BLADERF_GPIO_PACKET_CORE_PRESENT) ?
+ (void *)&bladerf2_rfic_init_params_fastagc_burst :
+ (void *)&bladerf2_rfic_init_params;
+
+ /* Initialize AD9361 */
+ CHECK_AD936X(ad9361_init(&phy, (AD9361_InitParam *)board_data->rfic_init_params, dev));
+
+ if (NULL == phy || NULL == phy->pdata) {
+ RETURN_ERROR_STATUS("ad9361_init struct initialization",
+ BLADERF_ERR_UNEXPECTED);
+ }
+
+ log_verbose("%s: ad9361 initialized @ %p\n", __FUNCTION__, phy);
+
+ board_data->phy = phy;
+
+ /* Force AD9361 to a non-default freq. This will entice it to do a
+ * proper re-tuning when we set it back to the default freq later on. */
+ FOR_EACH_DIRECTION(dir)
+ {
+ FOR_EACH_CHANNEL(dir, 1, i, ch)
+ {
+ CHECK_STATUS(rfic->set_frequency(dev, ch, RESET_FREQUENCY));
+ }
+ }
+
+ /* Set up AD9361 FIR filters */
+ /* TODO: permit specification of filter taps, for the truly brave */
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0),
+ BLADERF_RFIC_RXFIR_DEFAULT, 0));
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0,
+ BLADERF_RFIC_TXFIR_DEFAULT));
+
+ /* Clear RFFE control */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+ reg &= ~(1 << RFFE_CONTROL_TXNRX);
+ reg &= ~(1 << RFFE_CONTROL_ENABLE);
+ reg &= ~(1 << RFFE_CONTROL_RX_SPDT_1);
+ reg &= ~(1 << RFFE_CONTROL_RX_SPDT_2);
+ reg &= ~(1 << RFFE_CONTROL_TX_SPDT_1);
+ reg &= ~(1 << RFFE_CONTROL_TX_SPDT_2);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_0);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_0);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_1);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_1);
+ CHECK_STATUS(dev->backend->rffe_control_write(dev, reg));
+
+ /* Move AD9361 back to desired frequency */
+ CHECK_STATUS(rfic->set_frequency(dev, BLADERF_CHANNEL_RX(0),
+ phy->pdata->rx_synth_freq));
+ CHECK_STATUS(rfic->set_frequency(dev, BLADERF_CHANNEL_TX(0),
+ phy->pdata->tx_synth_freq));
+
+ /* Mute TX channels */
+ FOR_EACH_CHANNEL(BLADERF_TX, dev->board->get_channel_count(dev, BLADERF_TX),
+ i, ch)
+ {
+ CHECK_STATUS(rfic->set_txmute(dev, ch, true));
+ }
+
+ log_debug("%s: initialization complete\n", __FUNCTION__);
+
+ return 0;
+}
+
+static int _rfic_host_deinitialize(struct bladerf *dev)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ uint32_t reg;
+
+ log_debug("%s: deinitializing\n", __FUNCTION__);
+
+ /* Clear RFFE control */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+ reg &= ~(1 << RFFE_CONTROL_TXNRX);
+ reg &= ~(1 << RFFE_CONTROL_ENABLE);
+ reg &= ~(1 << RFFE_CONTROL_RX_SPDT_1);
+ reg &= ~(1 << RFFE_CONTROL_RX_SPDT_2);
+ reg &= ~(1 << RFFE_CONTROL_TX_SPDT_1);
+ reg &= ~(1 << RFFE_CONTROL_TX_SPDT_2);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_0);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_0);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_1);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_1);
+ CHECK_STATUS(dev->backend->rffe_control_write(dev, reg));
+
+ if (NULL != board_data->phy) {
+ CHECK_STATUS(ad9361_deinit(board_data->phy));
+ board_data->phy = NULL;
+ }
+
+ return 0;
+}
+
+static int _rfic_host_standby(struct bladerf *dev)
+{
+ return _rfic_host_deinitialize(dev);
+}
+
+
+/******************************************************************************/
+/* Enable */
+/******************************************************************************/
+
+static int _rfic_host_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool enable)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ bladerf_direction dir = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX;
+ bladerf_frequency freq = 0;
+ bladerf_channel_layout layout;
+ uint32_t reg; /* RFFE register value */
+ uint32_t reg_old; /* Original RFFE register value */
+ int ch_bit; /* RFFE MIMO channel bit */
+ int dir_bit; /* RFFE RX/TX enable bit */
+ bool ch_pending; /* Requested channel state differs */
+ bool dir_enable; /* Direction is enabled */
+ bool dir_pending; /* Requested direction state differs */
+ bool backend_clear; /* Backend requires reset */
+ bool mimo_enabled;
+
+ /* Look up the RFFE bits affecting this channel */
+ ch_bit = _get_rffe_control_bit_for_ch(ch);
+ dir_bit = _get_rffe_control_bit_for_dir(dir);
+ if (ch_bit < 0 || dir_bit < 0) {
+ RETURN_ERROR_STATUS("_get_rffe_control_bit", BLADERF_ERR_INVAL);
+ }
+
+ /* Query the current frequency if necessary */
+ if (enable) {
+ CHECK_STATUS(rfic->get_frequency(dev, ch, &freq));
+ }
+
+ /**
+ * If we are moving from 0 active channels to 1 active channel:
+ * Channel: Setup SPDT, MIMO, TX Mute
+ * Direction: Setup ENABLE/TXNRX, RFIC port
+ * Backend: Enable
+ *
+ * If we are moving from 1 active channel to 0 active channels:
+ * Channel: Teardown SPDT, MIMO, TX Mute
+ * Direction: Teardown ENABLE/TXNRX, RFIC port, Sync
+ * Backend: Disable
+ *
+ * If we are enabling an nth channel, where n > 1:
+ * Channel: Setup SPDT, MIMO, TX Mute
+ * Direction: no change
+ * Backend: Clear
+ *
+ * If we are disabling an nth channel, where n > 1:
+ * Channel: Teardown SPDT, MIMO, TX Mute
+ * Direction: no change
+ * Backend: no change
+ */
+
+ /* Read RFFE control register */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+ reg_old = reg;
+ ch_pending = _rffe_ch_enabled(reg, ch) != enable;
+ layout = board_data->sync->stream_config.layout;
+ mimo_enabled = layout == BLADERF_RX_X2 || layout == BLADERF_TX_X2;
+
+ if (layout == BLADERF_TX_X2) {
+ if (enable) {
+ log_debug("Enabling both TX channels. MIMO layout configured.\n");
+ reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(0)));
+ reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(1)));
+ } else {
+ log_debug("Disabling both TX channels. MIMO layout configured.\n");
+ reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(0)));
+ reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(1)));
+ }
+ }
+
+ if (layout == BLADERF_RX_X2) {
+ if (enable) {
+ log_debug("Enabling both RX channels. MIMO layout configured.\n");
+ reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(0)));
+ reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(1)));
+ } else {
+ log_debug("Disabling both RX channels. MIMO layout configured.\n");
+ reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(0)));
+ reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(1)));
+ }
+ }
+
+ /* Channel Setup/Teardown */
+ if (ch_pending) {
+ /* Modify SPDT bits */
+ CHECK_STATUS(_modify_spdt_bits_by_freq(&reg, ch, enable, freq));
+
+ /* Modify MIMO channel enable bits */
+ if (enable) {
+ reg |= (1 << ch_bit);
+ } else {
+ reg &= ~(1 << ch_bit);
+ }
+
+ /* Set/unset TX mute */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ txmute_set(phy, ch, !enable);
+ }
+ }
+
+ dir_enable = enable || does_rffe_dir_have_enabled_ch(reg, dir);
+ dir_pending = _rffe_dir_enabled(reg, dir) != dir_enable;
+
+ /* Direction Setup/Teardown */
+ if (dir_pending) {
+ /* Modify ENABLE/TXNRX bits */
+ if (dir_enable) {
+ reg |= (1 << dir_bit);
+ } else {
+ reg &= ~(1 << dir_bit);
+ }
+
+ /* Select RFIC port */
+ CHECK_STATUS(set_ad9361_port_by_freq(phy, ch, dir_enable, freq));
+
+ /* Tear down sync interface if required */
+ if (!dir_enable) {
+ sync_deinit(&board_data->sync[dir]);
+ perform_format_deconfig(dev, dir);
+ }
+ }
+
+ /* Reset FIFO if we are enabling an additional RX channel */
+ backend_clear = enable && !dir_pending && BLADERF_RX == dir && !mimo_enabled;
+
+ /* Write RFFE control register */
+ if (reg_old != reg) {
+ CHECK_STATUS(dev->backend->rffe_control_write(dev, reg));
+ } else {
+ log_debug("%s: reg value unchanged? (%08x)\n", __FUNCTION__, reg);
+ }
+
+ /* Backend Setup/Teardown/Reset */
+ if (dir_pending || backend_clear) {
+ if (!dir_enable || backend_clear) {
+ CHECK_STATUS(dev->backend->enable_module(dev, dir, false));
+ }
+
+ if (dir_enable || backend_clear) {
+ CHECK_STATUS(dev->backend->enable_module(dev, dir, true));
+ }
+ }
+
+ /* Warn the user if the sample rate isn't reasonable */
+ if (ch_pending && enable) {
+ check_total_sample_rate(dev);
+ }
+
+#ifdef BLADERF_HOSTED_C_DEBUG
+ /* Debug logging */
+ static uint64_t lastrun = 0; /* nsec value at last run */
+ uint64_t nsec = wallclock_get_current_nsec();
+
+ log_debug("%s: %s%d ch_en=%d ch_pend=%d dir_en=%d dir_pend=%d be_clr=%d "
+ "reg=0x%08x->0x%08x nsec=%" PRIu64 " (delta: %" PRIu64 ")\n",
+ __FUNCTION__, BLADERF_TX == dir ? "TX" : "RX", (ch >> 1) + 1,
+ enable, ch_pending, dir_enable, dir_pending, backend_clear,
+ reg_old, reg, nsec, nsec - lastrun);
+
+ lastrun = nsec;
+#endif
+
+ /** Force rerun frequency calibration */
+ if (enable) {
+ bladerf_frequency current_frequency;
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &current_frequency));
+ CHECK_STATUS(dev->board->set_frequency(dev, ch, current_frequency));
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Sample rate */
+/******************************************************************************/
+
+static int _rfic_host_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_get_tx_sampling_freq(phy, rate));
+ } else {
+ CHECK_AD936X(ad9361_get_rx_sampling_freq(phy, rate));
+ }
+
+ return 0;
+}
+
+static int _rfic_host_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_set_tx_sampling_freq(phy, rate));
+ } else {
+ CHECK_AD936X(ad9361_set_rx_sampling_freq(phy, rate));
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+static int _rfic_host_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ bladerf_frequency lo_frequency;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_get_tx_lo_freq(phy, &lo_frequency));
+ } else {
+ CHECK_AD936X(ad9361_get_rx_lo_freq(phy, &lo_frequency));
+ }
+
+ *frequency = lo_frequency;
+
+ return 0;
+}
+
+static int _rfic_host_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_frequency_range(dev, ch, &range));
+
+ if (!is_within_range(range, frequency)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ /* Set up band selection */
+ CHECK_STATUS(rfic->select_band(dev, ch, frequency));
+
+ /* Change LO frequency */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_set_tx_lo_freq(phy, frequency));
+ } else {
+ CHECK_AD936X(ad9361_set_rx_lo_freq(phy, frequency));
+ }
+
+ return 0;
+}
+
+static int _rfic_host_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ uint32_t reg;
+ size_t i;
+
+ /* Read RFFE control register */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+
+ /* Modify the SPDT bits. */
+ /* We have to do this for all the channels sharing the same LO. */
+ for (i = 0; i < 2; ++i) {
+ bladerf_channel bch = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_CHANNEL_TX(i)
+ : BLADERF_CHANNEL_RX(i);
+ CHECK_STATUS(_modify_spdt_bits_by_freq(
+ &reg, bch, _rffe_ch_enabled(reg, bch), frequency));
+ }
+
+ /* Write RFFE control register */
+ CHECK_STATUS(dev->backend->rffe_control_write(dev, reg));
+
+ /* Set AD9361 port */
+ CHECK_STATUS(
+ set_ad9361_port_by_freq(phy, ch, _rffe_ch_enabled(reg, ch), frequency));
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+static int _rfic_host_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_get_tx_rf_bandwidth(phy, bandwidth));
+ } else {
+ CHECK_AD936X(ad9361_get_rx_rf_bandwidth(phy, bandwidth));
+ }
+
+ return 0;
+}
+
+static int _rfic_host_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_bandwidth_range(dev, ch, &range));
+
+ bandwidth = (unsigned int)clamp_to_range(range, bandwidth);
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_set_tx_rf_bandwidth(phy, bandwidth));
+ } else {
+ CHECK_AD936X(ad9361_set_rx_rf_bandwidth(phy, bandwidth));
+ }
+
+ if (actual != NULL) {
+ return rfic->get_bandwidth(dev, ch, actual);
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Gain mode */
+/******************************************************************************/
+
+static int _rfic_host_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ uint8_t const rfic_ch = ch >> 1;
+ uint8_t gc_mode;
+ bool ok;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_warning("%s: automatic gain control not valid for TX channels\n",
+ __FUNCTION__);
+ *mode = BLADERF_GAIN_DEFAULT;
+ return 0;
+ }
+
+ /* Get the gain */
+ CHECK_AD936X(ad9361_get_rx_gain_control_mode(phy, rfic_ch, &gc_mode));
+
+ /* Mode conversion */
+ if (mode != NULL) {
+ *mode = gainmode_ad9361_to_bladerf(gc_mode, &ok);
+ if (!ok) {
+ RETURN_INVAL("mode", "is not valid");
+ }
+ }
+
+ return 0;
+}
+
+static int _rfic_host_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ uint8_t const rfic_ch = ch >> 1;
+ enum rf_gain_ctrl_mode gc_mode;
+ bool ok;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_warning("%s: automatic gain control not valid for TX channels\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ /* Channel conversion */
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ gc_mode = ((AD9361_InitParam *)board_data->rfic_init_params)->gc_rx1_mode;
+ break;
+
+ case BLADERF_CHANNEL_RX(1):
+ gc_mode = ((AD9361_InitParam *)board_data->rfic_init_params)->gc_rx2_mode;
+ break;
+
+ default:
+ log_error("%s: unknown channel index (%d)\n", __FUNCTION__, ch);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ /* Mode conversion */
+ if (mode != BLADERF_GAIN_DEFAULT) {
+ gc_mode = gainmode_bladerf_to_ad9361(mode, &ok);
+ if (!ok) {
+ RETURN_INVAL("mode", "is not valid");
+ }
+ }
+
+ /* Set the mode! */
+ CHECK_AD936X(ad9361_set_rx_gain_control_mode(phy, rfic_ch, gc_mode));
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Gain */
+/* These functions handle offset */
+/******************************************************************************/
+
+static int _rfic_host_get_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ int *gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ int val;
+ float offset;
+
+ CHECK_STATUS(get_gain_offset(dev, ch, &offset));
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ bool muted;
+
+ CHECK_STATUS(txmute_get(phy, ch, &muted));
+
+ if (muted) {
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(
+ dev->board->get_gain_stage_range(dev, ch, "dsa", &range));
+
+ val = -__unscale_int(range, txmute_get_cached(phy, ch));
+ } else {
+ CHECK_STATUS(rfic->get_gain_stage(dev, ch, "dsa", &val));
+ }
+ } else {
+ CHECK_STATUS(rfic->get_gain_stage(dev, ch, "full", &val));
+ }
+
+ *gain = __round_int(val + offset);
+
+ return 0;
+}
+
+static int _rfic_host_set_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ int gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ int val;
+ float offset;
+
+ CHECK_STATUS(get_gain_offset(dev, ch, &offset));
+
+ val = gain - offset;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ bool muted;
+
+ CHECK_STATUS(txmute_get(phy, ch, &muted));
+
+ if (muted) {
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(
+ dev->board->get_gain_stage_range(dev, ch, "dsa", &range));
+
+ CHECK_STATUS(txmute_set_cached(phy, ch, -__scale_int(range, val)));
+ } else {
+ CHECK_STATUS(rfic->set_gain_stage(dev, ch, "dsa", val));
+ }
+ } else {
+ CHECK_STATUS(rfic->set_gain_stage(dev, ch, "full", val));
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Gain stage */
+/* These functions handle scaling */
+/******************************************************************************/
+
+static int _rfic_host_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct bladerf_range const *range = NULL;
+ uint8_t const rfic_ch = ch >> 1;
+ int val;
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (strcmp(stage, "dsa") == 0) {
+ uint32_t atten;
+
+ CHECK_AD936X(ad9361_get_tx_attenuation(phy, rfic_ch, &atten));
+
+ val = -atten;
+ } else {
+ log_error("%s: gain stage '%s' invalid\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+ } else {
+ struct rf_rx_gain rx_gain;
+ CHECK_AD936X(ad9361_get_rx_gain(phy, rfic_ch + 1, &rx_gain));
+
+ if (strcmp(stage, "full") == 0) {
+ val = rx_gain.gain_db;
+ } else if (strcmp(stage, "digital") == 0) {
+ val = rx_gain.digital_gain;
+ } else {
+ log_error("%s: gain stage '%s' invalid\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ *gain = __unscale_int(range, val);
+
+ return 0;
+}
+
+static int _rfic_host_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct bladerf_range const *range = NULL;
+ uint8_t const rfic_ch = ch >> 1;
+ int val;
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ /* Scale/round/clamp as required */
+ if (BLADERF_CHANNEL_IS_TX(ch) && gain < -89) {
+ val = -89750;
+ } else {
+ val = __scale_int(range, clamp_to_range(range, gain));
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (strcmp(stage, "dsa") == 0) {
+ CHECK_AD936X(ad9361_set_tx_attenuation(phy, rfic_ch, -val));
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+ } else {
+ if (strcmp(stage, "full") == 0) {
+ CHECK_AD936X(ad9361_set_rx_rf_gain(phy, rfic_ch, val));
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* RSSI */
+/******************************************************************************/
+
+static int _rfic_host_get_rssi(struct bladerf *dev,
+ bladerf_channel ch,
+ int *pre_rssi,
+ int *sym_rssi)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ uint8_t const rfic_ch = ch >> 1;
+
+ int pre, sym;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ uint32_t rssi = 0;
+
+ CHECK_AD936X(ad9361_get_tx_rssi(phy, rfic_ch, &rssi));
+
+ pre = __round_int(rssi / 1000.0);
+ sym = __round_int(rssi / 1000.0);
+ } else {
+ struct rf_rssi rssi;
+
+ CHECK_AD936X(ad9361_get_rx_rssi(phy, rfic_ch, &rssi));
+
+ pre = __round_int(rssi.preamble / (float)rssi.multiplier);
+ sym = __round_int(rssi.symbol / (float)rssi.multiplier);
+ }
+
+ if (NULL != pre_rssi) {
+ *pre_rssi = -pre;
+ }
+
+ if (NULL != sym_rssi) {
+ *sym_rssi = -sym;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Filter */
+/******************************************************************************/
+
+static int _rfic_host_get_filter(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir *rxfir,
+ bladerf_rfic_txfir *txfir)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (NULL != rxfir) {
+ *rxfir = board_data->rxfir;
+ }
+
+ if (NULL != txfir) {
+ *txfir = board_data->txfir;
+ }
+
+ return 0;
+}
+
+static int _rfic_host_set_filter(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir rxfir,
+ bladerf_rfic_txfir txfir)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ AD9361_TXFIRConfig *fir_config = NULL;
+ uint8_t enable;
+
+ if (BLADERF_RFIC_TXFIR_CUSTOM == txfir) {
+ log_warning("custom FIR not implemented, assuming default\n");
+ txfir = BLADERF_RFIC_TXFIR_DEFAULT;
+ }
+
+ switch (txfir) {
+ case BLADERF_RFIC_TXFIR_BYPASS:
+ fir_config = &bladerf2_rfic_tx_fir_config;
+ enable = 0;
+ break;
+ case BLADERF_RFIC_TXFIR_INT1:
+ fir_config = &bladerf2_rfic_tx_fir_config;
+ enable = 1;
+ break;
+ case BLADERF_RFIC_TXFIR_INT2:
+ fir_config = &bladerf2_rfic_tx_fir_config_int2;
+ enable = 1;
+ break;
+ case BLADERF_RFIC_TXFIR_INT4:
+ fir_config = &bladerf2_rfic_tx_fir_config_int4;
+ enable = 1;
+ break;
+ default:
+ MUTEX_UNLOCK(&dev->lock);
+ assert(!"Bug: unhandled txfir selection");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ CHECK_AD936X(ad9361_set_tx_fir_config(phy, *fir_config));
+ CHECK_AD936X(ad9361_set_tx_fir_en_dis(phy, enable));
+
+ board_data->txfir = txfir;
+ } else {
+ AD9361_RXFIRConfig *fir_config = NULL;
+ uint8_t enable;
+
+ if (BLADERF_RFIC_RXFIR_CUSTOM == rxfir) {
+ log_warning("custom FIR not implemented, assuming default\n");
+ rxfir = BLADERF_RFIC_RXFIR_DEFAULT;
+ }
+
+ switch (rxfir) {
+ case BLADERF_RFIC_RXFIR_BYPASS:
+ fir_config = &bladerf2_rfic_rx_fir_config;
+ enable = 0;
+ break;
+ case BLADERF_RFIC_RXFIR_DEC1:
+ fir_config = &bladerf2_rfic_rx_fir_config;
+ enable = 1;
+ break;
+ case BLADERF_RFIC_RXFIR_DEC2:
+ fir_config = &bladerf2_rfic_rx_fir_config_dec2;
+ enable = 1;
+ break;
+ case BLADERF_RFIC_RXFIR_DEC4:
+ fir_config = &bladerf2_rfic_rx_fir_config_dec4;
+ enable = 1;
+ break;
+ default:
+ MUTEX_UNLOCK(&dev->lock);
+ assert(!"Bug: unhandled rxfir selection");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ CHECK_AD936X(ad9361_set_rx_fir_config(phy, *fir_config));
+ CHECK_AD936X(ad9361_set_rx_fir_en_dis(phy, enable));
+
+ board_data->rxfir = rxfir;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* TX Mute */
+/******************************************************************************/
+
+static int _rfic_host_get_txmute(struct bladerf *dev,
+ bladerf_channel ch,
+ bool *state)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ return txmute_get(phy, ch, state);
+ }
+
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int _rfic_host_set_txmute(struct bladerf *dev,
+ bladerf_channel ch,
+ bool state)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ return txmute_set(phy, ch, state);
+ }
+
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+
+/******************************************************************************/
+/* Fastlock */
+/******************************************************************************/
+
+static int _rfic_host_store_fastlock_profile(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_tx_fastlock_store(phy, profile));
+ } else {
+ CHECK_AD936X(ad9361_rx_fastlock_store(phy, profile));
+ }
+
+ return 0;
+}
+
+static int _rfic_host_save_fastlock_profile(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile,
+ uint8_t *values)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_tx_fastlock_save(phy, profile, values));
+ } else {
+ CHECK_AD936X(ad9361_rx_fastlock_save(phy, profile, values));
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Function pointers */
+/******************************************************************************/
+
+struct controller_fns const rfic_host_control = {
+ FIELD_INIT(.is_present, _rfic_host_is_present),
+ FIELD_INIT(.is_standby, _rfic_host_is_standby),
+ FIELD_INIT(.is_initialized, _rfic_host_is_initialized),
+ FIELD_INIT(.get_init_state, _rfic_host_get_init_state),
+
+ FIELD_INIT(.initialize, _rfic_host_initialize),
+ FIELD_INIT(.standby, _rfic_host_standby),
+ FIELD_INIT(.deinitialize, _rfic_host_deinitialize),
+
+ FIELD_INIT(.enable_module, _rfic_host_enable_module),
+
+ FIELD_INIT(.get_sample_rate, _rfic_host_get_sample_rate),
+ FIELD_INIT(.set_sample_rate, _rfic_host_set_sample_rate),
+
+ FIELD_INIT(.get_frequency, _rfic_host_get_frequency),
+ FIELD_INIT(.set_frequency, _rfic_host_set_frequency),
+ FIELD_INIT(.select_band, _rfic_host_select_band),
+
+ FIELD_INIT(.get_bandwidth, _rfic_host_get_bandwidth),
+ FIELD_INIT(.set_bandwidth, _rfic_host_set_bandwidth),
+
+ FIELD_INIT(.get_gain_mode, _rfic_host_get_gain_mode),
+ FIELD_INIT(.set_gain_mode, _rfic_host_set_gain_mode),
+
+ FIELD_INIT(.get_gain, _rfic_host_get_gain),
+ FIELD_INIT(.set_gain, _rfic_host_set_gain),
+
+ FIELD_INIT(.get_gain_stage, _rfic_host_get_gain_stage),
+ FIELD_INIT(.set_gain_stage, _rfic_host_set_gain_stage),
+
+ FIELD_INIT(.get_rssi, _rfic_host_get_rssi),
+
+ FIELD_INIT(.get_filter, _rfic_host_get_filter),
+ FIELD_INIT(.set_filter, _rfic_host_set_filter),
+
+ FIELD_INIT(.get_txmute, _rfic_host_get_txmute),
+ FIELD_INIT(.set_txmute, _rfic_host_set_txmute),
+
+ FIELD_INIT(.store_fastlock_profile, _rfic_host_store_fastlock_profile),
+ FIELD_INIT(.save_fastlock_profile, _rfic_host_save_fastlock_profile),
+
+ FIELD_INIT(.command_mode, RFIC_COMMAND_HOST),
+};
diff --git a/Radio/HW/BladeRF/src/board/board.c b/Radio/HW/BladeRF/src/board/board.c
new file mode 100644
index 0000000..9b49242
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/board.c
@@ -0,0 +1,13 @@
+#include "host_config.h"
+
+#include "board.h"
+
+extern const struct board_fns bladerf1_board_fns;
+extern const struct board_fns bladerf2_board_fns;
+
+const struct board_fns *bladerf_boards[] = {
+ &bladerf1_board_fns,
+ &bladerf2_board_fns,
+};
+
+const unsigned int bladerf_boards_len = ARRAY_SIZE(bladerf_boards);
diff --git a/Radio/HW/BladeRF/src/board/board.h b/Radio/HW/BladeRF/src/board/board.h
new file mode 100644
index 0000000..279a0c1
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/board.h
@@ -0,0 +1,495 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef BOARD_BOARD_H_
+#define BOARD_BOARD_H_
+
+#include <stdint.h>
+
+#include <libbladeRF.h>
+
+#include "host_config.h"
+#include "thread.h"
+
+#include "backend/backend.h"
+
+/* Device capabilities are stored in a 64-bit mask.
+ *
+ * FPGA-oriented capabilities start at bit 0.
+ * FX3-oriented capabilities start at bit 24.
+ * Other/mixed capabilities start at bit 48.
+ */
+
+/**
+ * Prior to FPGA 0.0.4, the DAC register were located at a different address
+ */
+#define BLADERF_CAP_UPDATED_DAC_ADDR (1 << 0)
+
+/**
+ * FPGA version 0.0.5 introduced XB-200 support
+ */
+#define BLADERF_CAP_XB200 (1 << 1)
+
+/**
+ * FPGA version 0.1.0 introduced timestamp support
+ */
+#define BLADERF_CAP_TIMESTAMPS (1 << 2)
+
+/**
+ * FPGA v0.2.0 introduced scheduled retune support on the bladeRF 1, and FPGA
+ * v0.10.0 introduced it on the bladeRF 2.
+ */
+#define BLADERF_CAP_SCHEDULED_RETUNE (1 << 3)
+
+/**
+ * FPGA version 0.3.0 introduced new packet handler formats that pack
+ * operations into a single requests.
+ */
+#define BLADERF_CAP_PKT_HANDLER_FMT (1 << 4)
+
+/**
+ * A bug fix in FPGA version 0.3.2 allowed support for reading back
+ * the current VCTCXO trim dac value.
+ */
+#define BLADERF_CAP_VCTCXO_TRIMDAC_READ (1 << 5)
+
+/**
+ * FPGA v0.4.0 introduced the ability to write LMS6002D TX/RX PLL
+ * NINT & NFRAC regiters atomically.
+ */
+#define BLADERF_CAP_ATOMIC_NINT_NFRAC_WRITE (1 << 6)
+
+/**
+ * FPGA v0.4.1 fixed an issue with masked writes to the expansion GPIO
+ * and expansion GPIO direction registers.
+ *
+ * To help users avoid getting bitten by this bug, we'll mark this
+ * as a capability and disallow masked writes unless an FPGA with the
+ * fix is being used.
+ */
+#define BLADERF_CAP_MASKED_XBIO_WRITE (1 << 7)
+
+/**
+ * FPGA v0.5.0 introduces the ability to tame the VCTCXO via a 1 pps or 10 MHz
+ * input source on the mini expansion header.
+ */
+#define BLADERF_CAP_VCTCXO_TAMING_MODE (1 << 8)
+
+/**
+ * FPGA v0.6.0 introduced trx synchronization trigger via J71-4
+ */
+#define BLADERF_CAP_TRX_SYNC_TRIG (1 << 9)
+
+/**
+ * FPGA v0.7.0 introduced AGC DC correction Look-Up-Table
+ */
+#define BLADERF_CAP_AGC_DC_LUT (1 << 10)
+
+/**
+ * FPGA v0.2.0 introduced NIOS-based tuning support on the bladeRF 1, and FPGA
+ * v0.10.1 introduced it on the bladeRF 2.
+ */
+#define BLADERF_CAP_FPGA_TUNING (1 << 11)
+
+/**
+ * FPGA v0.12.0 introduces the packet meta format, allowing for variable length
+ * packets, with core target IDs.
+ */
+#define BLADERF_CAP_FPGA_PACKET_META (1 << 12)
+
+/**
+ * Firmware 1.7.1 introduced firmware-based loopback
+ */
+#define BLADERF_CAP_FW_LOOPBACK (((uint64_t)1) << 32)
+
+/**
+ * FX3 firmware version 1.8.0 introduced the ability to query when the
+ * device has become ready for use by the host. This was done to avoid
+ * opening and attempting to use the device while flash-based FPGA autoloading
+ * was occurring.
+ */
+#define BLADERF_CAP_QUERY_DEVICE_READY (((uint64_t)1) << 33)
+
+/**
+ * FX3 firmware v1.9.0 introduced a vendor request by which firmware log
+ * events could be retrieved.
+ */
+#define BLADERF_CAP_READ_FW_LOG_ENTRY (((uint64_t)1) << 34)
+
+/**
+ * FX3 firmware v2.1.0 introduced support for bladeRF 2
+ */
+#define BLADERF_CAP_FW_SUPPORTS_BLADERF2 (((uint64_t)1) << 35)
+
+/**
+ * FX3 firmware v2.3.0 introduced support for reporting the SPI Flash
+ * manufacturer ID and device ID.
+ */
+#define BLADERF_CAP_FW_FLASH_ID (((uint64_t)1) << 36)
+
+/**
+ * FX3 firmware v2.3.1 introduced support for querying the source of the
+ * currently-configured FPGA (e.g. flash autoload, host, etc)
+ */
+#define BLADERF_CAP_FW_FPGA_SOURCE (((uint64_t)1) << 37)
+
+/**
+ * FX3 firmware v2.4.0 introduced support for short packets.
+ */
+#define BLADERF_CAP_FW_SHORT_PACKET (((uint64_t)1) << 38)
+
+/**
+ * FPGA v0.15.0 introduces support for 8bit mode.
+ */
+#define BLADERF_CAP_FPGA_8BIT_SAMPLES (((uint64_t)1) << 39)
+
+/**
+ * Max number of gain calibration tables associated to max number of channels
+ */
+#define NUM_GAIN_CAL_TBLS 4
+
+struct bladerf {
+ /* Handle lock - to ensure atomic access to control and configuration
+ * operations */
+ MUTEX lock;
+
+ /* Identifying information */
+ struct bladerf_devinfo ident;
+
+ /* Backend-specific implementations */
+ const struct backend_fns *backend;
+
+ /* Backend's private data */
+ void *backend_data;
+
+ /* Board-specific implementations */
+ const struct board_fns *board;
+
+ /* Flash architecture */
+ struct bladerf_flash_arch *flash_arch;
+
+ /* Board's private data */
+ void *board_data;
+
+ /* XB attached */
+ bladerf_xb xb;
+
+ /* XB's private data */
+ void *xb_data;
+
+ /* Enabled feature */
+ bladerf_feature feature;
+
+ /* Calibration */
+ struct bladerf_gain_cal_tbl gain_tbls[NUM_GAIN_CAL_TBLS];
+};
+
+struct board_fns {
+ /* Board is compatible with backend */
+ bool (*matches)(struct bladerf *dev);
+
+ /* Open/close */
+ int (*open)(struct bladerf *dev, struct bladerf_devinfo *devinfo);
+ void (*close)(struct bladerf *dev);
+
+ /* Properties */
+ bladerf_dev_speed (*device_speed)(struct bladerf *dev);
+ int (*get_serial)(struct bladerf *dev, char *serial);
+ int (*get_fpga_size)(struct bladerf *dev, bladerf_fpga_size *size);
+ int (*get_fpga_bytes)(struct bladerf *dev, size_t *size);
+ int (*get_flash_size)(struct bladerf *dev, uint32_t *size, bool *is_guess);
+ int (*is_fpga_configured)(struct bladerf *dev);
+ int (*get_fpga_source)(struct bladerf *dev, bladerf_fpga_source *source);
+ uint64_t (*get_capabilities)(struct bladerf *dev);
+ size_t (*get_channel_count)(struct bladerf *dev, bladerf_direction dir);
+
+ /* Versions */
+ int (*get_fpga_version)(struct bladerf *dev,
+ struct bladerf_version *version);
+ int (*get_fw_version)(struct bladerf *dev, struct bladerf_version *version);
+
+ /* Gain */
+ int (*set_gain)(struct bladerf *dev, bladerf_channel ch, bladerf_gain gain);
+ int (*get_gain)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain *gain);
+ int (*set_gain_mode)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode);
+ int (*get_gain_mode)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode);
+ int (*get_gain_modes)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_gain_modes **modes);
+ int (*get_gain_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+ int (*set_gain_stage)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain gain);
+ int (*get_gain_stage)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain *gain);
+ int (*get_gain_stage_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ const struct bladerf_range **range);
+ int (*get_gain_stages)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **stages,
+ size_t count);
+
+ /* Sample Rate */
+ int (*set_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate,
+ bladerf_sample_rate *actual);
+ int (*set_rational_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual);
+ int (*get_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate);
+ int (*get_sample_rate_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+ int (*get_rational_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate);
+
+ /* Bandwidth */
+ int (*set_bandwidth)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual);
+ int (*get_bandwidth)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth);
+ int (*get_bandwidth_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+
+ /* Frequency */
+ int (*get_frequency)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency);
+ int (*set_frequency)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+ int (*get_frequency_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+ int (*select_band)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+
+ /* RF Ports */
+ int (*set_rf_port)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port);
+ int (*get_rf_port)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **port);
+ int (*get_rf_ports)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **ports,
+ unsigned int count);
+
+ /* Scheduled Tuning */
+ int (*get_quick_tune)(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune);
+ int (*schedule_retune)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune);
+ int (*cancel_scheduled_retunes)(struct bladerf *dev, bladerf_channel ch);
+
+ /* DC/Phase/Gain Correction */
+ int (*get_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ int16_t *value);
+ int (*set_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ int16_t value);
+
+ /* Trigger */
+ int (*trigger_init)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger);
+ int (*trigger_arm)(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool arm,
+ uint64_t resv1,
+ uint64_t resv2);
+ int (*trigger_fire)(struct bladerf *dev,
+ const struct bladerf_trigger *trigger);
+ int (*trigger_state)(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested,
+ uint64_t *resv1,
+ uint64_t *resv2);
+
+ /* Streaming */
+ int (*enable_module)(struct bladerf *dev, bladerf_channel ch, bool enable);
+ int (*init_stream)(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *user_data);
+ int (*stream)(struct bladerf_stream *stream, bladerf_channel_layout layout);
+ int (*submit_stream_buffer)(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms,
+ bool nonblock);
+ void (*deinit_stream)(struct bladerf_stream *stream);
+ int (*set_stream_timeout)(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int timeout);
+ int (*get_stream_timeout)(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int *timeout);
+ int (*sync_config)(struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ unsigned int buffer_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout);
+ int (*sync_tx)(struct bladerf *dev,
+ const void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+ int (*sync_rx)(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+ int (*get_timestamp)(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *timestamp);
+
+ /* FPGA/Firmware Loading/Flashing */
+ int (*load_fpga)(struct bladerf *dev, const uint8_t *buf, size_t length);
+ int (*flash_fpga)(struct bladerf *dev, const uint8_t *buf, size_t length);
+ int (*erase_stored_fpga)(struct bladerf *dev);
+ int (*flash_firmware)(struct bladerf *dev,
+ const uint8_t *buf,
+ size_t length);
+ int (*device_reset)(struct bladerf *dev);
+
+ /* Tuning mode */
+ int (*set_tuning_mode)(struct bladerf *dev, bladerf_tuning_mode mode);
+ int (*get_tuning_mode)(struct bladerf *dev, bladerf_tuning_mode *mode);
+
+ /* Loopback */
+ int (*get_loopback_modes)(struct bladerf *dev,
+ const struct bladerf_loopback_modes **modes);
+ int (*set_loopback)(struct bladerf *dev, bladerf_loopback l);
+ int (*get_loopback)(struct bladerf *dev, bladerf_loopback *l);
+
+ /* Sample RX FPGA Mux */
+ int (*get_rx_mux)(struct bladerf *dev, bladerf_rx_mux *mode);
+ int (*set_rx_mux)(struct bladerf *dev, bladerf_rx_mux mode);
+
+ /* Low-level VCTCXO Tamer Mode */
+ 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);
+
+ /* Low-level VCTCXO Trim DAC access */
+ int (*get_vctcxo_trim)(struct bladerf *dev, uint16_t *trim);
+ int (*trim_dac_read)(struct bladerf *dev, uint16_t *trim);
+ int (*trim_dac_write)(struct bladerf *dev, uint16_t trim);
+
+ /* Low-level Trigger control access */
+ 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);
+
+ /* Low-level Wishbone Master access */
+ int (*wishbone_master_read)(struct bladerf *dev, uint32_t addr, uint32_t *data);
+ int (*wishbone_master_write)(struct bladerf *dev, uint32_t addr, uint32_t data);
+
+ /* Low-level Configuration GPIO access */
+ int (*config_gpio_read)(struct bladerf *dev, uint32_t *val);
+ int (*config_gpio_write)(struct bladerf *dev, uint32_t val);
+
+ /* Low-level SPI flash access */
+ int (*erase_flash)(struct bladerf *dev,
+ uint32_t erase_block,
+ uint32_t count);
+ int (*read_flash)(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+ int (*write_flash)(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+ /* Expansion support */
+ int (*expansion_attach)(struct bladerf *dev, bladerf_xb xb);
+ int (*expansion_get_attached)(struct bladerf *dev, bladerf_xb *xb);
+
+ /* Board name */
+ const char *name;
+};
+
+/* Information about the (SPI) flash architecture */
+struct bladerf_flash_arch {
+ enum { STATUS_FLASH_UNINITIALIZED, STATUS_SUCCESS, STATUS_ASSUMED } status;
+
+ uint8_t manufacturer_id; /**< Raw manufacturer ID */
+ uint8_t device_id; /**< Raw device ID */
+ uint32_t tsize_bytes; /**< Total size of flash, in bytes */
+ uint32_t psize_bytes; /**< Flash page size, in bytes */
+ uint32_t ebsize_bytes; /**< Flash erase block size, in bytes */
+ uint32_t num_pages; /**< Size of flash, in pages */
+ uint32_t num_ebs; /**< Size of flash, in erase blocks */
+};
+
+/* Boards */
+extern const struct board_fns *bladerf_boards[];
+extern const unsigned int bladerf_boards_len;
+
+#endif
diff --git a/Radio/HW/BladeRF/src/device_calibration.c b/Radio/HW/BladeRF/src/device_calibration.c
new file mode 100644
index 0000000..4495671
--- /dev/null
+++ b/Radio/HW/BladeRF/src/device_calibration.c
@@ -0,0 +1,522 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2023 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 <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include "libbladeRF.h"
+#include "board/board.h"
+#include "helpers/version.h"
+#include "device_calibration.h"
+#include "log.h"
+#include "common.h"
+
+#define GAIN_CAL_HEADER_RX "RX Chain,RX Gain,VSG Power into bladeRF RX (dBm),Frequency of signal (Hz),Frequency of bladeRF+PXI (Hz),AD9361 RSSI register value,Power of Signal from Full Scale (dBFS)\0"
+#define GAIN_CAL_HEADER_TX "TX Chain,TX Gain,Frequency of Signal (Hz),Frequency of bladeRF+PXI (Hz),VSA Measured Power (dBm)\0"
+
+#define GAIN_CAL_VERSION (struct bladerf_version) { \
+ .describe = "gain calibration table", \
+ .major = 1, \
+ .minor = 0, \
+ .patch = 0, \
+}
+
+#define __round_int(x) (x >= 0 ? (int)(x + 0.5) : (int)(x - 0.5))
+
+#define RETURN_ERROR_STATUS(_what, _status) \
+ do { \
+ log_error("%s: %s failed: %s\n", __FUNCTION__, _what, \
+ bladerf_strerror(_status)); \
+ return _status; \
+ } while (0)
+
+#define CHECK_STATUS(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ RETURN_ERROR_STATUS(#_fn, _s); \
+ } \
+ } while (0)
+
+static size_t count_csv_entries(const char *filename) {
+ char line[1024]; // Adjust buffer size as needed
+ int count = 0;
+
+ FILE *file = fopen(filename, "r");
+ if (file == NULL) {
+ perror("Error opening file");
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line), file)) {
+ count++;
+ }
+
+ fclose(file);
+
+ return count - 2; // Subtract 2 to account for the serial number and header lines
+}
+
+int gain_cal_csv_to_bin(struct bladerf *dev, const char *csv_path, const char *binary_path, bladerf_channel ch)
+{
+ int status = 0;
+ struct bladerf_image *image;
+ size_t data_size;
+ size_t entry_size;
+ size_t offset = 0;
+
+ char line[256];
+ char current_dir[1000];
+ char expected_header[1024];
+ char device_serial[BLADERF_SERIAL_LENGTH];
+ char csv_serial[BLADERF_SERIAL_LENGTH];
+
+ uint64_t frequency;
+ float power;
+ uint64_t cw_freq;
+ uint8_t chain;
+ bladerf_gain gain;
+ int32_t rssi;
+ float vsg_power;
+ uint64_t signal_freq;
+
+ FILE *csvFile = fopen(csv_path, "r");
+ FILE *binaryFile = fopen(binary_path, "wb");
+ if (!csvFile || !binaryFile) {
+ status = BLADERF_ERR_NO_FILE;
+ if (getcwd(current_dir, sizeof(current_dir)) != NULL) {
+ log_error("Error opening calibration file: %s\n", strcat(current_dir, csv_path));
+ } else {
+ log_error("Error opening calibration file\n");
+ }
+ goto error;
+ }
+
+ strncpy(device_serial, dev->ident.serial, BLADERF_SERIAL_LENGTH);
+ device_serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
+
+ if (!fgets(line, sizeof(line), csvFile)) {
+ status = BLADERF_ERR_INVAL;
+ log_error("Error reading serial number from CSV file or file is empty.\n");
+ goto error;
+ }
+
+ sscanf(line, "Serial: %s", csv_serial);
+ if (strcmp(device_serial, csv_serial) != 0) {
+ log_warning("Gain calibration file serial (%s) does not match device serial (%s)\n", csv_serial, device_serial);
+ }
+
+ size_t num_entries = count_csv_entries(csv_path);
+ if (num_entries == 0) {
+ status = BLADERF_ERR_INVAL;
+ log_error("Error reading header from CSV file or file is empty.\n");
+ goto error;
+ }
+
+ entry_size = (BLADERF_CHANNEL_IS_TX(ch))
+ ? sizeof(chain) + sizeof(gain) + sizeof(cw_freq) + sizeof(frequency) + sizeof(power)
+ : sizeof(chain) + sizeof(gain) + sizeof(vsg_power) + sizeof(signal_freq) + sizeof(frequency) + sizeof(rssi) + sizeof(power);
+
+ data_size = num_entries * entry_size + BLADERF_SERIAL_LENGTH;
+
+ image = bladerf_alloc_image(dev, BLADERF_IMAGE_TYPE_GAIN_CAL, 0xffffffff, data_size);
+ if (image == NULL) {
+ log_error("Failed to allocate image\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ if (!fgets(line, sizeof(line), csvFile)) {
+ status = BLADERF_ERR_INVAL;
+ log_error("Error reading header from CSV file or file is empty.\n");
+ goto error;
+ }
+
+ strncpy(expected_header, (BLADERF_CHANNEL_IS_TX(ch)) ? GAIN_CAL_HEADER_TX : GAIN_CAL_HEADER_RX, sizeof(expected_header));
+ if (strncmp(line, expected_header, strlen(expected_header)) != 0) {
+ status = BLADERF_ERR_INVAL;
+ log_error("CSV format does not match expected %s headers\n", (BLADERF_CHANNEL_IS_TX(ch)) ? "TX" : "RX");
+ goto error;
+ }
+
+ image->version = GAIN_CAL_VERSION;
+
+ memcpy(&image->data[offset], device_serial, BLADERF_SERIAL_LENGTH);
+ offset += BLADERF_SERIAL_LENGTH;
+
+ for (size_t i = 0; i < num_entries; i++) {
+ if (!fgets(line, sizeof(line), csvFile)) {
+ break;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ sscanf(line, "%" SCNu8 ",%" SCNi32 ",%" SCNu64 ",%" SCNu64 ",%f",
+ &chain, &gain, &cw_freq, &frequency, &power);
+
+ memcpy(&image->data[offset], &chain, sizeof(chain));
+ offset += sizeof(chain);
+ memcpy(&image->data[offset], &gain, sizeof(gain));
+ offset += sizeof(gain);
+ memcpy(&image->data[offset], &cw_freq, sizeof(cw_freq));
+ offset += sizeof(cw_freq);
+ memcpy(&image->data[offset], &frequency, sizeof(frequency));
+ offset += sizeof(frequency);
+ memcpy(&image->data[offset], &power, sizeof(power));
+ offset += sizeof(power);
+ } else {
+ sscanf(line, "%" SCNu8 ",%" SCNi32 ",%f,%" SCNu64 ",%" SCNu64 ",%" SCNi32 ",%f",
+ &chain, &gain, &vsg_power, &signal_freq, &frequency, &rssi, &power);
+
+ memcpy(&image->data[offset], &chain, sizeof(chain));
+ offset += sizeof(chain);
+ memcpy(&image->data[offset], &gain, sizeof(gain));
+ offset += sizeof(gain);
+ memcpy(&image->data[offset], &vsg_power, sizeof(vsg_power));
+ offset += sizeof(vsg_power);
+ memcpy(&image->data[offset], &signal_freq, sizeof(signal_freq));
+ offset += sizeof(signal_freq);
+ memcpy(&image->data[offset], &frequency, sizeof(frequency));
+ offset += sizeof(frequency);
+ memcpy(&image->data[offset], &rssi, sizeof(rssi));
+ offset += sizeof(rssi);
+ memcpy(&image->data[offset], &power, sizeof(power));
+ offset += sizeof(power);
+ }
+ }
+
+ log_debug("Writing image to file: %s\n", binary_path);
+ bladerf_image_write(dev, image, binary_path);
+ bladerf_free_image(image);
+
+error:
+ if (csvFile)
+ fclose(csvFile);
+ if (binaryFile)
+ fclose(binaryFile);
+ return status;
+}
+
+static int gain_cal_tbl_init(struct bladerf_gain_cal_tbl *tbl, uint32_t num_entries) {
+ if (tbl == NULL) {
+ log_error("calibration table is NULL\n");
+ return BLADERF_ERR_MEM;
+ }
+
+ tbl->version = (struct bladerf_version){0, 0, 0, NULL};
+ tbl->n_entries = num_entries;
+ tbl->start_freq = 0;
+ tbl->stop_freq = 0;
+ tbl->file_path_len = PATH_MAX;
+
+ tbl->entries = malloc(num_entries * sizeof(struct bladerf_gain_cal_entry));
+ if (tbl->entries == NULL) {
+ log_error("failed to allocate memory for calibration table entries\n");
+ return BLADERF_ERR_MEM;
+ }
+
+ tbl->file_path = malloc(tbl->file_path_len + 1);
+ if (tbl->file_path == NULL) {
+ log_error("failed to allocate memory for calibration table file path\n");
+ return BLADERF_ERR_MEM;
+ }
+
+ tbl->state = BLADERF_GAIN_CAL_LOADED;
+ return 0;
+}
+
+void gain_cal_tbl_free(struct bladerf_gain_cal_tbl *tbl) {
+ log_verbose("Freeing gain calibration table\n");
+
+ if (tbl->entries != NULL) {
+ free(tbl->entries);
+ tbl->entries = NULL;
+ }
+
+ if (tbl->file_path != NULL) {
+ free(tbl->file_path);
+ tbl->file_path = NULL;
+ }
+
+ tbl->version = (struct bladerf_version){0, 0, 0, NULL};
+ tbl->enabled = false;
+ tbl->ch = 0;
+ tbl->n_entries = 0;
+ tbl->start_freq = 0;
+ tbl->stop_freq = 0;
+ tbl->gain_target = 0;
+ tbl->file_path_len = 0;
+ tbl->state = BLADERF_GAIN_CAL_UNLOADED;
+}
+
+int load_gain_calibration(struct bladerf *dev, bladerf_channel ch, const char *binary_path) {
+ int num_channels = 4;
+ struct bladerf_gain_cal_tbl gain_tbls[num_channels];
+ bladerf_gain current_gain;
+ uint64_t frequency;
+ float power;
+ size_t entry_counter;
+ size_t offset;
+ int status = 0;
+
+ uint64_t cw_freq;
+ uint8_t chain;
+ bladerf_gain gain;
+ int32_t rssi;
+ float vsg_power;
+ bladerf_frequency signal_freq;
+
+ struct bladerf_image *image = NULL;
+ size_t entry_size;
+ size_t num_entries;
+ char device_serial[BLADERF_SERIAL_LENGTH];
+ char file_serial[BLADERF_SERIAL_LENGTH];
+
+ FILE *binaryFile = fopen(binary_path, "rb");
+ if (!binaryFile) {
+ log_error("Error opening binary file.\n");
+ status = BLADERF_ERR_NO_FILE;
+ goto error;
+ }
+
+ status = dev->board->get_gain(dev, ch, &current_gain);
+ if (status != 0) {
+ log_error("Failed to get gain: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ status = gain_cal_tbl_init(&gain_tbls[ch], (uint32_t) 10e3);
+ if (status != 0) {
+ log_error("Error initializing gain calibration table\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ entry_size = (BLADERF_CHANNEL_IS_TX(ch))
+ ? sizeof(chain) + sizeof(gain) + sizeof(cw_freq) + sizeof(frequency) + sizeof(power)
+ : sizeof(chain) + sizeof(gain) + sizeof(vsg_power) + sizeof(signal_freq) + sizeof(frequency) + sizeof(rssi) + sizeof(power);
+
+ image = bladerf_alloc_image(dev, BLADERF_IMAGE_TYPE_GAIN_CAL, 0, 0);
+ status = bladerf_image_read(image, binary_path);
+ if (status != 0) {
+ log_error("Failed to read image: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ if (version_equal(&image->version, &GAIN_CAL_VERSION) == false) {
+ log_error("Expected gain calibration table: v%i.%i.%i\n",
+ GAIN_CAL_VERSION.major, GAIN_CAL_VERSION.minor, GAIN_CAL_VERSION.patch);
+ log_error("Imported gain calibration table: v%i.%i.%i\n",
+ image->version.major, image->version.minor, image->version.patch);
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ strncpy(device_serial, dev->ident.serial, BLADERF_SERIAL_LENGTH);
+ device_serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
+ memcpy(file_serial, image->data, BLADERF_SERIAL_LENGTH);
+ file_serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
+
+ if (strcmp(device_serial, file_serial) != 0) {
+ log_warning("Calibration file serial (%s) does not match device serial (%s)\n", file_serial, device_serial);
+ }
+
+ offset = BLADERF_SERIAL_LENGTH;
+ entry_counter = 0;
+ num_entries = (image->length - BLADERF_SERIAL_LENGTH) / entry_size;
+ for (uint64_t i = 0; i < num_entries; i++) {
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ memcpy(&chain, &image->data[offset], sizeof(chain));
+ offset += sizeof(chain);
+ memcpy(&gain, &image->data[offset], sizeof(gain));
+ offset += sizeof(gain);
+ memcpy(&cw_freq, &image->data[offset], sizeof(cw_freq));
+ offset += sizeof(cw_freq);
+ memcpy(&frequency, &image->data[offset], sizeof(frequency));
+ offset += sizeof(frequency);
+ memcpy(&power, &image->data[offset], sizeof(power));
+ offset += sizeof(power);
+ } else {
+ memcpy(&chain, &image->data[offset], sizeof(chain));
+ offset += sizeof(chain);
+ memcpy(&gain, &image->data[offset], sizeof(gain));
+ offset += sizeof(gain);
+ memcpy(&vsg_power, &image->data[offset], sizeof(vsg_power));
+ offset += sizeof(vsg_power);
+ memcpy(&signal_freq, &image->data[offset], sizeof(signal_freq));
+ offset += sizeof(signal_freq);
+ memcpy(&frequency, &image->data[offset], sizeof(frequency));
+ offset += sizeof(frequency);
+ memcpy(&rssi, &image->data[offset], sizeof(rssi));
+ offset += sizeof(rssi);
+ memcpy(&power, &image->data[offset], sizeof(power));
+ offset += sizeof(power);
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch) && chain == 0 && gain == 60) {
+ gain_tbls[ch].entries[entry_counter].freq = frequency;
+ gain_tbls[ch].entries[entry_counter].gain_corr = power;
+ entry_counter++;
+ }
+
+ if (!BLADERF_CHANNEL_IS_TX(ch) && chain == 0 && gain == 0) {
+ gain_tbls[ch].entries[entry_counter].freq = frequency;
+ gain_tbls[ch].entries[entry_counter].gain_corr = power - vsg_power;
+ entry_counter++;
+ }
+ }
+
+ if (entry_counter == 0) {
+ log_error("No valid entries found: %s\n", binary_path);
+ status = BLADERF_ERR_UNEXPECTED;
+ goto error;
+ }
+
+ gain_tbls[ch].version = image->version;
+ gain_tbls[ch].start_freq = gain_tbls[ch].entries[0].freq;
+ gain_tbls[ch].stop_freq = gain_tbls[ch].entries[entry_counter-1].freq;
+ gain_tbls[ch].n_entries = entry_counter;
+ gain_tbls[ch].ch = ch;
+ gain_tbls[ch].state = BLADERF_GAIN_CAL_LOADED;
+ gain_tbls[ch].enabled = true;
+ gain_tbls[ch].gain_target = current_gain;
+ strncpy(gain_tbls[ch].file_path, binary_path, gain_tbls[ch].file_path_len);
+
+ gain_cal_tbl_free(&dev->gain_tbls[ch]);
+ dev->gain_tbls[ch] = gain_tbls[ch];
+
+error:
+ if (status != 0) {
+ log_error("binary_path: %s\n", binary_path);
+ }
+
+ if (binaryFile)
+ fclose(binaryFile);
+ if (image)
+ bladerf_free_image(image);
+ return status;
+}
+
+static void find_floor_ceil_entries_by_frequency(const struct bladerf_gain_cal_tbl *tbl, bladerf_frequency freq,
+ struct bladerf_gain_cal_entry **floor, struct bladerf_gain_cal_entry **ceil) {
+ int mid = 0;
+ *floor = NULL;
+ *ceil = NULL;
+
+ if (tbl == NULL || tbl->entries == NULL || tbl->n_entries == 0) {
+ return;
+ }
+
+ int32_t low = 0;
+ int32_t high = tbl->n_entries - 1;
+
+ /* Binary search for the entry with the closest frequency to 'freq' */
+ while (low <= high && high >= 0) {
+ mid = (low + high) / 2;
+ if (tbl->entries[mid].freq == freq) {
+ *floor = &tbl->entries[mid];
+ *ceil = &tbl->entries[mid];
+ return;
+ } else if (tbl->entries[mid].freq < freq) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+
+ /* At this point, 'low' points to the first entry greater than 'freq',
+ and 'high' points to the last entry less than 'freq'. */
+ if ((uint32_t)low < tbl->n_entries) {
+ *ceil = &tbl->entries[low];
+ }
+
+ /* If 'high' is negative, then there are no entries less than 'freq' */
+ *floor = (high >= 0) ? &tbl->entries[high] : &tbl->entries[0];
+}
+
+int get_gain_cal_entry(const struct bladerf_gain_cal_tbl *tbl, bladerf_frequency freq, struct bladerf_gain_cal_entry *result) {
+ struct bladerf_gain_cal_entry *floor_entry, *ceil_entry;
+
+ if (tbl == NULL || result == NULL) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ find_floor_ceil_entries_by_frequency(tbl, freq, &floor_entry, &ceil_entry);
+ if (!floor_entry || !ceil_entry) {
+ log_error("Could not find ceil or floor entries in the calibration table\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (floor_entry->freq == ceil_entry->freq) {
+ result->freq = freq;
+ result->gain_corr = floor_entry->gain_corr;
+ return 0;
+ }
+
+ double interpolated_gain_corr = floor_entry->gain_corr +
+ (freq - floor_entry->freq) *
+ (ceil_entry->gain_corr - floor_entry->gain_corr) /
+ (ceil_entry->freq - floor_entry->freq);
+
+ result->freq = freq;
+ result->gain_corr = interpolated_gain_corr;
+ return 0;
+}
+
+int get_gain_correction(struct bladerf *dev, bladerf_frequency freq, bladerf_channel ch, bladerf_gain *compensated_gain) {
+ int status = 0;
+ struct bladerf_gain_cal_tbl *cal_table = &dev->gain_tbls[ch];
+ struct bladerf_gain_cal_entry entry_next;
+
+ CHECK_STATUS(get_gain_cal_entry(cal_table, freq, &entry_next));
+
+ *compensated_gain = __round_int(cal_table->gain_target - entry_next.gain_corr);
+
+ log_verbose("Target gain: %i, Compen. gain: %i\n", dev->gain_tbls[ch].gain_target, *compensated_gain);
+ return status;
+}
+
+int apply_gain_correction(struct bladerf *dev, bladerf_channel ch, bladerf_frequency frequency) {
+ struct bladerf_range const *gain_range = NULL;
+ bladerf_frequency current_frequency;
+ bladerf_gain gain_compensated;
+
+ if (dev->gain_tbls[ch].enabled == false) {
+ log_error("Gain compensation disabled. Can't apply gain correction.\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ CHECK_STATUS(dev->board->get_gain_range(dev, ch, &gain_range));
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &current_frequency));
+ CHECK_STATUS(get_gain_correction(dev, frequency, ch, &gain_compensated));
+
+ if (gain_compensated > gain_range->max || gain_compensated < gain_range->min) {
+ log_warning("Power compensated gain out of range [%i:%i]: %i\n",
+ gain_range->min, gain_range->max, gain_compensated);
+ gain_compensated = (gain_compensated > gain_range->max) ? gain_range->max : gain_range->min;
+ log_warning("Gain clamped to: %i\n", gain_compensated);
+ }
+
+ CHECK_STATUS(dev->board->set_gain(dev, ch, gain_compensated););
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/devinfo.c b/Radio/HW/BladeRF/src/devinfo.c
new file mode 100644
index 0000000..cce70f8
--- /dev/null
+++ b/Radio/HW/BladeRF/src/devinfo.c
@@ -0,0 +1,420 @@
+/*
+ * 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 <ctype.h>
+#include <limits.h>
+
+#include "libbladeRF.h"
+
+#include "rel_assert.h"
+
+#include "devinfo.h"
+#include "conversions.h"
+#include "log.h"
+
+/******************************************************************************/
+/* Device List Probe */
+/******************************************************************************/
+
+int probe(backend_probe_target target_device, struct bladerf_devinfo **devices)
+{
+ int ret;
+ size_t num_devices;
+ struct bladerf_devinfo *devices_local;
+ int status;
+
+ status = backend_probe(target_device, &devices_local, &num_devices);
+
+ if (status < 0) {
+ ret = status;
+ } else {
+ assert(num_devices <= INT_MAX);
+ ret = (int)num_devices;
+ *devices = devices_local;
+ }
+
+ return ret;
+}
+
+int bladerf_get_device_list(struct bladerf_devinfo **devices)
+{
+ return probe(BACKEND_PROBE_BLADERF, devices);
+}
+
+void bladerf_free_device_list(struct bladerf_devinfo *devices)
+{
+ /* Admittedly, we could just have the user call free() directly,
+ * but this creates a 1:1 pair of calls, and this gives us a spot
+ * to do any additional cleanup here, if ever needed in the future */
+ free(devices);
+}
+
+/******************************************************************************/
+/* Device Information Helpers */
+/******************************************************************************/
+
+bool bladerf_instance_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b)
+{
+ return a->instance == DEVINFO_INST_ANY ||
+ b->instance == DEVINFO_INST_ANY ||
+ a->instance == b->instance;
+}
+
+bool bladerf_serial_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b)
+{
+ /* User specified a "Any serial number" so just report a match */
+ const bool wildcard_match = !strcmp(a->serial, DEVINFO_SERIAL_ANY) ||
+ !strcmp(b->serial, DEVINFO_SERIAL_ANY);
+
+ if (wildcard_match) {
+ return true;
+ } else {
+ /* The user-supplied serial number matches the a subset of the
+ * entire serial number, starting at the beginning.
+ *
+ * i.e., "abc01234" can be used to match "abc0123456789def..."
+ */
+ bool subset_match = (strstr(a->serial, b->serial) == a->serial) ||
+ (strstr(b->serial, a->serial) == b->serial);
+
+ return subset_match;
+ }
+}
+
+bool bladerf_bus_addr_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b)
+{
+ bool bus_match, addr_match;
+
+ bus_match = a->usb_bus == DEVINFO_BUS_ANY ||
+ b->usb_bus == DEVINFO_BUS_ANY ||
+ a->usb_bus == b->usb_bus;
+
+ addr_match = a->usb_addr == DEVINFO_BUS_ANY ||
+ b->usb_addr == DEVINFO_BUS_ANY ||
+ a->usb_addr == b->usb_addr;
+
+ return bus_match && addr_match;
+}
+
+int bladerf_devinfo_list_init(struct bladerf_devinfo_list *list)
+{
+ int status = 0;
+
+ list->num_elt = 0;
+ list->backing_size = 5;
+
+ list->elt = malloc(list->backing_size * sizeof(struct bladerf_devinfo));
+
+ if (!list->elt) {
+ status = BLADERF_ERR_MEM;
+ }
+
+ return status;
+}
+
+int bladerf_devinfo_list_add(struct bladerf_devinfo_list *list,
+ struct bladerf_devinfo *info)
+{
+ int status = 0;
+ struct bladerf_devinfo *info_tmp;
+
+ if (list->num_elt >= list->backing_size) {
+ info_tmp = realloc(list->elt, list->backing_size * 2 * sizeof(*list->elt));
+ if (!info_tmp) {
+ status = BLADERF_ERR_MEM;
+ } else {
+ list->elt = info_tmp;
+ list->backing_size = list->backing_size * 2;
+ }
+ }
+
+ if (status == 0) {
+ memcpy(&list->elt[list->num_elt], info, sizeof(*info));
+ list->num_elt++;
+ }
+
+ return status;
+}
+
+void bladerf_init_devinfo(struct bladerf_devinfo *info)
+{
+ info->backend = BLADERF_BACKEND_ANY;
+
+ memset(info->serial, 0, BLADERF_SERIAL_LENGTH);
+ strncpy(info->serial, DEVINFO_SERIAL_ANY, BLADERF_SERIAL_LENGTH - 1);
+
+ info->usb_bus = DEVINFO_BUS_ANY;
+ info->usb_addr = DEVINFO_ADDR_ANY;
+ info->instance = DEVINFO_INST_ANY;
+
+ memset(info->manufacturer, 0, BLADERF_DESCRIPTION_LENGTH);
+ strncpy(info->manufacturer, "<unknown>", BLADERF_DESCRIPTION_LENGTH - 1);
+
+ memset(info->product, 0, BLADERF_DESCRIPTION_LENGTH);
+ strncpy(info->product, "<unknown>", BLADERF_DESCRIPTION_LENGTH - 1);
+}
+
+bool bladerf_devinfo_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b)
+{
+ return bladerf_instance_matches(a, b) &&
+ bladerf_serial_matches(a, b) &&
+ bladerf_bus_addr_matches(a ,b);
+}
+
+bool bladerf_devstr_matches(const char *dev_str,
+ struct bladerf_devinfo *info)
+{
+ int status;
+ bool ret;
+ struct bladerf_devinfo from_str;
+
+ status = str2devinfo(dev_str, &from_str);
+ if (status < 0) {
+ ret = false;
+ log_debug("Failed to parse device string: %s\n",
+ bladerf_strerror(status));
+ } else {
+ ret = bladerf_devinfo_matches(&from_str, info);
+ }
+
+ return ret;
+}
+
+int bladerf_get_devinfo_from_str(const char *devstr,
+ struct bladerf_devinfo *info)
+{
+ return str2devinfo(devstr, info);
+}
+
+/******************************************************************************/
+/* str2devinfo */
+/******************************************************************************/
+
+#define DELIM_SPACE " \t\r\n\v\f"
+
+static int handle_backend(char *str, struct bladerf_devinfo *d)
+{
+ char *str_end;
+
+ if (!str || strlen(str) == 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Gobble up any leading whitespace */
+ while (*str && isspace((unsigned char) *str)) {
+ str++;
+ };
+
+ /* Likewise for trailing whitespace */
+ str_end = str + strlen(str) - 1;
+ while (str_end > str && isspace((unsigned char) *str_end)) { str_end--; };
+ str_end[1] = '\0';
+
+ return str2backend(str, &d->backend);
+}
+
+static int handle_device(struct bladerf_devinfo *d, char *value)
+{
+ int status = BLADERF_ERR_INVAL;
+ bool bus_ok, addr_ok;
+ char *bus = value;
+ char *addr = strchr(value, ':');
+
+ if (addr && addr[1] != '\0') {
+ /* Null-terminate bus and set addr to start of addr text */
+ *addr = '\0';
+ addr++;
+
+ d->usb_bus = str2uint(bus, 0, DEVINFO_BUS_ANY - 1, &bus_ok);
+ d->usb_addr = str2uint(addr, 0, DEVINFO_ADDR_ANY - 1, &addr_ok);
+
+ if (bus_ok && addr_ok) {
+ status = 0;
+ log_debug("Device: %d:%d\n", d->usb_bus, d->usb_addr);
+ } else {
+ log_debug("Bad bus (%s) or address (%s)\n", bus, addr);
+ }
+ }
+
+ return status;
+}
+
+static int handle_instance(struct bladerf_devinfo *d, char *value)
+{
+ bool ok;
+
+ if (value == NULL) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ d->instance = str2uint(value, 0, DEVINFO_INST_ANY - 1, &ok);
+ if (!ok) {
+ log_debug("Bad instance: %s\n", value);
+ return BLADERF_ERR_INVAL;
+ } else {
+ log_debug("Instance: %u\n", d->instance);
+ return 0;
+ }
+}
+
+static int handle_serial(struct bladerf_devinfo *d, char *value)
+{
+ char c;
+ size_t i;
+ size_t len;
+
+ if (value == NULL) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ len = strlen(value);
+ if (len > (BLADERF_SERIAL_LENGTH - 1)) {
+ log_debug("Provided serial # string too long: %"PRIu64"\n",
+ (uint64_t) len);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ for (i = 0; i < len; i++) {
+ c = value[i];
+ if (c >= 'A' && c <='F') {
+ value[i] = tolower((unsigned char) c);
+ }
+
+ if ((c < 'a' || c > 'f') && (c < '0' || c > '9')) {
+ log_debug("Bad serial: %s\n", value);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ strncpy(d->serial, value, sizeof(d->serial));
+ d->serial[sizeof(d->serial) - 1] = '\0';
+
+ if (len == (BLADERF_SERIAL_LENGTH - 1)) {
+ log_verbose("Requested serial number: %s\n", d->serial);
+ } else {
+ log_verbose("Requested serial number subset: %s\n", d->serial);
+ }
+ return 0;
+}
+
+/* Returns: 1 on arg and value populated
+ * 0 on no args left
+ * BLADERF_ERR_INVAL on bad format
+ */
+
+static int next_arg(char **saveptr, char **arg, char **value)
+{
+ char *saveptr_local;
+ char *token = strtok_r(NULL, DELIM_SPACE, saveptr);
+
+ /* No arguments left */
+ if (!token) {
+ return 0;
+ }
+
+ /* Argument name */
+ *arg = strtok_r(token, "=", &saveptr_local);
+
+ if (!*arg) {
+ *value = NULL;
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Argument value - gobble up the rest of the line*/
+ *value = strtok_r(NULL, "", &saveptr_local);
+
+ if (!*value) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 1;
+}
+
+int str2devinfo(const char *dev_id_const, struct bladerf_devinfo *d)
+{
+ char *dev_id = NULL;
+ char *token = NULL;
+ char *arg = NULL;
+ char *val = NULL;
+ char *saveptr = NULL;
+ int status = BLADERF_ERR_UNEXPECTED;
+ int arg_status = BLADERF_ERR_UNEXPECTED;
+
+ assert(d);
+
+ /* Prep our device info before we begin manpulating it, defaulting to
+ * a "wildcard" device indentification */
+ bladerf_init_devinfo(d);
+
+ /* No device indentifier -- pick anything we can find */
+ if (dev_id_const == NULL || strlen(dev_id_const) == 0) {
+ return 0;
+ }
+
+ /* Copy the string so we can butcher it a bit while parsing */
+ dev_id = strdup(dev_id_const);
+ if (!dev_id) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Extract backend */
+ token = strtok_r(dev_id, ":", &saveptr);
+
+ /* We require a valid backend -- args only is not supported */
+ if (token) {
+ status = handle_backend(token, d);
+
+ /* Loop over remainder of string, gathering up args */
+ arg_status = 1;
+ while (arg_status == 1 && status == 0) {
+ arg_status = next_arg(&saveptr, &arg, &val);
+ if (arg_status == 1) {
+
+ /* Handle argument if we can */
+ if (!strcasecmp("device", arg)) {
+ status = handle_device(d, val);
+ } else if (!strcasecmp("instance", arg)) {
+ status = handle_instance(d, val);
+ } else if (!strcasecmp("serial", arg)) {
+ status = handle_serial(d, val);
+ } else {
+ arg_status = BLADERF_ERR_INVAL;
+ }
+ }
+ };
+
+ if (arg_status < 0) {
+ status = arg_status;
+ }
+
+ } else {
+ status = BLADERF_ERR_INVAL;
+ }
+
+ free(dev_id);
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/devinfo.h b/Radio/HW/BladeRF/src/devinfo.h
new file mode 100644
index 0000000..db1c81b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/devinfo.h
@@ -0,0 +1,131 @@
+/**
+ * @file devinfo.h
+ *
+ * @brief Routines for parsing and handling device identifier
+ *
+ * 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 DEVINFO_H_
+#define DEVINFO_H_
+
+#include <stddef.h>
+
+/* Reserved values for bladerf_devinfo fields to indicate "undefined" */
+#define DEVINFO_SERIAL_ANY "ANY"
+#define DEVINFO_BUS_ANY UINT8_MAX
+#define DEVINFO_ADDR_ANY UINT8_MAX
+#define DEVINFO_INST_ANY UINT_MAX
+
+struct bladerf_devinfo_list {
+ struct bladerf_devinfo *elt;
+ size_t num_elt; /* Number of elements in the list */
+ size_t backing_size; /* Size of backing array */
+};
+
+#include "backend/backend.h"
+
+int probe(backend_probe_target target_device, struct bladerf_devinfo **devices);
+
+int bladerf_get_device_list(struct bladerf_devinfo **devices);
+
+void bladerf_free_device_list(struct bladerf_devinfo *devices);
+
+/**
+ * Do the device instances for the two provided device info structures match
+ * (taking wildcards into account)?
+ *
+ * @param[in] a Device information to compare
+ * @param[in] b Device information to compare
+ *
+ * @return true on match, false otherwise
+ */
+bool bladerf_instance_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b);
+
+/**
+ * Do the serials for the two provided device info structures match
+ * (taking wildcards into account)?
+ *
+ * @param[in] a Device information to compare
+ * @param[in] b Device information to compare
+ *
+ * @return true on match, false otherwise
+ */
+bool bladerf_serial_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b);
+
+/**
+ * Do the bus and addr for the two provided device info structures match
+ * (taking wildcards into account)?
+ *
+ * @param[in] a Device information to compare
+ * @param[in] b Device information to compare
+ *
+ * @return true on match, false otherwise
+ */
+bool bladerf_bus_addr_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b);
+
+/**
+ * Create list of devinfos
+ *
+ * @param[in] list List of devinfos
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int bladerf_devinfo_list_init(struct bladerf_devinfo_list *list);
+
+/**
+ * Get a pointer to the parent devinfo_list container of a devinfo
+ *
+ * @param[in] devinfo Device info
+ *
+ * @return pointer to container on success, NULL on error
+ */
+struct bladerf_devinfo_list *bladerf_get_devinfo_list(
+ struct bladerf_devinfo *devinfo);
+
+/**
+ * Add an item to our internal devinfo list
+ *
+ * @param[inout] list List to append to
+ * @param[in] info Info to copy into the list
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int bladerf_devinfo_list_add(struct bladerf_devinfo_list *list,
+ struct bladerf_devinfo *info);
+
+/**
+ * Fill out a device info structure based upon the provided device indentifer
+ * string. If a failure occurrs, the contents of d are undefined.
+ *
+ * For device identifier format, see the documentation for bladerf_open
+ * (in include/libbladeRF.h)
+ *
+ * @param[in] device_identifier Device identifier string
+ * @param[out] d Device info to fill in
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int str2devinfo(const char *device_identifier, struct bladerf_devinfo *d);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/dac161s055.c b/Radio/HW/BladeRF/src/driver/dac161s055.c
new file mode 100644
index 0000000..241b4c3
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/dac161s055.c
@@ -0,0 +1,65 @@
+/*
+ * 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 <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "host_config.h"
+#include "log.h"
+
+#include "dac161s055.h"
+
+int dac161s055_write(struct bladerf *dev, uint16_t value)
+{
+ int status;
+
+ /* Ensure device is in write-through mode */
+ status = dev->backend->vctcxo_dac_write(dev, 0x28, 0x0000);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Write DAC value to channel 0 */
+ status = dev->backend->vctcxo_dac_write(dev, 0x08, value);
+ if (status < 0) {
+ return status;
+ }
+
+ log_verbose("%s: Wrote 0x%04x\n", __FUNCTION__, value);
+
+ return 0;
+}
+
+int dac161s055_read(struct bladerf *dev, uint16_t *value)
+{
+ int status;
+
+ /* Read DAC value for channel 0 */
+ status = dev->backend->vctcxo_dac_read(dev, 0x98, value);
+ if (status < 0) {
+ *value = 0;
+ return status;
+ }
+
+ log_verbose("%s: Read 0x%04x\n", __FUNCTION__, *value);
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/driver/dac161s055.h b/Radio/HW/BladeRF/src/driver/dac161s055.h
new file mode 100644
index 0000000..009827b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/dac161s055.h
@@ -0,0 +1,51 @@
+/**
+ * @file dac161s055.h
+ *
+ * @brief DAC161S055 Support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017777777 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 DRIVER_DAC161S055_H_
+#define DRIVER_DAC161S055_H_
+
+#include "board/board.h"
+
+/**
+ * Write the output value to the DAC.
+ *
+ * @param dev Device handle
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int dac161s055_write(struct bladerf *dev, uint16_t value);
+
+/**
+ * Read the output value of the DAC.
+ *
+ * @param dev Device handle
+ * @param[out] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int dac161s055_read(struct bladerf *dev, uint16_t *value);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/fpga_trigger.c b/Radio/HW/BladeRF/src/driver/fpga_trigger.c
new file mode 100644
index 0000000..67818a0
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/fpga_trigger.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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 <libbladeRF.h>
+
+#include "log.h"
+
+#include "fpga_trigger.h"
+
+static bool is_valid_signal(bladerf_trigger_signal signal)
+{
+ switch (signal) {
+ case BLADERF_TRIGGER_J71_4:
+ case BLADERF_TRIGGER_J51_1:
+ case BLADERF_TRIGGER_MINI_EXP_1:
+
+ case BLADERF_TRIGGER_USER_0:
+ case BLADERF_TRIGGER_USER_1:
+ case BLADERF_TRIGGER_USER_2:
+ case BLADERF_TRIGGER_USER_3:
+ case BLADERF_TRIGGER_USER_4:
+ case BLADERF_TRIGGER_USER_5:
+ case BLADERF_TRIGGER_USER_6:
+ case BLADERF_TRIGGER_USER_7:
+ return true;
+
+ default:
+ log_debug("Invalid trigger signal: %d\n", signal);
+ return false;
+ }
+}
+
+int fpga_trigger_read(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal signal, uint8_t *regval)
+{
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ if (!is_valid_signal(signal))
+ return BLADERF_ERR_INVAL;
+
+ return dev->backend->read_trigger(dev, ch, signal, regval);
+}
+
+int fpga_trigger_write(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal signal, uint8_t regval)
+{
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ if (!is_valid_signal(signal))
+ return BLADERF_ERR_INVAL;
+
+ return dev->backend->write_trigger(dev, ch, signal, regval);
+}
+
+int fpga_trigger_init(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger)
+
+{
+ int status;
+ uint8_t regval;
+
+ trigger->options = 0;
+
+ status = fpga_trigger_read(dev, ch, signal, &regval);
+ if (status != 0) {
+ trigger->channel = BLADERF_CHANNEL_INVALID;
+ trigger->role = BLADERF_TRIGGER_ROLE_INVALID;
+ trigger->signal = BLADERF_TRIGGER_INVALID;
+ return status;
+ }
+
+ if ((regval & BLADERF_TRIGGER_REG_MASTER) != 0) {
+ trigger->role = BLADERF_TRIGGER_ROLE_MASTER;
+ } else {
+ trigger->role = BLADERF_TRIGGER_ROLE_SLAVE;
+ }
+
+ trigger->channel = ch;
+ trigger->signal = signal;
+
+ return 0;
+}
+
+int fpga_trigger_arm(struct bladerf *dev,
+ const struct bladerf_trigger *trigger, bool arm)
+{
+ int status;
+ uint8_t regval;
+
+ status = fpga_trigger_read(dev, trigger->channel, trigger->signal, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Reset any previous fire request */
+ regval &= ~BLADERF_TRIGGER_REG_FIRE;
+
+ if (arm) {
+ regval |= BLADERF_TRIGGER_REG_ARM;
+ } else {
+ regval &= ~BLADERF_TRIGGER_REG_ARM;
+ }
+
+ switch (trigger->role) {
+ case BLADERF_TRIGGER_ROLE_MASTER:
+ regval |= BLADERF_TRIGGER_REG_MASTER;
+ break;
+
+ case BLADERF_TRIGGER_ROLE_SLAVE:
+ regval &= ~BLADERF_TRIGGER_REG_MASTER;
+ break;
+
+ case BLADERF_TRIGGER_ROLE_DISABLED:
+ regval = 0;
+ break;
+
+ default:
+ log_debug("Invalid trigger role: %d\n", trigger->role);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = fpga_trigger_write(dev, trigger->channel, trigger->signal, regval);
+
+ return status;
+}
+
+int fpga_trigger_fire(struct bladerf *dev,
+ const struct bladerf_trigger *trigger)
+{
+ int status;
+ uint8_t regval;
+
+ status = fpga_trigger_read(dev, trigger->channel, trigger->signal, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval |= BLADERF_TRIGGER_REG_FIRE;
+ status = fpga_trigger_write(dev, trigger->channel, trigger->signal, regval);
+
+ return status;
+}
+
+int fpga_trigger_state(struct bladerf *dev, const struct bladerf_trigger *trigger,
+ bool *is_armed, bool *fired, bool *fire_requested)
+{
+ int status;
+ uint8_t regval;
+
+ status = fpga_trigger_read(dev, trigger->channel, trigger->signal, &regval);
+ if (status != 0) {
+ *fired = false;
+ return status;
+ }
+
+ if (is_armed != NULL) {
+ *is_armed = (regval & BLADERF_TRIGGER_REG_ARM) != 0;
+ }
+
+ if (fired != NULL) {
+ /* Signal is active-low */
+ *fired = (regval & BLADERF_TRIGGER_REG_LINE) == 0;
+ }
+
+ if (fire_requested != NULL) {
+ if (trigger->role == BLADERF_TRIGGER_ROLE_MASTER) {
+ *fire_requested = (regval & BLADERF_TRIGGER_REG_FIRE) != 0;
+ } else {
+ *fire_requested = false;
+ }
+ }
+
+ return status;
+}
+
diff --git a/Radio/HW/BladeRF/src/driver/fpga_trigger.h b/Radio/HW/BladeRF/src/driver/fpga_trigger.h
new file mode 100644
index 0000000..8995fed
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/fpga_trigger.h
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef DRIVER_FPGA_TRIGGER_H_
+#define DRIVER_FPGA_TRIGGER_H_
+
+#include "board/board.h"
+
+/**
+ * Read trigger control register
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal control register to read from
+ * @param[out] val Pointer to variable that register is read into See the
+ * BLADERF_TRIGGER_REG_* macros for the meaning of each
+ * bit.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_read(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val);
+
+/**
+ * Write trigger control register
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal to configure
+ * @param[in] val Data to write into the trigger control register. See
+ * the BLADERF_TRIGGER_REG_* macros for options.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_write(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val);
+
+
+/**
+ * Initialize a bladerf_trigger structure based upon the current state
+ * of a channel's trigger control register.
+ *
+ * @param dev Device to query
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal to query
+ * @param[out] trigger Updated to describe trigger
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_init(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger);
+
+/**
+ * Arm or re-arm the specified trigger.
+ *
+ * @param dev Device handle
+ * @param[in] trigger Description of trigger to arm
+ * @param[in] arm If true, the specified trigger will be armed. Setting
+ * this to false will disarm the trigger specified in
+ * `config`.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_arm(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool arm);
+
+/**
+ * Fire a trigger event.
+ *
+ * Calling this functiona with a trigger whose role is anything other than
+ * ::BLADERF_TRIGGER_REG_MASTER will yield a BLADERF_ERR_INVAL return value.
+ *
+ * @param dev Device handle
+ * @param[in] trigger Trigger to assert
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_fire(struct bladerf *dev,
+ const struct bladerf_trigger *trigger);
+
+/**
+ * Query the fire request status of a master trigger
+ *
+ * @param dev Device handle
+ * @param[in] trigger Trigger to query
+ * @param[out] is_armed Set to true if the trigger is armed, and false
+ * otherwise. May be NULL.
+ * @param[out] has_fired Set to true if the trigger has fired, and false
+ * otherwise. May be NULL.
+ * @param[out] fire_requested Only applicable to a trigger master. Set to
+ * true if a fire request has been previously
+ * submitted. May be NULL.
+ * @param[out] resv1 Reserved parameter. Set to NULL.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_state(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/fx3_fw.c b/Radio/HW/BladeRF/src/driver/fx3_fw.c
new file mode 100644
index 0000000..423322b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/fx3_fw.c
@@ -0,0 +1,343 @@
+/*
+ * This file implements functionality for reading and validating an FX3 firmware
+ * image, and providing access to the image contents.
+ *
+ * Details about the image format can be found and FX3 bootloader can be found
+ * in Cypress AN76405: EZ-USB (R) FX3 (TM) Boot Options:
+ * http://www.cypress.com/?docID=49862
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 <stdint.h>
+
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "host_config.h"
+#include "log.h"
+
+#include "fx3_fw.h"
+
+#define FX3_IMAGE_TYPE_NORMAL 0xb0 /* "Normal" image with checksum */
+
+#define FX3_HDR_SIG_IDX 0x00
+#define FX3_HDR_IMAGE_CTL_IDX 0x02
+#define FX3_HDR_IMAGE_TYPE_IDX 0x03
+#define FX3_HDR_IMAGE_LEN0_IDX 0x04
+#define FX3_HDR_IMAGE_ADDR0_IDX 0x08
+#define FX3_HDR_IMAGE_DATA0_IDX 0x0c
+
+#define FX3_HDR_LEN FX3_HDR_IMAGE_DATA0_IDX
+
+#define FX3_RAM_SIZE_WORDS (256 * 1024 / sizeof(uint32_t))
+
+struct fx3_firmware {
+ uint8_t *data;
+ uint32_t data_len;
+
+ uint32_t entry_addr;
+
+ uint32_t num_sections;
+ uint32_t curr_section;
+ uint32_t section_offset;
+};
+
+static inline uint32_t to_uint32(struct fx3_firmware *fw, uint32_t offset)
+{
+ uint32_t ret;
+
+ assert((offset + sizeof(uint32_t)) <= fw->data_len);
+
+ memcpy(&ret, &fw->data[offset], sizeof(ret));
+
+ return LE32_TO_HOST(ret);
+}
+
+static inline bool is_valid_fx3_ram_addr(uint32_t addr, uint32_t len) {
+ bool valid = true;
+
+ /* If you're doing something fun, wild, and crazy with the FX3 and your
+ * modifications of linker scripts has changed the firmware entry point,
+ * you'll need to add this compile-time definition to suppress this check.
+ *
+ * One potential improvement here would be to define the I-TCM and SYSMEM
+ * addresses at configuration/compilation-time to ensure they match
+ * what's in the FX3's linker script. The default values are assumed here.
+ */
+# ifndef BLADERF_SUPPRESS_FX3_FW_ENTRY_POINT_CHECK
+ const uint32_t itcm_base = 0x00000000;
+ const uint32_t itcm_len = 0x4000;
+ const uint32_t itcm_end = itcm_base + itcm_len;
+
+ const uint32_t sysmem_base = 0x40000000;
+ const uint32_t sysmem_len = 0x80000;
+ const uint32_t sysmem_end = sysmem_base + sysmem_len;
+
+ const bool in_itcm = (addr < itcm_end) &&
+ (len <= itcm_len) &&
+ ((addr + len) < itcm_end);
+
+ const bool in_sysmem = (addr >= sysmem_base) &&
+ (addr < sysmem_end) &&
+ (len <= sysmem_len) &&
+ ((addr + len) < sysmem_end);
+
+ /* In lieu of compilers issuing warnings over the fact that the condition
+ * (addr >= itcm_base) is always true, this condition has been removed.
+ *
+ * Instead, an assertion has been added to catch the attention of anyone
+ * making a change to the above itcm_base definition, albeit a *very*
+ * unlikely change to make. */
+ assert(itcm_base == 0); /* (addr >= itcm_base) guaranteed */
+
+ valid = in_itcm || in_sysmem;
+# endif
+
+ return valid;
+}
+
+static int scan_fw_sections(struct fx3_firmware *fw)
+{
+ int status = 0;
+ bool done = false; /* Have we read all the sections? */
+ uint32_t checksum = 0;
+
+ uint32_t offset, i; /* In bytes */
+ uint32_t next_section; /* Section offset in bytes */
+ uint32_t section_len_words; /* FW uses units of 32-bit words */
+ uint32_t section_len_bytes; /* Section length converted to bytes */
+
+ /* Byte offset where the checksum is expected to be */
+ const uint32_t checksum_off = fw->data_len - sizeof(uint32_t);
+
+ /* These assumptions should have been verified earlier */
+ assert(checksum_off > FX3_HDR_IMAGE_DATA0_IDX);
+ assert((checksum_off % 4) == 0);
+
+ offset = FX3_HDR_IMAGE_LEN0_IDX;
+
+ while (!done) {
+
+ /* Fetch the length of the current section */
+ section_len_words = to_uint32(fw, offset);
+
+ if (section_len_words > FX3_RAM_SIZE_WORDS) {
+ log_debug("Firmware section %u is unexpectedly large.\n",
+ fw->num_sections);
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ } else {
+ section_len_bytes = (uint32_t)(section_len_words * sizeof(uint32_t));
+ offset += sizeof(uint32_t);
+ }
+
+ /* The list of sections is terminated by a 0 section length field */
+ if (section_len_bytes == 0) {
+ fw->entry_addr = to_uint32(fw, offset);
+ if (!is_valid_fx3_ram_addr(fw->entry_addr, 0)) {
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ offset += sizeof(uint32_t);
+ done = true;
+ } else {
+# if LOGGING_ENABLED
+ /* Just a value to print in verbose output */
+ uint32_t section_start_offset = offset - sizeof(uint32_t);
+# endif
+
+ uint32_t addr = to_uint32(fw, offset);
+ if (!is_valid_fx3_ram_addr(addr, section_len_bytes)) {
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ offset += sizeof(uint32_t);
+ if (offset >= checksum_off) {
+ log_debug("Firmware truncated after section address.\n");
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ next_section = offset + section_len_bytes;
+
+ if (next_section >= checksum_off) {
+ log_debug("Firmware truncated in section %u\n",
+ fw->num_sections);
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ for (i = offset; i < next_section; i += sizeof(uint32_t)) {
+ checksum += to_uint32(fw, i);
+ }
+
+ offset = next_section;
+ log_verbose("Scanned section %u at offset 0x%08x: "
+ "addr=0x%08x, len=0x%08x\n",
+ fw->num_sections, section_start_offset,
+ addr, section_len_words);
+
+ fw->num_sections++;
+ }
+ }
+
+ if (offset != checksum_off) {
+ log_debug("Invalid offset or junk at the end of the firmware image.\n");
+ status = BLADERF_ERR_INVAL;
+ } else {
+ const uint32_t expected_checksum = to_uint32(fw, checksum_off);
+
+ if (checksum != expected_checksum) {
+ log_debug("Bad checksum. Expected 0x%08x, got 0x%08x\n",
+ expected_checksum, checksum);
+
+ status = BLADERF_ERR_INVAL;
+ } else {
+ log_verbose("Firmware checksum OK.\n");
+ fw->section_offset = FX3_HDR_IMAGE_LEN0_IDX;
+ }
+ }
+
+error:
+ return status;
+}
+
+int fx3_fw_parse(struct fx3_firmware **fw, uint8_t *buf, size_t buf_len)
+{
+ int status;
+
+ if (buf_len > UINT32_MAX) {
+ /* This is just intended to catch a crazy edge case, since we're casting
+ * to 32-bits below. If this passes, the data length might still be well
+ * over the 512 KiB RAM limit. */
+ log_debug("Size of provided image is too large.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (buf_len < FX3_HDR_LEN) {
+ log_debug("Provided image is too short.");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if ((buf_len % 4) != 0) {
+ log_debug("Size of provided image is not a multiple of 4 bytes.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (buf[FX3_HDR_SIG_IDX] != 'C' && buf[FX3_HDR_SIG_IDX + 1] != 'Y') {
+ log_debug("FX3 firmware does have 'CY' marker.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (buf[3] != FX3_IMAGE_TYPE_NORMAL) {
+ log_debug("FX3 firmware header contained unexpected image type: "
+ "0x%02x\n", buf[FX3_HDR_IMAGE_TYPE_IDX]);
+ return BLADERF_ERR_INVAL;
+ }
+
+ *fw = calloc(1, sizeof(struct fx3_firmware));
+ if (*fw == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ (*fw)->data = malloc(buf_len);
+ if ((*fw)->data == NULL) {
+ free(*fw);
+ return BLADERF_ERR_MEM;
+ }
+
+ memcpy((*fw)->data, buf, buf_len);
+ (*fw)->data_len = (uint32_t)buf_len;
+
+ status = scan_fw_sections(*fw);
+ if (status != 0) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ fx3_fw_free(*fw);
+ return status;
+}
+
+void fx3_fw_free(struct fx3_firmware *fw)
+{
+ free(fw->data);
+ free(fw);
+}
+
+bool fx3_fw_next_section(struct fx3_firmware *fw, uint32_t *section_addr,
+ uint8_t **section_data, uint32_t *section_len)
+{
+ uint32_t len;
+ uint32_t addr;
+ uint8_t *data;
+
+ /* Max offset is the checksum address */
+ const uint32_t max_offset = fw->data_len - sizeof(uint32_t);
+
+ assert(fw != NULL);
+ assert(fw->data != NULL);
+
+ *section_addr = 0;
+ *section_data = NULL;
+ *section_len = 0;
+
+ if (fw->curr_section >= fw->num_sections) {
+ return false;
+ }
+
+ /* Length in bytes (as converted from 32-bit words) */
+ len = to_uint32(fw, fw->section_offset) * sizeof(uint32_t);
+ if (len == 0) {
+ return false;
+ }
+
+ /* Advance to address field */
+ fw->section_offset += sizeof(uint32_t);
+ assert(fw->section_offset < max_offset);
+ addr = to_uint32(fw, fw->section_offset);
+
+ /* Advance to data field */
+ fw->section_offset += sizeof(uint32_t);
+ assert(fw->section_offset < max_offset);
+ data = &fw->data[fw->section_offset];
+
+ /* Advance to the next section for the next call */
+ fw->section_offset += len;
+ assert(fw->section_offset < max_offset);
+ fw->curr_section++;
+
+ *section_addr = addr;
+ *section_data = data;
+ *section_len = len;
+ return true;
+}
+
+uint32_t fx3_fw_entry_point(const struct fx3_firmware *fw)
+{
+ assert(fw != NULL);
+ return fw->entry_addr;
+}
diff --git a/Radio/HW/BladeRF/src/driver/fx3_fw.h b/Radio/HW/BladeRF/src/driver/fx3_fw.h
new file mode 100644
index 0000000..5e715d5
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/fx3_fw.h
@@ -0,0 +1,79 @@
+/*
+ * This file defines functionality for reading and validating an FX3 firmware
+ * image, and providing access to the image contents.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 DRIVER_FX3_FW_H_
+#define DRIVER_FX3_FW_H_
+
+#include "host_config.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "board/board.h"
+
+struct fx3_firmware;
+
+/**
+ * Parse the contents of an FX3 firmware file into a fx3_firmware structure.
+ *
+ * @param[out] fw Handle to FX3 firmware data
+ * @param[in] buf Buffer containing a FX3 firmware image
+ * @param[in] buf_len Length of buffer
+ *
+ * @return 0 on success, BLADERF_ERR_INVAL if image validation fails,
+ * BLADERF_ERR_* values on other errors.
+ */
+int fx3_fw_parse(struct fx3_firmware **fw, uint8_t *buf, size_t buf_len);
+
+/**
+ * Free the data stored in the provided fx3_firmware structure.
+ *
+ * @param[inout] fw Structure to deallocate
+ */
+void fx3_fw_free(struct fx3_firmware *fw);
+
+/**
+ * This function allows each section to be iterated over by calling it
+ * repeatedly, until it returns false.
+ *
+ * @param[in] fw Handle FX3 firmware data
+ * @param[out] section_addr Target RAM address of the section (on the FX3)
+ * @param[out] section_data Updated to point to start of next section's data
+ * @parma[out] section_len Length of the next section
+ *
+ * @return true if this function returned section data, false if the end of the
+ * FW has been reached and no data is available.
+ */
+bool fx3_fw_next_section(struct fx3_firmware *fw,
+ uint32_t *section_addr,
+ uint8_t **section_data,
+ uint32_t *section_len);
+
+/**
+ * @param[in] fw Handle FX3 firmware data
+ *
+ * @return The 32-bit little-endian address of the firmware entry point.
+ */
+uint32_t fx3_fw_entry_point(const struct fx3_firmware *fw);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/ina219.c b/Radio/HW/BladeRF/src/driver/ina219.c
new file mode 100644
index 0000000..f3cd23b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/ina219.c
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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 <libbladeRF.h>
+
+#include "log.h"
+
+#include "ina219.h"
+
+#define INA219_REG_CONFIGURATION 0x00
+#define INA219_REG_SHUNT_VOLTAGE 0x01
+#define INA219_REG_BUS_VOLTAGE 0x02
+#define INA219_REG_POWER 0x03
+#define INA219_REG_CURRENT 0x04
+#define INA219_REG_CALIBRATION 0x05
+
+int ina219_init(struct bladerf *dev, float r_shunt)
+{
+ int status;
+ uint16_t value;
+
+ /* Soft-reset INA219 */
+ value = 0x8000;
+ status = dev->backend->ina219_write(dev, INA219_REG_CONFIGURATION, value);
+ if (status < 0) {
+ log_error("INA219 soft reset error: %d\n", status);
+ return status;
+ }
+
+ /* Poll until we're out of reset */
+ while (value & 0x8000) {
+ status = dev->backend->ina219_read(dev, INA219_REG_CONFIGURATION, &value);
+ if (status < 0) {
+ log_error("INA219 soft reset poll error: %d\n", status);
+ return status;
+ }
+ }
+
+ /* Write configuration register */
+ /* BRNG (13) = 0 for 16V FSR
+ PG (12-11) = 00 for 40mV
+ BADC (10-7) = 0011 for 12-bit / 532uS
+ SADC (6-3) = 0011 for 12-bit / 532uS
+ MODE (2-0) = 111 for continuous shunt & bus */
+ value = 0x019f;
+ status = dev->backend->ina219_write(dev, INA219_REG_CONFIGURATION, value);
+ if (status < 0) {
+ log_error("INA219 configuration error: %d\n", status);
+ return status;
+ }
+
+ log_debug("Configuration register: 0x%04x\n", value);
+
+ /* Write calibration register */
+ /* Current_LSB = 0.001 A / LSB */
+ /* Calibration = 0.04096 / (Current_LSB * r_shunt) */
+ value = (uint16_t)((0.04096 / (0.001 * r_shunt)) + 0.5);
+ status = dev->backend->ina219_write(dev, INA219_REG_CALIBRATION, value);
+ if (status < 0) {
+ log_error("INA219 calibration error: %d\n", status);
+ return status;
+ }
+
+ log_debug("Calibration register: 0x%04x\n", value);
+
+ return 0;
+}
+
+int ina219_read_shunt_voltage(struct bladerf *dev, float *voltage)
+{
+ int status;
+ uint16_t data;
+
+ status = dev->backend->ina219_read(dev, INA219_REG_SHUNT_VOLTAGE, &data);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Scale by 1e-5 LSB / Volt */
+ *voltage = ((float)((int16_t)data)) * 1e-5F;
+
+ return 0;
+}
+
+int ina219_read_bus_voltage(struct bladerf *dev, float *voltage)
+{
+ int status;
+ uint16_t data;
+
+ status = dev->backend->ina219_read(dev, INA219_REG_BUS_VOLTAGE, &data);
+ if (status < 0) {
+ return status;
+ }
+
+ /* If overflow flag is set */
+ if (data & 0x1) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Scale by 0.004 LSB / Volt */
+ *voltage = ((float)(data >> 3)) * 0.004F;
+
+ return 0;
+}
+
+int ina219_read_current(struct bladerf *dev, float *current)
+{
+ int status;
+ uint16_t data;
+
+ status = dev->backend->ina219_read(dev, INA219_REG_CURRENT, &data);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Scale by 0.001 LSB / Ampere */
+ *current = ((float)((int16_t)data)) * 0.001F;
+
+ return 0;
+}
+
+int ina219_read_power(struct bladerf *dev, float *power)
+{
+ int status;
+ uint16_t data;
+
+ status = dev->backend->ina219_read(dev, INA219_REG_POWER, &data);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Scale by 0.020 LSB / Watt */
+ *power = ((float)((int16_t)data)) * 0.020F;
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/driver/ina219.h b/Radio/HW/BladeRF/src/driver/ina219.h
new file mode 100644
index 0000000..575d0da
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/ina219.h
@@ -0,0 +1,83 @@
+/**
+ * @file ina219.h
+ *
+ * @brief INA219 Support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef DRIVER_INA219_H_
+#define DRIVER_INA219_H_
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+
+/**
+ * Initialize the INA219 voltage/current/power monitor.
+ *
+ * @param dev Device handle
+ * @param[in] r_shunt Shunt resistor in ohms
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_init(struct bladerf *dev, float r_shunt);
+
+/**
+ * Read the shunt voltage.
+ *
+ * @param dev Device handle
+ * @param[out] voltage Shunt voltage in volts
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_read_shunt_voltage(struct bladerf *dev, float *voltage);
+
+/**
+ * Read the bus voltage.
+ *
+ * @param dev Device handle
+ * @param[out] voltage Bus voltage in volts
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_read_bus_voltage(struct bladerf *dev, float *voltage);
+
+/**
+ * Read the load current.
+ *
+ * @param dev Device handle
+ * @param[out] current Load current in amps
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_read_current(struct bladerf *dev, float *current);
+
+/**
+ * Read the load power.
+ *
+ * @param dev Device handle
+ * @param[out] power Load power in watts
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_read_power(struct bladerf *dev, float *power);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/si5338.c b/Radio/HW/BladeRF/src/driver/si5338.c
new file mode 100644
index 0000000..80cda11
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/si5338.c
@@ -0,0 +1,669 @@
+/*
+ * 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 <inttypes.h>
+#include <limits.h>
+
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "host_config.h"
+#include "log.h"
+
+#include "si5338.h"
+
+#define SI5338_EN_A 0x01
+#define SI5338_EN_B 0x02
+
+#define SI5338_F_VCO (38400000UL * 66UL)
+
+/**
+ * This is used set or recreate the si5338 frequency
+ * Each si5338 multisynth module can be set independently
+ */
+struct si5338_multisynth {
+ /* Multisynth to program (0-3) */
+ uint8_t index;
+
+ /* Base address of the multisynth */
+ uint16_t base;
+
+ /* Requested and actual sample rates */
+ struct bladerf_rational_rate requested;
+ struct bladerf_rational_rate actual;
+
+ /* Enables for A and/or B outputs */
+ uint8_t enable;
+
+ /* f_out = fvco / (a + b/c) / r */
+ uint32_t a, b, c, r;
+
+ /* (a, b, c) in multisynth (p1, p2, p3) form */
+ uint32_t p1, p2, p3;
+
+ /* (p1, p2, p3) in register form */
+ uint8_t regs[10];
+};
+
+static void si5338_log_read_error(int error, const char *s)
+{
+ log_debug("Could not read from si5338 (%d): %s\n", error, s);
+ return;
+}
+
+static void si5338_log_write_error(int error, const char *s)
+{
+ log_debug("Could not write to si5338 (%d): %s\n", error, s);
+ return;
+}
+
+static uint64_t si5338_gcd(uint64_t a, uint64_t b)
+{
+ uint64_t t;
+ while (b != 0) {
+ t = b;
+ b = a % t;
+ a = t;
+ }
+ return a;
+}
+
+static void si5338_rational_reduce(struct bladerf_rational_rate *r)
+{
+ int64_t val;
+
+ if ((r->den > 0) && (r->num >= r->den)) {
+ /* Get whole number */
+ uint64_t whole = r->num / r->den;
+ r->integer += whole;
+ r->num = r->num - whole*r->den;
+ }
+
+ /* Reduce fraction */
+ val = si5338_gcd(r->num, r->den);
+ if (val > 0) {
+ r->num /= val;
+ r->den /= val;
+ }
+
+ return ;
+}
+
+static void si5338_rational_double(struct bladerf_rational_rate *r)
+{
+ r->integer *= 2;
+ r->num *= 2;
+ si5338_rational_reduce(r);
+ return;
+}
+
+/**
+ * Update the base address of the selected multisynth
+ */
+static void si5338_update_base(struct si5338_multisynth *ms)
+{
+ ms->base = 53 + ms->index*11 ;
+ return;
+}
+
+/**
+ * Unpack the recently read registers into (p1, p2, p3) and (a, b, c)
+ *
+ * Precondition:
+ * regs[10] and r have been read
+ *
+ * Post-condition:
+ * (p1, p2, p3), (a, b, c) and actual are populated
+ */
+static void si5338_unpack_regs(struct si5338_multisynth *ms)
+{
+ uint64_t temp;
+
+ /* Zeroize */
+ ms->p1 = ms->p2 = ms->p3 = 0;
+
+ /* Populate */
+ ms->p1 = ((ms->regs[2]&3)<<16) | (ms->regs[1]<<8) | (ms->regs[0]);
+ ms->p2 = (ms->regs[5]<<22) | (ms->regs[4]<<14) | (ms->regs[3]<<6) | ((ms->regs[2]>>2)&0x3f);
+ ms->p3 = ((ms->regs[9]&0x3f)<<24) | (ms->regs[8]<<16) | (ms->regs[7]<<8) | (ms->regs[6]);
+
+ log_verbose("Unpacked P1: 0x%8.8x (%u) P2: 0x%8.8x (%u) P3: 0x%8.8x (%u)\n",
+ ms->p1, ms->p1, ms->p2, ms->p2, ms->p3, ms->p3);
+
+ /* c = p3 */
+ ms->c = ms->p3;
+
+ /* a = (p1+512)/128
+ *
+ * NOTE: The +64 is for rounding purposes.
+ */
+ ms->a = (ms->p1+512)/128;
+
+ /* b = (((p1+512)-128*a)*c + (b % c) + 64)/128 */
+ temp = (ms->p1+512)-128*(uint64_t)ms->a;
+ temp = (temp * ms->c) + ms->p2;
+ temp = (temp + 64) / 128;
+ assert(temp <= UINT32_MAX);
+ ms->b = (uint32_t)temp;
+
+ log_verbose("Unpacked a + b/c: %d + %d/%d\n", ms->a, ms->b, ms->c);
+ log_verbose("Unpacked r: %d\n", ms->r);
+}
+
+/*
+ * Pack (a, b, c, r) into (p1, p2, p3) and regs[]
+ */
+static void si5338_pack_regs(struct si5338_multisynth *ms)
+{
+ /* Precondition:
+ * (a, b, c) and r have been populated
+ *
+ * Post-condition:
+ * (p1, p2, p3) and regs[10] are populated
+ */
+
+ /* p1 = (a * c + b) * 128 / c - 512 */
+ uint64_t temp;
+ temp = (uint64_t)ms->a * ms->c + ms->b;
+ temp = temp * 128 ;
+ temp = temp / ms->c - 512;
+ assert(temp <= UINT32_MAX);
+ ms->p1 = (uint32_t)temp;
+ //ms->p1 = ms->a * ms->c + ms->b;
+ //ms->p1 = ms->p1 * 128;
+ //ms->p1 = ms->p1 / ms->c - 512;
+
+ /* p2 = (b * 128) % c */
+ temp = (uint64_t)ms->b * 128;
+ temp = temp % ms->c;
+ assert(temp <= UINT32_MAX);
+ ms->p2 = (uint32_t)temp;
+
+ /* p3 = c */
+ ms->p3 = ms->c;
+
+ log_verbose("MSx P1: 0x%8.8x (%u) P2: 0x%8.8x (%u) P3: 0x%8.8x (%u)\n",
+ ms->p1, ms->p1, ms->p2, ms->p2, ms->p3, ms->p3);
+
+ /* Regs */
+ ms->regs[0] = ms->p1 & 0xff;
+ ms->regs[1] = (ms->p1 >> 8) & 0xff;
+ ms->regs[2] = ((ms->p2 & 0x3f) << 2) | ((ms->p1 >> 16) & 0x3);
+ ms->regs[3] = (ms->p2 >> 6) & 0xff;
+ ms->regs[4] = (ms->p2 >> 14) & 0xff;
+ ms->regs[5] = (ms->p2 >> 22) & 0xff;
+ ms->regs[6] = ms->p3 & 0xff;
+ ms->regs[7] = (ms->p3 >> 8) & 0xff;
+ ms->regs[8] = (ms->p3 >> 16) & 0xff;
+ ms->regs[9] = (ms->p3 >> 24) & 0xff;
+
+ return ;
+}
+
+static int si5338_write_multisynth(struct bladerf *dev,
+ struct si5338_multisynth *ms)
+{
+ int i, status;
+ uint8_t r_power, r_count, val;
+
+ log_verbose("Writing MS%d\n", ms->index);
+
+ /* Write out the enables */
+ status = dev->backend->si5338_read(dev, 36 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ val |= ms->enable;
+ log_verbose("Wrote enable register: 0x%2.2x\n", val);
+ status = dev->backend->si5338_write(dev, 36 + ms->index, val);
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ /* Write out the registers */
+ for (i = 0 ; i < 10 ; i++) {
+ status = dev->backend->si5338_write(dev, ms->base + i, *(ms->regs+i));
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Wrote regs[%d]: 0x%2.2x\n", i, *(ms->regs+i));
+ }
+
+ /* Calculate r_power from c_count */
+ r_power = 0;
+ r_count = ms->r >> 1 ;
+ while (r_count > 0) {
+ r_count >>= 1;
+ r_power++;
+ }
+
+ /* Set the r value to the log2(r_count) to match Figure 18 */
+ val = 0xc0;
+ val |= (r_power<<2);
+
+ log_verbose("Wrote r register: 0x%2.2x\n", val);
+
+ status = dev->backend->si5338_write(dev, 31 + ms->index, val);
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ }
+
+ return status ;
+}
+
+static int si5338_read_multisynth(struct bladerf *dev,
+ struct si5338_multisynth *ms)
+{
+ int i, status;
+ uint8_t val;
+
+ log_verbose("Reading MS%d\n", ms->index);
+
+ /* Read the enable bits */
+ status = dev->backend->si5338_read(dev, 36 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status ;
+ }
+ ms->enable = val&7;
+ log_verbose("Read enable register: 0x%2.2x\n", val);
+
+ /* Read all of the multisynth registers */
+ for (i = 0; i < 10; i++) {
+ status = dev->backend->si5338_read(dev, ms->base + i, ms->regs+i);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Read regs[%d]: 0x%2.2x\n", i, *(ms->regs+i));
+ }
+
+ /* Populate the RxDIV value from the register */
+ status = dev->backend->si5338_read(dev, 31 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ /* RxDIV is stored as a power of 2, so restore it on readback */
+ log_verbose("Read r register: 0x%2.2x\n", val);
+ val = (val>>2)&7;
+ ms->r = (1<<val);
+
+ /* Unpack the regs into appropriate values */
+ si5338_unpack_regs(ms) ;
+
+ return 0;
+}
+
+static void si5338_calculate_ms_freq(struct si5338_multisynth *ms,
+ struct bladerf_rational_rate *rate)
+{
+ struct bladerf_rational_rate abc;
+ abc.integer = ms->a;
+ abc.num = ms->b;
+ abc.den = ms->c;
+
+ rate->integer = 0;
+ rate->num = SI5338_F_VCO * abc.den;
+ rate->den = (uint64_t)ms->r*(abc.integer * abc.den + abc.num);
+
+ /* Compensate for doubling of frequency for LMS sampling clocks */
+ if(ms->index == 1 || ms->index == 2) {
+ rate->den *= 2;
+ }
+
+ si5338_rational_reduce(rate);
+
+ log_verbose("Calculated multisynth frequency: %"
+ PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ rate->integer, rate->num, rate->den);
+
+ return;
+}
+
+static int si5338_calculate_multisynth(struct si5338_multisynth *ms,
+ struct bladerf_rational_rate *rate)
+{
+
+ struct bladerf_rational_rate req;
+ struct bladerf_rational_rate abc;
+ uint8_t r_value;
+
+ /* Don't muss with the users data */
+ req = *rate;
+
+ /* Double requested frequency for sample clocks since LMS requires
+ * 2:1 clock:sample rate
+ */
+ if(ms->index == 1 || ms->index == 2) {
+ si5338_rational_double(&req);
+ }
+
+ /* Find a suitable R value */
+ r_value = 1;
+ while (req.integer < 5000000 && r_value < 32) {
+ si5338_rational_double(&req);
+ r_value <<= 1;
+ }
+
+ if (r_value == 32 && req.integer < 5000000) {
+ log_debug("Sample rate requires r > 32\n");
+ return BLADERF_ERR_INVAL;
+ } else {
+ log_verbose("Found r value of: %d\n", r_value);
+ }
+
+ /* Find suitable MS (a, b, c) values */
+ abc.integer = 0;
+ abc.num = SI5338_F_VCO * req.den;
+ abc.den = req.integer * req.den + req.num;
+ si5338_rational_reduce(&abc);
+
+ log_verbose("MSx a + b/c: %"PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ abc.integer, abc.num, abc.den);
+
+ /* Check values to make sure they are OK */
+ if (abc.integer < 8) {
+ switch (abc.integer) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ case 7:
+ log_debug("Integer portion too small: %"PRIu64"\n", abc.integer);
+ return BLADERF_ERR_INVAL;
+ }
+ } else if (abc.integer > 567) {
+ log_debug("Integer portion too large: %"PRIu64"\n", abc.integer);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Loss of precision if num or den are greater than 2^30-1 */
+ while (abc.num > (1<<30) || abc.den > (1<<30) ) {
+ log_debug("Loss of precision in reducing fraction from "
+ "%"PRIu64"/%"PRIu64" to %"PRIu64"/%"PRIu64"\n",
+ abc.num, abc.den, abc.num>>1, abc.den>>1);
+ abc.num >>= 1;
+ abc.den >>= 1;
+ }
+
+ log_verbose("MSx a + b/c: %"PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ abc.integer, abc.num, abc.den);
+
+ /* Set it in the multisynth */
+ assert(abc.integer <= UINT32_MAX);
+ assert(abc.num <= UINT32_MAX);
+ assert(abc.den <= UINT32_MAX);
+ ms->a = (uint32_t)abc.integer;
+ ms->b = (uint32_t)abc.num;
+ ms->c = (uint32_t)abc.den;
+ ms->r = r_value;
+
+ /* Pack the registers */
+ si5338_pack_regs(ms);
+
+ return 0;
+}
+
+/**
+ * Configure a multisynth for either the RX/TX sample clocks (index=1 or 2)
+ * or for the SMB output (index=3).
+ */
+static int si5338_set_rational_multisynth(struct bladerf *dev,
+ uint8_t index, uint8_t channel,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual_ret)
+{
+ struct si5338_multisynth ms;
+ struct bladerf_rational_rate req;
+ struct bladerf_rational_rate actual;
+ int status;
+
+ si5338_rational_reduce(rate);
+
+ /* Save off the value */
+ req = *rate;
+
+ /* Setup the multisynth enables and index */
+ ms.index = index;
+ ms.enable = channel;
+
+ /* Update the base address register */
+ si5338_update_base(&ms);
+
+ /* Calculate multisynth values */
+ status = si5338_calculate_multisynth(&ms, &req);
+ if(status != 0) {
+ return status;
+ }
+
+ /* Get the actual rate */
+ si5338_calculate_ms_freq(&ms, &actual);
+ if (actual_ret) {
+ memcpy(actual_ret, &actual, sizeof(*actual_ret));
+ }
+
+ /* Program it to the part */
+ status = si5338_write_multisynth(dev, &ms);
+
+ /* Done */
+ return status ;
+}
+
+
+int si5338_set_rational_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ struct bladerf_rational_rate rate_reduced = *rate;
+ uint8_t index = (ch == BLADERF_CHANNEL_RX(0)) ? 1 : 2;
+ uint8_t channel = SI5338_EN_A;
+
+ /* Enforce minimum sample rate */
+ si5338_rational_reduce(&rate_reduced);
+ if (rate_reduced.integer < BLADERF_SAMPLERATE_MIN) {
+ log_debug("%s: provided sample rate violates minimum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (ch == BLADERF_CHANNEL_TX(0)) {
+ channel |= SI5338_EN_B;
+ }
+
+ return si5338_set_rational_multisynth(dev, index, channel, &rate_reduced, actual);
+}
+
+int si5338_set_rational_smb_freq(struct bladerf *dev,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ struct bladerf_rational_rate rate_reduced = *rate;
+
+ /* Enforce minimum and maximum frequencies */
+ si5338_rational_reduce(&rate_reduced);
+
+ if (rate_reduced.integer < BLADERF_SMB_FREQUENCY_MIN) {
+ log_debug("%s: provided SMB freq violates minimum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (rate_reduced.integer > BLADERF_SMB_FREQUENCY_MAX) {
+ log_debug("%s: provided SMB freq violates maximum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return si5338_set_rational_multisynth(dev, 3, SI5338_EN_A, &rate_reduced, actual);
+}
+
+int si5338_set_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ uint32_t rate, uint32_t *actual)
+{
+ struct bladerf_rational_rate req, act;
+ int status;
+
+ memset(&act, 0, sizeof(act));
+ log_verbose("Setting integer sample rate: %d\n", rate);
+ req.integer = rate;
+ req.num = 0;
+ req.den = 1;
+
+ status = si5338_set_rational_sample_rate(dev, ch, &req, &act);
+
+ if (status == 0 && act.num != 0) {
+ log_info("Non-integer sample rate set from integer sample rate, "
+ "truncating output.\n");
+ }
+
+ assert(act.integer <= UINT32_MAX);
+
+ if (actual) {
+ *actual = (uint32_t)act.integer;
+ }
+ log_verbose("Set actual integer sample rate: %d\n", act.integer);
+
+ return status ;
+}
+
+int si5338_set_smb_freq(struct bladerf *dev, uint32_t rate, uint32_t *actual)
+{
+ struct bladerf_rational_rate req, act;
+ int status;
+
+ memset(&act, 0, sizeof(act));
+ log_verbose("Setting integer SMB frequency: %d\n", rate);
+ req.integer = rate;
+ req.num = 0;
+ req.den = 1;
+
+ status = si5338_set_rational_smb_freq(dev, &req, &act);
+
+ if (status == 0 && act.num != 0) {
+ log_info("Non-integer SMB frequency set from integer frequency, "
+ "truncating output.\n");
+ }
+
+ assert(act.integer <= UINT32_MAX);
+
+ if (actual) {
+ *actual = (uint32_t)act.integer;
+ }
+ log_verbose("Set actual integer SMB frequency: %d\n", act.integer);
+
+ return status;
+}
+
+int si5338_get_rational_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ struct bladerf_rational_rate *rate)
+{
+
+ struct si5338_multisynth ms;
+ int status;
+
+ /* Select the multisynth we want to read */
+ ms.index = (ch == BLADERF_CHANNEL_RX(0)) ? 1 : 2;
+
+ /* Update the base address */
+ si5338_update_base(&ms);
+
+ /* Readback */
+ status = si5338_read_multisynth(dev, &ms);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ si5338_calculate_ms_freq(&ms, rate);
+
+ return 0;
+}
+
+int si5338_get_rational_smb_freq(struct bladerf *dev,
+ struct bladerf_rational_rate *rate)
+{
+ struct si5338_multisynth ms;
+ int status;
+
+ /* Select MS3 for the SMB output */
+ ms.index = 3;
+ si5338_update_base(&ms);
+
+ status = si5338_read_multisynth(dev, &ms);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ si5338_calculate_ms_freq(&ms, rate);
+
+ return 0;
+}
+
+int si5338_get_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ unsigned int *rate)
+{
+ struct bladerf_rational_rate actual;
+ int status;
+
+ status = si5338_get_rational_sample_rate(dev, ch, &actual);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ if (actual.num != 0) {
+ log_debug("Fractional sample rate truncated during integer sample rate"
+ "retrieval\n");
+ }
+
+ assert(actual.integer <= UINT_MAX);
+ *rate = (unsigned int)actual.integer;
+
+ return 0;
+}
+
+int si5338_get_smb_freq(struct bladerf *dev, unsigned int *rate)
+{
+ struct bladerf_rational_rate actual;
+ int status;
+
+ status = si5338_get_rational_smb_freq(dev, &actual);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ if (actual.num != 0) {
+ log_debug("Fractional SMB frequency truncated during integer SMB"
+ " frequency retrieval\n");
+ }
+
+ assert(actual.integer <= UINT_MAX);
+ *rate = (unsigned int)actual.integer;
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/driver/si5338.h b/Radio/HW/BladeRF/src/driver/si5338.h
new file mode 100644
index 0000000..331f8c1
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/si5338.h
@@ -0,0 +1,134 @@
+/**
+ * @file si5338.h
+ *
+ * @brief SI5339 Support
+ *
+ * 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 DRIVER_SI5338_H_
+#define DRIVER_SI5338_H_
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+
+/**
+ * Set the rational sample rate of the specified channel.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] rate Rational rate requested
+ * @param[out] actual Rational rate actually set
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_set_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual);
+
+/**
+ * Get the rational sample rate of the specified channel.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] rate Rational rate
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_get_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate);
+
+/**
+ * Set the integral sample rate of the specified channel.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] rate Integral rate requested
+ * @param[out] actual Integral rate actually set
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t rate,
+ uint32_t *actual);
+
+/**
+ * Get the integral sample rate of the specified channel.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] rate Integral rate
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ unsigned int *rate);
+
+/**
+ * Set the rational frequency of the external SMB port.
+ *
+ * @param dev Device handle
+ * @param[in] rate Rational rate requested
+ * @param[out] actual Rational rate actually set
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_set_rational_smb_freq(struct bladerf *dev,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual);
+
+/**
+ * Get the rational sample rate of the external SMB port.
+ *
+ * @param dev Device handle
+ * @param[out] rate Rational rate
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_get_rational_smb_freq(struct bladerf *dev,
+ struct bladerf_rational_rate *rate);
+
+/**
+ * Set the integral sample rate of the external SMB port.
+ *
+ * @param dev Device handle
+ * @param[in] rate Integral rate requested
+ * @param[out] actual Integral rate actually set
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_set_smb_freq(struct bladerf *dev, uint32_t rate, uint32_t *actual);
+
+/**
+ Get the integral sample rate of the external SMB port.
+ *
+ * @param dev Device handle
+ * @param[out] rate Integral rate
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_get_smb_freq(struct bladerf *dev, unsigned int *rate);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/smb_clock.c b/Radio/HW/BladeRF/src/driver/smb_clock.c
new file mode 100644
index 0000000..65ccc04
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/smb_clock.c
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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 "log.h"
+
+#include "smb_clock.h"
+#include "driver/si5338.h"
+#include "board/board.h"
+
+struct regvals {
+ uint8_t addr;
+ uint8_t data;
+};
+
+/* Power-on defaults with SMB clock port not in use */
+static const struct regvals default_config[] = {
+ { 6, 0x08 },
+ { 28, 0x0b },
+ { 29, 0x08 },
+ { 30, 0xb0 },
+ { 34, 0xe3 },
+ { 39, 0x00 },
+
+ /* Reset Multisynth 3 */
+ { 86, 0x00 },
+ { 87, 0x00 },
+ { 88, 0x00 },
+ { 89, 0x00 },
+ { 90, 0x00 },
+ { 91, 0x00 },
+ { 92, 0x00 },
+ { 93, 0x00 },
+ { 94, 0x00 },
+ { 95, 0x00 },
+};
+
+static const struct regvals input_config[] = {
+ { 6, 0x04 },
+ { 28, 0x2b },
+ { 29, 0x28 },
+ { 30, 0xa8 },
+};
+
+static const struct regvals output_config[] = {
+ { 34, 0x22 },
+};
+
+static int write_regs(struct bladerf *dev, const struct regvals *reg, size_t n)
+{
+ size_t i;
+ int status = 0;
+
+ for (i = 0; i < n && status == 0; i++) {
+ status = dev->backend->si5338_write(dev, reg[i].addr, reg[i].data);
+ }
+
+ return status;
+}
+
+static inline int smb_mode_input(struct bladerf *dev)
+{
+ int status;
+ uint8_t val;
+
+ status = write_regs(dev, input_config, ARRAY_SIZE(input_config));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Turn off any SMB connector output */
+ status = dev->backend->si5338_read(dev, 39, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(1);
+ status = dev->backend->si5338_write(dev, 39, val);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static inline int smb_mode_output(struct bladerf *dev)
+{
+ int status;
+ uint8_t val;
+
+ status = dev->backend->si5338_read(dev, 39, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val |= 1;
+ status = dev->backend->si5338_write(dev, 39, val);
+ if (status != 0) {
+ return status;
+ }
+
+ status = write_regs(dev, output_config, ARRAY_SIZE(output_config));
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static inline int smb_mode_disabled(struct bladerf *dev)
+{
+ return write_regs(dev, default_config, ARRAY_SIZE(default_config));
+}
+
+int smb_clock_set_mode(struct bladerf *dev, bladerf_smb_mode mode)
+{
+ int status;
+
+ /* Reset initial state */
+ status = smb_mode_disabled(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Apply changes */
+ switch (mode) {
+ case BLADERF_SMB_MODE_DISABLED:
+ break;
+
+ case BLADERF_SMB_MODE_OUTPUT:
+ status = smb_mode_output(dev);
+ break;
+
+ case BLADERF_SMB_MODE_INPUT:
+ status = smb_mode_input(dev);
+ break;
+
+ default:
+ log_debug("Invalid SMB clock port mode: %d\n", mode);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+
+/* For simplicity, we just check a few bits that are indicative of our known
+ * register configurations for our SMB clock port configurations.
+ *
+ * Inconsistent register states (or users manually changing registers) may
+ * cause this function to erroneously report the state.
+ */
+int smb_clock_get_mode(struct bladerf *dev, bladerf_smb_mode *mode)
+{
+ int status;
+ uint8_t val;
+
+ /* Check DRV3_FMT[2:0] for an output configuration */
+ status = dev->backend->si5338_read(dev, 39, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (val & 0x7) {
+ case 0x00: /* No output */
+ break;
+
+ case 0x01: /* CLK3A CMOS/SSTL/HSTL - we're outputting a clock */
+ *mode = BLADERF_SMB_MODE_OUTPUT;
+ return 0;
+
+ case 0x02: /* CLK3B CMOS/SSTL/HSTL - used by the XB-200. */
+ *mode = BLADERF_SMB_MODE_UNAVAILBLE;
+ return 0;
+
+ default:
+ *mode = BLADERF_SMB_MODE_INVALID;
+ log_debug("Si5338[39] in unexpected state: 0x%02x\n", val);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ /* Check P2DIV_IN[0] for an input configuration */
+ status = dev->backend->si5338_read(dev, 28, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ if ((val & (1 << 5)) != 0) {
+ *mode = BLADERF_SMB_MODE_INPUT;
+ } else {
+ *mode = BLADERF_SMB_MODE_DISABLED;
+ }
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/driver/smb_clock.h b/Radio/HW/BladeRF/src/driver/smb_clock.h
new file mode 100644
index 0000000..c0f206d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/smb_clock.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef DRIVER_SMB_CLOCK_H_
+#define DRIVER_SMB_CLOCK_H_
+
+#include <libbladeRF.h>
+
+/**
+ * Set the current mode of operation of the SMB clock port
+ *
+ * @param dev Device handle
+ * @param[in] mode Desired mode
+ *
+ * @return 0 on success, or a BLADERF_ERR_* value on failure.
+ */
+int smb_clock_set_mode(struct bladerf *dev, bladerf_smb_mode mode);
+
+/**
+ * Get the current mode of operation of the SMB clock port
+ *
+ * @param dev Device handle
+ * @param[out] mode Desired mode
+ *
+ * @return 0 on success, or a value from \ref RETCODES list on failure.
+ */
+int smb_clock_get_mode(struct bladerf *dev, bladerf_smb_mode *mode);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/spi_flash.c b/Radio/HW/BladeRF/src/driver/spi_flash.c
new file mode 100644
index 0000000..f0e1a1d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/spi_flash.c
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org>
+ * 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 <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "log.h"
+
+#include "spi_flash.h"
+#include "board/board.h"
+
+static inline int check_eb_access(struct bladerf *dev,
+ uint32_t erase_block, uint32_t count)
+{
+ if (erase_block >= dev->flash_arch->num_ebs) {
+ log_debug("Invalid erase block: %u\n", erase_block);
+ return BLADERF_ERR_INVAL;
+ } else if (count > dev->flash_arch->num_ebs) {
+ log_debug("Invalid number of erase blocks: %u\n", count);
+ return BLADERF_ERR_INVAL;
+ } else if ((erase_block + count) > dev->flash_arch->num_ebs) {
+ log_debug("Requested operation extends past end of flash: "
+ "eb=%u, count=%u\n", erase_block, count);
+ return BLADERF_ERR_INVAL;
+ } else {
+ return 0;
+ }
+}
+
+static inline int check_page_access(struct bladerf *dev,
+ uint32_t page, uint32_t count)
+{
+ if (page >= dev->flash_arch->num_pages) {
+ log_debug("Invalid page: %u\n", page);
+ return BLADERF_ERR_INVAL;
+ } else if (count > dev->flash_arch->num_pages) {
+ log_debug("Invalid number of pages: %u\n", count);
+ return BLADERF_ERR_INVAL;
+ } else if ((page + count) > dev->flash_arch->num_pages) {
+ log_debug("Requested operation extends past end of flash: "
+ "page=%u, count=%u\n", page, count);
+ return BLADERF_ERR_INVAL;
+ } else {
+ return 0;
+ }
+}
+
+int spi_flash_erase(struct bladerf *dev, uint32_t erase_block, uint32_t count)
+{
+ int status = check_eb_access(dev, erase_block, count);
+
+ if (status == 0) {
+ status = dev->backend->erase_flash_blocks(dev, erase_block, count);
+ }
+
+ return status;
+}
+
+int spi_flash_read(struct bladerf *dev, uint8_t *buf,
+ uint32_t page, uint32_t count)
+{
+ int status = check_page_access(dev, page, count);
+
+ if (status == 0) {
+ status = dev->backend->read_flash_pages(dev, buf, page, count);
+ }
+
+ return status;;
+}
+
+int spi_flash_write(struct bladerf *dev, const uint8_t *buf,
+ uint32_t page, uint32_t count)
+{
+ int status = check_page_access(dev, page, count);
+
+ if (status == 0) {
+ status = dev->backend->write_flash_pages(dev, buf, page, count);
+ }
+
+ return status;
+}
+
+int spi_flash_verify(struct bladerf *dev, uint8_t *readback_buf,
+ const uint8_t *expected_buf, uint32_t page,
+ uint32_t count)
+{
+ int status = 0;
+ size_t i;
+ const size_t len = count * dev->flash_arch->psize_bytes;
+
+ log_info("Verifying %u pages, starting at page %u\n", count, page);
+ status = spi_flash_read(dev, readback_buf, page, count);
+
+ if (status < 0) {
+ log_debug("Failed to read from flash: %s\n", bladerf_strerror(status));
+ return status;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (expected_buf[i] != readback_buf[i]) {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_info("Flash verification failed at byte %llu. "
+ "Read %02x, expected %02x\n",
+ i, readback_buf[i], expected_buf[i]);
+ break;
+ }
+ }
+
+ return status;
+}
+
diff --git a/Radio/HW/BladeRF/src/driver/spi_flash.h b/Radio/HW/BladeRF/src/driver/spi_flash.h
new file mode 100644
index 0000000..c425494
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/spi_flash.h
@@ -0,0 +1,99 @@
+/**
+ * @file flash.h
+ *
+ * @brief Flash conversion and alignment routines
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org>
+ * 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 DRIVER_SPI_FLASH_H_
+#define DRIVER_SPI_FLASH_H_
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <libbladeRF.h>
+
+/**
+ * Erase regions of SPI flash
+ *
+ * @param dev Device handle
+ * @param[in] erase_block Erase block to start erasing at
+ * @param[in] count Number of blocks to erase.
+ *
+ * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `erase_block` or
+ * `count` value, or a value from \ref RETCODES list on other failures
+ */
+int spi_flash_erase(struct bladerf *dev, uint32_t erase_block, uint32_t count);
+
+/**
+ * Read data from flash
+ *
+ * @param dev Device handle
+ * @param[out] buf Buffer to read data into. Must be `count` *
+ * flash-page-size bytes or larger.
+ * @param[in] page Page to begin reading from
+ * @param[in] count Number of pages to read
+ *
+ * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count`
+ * value, or a value from \ref RETCODES list on other failures.
+ */
+int spi_flash_read(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+/**
+ * Verify data in flash
+ *
+ * @param dev Device handle
+ * @param[out] readback_buf Buffer to read data into. Must be `count` *
+ * flash-page-size bytes or larger.
+ * @param[in] expected_buf Expected contents of buffer
+ * @param[in] page Page to begin reading from
+ * @param[in] count Number of pages to read
+ *
+ * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count`
+ * value, or a value from \ref RETCODES list on other failures.
+ */
+int spi_flash_verify(struct bladerf *dev,
+ uint8_t *readback_buf,
+ const uint8_t *expected_buf,
+ uint32_t page,
+ uint32_t count);
+
+/**
+ * Write data to flash
+ *
+ * @param dev Device handle
+ * @param[in] buf Data to write to flash
+ * @param[in] page Page to begin writing at
+ * @param[in] count Number of pages to write
+ *
+ * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count`
+ * value, or a value from \ref RETCODES list on other failures.
+ */
+int spi_flash_write(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/expansion/xb100.c b/Radio/HW/BladeRF/src/expansion/xb100.c
new file mode 100644
index 0000000..b432616
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb100.c
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 "xb100.h"
+
+#include "log.h"
+
+int xb100_attach(struct bladerf *dev)
+{
+ return 0;
+}
+
+void xb100_detach(struct bladerf *dev)
+{
+}
+
+int xb100_enable(struct bladerf *dev, bool enable)
+{
+ int status = 0;
+
+ const uint32_t mask =
+ BLADERF_XB100_LED_D1 |
+ BLADERF_XB100_LED_D2 |
+ BLADERF_XB100_LED_D3 |
+ BLADERF_XB100_LED_D4 |
+ BLADERF_XB100_LED_D5 |
+ BLADERF_XB100_LED_D6 |
+ BLADERF_XB100_LED_D7 |
+ BLADERF_XB100_LED_D8 |
+ BLADERF_XB100_TLED_RED |
+ BLADERF_XB100_TLED_GREEN |
+ BLADERF_XB100_TLED_BLUE;
+
+ const uint32_t outputs = mask;
+ const uint32_t default_values = mask;
+
+ if (enable) {
+ status = dev->backend->expansion_gpio_dir_write(dev, mask, outputs);
+ if (status == 0) {
+ status = dev->backend->expansion_gpio_write(dev, mask, default_values);
+ }
+ }
+
+ return status;
+}
+
+int xb100_init(struct bladerf *dev)
+{
+ return 0;
+}
+
+int xb100_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ return dev->backend->expansion_gpio_read(dev, val);
+}
+
+int xb100_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ return dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+}
+
+int xb100_gpio_masked_write(struct bladerf *dev, uint32_t mask, uint32_t val)
+{
+ return dev->backend->expansion_gpio_write(dev, mask, val);
+}
+
+int xb100_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ return dev->backend->expansion_gpio_dir_read(dev, val);
+}
+
+int xb100_gpio_dir_write(struct bladerf *dev, uint32_t val)
+{
+ return xb100_gpio_dir_masked_write(dev, 0xffffffff, val);
+}
+
+int xb100_gpio_dir_masked_write(struct bladerf *dev, uint32_t mask, uint32_t val)
+{
+ return dev->backend->expansion_gpio_dir_write(dev, mask, val);
+}
diff --git a/Radio/HW/BladeRF/src/expansion/xb100.h b/Radio/HW/BladeRF/src/expansion/xb100.h
new file mode 100644
index 0000000..2a49830
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb100.h
@@ -0,0 +1,47 @@
+/**
+ * @file xb.h
+ *
+ * @brief XB-100 support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 EXPANSION_XB100_H_
+#define EXPANSION_XB100_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "board/board.h"
+
+int xb100_attach(struct bladerf *dev);
+void xb100_detach(struct bladerf *dev);
+int xb100_enable(struct bladerf *dev, bool enable);
+int xb100_init(struct bladerf *dev);
+int xb100_gpio_read(struct bladerf *dev, uint32_t *val);
+int xb100_gpio_write(struct bladerf *dev, uint32_t val);
+int xb100_gpio_masked_write(struct bladerf *dev, uint32_t mask, uint32_t val);
+int xb100_gpio_dir_read(struct bladerf *dev, uint32_t *val);
+int xb100_gpio_dir_write(struct bladerf *dev, uint32_t val);
+int xb100_gpio_dir_masked_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/expansion/xb200.c b/Radio/HW/BladeRF/src/expansion/xb200.c
new file mode 100644
index 0000000..1e6dba2
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb200.c
@@ -0,0 +1,543 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 "xb200.h"
+
+#include "driver/si5338.h"
+#include "lms.h"
+#include "rel_assert.h"
+#include "log.h"
+
+#define BLADERF_XB_CONFIG_TX_PATH_MIX 0x04
+#define BLADERF_XB_CONFIG_TX_PATH_BYPASS 0x08
+#define BLADERF_XB_CONFIG_TX_BYPASS 0x04
+#define BLADERF_XB_CONFIG_TX_BYPASS_N 0x08
+#define BLADERF_XB_CONFIG_TX_BYPASS_MASK 0x0C
+#define BLADERF_XB_CONFIG_RX_PATH_MIX 0x10
+#define BLADERF_XB_CONFIG_RX_PATH_BYPASS 0x20
+#define BLADERF_XB_CONFIG_RX_BYPASS 0x10
+#define BLADERF_XB_CONFIG_RX_BYPASS_N 0x20
+#define BLADERF_XB_CONFIG_RX_BYPASS_MASK 0x30
+
+#define BLADERF_XB_RF_ON 0x0800
+#define BLADERF_XB_TX_ENABLE 0x1000
+#define BLADERF_XB_RX_ENABLE 0x2000
+
+#define BLADERF_XB_TX_RF_SW2 0x04000000
+#define BLADERF_XB_TX_RF_SW1 0x08000000
+#define BLADERF_XB_TX_MASK 0x0C000000
+#define BLADERF_XB_TX_SHIFT 26
+
+#define BLADERF_XB_RX_RF_SW2 0x10000000
+#define BLADERF_XB_RX_RF_SW1 0x20000000
+#define BLADERF_XB_RX_MASK 0x30000000
+#define BLADERF_XB_RX_SHIFT 28
+
+struct xb200_xb_data {
+ /* Track filterbank selection for RX and TX auto-selection */
+ bladerf_xb200_filter auto_filter[2];
+};
+
+int xb200_attach(struct bladerf *dev)
+{
+ struct xb200_xb_data *xb_data;
+ int status = 0;
+ uint32_t val;
+ uint8_t val8;
+ unsigned int muxout = 6;
+ const char *mux_lut[] = { "THREE-STATE OUTPUT",
+ "DVdd",
+ "DGND",
+ "R COUNTER OUTPUT",
+ "N DIVIDER OUTPUT",
+ "ANALOG LOCK DETECT",
+ "DIGITAL LOCK DETECT",
+ "RESERVED" };
+
+ xb_data = calloc(1, sizeof(struct xb200_xb_data));
+ if (xb_data == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ xb_data->auto_filter[BLADERF_CHANNEL_RX(0)] = -1;
+ xb_data->auto_filter[BLADERF_CHANNEL_TX(0)] = -1;
+
+ dev->xb_data = xb_data;
+
+ log_debug(" Attaching transverter board\n");
+ status = dev->backend->si5338_read(dev, 39, &val8);
+ if (status < 0) {
+ goto error;
+ }
+ val8 |= 2;
+ if ((status = dev->backend->si5338_write(dev, 39, val8))) {
+ goto error;
+ }
+ if ((status = dev->backend->si5338_write(dev, 34, 0x22))) {
+ goto error;
+ }
+ if ((status = dev->backend->config_gpio_read(dev, &val))) {
+ goto error;
+ }
+ val |= 0x80000000;
+ if ((status = dev->backend->config_gpio_write(dev, val))) {
+ goto error;
+ }
+ if ((status = dev->backend->expansion_gpio_read(dev, &val))) {
+ goto error;
+ }
+
+ if ((status = dev->backend->expansion_gpio_dir_write(dev, 0xffffffff,
+ 0x3C00383E))) {
+ goto error;
+ }
+
+ if ((status = dev->backend->expansion_gpio_write(dev, 0xffffffff, 0x800))) {
+ goto error;
+ }
+
+ // Load ADF4351 registers via SPI
+ // Refer to ADF4351 reference manual for register set
+ // The LO is set to a Int-N 1248MHz +3dBm tone
+ // Registers are written in order from 5 downto 0
+ if ((status = dev->backend->xb_spi(dev, 0x580005))) {
+ goto error;
+ }
+ if ((status = dev->backend->xb_spi(dev, 0x99A16C))) {
+ goto error;
+ }
+ if ((status = dev->backend->xb_spi(dev, 0xC004B3))) {
+ goto error;
+ }
+ log_debug(" MUXOUT: %s\n", mux_lut[muxout]);
+
+ if ((status = dev->backend->xb_spi(dev, 0x60008E42 | (1 << 8) |
+ (muxout << 26)))) {
+ goto error;
+ }
+ if ((status = dev->backend->xb_spi(dev, 0x08008011))) {
+ goto error;
+ }
+ if ((status = dev->backend->xb_spi(dev, 0x00410000))) {
+ goto error;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (!status && (val & 0x1))
+ log_debug(" MUXOUT Bit set: OK\n");
+ else {
+ log_debug(" MUXOUT Bit not set: FAIL\n");
+ }
+ if ((status =
+ dev->backend->expansion_gpio_write(dev, 0xffffffff, 0x3C000800))) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ free(dev->xb_data);
+ dev->xb_data = NULL;
+ return status;
+}
+
+void xb200_detach(struct bladerf *dev)
+{
+ if (dev->xb_data) {
+ free(dev->xb_data);
+ dev->xb_data = NULL;
+ }
+}
+
+int xb200_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint32_t val, orig;
+
+ status = dev->backend->expansion_gpio_read(dev, &orig);
+ if (status)
+ return status;
+
+ val = orig;
+ if (enable)
+ val |= BLADERF_XB_RF_ON;
+ else
+ val &= ~BLADERF_XB_RF_ON;
+
+ if (status || (val == orig))
+ return status;
+
+ return dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+}
+
+int xb200_init(struct bladerf *dev)
+{
+ int status;
+
+ log_verbose( "Setting RX path\n" );
+ status = xb200_set_path(dev, BLADERF_CHANNEL_RX(0), BLADERF_XB200_BYPASS);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose( "Setting TX path\n" );
+ status = xb200_set_path(dev, BLADERF_CHANNEL_TX(0), BLADERF_XB200_BYPASS);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose( "Setting RX filter\n" );
+ status = xb200_set_filterbank(dev, BLADERF_CHANNEL_RX(0), BLADERF_XB200_AUTO_1DB);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose( "Setting TX filter\n" );
+ status = xb200_set_filterbank(dev, BLADERF_CHANNEL_TX(0), BLADERF_XB200_AUTO_1DB);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+/**
+ * Validate XB-200 filter selection
+ *
+ * @param[in] f Filter supplied by API user.
+ *
+ * @return 0 for a valid enumeration value, BLADERF_ERR_INVAL otherwise.
+ */
+static int check_xb200_filter(bladerf_xb200_filter f)
+{
+ int status;
+
+ switch (f) {
+ case BLADERF_XB200_50M:
+ case BLADERF_XB200_144M:
+ case BLADERF_XB200_222M:
+ case BLADERF_XB200_CUSTOM:
+ case BLADERF_XB200_AUTO_3DB:
+ case BLADERF_XB200_AUTO_1DB:
+ status = 0;
+ break;
+
+ default:
+ log_debug("Invalid XB200 filter: %d\n", f);
+ status = BLADERF_ERR_INVAL;
+ break;
+ }
+
+ return status;
+}
+
+/**
+ * Validate XB-200 path selection
+ *
+ * @param[in] p Path supplied by API user.
+ *
+ * @return 0 for a valid enumeration value, BLADERF_ERR_INVAL otherwise.
+ */
+static int check_xb200_path(bladerf_xb200_path p)
+{
+ int status;
+
+ switch (p) {
+ case BLADERF_XB200_BYPASS:
+ case BLADERF_XB200_MIX:
+ status = 0;
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ log_debug("Invalid XB200 path: %d\n", p);
+ break;
+ }
+
+ return status;
+}
+int xb200_get_filterbank(struct bladerf *dev, bladerf_channel ch,
+ bladerf_xb200_filter *filter) {
+ int status;
+ uint32_t val;
+ unsigned int shift;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ shift = BLADERF_XB_RX_SHIFT;
+ } else {
+ shift = BLADERF_XB_TX_SHIFT;
+ }
+
+ *filter = (val >> shift) & 3;
+
+ status = check_xb200_filter(*filter);
+ if (status != 0) {
+ log_debug("Read back invalid GPIO state: 0x%08x\n", val);
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+static int set_filterbank_mux(struct bladerf *dev, bladerf_channel ch, bladerf_xb200_filter filter)
+{
+ int status;
+ uint32_t orig, val, mask;
+ unsigned int shift;
+ static const char *filters[] = { "50M", "144M", "222M", "custom" };
+
+ assert(filter >= 0);
+ assert(filter < ARRAY_SIZE(filters));
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ mask = BLADERF_XB_RX_MASK;
+ shift = BLADERF_XB_RX_SHIFT;
+ } else {
+ mask = BLADERF_XB_TX_MASK;
+ shift = BLADERF_XB_TX_SHIFT;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &orig);
+ if (status != 0) {
+ return status;
+ }
+
+ val = orig & ~mask;
+ val |= filter << shift;
+
+ if (orig != val) {
+ log_debug("Engaging %s band XB-200 %s filter\n", filters[filter],
+ mask == BLADERF_XB_TX_MASK ? "TX" : "RX");
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+
+ return 0;
+}
+
+int xb200_set_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter filter)
+{
+ struct xb200_xb_data *xb_data = dev->xb_data;
+ uint64_t frequency;
+
+ int status = 0;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (NULL == xb_data) {
+ log_error("xb_data is null (do you need to xb200_attach?)\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = check_xb200_filter(filter);
+ if (status != 0) {
+ return status;
+ }
+
+ if (filter == BLADERF_XB200_AUTO_1DB || filter == BLADERF_XB200_AUTO_3DB) {
+ /* Save which soft auto filter mode we're in */
+ xb_data->auto_filter[ch] = filter;
+
+ status = dev->board->get_frequency(dev, ch, &frequency);
+ if (status == 0) {
+ status = xb200_auto_filter_selection(dev, ch, frequency);
+ }
+
+ } else {
+ /* Invalidate the soft auto filter mode entry */
+ xb_data->auto_filter[ch] = -1;
+
+ status = set_filterbank_mux(dev, ch, filter);
+ }
+
+ return status;
+}
+
+int xb200_auto_filter_selection(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t frequency)
+{
+ struct xb200_xb_data *xb_data = dev->xb_data;
+ bladerf_xb200_filter filter;
+
+ int status = 0;
+
+ if (frequency >= 300000000u) {
+ return 0;
+ }
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (NULL == xb_data) {
+ log_error("xb_data is null (do you need to xb200_attach?)\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (xb_data->auto_filter[ch] == BLADERF_XB200_AUTO_1DB) {
+ if (37774405 <= frequency && frequency <= 59535436) {
+ filter = BLADERF_XB200_50M;
+ } else if (128326173 <= frequency && frequency <= 166711171) {
+ filter = BLADERF_XB200_144M;
+ } else if (187593160 <= frequency && frequency <= 245346403) {
+ filter = BLADERF_XB200_222M;
+ } else {
+ filter = BLADERF_XB200_CUSTOM;
+ }
+
+ status = set_filterbank_mux(dev, ch, filter);
+ } else if (xb_data->auto_filter[ch] == BLADERF_XB200_AUTO_3DB) {
+ if (34782924 <= frequency && frequency <= 61899260) {
+ filter = BLADERF_XB200_50M;
+ } else if (121956957 <= frequency && frequency <= 178444099) {
+ filter = BLADERF_XB200_144M;
+ } else if (177522675 <= frequency && frequency <= 260140935) {
+ filter = BLADERF_XB200_222M;
+ } else {
+ filter = BLADERF_XB200_CUSTOM;
+ }
+
+ status = set_filterbank_mux(dev, ch, filter);
+ }
+
+ return status;
+}
+
+#define LMS_RX_SWAP 0x40
+#define LMS_TX_SWAP 0x08
+
+int xb200_set_path(struct bladerf *dev,
+ bladerf_channel ch, bladerf_xb200_path path) {
+ int status;
+ uint32_t val;
+ uint32_t mask;
+ uint8_t lval, lorig = 0;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ status = check_xb200_path(path);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, 0x5A, &lorig);
+ if (status != 0) {
+ return status;
+ }
+
+ lval = lorig;
+
+ if (path == BLADERF_XB200_MIX) {
+ lval |= (ch == BLADERF_CHANNEL_RX(0)) ? LMS_RX_SWAP : LMS_TX_SWAP;
+ } else {
+ lval &= ~((ch == BLADERF_CHANNEL_RX(0)) ? LMS_RX_SWAP : LMS_TX_SWAP);
+ }
+
+ status = LMS_WRITE(dev, 0x5A, lval);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ if (!(val & BLADERF_XB_RF_ON)) {
+ status = xb200_attach(dev);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ mask = (BLADERF_XB_CONFIG_RX_BYPASS_MASK | BLADERF_XB_RX_ENABLE);
+ } else {
+ mask = (BLADERF_XB_CONFIG_TX_BYPASS_MASK | BLADERF_XB_TX_ENABLE);
+ }
+
+ val |= BLADERF_XB_RF_ON;
+ val &= ~mask;
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ if (path == BLADERF_XB200_MIX) {
+ val |= (BLADERF_XB_RX_ENABLE | BLADERF_XB_CONFIG_RX_PATH_MIX);
+ } else {
+ val |= BLADERF_XB_CONFIG_RX_PATH_BYPASS;
+ }
+ } else {
+ if (path == BLADERF_XB200_MIX) {
+ val |= (BLADERF_XB_TX_ENABLE | BLADERF_XB_CONFIG_TX_PATH_MIX);
+ } else {
+ val |= BLADERF_XB_CONFIG_TX_PATH_BYPASS;
+ }
+ }
+
+ return dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+}
+
+int xb200_get_path(struct bladerf *dev,
+ bladerf_channel ch, bladerf_xb200_path *path) {
+ int status;
+ uint32_t val;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ *path = (val & BLADERF_XB_CONFIG_RX_BYPASS) ?
+ BLADERF_XB200_MIX : BLADERF_XB200_BYPASS;
+
+ } else if (ch == BLADERF_CHANNEL_TX(0)) {
+ *path = (val & BLADERF_XB_CONFIG_TX_BYPASS) ?
+ BLADERF_XB200_MIX : BLADERF_XB200_BYPASS;
+ }
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/expansion/xb200.h b/Radio/HW/BladeRF/src/expansion/xb200.h
new file mode 100644
index 0000000..3234a7c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb200.h
@@ -0,0 +1,105 @@
+/**
+ * @file xb.h
+ *
+ * @brief XB-200 support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 EXPANSION_XB200_H_
+#define EXPANSION_XB200_H_
+
+#include <stdbool.h>
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+
+int xb200_attach(struct bladerf *dev);
+void xb200_detach(struct bladerf *dev);
+int xb200_enable(struct bladerf *dev, bool enable);
+int xb200_init(struct bladerf *dev);
+
+/**
+ * Select an XB-200 filterbank
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] filter XB200 filterbank
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb200_set_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter filter);
+
+/**
+ * Select an appropriate filterbank, based upon the specified frequency
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] frequency Frequency
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb200_auto_filter_selection(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t frequency);
+
+/**
+ * Get the current selected XB-200 filterbank
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] filter Pointer to filterbank, only updated if return value
+ * is 0.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb200_get_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter *filter);
+/**
+ * Configure the XB-200 signal path
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] path Desired XB-200 signal path
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb200_set_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path path);
+
+/**
+ * Get the current XB-200 signal path
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] path Pointer to XB200 signal path
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int xb200_get_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path *path);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/expansion/xb300.c b/Radio/HW/BladeRF/src/expansion/xb300.c
new file mode 100644
index 0000000..15a099d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb300.c
@@ -0,0 +1,299 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 "xb300.h"
+
+#include "log.h"
+
+#define BLADERF_XB_AUX_EN 0x000002
+#define BLADERF_XB_TX_LED 0x000010
+#define BLADERF_XB_RX_LED 0x000020
+#define BLADERF_XB_TRX_TXn 0x000040
+#define BLADERF_XB_TRX_RXn 0x000080
+#define BLADERF_XB_TRX_MASK 0x0000c0
+#define BLADERF_XB_PA_EN 0x000200
+#define BLADERF_XB_LNA_ENn 0x000400
+#define BLADERF_XB_CS 0x010000
+#define BLADERF_XB_CSEL 0x040000
+#define BLADERF_XB_DOUT 0x100000
+#define BLADERF_XB_SCLK 0x400000
+
+int xb300_attach(struct bladerf *dev) {
+ int status;
+ uint32_t val;
+
+ val = BLADERF_XB_TX_LED | BLADERF_XB_RX_LED | BLADERF_XB_TRX_MASK;
+ val |= BLADERF_XB_PA_EN | BLADERF_XB_LNA_ENn;
+ val |= BLADERF_XB_CSEL | BLADERF_XB_SCLK | BLADERF_XB_CS;
+
+ if ((status = dev->backend->expansion_gpio_dir_write(dev, 0xffffffff, val)))
+ return status;
+
+ val = BLADERF_XB_CS | BLADERF_XB_LNA_ENn;
+ if ((status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val)))
+ return status;
+
+ return status;
+}
+
+void xb300_detach(struct bladerf *dev)
+{
+}
+
+int xb300_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint32_t val;
+ float pwr;
+
+ val = BLADERF_XB_CS | BLADERF_XB_CSEL | BLADERF_XB_LNA_ENn;
+ if ((status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val)))
+ return status;
+
+ status = xb300_get_output_power(dev, &pwr);
+
+ return status;
+}
+
+int xb300_init(struct bladerf *dev)
+{
+ int status;
+
+ log_verbose( "Setting TRX path to TX\n" );
+ status = xb300_set_trx(dev, BLADERF_XB300_TRX_TX);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+int xb300_set_trx(struct bladerf *dev, bladerf_xb300_trx trx)
+{
+ int status;
+ uint32_t val;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(BLADERF_XB_TRX_MASK);
+
+ switch (trx) {
+ case BLADERF_XB300_TRX_RX:
+ val |= BLADERF_XB_TRX_RXn;
+ break;
+
+ case BLADERF_XB300_TRX_TX:
+ val |= BLADERF_XB_TRX_TXn;
+ break;
+
+ case BLADERF_XB300_TRX_UNSET:
+ break;
+
+ default:
+ log_debug("Invalid TRX option: %d\n", trx);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+ return status;
+}
+
+int xb300_get_trx(struct bladerf *dev, bladerf_xb300_trx *trx)
+{
+ int status;
+ uint32_t val;
+
+ *trx = BLADERF_XB300_TRX_INVAL;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= BLADERF_XB_TRX_MASK;
+
+ if (!val) {
+ *trx = BLADERF_XB300_TRX_UNSET;
+ } else {
+ *trx = (val & BLADERF_XB_TRX_RXn) ? BLADERF_XB300_TRX_RX : BLADERF_XB300_TRX_TX;
+ }
+
+ /* Sanity check */
+ switch (*trx) {
+ case BLADERF_XB300_TRX_TX:
+ case BLADERF_XB300_TRX_RX:
+ case BLADERF_XB300_TRX_UNSET:
+ break;
+
+ default:
+ log_debug("Read back invalid TRX setting value: %d\n", *trx);
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+
+int xb300_set_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp, bool enable) {
+ int status;
+ uint32_t val;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (amp) {
+ case BLADERF_XB300_AMP_PA:
+ if (enable) {
+ val |= BLADERF_XB_TX_LED;
+ val |= BLADERF_XB_PA_EN;
+ } else {
+ val &= ~BLADERF_XB_TX_LED;
+ val &= ~BLADERF_XB_PA_EN;
+ }
+ break;
+
+ case BLADERF_XB300_AMP_LNA:
+ if (enable) {
+ val |= BLADERF_XB_RX_LED;
+ val &= ~BLADERF_XB_LNA_ENn;
+ } else {
+ val &= ~BLADERF_XB_RX_LED;
+ val |= BLADERF_XB_LNA_ENn;
+ }
+ break;
+
+ case BLADERF_XB300_AMP_PA_AUX:
+ if (enable) {
+ val |= BLADERF_XB_AUX_EN;
+ } else {
+ val &= ~BLADERF_XB_AUX_EN;
+ }
+ break;
+
+ default:
+ log_debug("Invalid amplifier selection: %d\n", amp);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+
+ return status;
+}
+
+int xb300_get_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp, bool *enable)
+{
+ int status;
+ uint32_t val;
+
+ *enable = false;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (amp) {
+ case BLADERF_XB300_AMP_PA:
+ *enable = (val & BLADERF_XB_PA_EN);
+ break;
+
+ case BLADERF_XB300_AMP_LNA:
+ *enable = !(val & BLADERF_XB_LNA_ENn);
+ break;
+
+ case BLADERF_XB300_AMP_PA_AUX:
+ *enable = (val & BLADERF_XB_AUX_EN);
+ break;
+
+ default:
+ log_debug("Read back invalid amplifier setting: %d\n", amp);
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+
+int xb300_get_output_power(struct bladerf *dev, float *pwr)
+{
+ uint32_t val, rval;
+ int i;
+ int ret = 0;
+ int status;
+ float volt, volt2, volt3, volt4;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status) {
+ return status;
+ }
+
+ val &= ~(BLADERF_XB_CS | BLADERF_XB_SCLK | BLADERF_XB_CSEL);
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, BLADERF_XB_SCLK | val);
+ if (status) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, BLADERF_XB_CS | BLADERF_XB_SCLK | val);
+ if (status) {
+ return status;
+ }
+
+ for (i = 1; i <= 14; i++) {
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+ if (status) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, BLADERF_XB_SCLK | val);
+ if (status) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &rval);
+ if (status) {
+ return status;
+ }
+
+ if (i >= 2 && i <= 11) {
+ ret |= (!!(rval & BLADERF_XB_DOUT)) << (11 - i);
+ }
+ }
+
+ volt = (1.8f/1024.0f) * ret;
+ volt2 = volt * volt;
+ volt3 = volt2 * volt;
+ volt4 = volt3 * volt;
+
+ *pwr = -503.933f * volt4 +
+ 1409.489f * volt3 -
+ 1487.84f * volt2 +
+ 722.9793f * volt -
+ 114.7529f;
+
+ return 0;
+
+}
diff --git a/Radio/HW/BladeRF/src/expansion/xb300.h b/Radio/HW/BladeRF/src/expansion/xb300.h
new file mode 100644
index 0000000..5fe50de
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb300.h
@@ -0,0 +1,94 @@
+/**
+ * @file xb.h
+ *
+ * @brief XB-300 support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 EXPANSION_XB300_H_
+#define EXPANSION_XB300_H_
+
+#include <stdbool.h>
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+
+int xb300_attach(struct bladerf *dev);
+void xb300_detach(struct bladerf *dev);
+int xb300_enable(struct bladerf *dev, bool enable);
+int xb300_init(struct bladerf *dev);
+
+/**
+ * Configure the XB-300 TRX path
+ *
+ * @param dev Device handle
+ * @param[in] trx Desired XB-300 TRX setting
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb300_set_trx(struct bladerf *dev, bladerf_xb300_trx trx);
+
+/**
+ * Get the current XB-300 signal path
+ *
+ * @param dev Device handle
+ * @param[out] trx XB300 TRX antenna setting
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb300_get_trx(struct bladerf *dev, bladerf_xb300_trx *trx);
+
+/**
+ * Enable or disable selected XB-300 amplifier
+ *
+ * @param dev Device handle
+ * @param[in] amp XB-300 amplifier
+ * @param[in] enable Set true to enable or false to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb300_set_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool enable);
+/**
+ * Get state of selected XB-300 amplifier
+ *
+ * @param dev Device handle
+ * @param[in] amp XB-300 amplifier
+ * @param[out] enable Set true to enable or false to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb300_get_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool *enable);
+/**
+ * Get current PA PDET output power in dBm
+ *
+ * @param dev Device handle
+ * @param[out] val Output power in dBm
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int xb300_get_output_power(struct bladerf *dev, float *val);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/configfile.c b/Radio/HW/BladeRF/src/helpers/configfile.c
new file mode 100644
index 0000000..bfa339c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/configfile.c
@@ -0,0 +1,342 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2018 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 <libbladeRF.h>
+
+#include "conversions.h"
+#include "helpers/file.h"
+#include "log.h"
+#include "parse.h"
+
+/******************************************************************************/
+/* Config file stuff */
+/******************************************************************************/
+
+const struct numeric_suffix freq_suffixes[] = { { "G", 1000 * 1000 * 1000 },
+ { "GHz", 1000 * 1000 * 1000 },
+ { "M", 1000 * 1000 },
+ { "MHz", 1000 * 1000 },
+ { "k", 1000 },
+ { "kHz", 1000 } };
+#define NUM_FREQ_SUFFIXES (sizeof(freq_suffixes) / sizeof(freq_suffixes[0]))
+#define MAX(a, b) (a > b ? a : b)
+#define MIN(a, b) (a < b ? a : b)
+
+static int apply_config_options(struct bladerf *dev, struct config_options opt)
+{
+ int status;
+ bladerf_frequency freq;
+ bladerf_bandwidth bw;
+ uint32_t val;
+ bool ok;
+ bladerf_gain_mode gain_mode;
+ const struct bladerf_range *rx_range = NULL;
+ const struct bladerf_range *tx_range = NULL;
+ bladerf_sampling sampling_mode;
+ bladerf_vctcxo_tamer_mode tamer_mode = BLADERF_VCTCXO_TAMER_INVALID;
+
+ struct bladerf_rational_rate rate, actual;
+
+ status = BLADERF_ERR_INVAL;
+
+ if (!strcasecmp(opt.key, "biastee_tx")) {
+ bool enable = false;
+
+ status = str2bool(opt.value, &enable);
+ if (status < 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_bias_tee(dev, BLADERF_CHANNEL_TX(0), enable);
+ if (status < 0) {
+ return status;
+ }
+ } else if (!strcasecmp(opt.key, "biastee_rx")) {
+ bool enable = false;
+
+ status = str2bool(opt.value, &enable);
+ if (status < 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_bias_tee(dev, BLADERF_CHANNEL_RX(0), enable);
+ if (status < 0) {
+ return status;
+ }
+ } else if (!strcasecmp(opt.key, "fpga")) {
+ status = bladerf_load_fpga(dev, opt.value);
+ if (status < 0) {
+ log_warning("Config line %d: could not load FPGA from `%s'\n",
+ opt.lineno, opt.value);
+ }
+ return status;
+ } else if (!strcasecmp(opt.key, "frequency")) {
+ status =
+ bladerf_get_frequency_range(dev, BLADERF_CHANNEL_RX(0), &rx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status =
+ bladerf_get_frequency_range(dev, BLADERF_CHANNEL_TX(0), &tx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ freq = str2uint64_suffix(opt.value, MAX(rx_range->min, tx_range->min),
+ MIN(rx_range->max, tx_range->max),
+ freq_suffixes, NUM_FREQ_SUFFIXES, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(0), freq);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(0), freq);
+ } else if (!strcasecmp(opt.key, "samplerate")) {
+ status = bladerf_get_sample_rate_range(dev, BLADERF_CHANNEL_RX(0),
+ &rx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf_get_sample_rate_range(dev, BLADERF_CHANNEL_TX(0),
+ &tx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ freq = str2uint64_suffix(opt.value, MAX(rx_range->min, tx_range->min),
+ MIN(rx_range->max, tx_range->max),
+ freq_suffixes, NUM_FREQ_SUFFIXES, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ rate.integer = freq;
+ rate.num = 0;
+ rate.den = 1;
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0),
+ &rate, &actual);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0),
+ &rate, &actual);
+ } else if (!strcasecmp(opt.key, "bandwidth")) {
+ status =
+ bladerf_get_bandwidth_range(dev, BLADERF_CHANNEL_RX(0), &rx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status =
+ bladerf_get_bandwidth_range(dev, BLADERF_CHANNEL_TX(0), &tx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ if (MIN(rx_range->max, tx_range->max) >= UINT32_MAX) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ bw = str2uint_suffix(
+ opt.value, (bladerf_bandwidth)MAX(rx_range->min, tx_range->min),
+ (bladerf_bandwidth)MIN(rx_range->max, tx_range->max), freq_suffixes,
+ NUM_FREQ_SUFFIXES, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(0), bw, NULL);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), bw, NULL);
+ } else if (!strcasecmp(opt.key, "agc")) {
+ bool agcval = false;
+
+ status = str2bool(opt.value, &agcval);
+ if (status != 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ gain_mode = agcval ? BLADERF_GAIN_AUTOMATIC : BLADERF_GAIN_MANUAL;
+ status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(0), gain_mode);
+ } else if (!strcasecmp(opt.key, "gpio")) {
+ val = str2uint(opt.key, 0, -1, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_config_gpio_write(dev, val);
+ } else if (!strcasecmp(opt.key, "sampling")) {
+ if (!strcasecmp(opt.value, "internal")) {
+ sampling_mode = BLADERF_SAMPLING_INTERNAL;
+ } else if (!strcasecmp(opt.value, "external")) {
+ sampling_mode = BLADERF_SAMPLING_EXTERNAL;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_sampling(dev, sampling_mode);
+ } else if (!strcasecmp(opt.key, "trimdac")) {
+ val = str2uint(opt.value, 0, -1, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_dac_write(dev, val);
+ } else if (!strcasecmp(opt.key, "vctcxo_tamer")) {
+ if (!strcasecmp(opt.value, "disabled") ||
+ !strcasecmp(opt.value, "off")) {
+ tamer_mode = BLADERF_VCTCXO_TAMER_DISABLED;
+ } else if (!strcasecmp(opt.value, "1PPS") ||
+ !strcasecmp(opt.value, "1 PPS")) {
+ tamer_mode = BLADERF_VCTCXO_TAMER_1_PPS;
+ } else if (!strcasecmp(opt.value, "10MHZ") ||
+ !strcasecmp(opt.value, "10 MHZ")) {
+ tamer_mode = BLADERF_VCTCXO_TAMER_10_MHZ;
+ } else if (!strcasecmp(opt.value, "10M")) {
+ tamer_mode = BLADERF_VCTCXO_TAMER_10_MHZ;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_vctcxo_tamer_mode(dev, tamer_mode);
+ } else if (!strcasecmp(opt.key, "clock_ref")) {
+ bool enable = false;
+
+ status = str2bool(opt.value, &enable);
+ if (status != 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_pll_enable(dev, enable);
+ } else if (!strcasecmp(opt.key, "refin_freq")) {
+ status = bladerf_get_pll_refclk_range(dev, &rx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ freq = str2uint64_suffix(opt.value, rx_range->min, rx_range->max,
+ freq_suffixes, NUM_FREQ_SUFFIXES, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_pll_refclk(dev, freq);
+ } else if (!strcasecmp(opt.key, "clock_sel")) {
+ bladerf_clock_select clock_sel = CLOCK_SELECT_ONBOARD;
+
+ if (!strcasecmp(opt.value, "onboard") ||
+ !strcasecmp(opt.value, "internal")) {
+ clock_sel = CLOCK_SELECT_ONBOARD;
+ } else if (!strcasecmp(opt.value, "external")) {
+ clock_sel = CLOCK_SELECT_EXTERNAL;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_clock_select(dev, clock_sel);
+ } else if (!strcasecmp(opt.key, "clock_out")) {
+ bool enable = false;
+
+ status = str2bool(opt.value, &enable);
+ if (status != 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_clock_output(dev, enable);
+ } else {
+ log_warning("Invalid key `%s' on line %d\n", opt.key, opt.lineno);
+ }
+
+ if (status < 0)
+ log_warning("Error message for option (%s) on line %d:\n%s\n", opt.key,
+ opt.lineno, bladerf_strerror(status));
+
+ return status;
+}
+
+int config_load_options_file(struct bladerf *dev)
+{
+ char *filename = NULL;
+ int status = 0;
+
+ uint8_t *buf = NULL;
+ size_t buf_size;
+
+ int optc;
+ int j;
+ struct config_options *optv;
+
+ filename = file_find("bladeRF.conf");
+ if (!filename) {
+ filename = file_find("bladerf.conf");
+
+ /* A missing file that is optional is not an error */
+ if (!filename) {
+ return 0;
+ }
+ }
+
+ status = file_read_buffer(filename, &buf, &buf_size);
+ if (status < 0) {
+ goto out;
+ }
+
+ optc = str2options(dev, (const char *)buf, buf_size, &optv);
+ if (optc < 0) {
+ status = BLADERF_ERR_INVAL;
+ goto out_buf;
+ }
+
+ for (j = 0; j < optc; j++) {
+ status = apply_config_options(dev, optv[j]);
+ if (status < 0) {
+ log_warning("Invalid config option `%s' on line %d\n", optv[j].key,
+ optv[j].lineno);
+ /* Some config options will require the FPGA to be loaded, however
+ * this function is called during bladerf_open(). The solution is
+ * to treat BLADERF_ERR_NOT_INIT as a warning and continue. */
+ if (status == BLADERF_ERR_NOT_INIT) {
+ status = 0;
+ } else {
+ break;
+ }
+ }
+ }
+
+ free_opts(optv, optc);
+
+out_buf:
+ free(buf);
+out:
+ free(filename);
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/helpers/configfile.h b/Radio/HW/BladeRF/src/helpers/configfile.h
new file mode 100644
index 0000000..390a4cf
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/configfile.h
@@ -0,0 +1,27 @@
+/**
+ * @file configfile.h
+ *
+ * @brief Configuration file support for libbladeRF
+ *
+ * Copyright (C) 2013-2018 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 CONFIGFILE_H_
+#define CONFIGFILE_H_
+
+int config_load_options_file(struct bladerf *dev);
+
+#endif // CONFIGFILE_H_
diff --git a/Radio/HW/BladeRF/src/helpers/file.c b/Radio/HW/BladeRF/src/helpers/file.c
new file mode 100644
index 0000000..0977137
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/file.c
@@ -0,0 +1,511 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2014 Nuand LLC
+ * Copyright (C) 2013 Daniel Gröber <dxld ÄT darkboxed DOT org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <libbladeRF.h>
+
+#include "host_config.h"
+#include "minmax.h"
+#include "log.h"
+#include "rel_assert.h"
+
+#include "helpers/file.h"
+
+/* Paths to search for bladeRF files */
+struct search_path_entries {
+ bool prepend_home;
+ const char *path;
+};
+
+int file_write(FILE *f, uint8_t *buf, size_t len)
+{
+ size_t rv;
+
+ rv = fwrite(buf, 1, len, f);
+ if(rv < len) {
+ log_debug("File write failed: %s\n", strerror(errno));
+ return BLADERF_ERR_IO;
+ }
+
+ return 0;
+}
+
+int file_read(FILE *f, char *buf, size_t len)
+{
+ size_t rv;
+
+ rv = fread(buf, 1, len, f);
+ if(rv < len) {
+ if(feof(f))
+ log_debug("Unexpected end of file: %s\n", strerror(errno));
+ else
+ log_debug("Error reading file: %s\n", strerror(errno));
+
+ return BLADERF_ERR_IO;
+ }
+
+ return 0;
+}
+
+ssize_t file_size(FILE *f)
+{
+ ssize_t rv = BLADERF_ERR_IO;
+ long int fpos = ftell(f);
+ long len;
+
+ if(fpos < 0) {
+ log_verbose("ftell failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ if(fseek(f, 0, SEEK_END)) {
+ log_verbose("fseek failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ len = ftell(f);
+ if(len < 0) {
+ log_verbose("ftell failed: %s\n", strerror(errno));
+ goto out;
+ } else if (len == LONG_MAX) {
+ log_debug("ftell called with a directory?\n");
+ goto out;
+ }
+
+ if(fseek(f, fpos, SEEK_SET)) {
+ log_debug("fseek failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ rv = (ssize_t) len;
+ assert(rv == len);
+
+out:
+ return rv;
+}
+
+int file_read_buffer(const char *filename, uint8_t **buf_ret, size_t *size_ret)
+{
+ int status = BLADERF_ERR_UNEXPECTED;
+ FILE *f;
+ uint8_t *buf = NULL;
+ ssize_t len;
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ log_error("%s: could not open %s: %s\n", __FUNCTION__, filename,
+ strerror(errno));
+ switch (errno) {
+ case ENOENT:
+ return BLADERF_ERR_NO_FILE;
+
+ case EACCES:
+ return BLADERF_ERR_PERMISSION;
+
+ default:
+ return BLADERF_ERR_IO;
+ }
+ }
+
+ len = file_size(f);
+ if (len < 0) {
+ status = BLADERF_ERR_IO;
+ goto out;
+ }
+
+ buf = (uint8_t *)malloc(len);
+ if (!buf) {
+ status = BLADERF_ERR_MEM;
+ goto out;
+ }
+
+ status = file_read(f, (char *)buf, len);
+ if (status < 0) {
+ goto out;
+ }
+
+ *buf_ret = buf;
+ *size_ret = len;
+ fclose(f);
+ return 0;
+
+out:
+ free(buf);
+ if (f) {
+ fclose(f);
+ }
+
+ return status;
+}
+
+/* Remove the last entry in a path. This is used to strip the executable name
+* from a path to get the directory that the executable resides in. */
+static size_t strip_last_path_entry(char *buf, char dir_delim)
+{
+ size_t len = strlen(buf);
+
+ while (len > 0 && buf[len - 1] != dir_delim) {
+ buf[len - 1] = '\0';
+ len--;
+ }
+
+ return len;
+}
+
+#if BLADERF_OS_LINUX || BLADERF_OS_OSX || BLADERF_OS_FREEBSD
+#define ACCESS_FILE_EXISTS F_OK
+#define DIR_DELIMETER '/'
+
+static const struct search_path_entries search_paths[] = {
+ { false, "" },
+ { true, "/.config/Nuand/bladeRF/" },
+ { true, "/.Nuand/bladeRF/" },
+
+ /* LIBBLADERF_SEARCH_PATH_PREFIX is defined in the libbladeRF
+ * CMakeLists.txt file. It defaults to ${CMAKE_INSTALL_PREFIX}, but
+ * can be overridden via -DLIBBLADERF_SEARCH_PATH_OVERRIDE
+ */
+ //{ false, LIBBLADERF_SEARCH_PREFIX "/etc/Nuand/bladeRF/" },
+ //{ false, LIBBLADERF_SEARCH_PREFIX "/share/Nuand/bladeRF/" },
+
+ /* These two entries are here for reverse compatibility.
+ *
+ * We failed to prefix ${CMAKE_INSTALL_PREFIX} on these from the beginning,
+ * forcing package maintainers to hard-code one of these locations,
+ * despite having a different ${CMAKE_INSTALL_PREFIX}.
+ *
+ * We'll keep these around for some time as fall-backs, as not to break
+ * existing packaging scripts.
+ */
+ { false, "/etc/Nuand/bladeRF/" },
+ { false, "/usr/share/Nuand/bladeRF/" },
+};
+
+static inline size_t get_home_dir(char *buf, size_t max_len)
+{
+ const char *home;
+
+ home = getenv("HOME");
+ if (home != NULL && strlen(home) > 0 && strlen(home) < max_len) {
+ strncat(buf, home, max_len);
+ } else {
+ const struct passwd *passwd;
+ const uid_t uid = getuid();
+ passwd = getpwuid(uid);
+ strncat(buf, passwd->pw_dir, max_len);
+ }
+
+ return strlen(buf);
+}
+
+static inline size_t get_install_dir(char *buf, size_t max_len)
+{
+ return 0;
+}
+
+#if BLADERF_OS_LINUX
+static inline size_t get_binary_dir(char *buf, size_t max_len)
+{
+ ssize_t result = readlink("/proc/self/exe", buf, max_len);
+
+ if (result > 0) {
+ return strip_last_path_entry(buf, DIR_DELIMETER);
+ } else {
+ return 0;
+ }
+}
+#elif BLADERF_OS_FREEBSD
+static inline size_t get_binary_dir(char *buf, size_t max_len)
+{
+ int mib[4];
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+ ssize_t result = sysctl(mib, 4, buf, &max_len, NULL, 0);
+
+ if (result > 0) {
+ return strip_last_path_entry(buf, DIR_DELIMETER);
+ } else {
+ return 0;
+ }
+}
+#elif BLADERF_OS_OSX
+#include <mach-o/dyld.h>
+static inline size_t get_binary_dir(char *buf, size_t max_len)
+{
+ uint32_t buf_size = max_len;
+ int status = _NSGetExecutablePath(buf, &buf_size);
+
+ if (status == 0) {
+ return strip_last_path_entry(buf, DIR_DELIMETER);
+ } else {
+ return 0;
+ }
+
+}
+#endif
+
+#elif BLADERF_OS_WINDOWS
+#define ACCESS_FILE_EXISTS 0
+#define DIR_DELIMETER '\\'
+#include <shlobj.h>
+
+static const struct search_path_entries search_paths[] = {
+ { false, "" },
+ { true, "/Nuand/bladeRF/" },
+};
+
+static inline size_t get_home_dir(char *buf, size_t max_len)
+{
+ /* Explicitly link to a runtime DLL to get SHGetKnownFolderPath.
+ * This deals with the case where we might not be able to staticly
+ * link it at build time, e.g. mingw32.
+ *
+ * http://msdn.microsoft.com/en-us/library/784bt7z7.aspx
+ */
+ typedef HRESULT (CALLBACK* LPFNSHGKFP_T)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR*);
+ HINSTANCE hDLL; // Handle to DLL
+ LPFNSHGKFP_T lpfnSHGetKnownFolderPath; // Function pointer
+
+ const KNOWNFOLDERID folder_id = FOLDERID_RoamingAppData;
+ PWSTR path;
+ HRESULT status;
+
+ assert(max_len < INT_MAX);
+
+ hDLL = LoadLibrary("Shell32");
+ if (hDLL == NULL) {
+ // DLL couldn't be loaded, bail out.
+ return 0;
+ }
+
+ lpfnSHGetKnownFolderPath = (LPFNSHGKFP_T)GetProcAddress(hDLL, "SHGetKnownFolderPath");
+
+ if (!lpfnSHGetKnownFolderPath) {
+ // Can't find the procedure we want. Free and bail.
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ status = lpfnSHGetKnownFolderPath(&folder_id, 0, NULL, &path);
+ if (status == S_OK) {
+ WideCharToMultiByte(CP_ACP, 0, path, -1, buf, (int)max_len, NULL, NULL);
+ CoTaskMemFree(path);
+ }
+
+ FreeLibrary(hDLL);
+
+ return strlen(buf);
+}
+
+static inline size_t get_binary_dir(char *buf, size_t max_len)
+{
+ DWORD status;
+
+ assert(max_len <= MAXDWORD);
+ status = GetModuleFileName(NULL, buf, (DWORD) max_len);
+
+ if (status > 0) {
+ return strip_last_path_entry(buf, DIR_DELIMETER);
+ } else {
+ return 0;
+ }
+}
+
+static inline size_t get_install_dir(char *buf, size_t max_len)
+{
+ typedef LONG (CALLBACK* LPFNREGOPEN_T)(HKEY, LPCTSTR, DWORD, REGSAM, PHKEY);
+ typedef LONG (CALLBACK* LPFNREGQUERY_T)(HKEY, LPCTSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
+ typedef LONG (CALLBACK* LPFNREGCLOSE_T)(HKEY);
+
+ HINSTANCE hDLL; // Handle to DLL
+ LPFNREGOPEN_T lpfnRegOpenKeyEx; // Function pointer
+ LPFNREGQUERY_T lpfnRegQueryValueEx; // Function pointer
+ LPFNREGCLOSE_T lpfnRegCloseKey; // Function pointer
+ HKEY hk;
+ DWORD len;
+
+ assert(max_len < INT_MAX);
+
+ memset(buf, 0, max_len);
+ hDLL = LoadLibrary("Advapi32");
+ if (hDLL == NULL) {
+ // DLL couldn't be loaded, bail out.
+ return 0;
+ }
+
+ lpfnRegOpenKeyEx = (LPFNREGOPEN_T)GetProcAddress(hDLL, "RegOpenKeyExA");
+ if (!lpfnRegOpenKeyEx) {
+ // Can't find the procedure we want. Free and bail.
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ lpfnRegQueryValueEx = (LPFNREGQUERY_T)GetProcAddress(hDLL, "RegQueryValueExA");
+ if (!lpfnRegQueryValueEx) {
+ // Can't find the procedure we want. Free and bail.
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ lpfnRegCloseKey = (LPFNREGCLOSE_T)GetProcAddress(hDLL, "RegCloseKey");
+ if (!lpfnRegCloseKey) {
+ // Can't find the procedure we want. Free and bail.
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Nuand LLC", 0, KEY_READ, &hk)) {
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ len = (DWORD)max_len;
+ if (RegQueryValueEx(hk, "Path", 0, NULL, (LPBYTE) buf, &len) == ERROR_SUCCESS) {
+ if (len > 0 && len < max_len && buf[len - 1] != '\\')
+ strcat(buf, "\\");
+ } else
+ len = 0;
+
+ if (len) {
+ lpfnRegCloseKey(hk);
+ }
+
+ FreeLibrary(hDLL);
+
+ return len;
+}
+
+#else
+#error "Unknown OS or missing BLADERF_OS_* definition"
+#endif
+
+/* We're not using functions that use the *nix PATH_MAX (which is known to be
+ * problematic), or the WIN32 MAX_PATH. Therefore, we'll just use this
+ * arbitrary, but "sufficiently" large max buffer size for paths */
+#define PATH_MAX_LEN 4096
+
+char *file_find(const char *filename)
+{
+ size_t i, max_len;
+ char *full_path;
+ const char *env_var;
+
+ full_path = calloc(PATH_MAX_LEN+1, 1);
+ if (full_path == NULL) {
+ return NULL;
+ }
+
+ /* Check directory specified by environment variable */
+ env_var = getenv("BLADERF_SEARCH_DIR");
+ if (env_var != NULL) {
+ strncat(full_path, env_var, PATH_MAX_LEN - 1);
+ full_path[strlen(full_path)] = DIR_DELIMETER;
+
+ max_len = PATH_MAX_LEN - strlen(full_path);
+
+ if (max_len >= strlen(filename)) {
+ strncat(full_path, filename, max_len);
+ if (access(full_path, ACCESS_FILE_EXISTS) != -1) {
+ return full_path;
+ }
+ }
+ }
+
+ /* Check the directory containing the currently running binary */
+ memset(full_path, 0, PATH_MAX_LEN);
+ max_len = PATH_MAX_LEN - 1;
+
+ if (get_binary_dir(full_path, max_len) != 0) {
+ max_len -= strlen(full_path);
+
+ if (max_len >= strlen(filename)) {
+ strncat(full_path, filename, max_len);
+ if (access(full_path, ACCESS_FILE_EXISTS) != -1) {
+ return full_path;
+ }
+ } else {
+ log_debug("Skipping search for %s in %s. "
+ "Path would be truncated.\n",
+ filename, full_path);
+ }
+ }
+
+ /* Search our list of pre-determined paths */
+ for (i = 0; i < ARRAY_SIZE(search_paths); i++) {
+ memset(full_path, 0, PATH_MAX_LEN);
+ max_len = PATH_MAX_LEN;
+
+ if (search_paths[i].prepend_home) {
+ const size_t len = get_home_dir(full_path, max_len);
+
+ if (len != 0) {
+ max_len -= len;
+ } else {
+ continue;
+ }
+
+ }
+
+ strncat(full_path, search_paths[i].path, max_len);
+ max_len = PATH_MAX_LEN - strlen(full_path);
+
+ if (max_len >= strlen(filename)) {
+ strncat(full_path, filename, max_len);
+
+ if (access(full_path, ACCESS_FILE_EXISTS) != -1) {
+ return full_path;
+ }
+ } else {
+ log_debug("Skipping search for %s in %s. "
+ "Path would be truncated.\n",
+ filename, full_path);
+ }
+ }
+
+ /* Search the installation directory, if applicable */
+ if (get_install_dir(full_path, PATH_MAX_LEN)) {
+ max_len = PATH_MAX_LEN - strlen(full_path);
+
+ if (max_len >= strlen(filename)) {
+ strncat(full_path, filename, max_len);
+ if (access(full_path, ACCESS_FILE_EXISTS) != -1) {
+ return full_path;
+ }
+ } else {
+ log_debug("Skipping search for %s in %s. "
+ "Path would be truncated.\n",
+ filename, full_path);
+ }
+ }
+
+ free(full_path);
+ return NULL;
+}
diff --git a/Radio/HW/BladeRF/src/helpers/file.h b/Radio/HW/BladeRF/src/helpers/file.h
new file mode 100644
index 0000000..a1db20a
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/file.h
@@ -0,0 +1,97 @@
+/**
+ * @file file.h
+ *
+ * @brief Wrappers around misc. file operations.
+ *
+ * These are collected here so that they may easily be dummied out for NIOS
+ * headless builds in the future.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ * Copyright (C) 2013 Daniel Gröber <dxld ÄT darkboxed DOT org>
+ *
+ * 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 HELPERS_FILE_H_
+#define HELPERS_FILE_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+/**
+ * Read file contents into a buffer allocated internally and returned to the
+ * caller through `buf'.
+ *
+ * The caller is responsible for freeing the allocated buffer
+ *
+ * @param[in] filename File open
+ * @param[out] buf Upon success, this will point to a heap-allocated
+ * buffer containing the file contents
+ * @parma[out] size Upon success, this will be updated to reflect the
+ * size of the buffered file
+ *
+ * @return 0 on success, negative BLADERF_ERR_* value on failure
+ */
+int file_read_buffer(const char *filename, uint8_t **buf, size_t *size);
+
+/**
+ * Write to an open file stream.
+ *
+ * @param[in] f Open file stream.
+ * @param[in] buf Data to write to the stream.
+ * @parma[in] len Number of bytes to write to the stream.
+ *
+ * @return 0 on success, negative BLADERF_ERR_* value on failure
+ */
+int file_write(FILE *f, uint8_t *buf, size_t len);
+
+/**
+ * Read data from an open file stream.
+ *
+ * @param[in] f Open file stream.
+ * @param[out] buf Buffer to fill with data read.
+ * @parma[in] len Number of bytes to read. If EOF is encountered
+ * before this many bytes have been read will return
+ * an error.
+ *
+ * @return 0 on success, negative BLADERF_ERR_* value on failure
+ */
+int file_read(FILE *f, char *buf, size_t len);
+
+/**
+ * Determine the size of an open file stream.
+ *
+ * @param[in] f Open file stream.
+ *
+ * @return positive size of file on success, negative BLADERF_ERR_* value on
+ * failure
+ */
+ssize_t file_size(FILE *f);
+
+/**
+ * Search for the specified filename in bladeRF config directories. If found,
+ * the full path is returned. There is a chance that the file will be removed
+ * in between this call indicating it exists and attempting to open it.
+ *
+ * @param[in] filename File to search for
+ *
+ * @return Full path if the file is found, NULL otherwise.
+ */
+char *file_find(const char *filename);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/have_cap.h b/Radio/HW/BladeRF/src/helpers/have_cap.h
new file mode 100644
index 0000000..f6ed471
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/have_cap.h
@@ -0,0 +1,43 @@
+/**
+ * @file have_cap.h
+ *
+ * @brief Convenience wrapper for testing capabilities mask
+ *
+ * Copyright (C) 2020 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 HAVE_CAP_H_
+#define HAVE_CAP_H_
+
+#include <stdint.h>
+
+/**
+ * Convenience wrapper for testing capabilities mask
+ */
+static inline bool have_cap(uint64_t capabilities, uint64_t capability)
+{
+ return (capabilities & capability) != 0;
+}
+
+/**
+ * Convenience wrapper for testing capabilities mask
+ */
+static inline bool have_cap_dev(struct bladerf *dev, uint64_t capability)
+{
+ uint64_t capabilities = dev->board->get_capabilities(dev);
+ return (capabilities & capability) != 0;
+}
+#endif // HAVE_CAP_H_
diff --git a/Radio/HW/BladeRF/src/helpers/interleave.c b/Radio/HW/BladeRF/src/helpers/interleave.c
new file mode 100644
index 0000000..0989c4c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/interleave.c
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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 <string.h>
+
+#include <libbladeRF.h>
+
+#include "helpers/interleave.h"
+
+size_t _interleave_calc_num_channels(bladerf_channel_layout layout)
+{
+ switch (layout) {
+ case BLADERF_RX_X1:
+ case BLADERF_TX_X1:
+ return 1;
+ case BLADERF_RX_X2:
+ case BLADERF_TX_X2:
+ return 2;
+ }
+
+ return 0;
+}
+
+size_t _interleave_calc_bytes_per_sample(bladerf_format format)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ return 2;
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ return 4;
+ }
+
+ return 0;
+}
+
+size_t _interleave_calc_metadata_bytes(bladerf_format format)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7_META:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ return 0x10;
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC16_Q11:
+ return 0;
+ }
+
+ return 0;
+}
+
+int _interleave_interleave_buf(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples)
+{
+ void *buf;
+ uint8_t *srcptr, *dstptr;
+ size_t num_channels = _interleave_calc_num_channels(layout);
+ size_t samp_size, meta_size, samps_per_ch;
+ size_t srcidx, dstidx, samp, ch;
+
+ // Easy:
+ if (num_channels < 2) {
+ return 0;
+ }
+
+ // Placeholder for an actually efficient algorithm
+ samp_size = _interleave_calc_bytes_per_sample(format);
+ meta_size = _interleave_calc_metadata_bytes(format);
+ samps_per_ch = buffer_size / num_channels;
+ buf = malloc(samp_size * buffer_size);
+ srcptr = samples;
+ dstptr = buf;
+
+ if (NULL == buf) {
+ return BLADERF_ERR_MEM;
+ }
+
+ // Copy metadata if applicable
+ if (meta_size > 0) {
+ memcpy(dstptr, srcptr, meta_size);
+ srcptr += meta_size;
+ dstptr += meta_size;
+ samps_per_ch -= (meta_size / samp_size / num_channels);
+ }
+
+ // Iterate...
+ for (ch = 0; ch < num_channels; ++ch) {
+ srcidx = samps_per_ch * ch;
+ for (samp = 0; samp < samps_per_ch; ++samp) {
+ dstidx = (samp * num_channels) + ch;
+ memcpy(dstptr + (dstidx * samp_size),
+ srcptr + ((srcidx + samp) * samp_size),
+ samp_size);
+ }
+ }
+
+ // Copy back...
+ memcpy(samples, buf, buffer_size * samp_size);
+
+ // Done
+ free(buf);
+
+ return 0;
+}
+
+int _interleave_deinterleave_buf(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples)
+{
+ void *buf;
+ uint8_t *srcptr, *dstptr;
+ size_t num_channels = _interleave_calc_num_channels(layout);
+ size_t samp_size, meta_size, samps_per_ch;
+ size_t srcidx, dstidx, samp, ch;
+
+ // Easy:
+ if (num_channels < 2) {
+ return 0;
+ }
+
+ // Placeholder for an actually efficient algorithm
+ samp_size = _interleave_calc_bytes_per_sample(format);
+ meta_size = _interleave_calc_metadata_bytes(format);
+ samps_per_ch = buffer_size / num_channels;
+ buf = malloc(samp_size * buffer_size);
+ srcptr = samples;
+ dstptr = buf;
+
+ if (NULL == buf) {
+ return BLADERF_ERR_MEM;
+ }
+
+ // Copy metadata if applicable
+ if (meta_size > 0) {
+ memcpy(dstptr, srcptr, meta_size);
+ srcptr += meta_size;
+ dstptr += meta_size;
+ samps_per_ch -= (meta_size / samp_size / num_channels);
+ }
+
+ // Iterate...
+ for (samp = 0; samp < samps_per_ch; ++samp) {
+ srcidx = num_channels * samp;
+ for (ch = 0; ch < num_channels; ++ch) {
+ dstidx = (samps_per_ch * ch) + samp;
+ memcpy(dstptr + (dstidx * samp_size),
+ srcptr + ((srcidx + ch) * samp_size), samp_size);
+ }
+ }
+
+ // Copy back...
+ memcpy(samples, buf, buffer_size * samp_size);
+
+ // Done
+ free(buf);
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/helpers/interleave.h b/Radio/HW/BladeRF/src/helpers/interleave.h
new file mode 100644
index 0000000..526dc41
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/interleave.h
@@ -0,0 +1,44 @@
+/**
+ * @file interleave.h
+ *
+ * This file is not part of the API and may be changed at any time.
+ * If you're interfacing with libbladeRF, DO NOT use this file.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef HELPERS_INTERLEAVE_H_
+#define HELPERS_INTERLEAVE_H_
+
+size_t _interleave_calc_bytes_per_sample(bladerf_format format);
+size_t _interleave_calc_metadata_bytes(bladerf_format format);
+size_t _interleave_calc_num_channels(bladerf_channel_layout layout);
+
+int _interleave_interleave_buf(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples);
+
+int _interleave_deinterleave_buf(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/timeout.c b/Radio/HW/BladeRF/src/helpers/timeout.c
new file mode 100644
index 0000000..d0f680b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/timeout.c
@@ -0,0 +1,52 @@
+/*
+ * 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 "host_config.h"
+
+#if BLADERF_OS_WINDOWS || BLADERF_OS_OSX
+#include "clock_gettime.h"
+#else
+#include <time.h>
+#endif
+
+#include <libbladeRF.h>
+
+int populate_abs_timeout(struct timespec *t, unsigned int timeout_ms)
+{
+ static const int nsec_per_sec = 1000 * 1000 * 1000;
+ const unsigned int timeout_sec = timeout_ms / 1000;
+ int status;
+
+ status = clock_gettime(CLOCK_REALTIME, t);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ t->tv_sec += timeout_sec;
+ t->tv_nsec += (timeout_ms % 1000) * 1000 * 1000;
+
+ if (t->tv_nsec >= nsec_per_sec) {
+ t->tv_sec += t->tv_nsec / nsec_per_sec;
+ t->tv_nsec %= nsec_per_sec;
+ }
+
+ return 0;
+ }
+}
diff --git a/Radio/HW/BladeRF/src/helpers/timeout.h b/Radio/HW/BladeRF/src/helpers/timeout.h
new file mode 100644
index 0000000..1b6cb7c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/timeout.h
@@ -0,0 +1,40 @@
+/**
+ * @file timeout.h
+ *
+ * This file is not part of the API and may be changed at any time.
+ * If you're interfacing with libbladeRF, DO NOT use this file.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-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 HELPERS_TIMEOUT_H_
+#define HELPERS_TIMEOUT_H_
+
+/**
+ * Populate the provided timeval structure for the specified timeout
+ *
+ * @param[out] t_abs Absolute timeout structure to populate
+ * @param[in] timeout_ms Desired timeout in ms.
+ *
+ * 0 on success, BLADERF_ERR_UNEXPECTED on failure
+ */
+int populate_abs_timeout(struct timespec *t_abs, unsigned int timeout_ms);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/version.c b/Radio/HW/BladeRF/src/helpers/version.c
new file mode 100644
index 0000000..a378d23
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/version.c
@@ -0,0 +1,177 @@
+/**
+ * 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
+ */
+
+#include "log.h"
+#include "rel_assert.h"
+
+#include "helpers/version.h"
+
+bool version_equal(const struct bladerf_version *v1,
+ const struct bladerf_version *v2)
+{
+ return v1->major == v2->major &&
+ v1->minor == v2->minor &&
+ v1->patch == v2->patch;
+}
+
+bool version_greater_or_equal(const struct bladerf_version *v1,
+ const struct bladerf_version *v2)
+{
+ return version_fields_greater_or_equal(v1, v2->major, v2->minor, v2->patch);
+}
+
+bool version_less_than(const struct bladerf_version *v1,
+ const struct bladerf_version *v2)
+{
+ return version_fields_less_than(v1, v2->major, v2->minor, v2->patch);
+}
+
+bool version_fields_greater_or_equal(const struct bladerf_version *version,
+ unsigned int major, unsigned int minor,
+ unsigned int patch)
+{
+ if (version->major > major) {
+ return true;
+ } else if ( (version->major == major) && (version->minor > minor) ) {
+ return true;
+ } else if ((version->major == major) &&
+ (version->minor == minor) &&
+ (version->patch >= patch) ) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool version_fields_less_than(const struct bladerf_version *version,
+ unsigned int major, unsigned int minor,
+ unsigned int patch)
+{
+ return !version_fields_greater_or_equal(version, major, minor, patch);
+}
+
+static const struct compat *find_fw_compat(const struct version_compat_table *fw_compat_table,
+ const struct bladerf_version *fw_version)
+{
+ const struct compat *newest_compat = &fw_compat_table->table[0];
+ size_t i;
+
+ /* Version is newer than what's in our table - complain */
+ if (version_less_than(&newest_compat->ver, fw_version)) {
+ log_info("Firmware version (v%u.%u.%u) is newer than entries in "
+ "libbladeRF's compatibility table. Please update libbladeRF "
+ "if problems arise.\n",
+ fw_version->major, fw_version->minor, fw_version->patch);
+ return newest_compat;
+ }
+
+ for (i = 0; i < fw_compat_table->len; i++) {
+ if (version_equal(fw_version, &fw_compat_table->table[i].ver)) {
+ return &fw_compat_table->table[i];
+ }
+ }
+
+ return NULL;
+}
+
+static const struct compat *find_fpga_compat(const struct version_compat_table *fpga_compat_table,
+ const struct bladerf_version *fpga_version)
+{
+ const struct compat *newest_compat = &fpga_compat_table->table[0];
+ size_t i;
+
+ /* Device's FPGA is newer than what's in our table - complain */
+ if (version_less_than(&newest_compat->ver, fpga_version)) {
+ log_info("FPGA version (v%u.%u.%u) is newer than entries in "
+ "libbladeRF's compatibility table. Please update libbladeRF "
+ "if problems arise.\n",
+ fpga_version->major, fpga_version->minor, fpga_version->patch);
+ return newest_compat;
+ }
+
+ for (i = 0; i < fpga_compat_table->len; i++) {
+ if (version_equal(fpga_version, &fpga_compat_table->table[i].ver)) {
+ return &fpga_compat_table->table[i];
+ }
+ }
+
+ return NULL;
+}
+
+int version_check_fw(const struct version_compat_table *fw_compat_table,
+ const struct bladerf_version *fw_version,
+ struct bladerf_version *required_fw_version)
+{
+ const struct bladerf_version *oldest_version = &fw_compat_table->table[fw_compat_table->len-1].ver;
+
+ if (required_fw_version) {
+ *required_fw_version = *oldest_version;
+ }
+
+ if (version_greater_or_equal(fw_version, oldest_version)) {
+ return 0;
+ }
+
+ return BLADERF_ERR_UPDATE_FW;
+}
+
+int version_check(const struct version_compat_table *fw_compat_table,
+ const struct version_compat_table *fpga_compat_table,
+ const struct bladerf_version *fw_version,
+ const struct bladerf_version *fpga_version,
+ struct bladerf_version *required_fw_version,
+ struct bladerf_version *required_fpga_version)
+{
+ const struct compat *fw_compat = find_fw_compat(fw_compat_table, fw_version);
+ const struct compat *fpga_compat = find_fpga_compat(fpga_compat_table, fpga_version);
+
+ if (fw_compat == NULL) {
+ log_debug("%s is missing FW version compat table entry?\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UPDATE_FW;
+ } else if (fpga_compat == NULL) {
+ log_debug("%s is missing FPGA version compat table entry?\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UPDATE_FPGA;
+ }
+
+ if (required_fw_version) {
+ *required_fw_version = fpga_compat->requires;
+ }
+ if (required_fpga_version) {
+ *required_fpga_version = fw_compat->requires;
+ }
+
+ /* Check if the FPGA meets the minimum requirements dictated by the
+ * firmware version */
+ if (version_less_than(fpga_version, &fw_compat->requires)) {
+ return BLADERF_ERR_UPDATE_FPGA;
+ }
+
+ /* Check if the firmware version meets the minimum requirements dictated
+ * by the FPGA version */
+ if (version_less_than(fw_version, &fpga_compat->requires)) {
+ return BLADERF_ERR_UPDATE_FW;
+ }
+
+ return 0;
+}
+
diff --git a/Radio/HW/BladeRF/src/helpers/version.h b/Radio/HW/BladeRF/src/helpers/version.h
new file mode 100644
index 0000000..b4e4559
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/version.h
@@ -0,0 +1,142 @@
+/**
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 HELPERS_VERSION_H_
+#define HELPERS_VERSION_H_
+
+#include <libbladeRF.h>
+
+#define BLADERF_VERSION_STR_MAX 32
+
+/**
+ * Test if two versions are equal. The "describe" field is not checked.
+ *
+ * @return true if equal, false otherwise
+ */
+bool version_equal(const struct bladerf_version *v1,
+ const struct bladerf_version *v2);
+
+/**
+ * Check if version v1 is greater than or equal to version v2.
+ *
+ * @param[in] v1 First version
+ * @param[in] v2 Second version
+ *
+ * @return true for greater or equal than, false otherwise
+ */
+bool version_greater_or_equal(const struct bladerf_version *v1,
+ const struct bladerf_version *v2);
+
+/**
+ * Check if version v1 is less than version v2.
+ *
+ * @param[in] v1 First version
+ * @param[in] v2 Second version
+ *
+ * @return true for less than, false otherwise
+ */
+bool version_less_than(const struct bladerf_version *v1,
+ const struct bladerf_version *v2);
+
+/**
+ * Check if version in the provided structure is greater or equal to
+ * the version specified by the provided major, minor, and patch values
+ *
+ * @param[in] version Version structure to check
+ * @param[in] major Minor version
+ * @param[in] minor Minor version
+ * @param[in] patch Patch version
+ *
+ * @return true for greater or equal than, false otherwise
+ */
+bool version_fields_greater_or_equal(const struct bladerf_version *version,
+ unsigned int major,
+ unsigned int minor,
+ unsigned int patch);
+
+/**
+ * Check if version in the provided structure is less than
+ * the version specied by the provided major, minor, and patch values
+ *
+ * @param[in] version Version structure to check
+ * @param[in] major Minor version
+ * @param[in] minor Minor version
+ * @param[in] patch Patch version
+ *
+ * @return true for less than, false otherwise
+ */
+bool version_fields_less_than(const struct bladerf_version *version,
+ unsigned int major,
+ unsigned int minor,
+ unsigned int patch);
+
+
+/* Version compatibility table structure. */
+struct version_compat_table {
+ const struct compat {
+ struct bladerf_version ver;
+ struct bladerf_version requires;
+ } * table;
+ unsigned int len;
+};
+
+/**
+ * Check if the firmware version is sufficient for the current libbladeRF
+ * version. If it's not, the user will need to use the bootloader to flash a
+ * newer version.
+ *
+ * @param[in] fw_compat_table Firmware-FPGA version compat. table
+ * @param[in] fw_version Firmware version
+ * @param[out] required_fw_version If not-NULL, populated with minimum
+ * required firmware version
+ *
+ * @return 0 if the FW version is sufficient for normal operation,
+ * BLADERF_ERR_UPDATE_FW if a firmware update is required.
+ */
+int version_check_fw(const struct version_compat_table *fw_compat_table,
+ const struct bladerf_version *fw_version,
+ struct bladerf_version *required_fw_version);
+
+/**
+ * Check if the firmware and FPGA versions are sufficient and compatible.
+ *
+ * @param[in] fw_compat_table Firmware-FPGA version compat. table
+ * @param[in] fpga_compat_table FPGA-Firmware version compat. table
+ * @param[in] fw_version Firmware version
+ * @param[in] fpga_version Firmware version
+ * @param[out] required_fw_version If not-NULL, populated with minimum
+ * required firmware version
+ * @param[out] required_fpga_version If not-NULL, populated with minimum
+ * required FPGA version
+ *
+ * @return 0 if the FPGA version is sufficient for normal operation,
+ * BLADERF_ERR_UPDATE_FPGA if the firmware requires a newer FPGA,
+ * BLADERF_ERR_UPDATE_FW if a firmware update is required to support
+ * the currently loaded FPGA.
+ */
+int version_check(const struct version_compat_table *fw_compat_table,
+ const struct version_compat_table *fpga_compat_table,
+ const struct bladerf_version *fw_version,
+ const struct bladerf_version *fpga_version,
+ struct bladerf_version *required_fw_version,
+ struct bladerf_version *required_fpga_version);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/wallclock.c b/Radio/HW/BladeRF/src/helpers/wallclock.c
new file mode 100644
index 0000000..c75ae1e
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/wallclock.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2018 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 "wallclock.h"
+#include "host_config.h"
+
+#if BLADERF_OS_WINDOWS || BLADERF_OS_OSX
+#include "clock_gettime.h"
+#else
+#include <time.h>
+#endif
+
+uint64_t wallclock_get_current_nsec()
+{
+ static const int nsec_per_sec = 1000 * 1000 * 1000;
+ struct timespec t;
+ int status;
+ uint64_t rv;
+
+ status = clock_gettime(CLOCK_REALTIME, &t);
+ if (status != 0) {
+ rv = 0;
+ } else {
+ rv = (t.tv_sec * nsec_per_sec);
+ rv += (t.tv_nsec);
+ }
+
+ return rv;
+}
diff --git a/Radio/HW/BladeRF/src/helpers/wallclock.h b/Radio/HW/BladeRF/src/helpers/wallclock.h
new file mode 100644
index 0000000..d6fcbb9
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/wallclock.h
@@ -0,0 +1,29 @@
+/**
+ * @file wallclock.h
+ *
+ * @brief System clock access helpers
+ *
+ * Copyright (C) 2013-2018 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 WALLCLOCK_H_
+#define WALLCLOCK_H_
+
+#include <stdint.h>
+
+uint64_t wallclock_get_current_nsec();
+
+#endif // WALLCLOCK_H_
diff --git a/Radio/HW/BladeRF/src/init_fini.c b/Radio/HW/BladeRF/src/init_fini.c
new file mode 100644
index 0000000..f38eaf5
--- /dev/null
+++ b/Radio/HW/BladeRF/src/init_fini.c
@@ -0,0 +1,103 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include "conversions.h"
+#include "version.h"
+#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED)
+#include <syslog.h>
+#endif
+#include "log.h"
+
+#if !defined(WIN32) && !defined(__CYGWIN__)
+#if !defined(__clang__) && !defined(__GNUC__)
+#error init/fini mechanism not known to work for your compiler.
+#endif
+#define __init __attribute__((constructor))
+#define __fini __attribute__((destructor))
+#else
+/* Corresponding syntax for Windows (TBD) */
+#define __init
+#define __fini
+#endif
+
+#ifdef LOG_SYSLOG_ENABLED
+# define DEF_LOG_LEVEL BLADERF_LOG_LEVEL_WARNING
+#else
+# define DEF_LOG_LEVEL BLADERF_LOG_LEVEL_INFO
+#endif
+#define ENV_LOG_LEVEL "BLADERF_LOG_LEVEL"
+
+/* Module initializers/deinitializers. When used as library (who don't have
+ * a natural entry/exit function) these are used to initialize
+ * deinitialize. Use to set predefined/default states and cleanup.
+ *
+ * This will work with shared libraries as well as with static as they get
+ * invoked by RTL load/unload, with or without C++ code (i.e. functions will
+ * play nice with C++ normal ctors/dtors).
+ *
+ * Keep log in to at least once per new build-/run-environment assert that
+ * the mechanism works.
+ */
+
+static int get_loglevel(void) {
+ int log_level = DEF_LOG_LEVEL;
+
+ if ((getenv(ENV_LOG_LEVEL) != NULL)
+ && (strlen(getenv(ENV_LOG_LEVEL)) > 0)) {
+
+ bool valid_value;
+ log_level = str2loglevel(getenv(ENV_LOG_LEVEL), &valid_value);
+
+ if (!valid_value) {
+ log_level = DEF_LOG_LEVEL;
+ }
+ }
+ return log_level;
+}
+
+void __init __bladerf_init(void)
+{
+ int log_level = get_loglevel();
+
+#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED)
+ openlog("bladeRF",
+ LOG_CONS | LOG_NDELAY | LOG_NOWAIT | LOG_PERROR | LOG_PID,
+ LOG_USER);
+#endif
+
+ bladerf_log_set_verbosity(log_level);
+ log_debug("libbladeRF %s: initializing\n", LIBBLADERF_VERSION);
+}
+
+void __fini __bladerf_fini(void)
+{
+ int log_level = get_loglevel();
+
+ bladerf_log_set_verbosity(log_level);
+ log_debug("libbladeRF %s: deinitializing\n", LIBBLADERF_VERSION);
+ fflush(NULL);
+#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED)
+ closelog();
+#endif
+}
diff --git a/Radio/HW/BladeRF/src/libbladerf-Bridging-Header.h b/Radio/HW/BladeRF/src/libbladerf-Bridging-Header.h
new file mode 100644
index 0000000..a9fb786
--- /dev/null
+++ b/Radio/HW/BladeRF/src/libbladerf-Bridging-Header.h
@@ -0,0 +1,8 @@
+//
+// libbladerf-Bridging-Header.h
+// PrySDR
+//
+// Created by Jacky Jack on 01/11/2024.
+//
+
+#include "../include/libbladeRF.h"
diff --git a/Radio/HW/BladeRF/src/streaming/async.c b/Radio/HW/BladeRF/src/streaming/async.c
new file mode 100644
index 0000000..cffa9d2
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/async.c
@@ -0,0 +1,294 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "log.h"
+
+#include "backend/usb/usb.h"
+
+#include "async.h"
+#include "board/board.h"
+#include "helpers/timeout.h"
+#include "helpers/have_cap.h"
+
+int async_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *user_data)
+{
+ struct bladerf_stream *lstream;
+ size_t buffer_size_bytes;
+ size_t i;
+ int status = 0;
+
+ if (num_transfers > num_buffers) {
+ log_error("num_transfers must be <= num_buffers\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (samples_per_buffer < 1024 || samples_per_buffer % 1024 != 0) {
+ log_error("samples_per_buffer must be multiples of 1024\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Create a stream and populate it with the appropriate information */
+ lstream = malloc(sizeof(struct bladerf_stream));
+
+ if (!lstream) {
+ return BLADERF_ERR_MEM;
+ }
+
+ MUTEX_INIT(&lstream->lock);
+
+ if (pthread_cond_init(&lstream->can_submit_buffer, NULL) != 0) {
+ free(lstream);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (pthread_cond_init(&lstream->stream_started, NULL) != 0) {
+ free(lstream);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ lstream->dev = dev;
+ lstream->error_code = 0;
+ lstream->state = STREAM_IDLE;
+ lstream->samples_per_buffer = samples_per_buffer;
+ lstream->num_buffers = num_buffers;
+ lstream->format = format;
+ lstream->transfer_timeout = BULK_TIMEOUT_MS;
+ lstream->cb = callback;
+ lstream->user_data = user_data;
+ lstream->buffers = NULL;
+
+ if (format == BLADERF_FORMAT_PACKET_META) {
+ if (!have_cap_dev(dev, BLADERF_CAP_FW_SHORT_PACKET)) {
+ log_error("Firmware does not support short packets. "
+ "Upgrade to at least firmware version 2.4.0.");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (!have_cap_dev(dev, BLADERF_CAP_FPGA_PACKET_META)) {
+ log_error("FPGA does not support packet meta format. "
+ "Upgrade to at least FPGA version 0.12.0 .");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ if (!have_cap_dev(dev, BLADERF_CAP_FPGA_8BIT_SAMPLES)) {
+ log_error("FPGA does not support 8bit mode. "
+ "Upgrade to at least FPGA version 0.15.0.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ switch(format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ buffer_size_bytes = sc8q7_to_bytes(samples_per_buffer);
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ buffer_size_bytes = sc16q11_to_bytes(samples_per_buffer);
+ break;
+
+ case BLADERF_FORMAT_PACKET_META:
+ buffer_size_bytes = samples_per_buffer;
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ break;
+ }
+
+ if (!status) {
+ lstream->buffers = calloc(num_buffers, sizeof(lstream->buffers[0]));
+ if (lstream->buffers) {
+ for (i = 0; i < num_buffers && !status; i++) {
+ lstream->buffers[i] = calloc(1, buffer_size_bytes);
+ if (!lstream->buffers[i]) {
+ status = BLADERF_ERR_MEM;
+ }
+ }
+ } else {
+ status = BLADERF_ERR_MEM;
+ }
+ }
+
+ /* Clean up everything we've allocated if we hit any errors */
+ if (status) {
+
+ if (lstream->buffers) {
+ for (i = 0; i < num_buffers; i++) {
+ free(lstream->buffers[i]);
+ }
+
+ free(lstream->buffers);
+ }
+
+ free(lstream);
+ } else {
+ /* Perform any backend-specific stream initialization */
+ status = dev->backend->init_stream(lstream, num_transfers);
+
+ if (status < 0) {
+ async_deinit_stream(lstream);
+ *stream = NULL;
+ } else {
+ /* Update the caller's pointers */
+ *stream = lstream;
+
+ if (buffers) {
+ *buffers = lstream->buffers;
+ }
+ }
+ }
+
+ return status;
+}
+
+int async_set_transfer_timeout(struct bladerf_stream *stream,
+ unsigned int transfer_timeout_ms)
+{
+ MUTEX_LOCK(&stream->lock);
+ stream->transfer_timeout = transfer_timeout_ms;
+ MUTEX_UNLOCK(&stream->lock);
+
+ return 0;
+}
+
+int async_get_transfer_timeout(struct bladerf_stream *stream,
+ unsigned int *transfer_timeout_ms)
+{
+ MUTEX_LOCK(&stream->lock);
+ *transfer_timeout_ms = stream->transfer_timeout;
+ MUTEX_UNLOCK(&stream->lock);
+
+ return 0;
+}
+
+int async_run_stream(struct bladerf_stream *stream, bladerf_channel_layout layout)
+{
+ int status;
+ struct bladerf *dev = stream->dev;
+
+ MUTEX_LOCK(&stream->lock);
+ stream->layout = layout;
+ stream->state = STREAM_RUNNING;
+ pthread_cond_signal(&stream->stream_started);
+ MUTEX_UNLOCK(&stream->lock);
+
+ status = dev->backend->stream(stream, layout);
+
+ /* Backend return value takes precedence over stream error status */
+ return status == 0 ? stream->error_code : status;
+}
+
+int async_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer, size_t *length,
+ unsigned int timeout_ms,
+ bool nonblock)
+{
+ int status = 0;
+ struct timespec timeout_abs;
+
+ MUTEX_LOCK(&stream->lock);
+
+ if (buffer != BLADERF_STREAM_SHUTDOWN) {
+ if (stream->state != STREAM_RUNNING && timeout_ms != 0) {
+ status = populate_abs_timeout(&timeout_abs, timeout_ms);
+ if (status != 0) {
+ log_debug("Failed to populate timeout value\n");
+ goto error;
+ }
+ }
+
+ while (stream->state != STREAM_RUNNING) {
+ log_debug("Buffer submitted while stream's not running. "
+ "Waiting for stream to start.\n");
+
+ if (timeout_ms == 0) {
+ status = pthread_cond_wait(&stream->stream_started,
+ &stream->lock);
+ } else {
+ status = pthread_cond_timedwait(&stream->stream_started,
+ &stream->lock, &timeout_abs);
+ }
+
+ if (status == ETIMEDOUT) {
+ status = BLADERF_ERR_TIMEOUT;
+ log_debug("%s: %u ms timeout expired",
+ __FUNCTION__, timeout_ms);
+ goto error;
+ } else if (status != 0) {
+ status = BLADERF_ERR_UNEXPECTED;
+ goto error;
+ }
+ }
+ }
+
+ status = stream->dev->backend->submit_stream_buffer(stream, buffer,
+ length, timeout_ms, nonblock);
+
+error:
+ MUTEX_UNLOCK(&stream->lock);
+ return status;
+}
+
+void async_deinit_stream(struct bladerf_stream *stream)
+{
+ size_t i;
+
+ if (!stream) {
+ log_debug("%s called with NULL stream\n", __FUNCTION__);
+ return;
+ }
+
+ while(stream->state != STREAM_DONE && stream->state != STREAM_IDLE) {
+ log_verbose( "Stream not done...\n" );
+ usleep(1000000);
+ }
+
+ /* Free up the backend data */
+ stream->dev->backend->deinit_stream(stream);
+
+ /* Free up the buffers */
+ for (i = 0; i < stream->num_buffers; i++) {
+ free(stream->buffers[i]);
+ }
+
+ /* Free up the pointer to the buffers */
+ free(stream->buffers);
+
+ /* Free up the stream itself */
+ free(stream);
+}
+
diff --git a/Radio/HW/BladeRF/src/streaming/async.h b/Radio/HW/BladeRF/src/streaming/async.h
new file mode 100644
index 0000000..b0fd3b8
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/async.h
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 STREAMING_ASYNC_H_
+#define STREAMING_ASYNC_H_
+
+#include <pthread.h>
+
+#include <libbladeRF.h>
+
+#include "thread.h"
+
+#include "format.h"
+
+typedef enum {
+ STREAM_IDLE, /* Idle and initialized */
+ STREAM_RUNNING, /* Currently running */
+ STREAM_SHUTTING_DOWN, /* Currently tearing down.
+ * See bladerf_stream->error_code to determine
+ * whether or not the shutdown was a clean exit
+ * or due to an error. */
+ STREAM_DONE /* Done and deallocated */
+} bladerf_stream_state;
+
+struct bladerf_stream {
+ /* These items are configured in async_init_stream() and should only be
+ * read (NOT MODIFIED) during the execution of the stream */
+ struct bladerf *dev;
+ bladerf_channel_layout layout;
+ bladerf_format format;
+ unsigned int transfer_timeout;
+ bladerf_stream_cb cb;
+ void *user_data;
+ size_t samples_per_buffer;
+ size_t num_buffers;
+ void **buffers;
+
+ MUTEX lock;
+
+ /* The following items must be accessed atomically */
+ int error_code;
+ bladerf_stream_state state;
+ pthread_cond_t can_submit_buffer;
+ pthread_cond_t stream_started;
+ void *backend_data;
+};
+
+/* Get the number of bytes per stream buffer */
+static inline size_t async_stream_buf_bytes(struct bladerf_stream *s)
+{
+ if (s->format == BLADERF_FORMAT_PACKET_META)
+ return s->samples_per_buffer;
+ return samples_to_bytes(s->format, s->samples_per_buffer);
+}
+
+int async_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t buffer_size,
+ size_t num_transfers,
+ void *user_data);
+
+/* Set the transfer timeout. This acquires stream->lock. */
+int async_set_transfer_timeout(struct bladerf_stream *stream,
+ unsigned int transfer_timeout_ms);
+
+/* Get the transfer timeout. This acquires stream->lock. */
+int async_get_transfer_timeout(struct bladerf_stream *stream,
+ unsigned int *transfer_timeout_ms);
+
+/* Backend code is responsible for acquiring stream->lock in their callbacks */
+int async_run_stream(struct bladerf_stream *stream,
+ bladerf_channel_layout layout);
+
+
+/* This function WILL acquire stream->lock before calling backend code.
+ *
+ * If nonblock=true and no transfers are available, this function shall return
+ * BLADERF_ERR_WOULD_BLOCK.
+ */
+int async_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ size_t *length,
+ unsigned int timeout_ms,
+ bool nonblock);
+
+
+void async_deinit_stream(struct bladerf_stream *stream);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/streaming/format.h b/Radio/HW/BladeRF/src/streaming/format.h
new file mode 100644
index 0000000..95cf5da
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/format.h
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 STREAMING_FORMAT_H_
+#define STREAMING_FORMAT_H_
+
+#include "rel_assert.h"
+
+/*
+ * Convert SC8Q8 samples to bytes
+ */
+static inline size_t sc8q7_to_bytes(size_t n_samples)
+{
+ const size_t sample_size = 2 * sizeof(int8_t);
+ assert(n_samples <= (SIZE_MAX / sample_size));
+ return n_samples * sample_size;
+}
+
+/*
+ * Convert bytes to SC8Q8 samples
+ */
+static inline size_t bytes_to_sc8q7(size_t n_bytes)
+{
+ const size_t sample_size = 2 * sizeof(int8_t);
+ assert((n_bytes % sample_size) == 0);
+ return n_bytes / sample_size;
+}
+
+/*
+ * Convert SC16Q11 samples to bytes
+ */
+static inline size_t sc16q11_to_bytes(size_t n_samples)
+{
+ const size_t sample_size = 2 * sizeof(int16_t);
+ assert(n_samples <= (SIZE_MAX / sample_size));
+ return n_samples * sample_size;
+}
+
+/*
+ * Convert bytes to SC16Q11 samples
+ */
+static inline size_t bytes_to_sc16q11(size_t n_bytes)
+{
+ const size_t sample_size = 2 * sizeof(int16_t);
+ assert((n_bytes % sample_size) == 0);
+ return n_bytes / sample_size;
+}
+
+/* Covert samples to bytes based upon the provided format */
+static inline size_t samples_to_bytes(bladerf_format format, size_t n)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ return sc8q7_to_bytes(n);
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ return sc16q11_to_bytes(n);
+
+ case BLADERF_FORMAT_PACKET_META:
+ return n*4;
+
+ default:
+ assert(!"Invalid format");
+ return 0;
+ }
+}
+
+/* Convert bytes to samples based upon the provided format */
+static inline size_t bytes_to_samples(bladerf_format format, size_t n)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ return bytes_to_sc8q7(n);
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ return bytes_to_sc16q11(n);
+
+ case BLADERF_FORMAT_PACKET_META:
+ return (n+3)/4;
+
+ default:
+ assert(!"Invalid format");
+ return 0;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/src/streaming/metadata.h b/Radio/HW/BladeRF/src/streaming/metadata.h
new file mode 100644
index 0000000..e268559
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/metadata.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 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 STREAMING_METADATA_H_
+#define STREAMING_METADATA_H_
+
+/*
+ * Metadata layout
+ * ~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The FPGA handles data in units of "messages." These messages are
+ * 1024 or 2048 bytes for USB 2.0 (Hi-Speed) or USB 3.0 (SuperSpeed),
+ * respectively.
+ *
+ * The first 16 bytes of the message form a header, which includes metadata
+ * for the samples within the message. This header is shown below:
+ *
+ * +-----------------+
+ * 0x00 | Packet length | 2 bytes, Little-endian uint16_t
+ * +-----------------+
+ * 0x02 | Packet flags | 1 byte
+ * +-----------------+
+ * 0x03 | Packet core ID | 1 byte
+ * +-----------------+
+ * 0x04 | Timestamp | 8 bytes, Little-endian uint64_t
+ * +-----------------+
+ * 0x0c | Flags | 4 bytes, Little-endian uint32_t
+ * +-----------------+
+ *
+ * The term "buffer" is used to describe a block of of data received from or
+ * sent to the device. The size of a "buffer" (in bytes) is always a multiple
+ * of the size of a "message." Said another way, a buffer will always evenly
+ * divide into multiple messages. Messages are *not* fragmented across
+ * consecutive buffers.
+ *
+ * +-----------------+ <-. <-.
+ * | header | | |
+ * +-----------------+ | |
+ * | | | |
+ * | samples | | |
+ * | | | |
+ * +-----------------+ | <-+---- message
+ * | header | |
+ * +-----------------+ |
+ * | | |
+ * | samples | |
+ * | | |
+ * +-----------------+ |
+ * | header | |
+ * +-----------------+ |
+ * | | |
+ * | samples | |
+ * | | |
+ * +-----------------+ |
+ * | header | |
+ * +-----------------+ |
+ * | | |
+ * | samples | |
+ * | | |
+ * +-----------------+ <-+---------- buffer
+ *
+ *
+ * When intentionally transmitting discontinuous groups of samples (such
+ * as bursts), it is important that the last two samples within a message
+ * be (0 + 0j). Otherwise, the DAC will not properly hold its output
+ * at (0 + 0j) for the duration of the discontinuity.
+ */
+
+/* Components of the metadata header */
+#define METADATA_RESV_SIZE (sizeof(uint32_t))
+#define METADATA_TIMESTAMP_SIZE (sizeof(uint64_t))
+#define METADATA_FLAGS_SIZE (sizeof(uint32_t))
+#define METADATA_PACKET_LEN_SIZE (sizeof(uint16_t))
+#define METADATA_PACKET_CORE_SIZE (sizeof(uint8_t))
+#define METADATA_PACKET_FLAGS_SIZE (sizeof(uint8_t))
+
+#define METADATA_RESV_OFFSET 0
+#define METADATA_PACKET_LEN_OFFSET 0
+#define METADATA_PACKET_FLAGS_OFFSET 2
+#define METADATA_PACKET_CORE_OFFSET 3
+#define METADATA_TIMESTAMP_OFFSET (METADATA_RESV_SIZE)
+#define METADATA_FLAGS_OFFSET \
+ (METADATA_TIMESTAMP_OFFSET + METADATA_TIMESTAMP_SIZE)
+
+#define METADATA_HEADER_SIZE (METADATA_FLAGS_OFFSET + METADATA_FLAGS_SIZE)
+
+static inline uint64_t metadata_get_timestamp(const uint8_t *header)
+{
+ uint64_t ret;
+ assert(sizeof(ret) == METADATA_TIMESTAMP_SIZE);
+ memcpy(&ret, &header[METADATA_TIMESTAMP_OFFSET], METADATA_TIMESTAMP_SIZE);
+
+ ret = LE64_TO_HOST(ret);
+
+ return ret;
+}
+
+static inline uint32_t metadata_get_flags(const uint8_t *header)
+{
+ uint32_t ret;
+ assert(sizeof(ret) == METADATA_FLAGS_SIZE);
+ memcpy(&ret, &header[METADATA_FLAGS_OFFSET], METADATA_FLAGS_SIZE);
+ return LE32_TO_HOST(ret);
+}
+
+static inline uint16_t metadata_get_packet_len(const uint8_t *header)
+{
+ uint16_t ret;
+ assert(sizeof(ret) == METADATA_PACKET_LEN_SIZE);
+ memcpy(&ret, &header[METADATA_PACKET_LEN_OFFSET], METADATA_PACKET_LEN_SIZE);
+ return LE16_TO_HOST(ret);
+}
+
+static inline uint8_t metadata_get_packet_core(const uint8_t *header)
+{
+ uint8_t ret;
+ assert(sizeof(ret) == METADATA_PACKET_CORE_SIZE);
+ memcpy(&ret, &header[METADATA_PACKET_CORE_OFFSET], METADATA_PACKET_CORE_SIZE);
+ return ret;
+}
+
+static inline uint8_t metadata_get_packet_flags(const uint8_t *header)
+{
+ uint8_t ret;
+ assert(sizeof(ret) == METADATA_PACKET_FLAGS_SIZE);
+ memcpy(&ret, &header[METADATA_PACKET_FLAGS_OFFSET], METADATA_PACKET_FLAGS_SIZE);
+ return ret;
+}
+
+static inline void metadata_set_packet(uint8_t *header,
+ uint64_t timestamp,
+ uint32_t flags,
+ uint16_t length,
+ uint8_t core,
+ uint8_t pkt_flags)
+{
+ timestamp = HOST_TO_LE64(timestamp);
+
+ flags = HOST_TO_LE32(flags);
+
+ length = HOST_TO_LE16(length);
+
+ assert(sizeof(timestamp) == METADATA_TIMESTAMP_SIZE);
+ assert(sizeof(flags) == METADATA_FLAGS_SIZE);
+
+ memset(&header[METADATA_RESV_OFFSET], 0, METADATA_RESV_SIZE);
+
+ memcpy(&header[METADATA_PACKET_LEN_OFFSET], &length, METADATA_PACKET_LEN_SIZE);
+ memcpy(&header[METADATA_PACKET_CORE_OFFSET], &core, METADATA_PACKET_CORE_SIZE);
+ memcpy(&header[METADATA_PACKET_FLAGS_OFFSET], &pkt_flags, METADATA_PACKET_FLAGS_SIZE);
+
+ memcpy(&header[METADATA_TIMESTAMP_OFFSET], &timestamp,
+ METADATA_TIMESTAMP_SIZE);
+
+ memcpy(&header[METADATA_FLAGS_OFFSET], &flags, METADATA_FLAGS_SIZE);
+}
+
+static inline void metadata_set(uint8_t *header,
+ uint64_t timestamp,
+ uint32_t flags)
+{
+ metadata_set_packet(header, timestamp, flags, 0, 0, 0);
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/src/streaming/sync.c b/Radio/HW/BladeRF/src/streaming/sync.c
new file mode 100644
index 0000000..feef101
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/sync.c
@@ -0,0 +1,1339 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+/* Only switch on the verbose debug prints in this file when we *really* want
+ * them. Otherwise, compile them out to avoid excessive log level checks
+ * in our data path */
+#include "log.h"
+#ifndef ENABLE_LIBBLADERF_SYNC_LOG_VERBOSE
+#undef log_verbose
+#define log_verbose(...)
+#endif
+#include "minmax.h"
+#include "rel_assert.h"
+
+#include "async.h"
+#include "sync.h"
+#include "sync_worker.h"
+#include "metadata.h"
+
+#include "board/board.h"
+#include "helpers/timeout.h"
+#include "helpers/have_cap.h"
+
+#ifdef ENABLE_LIBBLADERF_SYNC_LOG_VERBOSE
+static inline void dump_buf_states(struct bladerf_sync *s)
+{
+ static char *out = NULL;
+ struct buffer_mgmt *b = &s->buf_mgmt;
+ char *statestr = "UNKNOWN";
+
+ if (out == NULL) {
+ out = malloc((b->num_buffers + 1) * sizeof(char));
+ }
+
+ if (out == NULL) {
+ log_verbose("%s: malloc failed\n");
+ return;
+ }
+
+ out[b->num_buffers] = '\0';
+
+ for (size_t i = 0; i < b->num_buffers; ++i) {
+ switch (b->status[i]) {
+ case SYNC_BUFFER_EMPTY:
+ out[i] = '_';
+ break;
+ case SYNC_BUFFER_IN_FLIGHT:
+ out[i] = '-';
+ break;
+ case SYNC_BUFFER_FULL:
+ out[i] = '*';
+ break;
+ case SYNC_BUFFER_PARTIAL:
+ out[i] = 'o';
+ break;
+ }
+ }
+
+ switch (s->state) {
+ case SYNC_STATE_BUFFER_READY:
+ statestr = "BUFFER_READY";
+ break;
+ case SYNC_STATE_CHECK_WORKER:
+ statestr = "CHECK_WORKER";
+ break;
+ case SYNC_STATE_RESET_BUF_MGMT:
+ statestr = "RESET_BUF_MGMT";
+ break;
+ case SYNC_STATE_START_WORKER:
+ statestr = "START_WORKER";
+ break;
+ case SYNC_STATE_USING_BUFFER:
+ statestr = "USING_BUFFER";
+ break;
+ case SYNC_STATE_USING_BUFFER_META:
+ statestr = "USING_BUFFER_META";
+ break;
+ case SYNC_STATE_USING_PACKET_META:
+ statestr = "USING_PACKET_META";
+ break;
+ case SYNC_STATE_WAIT_FOR_BUFFER:
+ statestr = "WAIT_FOR_BUFFER";
+ break;
+ }
+
+ log_verbose("%s: %s (%s)\n", __FUNCTION__, out, statestr);
+}
+#else
+#define dump_buf_states(...)
+#endif // ENABLE_LIBBLADERF_SYNC_LOG_VERBOSE
+
+static inline size_t samples2bytes(struct bladerf_sync *s, size_t n) {
+ return s->stream_config.bytes_per_sample * n;
+}
+
+static inline unsigned int msg_per_buf(size_t msg_size, size_t buf_size,
+ size_t bytes_per_sample)
+{
+ size_t n = buf_size / (msg_size / bytes_per_sample);
+ assert(n <= UINT_MAX);
+ return (unsigned int) n;
+}
+
+static inline unsigned int samples_per_msg(size_t msg_size,
+ size_t bytes_per_sample)
+{
+ size_t n = (msg_size - METADATA_HEADER_SIZE) / bytes_per_sample;
+ assert(n <= UINT_MAX);
+ return (unsigned int) n;
+}
+
+int sync_init(struct bladerf_sync *sync,
+ struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ size_t buffer_size,
+ size_t msg_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout)
+
+{
+ int status = 0;
+ size_t i, bytes_per_sample;
+
+ if (num_transfers >= num_buffers) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (format == BLADERF_FORMAT_PACKET_META) {
+ if (!have_cap_dev(dev, BLADERF_CAP_FW_SHORT_PACKET)) {
+ log_error("Firmware does not support short packets. "
+ "Upgrade to at least firmware version 2.4.0.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (!have_cap_dev(dev, BLADERF_CAP_FPGA_PACKET_META)) {
+ log_error("FPGA does not support packet meta format. "
+ "Upgrade to at least FPGA version 0.12.0.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ if (!have_cap_dev(dev, BLADERF_CAP_FPGA_8BIT_SAMPLES)) {
+ log_error("FPGA does not support 8bit mode. "
+ "Upgrade to at least FPGA version 0.15.0.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ bytes_per_sample = 2;
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ bytes_per_sample = 4;
+ break;
+
+ default:
+ log_debug("Invalid format value: %d\n", format);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* bladeRF GPIF DMA requirement */
+ if ((bytes_per_sample * buffer_size) % 4096 != 0) {
+ assert(!"Invalid buffer size");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Deinitialize sync handle if it's initialized */
+ sync_deinit(sync);
+
+ MUTEX_INIT(&sync->lock);
+
+ switch (layout & BLADERF_DIRECTION_MASK) {
+ case BLADERF_TX:
+ sync->buf_mgmt.submitter = SYNC_TX_SUBMITTER_FN;
+ break;
+ case BLADERF_RX:
+ sync->buf_mgmt.submitter = SYNC_TX_SUBMITTER_INVALID;
+ break;
+ }
+
+ sync->dev = dev;
+ sync->state = SYNC_STATE_CHECK_WORKER;
+
+ sync->buf_mgmt.num_buffers = num_buffers;
+ sync->buf_mgmt.resubmit_count = 0;
+
+ sync->stream_config.layout = layout;
+ sync->stream_config.format = format;
+ sync->stream_config.samples_per_buffer = (unsigned int)buffer_size;
+ sync->stream_config.num_xfers = num_transfers;
+ sync->stream_config.timeout_ms = stream_timeout;
+ sync->stream_config.bytes_per_sample = bytes_per_sample;
+
+ sync->meta.state = SYNC_META_STATE_HEADER;
+ sync->meta.msg_size = msg_size;
+ sync->meta.msg_per_buf = msg_per_buf(msg_size, buffer_size, bytes_per_sample);
+ sync->meta.samples_per_msg = samples_per_msg(msg_size, bytes_per_sample);
+ sync->meta.samples_per_ts = (layout == BLADERF_RX_X2 || layout == BLADERF_TX_X2) ? 2:1;
+
+ log_verbose("%s: Buffer size (in bytes): %u\n",
+ __FUNCTION__, buffer_size * bytes_per_sample);
+
+ log_verbose("%s: Buffer size (in samples): %u\n",
+ __FUNCTION__, buffer_size);
+
+ log_verbose("%s: Msg per buffer: %u\n",
+ __FUNCTION__, sync->meta.msg_per_buf);
+
+ log_verbose("%s: Samples per msg: %u\n",
+ __FUNCTION__, sync->meta.samples_per_msg);
+
+ MUTEX_INIT(&sync->buf_mgmt.lock);
+ pthread_cond_init(&sync->buf_mgmt.buf_ready, NULL);
+
+ sync->buf_mgmt.status = (sync_buffer_status*) malloc(num_buffers * sizeof(sync_buffer_status));
+ if (sync->buf_mgmt.status == NULL) {
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ sync->buf_mgmt.actual_lengths = (size_t *) malloc(num_buffers * sizeof(size_t));
+ if (sync->buf_mgmt.actual_lengths == NULL) {
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ switch (layout & BLADERF_DIRECTION_MASK) {
+ case BLADERF_RX:
+ /* When starting up an RX stream, the first 'num_transfers'
+ * transfers will be submitted to the USB layer to grab data */
+ sync->buf_mgmt.prod_i = num_transfers;
+ sync->buf_mgmt.cons_i = 0;
+ sync->buf_mgmt.partial_off = 0;
+
+ for (i = 0; i < num_buffers; i++) {
+ if (i < num_transfers) {
+ sync->buf_mgmt.status[i] = SYNC_BUFFER_IN_FLIGHT;
+ } else {
+ sync->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY;
+ }
+ }
+
+ sync->meta.msg_timestamp = 0;
+ sync->meta.msg_flags = 0;
+
+ break;
+
+ case BLADERF_TX:
+ sync->buf_mgmt.prod_i = 0;
+ sync->buf_mgmt.cons_i = BUFFER_MGMT_INVALID_INDEX;
+ sync->buf_mgmt.partial_off = 0;
+
+ for (i = 0; i < num_buffers; i++) {
+ sync->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY;
+ }
+
+ sync->meta.msg_timestamp = 0;
+ sync->meta.in_burst = false;
+ sync->meta.now = false;
+ break;
+ }
+
+ status = sync_worker_init(sync);
+ if (status < 0) {
+ goto error;
+ }
+
+ sync->initialized = true;
+
+ return 0;
+
+error:
+ sync_deinit(sync);
+ return status;
+}
+
+void sync_deinit(struct bladerf_sync *sync)
+{
+ if (sync->initialized) {
+ if ((sync->stream_config.layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ async_submit_stream_buffer(sync->worker->stream,
+ BLADERF_STREAM_SHUTDOWN, NULL, 0, false);
+ }
+
+ sync_worker_deinit(sync->worker, &sync->buf_mgmt.lock,
+ &sync->buf_mgmt.buf_ready);
+
+ if (sync->buf_mgmt.actual_lengths) {
+ free(sync->buf_mgmt.actual_lengths);
+ }
+ /* De-allocate our buffer management resources */
+ if (sync->buf_mgmt.status) {
+ MUTEX_DESTROY(&sync->buf_mgmt.lock);
+ free(sync->buf_mgmt.status);
+ }
+
+ MUTEX_DESTROY(&sync->lock);
+
+ sync->initialized = false;
+ }
+}
+
+static int wait_for_buffer(struct buffer_mgmt *b,
+ unsigned int timeout_ms,
+ const char *dbg_name,
+ unsigned int dbg_idx)
+{
+ int status;
+ struct timespec timeout;
+
+ if (timeout_ms == 0) {
+ log_verbose("%s: Infinite wait for buffer[%d] (status: %d).\n",
+ dbg_name, dbg_idx, b->status[dbg_idx]);
+ status = pthread_cond_wait(&b->buf_ready, &b->lock);
+ } else {
+ log_verbose("%s: Timed wait for buffer[%d] (status: %d).\n", dbg_name,
+ dbg_idx, b->status[dbg_idx]);
+ status = populate_abs_timeout(&timeout, timeout_ms);
+ if (status == 0) {
+ status = pthread_cond_timedwait(&b->buf_ready, &b->lock, &timeout);
+ }
+ }
+
+ if (status == ETIMEDOUT) {
+ log_error("%s: Timed out waiting for buf_ready after %d ms\n",
+ __FUNCTION__, timeout_ms);
+ status = BLADERF_ERR_TIMEOUT;
+ } else if (status != 0) {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+#ifndef SYNC_WORKER_START_TIMEOUT_MS
+# define SYNC_WORKER_START_TIMEOUT_MS 250
+#endif
+
+/* Returns # of timestamps (or time steps) left in a message */
+static inline unsigned int ts_remaining(struct bladerf_sync *s)
+{
+ size_t ret = s->meta.samples_per_msg / s->meta.samples_per_ts - s->meta.curr_msg_off;
+ assert(ret <= UINT_MAX);
+
+ return (unsigned int) ret;
+}
+
+/* Returns # of samples left in a message (SC16Q11 mode only) */
+static inline unsigned int left_in_msg(struct bladerf_sync *s)
+{
+ size_t ret = s->meta.samples_per_msg - s->meta.curr_msg_off;
+ assert(ret <= UINT_MAX);
+
+ return (unsigned int) ret;
+}
+
+static inline void advance_rx_buffer(struct buffer_mgmt *b)
+{
+ log_verbose("%s: Marking buf[%u] empty.\n", __FUNCTION__, b->cons_i);
+
+ b->status[b->cons_i] = SYNC_BUFFER_EMPTY;
+ b->cons_i = (b->cons_i + 1) % b->num_buffers;
+}
+
+static inline unsigned int timestamp_to_msg(struct bladerf_sync *s, uint64_t t)
+{
+ uint64_t m = t / s->meta.samples_per_msg;
+ assert(m <= UINT_MAX);
+ return (unsigned int) m;
+}
+
+int sync_rx(struct bladerf_sync *s, void *samples, unsigned num_samples,
+ struct bladerf_metadata *user_meta, unsigned int timeout_ms)
+{
+ struct buffer_mgmt *b;
+
+ int status = 0;
+ bool exit_early = false;
+ bool copied_data = false;
+ unsigned int samples_returned = 0;
+ uint8_t *samples_dest = (uint8_t*)samples;
+ uint8_t *buf_src = NULL;
+ unsigned int samples_to_copy = 0;
+ unsigned int samples_per_buffer = 0;
+ uint64_t target_timestamp = UINT64_MAX;
+ unsigned int pkt_len_dwords = 0;
+
+ if (s == NULL || samples == NULL) {
+ log_debug("NULL pointer passed to %s\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (!s->initialized) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (num_samples % s->meta.samples_per_ts != 0) {
+ log_debug("%s: %u samples %% %u channels != 0\n",
+ __FUNCTION__, num_samples, s->meta.samples_per_ts);
+ return BLADERF_ERR_INVAL;
+ }
+
+ MUTEX_LOCK(&s->lock);
+
+ if (s->stream_config.format == BLADERF_FORMAT_SC16_Q11_META ||
+ s->stream_config.format == BLADERF_FORMAT_SC8_Q7_META ||
+ s->stream_config.format == BLADERF_FORMAT_PACKET_META) {
+ if (user_meta == NULL) {
+ log_debug("NULL metadata pointer passed to %s\n", __FUNCTION__);
+ status = BLADERF_ERR_INVAL;
+ goto out;
+ } else {
+ user_meta->status = 0;
+ target_timestamp = user_meta->timestamp;
+ }
+ }
+
+ b = &s->buf_mgmt;
+ samples_per_buffer = s->stream_config.samples_per_buffer;
+
+ log_verbose("%s: Requests %u samples.\n", __FUNCTION__, num_samples);
+
+ while (!exit_early && samples_returned < num_samples && status == 0) {
+ dump_buf_states(s);
+
+ switch (s->state) {
+ case SYNC_STATE_CHECK_WORKER: {
+ int stream_error;
+ sync_worker_state worker_state =
+ sync_worker_get_state(s->worker, &stream_error);
+
+ /* Propagate stream error back to the caller.
+ * They can call this function again to restart the stream and
+ * try again.
+ */
+ if (stream_error != 0) {
+ status = stream_error;
+ } else {
+ if (worker_state == SYNC_WORKER_STATE_IDLE) {
+ log_debug("%s: Worker is idle. Going to reset buf "
+ "mgmt.\n", __FUNCTION__);
+ s->state = SYNC_STATE_RESET_BUF_MGMT;
+ } else if (worker_state == SYNC_WORKER_STATE_RUNNING) {
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("%s: Unexpected worker state=%d\n",
+ __FUNCTION__, worker_state);
+ }
+ }
+
+ break;
+ }
+
+ case SYNC_STATE_RESET_BUF_MGMT:
+ MUTEX_LOCK(&b->lock);
+ /* When the RX stream starts up, it will submit the first T
+ * transfers, so the consumer index must be reset to 0 */
+ b->cons_i = 0;
+ MUTEX_UNLOCK(&b->lock);
+ log_debug("%s: Reset buf_mgmt consumer index\n", __FUNCTION__);
+ s->state = SYNC_STATE_START_WORKER;
+ break;
+
+
+ case SYNC_STATE_START_WORKER:
+ sync_worker_submit_request(s->worker, SYNC_WORKER_START);
+
+ status = sync_worker_wait_for_state(
+ s->worker,
+ SYNC_WORKER_STATE_RUNNING,
+ SYNC_WORKER_START_TIMEOUT_MS);
+
+ if (status == 0) {
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ log_debug("%s: Worker is now running.\n", __FUNCTION__);
+ } else {
+ log_debug("%s: Failed to start worker, (%d)\n",
+ __FUNCTION__, status);
+ }
+ break;
+
+ case SYNC_STATE_WAIT_FOR_BUFFER:
+ MUTEX_LOCK(&b->lock);
+
+ /* Check the buffer state, as the worker may have produced one
+ * since we last queried the status */
+ if (b->status[b->cons_i] == SYNC_BUFFER_FULL) {
+ s->state = SYNC_STATE_BUFFER_READY;
+ log_verbose("%s: buffer %u is ready to consume\n",
+ __FUNCTION__, b->cons_i);
+ } else {
+ status = wait_for_buffer(b, timeout_ms,
+ __FUNCTION__, b->cons_i);
+
+ if (status == 0) {
+ if (b->status[b->cons_i] != SYNC_BUFFER_FULL) {
+ s->state = SYNC_STATE_CHECK_WORKER;
+ } else {
+ s->state = SYNC_STATE_BUFFER_READY;
+ log_verbose("%s: buffer %u is ready to consume\n",
+ __FUNCTION__, b->cons_i);
+ }
+ }
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_BUFFER_READY:
+ MUTEX_LOCK(&b->lock);
+ b->status[b->cons_i] = SYNC_BUFFER_PARTIAL;
+ b->partial_off = 0;
+
+ switch (s->stream_config.format) {
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC8_Q7:
+ s->state = SYNC_STATE_USING_BUFFER;
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ s->state = SYNC_STATE_USING_BUFFER_META;
+ s->meta.curr_msg_off = 0;
+ s->meta.msg_num = 0;
+ break;
+
+ case BLADERF_FORMAT_PACKET_META:
+ s->state = SYNC_STATE_USING_PACKET_META;
+ break;
+
+ default:
+ assert(!"Invalid stream format");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_USING_BUFFER: /* SC16Q11 buffers w/o metadata */
+ MUTEX_LOCK(&b->lock);
+
+ buf_src = (uint8_t*)b->buffers[b->cons_i];
+
+ samples_to_copy = uint_min(num_samples - samples_returned,
+ samples_per_buffer - b->partial_off);
+
+ memcpy(samples_dest + samples2bytes(s, samples_returned),
+ buf_src + samples2bytes(s, b->partial_off),
+ samples2bytes(s, samples_to_copy));
+
+ b->partial_off += samples_to_copy;
+ samples_returned += samples_to_copy;
+
+ log_verbose("%s: Provided %u samples to caller\n",
+ __FUNCTION__, samples_to_copy);
+
+ /* We've finished consuming this buffer and can start looking
+ * for available samples in the next buffer */
+ if (b->partial_off >= samples_per_buffer) {
+
+ /* Check for symptom of out-of-bounds accesses */
+ assert(b->partial_off == samples_per_buffer);
+
+ advance_rx_buffer(b);
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+
+ case SYNC_STATE_USING_BUFFER_META: /* SC16Q11 buffers w/ metadata */
+ MUTEX_LOCK(&b->lock);
+
+ switch (s->meta.state) {
+ case SYNC_META_STATE_HEADER:
+
+ assert(s->meta.msg_num < s->meta.msg_per_buf);
+
+ buf_src = (uint8_t*)b->buffers[b->cons_i];
+
+ s->meta.curr_msg =
+ buf_src + s->meta.msg_size * s->meta.msg_num;
+
+ s->meta.msg_timestamp =
+ metadata_get_timestamp(s->meta.curr_msg);
+
+ s->meta.msg_flags =
+ metadata_get_flags(s->meta.curr_msg);
+
+ user_meta->status |= s->meta.msg_flags &
+ (BLADERF_META_FLAG_RX_HW_UNDERFLOW |
+ BLADERF_META_FLAG_RX_HW_MINIEXP1 |
+ BLADERF_META_FLAG_RX_HW_MINIEXP2);
+
+ s->meta.curr_msg_off = 0;
+
+ /* We've encountered a discontinuity and need to return
+ * what we have so far, setting the status flags */
+ if (copied_data &&
+ s->meta.msg_timestamp != s->meta.curr_timestamp) {
+
+ user_meta->status |= BLADERF_META_STATUS_OVERRUN;
+ exit_early = true;
+ log_debug("Sample discontinuity detected @ "
+ "buffer %u, message %u: Expected t=%llu, "
+ "got t=%llu\n",
+ b->cons_i, s->meta.msg_num,
+ (unsigned long long)s->meta.curr_timestamp,
+ (unsigned long long)s->meta.msg_timestamp);
+
+ } else {
+ log_verbose("Got header for message %u: "
+ "t_new=%u, t_old=%u\n",
+ s->meta.msg_num,
+ s->meta.msg_timestamp,
+ s->meta.curr_timestamp);
+ }
+
+ s->meta.curr_timestamp = s->meta.msg_timestamp;
+ s->meta.state = SYNC_META_STATE_SAMPLES;
+ break;
+
+ case SYNC_META_STATE_SAMPLES:
+ if (!copied_data &&
+ (user_meta->flags & BLADERF_META_FLAG_RX_NOW) == 0 &&
+ target_timestamp < s->meta.curr_timestamp) {
+
+ log_debug("Current timestamp is %llu, "
+ "target=%llu (user=%llu)\n",
+ (unsigned long long)s->meta.curr_timestamp,
+ (unsigned long long)target_timestamp,
+ (unsigned long long)user_meta->timestamp);
+
+ status = BLADERF_ERR_TIME_PAST;
+ } else if ((user_meta->flags & BLADERF_META_FLAG_RX_NOW) ||
+ target_timestamp == s->meta.curr_timestamp) {
+
+ /* Copy the request amount up to the end of a
+ * this message in the current buffer */
+ samples_to_copy =
+ uint_min(num_samples - samples_returned,
+ left_in_msg(s));
+
+ memcpy(samples_dest + samples2bytes(s, samples_returned),
+ s->meta.curr_msg +
+ METADATA_HEADER_SIZE +
+ samples2bytes(s, s->meta.curr_msg_off),
+ samples2bytes(s, samples_to_copy));
+
+ samples_returned += samples_to_copy;
+ s->meta.curr_msg_off += samples_to_copy;
+
+ if (!copied_data &&
+ (user_meta->flags & BLADERF_META_FLAG_RX_NOW)) {
+
+ /* Provide the user with the timestamp at the
+ * first returned sample when the
+ * NOW flag has been provided */
+ user_meta->timestamp = s->meta.curr_timestamp;
+ log_verbose("Updated user meta timestamp with: "
+ "%llu\n", (unsigned long long)
+ user_meta->timestamp);
+ }
+
+ copied_data = true;
+
+ s->meta.curr_timestamp += samples_to_copy / s->meta.samples_per_ts;
+
+ /* We've begun copying samples, so our target will
+ * just keep tracking the current timestamp. */
+ target_timestamp = s->meta.curr_timestamp;
+
+ log_verbose("After copying samples, t=%llu\n",
+ (unsigned long long)s->meta.curr_timestamp);
+
+ if (left_in_msg(s) == 0) {
+ assert(s->meta.curr_msg_off == s->meta.samples_per_msg);
+
+ s->meta.state = SYNC_META_STATE_HEADER;
+ s->meta.msg_num++;
+
+ if (s->meta.msg_num >= s->meta.msg_per_buf) {
+ assert(s->meta.msg_num == s->meta.msg_per_buf);
+ advance_rx_buffer(b);
+ s->meta.msg_num = 0;
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ }
+ }
+
+ } else {
+ const uint64_t time_delta = target_timestamp - s->meta.curr_timestamp;
+ uint64_t samples_left = time_delta * s->meta.samples_per_ts;
+ uint64_t left_in_buffer =
+ (uint64_t) s->meta.samples_per_msg *
+ (s->meta.msg_per_buf - s->meta.msg_num);
+
+ /* Account for current position in buffer */
+ left_in_buffer -= s->meta.curr_msg_off;
+
+ if (samples_left >= left_in_buffer) {
+ /* Discard the remainder of this buffer */
+ advance_rx_buffer(b);
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ s->meta.state = SYNC_META_STATE_HEADER;
+
+ log_verbose("%s: Discarding rest of buffer.\n",
+ __FUNCTION__);
+
+ } else if (time_delta <= ts_remaining(s)) {
+ /* Fast forward within the current message */
+ assert(time_delta <= SIZE_MAX);
+
+ s->meta.curr_msg_off += (size_t)samples_left;
+ s->meta.curr_timestamp += time_delta;
+
+ log_verbose("%s: Seeking within message (t=%llu)\n",
+ __FUNCTION__,
+ s->meta.curr_timestamp);
+ } else {
+ s->meta.state = SYNC_META_STATE_HEADER;
+ s->meta.msg_num += timestamp_to_msg(s, samples_left);
+
+ log_verbose("%s: Seeking to message %u.\n",
+ __FUNCTION__, s->meta.msg_num);
+ }
+ }
+ break;
+
+ default:
+ assert(!"Invalid state");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_USING_PACKET_META: /* Packet buffers w/ metadata */
+ MUTEX_LOCK(&b->lock);
+
+ buf_src = (uint8_t*)b->buffers[b->cons_i];
+
+ pkt_len_dwords = metadata_get_packet_len(buf_src);
+
+ if (pkt_len_dwords > 0) {
+ samples_returned += num_samples;
+ user_meta->actual_count = pkt_len_dwords;
+ memcpy(samples_dest, buf_src + METADATA_HEADER_SIZE, samples2bytes(s, pkt_len_dwords));
+ }
+
+ advance_rx_buffer(b);
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+
+ }
+ }
+
+ if (user_meta && s->stream_config.format != BLADERF_FORMAT_PACKET_META) {
+ user_meta->actual_count = samples_returned;
+ }
+
+out:
+ MUTEX_UNLOCK(&s->lock);
+
+ return status;
+}
+
+/* Assumes buffer lock is held */
+static int advance_tx_buffer(struct bladerf_sync *s, struct buffer_mgmt *b)
+{
+ int status = 0;
+ const unsigned int idx = b->prod_i;
+
+ if (b->submitter == SYNC_TX_SUBMITTER_FN) {
+ /* Mark buffer in flight because we're going to send it out.
+ * This ensures that if the callback fires before this function
+ * completes, its state will be correct. */
+ b->status[idx] = SYNC_BUFFER_IN_FLIGHT;
+
+ /* This call may block and it results in a per-stream lock being held,
+ * so the buffer lock must be dropped.
+ *
+ * A callback may occur in the meantime, but this will not touch the
+ * status for this this buffer, or the producer index.
+ */
+ MUTEX_UNLOCK(&b->lock);
+ size_t len;
+ if (s->stream_config.format == BLADERF_FORMAT_PACKET_META) {
+ len = b->actual_lengths[idx];
+ } else {
+ len = async_stream_buf_bytes(s->worker->stream);
+ }
+ status = async_submit_stream_buffer(s->worker->stream,
+ b->buffers[idx],
+ &len,
+ s->stream_config.timeout_ms,
+ true);
+ MUTEX_LOCK(&b->lock);
+
+ if (status == 0) {
+ log_verbose("%s: buf[%u] submitted.\n",
+ __FUNCTION__, idx);
+
+ } else if (status == BLADERF_ERR_WOULD_BLOCK) {
+ log_verbose("%s: Deferring buf[%u] submission to worker callback.\n",
+ __FUNCTION__, idx);
+
+ /* Mark this buffer as being full of data, but not in flight */
+ b->status[idx] = SYNC_BUFFER_FULL;
+
+ /* Assign callback the duty of submitting deferred buffers,
+ * and use buffer_mgmt.cons_i to denote which it should submit
+ * (i.e., consume). */
+ b->submitter = SYNC_TX_SUBMITTER_CALLBACK;
+ b->cons_i = idx;
+
+ /* This is expected and we are handling it. Don't propagate this
+ * status back up */
+ status = 0;
+ } else {
+ /* Unmark this as being in flight */
+ b->status[idx] = SYNC_BUFFER_FULL;
+
+ log_debug("%s: Failed to submit buf[%u].\n", __FUNCTION__, idx);
+ return status;
+ }
+ } else {
+ /* We are not submitting this buffer; this is deffered to the worker
+ * call back. Just update its state to being full of samples. */
+ b->status[idx] = SYNC_BUFFER_FULL;
+ }
+
+ /* Advance "producer" insertion index. */
+ b->prod_i = (idx + 1) % b->num_buffers;
+
+ /* Determine our next state based upon the state of the next buffer we
+ * want to use. */
+ if (b->status[b->prod_i] == SYNC_BUFFER_EMPTY) {
+ /* Buffer is empty and ready for use */
+ s->state = SYNC_STATE_BUFFER_READY;
+ } else {
+ /* We'll have to wait on this buffer to become ready. First, we'll
+ * verify that the worker is running. */
+ s->state = SYNC_STATE_CHECK_WORKER;
+ }
+
+ return status;
+}
+
+static inline bool timestamp_in_past(struct bladerf_metadata *user_meta,
+ struct bladerf_sync *s)
+{
+ const bool in_past = user_meta->timestamp < s->meta.curr_timestamp;
+
+ if (in_past) {
+ log_debug("Provided timestamp=%"PRIu64" is in past: current=%"PRIu64"\n",
+ user_meta->timestamp, s->meta.curr_timestamp);
+ }
+
+ return in_past;
+}
+
+struct tx_options {
+ bool flush;
+ bool zero_pad;
+};
+
+static inline int handle_tx_parameters(struct bladerf_metadata *user_meta,
+ struct bladerf_sync *s,
+ struct tx_options *options)
+{
+ if (s->stream_config.format == BLADERF_FORMAT_SC16_Q11_META) {
+ if (user_meta == NULL) {
+ log_debug("NULL metadata pointer passed to %s\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (user_meta->flags & BLADERF_META_FLAG_TX_BURST_START) {
+ bool now = user_meta->flags & BLADERF_META_FLAG_TX_NOW;
+
+ if (s->meta.in_burst) {
+ log_debug("%s: BURST_START provided while already in a burst.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (!now && timestamp_in_past(user_meta, s)) {
+ return BLADERF_ERR_TIME_PAST;
+ }
+
+ s->meta.in_burst = true;
+ if (now) {
+ s->meta.now = true;
+ log_verbose("%s: Starting burst \"now\"\n", __FUNCTION__);
+ } else {
+ s->meta.curr_timestamp = user_meta->timestamp;
+ log_verbose("%s: Starting burst @ %llu\n", __FUNCTION__,
+ (unsigned long long)s->meta.curr_timestamp);
+ }
+
+ if (user_meta->flags & BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP) {
+ log_debug("UPDATE_TIMESTAMP ignored; BURST_START flag was used.\n");
+ }
+
+ } else if (user_meta->flags & BLADERF_META_FLAG_TX_NOW) {
+ log_debug("%s: TX_NOW was specified without BURST_START.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (user_meta->flags & BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP) {
+ if (timestamp_in_past(user_meta, s)) {
+ return BLADERF_ERR_TIME_PAST;
+ } else {
+ options->zero_pad = true;
+ }
+ }
+
+ if (user_meta->flags & BLADERF_META_FLAG_TX_BURST_END) {
+ if (s->meta.in_burst) {
+ options->flush = true;
+ } else {
+ log_debug("%s: BURST_END provided while not in a burst.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ user_meta->status = 0;
+ }
+
+ return 0;
+}
+
+int sync_tx(struct bladerf_sync *s,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *user_meta,
+ unsigned int timeout_ms)
+{
+ struct buffer_mgmt *b = NULL;
+
+ int status = 0;
+ unsigned int samples_written = 0;
+ unsigned int samples_to_copy = 0;
+ unsigned int samples_per_buffer = 0;
+ uint8_t const *samples_src = (uint8_t const *)samples;
+ uint8_t *buf_dest = NULL;
+ struct tx_options op = {
+ FIELD_INIT(.flush, false), FIELD_INIT(.zero_pad, false),
+ };
+
+ log_verbose("%s: called for %u samples.\n", __FUNCTION__, num_samples);
+
+ if (s == NULL || samples == NULL || !s->initialized) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ MUTEX_LOCK(&s->lock);
+
+ status = handle_tx_parameters(user_meta, s, &op);
+ if (status != 0) {
+ goto out;
+ }
+
+ b = &s->buf_mgmt;
+ samples_per_buffer = s->stream_config.samples_per_buffer;
+
+ while (status == 0 && ((samples_written < num_samples) || op.flush)) {
+ switch (s->state) {
+ case SYNC_STATE_CHECK_WORKER: {
+ int stream_error;
+ sync_worker_state worker_state =
+ sync_worker_get_state(s->worker, &stream_error);
+
+ if (stream_error != 0) {
+ status = stream_error;
+ } else {
+ if (worker_state == SYNC_WORKER_STATE_IDLE) {
+ /* No need to reset any buffer management for TX since
+ * the TX stream does not submit an initial set of
+ * buffers. Therefore the RESET_BUF_MGMT state is
+ * skipped here. */
+ s->state = SYNC_STATE_START_WORKER;
+ } else {
+ /* Worker is running - continue onto checking for and
+ * potentially waiting for an available buffer */
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ }
+ }
+ break;
+ }
+
+ case SYNC_STATE_RESET_BUF_MGMT:
+ assert(!"Bug");
+ break;
+
+ case SYNC_STATE_START_WORKER:
+ sync_worker_submit_request(s->worker, SYNC_WORKER_START);
+
+ status = sync_worker_wait_for_state(
+ s->worker, SYNC_WORKER_STATE_RUNNING,
+ SYNC_WORKER_START_TIMEOUT_MS);
+
+ if (status == 0) {
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ log_debug("%s: Worker is now running.\n", __FUNCTION__);
+ }
+ break;
+
+ case SYNC_STATE_WAIT_FOR_BUFFER:
+ MUTEX_LOCK(&b->lock);
+
+ /* Check the buffer state, as the worker may have consumed one
+ * since we last queried the status */
+ if (b->status[b->prod_i] == SYNC_BUFFER_EMPTY) {
+ s->state = SYNC_STATE_BUFFER_READY;
+ } else {
+ status =
+ wait_for_buffer(b, timeout_ms, __FUNCTION__, b->prod_i);
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_BUFFER_READY:
+ MUTEX_LOCK(&b->lock);
+ b->status[b->prod_i] = SYNC_BUFFER_PARTIAL;
+ b->partial_off = 0;
+
+ switch (s->stream_config.format) {
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC8_Q7:
+ s->state = SYNC_STATE_USING_BUFFER;
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ s->state = SYNC_STATE_USING_BUFFER_META;
+ s->meta.curr_msg_off = 0;
+ s->meta.msg_num = 0;
+ break;
+
+ case BLADERF_FORMAT_PACKET_META:
+ s->state = SYNC_STATE_USING_PACKET_META;
+ s->meta.curr_msg_off = 0;
+ s->meta.msg_num = 0;
+ break;
+
+ default:
+ assert(!"Invalid stream format");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+
+ case SYNC_STATE_USING_BUFFER:
+ MUTEX_LOCK(&b->lock);
+
+ buf_dest = (uint8_t *)b->buffers[b->prod_i];
+ samples_to_copy = uint_min(num_samples - samples_written,
+ samples_per_buffer - b->partial_off);
+
+ memcpy(buf_dest + samples2bytes(s, b->partial_off),
+ samples_src + samples2bytes(s, samples_written),
+ samples2bytes(s, samples_to_copy));
+
+ b->partial_off += samples_to_copy;
+ samples_written += samples_to_copy;
+
+ log_verbose("%s: Buffered %u samples from caller\n",
+ __FUNCTION__, samples_to_copy);
+
+ if (b->partial_off >= samples_per_buffer) {
+ /* Check for symptom of out-of-bounds accesses */
+ assert(b->partial_off == samples_per_buffer);
+
+ /* Submit buffer and advance to the next one */
+ status = advance_tx_buffer(s, b);
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_USING_BUFFER_META: /* SC16Q11 buffers w/ metadata */
+ MUTEX_LOCK(&b->lock);
+
+ switch (s->meta.state) {
+ case SYNC_META_STATE_HEADER:
+ buf_dest = (uint8_t *)b->buffers[b->prod_i];
+
+ s->meta.curr_msg =
+ buf_dest + s->meta.msg_size * s->meta.msg_num;
+
+ log_verbose("%s: Set curr_msg to: %p (buf @ %p)\n",
+ __FUNCTION__, s->meta.curr_msg, buf_dest);
+
+ s->meta.curr_msg_off = 0;
+
+ if (s->meta.now) {
+ metadata_set(s->meta.curr_msg, 0, 0);
+ } else {
+ metadata_set(s->meta.curr_msg,
+ s->meta.curr_timestamp, 0);
+ }
+
+ s->meta.state = SYNC_META_STATE_SAMPLES;
+
+ log_verbose("%s: Filled in header (t=%llu)\n",
+ __FUNCTION__,
+ (unsigned long long)s->meta.curr_timestamp);
+ break;
+
+ case SYNC_META_STATE_SAMPLES:
+ if (op.zero_pad) {
+ const uint64_t delta =
+ user_meta->timestamp - s->meta.curr_timestamp;
+
+ size_t to_zero;
+
+ log_verbose("%s: User requested zero padding to "
+ "t=%" PRIu64 " (%" PRIu64 " + %" PRIu64
+ ")\n",
+ __FUNCTION__, user_meta->timestamp,
+ s->meta.curr_timestamp, delta);
+
+ if (delta < left_in_msg(s)) {
+ to_zero = (size_t)delta;
+
+ log_verbose("%s: Padded subset of msg "
+ "(%" PRIu64 " samples)\n",
+ __FUNCTION__, (uint64_t)to_zero);
+ } else {
+ to_zero = left_in_msg(s);
+
+ log_verbose("%s: Padded remainder of msg "
+ "(%" PRIu64 " samples)\n",
+ __FUNCTION__, (uint64_t)to_zero);
+ }
+
+ memset(s->meta.curr_msg + METADATA_HEADER_SIZE +
+ samples2bytes(s, s->meta.curr_msg_off),
+ 0, samples2bytes(s, to_zero));
+
+ s->meta.curr_msg_off += to_zero;
+
+ /* If we're going to supply the FPGA with a
+ * discontinuity, it is required that the last three
+ * samples provided be zero in order to hold the
+ * DAC @ (0 + 0j).
+ *
+ * See "Figure 9: TX data interface" in the LMS6002D
+ * data sheet for the register stages that create
+ * this requirement.
+ *
+ * If we're ending a burst with < 3 zeros samples at
+ * the end of the message, we'll need to continue
+ * onto the next message. At this next message,
+ * we'll either encounter the requested timestamp or
+ * zero-fill the message to fulfil this "three zero
+ * sample" requirement, and set the timestamp
+ * appropriately at the following message.
+ */
+ if (to_zero < 3 && left_in_msg(s) == 0) {
+ s->meta.curr_timestamp += to_zero;
+ log_verbose("Ended msg with < 3 zero samples. "
+ "Padding into next message.\n");
+ } else {
+ s->meta.curr_timestamp = user_meta->timestamp;
+ op.zero_pad = false;
+ }
+ }
+
+ samples_to_copy = uint_min(
+ num_samples - samples_written, left_in_msg(s));
+
+ if (samples_to_copy != 0) {
+ /* We have user data to copy into the current
+ * message within the buffer */
+ memcpy(s->meta.curr_msg + METADATA_HEADER_SIZE +
+ samples2bytes(s, s->meta.curr_msg_off),
+ samples_src +
+ samples2bytes(s, samples_written),
+ samples2bytes(s, samples_to_copy));
+
+ s->meta.curr_msg_off += samples_to_copy;
+ if (s->stream_config.layout == BLADERF_TX_X2)
+ s->meta.curr_timestamp += samples_to_copy / 2;
+ else
+ s->meta.curr_timestamp += samples_to_copy;
+
+ samples_written += samples_to_copy;
+
+ log_verbose("%s: Copied %u samples. "
+ "Current message offset is now: %u\n",
+ __FUNCTION__, samples_to_copy,
+ s->meta.curr_msg_off);
+ }
+
+ if (left_in_msg(s) != 0 && op.flush) {
+ /* We're ending this buffer early and need to
+ * flush the remaining samples by setting all
+ * samples in the messages to (0 + 0j) */
+ const unsigned int to_zero = left_in_msg(s);
+
+ const size_t off =
+ METADATA_HEADER_SIZE +
+ samples2bytes(s, s->meta.curr_msg_off);
+
+ /* If we're here, we should have already copied
+ * all requested data to the buffer */
+ assert(num_samples == samples_written);
+
+ memset(s->meta.curr_msg + off, 0,
+ samples2bytes(s, to_zero));
+
+ log_verbose(
+ "%s: Flushed %u samples @ %u (0x%08x)\n",
+ __FUNCTION__, to_zero, s->meta.curr_msg_off,
+ off);
+
+ s->meta.curr_msg_off += to_zero;
+ s->meta.curr_timestamp += to_zero;
+ }
+
+ if (left_in_msg(s) == 0) {
+ s->meta.msg_num++;
+ s->meta.state = SYNC_META_STATE_HEADER;
+
+ log_verbose("%s: Advancing to next message (%u)\n",
+ __FUNCTION__, s->meta.msg_num);
+ }
+
+ if (s->meta.msg_num >= s->meta.msg_per_buf) {
+ assert(s->meta.msg_num == s->meta.msg_per_buf);
+
+ /* Submit buffer of samples for transmission */
+ status = advance_tx_buffer(s, b);
+
+ s->meta.msg_num = 0;
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+
+ /* We want to clear the flush flag if we've written
+ * all of our data, but keep it set if we have more
+ * data and need wrap around to another buffer */
+ op.flush =
+ op.flush && (samples_written != num_samples);
+ }
+
+ break;
+
+ default:
+ assert(!"Invalid state");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_USING_PACKET_META: /* Packet buffers w/ metadata */
+ MUTEX_LOCK(&b->lock);
+
+ buf_dest = (uint8_t *)b->buffers[b->prod_i];
+
+ memcpy(buf_dest + METADATA_HEADER_SIZE, samples_src, num_samples*4);
+
+ b->actual_lengths[b->prod_i] = samples2bytes(s, num_samples) + METADATA_HEADER_SIZE;
+
+ metadata_set_packet(buf_dest, 0, 0, num_samples, 0, 0);
+
+ samples_written = num_samples;
+
+ status = advance_tx_buffer(s, b);
+
+ s->meta.msg_num = 0;
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ }
+ }
+
+ if (status == 0 &&
+ s->stream_config.format == BLADERF_FORMAT_SC16_Q11_META &&
+ (user_meta->flags & BLADERF_META_FLAG_TX_BURST_END)) {
+ s->meta.in_burst = false;
+ s->meta.now = false;
+ }
+
+out:
+ MUTEX_UNLOCK(&s->lock);
+
+ return status;
+}
+
+unsigned int sync_buf2idx(struct buffer_mgmt *b, void *addr)
+{
+ unsigned int i;
+
+ for (i = 0; i < b->num_buffers; i++) {
+ if (b->buffers[i] == addr) {
+ return i;
+ }
+ }
+
+ assert(!"Bug: Buffer not found.");
+
+ /* Assertions are intended to always remain on. If someone turned them
+ * off, do the best we can...complain loudly and clobber a buffer */
+ log_critical("Bug: Buffer not found.");
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/streaming/sync.h b/Radio/HW/BladeRF/src/streaming/sync.h
new file mode 100644
index 0000000..8977791
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/sync.h
@@ -0,0 +1,193 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 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 STREAMING_SYNC_H_
+#define STREAMING_SYNC_H_
+
+#include <limits.h>
+#include <pthread.h>
+
+#include <libbladeRF.h>
+
+#include "thread.h"
+
+/* These parameters are only written during sync_init */
+struct stream_config {
+ bladerf_format format;
+ bladerf_channel_layout layout;
+
+ unsigned int samples_per_buffer;
+ unsigned int num_xfers;
+ unsigned int timeout_ms;
+
+ size_t bytes_per_sample;
+};
+
+typedef enum {
+ SYNC_BUFFER_EMPTY = 0, /**< Buffer contains no data */
+ SYNC_BUFFER_PARTIAL, /**< sync_rx/tx is currently emptying/filling */
+ SYNC_BUFFER_FULL, /**< Buffer is full of data */
+ SYNC_BUFFER_IN_FLIGHT, /**< Currently being transferred */
+} sync_buffer_status;
+
+typedef enum {
+ SYNC_META_STATE_HEADER, /**< Extract the metadata header */
+ SYNC_META_STATE_SAMPLES, /**< Process samples */
+} sync_meta_state;
+
+typedef enum {
+ /** Invalid selection */
+ SYNC_TX_SUBMITTER_INVALID = -1,
+
+ /** sync_tx() is repsonsible for submitting buffers for async transfer */
+ SYNC_TX_SUBMITTER_FN,
+
+ /** The TX worker callbacks should be returning buffers for submission */
+ SYNC_TX_SUBMITTER_CALLBACK
+} sync_tx_submitter;
+
+#define BUFFER_MGMT_INVALID_INDEX (UINT_MAX)
+
+struct buffer_mgmt {
+ sync_buffer_status *status;
+ size_t *actual_lengths;
+
+ void **buffers;
+ unsigned int num_buffers;
+
+ unsigned int prod_i; /**< Producer index - next buffer to fill */
+ unsigned int cons_i; /**< Consumer index - next buffer to empty */
+ unsigned int partial_off; /**< Current index into partial buffer */
+
+ /* In the event of a SW RX overrun, this count is used to determine
+ * how many more transfers should be considered invalid and require
+ * resubmission */
+ unsigned int resubmit_count;
+
+ /* Applicable to TX only. Denotes which context is responsible for
+ * submitting full buffers to the underlying async system */
+ sync_tx_submitter submitter;
+
+
+ MUTEX lock;
+ pthread_cond_t buf_ready; /**< Buffer produced by RX callback, or
+ * buffer emptied by TX callback */
+};
+
+/* State of API-side sync interface */
+typedef enum {
+ SYNC_STATE_CHECK_WORKER,
+ SYNC_STATE_RESET_BUF_MGMT,
+ SYNC_STATE_START_WORKER,
+ SYNC_STATE_WAIT_FOR_BUFFER,
+ SYNC_STATE_BUFFER_READY,
+ SYNC_STATE_USING_BUFFER,
+ SYNC_STATE_USING_PACKET_META,
+ SYNC_STATE_USING_BUFFER_META
+} sync_state;
+
+struct sync_meta {
+ sync_meta_state state; /* State of metadata processing */
+
+ uint8_t *curr_msg; /* Points to current message in the buffer */
+ size_t curr_msg_off; /* Offset into current message (samples),
+ * ignoring the 4-samples worth of metadata */
+ size_t msg_size; /* Size of data message */
+ unsigned int msg_per_buf; /* Number of data messages per buffer */
+ unsigned int msg_num; /* Which message within the buffer are we in?
+ * Range is: 0 to msg_per_buf */
+ unsigned int samples_per_msg; /* Number of samples within a message */
+ unsigned int samples_per_ts; /* Number of samples within a timestamp */
+
+ union {
+ /* Used only for RX */
+ struct {
+ uint64_t
+ msg_timestamp; /* Timestamp contained in the current message */
+ uint32_t msg_flags; /* Flags for the current message */
+ };
+
+ /* Used only for TX */
+ struct {
+ bool in_burst;
+ bool now;
+ };
+ };
+
+ uint64_t curr_timestamp; /* Timestamp at the sample we've
+ * consumed up to */
+};
+
+struct bladerf_sync {
+ MUTEX lock;
+ struct bladerf *dev;
+ bool initialized;
+ sync_state state;
+ struct buffer_mgmt buf_mgmt;
+ struct stream_config stream_config;
+ struct sync_worker *worker;
+ struct sync_meta meta;
+};
+
+/**
+ * Create and initialize as synchronous interface handle for the specified
+ * device and direction. If the synchronous handle is already initialized, this
+ * call will first deinitialize it.
+ *
+ * The associated stream will be started at the first RX or TX call
+ *
+ * @return 0 or BLADERF_ERR_* value on failure
+ */
+int sync_init(struct bladerf_sync *sync,
+ struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ size_t buffer_size,
+ size_t msg_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout);
+
+/**
+ * Deinitialize the sync handle. This tears down and deallocates the underlying
+ * asynchronous stream.
+ *
+ * @param[inout] sync Handle to deinitialize.
+ */
+void sync_deinit(struct bladerf_sync *sync);
+
+int sync_rx(struct bladerf_sync *sync,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+
+int sync_tx(struct bladerf_sync *sync,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+
+unsigned int sync_buf2idx(struct buffer_mgmt *b, void *addr);
+
+void *sync_idx2buf(struct buffer_mgmt *b, unsigned int idx);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/streaming/sync_worker.c b/Radio/HW/BladeRF/src/streaming/sync_worker.c
new file mode 100644
index 0000000..b2ec806
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/sync_worker.c
@@ -0,0 +1,532 @@
+/*
+ * 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
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Only switch on the verbose debug prints in this file when we *really* want
+ * them. Otherwise, compile them out to avoid excessive log level checks
+ * in our data path */
+#include "log.h"
+#ifndef ENABLE_LIBBLADERF_SYNC_LOG_VERBOSE
+#undef log_verbose
+#define log_verbose(...)
+#endif
+#include "rel_assert.h"
+#include "conversions.h"
+#include "minmax.h"
+
+#include "async.h"
+#include "sync.h"
+#include "sync_worker.h"
+
+#include "board/board.h"
+#include "backend/usb/usb.h"
+
+#define worker2str(s) (direction2str(s->stream_config.layout & BLADERF_DIRECTION_MASK))
+
+void *sync_worker_task(void *arg);
+
+static void *rx_callback(struct bladerf *dev,
+ struct bladerf_stream *stream,
+ struct bladerf_metadata *meta,
+ void *samples,
+ size_t num_samples,
+ void *user_data)
+{
+ unsigned int requests; /* Pending requests */
+ unsigned int next_idx;
+ unsigned int samples_idx;
+ void *next_buf = NULL; /* Next buffer to submit for reception */
+
+ struct bladerf_sync *s = (struct bladerf_sync *)user_data;
+ struct sync_worker *w = s->worker;
+ struct buffer_mgmt *b = &s->buf_mgmt;
+
+ /* Check if the caller has requested us to shut down. We'll keep the
+ * SHUTDOWN bit set through our transition into the IDLE state so we
+ * can act on it there. */
+ MUTEX_LOCK(&w->request_lock);
+ requests = w->requests;
+ MUTEX_UNLOCK(&w->request_lock);
+
+ if (requests & SYNC_WORKER_STOP) {
+ log_verbose("%s worker: Got STOP request upon entering callback. "
+ "Ending stream.\n", worker2str(s));
+ return NULL;
+ }
+
+ MUTEX_LOCK(&b->lock);
+
+ /* Get the index of the buffer that was just filled */
+ samples_idx = sync_buf2idx(b, samples);
+
+ if (b->resubmit_count == 0) {
+ if (b->status[b->prod_i] == SYNC_BUFFER_EMPTY) {
+
+ /* This buffer is now ready for the consumer */
+ b->status[samples_idx] = SYNC_BUFFER_FULL;
+ b->actual_lengths[samples_idx] = num_samples;
+ pthread_cond_signal(&b->buf_ready);
+
+ /* Update the state of the buffer being submitted next */
+ next_idx = b->prod_i;
+ b->status[next_idx] = SYNC_BUFFER_IN_FLIGHT;
+ next_buf = b->buffers[next_idx];
+
+ /* Advance to the next buffer for the next callback */
+ b->prod_i = (next_idx + 1) % b->num_buffers;
+
+ log_verbose("%s worker: buf[%u] = full, buf[%u] = in_flight\n",
+ worker2str(s), samples_idx, next_idx);
+
+ } else {
+ /* TODO propagate back the RX Overrun to the sync_rx() caller */
+ log_debug("RX overrun @ buffer %u\r\n", samples_idx);
+
+ next_buf = samples;
+ b->resubmit_count = s->stream_config.num_xfers - 1;
+ }
+ } else {
+ /* We're still recovering from an overrun at this point. Just
+ * turn around and resubmit this buffer */
+ next_buf = samples;
+ b->resubmit_count--;
+ log_verbose("Resubmitting buffer %u (%u resubmissions left)\r\n",
+ samples_idx, b->resubmit_count);
+ }
+
+
+ MUTEX_UNLOCK(&b->lock);
+ return next_buf;
+}
+
+static void *tx_callback(struct bladerf *dev,
+ struct bladerf_stream *stream,
+ struct bladerf_metadata *meta,
+ void *samples,
+ size_t num_samples,
+ void *user_data)
+{
+ unsigned int requests; /* Pending requests */
+ unsigned int completed_idx; /* Index of completed buffer */
+
+ struct bladerf_sync *s = (struct bladerf_sync *)user_data;
+ struct sync_worker *w = s->worker;
+ struct buffer_mgmt *b = &s->buf_mgmt;
+
+ void *ret = BLADERF_STREAM_NO_DATA;
+
+ /* Check if the caller has requested us to shut down. We'll keep the
+ * SHUTDOWN bit set through our transition into the IDLE state so we
+ * can act on it there. */
+ MUTEX_LOCK(&w->request_lock);
+ requests = w->requests;
+ MUTEX_UNLOCK(&w->request_lock);
+
+ if (requests & SYNC_WORKER_STOP) {
+ log_verbose("%s worker: Got STOP request upon entering callback. "
+ "Ending stream.\r\n", worker2str(s));
+ return NULL;
+ }
+
+ /* The initial set of callbacks will do not provide us with any
+ * completed sample buffers */
+ if (samples != NULL) {
+ MUTEX_LOCK(&b->lock);
+
+ /* Mark the completed buffer as being empty */
+ completed_idx = sync_buf2idx(b, samples);
+ assert(b->status[completed_idx] == SYNC_BUFFER_IN_FLIGHT);
+ b->status[completed_idx] = SYNC_BUFFER_EMPTY;
+ pthread_cond_signal(&b->buf_ready);
+
+ /* If the callback is assigned to be the submitter, there are
+ * buffers pending submission */
+ if (b->submitter == SYNC_TX_SUBMITTER_CALLBACK) {
+ assert(b->cons_i != BUFFER_MGMT_INVALID_INDEX);
+ if (b->status[b->cons_i] == SYNC_BUFFER_FULL) {
+ /* This buffer is ready to ship out ("consume") */
+ log_verbose("%s: Submitting deferred buf[%u]\n",
+ __FUNCTION__, b->cons_i);
+
+ ret = b->buffers[b->cons_i];
+ /* This is actually # of 32bit DWORDs for PACKET_META */
+ meta->actual_count = b->actual_lengths[b->cons_i];
+ b->status[b->cons_i] = SYNC_BUFFER_IN_FLIGHT;
+ b->cons_i = (b->cons_i + 1) % b->num_buffers;
+ } else {
+ log_verbose("%s: No deferred buffer available. "
+ "Assigning submitter=FN\n", __FUNCTION__);
+
+ b->submitter = SYNC_TX_SUBMITTER_FN;
+ b->cons_i = BUFFER_MGMT_INVALID_INDEX;
+ }
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+
+ log_verbose("%s worker: Buffer %u emptied.\r\n",
+ worker2str(s), completed_idx);
+ }
+
+ return ret;
+}
+
+int sync_worker_init(struct bladerf_sync *s)
+{
+ int status = 0;
+ s->worker = (struct sync_worker *)calloc(1, sizeof(*s->worker));
+
+ if (s->worker == NULL) {
+ status = BLADERF_ERR_MEM;
+ goto worker_init_out;
+ }
+
+ s->worker->state = SYNC_WORKER_STATE_STARTUP;
+ s->worker->err_code = 0;
+
+ s->worker->cb =
+ (s->stream_config.layout & BLADERF_DIRECTION_MASK) == BLADERF_RX
+ ? rx_callback
+ : tx_callback;
+
+ status = async_init_stream(
+ &s->worker->stream, s->dev, s->worker->cb, &s->buf_mgmt.buffers,
+ s->buf_mgmt.num_buffers, s->stream_config.format,
+ s->stream_config.samples_per_buffer, s->stream_config.num_xfers, s);
+
+ if (status != 0) {
+ log_debug("%s worker: Failed to init stream: %s\n", worker2str(s),
+ bladerf_strerror(status));
+ goto worker_init_out;
+ }
+
+ status = async_set_transfer_timeout(
+ s->worker->stream,
+ uint_max(s->stream_config.timeout_ms, BULK_TIMEOUT_MS));
+ if (status != 0) {
+ log_debug("%s worker: Failed to set transfer timeout: %s\n",
+ worker2str(s), bladerf_strerror(status));
+ goto worker_init_out;
+ }
+
+ MUTEX_INIT(&s->worker->state_lock);
+ MUTEX_INIT(&s->worker->request_lock);
+
+ status = pthread_cond_init(&s->worker->state_changed, NULL);
+ if (status != 0) {
+ log_debug("%s worker: pthread_cond_init(state_changed) failed: %d\n",
+ worker2str(s), status);
+ status = BLADERF_ERR_UNEXPECTED;
+ goto worker_init_out;
+ }
+
+ status = pthread_cond_init(&s->worker->requests_pending, NULL);
+ if (status != 0) {
+ log_debug("%s worker: pthread_cond_init(requests_pending) failed: %d\n",
+ worker2str(s), status);
+ status = BLADERF_ERR_UNEXPECTED;
+ goto worker_init_out;
+ }
+
+ status = pthread_create(&s->worker->thread, NULL, sync_worker_task, s);
+ if (status != 0) {
+ log_debug("%s worker: pthread_create failed: %d\n", worker2str(s),
+ status);
+ status = BLADERF_ERR_UNEXPECTED;
+ goto worker_init_out;
+ }
+
+ /* Wait until the worker thread has initialized and is ready to go */
+ status =
+ sync_worker_wait_for_state(s->worker, SYNC_WORKER_STATE_IDLE, 1000);
+ if (status != 0) {
+ log_debug("%s worker: sync_worker_wait_for_state failed: %d\n",
+ worker2str(s), status);
+ status = BLADERF_ERR_TIMEOUT;
+ goto worker_init_out;
+ }
+
+worker_init_out:
+ if (status != 0) {
+ free(s->worker);
+ s->worker = NULL;
+ }
+
+ return status;
+}
+
+void sync_worker_deinit(struct sync_worker *w,
+ pthread_mutex_t *lock, pthread_cond_t *cond)
+{
+ int status;
+
+ if (w == NULL) {
+ log_debug("%s called with NULL ptr\n", __FUNCTION__);
+ return;
+ }
+
+ log_verbose("%s: Requesting worker %p to stop...\n", __FUNCTION__, w);
+
+ sync_worker_submit_request(w, SYNC_WORKER_STOP);
+
+ if (lock != NULL && cond != NULL) {
+ MUTEX_LOCK(lock);
+ pthread_cond_signal(cond);
+ MUTEX_UNLOCK(lock);
+ }
+
+ status = sync_worker_wait_for_state(w, SYNC_WORKER_STATE_STOPPED, 3000);
+
+ if (status != 0) {
+ log_warning("Timed out while stopping worker. Canceling thread.\n");
+ pthread_cancel(w->thread);
+ }
+
+ pthread_join(w->thread, NULL);
+ log_verbose("%s: Worker joined.\n", __FUNCTION__);
+
+ async_deinit_stream(w->stream);
+
+ free(w);
+}
+
+void sync_worker_submit_request(struct sync_worker *w, unsigned int request)
+{
+ MUTEX_LOCK(&w->request_lock);
+ w->requests |= request;
+ pthread_cond_signal(&w->requests_pending);
+ MUTEX_UNLOCK(&w->request_lock);
+}
+
+int sync_worker_wait_for_state(struct sync_worker *w, sync_worker_state state,
+ unsigned int timeout_ms)
+{
+ int status = 0;
+ struct timespec timeout_abs;
+ const int nsec_per_sec = 1000 * 1000 * 1000;
+
+ if (timeout_ms != 0) {
+ const unsigned int timeout_sec = timeout_ms / 1000;
+
+ status = clock_gettime(CLOCK_REALTIME, &timeout_abs);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ timeout_abs.tv_sec += timeout_sec;
+ timeout_abs.tv_nsec += (timeout_ms % 1000) * 1000 * 1000;
+
+ if (timeout_abs.tv_nsec >= nsec_per_sec) {
+ timeout_abs.tv_sec += timeout_abs.tv_nsec / nsec_per_sec;
+ timeout_abs.tv_nsec %= nsec_per_sec;
+ }
+
+ MUTEX_LOCK(&w->state_lock);
+ status = 0;
+ while (w->state != state && status == 0) {
+ status = pthread_cond_timedwait(&w->state_changed,
+ &w->state_lock,
+ &timeout_abs);
+ }
+ MUTEX_UNLOCK(&w->state_lock);
+
+ } else {
+ MUTEX_LOCK(&w->state_lock);
+ while (w->state != state) {
+ log_verbose(": Waiting for state change, current = %d\n", w->state);
+ status = pthread_cond_wait(&w->state_changed,
+ &w->state_lock);
+ }
+ MUTEX_UNLOCK(&w->state_lock);
+ }
+
+ if (status != 0) {
+ log_debug("%s: Wait on state change failed: %s\n",
+ __FUNCTION__, strerror(status));
+
+ if (status == ETIMEDOUT) {
+ status = BLADERF_ERR_TIMEOUT;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+sync_worker_state sync_worker_get_state(struct sync_worker *w,
+ int *err_code)
+{
+ sync_worker_state ret;
+
+ MUTEX_LOCK(&w->state_lock);
+ ret = w->state;
+ if (err_code) {
+ *err_code = w->err_code;
+ w->err_code = 0;
+ }
+ MUTEX_UNLOCK(&w->state_lock);
+
+ return ret;
+}
+
+static void set_state(struct sync_worker *w, sync_worker_state state)
+{
+ MUTEX_LOCK(&w->state_lock);
+ w->state = state;
+ pthread_cond_signal(&w->state_changed);
+ MUTEX_UNLOCK(&w->state_lock);
+}
+
+
+static sync_worker_state exec_idle_state(struct bladerf_sync *s)
+{
+ sync_worker_state next_state = SYNC_WORKER_STATE_IDLE;
+ unsigned int requests;
+ unsigned int i;
+
+ MUTEX_LOCK(&s->worker->request_lock);
+
+ while (s->worker->requests == 0) {
+ log_verbose("%s worker: Waiting for pending requests\n", worker2str(s));
+
+ pthread_cond_wait(&s->worker->requests_pending,
+ &s->worker->request_lock);
+ }
+
+ requests = s->worker->requests;
+ s->worker->requests = 0;
+ MUTEX_UNLOCK(&s->worker->request_lock);
+
+ if (requests & SYNC_WORKER_STOP) {
+ log_verbose("%s worker: Got request to stop\n", worker2str(s));
+
+ next_state = SYNC_WORKER_STATE_SHUTTING_DOWN;
+
+ } else if (requests & SYNC_WORKER_START) {
+ log_verbose("%s worker: Got request to start\n", worker2str(s));
+ MUTEX_LOCK(&s->buf_mgmt.lock);
+
+ if ((s->stream_config.layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ /* If we've previously timed out on a stream, we'll likely have some
+ * stale buffers marked "in-flight" that have since been cancelled. */
+ for (i = 0; i < s->buf_mgmt.num_buffers; i++) {
+ if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) {
+ s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY;
+ }
+ }
+
+ pthread_cond_signal(&s->buf_mgmt.buf_ready);
+ } else {
+ s->buf_mgmt.prod_i = s->stream_config.num_xfers;
+
+ for (i = 0; i < s->buf_mgmt.num_buffers; i++) {
+ if (i < s->stream_config.num_xfers) {
+ s->buf_mgmt.status[i] = SYNC_BUFFER_IN_FLIGHT;
+ } else if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) {
+ s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY;
+ }
+ }
+ }
+
+ MUTEX_UNLOCK(&s->buf_mgmt.lock);
+
+ next_state = SYNC_WORKER_STATE_RUNNING;
+ } else {
+ log_warning("Invalid request value encountered: 0x%08X\n",
+ s->worker->requests);
+ }
+
+ return next_state;
+}
+
+static void exec_running_state(struct bladerf_sync *s)
+{
+ int status;
+
+ status = async_run_stream(s->worker->stream, s->stream_config.layout);
+
+ log_verbose("%s worker: stream ended with: %s\n",
+ worker2str(s), bladerf_strerror(status));
+
+ /* Save off the result of running the stream so we can report what
+ * happened to the API caller */
+ MUTEX_LOCK(&s->worker->state_lock);
+ s->worker->err_code = status;
+ MUTEX_UNLOCK(&s->worker->state_lock);
+
+ /* Wake the API-side if an error occurred, so that it can propagate
+ * the stream error code back to the API caller */
+ if (status != 0) {
+ MUTEX_LOCK(&s->buf_mgmt.lock);
+ pthread_cond_signal(&s->buf_mgmt.buf_ready);
+ MUTEX_UNLOCK(&s->buf_mgmt.lock);
+ }
+}
+
+void *sync_worker_task(void *arg)
+{
+ sync_worker_state state = SYNC_WORKER_STATE_IDLE;
+ struct bladerf_sync *s = (struct bladerf_sync *)arg;
+
+ log_verbose("%s worker: task started\n", worker2str(s));
+ set_state(s->worker, state);
+ log_verbose("%s worker: task state set\n", worker2str(s));
+
+ while (state != SYNC_WORKER_STATE_STOPPED) {
+
+ switch (state) {
+ case SYNC_WORKER_STATE_STARTUP:
+ assert(!"Worker in unexpected state, shutting down. (STARTUP)");
+ set_state(s->worker, SYNC_WORKER_STATE_SHUTTING_DOWN);
+ break;
+
+ case SYNC_WORKER_STATE_IDLE:
+ state = exec_idle_state(s);
+ set_state(s->worker, state);
+ break;
+
+ case SYNC_WORKER_STATE_RUNNING:
+ exec_running_state(s);
+ state = SYNC_WORKER_STATE_IDLE;
+ set_state(s->worker, state);
+ break;
+
+ case SYNC_WORKER_STATE_SHUTTING_DOWN:
+ log_verbose("%s worker: Shutting down...\n", worker2str(s));
+
+ state = SYNC_WORKER_STATE_STOPPED;
+ set_state(s->worker, state);
+ break;
+
+ case SYNC_WORKER_STATE_STOPPED:
+ assert(!"Worker in unexpected state: STOPPED");
+ break;
+
+ default:
+ assert(!"Worker in unexpected state, shutting down. (UNKNOWN)");
+ set_state(s->worker, SYNC_WORKER_STATE_SHUTTING_DOWN);
+ break;
+ }
+ }
+
+ return NULL;
+}
diff --git a/Radio/HW/BladeRF/src/streaming/sync_worker.h b/Radio/HW/BladeRF/src/streaming/sync_worker.h
new file mode 100644
index 0000000..35d5075
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/sync_worker.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 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 STREAMING_SYNC_WORKER_H_
+#define STREAMING_SYNC_WORKER_H_
+
+#include "host_config.h"
+#include "sync.h"
+#include <libbladeRF.h>
+#include <pthread.h>
+
+#if BLADERF_OS_WINDOWS || BLADERF_OS_OSX
+#include "clock_gettime.h"
+#else
+#include <time.h>
+#endif
+
+/* Worker lifetime:
+ *
+ * STARTUP --+--> IDLE --> RUNNING --+--> SHUTTING_DOWN --> STOPPED
+ * ^----------------------/
+ */
+
+/* Request flags */
+#define SYNC_WORKER_START (1 << 0)
+#define SYNC_WORKER_STOP (1 << 1)
+
+typedef enum {
+ SYNC_WORKER_STATE_STARTUP,
+ SYNC_WORKER_STATE_IDLE,
+ SYNC_WORKER_STATE_RUNNING,
+ SYNC_WORKER_STATE_SHUTTING_DOWN,
+ SYNC_WORKER_STATE_STOPPED
+} sync_worker_state;
+
+struct sync_worker {
+ pthread_t thread;
+
+ struct bladerf_stream *stream;
+ bladerf_stream_cb cb;
+
+ /* These items should be accessed while holding state_lock */
+ sync_worker_state state;
+ int err_code;
+ MUTEX state_lock;
+ pthread_cond_t state_changed; /* Worker thread uses this to inform a
+ * waiting main thread about a state
+ * change */
+
+ /* The requests lock should always be acquired AFTER
+ * the sync->buf_mgmt.lock
+ */
+ unsigned int requests;
+ pthread_cond_t requests_pending;
+ MUTEX request_lock;
+};
+
+/**
+ * Create a launch a worker thread. It will enter the IDLE state upon
+ * executing.
+ *
+ * @param s Sync handle containing worker to initialize
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int sync_worker_init(struct bladerf_sync *s);
+
+/**
+ * Shutdown and deinitialize
+ *
+ * @param w Worker to deinitialize
+ * @param[in] lock Acquired to signal `cond` if non-NULL
+ * @param[in] cond If non-NULL, this is signaled after requesting the
+ * worker to shut down, waking a potentially blocked
+ * workers.
+ */
+void sync_worker_deinit(struct sync_worker *w,
+ pthread_mutex_t *lock,
+ pthread_cond_t *cond);
+
+/**
+ * Wait for state change with optional timeout
+ *
+ * @param w Worker to wait for
+ * @param[in] state State to wait for
+ * @param[in] timeout_ms Timeout in ms. 0 implies "wait forever"
+ *
+ * @return 0 on success, BLADERF_ERR_TIMEOUT on timeout, BLADERF_ERR_UNKNOWN on
+ * other errors
+ */
+int sync_worker_wait_for_state(struct sync_worker *w,
+ sync_worker_state state,
+ unsigned int timeout_ms);
+
+/**
+ * Get the worker's current state.
+ *
+ * @param w Worker to query
+ * @param[out] err_code Stream error code (libbladeRF error code value).
+ * Querying this value will reset the interal error
+ * code value.
+ *
+ * @return Worker's current state
+ */
+sync_worker_state sync_worker_get_state(struct sync_worker *w, int *err_code);
+
+/**
+ * Submit a request to the worker task
+ *
+ * @param w Worker to send request to
+ * @param[in] request Bitmask of requests to submit
+ */
+void sync_worker_submit_request(struct sync_worker *w, unsigned int request);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/test.swift b/Radio/HW/BladeRF/src/test.swift
new file mode 100644
index 0000000..29717de
--- /dev/null
+++ b/Radio/HW/BladeRF/src/test.swift
@@ -0,0 +1,8 @@
+//
+// test.swift
+// PrySDR
+//
+// Created by Jacky Jack on 01/11/2024.
+//
+import libbladerf
+//var version:bladerf_version
diff --git a/Radio/HW/BladeRF/src/version.h b/Radio/HW/BladeRF/src/version.h
new file mode 100644
index 0000000..999d860
--- /dev/null
+++ b/Radio/HW/BladeRF/src/version.h
@@ -0,0 +1,12 @@
+#ifndef VERSION_H_
+#define VERSION_H_
+
+#define LIBBLADERF_VERSION "2.5.1-git-fe3304d7-dirty"
+
+// clang-format off
+#define LIBBLADERF_VERSION_MAJOR 2
+#define LIBBLADERF_VERSION_MINOR 5
+#define LIBBLADERF_VERSION_PATCH 1
+// clang-format on
+
+#endif
diff --git a/Radio/HW/BladeRF/src/version.h.in b/Radio/HW/BladeRF/src/version.h.in
new file mode 100644
index 0000000..7e66202
--- /dev/null
+++ b/Radio/HW/BladeRF/src/version.h.in
@@ -0,0 +1,12 @@
+#ifndef VERSION_H_
+#define VERSION_H_
+
+#define LIBBLADERF_VERSION "@VERSION@"
+
+// clang-format off
+#define LIBBLADERF_VERSION_MAJOR @VERSION_INFO_MAJOR@
+#define LIBBLADERF_VERSION_MINOR @VERSION_INFO_MINOR@
+#define LIBBLADERF_VERSION_PATCH @VERSION_INFO_PATCH@
+// clang-format on
+
+#endif