summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/board/bladerf1
diff options
context:
space:
mode:
authorArturs Artamonovs <arturs.artamonovs@protonmail.com>2024-11-03 15:56:55 +0000
committerArturs Artamonovs <arturs.artamonovs@protonmail.com>2024-11-03 15:56:55 +0000
commitcf4444e7390365df43ecbd3d130015c1e06ef88f (patch)
tree8a6eb114135a04d5efd5af213577b4fac47532ae /Radio/HW/BladeRF/src/board/bladerf1
parentca50c0f64f1b2fce46b4cb83ed111854bac13852 (diff)
downloadPrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.tar.gz
PrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.zip
BladeRF library compiles
Diffstat (limited to 'Radio/HW/BladeRF/src/board/bladerf1')
-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
10 files changed, 6352 insertions, 0 deletions
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);
+ }
+}